half working samples
This commit is contained in:
parent
ac7ee376e5
commit
0f2aa9569b
4 changed files with 182 additions and 12 deletions
|
|
@ -6,7 +6,19 @@ class DoughProcessor extends AudioWorkletProcessor {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.dough = new Dough(sampleRate, currentTime);
|
this.dough = new Dough(sampleRate, currentTime);
|
||||||
this.port.onmessage = (event) => this.dough.scheduleSpawn(event.data);
|
this.port.onmessage = (event) => {
|
||||||
|
if (event.data.spawn) {
|
||||||
|
this.dough.scheduleSpawn(event.data.spawn);
|
||||||
|
} else if (event.data.sample) {
|
||||||
|
this.dough.loadSample(event.data.sample, event.data.channels);
|
||||||
|
} else if (event.data.samples) {
|
||||||
|
event.data.samples.forEach(([name, channels]) => {
|
||||||
|
this.dough.loadSample(name, channels);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log('unrecognized event type', event.data);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
process(inputs, outputs, params) {
|
process(inputs, outputs, params) {
|
||||||
if (this.disconnected) {
|
if (this.disconnected) {
|
||||||
|
|
|
||||||
|
|
@ -383,6 +383,19 @@ export class Distort {
|
||||||
}
|
}
|
||||||
// distortion could be expressed as a function, because it's stateless
|
// distortion could be expressed as a function, because it's stateless
|
||||||
|
|
||||||
|
export class BufferPlayer {
|
||||||
|
channels = [];
|
||||||
|
pos = 0;
|
||||||
|
update(freq, channel = 0) {
|
||||||
|
if (this.pos >= this.channels[channel].length) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
let s = this.channels[channel][this.pos];
|
||||||
|
this.pos++;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function _rangex(sig, min, max) {
|
export function _rangex(sig, min, max) {
|
||||||
let logmin = Math.log(min);
|
let logmin = Math.log(min);
|
||||||
let range = Math.log(max) - logmin;
|
let range = Math.log(max) - logmin;
|
||||||
|
|
@ -403,7 +416,7 @@ export const getADSRValues = (params, curve = 'linear', defaultValues) => {
|
||||||
return [Math.max(a ?? 0, envmin), Math.max(d ?? 0, envmin), Math.min(sustain, envmax), Math.max(r ?? 0, releaseMin)];
|
return [Math.max(a ?? 0, envmin), Math.max(d ?? 0, envmin), Math.min(sustain, envmax), Math.max(r ?? 0, releaseMin)];
|
||||||
};
|
};
|
||||||
|
|
||||||
let oscillators = {
|
let shapes = {
|
||||||
sine: SineOsc,
|
sine: SineOsc,
|
||||||
saw: SawOsc,
|
saw: SawOsc,
|
||||||
zaw: ZawOsc,
|
zaw: ZawOsc,
|
||||||
|
|
@ -553,11 +566,20 @@ export class DoughVoice {
|
||||||
this._holdEnd = this._begin + this._duration; // needed for gate
|
this._holdEnd = this._begin + this._duration; // needed for gate
|
||||||
this._end = this._holdEnd + this.release + 0.01; // needed for despawn
|
this._end = this._holdEnd + this.release + 0.01; // needed for despawn
|
||||||
|
|
||||||
|
this.s ??= 'triangle';
|
||||||
if (this.s === 'saw' || this.s === 'sawtooth') {
|
if (this.s === 'saw' || this.s === 'sawtooth') {
|
||||||
this.s = 'zaw'; // polyblepped saw when fm is applied
|
this.s = 'zaw'; // polyblepped saw when fm is applied
|
||||||
}
|
}
|
||||||
const SourceClass = oscillators[this.s] ?? TriOsc;
|
if (shapes[this.s]) {
|
||||||
this._sound = new SourceClass();
|
const SourceClass = shapes[this.s];
|
||||||
|
this._sound = new SourceClass();
|
||||||
|
} else if (value.samples.has(this.s)) {
|
||||||
|
this._sample = new BufferPlayer();
|
||||||
|
this._sample.channels = value.samples.get(this.s);
|
||||||
|
this._sample.pos = 0;
|
||||||
|
} else {
|
||||||
|
console.warn('sound not found', this.s);
|
||||||
|
}
|
||||||
|
|
||||||
if (this.penv) {
|
if (this.penv) {
|
||||||
this._penv = new ADSR();
|
this._penv = new ADSR();
|
||||||
|
|
@ -642,7 +664,7 @@ export class DoughVoice {
|
||||||
return 1 - Math.log(c) * this.eighthOverLogHalf;
|
return 1 - Math.log(c) * this.eighthOverLogHalf;
|
||||||
}
|
}
|
||||||
update(t) {
|
update(t) {
|
||||||
if (!this._sound) {
|
if (!this._sound && !this._sample) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
let s = 0;
|
let s = 0;
|
||||||
|
|
@ -666,10 +688,12 @@ export class DoughVoice {
|
||||||
}
|
}
|
||||||
|
|
||||||
// sound source
|
// sound source
|
||||||
if (this.s === 'pulse') {
|
if (this._sound && this.s === 'pulse') {
|
||||||
s = this._sound.update(freq, this.pw ?? 0.5);
|
s = this._sound.update(freq, this.pw ?? 0.5);
|
||||||
} else {
|
} else if (this._sound) {
|
||||||
s = this._sound.update(freq);
|
s = this._sound.update(freq);
|
||||||
|
} else if (this._sample) {
|
||||||
|
s = this._sample.update(freq, 0); // tbd: stereo samples...
|
||||||
}
|
}
|
||||||
s = s * this.gain * this.velocity;
|
s = s * this.gain * this.velocity;
|
||||||
|
|
||||||
|
|
@ -738,6 +762,7 @@ export class Dough {
|
||||||
delaysend = [0, 0];
|
delaysend = [0, 0];
|
||||||
delaytime = getDefaultValue('delaytime');
|
delaytime = getDefaultValue('delaytime');
|
||||||
delayfeedback = getDefaultValue('delayfeedback');
|
delayfeedback = getDefaultValue('delayfeedback');
|
||||||
|
samples = new Map();
|
||||||
t = 0;
|
t = 0;
|
||||||
// sampleRate: number, currentTime: number (seconds)
|
// sampleRate: number, currentTime: number (seconds)
|
||||||
constructor(sampleRate = 48000, currentTime = 0) {
|
constructor(sampleRate = 48000, currentTime = 0) {
|
||||||
|
|
@ -747,6 +772,9 @@ export class Dough {
|
||||||
this._delayL = new Delay();
|
this._delayL = new Delay();
|
||||||
this._delayR = new Delay();
|
this._delayR = new Delay();
|
||||||
}
|
}
|
||||||
|
loadSample(name, channels) {
|
||||||
|
this.samples.set(name, channels);
|
||||||
|
}
|
||||||
scheduleSpawn(value) {
|
scheduleSpawn(value) {
|
||||||
if (value._begin === undefined) {
|
if (value._begin === undefined) {
|
||||||
throw new Error('[dough]: scheduleSpawn expected _begin to be set');
|
throw new Error('[dough]: scheduleSpawn expected _begin to be set');
|
||||||
|
|
@ -760,6 +788,7 @@ export class Dough {
|
||||||
}
|
}
|
||||||
spawn(value) {
|
spawn(value) {
|
||||||
value.id = this.vid++;
|
value.id = this.vid++;
|
||||||
|
value.samples = this.samples;
|
||||||
const voice = new DoughVoice(value);
|
const voice = new DoughVoice(value);
|
||||||
this.voices.push(voice);
|
this.voices.push(voice);
|
||||||
// console.log('spawn', voice.id, 'voices:', this.voices.length);
|
// console.log('spawn', voice.id, 'voices:', this.voices.length);
|
||||||
|
|
|
||||||
|
|
@ -7,4 +7,5 @@ This program is free software: you can redistribute it and/or modify it under th
|
||||||
export * from './webaudio.mjs';
|
export * from './webaudio.mjs';
|
||||||
export * from './scope.mjs';
|
export * from './scope.mjs';
|
||||||
export * from './spectrum.mjs';
|
export * from './spectrum.mjs';
|
||||||
|
export * from './supradough.mjs';
|
||||||
export * from 'superdough';
|
export * from 'superdough';
|
||||||
|
|
|
||||||
|
|
@ -20,10 +20,138 @@ Pattern.prototype.supradough = function () {
|
||||||
return this.onTrigger((_, hap, __, cps, begin) => {
|
return this.onTrigger((_, hap, __, cps, begin) => {
|
||||||
hap.value._begin = begin;
|
hap.value._begin = begin;
|
||||||
hap.value._duration = hap.duration / cps;
|
hap.value._duration = hap.duration / cps;
|
||||||
|
!doughWorklet && initDoughWorklet();
|
||||||
if (!doughWorklet) {
|
doughWorklet.port.postMessage({ spawn: hap.value });
|
||||||
initDoughWorklet();
|
|
||||||
}
|
|
||||||
doughWorklet.port.postMessage(hap.value);
|
|
||||||
}, 1);
|
}, 1);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
async function loadSampleChannels(url) {
|
||||||
|
const buffer = await fetch(url)
|
||||||
|
.then((res) => res.arrayBuffer())
|
||||||
|
.then((buf) => getAudioContext().decodeAudioData(buf));
|
||||||
|
// console.log('buffer', buffer, buffer.numberOfChannels);
|
||||||
|
let channels = [];
|
||||||
|
for (let i = 0; i < buffer.numberOfChannels; i++) {
|
||||||
|
channels.push(buffer.getChannelData(i));
|
||||||
|
}
|
||||||
|
return channels;
|
||||||
|
}
|
||||||
|
|
||||||
|
let samples = {
|
||||||
|
casio: [
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/casio/high.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/casio/low.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/casio/noise.wav',
|
||||||
|
],
|
||||||
|
crow: [
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/crow/000_crow.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/crow/001_crow2.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/crow/002_crow3.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/crow/003_crow4.wav',
|
||||||
|
],
|
||||||
|
insect: [
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/insect/000_everglades_conehead.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/insect/001_robust_shieldback.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/insect/002_seashore_meadow_katydid.wav',
|
||||||
|
],
|
||||||
|
wind: [
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/wind/000_wind1.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/wind/001_wind10.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/wind/002_wind2.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/wind/003_wind3.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/wind/004_wind4.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/wind/005_wind5.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/wind/006_wind6.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/wind/007_wind7.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/wind/008_wind8.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/wind/009_wind9.wav',
|
||||||
|
],
|
||||||
|
jazz: [
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/jazz/000_BD.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/jazz/001_CB.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/jazz/002_FX.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/jazz/003_HH.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/jazz/004_OH.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/jazz/005_P1.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/jazz/006_P2.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/jazz/007_SN.wav',
|
||||||
|
],
|
||||||
|
metal: [
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/metal/000_0.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/metal/001_1.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/metal/002_2.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/metal/003_3.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/metal/004_4.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/metal/005_5.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/metal/006_6.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/metal/007_7.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/metal/008_8.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/metal/009_9.wav',
|
||||||
|
],
|
||||||
|
east: [
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/east/000_nipon_wood_block.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/east/001_ohkawa_mute.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/east/002_ohkawa_open.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/east/003_shime_hi.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/east/004_shime_hi_2.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/east/005_shime_mute.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/east/006_taiko_1.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/east/007_taiko_2.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/east/008_taiko_3.wav',
|
||||||
|
],
|
||||||
|
space: [
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/space/000_0.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/space/001_1.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/space/002_11.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/space/003_12.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/space/004_13.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/space/005_14.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/space/006_15.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/space/007_16.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/space/008_17.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/space/009_18.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/space/010_2.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/space/011_3.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/space/012_4.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/space/013_5.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/space/014_6.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/space/015_7.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/space/016_8.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/space/017_9.wav',
|
||||||
|
],
|
||||||
|
numbers: [
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/numbers/0.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/numbers/1.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/numbers/2.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/numbers/3.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/numbers/4.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/numbers/5.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/numbers/6.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/numbers/7.wav',
|
||||||
|
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/numbers/8.wav',
|
||||||
|
],
|
||||||
|
piano: ['https://raw.githubusercontent.com/felixroos/dough-samples/refs/heads/main/piano/A3v8.mp3'],
|
||||||
|
flute: ['https://raw.githubusercontent.com/felixroos/samples/refs/heads/main/flute/c4.mp3'],
|
||||||
|
bd: [
|
||||||
|
'https://raw.githubusercontent.com/geikha/tidal-drum-machines/15eac73c5e878550f91d864a4863e014799403f1/machines/RolandTR909/rolandtr909-bd/Bassdrum-01.wav',
|
||||||
|
],
|
||||||
|
};
|
||||||
|
// for some reason, only piano and flute work.. is it because mp3??
|
||||||
|
|
||||||
|
let loaded = false;
|
||||||
|
export async function doughsample() {
|
||||||
|
!doughWorklet && initDoughWorklet();
|
||||||
|
if (loaded) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
loaded = true;
|
||||||
|
const sampleMap = await Promise.all(
|
||||||
|
Object.entries(samples).map(async ([key, url]) => {
|
||||||
|
url = url[0];
|
||||||
|
console.log(key, 'url', url);
|
||||||
|
return [key, await loadSampleChannels(url)];
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
console.log('sampleMap', sampleMap);
|
||||||
|
doughWorklet.port.postMessage({ samples: sampleMap });
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue