Merge remote-tracking branch 'origin/main' into general-purpose-scheduler
This commit is contained in:
commit
7e7e299bce
33 changed files with 3697 additions and 11642 deletions
|
|
@ -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()
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
*
|
||||
*/
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue