Skip to content

Commit

Permalink
Merge pull request #1417 from xaviliz/add-essentiamath
Browse files Browse the repository at this point in the history
Add essentiamath
  • Loading branch information
dbogdanov committed May 29, 2024
2 parents 8f324ea + 1e6b692 commit a94e021
Show file tree
Hide file tree
Showing 5 changed files with 408 additions and 26 deletions.
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

0 comments on commit a94e021

Please sign in to comment.