MTG / essentia.js

JavaScript library for music/audio analysis and processing powered by Essentia WebAssembly
https://essentia.upf.edu/essentiajs
GNU Affero General Public License v3.0
645 stars 42 forks source link

Initial version of generic JS bindings for all essentia algorithms #11

Closed albincorreya closed 4 years ago

albincorreya commented 4 years ago

This PR addresses the following changes,

You can find pre-compiled builds here.

Currently, the python code_generator.py script creates cpp source code and bindings for all the algos listed in included_algos.md. This list of included algorithms can be customised using the configure_bindings.py script.

cd src/python
# configure default list of algorithms for creating the js bindings
python configure_bindings.py 
# OR
# specify a list of algorithms for which you need to create the js bindings
python configure_bindings.py -i "['Key', 'HPCP']"
# OR
# you can also specify the algorithm list by a txt file
python configure_bindings.py -i your_included_algos_list.txt
# for more cli options
python configure_bindings.py -h

On JavaScript end, the usage would be like,

let essentia = new Module.EssentiaJS(false);

// outputs the version of essentia library on which the bindings are built on
essentia.version

// outputs the names of all available algorithms inside essentia.js
essentia.algorithmNames

// for algorithms with single output
let yourOutputVar = essentia.'<your-essentia-algo>'(<inputs> ..., <parameters> ...);

// for algorithms with multiple outputs
essentia.'<your-essentia-algo>'(<inputs> ..., <outputs> ..., <parameters> ...);

For example in real use-cases, it will look like below,


// Computing ReplayGain from an input audio signal
// The algorithm return float type
// check https://essentia.upf.edu/reference/std_ReplayGain.html
let replaygain = essentia.ReplayGain(inputSignalVector, // input
                                     44100); // sampleRate (parameter)

// Running PitchYinProbabilistic algorithm on an input audio signal
// create empty std::vector<float> vector for populating the output of 
// essentia.PitchYinProbabilistic algorithm
var pitches = new Module.VectorFloat();
var voicedProbabilities = new Module.VectorFloat();

// check https://essentia.upf.edu/reference/std_PitchYinProbabilistic.html
essentia.PitchYinProbabilistic(inputSignalVector, // input_1
                               pitches, // output_1
                               voicedProbabilities, // output_2
                               // parameters
                               4096, // frameSize
                               256, // hopSize
                               0.1, // lowRMSThreshold
                               'zero', // outputUnvoiced,
                               false, // preciseTime
                               44100); //sampleRate

As we can see in the above example, the user needs to specify all the required value for all the parameters. This can be irritating since a lot of essentia Algorithms has more than 10 parameters such as PredominantPitchMelodia, MultiPitchKlapurietc. The current way of binding the class EssentiaJS using embind is not binding the default parameter values specified in the class.

In order to add bindings for default parameter values in JS front of essentia, we need to do overload every method inside the class EssentiaJS as detailed in embind documentation. In order to deliver MVP build of essentia.js 0.1.0, this feature is currently ignored and will be addressed in the next version builds.

TODO: Currently there are no detailed unit tests for the bindings besides some base tests. Ideally, this should be automated. We have to check on all of the possibilities for that.

alastair commented 4 years ago

This looks OK to me, I don't really know much about the emscripten stuff so I'm happy to follow your lead on this.

One important thing is that we shouldn't have any assets in git (.js file, .wasm files, etc). This is because each time you update them we'll end up with more items in the git history, causing it to grow larger and larger. We should use github's release file upload functionality, or else get it up on npm instead.

albincorreya commented 4 years ago

Thanks for the review @alastair.

While we will be uploading essentia-module.js to npm, we still may need to provide the asynchronous wasm builds of essentia - essentia.js (js glue code) and essentia.wasm.

The main difference is that these async builds ables users to use it with standard HTML <script> tag and also it doesn't support AudioWorklets and ES6 style import/export. In that case, we can also provide essentia.js using CDN services like https://www.jsdelivr.com/features beside the GitHub releases and npm. Or do you think we should have both of these versions in npm (ie, essentia.js and essentia-module.js) and provide both of these in the CDN service?

What are your thoughts about this?