diff --git a/packages/superdough/README.md b/packages/superdough/README.md index 7a441841..4d09e7fd 100644 --- a/packages/superdough/README.md +++ b/packages/superdough/README.md @@ -17,10 +17,11 @@ npm i superdough --save ```js import { superdough, samples } from 'superdough'; // load samples from github -const loadSamples = samples('github:tidalcycles/Dirt-Samples/master'); +const init = samples('github:tidalcycles/Dirt-Samples/master'); // play some sounds when a button is clicked -document.getElementById('play').addEventListener('click', () => { +document.getElementById('play').addEventListener('click', async () => { + await init; superdough({ s: "bd", delay: .5 }, 0); superdough({ s: "sawtooth", cutoff: 600, resonance: 8 }, 0); superdough({ s: "hh" }, 0.25); diff --git a/packages/superdough/example/main.js b/packages/superdough/example/main.js index 50861938..7b74fb40 100644 --- a/packages/superdough/example/main.js +++ b/packages/superdough/example/main.js @@ -1,12 +1,13 @@ import { superdough, samples, initAudioOnFirstClick, registerSynthSounds } from 'superdough'; -initAudioOnFirstClick(); - -const load = Promise.all([samples('github:tidalcycles/Dirt-Samples/master'), registerSynthSounds()]); - -let button = document.getElementById('play'); +const init = Promise.all([ + initAudioOnFirstClick(), + samples('github:tidalcycles/Dirt-Samples/master'), + registerSynthSounds(), +]); const loop = (t = 0) => { + // superdough(value, time, duration) superdough({ s: 'bd', delay: 0.5 }, t); superdough({ note: 'g1', s: 'sawtooth', cutoff: 600, resonance: 8 }, t, 0.125); superdough({ note: 'g2', s: 'sawtooth', cutoff: 600, resonance: 8 }, t + 0.25, 0.125); @@ -15,9 +16,8 @@ const loop = (t = 0) => { superdough({ s: 'hh' }, t + 0.75); }; -button.addEventListener('click', async () => { - console.log('play'); - await load; +document.getElementById('play').addEventListener('click', async () => { + await init; let t = 0.1; while (t < 16) { loop(t++); diff --git a/packages/superdough/package.json b/packages/superdough/package.json index 1065d3f8..6f487cd0 100644 --- a/packages/superdough/package.json +++ b/packages/superdough/package.json @@ -1,6 +1,6 @@ { "name": "superdough", - "version": "0.9.0", + "version": "0.9.1", "description": "simple web audio synth and sampler intended for live coding. inspired by superdirt and webdirt.", "main": "index.mjs", "type": "module", diff --git a/packages/superdough/sampler.mjs b/packages/superdough/sampler.mjs index 17275a99..0df5ec15 100644 --- a/packages/superdough/sampler.mjs +++ b/packages/superdough/sampler.mjs @@ -1,4 +1,4 @@ -import { logger, noteToMidi, valueToMidi } from '@strudel.cycles/core'; +import { noteToMidi, valueToMidi } from './util.mjs'; import { getAudioContext, registerSound } from './index.mjs'; import { getEnvelope } from './helpers.mjs'; @@ -24,7 +24,7 @@ function humanFileSize(bytes, si) { export const getSampleBufferSource = async (s, n, note, speed, freq, bank, resolveUrl) => { let transpose = 0; if (freq !== undefined && note !== undefined) { - logger('[sampler] hap has note and freq. ignoring note', 'warning'); + // logger('[sampler] hap has note and freq. ignoring note', 'warning'); } let midi = valueToMidi({ freq, note }, 36); transpose = midi - 36; // C3 is middle C @@ -64,7 +64,7 @@ export const getSampleBufferSource = async (s, n, note, speed, freq, bank, resol export const loadBuffer = (url, ac, s, n = 0) => { const label = s ? `sound "${s}:${n}"` : 'sample'; if (!loadCache[url]) { - logger(`[sampler] load ${label}..`, 'load-sample', { url }); + //logger(`[sampler] load ${label}..`, 'load-sample', { url }); const timestamp = Date.now(); loadCache[url] = fetch(url) .then((res) => res.arrayBuffer()) @@ -72,7 +72,7 @@ export const loadBuffer = (url, ac, s, n = 0) => { const took = Date.now() - timestamp; const size = humanFileSize(res.byteLength); // const downSpeed = humanFileSize(res.byteLength / took); - logger(`[sampler] load ${label}... done! loaded ${size} in ${took}ms`, 'loaded-sample', { url }); + //logger(`[sampler] load ${label}... done! loaded ${size} in ${took}ms`, 'loaded-sample', { url }); const decoded = await ac.decodeAudioData(res); bufferCache[url] = decoded; return decoded; @@ -224,12 +224,12 @@ export async function onTriggerSample(t, value, onended, bank, resolveUrl) { // asny stuff above took too long? if (ac.currentTime > t) { - logger(`[sampler] still loading sound "${s}:${n}"`, 'highlight'); + //logger(`[sampler] still loading sound "${s}:${n}"`, 'highlight'); // console.warn('sample still loading:', s, n); return; } if (!bufferSource) { - logger(`[sampler] could not load "${s}:${n}"`, 'error'); + //logger(`[sampler] could not load "${s}:${n}"`, 'error'); return; } bufferSource.playbackRate.value = Math.abs(speed) * bufferSource.playbackRate.value; diff --git a/packages/superdough/util.mjs b/packages/superdough/util.mjs index 860aa9c1..db056376 100644 --- a/packages/superdough/util.mjs +++ b/packages/superdough/util.mjs @@ -27,3 +27,27 @@ export const midiToFreq = (n) => { return Math.pow(2, (n - 69) / 12) * 440; }; export const clamp = (num, min, max) => Math.min(Math.max(num, min), max); + +export const freqToMidi = (freq) => { + return (12 * Math.log(freq / 440)) / Math.LN2 + 69; +}; + +export const valueToMidi = (value, fallbackValue) => { + if (typeof value !== 'object') { + throw new Error('valueToMidi: expected object value'); + } + let { freq, note } = value; + if (typeof freq === 'number') { + return freqToMidi(freq); + } + if (typeof note === 'string') { + return noteToMidi(note); + } + if (typeof note === 'number') { + return note; + } + if (!fallbackValue) { + throw new Error('valueToMidi: expected freq or note to be set'); + } + return fallbackValue; +};