diff --git a/src/essentia/essentiamath.h b/src/essentia/essentiamath.h index 8dc6566e8..ef5b0cfb6 100644 --- a/src/essentia/essentiamath.h +++ b/src/essentia/essentiamath.h @@ -762,6 +762,15 @@ inline std::string midi2note(int midiNoteNumber) { return closest_note; } +inline std::string note2root(std::string note) { + return note.substr(0, note.size()-1); +} + +inline int note2octave(std::string note) { + char octaveChar = note.back(); + return octaveChar - '0'; +} + inline Real midi2hz(int midiNoteNumber, Real tuningFrequency) { return tuningFrequency * powf(2, (midiNoteNumber - 69) / 12.0); } diff --git a/src/python/essentia/utils.py b/src/python/essentia/utils.py index 2c3653d4c..279004dca 100644 --- a/src/python/essentia/utils.py +++ b/src/python/essentia/utils.py @@ -82,6 +82,12 @@ def hz2cents(arg1, arg2): def midi2note(arg): return _essentia.midi2note( _c.convertData(arg, _c.Edt.INTEGER) ) +def note2root(arg): + return _essentia.note2root( _c.convertData(arg, _c.Edt.STRING) ) + +def note2octave(arg): + return _essentia.note2octave( _c.convertData(arg, _c.Edt.STRING) ) + def equivalentKey(arg): return _essentia.equivalentKey( _c.convertData(arg, _c.Edt.STRING) ) @@ -107,6 +113,7 @@ def derivative(array): 'mel2hz', 'hz2mel', 'midi2hz', 'hz2midi', 'cents2hz', 'hz2cents', + 'note2root', 'note2octave', 'midi2note', 'postProcessTicks', 'normalize', 'derivative', diff --git a/src/python/globalfuncs.cpp b/src/python/globalfuncs.cpp index 52460675c..b166b30bf 100644 --- a/src/python/globalfuncs.cpp +++ b/src/python/globalfuncs.cpp @@ -457,6 +457,31 @@ midiToNote(PyObject* notUsed, PyObject* arg) { return PyString_FromString( c_note ); } +static PyObject* +noteToRoot(PyObject* notUsed, PyObject* arg) { + + if (!PyString_Check(arg)) { + PyErr_SetString(PyExc_TypeError, (char*)"expecting arguments (string note)"); + return NULL; + } + + std::string root = note2root( PyString_AS_STRING(arg) ); + const char *c_root = root.c_str(); + return PyString_FromString( c_root ); +} + +static PyObject* +noteToOctave(PyObject* notUsed, PyObject* arg) { + + if (!PyString_Check(arg)) { + PyErr_SetString(PyExc_TypeError, (char*)"expecting arguments (string note)"); + return NULL; + } + + int octave = note2octave( PyString_AS_STRING(arg) ); + return PyLong_FromLong( int(octave) ); +} + static PyObject* getEquivalentKey(PyObject* notUsed, PyObject* arg) { if (!PyString_Check(arg)) { @@ -1073,6 +1098,8 @@ static PyMethodDef Essentia__Methods[] = { { "hz2cents", hzToCents, METH_VARARGS, "Returns the cents distance between two frequencies in Hz" }, { "cents2hz", centsToHz, METH_VARARGS, "Returns the frequency from a frequency in Hz and cents distance" }, { "midi2note", midiToNote, METH_O, "Converts a midi note number to note applying the international pitch standard (A4=440Hz)" }, + { "note2root", noteToRoot, METH_O, "Returns the root of a note" }, + { "note2octave", noteToOctave, METH_O, "Returns the octave of a note" }, { "lin2db", linToDb, METH_O, "Converts a linear measure of power to a measure in dB" }, { "db2lin", dbToLin, METH_O, "Converts a dB measure of power to a linear measure" }, { "db2pow", dbToPow, METH_O, "Converts a dB measure of power to a linear measure" }, diff --git a/test/src/unittests/base/test_utils.py b/test/src/unittests/base/test_utils.py index 102edb877..a02f2bc9f 100644 --- a/test/src/unittests/base/test_utils.py +++ b/test/src/unittests/base/test_utils.py @@ -139,6 +139,16 @@ def testMidiToNote(self): expected_note = "A4" self.assertEqual(expected_note, midi2note(midi)) + def testNoteToRoot(self): + note = "A4" + expected_root = note[0] + self.assertEqual(expected_root, note2root(note)) + + def testNoteToOctave(self): + note = "A4" + expected_octave = int(note[1]) + self.assertEqual(expected_octave, note2octave(note)) + suite = allTests(TestUtils)