Merge pull request #1245 from TodePond/lu/add-drum-suffixes

Add bank aliasing and case insensitivity
This commit is contained in:
Felix Roos 2025-02-01 22:32:59 +01:00 committed by GitHub
commit 3d37478577
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 154 additions and 4 deletions

View File

@ -11,3 +11,5 @@ pnpm-lock.yaml
pnpm-workspace.yaml pnpm-workspace.yaml
**/dev-dist **/dev-dist
website/.astro website/.astro
!tidal-drum-machines.json
!tidal-drum-machines-alias.json

View File

@ -94,3 +94,15 @@ or
The `.editor` property on the `strudel-editor` web component gives you the instance of [StrudelMirror](https://github.com/tidalcycles/strudel/blob/a46bd9b36ea7d31c9f1d3fca484297c7da86893f/packages/codemirror/codemirror.mjs#L124) that runs the REPL. The `.editor` property on the `strudel-editor` web component gives you the instance of [StrudelMirror](https://github.com/tidalcycles/strudel/blob/a46bd9b36ea7d31c9f1d3fca484297c7da86893f/packages/codemirror/codemirror.mjs#L124) that runs the REPL.
For example, you could use `setCode` to change the code from the outside, `start` / `stop` to toggle playback or `evaluate` to evaluate the code. For example, you could use `setCode` to change the code from the outside, `start` / `stop` to toggle playback or `evaluate` to evaluate the code.
## Development: How to Test
```sh
cd packages/repl
pnpm build
cd ../.. # back to root folder
# edit ./examples/buildless/web-component-no-iframe.html
# use <script src="/packages/repl/dist/index.js"></script>
pnpx serve # from root folder
# go to http://localhost:3000/examples/buildless/web-component-no-iframe
```

View File

@ -1,5 +1,5 @@
import { noteToMidi, valueToMidi, Pattern, evalScope } from '@strudel/core'; import { noteToMidi, valueToMidi, Pattern, evalScope } from '@strudel/core';
import { 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() {
@ -21,6 +21,9 @@ export async function prebake() {
); );
// load samples // load samples
const ds = 'https://raw.githubusercontent.com/felixroos/dough-samples/main/'; const ds = 'https://raw.githubusercontent.com/felixroos/dough-samples/main/';
// TODO: move this onto the strudel repo
const ts = 'https://raw.githubusercontent.com/todepond/samples/main/';
await Promise.all([ await Promise.all([
modulesLoading, modulesLoading,
registerSynthSounds(), registerSynthSounds(),
@ -36,6 +39,8 @@ export async function prebake() {
samples(`${ds}/EmuSP12.json`), samples(`${ds}/EmuSP12.json`),
samples(`${ds}/vcsl.json`), samples(`${ds}/vcsl.json`),
]); ]);
aliasBank(`${ts}/tidal-drum-machines-alias.json`);
} }
const maxPan = noteToMidi('C8'); const maxPan = noteToMidi('C8');

View File

@ -17,11 +17,72 @@ import { loadBuffer } from './sampler.mjs';
export const soundMap = map(); export const soundMap = map();
export function registerSound(key, onTrigger, data = {}) { export function registerSound(key, onTrigger, data = {}) {
soundMap.setKey(key, { onTrigger, data }); soundMap.setKey(key.toLowerCase(), { onTrigger, data });
}
function aliasBankMap(aliasMap) {
// Make all bank keys lower case for case insensitivity
for (const key in aliasMap) {
aliasMap[key.toLowerCase()] = aliasMap[key];
}
// Look through every sound...
const soundDictionary = soundMap.get();
for (const key in soundDictionary) {
// Check if the sound is part of a bank...
const [bank, suffix] = key.split('_');
if (!suffix) continue;
// Check if the bank is aliased...
const aliasValue = aliasMap[bank];
if (aliasValue) {
if (typeof aliasValue === 'string') {
// Alias a single alias
soundDictionary[`${aliasValue}_${suffix}`.toLowerCase()] = soundDictionary[key];
} else if (Array.isArray(aliasValue)) {
// Alias multiple aliases
for (const alias of aliasValue) {
soundDictionary[`${alias}_${suffix}`.toLowerCase()] = soundDictionary[key];
}
}
}
}
// Update the sound map!
// We need to destructure here to trigger the update
soundMap.set({ ...soundDictionary });
}
async function aliasBankPath(path) {
const response = await fetch(path);
const aliasMap = await response.json();
aliasBankMap(aliasMap);
}
/**
* Register an alias for a bank of sounds.
* Optionally accepts a single argument map of bank aliases.
* Optionally accepts a single argument string of a path to a JSON file containing bank aliases.
* @param {string} bank - The bank to alias
* @param {string} alias - The alias to use for the bank
*/
export async function aliasBank(...args) {
switch (args.length) {
case 1:
if (typeof args[0] === 'string') {
return aliasBankPath(args[0]);
} else {
return aliasBankMap(args[0]);
}
case 2:
return aliasBankMap({ [args[0]]: args[1] });
default:
throw new Error('aliasMap expects 1 or 2 arguments, received ' + args.length);
}
} }
export function getSound(s) { export function getSound(s) {
return soundMap.get()[s]; return soundMap.get()[s.toLowerCase()];
} }
const defaultDefaultValues = { const defaultDefaultValues = {

View File

@ -0,0 +1,68 @@
{
"AJKPercusyn": "Percysyn",
"AkaiLinn": "Linn",
"AkaiMPC60": "MPC60",
"AkaiXR10": "XR10",
"AlesisHR16": "HR16",
"AlesisSR16": "SR16",
"BossDR110": "DR110",
"BossDR220": "DR220",
"BossDR55": "DR55",
"BossDR550": "DR550",
"CasioRZ1": "RZ1",
"CasioSK1": "SK1",
"CasioVL1": "VL1",
"DoepferMS404": "MS404",
"EmuDrumulator": "Drumulator",
"EmuSP12": "SP12",
"KorgDDM110": "DDM110",
"KorgKPR77": "KPR77",
"KorgKR55": "KR55",
"KorgKRZ": "KRZ",
"KorgM1": "M1",
"KorgMinipops": "Minipops",
"KorgPoly800": "Poly800",
"KorgT3": "T3",
"Linn9000": "9000",
"LinnLM1": "LM1",
"LinnLM2": "LM2",
"MoogConcertMateMG1": "ConcertMateMG1",
"OberheimDMX": "DMX",
"RhodesPolaris": "Polaris",
"RhythmAce": "Ace",
"RolandCompurhythm1000": "Compurhythm1000",
"RolandCompurhythm78": "Compurhythm78",
"RolandCompurhythm8000": "Compurhythm8000",
"RolandD110": "D110",
"RolandD70": "D70",
"RolandDDR30": "DDR30",
"RolandJD990": "JD990",
"RolandMC202": "MC202",
"RolandMC303": "MC303",
"RolandMT32": "MT32",
"RolandR8": "R8",
"RolandS50": "S50",
"RolandSH09": "SH09",
"RolandSystem100": "System100",
"RolandTR505": "TR505",
"RolandTR606": "TR606",
"RolandTR626": "TR626",
"RolandTR707": "TR707",
"RolandTR727": "TR727",
"RolandTR808": "TR808",
"RolandTR909": "TR909",
"SakataDPM48": "DPM48",
"SequentialCircuitsDrumtracks": "CircuitsDrumtracks",
"SequentialCircuitsTom": "CircuitsTom",
"SimmonsSDS400": "SDS400",
"SimmonsSDS5": "SDS5",
"SoundmastersR88": "R88",
"UnivoxMicroRhythmer12": "MicroRhythmer12",
"ViscoSpaceDrum": "SpaceDrum",
"XdrumLM8953": "LM8953",
"YamahaRM50": "RM50",
"YamahaRX21": "RX21",
"YamahaRX5": "RX5",
"YamahaRY30": "RY30",
"YamahaTG33": "TG33"
}

View File

@ -1,5 +1,5 @@
import { Pattern, noteToMidi, valueToMidi } from '@strudel/core'; import { Pattern, noteToMidi, valueToMidi } from '@strudel/core';
import { registerSynthSounds, registerZZFXSounds, samples } from '@strudel/webaudio'; import { aliasBank, registerSynthSounds, registerZZFXSounds, samples } from '@strudel/webaudio';
import { registerSamplesFromDB } from './idbutils.mjs'; import { registerSamplesFromDB } from './idbutils.mjs';
import './piano.mjs'; import './piano.mjs';
import './files.mjs'; import './files.mjs';
@ -121,6 +121,8 @@ export async function prebake() {
}, },
), ),
]); ]);
aliasBank(`${baseNoTrailing}/tidal-drum-machines-alias.json`);
} }
const maxPan = noteToMidi('C8'); const maxPan = noteToMidi('C8');