rudimentary sample map loading (doughsamples)
This commit is contained in:
parent
d1b6b6be85
commit
3ac0fdf619
3 changed files with 126 additions and 162 deletions
|
|
@ -196,6 +196,52 @@ function getSamplesPrefixHandler(url) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function fetchSampleMap(url) {
|
||||||
|
// check if custom prefix handler
|
||||||
|
const handler = getSamplesPrefixHandler(url);
|
||||||
|
if (handler) {
|
||||||
|
return handler(url);
|
||||||
|
}
|
||||||
|
url = resolveSpecialPaths(url);
|
||||||
|
if (url.startsWith('github:')) {
|
||||||
|
url = githubPath(url, 'strudel.json');
|
||||||
|
}
|
||||||
|
if (url.startsWith('local:')) {
|
||||||
|
url = `http://localhost:5432`;
|
||||||
|
}
|
||||||
|
if (url.startsWith('shabda:')) {
|
||||||
|
let [_, path] = url.split('shabda:');
|
||||||
|
url = `https://shabda.ndre.gr/${path}.json?strudel=1`;
|
||||||
|
}
|
||||||
|
if (url.startsWith('shabda/speech')) {
|
||||||
|
let [_, path] = url.split('shabda/speech');
|
||||||
|
path = path.startsWith('/') ? path.substring(1) : path;
|
||||||
|
let [params, words] = path.split(':');
|
||||||
|
let gender = 'f';
|
||||||
|
let language = 'en-GB';
|
||||||
|
if (params) {
|
||||||
|
[language, gender] = params.split('/');
|
||||||
|
}
|
||||||
|
url = `https://shabda.ndre.gr/speech/${words}.json?gender=${gender}&language=${language}&strudel=1'`;
|
||||||
|
}
|
||||||
|
if (typeof fetch !== 'function') {
|
||||||
|
// not a browser
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const base = url.split('/').slice(0, -1).join('/');
|
||||||
|
if (typeof fetch === 'undefined') {
|
||||||
|
// skip fetch when in node / testing
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const json = await fetch(url)
|
||||||
|
.then((res) => res.json())
|
||||||
|
.catch((error) => {
|
||||||
|
console.error(error);
|
||||||
|
throw new Error(`error loading "${url}"`);
|
||||||
|
});
|
||||||
|
return [json, json._base || base];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads a collection of samples to use with `s`
|
* Loads a collection of samples to use with `s`
|
||||||
* @example
|
* @example
|
||||||
|
|
@ -217,49 +263,8 @@ function getSamplesPrefixHandler(url) {
|
||||||
|
|
||||||
export const samples = async (sampleMap, baseUrl = sampleMap._base || '', options = {}) => {
|
export const samples = async (sampleMap, baseUrl = sampleMap._base || '', options = {}) => {
|
||||||
if (typeof sampleMap === 'string') {
|
if (typeof sampleMap === 'string') {
|
||||||
// check if custom prefix handler
|
const [json, base] = await fetchSampleMap(sampleMap);
|
||||||
const handler = getSamplesPrefixHandler(sampleMap);
|
return samples(json, baseUrl || base, options);
|
||||||
if (handler) {
|
|
||||||
return handler(sampleMap);
|
|
||||||
}
|
|
||||||
sampleMap = resolveSpecialPaths(sampleMap);
|
|
||||||
if (sampleMap.startsWith('github:')) {
|
|
||||||
sampleMap = githubPath(sampleMap, 'strudel.json');
|
|
||||||
}
|
|
||||||
if (sampleMap.startsWith('local:')) {
|
|
||||||
sampleMap = `http://localhost:5432`;
|
|
||||||
}
|
|
||||||
if (sampleMap.startsWith('shabda:')) {
|
|
||||||
let [_, path] = sampleMap.split('shabda:');
|
|
||||||
sampleMap = `https://shabda.ndre.gr/${path}.json?strudel=1`;
|
|
||||||
}
|
|
||||||
if (sampleMap.startsWith('shabda/speech')) {
|
|
||||||
let [_, path] = sampleMap.split('shabda/speech');
|
|
||||||
path = path.startsWith('/') ? path.substring(1) : path;
|
|
||||||
let [params, words] = path.split(':');
|
|
||||||
let gender = 'f';
|
|
||||||
let language = 'en-GB';
|
|
||||||
if (params) {
|
|
||||||
[language, gender] = params.split('/');
|
|
||||||
}
|
|
||||||
sampleMap = `https://shabda.ndre.gr/speech/${words}.json?gender=${gender}&language=${language}&strudel=1'`;
|
|
||||||
}
|
|
||||||
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, options))
|
|
||||||
.catch((error) => {
|
|
||||||
console.error(error);
|
|
||||||
throw new Error(`error loading "${sampleMap}"`);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
const { prebake, tag } = options;
|
const { prebake, tag } = options;
|
||||||
processSampleMap(
|
processSampleMap(
|
||||||
|
|
|
||||||
|
|
@ -402,7 +402,7 @@ export class BufferPlayer {
|
||||||
buffer; // Float32Array
|
buffer; // Float32Array
|
||||||
sampleRate;
|
sampleRate;
|
||||||
pos = 0;
|
pos = 0;
|
||||||
sampleFreq = 261.626; // middle c
|
sampleFreq = note2freq();
|
||||||
constructor(buffer, sampleRate) {
|
constructor(buffer, sampleRate) {
|
||||||
this.buffer = buffer;
|
this.buffer = buffer;
|
||||||
this.sampleRate = sampleRate;
|
this.sampleRate = sampleRate;
|
||||||
|
|
@ -411,7 +411,7 @@ export class BufferPlayer {
|
||||||
if (this.pos >= this.buffer.length) {
|
if (this.pos >= this.buffer.length) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
const speed = ((freq / this.sampleFreq) * this.sampleRate) / SAMPLE_RATE;
|
const speed = ((freq / this.sampleFreq) * SAMPLE_RATE) / this.sampleRate;
|
||||||
let s = this.buffer[Math.floor(this.pos)];
|
let s = this.buffer[Math.floor(this.pos)];
|
||||||
this.pos = this.pos + speed;
|
this.pos = this.pos + speed;
|
||||||
return s;
|
return s;
|
||||||
|
|
@ -458,6 +458,7 @@ let shapes = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultDefaultValues = {
|
const defaultDefaultValues = {
|
||||||
|
note: 48,
|
||||||
s: 'triangle',
|
s: 'triangle',
|
||||||
gain: 1,
|
gain: 1,
|
||||||
postgain: 1,
|
postgain: 1,
|
||||||
|
|
@ -505,23 +506,19 @@ const note2midi = (note, defaultOctave = 3) => {
|
||||||
oct = Number(oct || defaultOctave);
|
oct = Number(oct || defaultOctave);
|
||||||
return (oct + 1) * 12 + chroma + offset;
|
return (oct + 1) * 12 + chroma + offset;
|
||||||
};
|
};
|
||||||
const getFrequency = (value) => {
|
const midi2freq = (midi) => Math.pow(2, (midi - 69) / 12) * 440;
|
||||||
let { note, freq } = value;
|
const note2freq = (note) => {
|
||||||
note = note || 36;
|
note = note || getDefaultValue('note');
|
||||||
if (typeof note === 'string') {
|
if (typeof note === 'string') {
|
||||||
note = note2midi(note, 3); // e.g. c3 => 48
|
note = note2midi(note, 3); // e.g. c3 => 48
|
||||||
}
|
}
|
||||||
if (!freq && typeof note === 'number') {
|
return midi2freq(note);
|
||||||
freq = Math.pow(2, (note - 69) / 12) * 440;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Number(freq);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export class DoughVoice {
|
export class DoughVoice {
|
||||||
out = [0, 0];
|
out = [0, 0];
|
||||||
constructor(value) {
|
constructor(value) {
|
||||||
value.freq = getFrequency(value);
|
value.freq ??= note2freq(value.note);
|
||||||
let $ = this;
|
let $ = this;
|
||||||
Object.assign($, value);
|
Object.assign($, value);
|
||||||
$.s = $.s ?? getDefaultValue('s');
|
$.s = $.s ?? getDefaultValue('s');
|
||||||
|
|
|
||||||
|
|
@ -25,105 +25,58 @@ Pattern.prototype.supradough = function () {
|
||||||
}, 1);
|
}, 1);
|
||||||
};
|
};
|
||||||
|
|
||||||
let samples = {
|
function githubPath(base, subpath = '') {
|
||||||
casio: [
|
if (!base.startsWith('github:')) {
|
||||||
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/casio/high.wav',
|
throw new Error('expected "github:" at the start of pseudoUrl');
|
||||||
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/casio/low.wav',
|
}
|
||||||
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/casio/noise.wav',
|
let [_, path] = base.split('github:');
|
||||||
],
|
path = path.endsWith('/') ? path.slice(0, -1) : path;
|
||||||
crow: [
|
if (path.split('/').length === 2) {
|
||||||
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/crow/000_crow.wav',
|
// assume main as default branch if none set
|
||||||
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/crow/001_crow2.wav',
|
path += '/main';
|
||||||
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/crow/002_crow3.wav',
|
}
|
||||||
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/crow/003_crow4.wav',
|
return `https://raw.githubusercontent.com/${path}/${subpath}`;
|
||||||
],
|
}
|
||||||
insect: [
|
export async function fetchSampleMap(url) {
|
||||||
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/insect/000_everglades_conehead.wav',
|
if (url.startsWith('github:')) {
|
||||||
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/insect/001_robust_shieldback.wav',
|
url = githubPath(url, 'strudel.json');
|
||||||
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/insect/002_seashore_meadow_katydid.wav',
|
}
|
||||||
],
|
if (url.startsWith('local:')) {
|
||||||
wind: [
|
url = `http://localhost:5432`;
|
||||||
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/wind/000_wind1.wav',
|
}
|
||||||
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/wind/001_wind10.wav',
|
if (url.startsWith('shabda:')) {
|
||||||
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/wind/002_wind2.wav',
|
let [_, path] = url.split('shabda:');
|
||||||
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/wind/003_wind3.wav',
|
url = `https://shabda.ndre.gr/${path}.json?strudel=1`;
|
||||||
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/wind/004_wind4.wav',
|
}
|
||||||
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/wind/005_wind5.wav',
|
if (url.startsWith('shabda/speech')) {
|
||||||
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/wind/006_wind6.wav',
|
let [_, path] = url.split('shabda/speech');
|
||||||
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/wind/007_wind7.wav',
|
path = path.startsWith('/') ? path.substring(1) : path;
|
||||||
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/wind/008_wind8.wav',
|
let [params, words] = path.split(':');
|
||||||
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/wind/009_wind9.wav',
|
let gender = 'f';
|
||||||
],
|
let language = 'en-GB';
|
||||||
jazz: [
|
if (params) {
|
||||||
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/jazz/000_BD.wav',
|
[language, gender] = params.split('/');
|
||||||
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/jazz/001_CB.wav',
|
}
|
||||||
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/jazz/002_FX.wav',
|
url = `https://shabda.ndre.gr/speech/${words}.json?gender=${gender}&language=${language}&strudel=1'`;
|
||||||
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/jazz/003_HH.wav',
|
}
|
||||||
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/jazz/004_OH.wav',
|
if (typeof fetch !== 'function') {
|
||||||
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/jazz/005_P1.wav',
|
// not a browser
|
||||||
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/jazz/006_P2.wav',
|
return;
|
||||||
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/jazz/007_SN.wav',
|
}
|
||||||
],
|
const base = url.split('/').slice(0, -1).join('/');
|
||||||
metal: [
|
if (typeof fetch === 'undefined') {
|
||||||
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/metal/000_0.wav',
|
// skip fetch when in node / testing
|
||||||
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/metal/001_1.wav',
|
return;
|
||||||
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/metal/002_2.wav',
|
}
|
||||||
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/metal/003_3.wav',
|
const json = await fetch(url)
|
||||||
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/metal/004_4.wav',
|
.then((res) => res.json())
|
||||||
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/metal/005_5.wav',
|
.catch((error) => {
|
||||||
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/metal/006_6.wav',
|
console.error(error);
|
||||||
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/metal/007_7.wav',
|
throw new Error(`error loading "${url}"`);
|
||||||
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/metal/008_8.wav',
|
});
|
||||||
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/metal/009_9.wav',
|
return [json, json._base || base];
|
||||||
],
|
}
|
||||||
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/C3v8.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??
|
// for some reason, only piano and flute work.. is it because mp3??
|
||||||
|
|
||||||
async function loadSampleChannels(key, url) {
|
async function loadSampleChannels(key, url) {
|
||||||
|
|
@ -138,18 +91,27 @@ async function loadSampleChannels(key, url) {
|
||||||
}
|
}
|
||||||
|
|
||||||
let loaded = false;
|
let loaded = false;
|
||||||
export async function doughsample() {
|
export async function doughsamples(sampleMap, baseUrl) {
|
||||||
|
if (typeof sampleMap === 'string') {
|
||||||
|
const [json, base] = await fetchSampleMap(sampleMap);
|
||||||
|
// console.log('json', json, 'base', base);
|
||||||
|
return doughsamples(json, base);
|
||||||
|
}
|
||||||
!doughWorklet && initDoughWorklet();
|
!doughWorklet && initDoughWorklet();
|
||||||
if (loaded) {
|
if (loaded) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
loaded = true;
|
loaded = true;
|
||||||
const sampleMap = await Promise.all(
|
const json = (
|
||||||
Object.entries(samples).map(async ([key, url]) => {
|
await Promise.all(
|
||||||
url = url[0];
|
Object.entries(sampleMap).map(async ([key, url]) => {
|
||||||
return loadSampleChannels(key, url);
|
if (key !== '_base') {
|
||||||
}),
|
url = baseUrl + url[0];
|
||||||
);
|
return loadSampleChannels(key, url);
|
||||||
console.log('sampleMap', sampleMap);
|
}
|
||||||
doughWorklet.port.postMessage({ samples: sampleMap });
|
}),
|
||||||
|
)
|
||||||
|
).filter(Boolean);
|
||||||
|
// console.log('sampleMap', json);
|
||||||
|
doughWorklet.port.postMessage({ samples: json });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue