Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
…sm bindings for several Signals, with example.

This commit temporarily breaks the midiToFreq example and running
the wasm unit tests in Node.js
  • Loading branch information
colinbdclark committed Feb 16, 2022
1 parent 58832af commit f4a1482
Show file tree
Hide file tree
Showing 13 changed files with 539 additions and 90 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 = {
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
85 changes: 85 additions & 0 deletions hosts/web/examples/oscillator/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Oscillator - libstar wasm example</title>
<link href="web-bluemchen.css" rel="stylesheet">
<script src="https://g200kg.github.io/webaudio-controls/webaudio-controls.js"></script>
</head>

<body>
<div id="bluemchen">
<!-- encoder -->
<webaudio-knob id="enc" colors="#000;#000;#fff"></webaudio-knob>

<canvas id="oled"></canvas>

<!-- knobs -->
<webaudio-knob id="blueKnob" colors="#fff;#00f;#fff"></webaudio-knob>
<webaudio-knob id="redKnob" colors="#fff;#f00;#fff"></webaudio-knob>

<!-- jacks midi out, cv, in, out-->
<div class="jack"></div>
<div>midi</div>
<span class="jack"></span>
<span class="jack"></span>
<div>cv</div>
<span class="jack"></span>
<span class="jack"></span>
<div>in</div>
<span class="jack"></span>
<span class="jack"></span>
<div>out</div>
<div>&nbsp; </div>
<div>bluem</div>
<div>chen</div>
</div>
<script>
async function initAudioGraph() {
let context = new AudioContext();

let stream = await navigator.mediaDevices.getUserMedia({
audio: {
channelCount: 2,
echoCancellation: false,
noiseSuppression: false,
autoGainControl: false
},
video: false
});

let source = context.createMediaStreamSource(stream);

await context.audioWorklet.addModule(
"signaletic-oscillator-worklet.js");

const signaleticOsc = new AudioWorkletNode(context,
"SignaleticOscillator");

source.connect(signaleticOsc);
signaleticOsc.connect(context.destination);

function bindKnobChange(knobEl, param) {
knobEl.addEventListener("input", (event) => {
if (context.state === "suspended") {
context.resume();
}

param.value = event.target.value / 100;
});
}

bindKnobChange(document.getElementById("blueKnob"),
signaleticOsc.parameters.get("blueKnobParam"));
bindKnobChange(document.getElementById("redKnob"),
signaleticOsc.parameters.get("redKnobParam"));
}

initAudioGraph();

// set resolution to same as device
var oled = document.getElementById("oled");
oled.width = 64;
oled.height = 32;
</script>
</body>
</html>
132 changes: 132 additions & 0 deletions hosts/web/examples/oscillator/signaletic-oscillator-worklet.js
Original file line number Diff line number Diff line change
@@ -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);
30 changes: 30 additions & 0 deletions hosts/web/examples/oscillator/web-bluemchen.css
Original file line number Diff line number Diff line change
@@ -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;
}
16 changes: 8 additions & 8 deletions libstar/examples/console/src/print-sine.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Loading

0 comments on commit f4a1482

Please sign in to comment.