Merge remote-tracking branch 'origin/main' into general-purpose-scheduler
This commit is contained in:
commit
0485632e22
53 changed files with 24365 additions and 8987 deletions
|
|
@ -1,7 +1,7 @@
|
|||
if (typeof AudioContext !== 'undefined') {
|
||||
AudioContext.prototype.impulseResponse = function (duration) {
|
||||
AudioContext.prototype.impulseResponse = function (duration, channels = 1) {
|
||||
const length = this.sampleRate * duration;
|
||||
const impulse = this.createBuffer(2, length, this.sampleRate);
|
||||
const impulse = this.createBuffer(channels, length, this.sampleRate);
|
||||
const IR = impulse.getChannelData(0);
|
||||
for (let i = 0; i < length; i++) IR[i] = (2 * Math.random() - 1) * Math.pow(1 - i / length, duration);
|
||||
return impulse;
|
||||
|
|
|
|||
|
|
@ -102,7 +102,29 @@ export const loadGithubSamples = async (path, nameFn) => {
|
|||
*
|
||||
*/
|
||||
|
||||
export const samples = (sampleMap, baseUrl = sampleMap._base || '') => {
|
||||
export const samples = async (sampleMap, baseUrl = sampleMap._base || '') => {
|
||||
if (typeof sampleMap === 'string') {
|
||||
if (sampleMap.startsWith('github:')) {
|
||||
const [_, path] = sampleMap.split('github:');
|
||||
sampleMap = `https://raw.githubusercontent.com/${path}/strudel.json`;
|
||||
}
|
||||
if (typeof fetch !== 'function') {
|
||||
// not a browser
|
||||
return;
|
||||
}
|
||||
const base = sampleMap.split('/').slice(0, -1).join('/');
|
||||
if (typeof fetch === 'undefined') {
|
||||
// skip fetch when in node / testing
|
||||
return;
|
||||
}
|
||||
return fetch(sampleMap)
|
||||
.then((res) => res.json())
|
||||
.then((json) => samples(json, baseUrl || json._base || base))
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
throw new Error(`error loading "${sampleMap}"`);
|
||||
});
|
||||
}
|
||||
sampleCache.current = {
|
||||
...sampleCache.current,
|
||||
...Object.fromEntries(
|
||||
|
|
|
|||
|
|
@ -100,12 +100,8 @@ const getSoundfontKey = (s) => {
|
|||
|
||||
const getSampleBufferSource = async (s, n, note, speed) => {
|
||||
let transpose = 0;
|
||||
let midi;
|
||||
|
||||
if (note !== undefined) {
|
||||
midi = typeof note === 'string' ? toMidi(note) : note;
|
||||
transpose = midi - 36; // C3 is middle C
|
||||
}
|
||||
let midi = typeof note === 'string' ? toMidi(note) : note || 36;
|
||||
transpose = midi - 36; // C3 is middle C
|
||||
|
||||
const ac = getAudioContext();
|
||||
// is sample from loaded samples(..)
|
||||
|
|
@ -128,9 +124,6 @@ const getSampleBufferSource = async (s, n, note, speed) => {
|
|||
if (Array.isArray(bank)) {
|
||||
sampleUrl = bank[n % bank.length];
|
||||
} else {
|
||||
if (!note) {
|
||||
throw new Error('no note(...) set for sound', s);
|
||||
}
|
||||
const midiDiff = (noteA) => toMidi(noteA) - midi;
|
||||
// object format will expect keys as notes
|
||||
const closest = Object.keys(bank)
|
||||
|
|
@ -253,6 +246,7 @@ export const webaudioOutput = async (hap, deadline, hapDuration) => {
|
|||
let {
|
||||
freq,
|
||||
s,
|
||||
bank,
|
||||
sf,
|
||||
clip = 0, // if 1, samples will be cut off when the hap ends
|
||||
n = 0,
|
||||
|
|
@ -288,6 +282,9 @@ export const webaudioOutput = async (hap, deadline, hapDuration) => {
|
|||
gain *= velocity; // legacy fix for velocity
|
||||
// the chain will hold all audio nodes that connect to each other
|
||||
const chain = [];
|
||||
if (bank && s) {
|
||||
s = `${bank}_${s}`;
|
||||
}
|
||||
if (typeof s === 'string') {
|
||||
[s, n] = splitSN(s, n);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,8 +22,9 @@ class CoarseProcessor extends AudioWorkletProcessor {
|
|||
this.notStarted = false;
|
||||
output[0][0] = input[0][0];
|
||||
for (let n = 1; n < blockSize; n++) {
|
||||
if (n % coarse == 0) output[0][n] = input[0][n];
|
||||
else output[0][n] = output[0][n - 1];
|
||||
for (let o = 0; o < output.length; o++) {
|
||||
output[o][n] = n % coarse == 0 ? input[0][n] : output[o][n - 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.notStarted || hasInput;
|
||||
|
|
@ -52,11 +53,19 @@ class CrushProcessor extends AudioWorkletProcessor {
|
|||
this.notStarted = false;
|
||||
if (crush.length === 1) {
|
||||
const x = Math.pow(2, crush[0] - 1);
|
||||
for (let n = 0; n < blockSize; n++) output[0][n] = Math.round(input[0][n] * x) / x;
|
||||
for (let n = 0; n < blockSize; n++) {
|
||||
const value = Math.round(input[0][n] * x) / x;
|
||||
for (let o = 0; o < output.length; o++) {
|
||||
output[o][n] = value;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (let n = 0; n < blockSize; n++) {
|
||||
let x = Math.pow(2, crush[n] - 1);
|
||||
output[0][n] = Math.round(input[0][n] * x) / x;
|
||||
const value = Math.round(input[0][n] * x) / x;
|
||||
for (let o = 0; o < output.length; o++) {
|
||||
output[o][n] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -86,7 +95,10 @@ class ShapeProcessor extends AudioWorkletProcessor {
|
|||
if (hasInput) {
|
||||
this.notStarted = false;
|
||||
for (let n = 0; n < blockSize; n++) {
|
||||
output[0][n] = ((1 + shape) * input[0][n]) / (1 + shape * Math.abs(input[0][n]));
|
||||
const value = ((1 + shape) * input[0][n]) / (1 + shape * Math.abs(input[0][n]));
|
||||
for (let o = 0; o < output.length; o++) {
|
||||
output[o][n] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.notStarted || hasInput;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue