Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add essentiamath #1417

Merged
merged 23 commits into from
May 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
e7544ae
Add hz2midi, midi2hz, hz2cents, midi2note and db2velocity in essentia…
xaviliz May 22, 2024
0813303
Add python binding and unittest for hz2midi()
xaviliz May 22, 2024
9bbc41d
Add unittest for midi2hz()
xaviliz May 22, 2024
905fd16
Replace old hz2cents() by hz2midi()
xaviliz May 22, 2024
4b061bf
Add hz2cents unittest
xaviliz May 23, 2024
b426754
Harcode all_notes vector in midi2note()
xaviliz May 24, 2024
2191b6d
Add cents2hz
xaviliz May 24, 2024
be9d709
Apply identation style
xaviliz May 24, 2024
9f8cb11
Add default tuning frequency for midi2hz() and hz2midi()
xaviliz May 24, 2024
4c522f9
Add python binding and unittest for cents2hz()
xaviliz May 24, 2024
323cc44
Small fix
xaviliz May 27, 2024
3340cc9
Fix cents2hz() and add midi2note()
xaviliz May 27, 2024
c10dc6b
Add note2root() and note2octave()
xaviliz May 27, 2024
c84a9a5
Add note2midi() math function
xaviliz May 27, 2024
a06268a
Small refactor
xaviliz May 27, 2024
c50c337
Add hz2note() and note2hz() math functions
xaviliz May 27, 2024
ed89c21
Add velocity2db() and db2velocity() as math functions
xaviliz May 27, 2024
4d2acbe
Change the frequencyB variable by referenceFrequency in cents2hz() an…
xaviliz May 28, 2024
5575528
Break out the loop in explicit way.
xaviliz May 28, 2024
5dc2414
Apply identation style
xaviliz May 28, 2024
1e09fd7
Change the input sorting in testCentsToHz()
xaviliz May 28, 2024
9b383ab
Add ALL_NOTES as a macro
xaviliz May 29, 2024
1e6b692
Fix issue with tuning frequency estimating midi notes.
xaviliz May 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/algorithms/tonal/pitchyinprobabilities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ void PitchYinProbabilities::compute() {
bool isLowAmplitude = (RMS < _lowAmp);

for (size_t iCandidate = 0; iCandidate < _freq.size(); ++iCandidate) {
Real pitchCents = hz2cents(_freq[iCandidate]);
Real pitchCents = hz2midi(_freq[iCandidate], 440.0);
_freq[iCandidate] = pitchCents;
if (isLowAmplitude) {
// lower the probabilities of the frequencies by calculating the weighted sum
Expand Down
88 changes: 86 additions & 2 deletions src/essentia/essentiamath.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#include "utils/tnt/tnt2essentiautils.h"

#define M_2PI (2 * M_PI)
#define ALL_NOTES "A", "A#", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#"

namespace essentia {

Expand Down Expand Up @@ -738,8 +739,91 @@ inline Real hz2hz(Real hz){
return hz;
}

inline Real hz2cents(Real hz) {
return 12 * std::log(hz/440)/std::log(2.) + 69;
inline Real cents2hz(Real cents, Real referenceFrequency) {
return referenceFrequency * powf(2.0, cents / 1200.0);
}

inline Real hz2cents(Real hz, Real referenceFrequency) {
return 1200 * log2(hz / referenceFrequency);
}

inline int hz2midi(Real hz, Real tuningFrequency) {
return 69 + (int) round(log2(hz / tuningFrequency) * 12);
}

inline Real midi2hz(int midiNoteNumber, Real tuningFrequency) {
return tuningFrequency * powf(2, (midiNoteNumber - 69) / 12.0);
}

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 std::string midi2note(int midiNoteNumber) {
std::string NOTES[] = {ALL_NOTES};
//int nNotes = NOTES.size();
int nNotes = *(&NOTES + 1) - NOTES;
int CIdx = 3;
int diffCIdx = nNotes - CIdx;
int noteIdx = midiNoteNumber - 69;
int idx = abs(noteIdx) % nNotes;
int octave = (CIdx + 1) + floor((noteIdx + diffCIdx) / nNotes);
if (noteIdx < 0) {
idx = abs(idx - nNotes) % nNotes;
}
std::string closest_note = NOTES[idx] + std::to_string(octave);
return closest_note;
}

inline int note2midi(std::string note) {
//const std::vector<std::string> ALL_NOTES { "A", "A#", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#" };
std::string NOTES[] = {ALL_NOTES};
int octave = note2octave(note);
std::string root = note2root(note);
int nNotes = *(&NOTES + 1) - NOTES;
//int nNotes = NOTES.size();
int CIdx = 3;

int noteIdx = floor((octave - (CIdx + 1)) * nNotes);
int idx = 0;
for (int i = 0; i < nNotes; i++) {
if (NOTES[i] == root) {
idx = i;
if (idx >= CIdx) {
idx = idx - nNotes;
}
break;
}
}
int midiNote = noteIdx + 69 + idx;
return midiNote;
}

inline std::string hz2note(Real hz, Real tuningFrequency) {
int midiNoteNumber = hz2midi(hz, tuningFrequency);
return midi2note(midiNoteNumber);
}

inline int note2hz(std::string note, Real tuningFrequency) {
int midiNoteNumber = note2midi(note);
return midi2hz(midiNoteNumber, tuningFrequency);
}

inline int db2velocity (Real decibels, Real hearingThreshold) {
int velocity = 0;
if (decibels > hearingThreshold) {
velocity = (int)((hearingThreshold - decibels) * 127 / hearingThreshold); // decibels should be negative
}
return velocity;
}

inline Real velocity2db(int velocity, Real hearingThreshold) {
return -(hearingThreshold * velocity / 127 -hearingThreshold);
}

inline int argmin(const std::vector<Real>& input) {
Expand Down
50 changes: 50 additions & 0 deletions src/python/essentia/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,50 @@ def mel2hz(arg):
def hz2mel(arg):
return _essentia.hz2mel( _c.convertData(arg, _c.Edt.REAL) )

def midi2hz(arg1, arg2=440.0):
return _essentia.midi2hz( _c.convertData(arg1, _c.Edt.INTEGER),
_c.convertData(arg2, _c.Edt.REAL) )

def hz2midi(arg1, arg2=440.0):
return _essentia.hz2midi( _c.convertData(arg1, _c.Edt.REAL),
_c.convertData(arg2, _c.Edt.REAL) )

def cents2hz(arg1, arg2):
return _essentia.cents2hz(_c.convertData(arg1, _c.Edt.REAL),
_c.convertData(arg2, _c.Edt.REAL) )

def hz2cents(arg1, arg2):
return _essentia.hz2cents(_c.convertData(arg1, _c.Edt.REAL),
_c.convertData(arg2, _c.Edt.REAL) )

def midi2note(arg):
return _essentia.midi2note( _c.convertData(arg, _c.Edt.INTEGER) )

def note2midi(arg):
return _essentia.note2midi( _c.convertData(arg, _c.Edt.STRING) )

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 hz2note(arg1, arg2=440.0):
return _essentia.hz2note( _c.convertData(arg1, _c.Edt.REAL),
_c.convertData(arg2, _c.Edt.REAL) )

def note2hz(arg1, arg2=440.0):
return _essentia.note2hz( _c.convertData(arg1, _c.Edt.STRING),
_c.convertData(arg2, _c.Edt.REAL) )

def velocity2db(arg1, arg2=-96):
return _essentia.velocity2db( _c.convertData(arg1, _c.Edt.INTEGER),
_c.convertData(arg2, _c.Edt.REAL) )

def db2velocity(arg1, arg2=-96):
return _essentia.db2velocity( _c.convertData(arg1, _c.Edt.REAL),
_c.convertData(arg2, _c.Edt.REAL) )

def equivalentKey(arg):
return _essentia.equivalentKey( _c.convertData(arg, _c.Edt.STRING) )

Expand All @@ -86,6 +130,12 @@ def derivative(array):
'amp2db', 'db2amp',
'bark2hz', 'hz2bark',
'mel2hz', 'hz2mel',
'midi2hz', 'hz2midi',
'cents2hz', 'hz2cents',
'note2root', 'note2octave',
'midi2note', 'note2midi',
'hz2note', 'note2hz',
'velocity2db', 'db2velocity',
'postProcessTicks',
'normalize', 'derivative',
'equivalentKey', 'lin2log']
173 changes: 173 additions & 0 deletions src/python/globalfuncs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,167 @@ hzToMel(PyObject* notUsed, PyObject* arg) {
return PyFloat_FromDouble( double(mel) );
}

static PyObject*
midiToHz(PyObject* notUsed, PyObject* args) {
// parse args to get Source alg, name and source alg and source name
vector<PyObject*> argsV = unpack(args);
if (argsV.size() != 2 ||
(!PyLong_Check(argsV[0]) || !PyFloat_Check(argsV[1]))) {
PyErr_SetString(PyExc_ValueError, "expecting arguments (int midiNoteNumber, Real tuningFrequency)");
return NULL;
}
Real hz = midi2hz( long( PyLong_AsLong(argsV[0]) ), Real( PyFloat_AS_DOUBLE(argsV[1])) );
return PyFloat_FromDouble( double(hz) );
}

static PyObject*
hzToMidi(PyObject* notUsed, PyObject* args) {
// parse args to get Source alg, name and source alg and source name
vector<PyObject*> argsV = unpack(args);
if (argsV.size() != 2 ||
(!PyFloat_Check(argsV[0]) || !PyFloat_Check(argsV[1]))) {
PyErr_SetString(PyExc_ValueError, "expecting arguments (Real hertz, Real tuningFrequency)");
return NULL;
}

int midi = hz2midi( Real( PyFloat_AS_DOUBLE(argsV[0]) ), Real( PyFloat_AS_DOUBLE(argsV[1])) );
return PyLong_FromLong( int(midi) );
}

static PyObject*
hzToCents(PyObject* notUsed, PyObject* args) {
// parse args to get Source alg, name and source alg and source name
vector<PyObject*> argsV = unpack(args);

if (argsV.size() != 2 || !PyFloat_Check(argsV[0]) || !PyFloat_Check(argsV[1])) {
PyErr_SetString(PyExc_TypeError, (char*)"expecting arguments (Real hertz, Real referenceFrequency)");
return NULL;
}

int cents = hz2cents( Real( PyFloat_AS_DOUBLE(argsV[0]) ), Real( PyFloat_AS_DOUBLE(argsV[1]) ) );
return PyFloat_FromDouble( int(cents) );
}

static PyObject*
centsToHz(PyObject* notUsed, PyObject* args) {
// parse args to get Source alg, name and source alg and source name
vector<PyObject*> argsV = unpack(args);

if (argsV.size() != 2 || !PyFloat_Check(argsV[0]) || !PyFloat_Check(argsV[1])) {
PyErr_SetString(PyExc_TypeError, (char*)"expecting arguments (Real cents, Real referenceFrequency)");
return NULL;
}

Real hz = cents2hz( Real( PyFloat_AS_DOUBLE(argsV[0]) ), Real( PyFloat_AS_DOUBLE(argsV[1]) ) );
return PyFloat_FromDouble( hz );
}

static PyObject*
midiToNote(PyObject* notUsed, PyObject* arg) {

if (!PyLong_Check(arg)) {
PyErr_SetString(PyExc_TypeError, (char*)"expecting arguments (int midiNoteNumber)");
return NULL;
}

std::string note = midi2note( long( PyLong_AsLong(arg) ) );
const char *c_note = note.c_str();
return PyString_FromString( c_note );
}

static PyObject*
noteToMidi(PyObject* notUsed, PyObject* arg) {

if (!PyString_Check(arg)) {
PyErr_SetString(PyExc_TypeError, (char*)"expecting arguments (string note)");
return NULL;
}

int octave = note2midi( PyString_AS_STRING(arg) );
return PyLong_FromLong( int(octave) );
}

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*
hzToNote(PyObject* notUsed, PyObject* args) {
// parse args to get Source alg, name and source alg and source name
vector<PyObject*> argsV = unpack(args);
if (argsV.size() != 2 ||
(!PyFloat_Check(argsV[0]) || !PyFloat_Check(argsV[1]))) {
PyErr_SetString(PyExc_ValueError, "expecting arguments (Real hertz, Real tuningFrequency)");
return NULL;
}

std::string note = hz2note( Real( PyFloat_AS_DOUBLE(argsV[0]) ), Real( PyFloat_AS_DOUBLE(argsV[1]) ) );
const char *c_note = note.c_str();
return PyString_FromString( c_note );
}

static PyObject*
noteToHz(PyObject* notUsed, PyObject* args) {
// parse args to get Source alg, name and source alg and source name
vector<PyObject*> argsV = unpack(args);
if (argsV.size() != 2 ||
(!PyString_Check(argsV[0]) || !PyFloat_Check(argsV[1]))) {
PyErr_SetString(PyExc_ValueError, "expecting arguments (string note, Real tuningFrequency)");
return NULL;
}

Real hz = note2hz( PyString_AS_STRING(argsV[0]), Real( PyFloat_AS_DOUBLE(argsV[1]) ) );
return PyFloat_FromDouble( hz );
}

static PyObject*
velocityToDb(PyObject* notUsed, PyObject* args) {
// parse args to get Source alg, name and source alg and source name
vector<PyObject*> argsV = unpack(args);
if (argsV.size() != 2 ||
(!PyLong_Check(argsV[0]) || !PyFloat_Check(argsV[1]))) {
PyErr_SetString(PyExc_ValueError, "expecting arguments (int velocity, Real hearingThreshold)");
return NULL;
}

Real db = velocity2db( long( PyLong_AsLong(argsV[0]) ), Real( PyFloat_AS_DOUBLE(argsV[1]) ) );
return PyFloat_FromDouble( db );
}

static PyObject*
dbToVelocity(PyObject* notUsed, PyObject* args) {
// parse args to get Source alg, name and source alg and source name
vector<PyObject*> argsV = unpack(args);
if (argsV.size() != 2 ||
(!PyFloat_Check(argsV[0]) || !PyFloat_Check(argsV[1]))) {
PyErr_SetString(PyExc_ValueError, "expecting arguments (Real decibels, Real hearingThreshold)");
return NULL;
}

long velocity = db2velocity( Real( PyFloat_AS_DOUBLE(argsV[0])), Real( PyFloat_AS_DOUBLE(argsV[1])) );
return PyLong_FromLong( int(velocity) );
}

static PyObject*
getEquivalentKey(PyObject* notUsed, PyObject* arg) {
Expand Down Expand Up @@ -1001,6 +1162,18 @@ static PyMethodDef Essentia__Methods[] = {
{ "hz2bark", hzToBark, METH_O, "Converts a frequency in Hz to a bark band" },
{ "mel2hz", melToHz, METH_O, "Converts a mel band to frequency in Hz" },
{ "hz2mel", hzToMel, METH_O, "Converts a frequency in Hz to a mel band" },
{ "midi2hz", midiToHz, METH_VARARGS, "Converts a midi note number to frequency in Hz" },
{ "hz2midi", hzToMidi, METH_VARARGS, "Converts a frequency in Hz to a midi note number" },
{ "hz2cents", hzToCents, METH_VARARGS, "Returns the cents distance between a frequency and a reference frequency in Hz" },
{ "cents2hz", centsToHz, METH_VARARGS, "Returns the frequency from a cents distance [0-1200] and a reference frequency in Hz" },
{ "midi2note", midiToNote, METH_O, "Converts a midi note number to note applying the international pitch standard (A4=440Hz)" },
{ "note2midi", noteToMidi, METH_O, "Converts note (applying the international pitch standard A4=440Hz) to midi note number" },
{ "note2root", noteToRoot, METH_O, "Returns the root of a note" },
{ "note2octave", noteToOctave, METH_O, "Returns the octave of a note" },
{ "hz2note", hzToNote, METH_VARARGS, "Converts a frequency in Hz to a note - applying the international pitch standard A4=440Hz" },
{ "note2hz", noteToHz, METH_VARARGS, "Converts a note - applying the international pitch standard A4=440Hz - into a frequency in Hz" },
{ "velocity2db", velocityToDb, METH_VARARGS, "Converts a velocity to a measure in dB" },
{ "db2velocity", dbToVelocity, METH_VARARGS, "Converts a dB measure of power to velocity [0-127]" },
{ "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" },
Expand Down
Loading
Loading