Source: CsoundNode.js

/*
 * C S O U N D
 *
 * L I C E N S E
 *
 * This software is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 */

// Setup a single global AudioContext object if not already defined
var CSOUND_AUDIO_CONTEXT = CSOUND_AUDIO_CONTEXT || 
    (function() {

        try {
            var AudioContext = window.AudioContext || window.webkitAudioContext;
            return new AudioContext();      
        }
        catch(error) {

            console.log('Web Audio API is not supported in this browser');
        }
        return null;
    }());


/** This ES6 Class defines a Custom Node as an AudioWorkletNode 
 *  that holds a Csound engine.
 */
class CsoundNode extends AudioWorkletNode {

    /** 
     *
     * @constructor
     * @param {AudioContext} context AudioContext in which this node will run
     * @param {object} options Configuration options, holding numberOfInputs,
     *   numberOfOutputs
     * @returns {object} A new CsoundNode
     */
    constructor(context, options) {
        options = options || {};
        options.numberOfInputs  = 1;
        options.numberOfOutputs = 2;
        options.channelCount = 2;
        options.sampleRate = context.sampleRate;  

        super(context, 'Csound', options);

        this.msgCallback = (msg) => { console.log(msg); }

        this.port.start();
        this.port.onmessage = (event) => {
            let data = event.data;
            switch(data[0]) {
            case "log":
                this.msgCallback(data[1]);
                break;
            default:
                console.log('[CsoundNode] Invalid Message: "' + event.data);
            }
        };
    }
    
    /** Writes data to a file in the WASM filesystem for
     *  use with csound.
     *
     * @param {string} filePath A string containing the path to write to.
     * @param {blob}   blobData The data to write to file.
     */  
    writeToFS(filePath, blobData) {
        this.port.postMessage(["writeToFS", filePath, blobData]);
    }

    /** Compiles a CSD, which may be given as a filename in the
     *  WASM filesystem or a string containing the code
     *
     * @param {string} csd A string containing the CSD filename or the CSD code.
     */
    compileCSD(filePath) {
        this.port.postMessage(["compileCSD", filePath]);
    }

    /** Compiles Csound orchestra code.
     *
     * @param {string} orcString A string containing the orchestra code.
     */    
    compileOrc(orcString) {
        this.port.postMessage(["compileOrc", orcString]);
    }

    /** Sets a Csound engine option (flag)
     *  
     *
     * @param {string} option The Csound engine option to set. This should
     * not contain any whitespace.
     */
    setOption(option) {
        this.port.postMessage(["setOption", option]);    
    }

    render(filePath) {
    }

    /** Evaluates Csound orchestra code.
     *
     * @param {string} codeString A string containing the orchestra code.
     */   
    evaluateCode(codeString) {
        this.port.postMessage(["evalCode", codeString]);
    }

    /** Reads a numeric score string.
     *
     * @param {string} scoreString A string containing a numeric score.
     */    
    readScore(scoreString) {
        this.port.postMessage(["readScore", scoreString]);
    }

    /** Sets the value of a control channel in the software bus
     *
     * @param {string} channelName A string containing the channel name.
     * @param {number} value The value to be set.
     */ 
    setControlChannel(channelName, value) {
        this.port.postMessage(["setControlChannel",
                               channelName, value]);
    }

    /** Sets the value of a string channel in the software bus
     *
     * @param {string} channelName A string containing the channel name.
     * @param {string} stringValue The string to be set.
     */ 
    setStringChannel(channelName, value) {
        this.port.postMessage(["setStringChannel",
                               channelName, value]);
    }

    /** Starts processing in this node
     */
    start() {
        this.port.postMessage(["start"]);
    }

    /** Resets the Csound engine.
     */
    reset() {
        this.port.postMessage(["reset"]);
    }

    destroy() {
    }

    /** Starts performance, same as start()
     */
    play() {
        this.port.postMessage(["play"]);
    }
    
    /** Stops (pauses) performance
     */
    stop() {
        this.port.postMessage(["stop"]);
    }

    /** Sets a callback to process Csound console messages.
     *
     * @param {function} msgCallback A callback to process messages 
     * with signature function(message), where message is a string
     * from Csound.
     */ 
    setMessageCallback(msgCallback) {
        this.msgCallback = msgCallback;
    }

    /** Sends a MIDI channel message to Csound
     *
     * @param {number} byte1 MIDI status byte
     * @param {number} byte2 MIDI data byte 1
     * @param {number} byte1 MIDI data byte 2
     *
     */
    midiMessage(byte1, byte2, byte3) {
        this.port.postMessage(["midiMessage", byte1, byte2, byte3]);
    }

}

/** This E6 class is used to setup scripts and
    allow the creation of new CsoundNode objects
   @hideconstructor
*/
class CsoundNodeFactory {

    /** 
     * This static method is used to asynchronously setup scripts for AudioWorklet Csound
     *
     * @param {string} script_base A string containing the base path to scripts
     */
    static importScripts(script_base='./') {
        let actx = CSOUND_AUDIO_CONTEXT;
        return new Promise( (resolve) => {
            actx.audioWorklet.addModule(script_base + 'libcsound-worklet.wasm.js').then(() => {
                actx.audioWorklet.addModule(script_base + 'libcsound-worklet.js').then(() => {
                    actx.audioWorklet.addModule(script_base + 'CsoundProcessor.js').then(() => {
                        resolve(); 
                    }) }) })      
        }) 
    }

    /** 
     * This static method creates a new CsoundNode. 
     *  @param {number} InputChannelCount number of input channels
     *  @param {number} OutputChannelCount number of output channels
     *  @returns {object}
     */
    static createNode(inputChannelCount=1, outputChannelCount=2) {
        var options = {};
        options.numberOfInputs  = inputChannelCount;
        options.numberOfOutputs  = outputChannelCount;
        return new CsoundNode(CSOUND_AUDIO_CONTEXT, options);
    }
}