Add .configure() on web release
This commit is contained in:
parent
eaf484a9f7
commit
49b2f89ae6
@ -109,12 +109,12 @@
|
|||||||
<label>tonality limit</label>
|
<label>tonality limit</label>
|
||||||
<input type="range" min="2000" max="20000" step="100" data-key="tonalityHz" class="diagram-red">
|
<input type="range" min="2000" max="20000" step="100" data-key="tonalityHz" class="diagram-red">
|
||||||
<input type="number" min="2000" max="20000" step="1" data-key="tonalityHz" class="diagram-red">
|
<input type="number" min="2000" max="20000" step="1" data-key="tonalityHz" class="diagram-red">
|
||||||
<label>shelf freq</label>
|
<label>block (ms)</label>
|
||||||
<input type="range" min="4000" max="12000" step="100" data-key="shelfFreq">
|
<input type="range" min="50" max="180" step="1" data-key="blockMs" class="diagram-green">
|
||||||
<input type="number" min="4000" max="12000" step="100" data-key="shelfFreq">
|
<input type="number" min="50" max="180" step="1" data-key="blockMs" class="diagram-green">
|
||||||
<label>shelf dB</label>
|
<label>overlap</label>
|
||||||
<input type="range" min="-24" max="12" step="0.1" data-key="shelfDb">
|
<input type="range" min="2" max="8" step="0.1" data-key="overlap" class="diagram-green">
|
||||||
<input type="number" min="-24" max="12" step="0.1" data-key="shelfDb">
|
<input type="number" min="2" max="8" step="0.1" data-key="overlap" class="diagram-green">
|
||||||
</div>
|
</div>
|
||||||
<script type="module">
|
<script type="module">
|
||||||
import SignalsmithStretch from "../release/SignalsmithStretch.mjs";
|
import SignalsmithStretch from "../release/SignalsmithStretch.mjs";
|
||||||
@ -127,6 +127,21 @@
|
|||||||
let stretch;
|
let stretch;
|
||||||
let audioDuration = 1;
|
let audioDuration = 1;
|
||||||
|
|
||||||
|
let controlValuesInitial = {
|
||||||
|
active: false,
|
||||||
|
rate: 1,
|
||||||
|
semitones: 0,
|
||||||
|
loopStart: 0,
|
||||||
|
loopEnd: 0 // disabled (<= start), but this gets set when we load an audio file
|
||||||
|
};
|
||||||
|
let controlValues = Object.assign({}, controlValuesInitial);
|
||||||
|
let configValuesInitial = {
|
||||||
|
tonalityHz: 8000,
|
||||||
|
blockMs: 120,
|
||||||
|
overlap: 4
|
||||||
|
};
|
||||||
|
let configValues = Object.assign({}, configValuesInitial);
|
||||||
|
|
||||||
// add scope, for fun
|
// add scope, for fun
|
||||||
let scope = await Scope(audioContext);
|
let scope = await Scope(audioContext);
|
||||||
scope.connect(audioContext.destination);
|
scope.connect(audioContext.destination);
|
||||||
@ -134,9 +149,6 @@
|
|||||||
scopeFrame.id = 'scope';
|
scopeFrame.id = 'scope';
|
||||||
document.body.appendChild(scopeFrame);
|
document.body.appendChild(scopeFrame);
|
||||||
|
|
||||||
let filter = audioContext.createBiquadFilter();
|
|
||||||
filter.connect(scope);
|
|
||||||
|
|
||||||
// Drop zone
|
// Drop zone
|
||||||
document.body.ondragover = event => {
|
document.body.ondragover = event => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@ -169,9 +181,10 @@
|
|||||||
stretch.disconnect();
|
stretch.disconnect();
|
||||||
}
|
}
|
||||||
stretch = await SignalsmithStretch(audioContext);
|
stretch = await SignalsmithStretch(audioContext);
|
||||||
stretch.connect(filter);
|
stretch.connect(scope);
|
||||||
await stretch.addBuffers(channelBuffers);
|
await stretch.addBuffers(channelBuffers);
|
||||||
controlValues.loopEnd = audioDuration;
|
controlValues.loopEnd = audioDuration;
|
||||||
|
configChanged();
|
||||||
controlsChanged();
|
controlsChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,49 +192,75 @@
|
|||||||
let response = await fetch('loop.mp3');
|
let response = await fetch('loop.mp3');
|
||||||
handleArrayBuffer(await response.arrayBuffer());
|
handleArrayBuffer(await response.arrayBuffer());
|
||||||
|
|
||||||
let controlValuesInitial = {
|
|
||||||
active: false,
|
|
||||||
rate: 1,
|
|
||||||
semitones: 0,
|
|
||||||
tonalityHz: 8000,
|
|
||||||
shelfFreq: 8000,
|
|
||||||
shelfDb: 0
|
|
||||||
};
|
|
||||||
let controlValues = Object.assign({}, controlValuesInitial);
|
|
||||||
$('#playstop').onclick = e => {
|
$('#playstop').onclick = e => {
|
||||||
controlValues.active = !controlValues.active;
|
controlValues.active = !controlValues.active;
|
||||||
controlsChanged(0.15); // play state schedules slightly in the future because of latency, otherwise it ends up fading in to jump ahead.
|
controlsChanged(0.15);
|
||||||
};
|
};
|
||||||
$$('#controls input').forEach(input => {
|
$$('#controls input').forEach(input => {
|
||||||
let isCheckbox = input.type == 'checkbox';
|
let isCheckbox = input.type == 'checkbox';
|
||||||
let key = input.dataset.key;
|
let key = input.dataset.key;
|
||||||
input.oninput = input.onchange = e => {
|
input.oninput = input.onchange = e => {
|
||||||
controlValues[key] = isCheckbox ? input.checked : parseFloat(input.value);
|
let value = isCheckbox ? input.checked : parseFloat(input.value);
|
||||||
|
if (key in controlValues) {
|
||||||
|
controlValues[key] = value;
|
||||||
controlsChanged();
|
controlsChanged();
|
||||||
|
} else if (key in configValues) {
|
||||||
|
configValues[key] = value;
|
||||||
|
configChanged();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
if (!isCheckbox) input.ondblclick = e => {
|
if (!isCheckbox) input.ondblclick = e => {
|
||||||
|
if (key in controlValues) {
|
||||||
controlValues[key] = controlValuesInitial[key];
|
controlValues[key] = controlValuesInitial[key];
|
||||||
controlsChanged();
|
controlsChanged();
|
||||||
|
} else if (key in configValues) {
|
||||||
|
configValues[key] = configValuesInitial[key];
|
||||||
|
configChanged();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
function controlsChanged(scheduleAhead) {
|
function controlsChanged(scheduleAhead) {
|
||||||
let playing = controlValues.active;
|
$('#playstop').innerHTML = '<svg alt="toggle play" height="1em" width="1em" viewbox="0 0 8 8" style="vertical-align:middle"><path d="' + (controlValues.active ? 'M1 1L3 1 3 7 1 7ZM5 1 7 1 7 7 5 7Z' : 'M1 0L8 4 1 8') + '" fill="currentColor"/></svg>';
|
||||||
$('#playstop').innerHTML = '<svg alt="toggle play" height="1em" width="1em" viewbox="0 0 8 8" style="vertical-align:middle"><path d="' + (playing ? 'M1 1L3 1 3 7 1 7ZM5 1 7 1 7 7 5 7Z' : 'M1 0L8 4 1 8') + '" fill="currentColor"/></svg>';
|
|
||||||
|
|
||||||
$$('#controls input').forEach(input => {
|
$$('#controls input').forEach(input => {
|
||||||
let key = input.dataset.key;
|
let key = input.dataset.key;
|
||||||
|
if (key in controlValues) {
|
||||||
let value = controlValues[key];
|
let value = controlValues[key];
|
||||||
// Update value if it doesn't match
|
// Update value if it doesn't match
|
||||||
if (value !== parseFloat(input.value)) input.value = value;
|
if (value !== parseFloat(input.value)) input.value = value;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (stretch) {
|
if (stretch) {
|
||||||
let obj = Object.assign({output: audioContext.currentTime + (scheduleAhead || 0)}, controlValues);
|
let obj = Object.assign({output: audioContext.currentTime + (scheduleAhead || 0)}, controlValues);
|
||||||
stretch.schedule(obj);
|
stretch.schedule(obj);
|
||||||
}
|
}
|
||||||
filter.type = 'highshelf'; // https://developer.mozilla.org/en-US/docs/Web/API/BiquadFilterNode/type
|
audioContext.resume();
|
||||||
filter.Q.value = 0.71;
|
}
|
||||||
filter.frequency.value = controlValues.shelfFreq;
|
controlsChanged();
|
||||||
filter.gain.value = controlValues.shelfDb;
|
let configTimeout = null;
|
||||||
|
function configChanged() {
|
||||||
|
$$('#controls input').forEach(input => {
|
||||||
|
let key = input.dataset.key;
|
||||||
|
if (key in configValues) {
|
||||||
|
let value = configValues[key];
|
||||||
|
// Update value if it doesn't match
|
||||||
|
if (value !== parseFloat(input.value)) input.value = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (configTimeout == null) {
|
||||||
|
configTimeout = setTimeout(_ => {
|
||||||
|
configTimeout = null;
|
||||||
|
if (stretch) {
|
||||||
|
stretch.configure({
|
||||||
|
tonalityHz: configValues.tonalityHz,
|
||||||
|
blockMs: configValues.blockMs,
|
||||||
|
intervalMs: configValues.blockMs/configValues.overlap
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, 50);
|
||||||
|
}
|
||||||
audioContext.resume();
|
audioContext.resume();
|
||||||
}
|
}
|
||||||
controlsChanged();
|
controlsChanged();
|
||||||
|
|||||||
@ -49,5 +49,5 @@ em++ \
|
|||||||
-sINITIAL_MEMORY=512kb -sALLOW_MEMORY_GROWTH=1 -sMEMORY_GROWTH_GEOMETRIC_STEP=0.5 -sABORTING_MALLOC=1 \
|
-sINITIAL_MEMORY=512kb -sALLOW_MEMORY_GROWTH=1 -sMEMORY_GROWTH_GEOMETRIC_STEP=0.5 -sABORTING_MALLOC=1 \
|
||||||
-sSTRICT=1 -sDYNAMIC_EXECUTION=0
|
-sSTRICT=1 -sDYNAMIC_EXECUTION=0
|
||||||
|
|
||||||
# Remove last 4 lines (UMD definition)
|
# Remove UMD definition
|
||||||
node -e "let f=process.argv[1],fs=require('fs');fs.writeFileSync(f,fs.readFileSync(f,'utf8').split('\n').slice(0,-5).join('\n')+'\n')" "${OUTPUT_JS}"
|
node -e "let f=process.argv[1],fs=require('fs');fs.writeFileSync(f,fs.readFileSync(f,'utf8').split(\"if (typeof exports === 'object' && typeof module === 'object') {\")[0])" "${OUTPUT_JS}"
|
||||||
|
|||||||
@ -16,6 +16,8 @@ extern "C" {
|
|||||||
Sample * EMSCRIPTEN_KEEPALIVE setBuffers(int channels, int length) {
|
Sample * EMSCRIPTEN_KEEPALIVE setBuffers(int channels, int length) {
|
||||||
buffers.resize(length*channels*2);
|
buffers.resize(length*channels*2);
|
||||||
Sample *data = buffers.data();
|
Sample *data = buffers.data();
|
||||||
|
buffersIn.resize(0);
|
||||||
|
buffersOut.resize(0);
|
||||||
for (int c = 0; c < channels; ++c) {
|
for (int c = 0; c < channels; ++c) {
|
||||||
buffersIn.push_back(data + length*c);
|
buffersIn.push_back(data + length*c);
|
||||||
buffersOut.push_back(data + length*(c + channels));
|
buffersOut.push_back(data + length*(c + channels));
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -26,6 +26,11 @@ function registerWorkletProcessor(Module, audioNodeKey) {
|
|||||||
}];
|
}];
|
||||||
|
|
||||||
let remoteMethods = {
|
let remoteMethods = {
|
||||||
|
configure: config => {
|
||||||
|
let blockChanged = (config.blockMs != this.config.blockMs || config.intervalMs != this.config.intervalMs);
|
||||||
|
Object.assign(this.config, config);
|
||||||
|
if (config.blockMs && blockChanged) this.configure();
|
||||||
|
},
|
||||||
setUpdateInterval: seconds => {
|
setUpdateInterval: seconds => {
|
||||||
this.timeIntervalSamples = sampleRate*seconds;
|
this.timeIntervalSamples = sampleRate*seconds;
|
||||||
},
|
},
|
||||||
@ -170,8 +175,18 @@ function registerWorkletProcessor(Module, audioNodeKey) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
config = {
|
||||||
|
tonalityHz: 8000
|
||||||
|
};
|
||||||
configure() {
|
configure() {
|
||||||
|
if (this.config.blockMs) {
|
||||||
|
let blockSamples = Math.round(this.config.blockMs/1000*sampleRate);
|
||||||
|
let intervalSamples = Math.round((this.config.intervalMs || this.config.blockMs*0.25)/1000*sampleRate);
|
||||||
|
this.wasmModule._configure(this.channels, blockSamples, intervalSamples);
|
||||||
|
this.wasmModule._reset();
|
||||||
|
} else {
|
||||||
this.wasmModule._presetDefault(this.channels, sampleRate);
|
this.wasmModule._presetDefault(this.channels, sampleRate);
|
||||||
|
}
|
||||||
this.updateBuffers();
|
this.updateBuffers();
|
||||||
this.inputLatencySeconds = this.wasmModule._inputLatency()/sampleRate;
|
this.inputLatencySeconds = this.wasmModule._inputLatency()/sampleRate;
|
||||||
this.outputLatencySeconds = this.wasmModule._outputLatency()/sampleRate;
|
this.outputLatencySeconds = this.wasmModule._outputLatency()/sampleRate;
|
||||||
@ -210,7 +225,7 @@ function registerWorkletProcessor(Module, audioNodeKey) {
|
|||||||
let currentMapSegment = this.timeMap[0];
|
let currentMapSegment = this.timeMap[0];
|
||||||
|
|
||||||
let wasmModule = this.wasmModule;
|
let wasmModule = this.wasmModule;
|
||||||
wasmModule._setTransposeSemitones(currentMapSegment.semitones, 8000/sampleRate)
|
wasmModule._setTransposeSemitones(currentMapSegment.semitones, this.config.tonalityHz/sampleRate)
|
||||||
|
|
||||||
// Check the input/output channel counts
|
// Check the input/output channel counts
|
||||||
if (outputList[0].length != this.channels) {
|
if (outputList[0].length != this.channels) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user