aolsenjazz/libsamplerate-js

View on GitHub
src/libsamplerate-wrapper.cpp

Summary

Maintainability
Test Coverage
#include <emscripten/bind.h>

#include "libsamplerate-headers.h"

using namespace emscripten;

/**
 * The length (in `float`s) of the input and output buffers used to transmit data between
 * JS and WASM. Each buffer is currently set to ~4MB.
 */
const int BUFFER_LEN = 1008000;

// buffers to communicate w/js
// TODO: Why on earth are we using hard-coded values here
float source[1008000];
float target[1008000];

SRC_STATE *src = nullptr;
SRC_DATA srcData;

/**
 * Called when the WASM module is loaded. Initializes part of SRC_DATA object
 *
 * @return 0 to be compliant
 */
int main() {
    srcData.end_of_input = 0;
    srcData.data_in = source;
    srcData.data_out = target;

    return 0;
}

/**
 * Initializes the SRC_STATE
 * 
 * @param nChannels The number of channels
 * @param quality   Converter algorithm. more (better) info: http://www.mega-nerd.com/SRC/api_misc.html#ErrorReporting
 * @param inSr      Input sample rate
 * @param outSr     Output sample rate
 * 
 * @return          Error code or zero
 */
int init(int nChannels, int quality, int inSr, int outSr) {
    srcData.src_ratio = outSr / (double) inSr;

    int* error = new int;
    src = src_new(quality, nChannels, error);
    
    if (*error != 0) {
        int err = *error;
        delete error;
        return err;
    }

    delete error;
    return 0;
}

/**
 * Returns the input array which is used to send data from JS to WASM
 * 
 * @return The number of samples from the array to return. Probably all of them
 */
emscripten::val sourceArray(int length) {
    return emscripten::val(emscripten::typed_memory_view(length, source));
}

/**
 * Returns the output array which is used to get data from WASM in JS
 * 
 * @return The number of samples from the array to return. Probably all of them
 */
emscripten::val targetArray(int length) {
    return emscripten::val(emscripten::typed_memory_view(length, target));
}

/**
 * Calls the libsamplerate `simple` API
 * 
 * @param length    The number of input frames to resampled
 * @param nChannels The number of channels represented
 * @param quality   Converter algorithm. more (better) info: http://www.mega-nerd.com/SRC/api_misc.html#ErrorReporting
 * @param inSr      Input sample rate
 * @param outSr     Output sample rate
 *
 * @return          Output frames generated (per channel) or error code
 */
int simple(int length, int nChannels, int quality, int inSr, int outSr) {
    srcData.src_ratio = outSr / (double) inSr;
    srcData.input_frames = length / nChannels;
    srcData.output_frames = BUFFER_LEN / nChannels;

    int error = src_simple(&srcData, quality, nChannels);
    std::string strError = src_strerror(error);

    if (error != 0) {
        return error;
    }

    return srcData.output_frames_gen;
}

/**
 * Calls the libsamplerate `full` API. nChannels, quality, inSr, and outSr all ignored. They're kept as
 * arguments because it simplifies the JS api.
 * 
 * @param length    The number of input frames to resampled
 * @param nChannels The number of channels represented
 * @param quality   Converter algorithm. more (better) info: http://www.mega-nerd.com/SRC/api_misc.html#ErrorReporting
 * @param inSr      Input sample rate
 * @param outSr     Output sample rate
 *
 * @return          Output frames generated (per channel) or error code
 */
int full(int length, int nChannels, int quality, int inSr, int outSr) {
    srcData.input_frames = length / nChannels;
    srcData.output_frames = BUFFER_LEN / nChannels;

    int processError = src_process(src, &srcData);
    if (processError != 0) {
        return processError;
    }
    
    return srcData.output_frames_gen;
}

/** Calls src_delete. Use to clean up resources once resampling is complete */
void destroy() {
    src_delete(src);
}

EMSCRIPTEN_BINDINGS(src) {
    function("init", &init);
    function("sourceArray", &sourceArray);
    function("targetArray", &targetArray);
    function("simple", &simple);
    function("full", &full);
    function("destroy", &destroy);
}