Merge remote-tracking branch 'origin/main' into general-purpose-scheduler

This commit is contained in:
Felix Roos 2022-10-29 17:56:30 +02:00
commit 7e7e299bce
33 changed files with 3697 additions and 11642 deletions

View file

@ -15,13 +15,13 @@ npm i @strudel.cycles/webaudio --save
import { Scheduler, getAudioContext } from '@strudel.cycles/webaudio';
const scheduler = new Scheduler({
audioContext: getAudioContext(),
interval: 0.1,
onEvent: (e) => e.context?.createAudioNode?.(e),
});
const pattern = sequence([55, 99], 110).osc('sawtooth').out()
audioContext: getAudioContext(),
interval: 0.1,
onEvent: (e) => e.context?.createAudioNode?.(e),
});
const pattern = sequence([55, 99], 110).osc('sawtooth');
scheduler.setPattern(pattern);
scheduler.start()
scheduler.start();
//scheduler.stop()
```

View file

@ -18,3 +18,6 @@ if (typeof AudioContext !== 'undefined') {
return convolver;
};
}
// TODO: make the reverb more exciting
// check out https://blog.gskinner.com/archives/2019/02/reverb-web-audio-api.html

View file

@ -98,7 +98,7 @@ export const loadGithubSamples = async (path, nameFn) => {
* bd: '808bd/BD0000.WAV',
* sd: '808sd/SD0010.WAV'
* }, 'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/');
* s("[bd ~]*2, [~ hh]*2, ~ sd").out()
* s("[bd ~]*2, [~ hh]*2, ~ sd")
*
*/

View file

@ -6,7 +6,7 @@ This program is free software: you can redistribute it and/or modify it under th
// import { Pattern, getFrequency, patternify2 } from '@strudel.cycles/core';
import * as strudel from '@strudel.cycles/core';
import { fromMidi, toMidi } from '@strudel.cycles/core';
import { fromMidi, isNote, toMidi } from '@strudel.cycles/core';
import './feedbackdelay.mjs';
import './reverb.mjs';
import { loadBuffer, reverseBuffer } from './sampler.mjs';
@ -115,7 +115,11 @@ const getSampleBufferSource = async (s, n, note, speed) => {
}
const bank = samples?.[s];
if (!bank) {
throw new Error(`sample not found: "${s}", try one of ${Object.keys(samples).join(', ')}`);
throw new Error(
`sample not found: "${s}", try one of ${Object.keys(samples)
.map((s) => `"${s}"`)
.join(', ')}.`,
);
}
if (typeof bank !== 'object') {
throw new Error('wrong format for sample bank:', s);
@ -234,6 +238,10 @@ function effectSend(input, effect, wet) {
export const webaudioOutput = async (hap, deadline, hapDuration) => {
try {
const ac = getAudioContext();
/* if (isNote(hap.value)) {
// supports primitive hap values that look like notes
hap.value = { note: hap.value };
} */
if (typeof hap.value !== 'object') {
throw new Error(
`hap.value ${hap.value} is not supported by webaudio output. Hint: append .note() or .s() to the end`,
@ -249,7 +257,7 @@ export const webaudioOutput = async (hap, deadline, hapDuration) => {
clip = 0, // if 1, samples will be cut off when the hap ends
n = 0,
note,
gain = 1,
gain = 0.8,
cutoff,
resonance = 1,
hcutoff,
@ -260,10 +268,6 @@ export const webaudioOutput = async (hap, deadline, hapDuration) => {
crush,
shape,
pan,
attack = 0.001,
decay = 0.001,
sustain = 1,
release = 0.001,
speed = 1, // sample playback speed
begin = 0,
end = 1,
@ -291,6 +295,8 @@ export const webaudioOutput = async (hap, deadline, hapDuration) => {
[note, n] = splitSN(note, n);
}
if (!s || ['sine', 'square', 'triangle', 'sawtooth'].includes(s)) {
// destructure adsr here, because the default should be different for synths and samples
const { attack = 0.001, decay = 0.05, sustain = 0.6, release = 0.01 } = hap.value;
// with synths, n and note are the same thing
n = note || n || 36;
if (typeof n === 'string') {
@ -310,6 +316,8 @@ export const webaudioOutput = async (hap, deadline, hapDuration) => {
const adsr = getADSR(attack, decay, sustain, release, 1, t, t + hapDuration);
chain.push(adsr);
} else {
// destructure adsr here, because the default should be different for synths and samples
const { attack = 0.001, decay = 0.001, sustain = 1, release = 0.001 } = hap.value;
// load sample
if (speed === 0) {
// no playback
@ -422,7 +430,9 @@ export const webaudioOutput = async (hap, deadline, hapDuration) => {
}
};
export const webaudioOutputTrigger = (t, hap, ct, cps) => webaudioOutput(hap, t - ct, hap.duration / cps);
Pattern.prototype.out = function () {
// TODO: refactor (t, hap, ct, cps) to (hap, deadline, duration) ?
return this.onTrigger((t, hap, ct, cps) => webaudioOutput(hap, t - ct, hap.duration / cps));
return this.onTrigger(webaudioOutputTrigger);
};