Compare commits

...

2 Commits

Author SHA1 Message Date
Jade (Rose) Rowland ecbd7c77a5 almost working 2025-07-29 18:58:28 -04:00
Jade (Rose) Rowland ec920a1551 i made a mess 2025-07-29 18:44:12 -04:00
11 changed files with 218 additions and 272 deletions
@@ -1,9 +1,12 @@
import { registerSound, onTriggerSample } from '@strudel/webaudio'; import { registerSound, onTriggerSample } from '@strudel/webaudio';
import { isAudioFile } from './files.mjs';
import { logger } from '@strudel/core'; import { logger } from '@strudel/core';
//utilites for writing and reading to the indexdb //utilites for writing and reading to the indexdb
export const isAudioFile = (filename) =>
['wav', 'mp3', 'flac', 'ogg', 'm4a', 'aac'].includes(filename.split('.').slice(-1)[0]);
export const userSamplesDBConfig = { export const userSamplesDBConfig = {
dbName: 'samples', dbName: 'samples',
table: 'usersamples', table: 'usersamples',
+2
View File
@@ -25,6 +25,8 @@ export * from './cyclist.mjs';
export * from './logger.mjs'; export * from './logger.mjs';
export * from './time.mjs'; export * from './time.mjs';
export * from './ui.mjs'; export * from './ui.mjs';
export * from './ui.mjs';
export * from './idbutils.mjs'
export { default as drawLine } from './drawLine.mjs'; export { default as drawLine } from './drawLine.mjs';
// below won't work with runtime.mjs (json import fails) // below won't work with runtime.mjs (json import fails)
/* import * as p from './package.json'; /* import * as p from './package.json';
+1
View File
@@ -1,2 +1,3 @@
export * from './repl-component.mjs'; export * from './repl-component.mjs';
export * from './prebake.mjs'; export * from './prebake.mjs';
+5 -3
View File
@@ -1,5 +1,5 @@
import { noteToMidi, valueToMidi, Pattern, evalScope } from '@strudel/core'; import { noteToMidi, valueToMidi, Pattern, evalScope,registerSamplesFromDB } from '@strudel/core';
import { aliasBank, registerSynthSounds, registerZZFXSounds, samples } from '@strudel/webaudio'; import { aliasBank, registerSynthSounds, registerZZFXSounds, samples, } from '@strudel/webaudio';
import * as core from '@strudel/core'; import * as core from '@strudel/core';
export async function prebake() { export async function prebake() {
@@ -28,6 +28,7 @@ export async function prebake() {
modulesLoading, modulesLoading,
registerSynthSounds(), registerSynthSounds(),
registerZZFXSounds(), registerZZFXSounds(),
registerSamplesFromDB(),
//registerSoundfonts(), //registerSoundfonts(),
// need dynamic import here, because importing @strudel/soundfonts fails on server: // need dynamic import here, because importing @strudel/soundfonts fails on server:
// => getting "window is not defined", as soon as "@strudel/soundfonts" is imported statically // => getting "window is not defined", as soon as "@strudel/soundfonts" is imported statically
@@ -36,9 +37,10 @@ export async function prebake() {
samples(`${ds}/tidal-drum-machines.json`), samples(`${ds}/tidal-drum-machines.json`),
samples(`${ds}/piano.json`), samples(`${ds}/piano.json`),
samples(`${ds}/Dirt-Samples.json`), samples(`${ds}/Dirt-Samples.json`),
samples(`${ds}/uzu-drumkit.json`), samples(`https://raw.githubusercontent.com/tidalcycles/uzu-drumkit/main/strudel.json`),
samples(`${ds}/vcsl.json`), samples(`${ds}/vcsl.json`),
samples(`${ds}/mridangam.json`), samples(`${ds}/mridangam.json`),
registerSamplesFromDB(),
]); ]);
aliasBank(`${ts}/tidal-drum-machines-alias.json`); aliasBank(`${ts}/tidal-drum-machines-alias.json`);
+54 -41
View File
@@ -216,50 +216,63 @@ 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 handler = getSamplesPrefixHandler(sampleMap);
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('/'); const base = sampleMap.split('/').slice(0, -1).join('/');
if (typeof fetch === 'undefined') { const savedJSON = localStorage.getItem(sampleMap)
// skip fetch when in node / testing if (savedJSON) {
return; const json = JSON.parse(savedJSON)
samples(json, baseUrl || json._base || base, options)
} else {
// check if custom prefix handler
const handler = getSamplesPrefixHandler(sampleMap);
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;
}
if (typeof fetch === 'undefined') {
// skip fetch when in node / testing
return;
}
return fetch(sampleMap)
.then((res) => res.json())
.then((json) => {
localStorage.setItem(sampleMap, JSON.stringify(json))
samples(json, baseUrl || json._base || base, options)
})
.catch((error) => {
console.error(error);
throw new Error(`error loading "${sampleMap}"`);
});
} }
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(
-76
View File
@@ -1,76 +0,0 @@
{
"_base": "https://raw.githubusercontent.com/tidalcycles/uzu-drumkit/main/",
"bd": [
"bd/10_bd_switchangel.wav",
"bd/11_bd_mot4i.wav",
"bd/12_bd_mot4i.wav",
"bd/13_bd_mot4i.wav",
"bd/14_bd_switchangel.wav",
"bd/15_bd_switchangel.wav",
"bd/16_bd_switchangel.wav",
"bd/17_bd_switchangel.wav"
],
"brk": [
"brk/10_break_amen_pprocessed.wav"
],
"cb": [
"cb/10_perc_switchangel.wav"
],
"cp": [
"cp/10_cp_switchangel.wav",
"cp/11_cp_mot4i.wav"
],
"cr": [
"cr/10_cr_switchangel.wav",
"cr/11_cr_mot4i.wav"
],
"hh": [
"hh/10_hh_switchangel.wav",
"hh/11_hh_mot4i.wav",
"hh/12_hh_switchangel.wav",
"hh/13_hh_switchangel.wav",
"hh/14_hh_mot4i.wav"
],
"ht": [
"ht/10_ht_mot4i.wav"
],
"lt": [
"lt/10_lt_mot4i.wav"
],
"misc": [
"misc/10_misc_switchangel_ludens.wav",
"misc/11_misc_switchangel_ludens.wav",
"misc/12_misc_switchangel_ludens.wav",
"misc/13_misc_switchangel_ludens.wav",
"misc/14_misc_switchangel_ludens.wav"
],
"mt": [
"mt/10_mt_mot4i.wav"
],
"oh": [
"oh/10_oh_switchangel.wav",
"oh/11_oh_switchangel.wav",
"oh/12_oh_switchangel.wav",
"oh/13_oh_switchangel.wav"
],
"rd": [
"rd/10_rd_switchangel.wav"
],
"rim": [
"rim/10_rim_switchangel.wav",
"rim/11_rim_switch_angel.wav"
],
"sd": [
"sd/10_sd_switchangel-bounce-2.wav",
"sd/11_sd_switchangel_3.wav",
"sd/12_sd_switchangel_2.wav",
"sd/13_sd_switchangel_2.wav",
"sd/14_sd.wav"
],
"sh": [
"sh/10_sh_switchangel.wav"
],
"tb": [
"tb/10_tb.wav"
]
}
@@ -1,7 +1,7 @@
import { Fragment, useEffect } from 'react'; import { Fragment, useEffect } from 'react';
import React, { useMemo, useState } from 'react'; import { useMemo, useState } from 'react';
import { isAudioFile, readDir, dir, playFile } from '../../files.mjs'; import { readDir, dir, playFile } from '../../files.mjs';
import { isAudioFile } from '@strudel/core';
export function FilesTab() { export function FilesTab() {
const [path, setPath] = useState([]); const [path, setPath] = useState([]);
useEffect(() => { useEffect(() => {
@@ -1,5 +1,5 @@
import React, { useCallback, useState } from 'react'; import React, { useCallback, useState } from 'react';
import { registerSamplesFromDB, uploadSamplesToDB, userSamplesDBConfig } from '../../idbutils.mjs'; import { registerSamplesFromDB, uploadSamplesToDB, userSamplesDBConfig } from '@strudel/core';
//choose a directory to locally import samples //choose a directory to locally import samples
export default function ImportSoundsButton({ onComplete }) { export default function ImportSoundsButton({ onComplete }) {
+1 -2
View File
@@ -75,8 +75,7 @@ export const walkFileTree = (node, fn) => {
} }
}; };
export const isAudioFile = (filename) =>
['wav', 'mp3', 'flac', 'ogg', 'm4a', 'aac'].includes(filename.split('.').slice(-1)[0]);
function uint8ArrayToDataURL(uint8Array) { function uint8ArrayToDataURL(uint8Array) {
const blob = new Blob([uint8Array], { type: 'audio/*' }); const blob = new Blob([uint8Array], { type: 'audio/*' });
+146 -144
View File
@@ -1,156 +1,158 @@
import { Pattern, noteToMidi, valueToMidi } from '@strudel/core'; import { Pattern, noteToMidi, valueToMidi } from '@strudel/core';
import { aliasBank, registerSynthSounds, registerZZFXSounds, samples } from '@strudel/webaudio'; import { aliasBank, registerSynthSounds, registerZZFXSounds, samples } from '@strudel/webaudio';
import { registerSamplesFromDB } from './idbutils.mjs'; import { registerSamplesFromDB } from '@strudel/core'
import './piano.mjs'; import './piano.mjs';
import './files.mjs'; import './files.mjs';
const { BASE_URL } = import.meta.env; const { BASE_URL } = import.meta.env;
const baseNoTrailing = BASE_URL.endsWith('/') ? BASE_URL.slice(0, -1) : BASE_URL; const baseNoTrailing = BASE_URL.endsWith('/') ? BASE_URL.slice(0, -1) : BASE_URL;
export async function prebake() { // export async function prebake() {
// https://archive.org/details/SalamanderGrandPianoV3 // // https://archive.org/details/SalamanderGrandPianoV3
// License: CC-by http://creativecommons.org/licenses/by/3.0/ Author: Alexander Holm // // License: CC-by http://creativecommons.org/licenses/by/3.0/ Author: Alexander Holm
await Promise.all([
registerSynthSounds(),
registerZZFXSounds(), // await Promise.all([
registerSamplesFromDB(), // registerSynthSounds(),
//registerSoundfonts(), // registerZZFXSounds(),
// need dynamic import here, because importing @strudel/soundfonts fails on server: // registerSamplesFromDB(),
// => getting "window is not defined", as soon as "@strudel/soundfonts" is imported statically // //registerSoundfonts(),
// seems to be a problem with soundfont2 // // need dynamic import here, because importing @strudel/soundfonts fails on server:
import('@strudel/soundfonts').then(({ registerSoundfonts }) => registerSoundfonts()), // // => getting "window is not defined", as soon as "@strudel/soundfonts" is imported statically
samples(`${baseNoTrailing}/piano.json`, undefined, { prebake: true }), // // seems to be a problem with soundfont2
// https://github.com/sgossner/VCSL/ // import('@strudel/soundfonts').then(({ registerSoundfonts }) => registerSoundfonts()),
// https://api.github.com/repositories/126427031/contents/ // samples(`${baseNoTrailing}/piano.json`, undefined, { prebake: true }),
// LICENSE: CC0 general-purpose // // https://github.com/sgossner/VCSL/
samples(`${baseNoTrailing}/vcsl.json`, 'github:sgossner/VCSL/master/', { prebake: true }), // // https://api.github.com/repositories/126427031/contents/
samples(`${baseNoTrailing}/tidal-drum-machines.json`, 'github:ritchse/tidal-drum-machines/main/machines/', { // // LICENSE: CC0 general-purpose
prebake: true, // samples(`${baseNoTrailing}/vcsl.json`, 'github:sgossner/VCSL/master/', { prebake: true }),
tag: 'drum-machines', // samples(`${baseNoTrailing}/tidal-drum-machines.json`, 'github:ritchse/tidal-drum-machines/main/machines/', {
}), // prebake: true,
samples(`${baseNoTrailing}/uzu-drumkit.json`, undefined, { // tag: 'drum-machines',
prebake: true, // }),
tag: 'drum-machines', // samples(`https://raw.githubusercontent.com/tidalcycles/uzu-drumkit/main/strudel.json`, undefined, {
}), // prebake: true,
samples(`${baseNoTrailing}/mridangam.json`, undefined, { prebake: true, tag: 'drum-machines' }), // tag: 'drum-machines',
samples( // }),
{ // samples(`${baseNoTrailing}/mridangam.json`, undefined, { prebake: true, tag: 'drum-machines' }),
casio: ['casio/high.wav', 'casio/low.wav', 'casio/noise.wav'], // samples(
crow: ['crow/000_crow.wav', 'crow/001_crow2.wav', 'crow/002_crow3.wav', 'crow/003_crow4.wav'], // {
insect: [ // casio: ['casio/high.wav', 'casio/low.wav', 'casio/noise.wav'],
'insect/000_everglades_conehead.wav', // crow: ['crow/000_crow.wav', 'crow/001_crow2.wav', 'crow/002_crow3.wav', 'crow/003_crow4.wav'],
'insect/001_robust_shieldback.wav', // insect: [
'insect/002_seashore_meadow_katydid.wav', // 'insect/000_everglades_conehead.wav',
], // 'insect/001_robust_shieldback.wav',
wind: [ // 'insect/002_seashore_meadow_katydid.wav',
'wind/000_wind1.wav', // ],
'wind/001_wind10.wav', // wind: [
'wind/002_wind2.wav', // 'wind/000_wind1.wav',
'wind/003_wind3.wav', // 'wind/001_wind10.wav',
'wind/004_wind4.wav', // 'wind/002_wind2.wav',
'wind/005_wind5.wav', // 'wind/003_wind3.wav',
'wind/006_wind6.wav', // 'wind/004_wind4.wav',
'wind/007_wind7.wav', // 'wind/005_wind5.wav',
'wind/008_wind8.wav', // 'wind/006_wind6.wav',
'wind/009_wind9.wav', // 'wind/007_wind7.wav',
], // 'wind/008_wind8.wav',
jazz: [ // 'wind/009_wind9.wav',
'jazz/000_BD.wav', // ],
'jazz/001_CB.wav', // jazz: [
'jazz/002_FX.wav', // 'jazz/000_BD.wav',
'jazz/003_HH.wav', // 'jazz/001_CB.wav',
'jazz/004_OH.wav', // 'jazz/002_FX.wav',
'jazz/005_P1.wav', // 'jazz/003_HH.wav',
'jazz/006_P2.wav', // 'jazz/004_OH.wav',
'jazz/007_SN.wav', // 'jazz/005_P1.wav',
], // 'jazz/006_P2.wav',
metal: [ // 'jazz/007_SN.wav',
'metal/000_0.wav', // ],
'metal/001_1.wav', // metal: [
'metal/002_2.wav', // 'metal/000_0.wav',
'metal/003_3.wav', // 'metal/001_1.wav',
'metal/004_4.wav', // 'metal/002_2.wav',
'metal/005_5.wav', // 'metal/003_3.wav',
'metal/006_6.wav', // 'metal/004_4.wav',
'metal/007_7.wav', // 'metal/005_5.wav',
'metal/008_8.wav', // 'metal/006_6.wav',
'metal/009_9.wav', // 'metal/007_7.wav',
], // 'metal/008_8.wav',
east: [ // 'metal/009_9.wav',
'east/000_nipon_wood_block.wav', // ],
'east/001_ohkawa_mute.wav', // east: [
'east/002_ohkawa_open.wav', // 'east/000_nipon_wood_block.wav',
'east/003_shime_hi.wav', // 'east/001_ohkawa_mute.wav',
'east/004_shime_hi_2.wav', // 'east/002_ohkawa_open.wav',
'east/005_shime_mute.wav', // 'east/003_shime_hi.wav',
'east/006_taiko_1.wav', // 'east/004_shime_hi_2.wav',
'east/007_taiko_2.wav', // 'east/005_shime_mute.wav',
'east/008_taiko_3.wav', // 'east/006_taiko_1.wav',
], // 'east/007_taiko_2.wav',
space: [ // 'east/008_taiko_3.wav',
'space/000_0.wav', // ],
'space/001_1.wav', // space: [
'space/002_11.wav', // 'space/000_0.wav',
'space/003_12.wav', // 'space/001_1.wav',
'space/004_13.wav', // 'space/002_11.wav',
'space/005_14.wav', // 'space/003_12.wav',
'space/006_15.wav', // 'space/004_13.wav',
'space/007_16.wav', // 'space/005_14.wav',
'space/008_17.wav', // 'space/006_15.wav',
'space/009_18.wav', // 'space/007_16.wav',
'space/010_2.wav', // 'space/008_17.wav',
'space/011_3.wav', // 'space/009_18.wav',
'space/012_4.wav', // 'space/010_2.wav',
'space/013_5.wav', // 'space/011_3.wav',
'space/014_6.wav', // 'space/012_4.wav',
'space/015_7.wav', // 'space/013_5.wav',
'space/016_8.wav', // 'space/014_6.wav',
'space/017_9.wav', // 'space/015_7.wav',
], // 'space/016_8.wav',
numbers: [ // 'space/017_9.wav',
'numbers/0.wav', // ],
'numbers/1.wav', // numbers: [
'numbers/2.wav', // 'numbers/0.wav',
'numbers/3.wav', // 'numbers/1.wav',
'numbers/4.wav', // 'numbers/2.wav',
'numbers/5.wav', // 'numbers/3.wav',
'numbers/6.wav', // 'numbers/4.wav',
'numbers/7.wav', // 'numbers/5.wav',
'numbers/8.wav', // 'numbers/6.wav',
], // 'numbers/7.wav',
num: [ // 'numbers/8.wav',
'num/00.wav', // ],
'num/01.wav', // num: [
'num/02.wav', // 'num/00.wav',
'num/03.wav', // 'num/01.wav',
'num/04.wav', // 'num/02.wav',
'num/05.wav', // 'num/03.wav',
'num/06.wav', // 'num/04.wav',
'num/07.wav', // 'num/05.wav',
'num/08.wav', // 'num/06.wav',
'num/09.wav', // 'num/07.wav',
'num/10.wav', // 'num/08.wav',
'num/11.wav', // 'num/09.wav',
'num/12.wav', // 'num/10.wav',
'num/13.wav', // 'num/11.wav',
'num/14.wav', // 'num/12.wav',
'num/15.wav', // 'num/13.wav',
'num/16.wav', // 'num/14.wav',
'num/17.wav', // 'num/15.wav',
'num/18.wav', // 'num/16.wav',
'num/19.wav', // 'num/17.wav',
'num/20.wav', // 'num/18.wav',
], // 'num/19.wav',
}, // 'num/20.wav',
'github:tidalcycles/dirt-samples', // ],
{ // },
prebake: true, // 'github:tidalcycles/dirt-samples',
}, // {
), // prebake: true,
]); // },
// ),
// ]);
aliasBank(`${baseNoTrailing}/tidal-drum-machines-alias.json`); // aliasBank(`${baseNoTrailing}/tidal-drum-machines-alias.json`);
} // }
const maxPan = noteToMidi('C8'); const maxPan = noteToMidi('C8');
const panwidth = (pan, width) => pan * width + (1 - width) / 2; const panwidth = (pan, width) => pan * width + (1 - width) / 2;
+1 -1
View File
@@ -31,7 +31,7 @@ import {
import { superdirtOutput } from '@strudel/osc/superdirtoutput'; import { superdirtOutput } from '@strudel/osc/superdirtoutput';
import { audioEngineTargets } from '../settings.mjs'; import { audioEngineTargets } from '../settings.mjs';
import { useStore } from '@nanostores/react'; import { useStore } from '@nanostores/react';
import { prebake } from './prebake.mjs'; import { prebake } from '../../../packages/repl/prebake.mjs'
import { getRandomTune, initCode, loadModules, shareCode } from './util.mjs'; import { getRandomTune, initCode, loadModules, shareCode } from './util.mjs';
import './Repl.css'; import './Repl.css';
import { setInterval, clearInterval } from 'worker-timers'; import { setInterval, clearInterval } from 'worker-timers';