python/lib/aubio/midiconv.py: improve unicode handling, skip UnicodeEncodeError on...
authorPaul Brossier <piem@piem.org>
Sat, 3 Dec 2016 03:03:47 +0000 (04:03 +0100)
committerPaul Brossier <piem@piem.org>
Sat, 3 Dec 2016 03:03:47 +0000 (04:03 +0100)
python/lib/aubio/midiconv.py
python/tests/test_note2midi.py

index efe7ec0..c79523f 100644 (file)
@@ -15,11 +15,13 @@ else:
 def note2midi(note):
     " convert note name to midi note number, e.g. [C-1, G9] -> [0, 127] "
     _valid_notenames = {'C': 0, 'D': 2, 'E': 4, 'F': 5, 'G': 7, 'A': 9, 'B': 11}
-    _valid_modifiers = {None: 0, u'♮': 0, '#': +1, u'♯': +1, u'\udd2a': +2,
-                        u'\U0001D12A': +2,
-                        'b': -1, u'♭': -1, u'\ufffd': -2,
-                        u'\U0001D12B': -2,
-                        }
+    _valid_modifiers = {
+            u'𝄫': -2,                        # double flat
+            u'♭': -1, 'b': -1, '\u266d': -1, # simple flat
+            u'♮': 0, '\u266e': 0, None: 0,   # natural
+            '#': +1, u'♯': +1, '\u266f': +1, # sharp
+            u'𝄪': +2,                        # double sharp
+            }
     _valid_octaves = range(-1, 10)
     if not isinstance(note, str_instances):
         raise TypeError("a string is required, got %s (%s)" % (note, str(type(note))))
index 7524a51..261b025 100755 (executable)
@@ -14,21 +14,36 @@ list_of_known_notes = (
         ( 'C3', 48 ),
         ( 'B3', 59 ),
         ( 'B#3', 60 ),
-        ( 'C\u266f4', 61 ),
-        ( 'C\U0001D12A4', 62 ),
-        ( 'E\U0001D12B4', 62 ),
+        ( 'C♯4', 61 ),
         ( 'A4', 69 ),
         ( 'A#4', 70 ),
+        ( 'A♯4', 70 ),
+        ( 'A\u266f4', 70 ),
         ( 'Bb4', 70 ),
         ( 'B♭4', 70 ),
+        ( 'B\u266d4', 70 ),
         ( 'G8', 115 ),
         ( 'G♯8', 116 ),
         ( 'G9', 127 ),
-        ( 'G\udd2a2', 45 ),
-        ( 'B\ufffd2', 45 ),
         ( 'A♮2', 45 ),
         )
 
+list_of_known_notes_with_unicode_issues = (
+        ('C𝄪4', 62 ),
+        ('E𝄫4', 62 ),
+        )
+
+list_of_unknown_notes = (
+        ( 'G\udd2a2' ),
+        ( 'B\ufffd2' ),
+        ( 'B\u266e\u266e2' ),
+        ( 'B\u266f\u266d3' ),
+        ( 'B33' ),
+        ( 'C.3' ),
+        ( 'A' ),
+        ( '2' ),
+        )
+
 class note2midi_good_values(unittest.TestCase):
 
     @params(*list_of_known_notes)
@@ -36,6 +51,21 @@ class note2midi_good_values(unittest.TestCase):
         " known values are correctly converted "
         self.assertEqual ( note2midi(note), midi )
 
+    @params(*list_of_known_notes_with_unicode_issues)
+    def test_note2midi_known_values_with_unicode_issues(self, note, midi):
+        " known values are correctly converted, unless decoding is expected to fail"
+        try:
+            self.assertEqual ( note2midi(note), midi )
+        except UnicodeEncodeError as e:
+            import sys
+            strfmt = "len(u'\\U0001D12A') != 1, excpected decoding failure | {:s} | {:s} {:s}"
+            strres = strfmt.format(e, sys.platform, sys.version)
+            # happens with: darwin 2.7.10, windows 2.7.12
+            if len('\U0001D12A') != 1 and sys.version[0] == '2':
+                self.skipTest(strres + " | upgrade to Python 3 to fix")
+            else:
+                raise
+
 class note2midi_wrong_values(unittest.TestCase):
 
     def test_note2midi_missing_octave(self):
@@ -70,6 +100,14 @@ class note2midi_wrong_values(unittest.TestCase):
         " fails when passed a non-string value "
         self.assertRaises(TypeError, note2midi, 123)
 
+    def test_note2midi_wrong_data_too_long(self):
+        " fails when passed a note with a note name longer than expected"
+        self.assertRaises(ValueError, note2midi, 'CB+-3')
+
+    @params(*list_of_unknown_notes)
+    def test_note2midi_unknown_values(self, note):
+        " unknown values throw out an error "
+        self.assertRaises(ValueError, note2midi, note)
 
 class freq2note_simple_test(unittest.TestCase):