From 33102ab973e7359036d4a0fdb8fe9194e5736f92 Mon Sep 17 00:00:00 2001 From: Paul Brossier Date: Sun, 10 Mar 2013 10:34:45 -0500 Subject: [PATCH] lib/aubio/midiconv.py: add note2midi --- python/lib/aubio/__init__.py | 1 + python/lib/aubio/midiconv.py | 37 +++++++++++++++++++++++ python/tests/test_note2midi.py | 54 ++++++++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+) create mode 100644 python/lib/aubio/midiconv.py create mode 100755 python/tests/test_note2midi.py diff --git a/python/lib/aubio/__init__.py b/python/lib/aubio/__init__.py index 136848b1..5fdc0ae0 100644 --- a/python/lib/aubio/__init__.py +++ b/python/lib/aubio/__init__.py @@ -1,5 +1,6 @@ import numpy from _aubio import * +from midiconv import * class fvec(numpy.ndarray): diff --git a/python/lib/aubio/midiconv.py b/python/lib/aubio/midiconv.py new file mode 100644 index 00000000..8ba3d58b --- /dev/null +++ b/python/lib/aubio/midiconv.py @@ -0,0 +1,37 @@ +# -*- encoding: utf8 -*- + +def note2midi(note): + " convert a note name to a midi note value " + # from C-2 to G8, though we do accept everything in the upper octave + _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, 'b': -1, u'♭': -1, u'\ufffd': -2} + _valid_octaves = range(-1, 10) + if not (1 < len(note) < 5): + raise ValueError, "string of 2 to 4 characters expected, got %d (%s)" % (len(note), note) + notename, modifier, octave = [None]*3 + + if len(note) == 4: + notename, modifier, octave_sign, octave = note + octave = octave_sign + octave + elif len(note) == 3: + notename, modifier, octave = note + if modifier == '-': + octave = modifier + octave + modifier = None + else: + notename, octave = note + + notename = notename.upper() + octave = int(octave) + + if notename not in _valid_notenames: + raise ValueError, "%s is not a valid note name" % notename + if modifier not in _valid_modifiers: + raise ValueError, "only # and b are acceptable modifiers, not %s" % modifier + if octave not in _valid_octaves: + raise ValueError, "%s is not a valid octave" % octave + + midi = 12 + octave * 12 + _valid_notenames[notename] + _valid_modifiers[modifier] + if midi > 127: + raise ValueError, "%s is outside of the range C-2 to G8" % note + return midi diff --git a/python/tests/test_note2midi.py b/python/tests/test_note2midi.py new file mode 100755 index 00000000..9004ff8f --- /dev/null +++ b/python/tests/test_note2midi.py @@ -0,0 +1,54 @@ +# -*- encoding: utf8 -*- + +from aubio import note2midi +import unittest + +list_of_known_notes = ( + ( 'C-1', 0 ), + ( 'C#-1', 1 ), + ( 'd2', 38 ), + ( 'C3', 48 ), + ( 'B3', 59 ), + ( 'B#3', 60 ), + ( 'A4', 69 ), + ( 'A#4', 70 ), + ( 'G8', 115 ), + ( u'G♯8', 116 ), + ( u'G♭9', 126 ), + ( 'G9', 127 ), + ( u'G\udd2a2', 45 ), + ( u'B\ufffd2', 45 ), + ( u'A♮2', 45 ), + ) + +class TestNote2MidiGoodValues(unittest.TestCase): + + def test_note2midi_known_values(self): + " known values are correctly converted " + for note, midi in list_of_known_notes: + self.assertEqual ( note2midi(note), midi ) + +class TestNote2MidiWrongValues(unittest.TestCase): + + def test_note2midi_missing_octave(self): + " fails when passed only one character" + self.assertRaises(ValueError, note2midi, 'C') + + def test_note2midi_wrong_modifier(self): + " fails when passed an invalid note name" + self.assertRaises(ValueError, note2midi, 'C.1') + + def test_note2midi_wronge_midifier_again(self): + " fails when passed a wrong modifier" + self.assertRaises(ValueError, note2midi, 'CB-3') + + def test_note2midi_wrong_octave(self): + " fails when passed a wrong octave" + self.assertRaises(ValueError, note2midi, 'CBc') + + def test_note2midi_out_of_range(self): + " fails when passed a non-existing note" + self.assertRaises(ValueError, note2midi, 'A9') + +if __name__ == '__main__': + unittest.main() -- 2.26.2