diff --git a/hosts/daisy/examples/bluemchen/looper/src/starlings-bluemchen-looper.cpp b/hosts/daisy/examples/bluemchen/looper/src/starlings-bluemchen-looper.cpp index f0311e6..d6348ee 100644 --- a/hosts/daisy/examples/bluemchen/looper/src/starlings-bluemchen-looper.cpp +++ b/hosts/daisy/examples/bluemchen/looper/src/starlings-bluemchen-looper.cpp @@ -173,8 +173,8 @@ int main(void) { // TODO: Introduce a constant Signal type // https://github.com/continuing-creativity/signaletic/issues/23 - float* smoothCoefficient = star_AudioBlock_newWithValue(0.01f, - &allocator, &audioSettings); + float* smoothCoefficient = star_AudioBlock_newWithValue( + &allocator, &audioSettings, 0.01f); start = star_sig_Value_new(&allocator, &audioSettings); start->parameters.value = 0.0f; @@ -201,8 +201,8 @@ int main(void) { struct star_sig_Accumulate_Inputs speedControlInputs = { .source = speedIncrement->signal.output, - .reset = star_AudioBlock_newWithValue(0.0f, &allocator, - &audioSettings) + .reset = star_AudioBlock_newWithValue(&allocator, + &audioSettings, 0.0f) }; struct star_sig_Accumulate_Parameters speedControlParams = { @@ -267,12 +267,12 @@ int main(void) { .source = encoderButton->signal.output, // TODO: Replace with constant value signal (gh-23). - .duration = star_AudioBlock_newWithValue(0.5f, &allocator, - &audioSettings), + .duration = star_AudioBlock_newWithValue(&allocator, + &audioSettings, 0.5f), // TODO: Replace with constant value signal (gh-23). - .count = star_AudioBlock_newWithValue(1.0f, &allocator, - &audioSettings) + .count = star_AudioBlock_newWithValue(&allocator, + &audioSettings, 1.0f) }; encoderTap = star_sig_TimedTriggerCounter_new(&allocator, &audioSettings, &encoderClickInputs); @@ -288,12 +288,12 @@ int main(void) { .gate = encoderButton->signal.output, // TODO: Replace with constant value signal (gh-23). - .duration = star_AudioBlock_newWithValue(LONG_ENCODER_PRESS, - &allocator, &audioSettings), + .duration = star_AudioBlock_newWithValue( + &allocator, &audioSettings, LONG_ENCODER_PRESS), // TODO: Replace with constant value signal (gh-23). - .loop = star_AudioBlock_newWithValue(0.0f, &allocator, - &audioSettings) + .loop = star_AudioBlock_newWithValue(&allocator, + &audioSettings, 0.0f) }; encoderLongPress = star_sig_GatedTimer_new(&allocator, &audioSettings, &encoderPressTimerInputs); @@ -304,8 +304,8 @@ int main(void) { // for reading audio input (gh-22). // For now, just use an empty block that // is copied into manually in the audio callback. - .source = star_AudioBlock_newWithValue(0.0f, - &allocator, &audioSettings), + .source = star_AudioBlock_newWithValue(&allocator, + &audioSettings, 0.0f), .start = startSmoother->signal.output, .end = endSmoother->signal.output, .speed = leftSpeedAdder->signal.output, @@ -321,8 +321,8 @@ int main(void) { leftLooper->buffer = &leftBuffer; struct star_sig_Looper_Inputs rightLooperInputs = { - .source = star_AudioBlock_newWithValue(0.0f, - &allocator, &audioSettings), + .source = star_AudioBlock_newWithValue(&allocator, + &audioSettings, 0.0f), .start = leftLooperInputs.start, .end = leftLooperInputs.end, .speed = rightSpeedAdder->signal.output, @@ -338,8 +338,8 @@ int main(void) { // Bluemchen's output circuit clips as it approaches full gain, // so 0.85 seems to be around the practical maximum value. // TODO: Replace with constant value Signal (gh-23). - .gain = star_AudioBlock_newWithValue(0.85f, &allocator, - &audioSettings), + .gain = star_AudioBlock_newWithValue(&allocator, + &audioSettings, 0.85f), .source = leftLooper->signal.output }; leftGain = star_sig_Gain_new(&allocator, &audioSettings, diff --git a/hosts/daisy/examples/bluemchen/oscillator/src/starlings-bluemchen-oscillator.cpp b/hosts/daisy/examples/bluemchen/oscillator/src/starlings-bluemchen-oscillator.cpp index 5347f7a..a807353 100644 --- a/hosts/daisy/examples/bluemchen/oscillator/src/starlings-bluemchen-oscillator.cpp +++ b/hosts/daisy/examples/bluemchen/oscillator/src/starlings-bluemchen-oscillator.cpp @@ -111,11 +111,11 @@ int main(void) { /** Carrier **/ struct star_sig_Sine_Inputs carrierInputs = { .freq = freqMod->signal.output, - .phaseOffset = star_AudioBlock_newWithValue(0.0f, - &allocator, &audioSettings), + .phaseOffset = star_AudioBlock_newWithValue(&allocator, + &audioSettings, 0.0f), .mul = ampMod->signal.output, - .add = star_AudioBlock_newWithValue(0.0f, - &allocator, &audioSettings), + .add = star_AudioBlock_newWithValue(&allocator, + &audioSettings, 0.0f), }; carrier = star_sig_Sine_new(&allocator, &audioSettings, diff --git a/hosts/web/examples/oscillator/index.html b/hosts/web/examples/oscillator/index.html new file mode 100644 index 0000000..04aa55d --- /dev/null +++ b/hosts/web/examples/oscillator/index.html @@ -0,0 +1,85 @@ + + + + Oscillator - libstar wasm example + + + + + +
+ + + + + + + + + + +
+
midi
+ + +
cv
+ + +
in
+ + +
out
+
 
+
bluem
+
chen
+
+ + + diff --git a/hosts/web/examples/oscillator/signaletic-oscillator-worklet.js b/hosts/web/examples/oscillator/signaletic-oscillator-worklet.js new file mode 100644 index 0000000..9c5f586 --- /dev/null +++ b/hosts/web/examples/oscillator/signaletic-oscillator-worklet.js @@ -0,0 +1,132 @@ +import ModuleLoader from "../../../../libstar/build/wasm/libstar.js"; + +let Module = ModuleLoader(); +let star = new Module.Starlings(); + +star.TYPED_VIEWS = { + "int8": "Int8Array", + "uint8": "Uint8Array", + "int16": "Int16Array", + "uint16": "Uint16Array", + "int32": "Int16Array", + "uint32": "Uint16Array", + "float32": "Float32Array" +}; + +star.dereferenceArray = function(ptr, length, type) { + let arrayViewType = star.TYPED_VIEWS[type]; + if (arrayViewType === undefined) { + throw Error("Can't dereference an array of type " + type); + } + + return new globalThis[arrayViewType](Module.HEAP8.buffer, + ptr, length); +}; + +class SignaleticOscillator extends AudioWorkletProcessor { + constructor() { + super(); + + this.allocator = star.Allocator_new(1024 * 256); + this.audioSettings = star.AudioSettings_new(this.allocator); + this.audioSettings.sampleRate = sampleRate; + this.audioSettings.blockSize = 128; + this.audioSettings.numChannels = 2; + + /** Modulators **/ + this.freqMod = star.sig.Value_new(this.allocator, + this.audioSettings); + this.freqMod.parameters.value = 440.0; + this.ampMod = star.sig.Value_new(this.allocator, + this.audioSettings); + this.ampMod.parameters.value = 1.0; + + + /** Carrier **/ + this.carrierInputs = star.sig.Sine_Inputs_new( + this.allocator, + this.freqMod.signal.output, + star.AudioBlock_newWithValue(this.allocator, + this.audioSettings, 0.0), + this.ampMod.signal.output, + star.AudioBlock_newWithValue(this.allocator, + this.audioSettings, 0.0) + ); + + this.carrier = star.sig.Sine_new(this.allocator, + this.audioSettings, this.carrierInputs); + + /** Gain **/ + this.gainValue = star.sig.Value_new(this.allocator, + this.audioSettings); + this.gainValue.parameters.value = 0.85; + + this.gainInputs = star.sig.Gain_Inputs_new( + this.allocator, + this.gainValue.signal.output, + this.carrier.signal.output + ); + + this.gain = star.sig.Gain_new(this.allocator, + this.audioSettings, this.gainInputs); + + this.gainOutput = star.dereferenceArray( + this.gain.signal.output, + this.audioSettings.blockSize, + "float32"); + } + + static get parameterDescriptors() { + return [ + { + name: 'blueKnobParam', + defaultValue: 0.0, + minValue: 0.0, + maxValue: 1.0, + automation: 'k-rate' + }, + { + name: 'redKnobParam', + defaultValue: 0.0, + minValue: 0.0, + maxValue: 1.0, + automation: 'k-rate' + } + ] + } + + process (inputs, outputs, parameters) { + // TODO: Signaletic needs value mapping functions + // like libDaisy, or control values should always + // mapped using Signals. + this.gainValue.parameters.value = + parameters.blueKnobParam[0]; + + // TODO: Read inputs at audio rate. + this.ampMod.parameters.value = inputs[0][0][0]; + + // Map to MIDI notes between 0..120 + let freqNote = inputs[0][1][0] * 60.0 + 60.0; + this.freqMod.parameters.value = star.midiToFreq(freqNote); + + for (let output of outputs) { + // Evaluate the Signaletic graph. + this.ampMod.signal.generate(this.ampMod); + this.freqMod.signal.generate(this.freqMod); + this.carrier.signal.generate(this.carrier); + this.gainValue.signal.generate(this.gainValue); + this.gain.signal.generate(this.gain); + + for (let channel of output) { + for (let i = 0; i < channel.length; i++) { + // channel[i] = inputs[0][0][i]; + channel[i] = this.gainOutput[i]; + } + } + } + + return true; + } +} + +registerProcessor('SignaleticOscillator', SignaleticOscillator); diff --git a/hosts/web/examples/oscillator/web-bluemchen.css b/hosts/web/examples/oscillator/web-bluemchen.css new file mode 100644 index 0000000..c216849 --- /dev/null +++ b/hosts/web/examples/oscillator/web-bluemchen.css @@ -0,0 +1,30 @@ +#bluemchen { + width: 20.32mm; + height: 128.5mm; + background-color: black; + color: white; + position: absolute; + left: 50%; + top: 50%; + -webkit-transform: translate(-50%, -50%); + transform: translate(-50%, -50%); + text-align:center; + align-content: center; +} + +#oled { + width: 11.2mm; + height: 5.6mm; +} + +.jack { + height: 9mm; + width: 9mm; + background-color: silver; + border-radius: 50%; + display: inline-block; +} + +canvas { + background-color: slateblue; +} diff --git a/libstar/examples/console/src/print-sine.c b/libstar/examples/console/src/print-sine.c index 947bb0e..d9c9bc3 100644 --- a/libstar/examples/console/src/print-sine.c +++ b/libstar/examples/console/src/print-sine.c @@ -30,14 +30,14 @@ int main(int argc, char *argv[]) { star_Allocator_init(&allocator); struct star_sig_Sine_Inputs inputs = { - .freq = star_AudioBlock_newWithValue(440.0f, &allocator, - &settings), - .phaseOffset = star_AudioBlock_newWithValue(0.0f, &allocator, - &settings), - .mul = star_AudioBlock_newWithValue(1.0f, &allocator, - &settings), - .add = star_AudioBlock_newWithValue(0.0f, &allocator, - &settings) + .freq = star_AudioBlock_newWithValue(&allocator, + &settings, 440.0f), + .phaseOffset = star_AudioBlock_newWithValue(&allocator, + &settings, 0.0f), + .mul = star_AudioBlock_newWithValue(&allocator, + &settings, 1.0f), + .add = star_AudioBlock_newWithValue(&allocator, + &settings, 0.0f) }; struct star_sig_Sine* sine = star_sig_Sine_new(&allocator, diff --git a/libstar/include/libstar.h b/libstar/include/libstar.h index 8386237..efd6b53 100644 --- a/libstar/include/libstar.h +++ b/libstar/include/libstar.h @@ -239,9 +239,10 @@ void star_Buffer_destroy(struct star_Allocator* allocator, struct star_Buffer* s float_array_ptr star_AudioBlock_new(struct star_Allocator* allocator, struct star_AudioSettings* audioSettings); -float_array_ptr star_AudioBlock_newWithValue(float value, +float_array_ptr star_AudioBlock_newWithValue( struct star_Allocator* allocator, - struct star_AudioSettings* audioSettings); + struct star_AudioSettings* audioSettings, + float value); // TODO: Should the signal argument at least be defined // as a struct star_sig_Signal*, rather than void*? @@ -555,7 +556,8 @@ struct star_sig_Gain { void star_sig_Gain_init(struct star_sig_Gain* self, struct star_AudioSettings* settings, struct star_sig_Gain_Inputs* inputs, float_array_ptr output); -struct star_sig_Gain* star_sig_Gain_new(struct star_Allocator* allocator, +struct star_sig_Gain* star_sig_Gain_new( + struct star_Allocator* allocator, struct star_AudioSettings* settings, struct star_sig_Gain_Inputs* inputs); void star_sig_Gain_generate(void* signal); diff --git a/libstar/setup-emscripten-env.sh b/libstar/setup-emscripten-env.sh new file mode 100755 index 0000000..e973c8b --- /dev/null +++ b/libstar/setup-emscripten-env.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +export EM_BINARYEN_ROOT=/opt/homebrew/opt/binaryen +export EMSCRIPTEN_TOOLS_PATH=../../emscripten/tools diff --git a/libstar/src/libstar.c b/libstar/src/libstar.c index 6e04df4..ac1ed23 100644 --- a/libstar/src/libstar.c +++ b/libstar/src/libstar.c @@ -147,22 +147,26 @@ void star_AudioSettings_destroy(struct star_Allocator* allocator, } -float_array_ptr star_samples_new(struct star_Allocator* allocator, size_t length) { +float_array_ptr star_samples_new(struct star_Allocator* allocator, + size_t length) { return (float_array_ptr) star_Allocator_malloc(allocator, sizeof(float) * length); } // TODO: Does an AudioBlock type need to be introduced? // TODO: Do we need a destroy function too? -float_array_ptr star_AudioBlock_new(struct star_Allocator* allocator, - struct star_AudioSettings* settings) { - return star_samples_new(allocator, settings->blockSize); +float_array_ptr star_AudioBlock_new( + struct star_Allocator* allocator, + struct star_AudioSettings* audioSettings) { + return star_samples_new(allocator, audioSettings->blockSize); } -float_array_ptr star_AudioBlock_newWithValue(float value, +float_array_ptr star_AudioBlock_newWithValue( struct star_Allocator* allocator, - struct star_AudioSettings* audioSettings) { - float_array_ptr block = star_AudioBlock_new(allocator, audioSettings); + struct star_AudioSettings* audioSettings, + float value) { + float_array_ptr block = star_AudioBlock_new(allocator, + audioSettings); star_fillWithValue(block, audioSettings->blockSize, value); return block; diff --git a/libstar/tests/test-libstar.c b/libstar/tests/test-libstar.c index e91b083..8b1f4eb 100644 --- a/libstar/tests/test-libstar.c +++ b/libstar/tests/test-libstar.c @@ -16,26 +16,26 @@ struct star_AudioSettings* audioSettings; float* silentBlock; // TODO: Factor into a test utilities file. -void TEST_ASSERT_BUFFER_CONTAINS_FLOAT_WITHIN( - float expected, float* actual, size_t length) { +void TEST_ASSERT_FLOAT_ARRAY_CONTAINS_VALUE_ONLY( + float expectedValue, float* actual, size_t length) { float* expectedArray = (float*) star_Allocator_malloc(&allocator, length * sizeof(float)); - star_fillWithValue(expectedArray, length, expected); + star_fillWithValue(expectedArray, length, expectedValue); TEST_ASSERT_EQUAL_FLOAT_ARRAY(expectedArray, actual, length); star_Allocator_free(&allocator, expectedArray); } void TEST_ASSERT_BUFFER_CONTAINS_SILENCE( float* buffer, size_t bufferLen) { - TEST_ASSERT_BUFFER_CONTAINS_FLOAT_WITHIN( + TEST_ASSERT_FLOAT_ARRAY_CONTAINS_VALUE_ONLY( 0.0f, buffer, bufferLen); } void setUp(void) { star_Allocator_init(&allocator); audioSettings = star_AudioSettings_new(&allocator); - silentBlock = star_AudioBlock_newWithValue(0.0, - &allocator, audioSettings); + silentBlock = star_AudioBlock_newWithValue(&allocator, + audioSettings, 0.0f); } void tearDown(void) {} @@ -68,10 +68,15 @@ void test_star_midiToFreq(void) { } void test_star_fillWithValue(void) { - float buffer[64]; - star_fillWithValue(buffer, 64, 440.4f); - TEST_ASSERT_BUFFER_CONTAINS_FLOAT_WITHIN( - 440.4f, buffer, 64); + size_t bufferLen = 64; + float actual[bufferLen]; + float expected[bufferLen]; + for (size_t i = 0; i < bufferLen; i++) { + expected[i] = 440.4f; + }; + + star_fillWithValue(actual, 64, 440.4f); + TEST_ASSERT_EQUAL_FLOAT_ARRAY(expected, actual, 64); } void test_star_fillWithSilence(void) { @@ -80,6 +85,38 @@ void test_star_fillWithSilence(void) { TEST_ASSERT_BUFFER_CONTAINS_SILENCE(buffer, 16); } +void test_star_Audio_Block_newWithValue_testForValue( + struct star_Allocator* alloc, + struct star_AudioSettings* settings, + float value) { + float* actual = star_AudioBlock_newWithValue(alloc, + settings, value); + TEST_ASSERT_FLOAT_ARRAY_CONTAINS_VALUE_ONLY(value, actual, + settings->blockSize); + star_Allocator_free(alloc, actual); +} + +void test_star_AudioBlock_newWithValue(void) { + char customHeap[1048576]; + struct star_Allocator customAlloc = { + .heap = (void*) customHeap, + .heapSize = 1048576 + }; + star_Allocator_init(&customAlloc); + + struct star_AudioSettings* customSettings = + star_AudioSettings_new(&customAlloc); + + test_star_Audio_Block_newWithValue_testForValue(&customAlloc, + customSettings, 440.0); + test_star_Audio_Block_newWithValue_testForValue(&customAlloc, + customSettings, 0.0); + test_star_Audio_Block_newWithValue_testForValue(&customAlloc, + customSettings, 1.0); + test_star_Audio_Block_newWithValue_testForValue(&customAlloc, + customSettings, 0.0); +} + void test_star_sig_Value(void) { struct star_sig_Value* value = star_sig_Value_new(&allocator, audioSettings); @@ -87,13 +124,13 @@ void test_star_sig_Value(void) { // Output should contain the value parameter. value->signal.generate(value); - TEST_ASSERT_BUFFER_CONTAINS_FLOAT_WITHIN( + TEST_ASSERT_FLOAT_ARRAY_CONTAINS_VALUE_ONLY( 123.45f, value->signal.output, audioSettings->blockSize); // Output should contain the updated value parameter. value->parameters.value = 1.111f; value->signal.generate(value); - TEST_ASSERT_BUFFER_CONTAINS_FLOAT_WITHIN( + TEST_ASSERT_FLOAT_ARRAY_CONTAINS_VALUE_ONLY( 1.111f, value->signal.output, audioSettings->blockSize); // The lastSample member should have been updated. @@ -105,7 +142,7 @@ void test_star_sig_Value(void) { // the output should continue to contain the value parameter. value->signal.generate(value); value->signal.generate(value); - TEST_ASSERT_BUFFER_CONTAINS_FLOAT_WITHIN( + TEST_ASSERT_FLOAT_ARRAY_CONTAINS_VALUE_ONLY( 1.111f, value->signal.output, audioSettings->blockSize); star_sig_Value_destroy(&allocator, value); @@ -116,16 +153,16 @@ void test_star_sig_TimedTriggerCounter(void) { audioSettings->sampleRate; // A trigger right at the beginning of the input buffer. - float* source = star_AudioBlock_newWithValue(0.0f, - &allocator, audioSettings); + float* source = star_AudioBlock_newWithValue(&allocator, + audioSettings, 0.0f); source[0] = 1.0; struct star_sig_TimedTriggerCounter_Inputs inputs = { .source = source, - .duration = star_AudioBlock_newWithValue(halfBlockSecs, - &allocator, audioSettings), - .count = star_AudioBlock_newWithValue(1.0f, &allocator, - audioSettings) + .duration = star_AudioBlock_newWithValue(&allocator, + audioSettings, halfBlockSecs), + .count = star_AudioBlock_newWithValue(&allocator, + audioSettings, 1.0f) }; struct star_sig_TimedTriggerCounter* counter = star_sig_TimedTriggerCounter_new(&allocator, audioSettings, @@ -135,8 +172,8 @@ void test_star_sig_TimedTriggerCounter(void) { // (i.e. 24 samples after we received the // rising edge of the input trigger) counter->signal.generate(counter); - float* expected = star_AudioBlock_newWithValue(0.0f, - &allocator, audioSettings); + float* expected = star_AudioBlock_newWithValue(&allocator, + audioSettings, 0.0f); expected[23] = 1.0f; TEST_ASSERT_EQUAL_FLOAT_ARRAY(expected, counter->signal.output, audioSettings->blockSize); @@ -186,32 +223,32 @@ void test_star_sig_TimedTriggerCounter(void) { void test_star_sig_Gain(void) { struct star_sig_Gain_Inputs inputs = { - .gain = star_AudioBlock_newWithValue(0.5f, - &allocator, audioSettings), - .source = star_AudioBlock_newWithValue(440.0f, - &allocator, audioSettings) + .gain = star_AudioBlock_newWithValue(&allocator, + audioSettings, 0.5f), + .source = star_AudioBlock_newWithValue(&allocator, + audioSettings, 440.0f) }; struct star_sig_Gain* gain = star_sig_Gain_new(&allocator, audioSettings, &inputs); gain->signal.generate(gain); - TEST_ASSERT_BUFFER_CONTAINS_FLOAT_WITHIN( + TEST_ASSERT_FLOAT_ARRAY_CONTAINS_VALUE_ONLY( 220.0f, gain->signal.output, audioSettings->blockSize); star_fillWithValue(inputs.gain, audioSettings->blockSize, 0.0f); gain->signal.generate(gain); - TEST_ASSERT_BUFFER_CONTAINS_FLOAT_WITHIN( + TEST_ASSERT_FLOAT_ARRAY_CONTAINS_VALUE_ONLY( 0.0f, gain->signal.output, audioSettings->blockSize); star_fillWithValue(inputs.gain, audioSettings->blockSize, 2.0f); gain->signal.generate(gain); - TEST_ASSERT_BUFFER_CONTAINS_FLOAT_WITHIN( + TEST_ASSERT_FLOAT_ARRAY_CONTAINS_VALUE_ONLY( 880.0f, gain->signal.output, audioSettings->blockSize); star_fillWithValue(inputs.gain, audioSettings->blockSize, -1.0f); gain->signal.generate(gain); - TEST_ASSERT_BUFFER_CONTAINS_FLOAT_WITHIN( + TEST_ASSERT_FLOAT_ARRAY_CONTAINS_VALUE_ONLY( -440.0f, gain->signal.output, audioSettings->blockSize); star_sig_Gain_destroy(&allocator, gain); @@ -228,14 +265,14 @@ struct star_sig_Sine_Inputs* createSineInputs(struct star_Allocator* allocator, struct star_sig_Sine_Inputs* inputs = (struct star_sig_Sine_Inputs*) star_Allocator_malloc(allocator, sizeof(struct star_sig_Sine_Inputs)); - inputs->freq = star_AudioBlock_newWithValue(freq, - allocator, audioSettings); - inputs->phaseOffset = star_AudioBlock_newWithValue(phaseOffset, - allocator, audioSettings); - inputs->mul = star_AudioBlock_newWithValue(mul, - allocator, audioSettings);; - inputs->add = star_AudioBlock_newWithValue(add, - allocator, audioSettings); + inputs->freq = star_AudioBlock_newWithValue(allocator, + audioSettings, freq); + inputs->phaseOffset = star_AudioBlock_newWithValue(allocator, + audioSettings, phaseOffset); + inputs->mul = star_AudioBlock_newWithValue(allocator, + audioSettings, mul); + inputs->add = star_AudioBlock_newWithValue(allocator, + audioSettings, add); return inputs; } @@ -358,6 +395,7 @@ int main(void) { RUN_TEST(test_star_midiToFreq); RUN_TEST(test_star_fillWithValue); RUN_TEST(test_star_fillWithSilence); + RUN_TEST(test_star_AudioBlock_newWithValue); RUN_TEST(test_star_sig_Value); RUN_TEST(test_star_sig_TimedTriggerCounter); RUN_TEST(test_star_sig_Gain); diff --git a/libstar/wasm-cross-compile.txt b/libstar/wasm-cross-compile.txt index b0ecf5c..f7064d4 100644 --- a/libstar/wasm-cross-compile.txt +++ b/libstar/wasm-cross-compile.txt @@ -5,12 +5,11 @@ ar = 'emar' exe_wrapper = 'node' [properties] -needs_exe_wrapper = true +needs_exe_wrapper = false [built-in options] -c_args = [] -c_link_args = ['-s', 'LINKABLE=1'] -cpp_link_args = ['-s', 'LINKABLE=1'] +c_link_args = ['-s', 'LINKABLE=1', '-s', 'EXPORTED_RUNTIME_METHODS=ccall', '-s', 'MODULARIZE=1', '-s', 'EXPORT_ES6=1', '-s', 'SINGLE_FILE=1', '-s', 'ENVIRONMENT=shell', '-s', 'WASM_ASYNC_COMPILATION=0'] +cpp_link_args = ['-s', 'LINKABLE=1', '-s', 'EXPORTED_RUNTIME_METHODS=ccall', '-s', 'MODULARIZE=1', '-s', 'EXPORT_ES6=1', '-s', 'SINGLE_FILE=1', '-s', 'ENVIRONMENT=shell', '-s', 'WASM_ASYNC_COMPILATION=0'] [host_machine] system = 'emscripten' diff --git a/libstar/wasm/bindings/libstar-web-bindings.idl b/libstar/wasm/bindings/libstar-web-bindings.idl index fa573b5..c379938 100644 --- a/libstar/wasm/bindings/libstar-web-bindings.idl +++ b/libstar/wasm/bindings/libstar-web-bindings.idl @@ -1,9 +1,44 @@ +interface Signals { + star_sig_Value Value_new(star_Allocator allocator, + star_AudioSettings audioSettings); + + void Value_destroy(star_Allocator allocator, + star_sig_Value value); + + star_sig_Sine Sine_new(star_Allocator allocator, + star_AudioSettings audioSettings, + star_sig_Sine_Inputs inputs); + + void Sine_destroy(star_Allocator allocator, + star_sig_Sine sine); + + star_sig_Sine_Inputs Sine_Inputs_new(star_Allocator allocator, + any freq, any phaseOffset, any mul, any add); + + void Sine_Inputs_destroy(star_Allocator allocator, + star_sig_Sine_Inputs inputs); + + star_sig_Gain Gain_new(star_Allocator allocator, + star_AudioSettings audioSettings, + star_sig_Gain_Inputs inputs); + + void Gain_destroy(star_Allocator allocator, star_sig_Gain gain); + + star_sig_Gain_Inputs Gain_Inputs_new(star_Allocator allocator, + any gain, any source); + + void Gain_Inputs_destroy(star_Allocator allocator, + star_sig_Gain_Inputs inputs); +}; + interface Starlings { readonly attribute float PI; readonly attribute float TWOPI; void Starlings(); + [Value] attribute Signals sig; + float fminf(float a, float b); float fmaxf(float a, float b); float clamp(float value, float min, float max); @@ -33,9 +68,8 @@ interface Starlings { any AudioBlock_new(star_Allocator allocator, star_AudioSettings audioSettings); - any AudioBlock_newWithValue(float value, - star_Allocator allocator, - star_AudioSettings audioSettings); + any AudioBlock_newWithValue(star_Allocator allocator, + star_AudioSettings audioSettings, float value); }; interface star_Allocator { @@ -53,3 +87,42 @@ interface star_Buffer { attribute unsigned long length; attribute any samples; }; + +interface star_sig_Signal { + attribute star_AudioSettings audioSettings; + attribute any output; + void generate(any signal); +}; + +interface star_sig_Sine { + [Value] attribute star_sig_Signal signal; + attribute star_sig_Sine_Inputs inputs; + attribute float phaseAccumulator; +}; + +interface star_sig_Sine_Inputs { + attribute any freq; + attribute any phaseOffset; + attribute any mul; + attribute any add; +}; + +interface star_sig_Value { + [Value] attribute star_sig_Signal signal; + [Value] attribute star_sig_Value_Parameters parameters; + attribute float lastSample; +}; + +interface star_sig_Value_Parameters { + attribute float value; +}; + +interface star_sig_Gain { + [Value] attribute star_sig_Signal signal; + attribute star_sig_Gain_Inputs inputs; +}; + +interface star_sig_Gain_Inputs { + attribute any gain; + attribute any source; +}; diff --git a/libstar/wasm/bindings/src/libstar-web.cpp b/libstar/wasm/bindings/src/libstar-web.cpp index c04f926..f4c8051 100644 --- a/libstar/wasm/bindings/src/libstar-web.cpp +++ b/libstar/wasm/bindings/src/libstar-web.cpp @@ -12,10 +12,11 @@ */ struct star_Allocator* star_Allocator_new(size_t heapSize) { char* heap = (char *) malloc(heapSize); - struct star_Allocator* allocator = (struct star_Allocator*) malloc( - sizeof(struct star_Allocator)); + struct star_Allocator* allocator = (struct star_Allocator*) + malloc(sizeof(struct star_Allocator)); allocator->heapSize = heapSize; allocator->heap = (void *) heap; + star_Allocator_init(allocator); return allocator; } @@ -31,6 +32,84 @@ void star_Allocator_destroy(struct star_Allocator* allocator) { free(allocator); } +class Signals { +public: + struct star_sig_Value* Value_new( + struct star_Allocator* allocator, + struct star_AudioSettings* audioSettings) { + return star_sig_Value_new(allocator, audioSettings); + } + + void Value_destroy(struct star_Allocator* allocator, + struct star_sig_Value* self) { + return star_sig_Value_destroy(allocator, self); + } + + struct star_sig_Sine* Sine_new(struct star_Allocator* allocator, + struct star_AudioSettings* audioSettings, + struct star_sig_Sine_Inputs* inputs) { + return star_sig_Sine_new(allocator, audioSettings, + inputs); + } + + void Sine_destroy(struct star_Allocator* allocator, + struct star_sig_Sine* self) { + return star_sig_Sine_destroy(allocator, self); + } + + // TODO: Should some version of this go directly into + // libsignaletic, or is the only purpose of this function to + // provide a means for creating star_sig_Sine_Input objects + // from JavaScript? + struct star_sig_Sine_Inputs* Sine_Inputs_new( + struct star_Allocator* allocator, + float_array_ptr freq, float_array_ptr phaseOffset, + float_array_ptr mul, float_array_ptr add) { + struct star_sig_Sine_Inputs* inputs = (struct star_sig_Sine_Inputs*) star_Allocator_malloc(allocator, sizeof(star_sig_Sine_Inputs)); + + inputs->freq = freq; + inputs->phaseOffset = phaseOffset; + inputs->mul = mul; + inputs->add = add; + + return inputs; + } + + void Sine_Inputs_destroy(struct star_Allocator* allocator, + struct star_sig_Sine_Inputs* self) { + star_Allocator_free(allocator, self); + } + + struct star_sig_Gain* Gain_new(struct star_Allocator* allocator, + struct star_AudioSettings* audioSettings, + struct star_sig_Gain_Inputs* inputs) { + return star_sig_Gain_new(allocator, audioSettings, + inputs); + } + + void Gain_destroy(struct star_Allocator* allocator, + struct star_sig_Gain* self) { + return star_sig_Gain_destroy(allocator, self); + } + + // TODO: Address duplication with other Input_new functions. + struct star_sig_Gain_Inputs* Gain_Inputs_new( + struct star_Allocator* allocator, + float_array_ptr gain, float_array_ptr source) { + struct star_sig_Gain_Inputs* inputs = (struct star_sig_Gain_Inputs*) star_Allocator_malloc(allocator, sizeof(star_sig_Gain_Inputs)); + + inputs->gain = gain; + inputs->source = source; + + return inputs; + } + + void Gain_Inputs_destroy(struct star_Allocator* allocator, + struct star_sig_Gain_Inputs* self) { + star_Allocator_free(allocator, self); + } +}; + class Starlings { public: @@ -40,6 +119,8 @@ class Starlings { // TODO: How do we expose DEFAULT_AUDIO_SETTINGS // here as a pointer? + Signals sig; + Starlings() {} float fminf(float a, float b) { @@ -131,11 +212,12 @@ class Starlings { return star_AudioBlock_new(allocator, audioSettings); } - float_array_ptr AudioBlock_newWithValue(float value, + float_array_ptr AudioBlock_newWithValue( struct star_Allocator* allocator, - struct star_AudioSettings* audioSettings) { - return star_AudioBlock_newWithValue(value, allocator, - audioSettings); + struct star_AudioSettings* audioSettings, + float value) { + return star_AudioBlock_newWithValue(allocator, + audioSettings, value); } ~Starlings() {}