mirror of
https://github.com/eliasstepanik/strudel-docker.git
synced 2026-01-22 19:18:31 +00:00
register soundfonts as sounds too
This commit is contained in:
parent
6f5d096e6d
commit
ac148b2f32
@ -1,4 +1,6 @@
|
|||||||
import { toMidi } from '@strudel.cycles/core';
|
import { toMidi } from '@strudel.cycles/core';
|
||||||
|
import { getAudioContext, registerSound } from '@strudel.cycles/webaudio';
|
||||||
|
import { instruments } from './list.mjs';
|
||||||
|
|
||||||
let loadCache = {};
|
let loadCache = {};
|
||||||
async function loadFont(name) {
|
async function loadFont(name) {
|
||||||
@ -8,7 +10,6 @@ async function loadFont(name) {
|
|||||||
const load = async () => {
|
const load = async () => {
|
||||||
// TODO: make soundfont source configurable
|
// TODO: make soundfont source configurable
|
||||||
const url = `https://felixroos.github.io/webaudiofontdata/sound/${name}.js`;
|
const url = `https://felixroos.github.io/webaudiofontdata/sound/${name}.js`;
|
||||||
console.log('load font', name, url);
|
|
||||||
const preset = await fetch(url).then((res) => res.text());
|
const preset = await fetch(url).then((res) => res.text());
|
||||||
let [_, data] = preset.split('={');
|
let [_, data] = preset.split('={');
|
||||||
return eval('{' + data);
|
return eval('{' + data);
|
||||||
@ -114,3 +115,24 @@ async function getBuffer(zone, audioContext) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function registerSoundfonts() {
|
||||||
|
instruments.forEach((instrument) => {
|
||||||
|
registerSound(
|
||||||
|
instrument,
|
||||||
|
async (time, value, onended) => {
|
||||||
|
const { note, n } = value;
|
||||||
|
const ctx = getAudioContext();
|
||||||
|
const bufferSource = await getFontBufferSource(instrument, note || n, ctx);
|
||||||
|
bufferSource.start(time);
|
||||||
|
const stop = (time) => bufferSource.stop(time);
|
||||||
|
bufferSource.onended = () => {
|
||||||
|
bufferSource.disconnect();
|
||||||
|
onended();
|
||||||
|
};
|
||||||
|
return { node: bufferSource, stop };
|
||||||
|
},
|
||||||
|
{ type: 'soundfont', prebake: true },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { getFontBufferSource } from './fontloader.mjs';
|
import { getFontBufferSource, registerSoundfonts } from './fontloader.mjs';
|
||||||
import * as soundfontList from './list.mjs';
|
import * as soundfontList from './list.mjs';
|
||||||
import { startPresetNote } from 'sfumato';
|
import { startPresetNote } from 'sfumato';
|
||||||
import { loadSoundfont } from './sfumato.mjs';
|
import { loadSoundfont } from './sfumato.mjs';
|
||||||
|
|
||||||
export { loadSoundfont, startPresetNote, getFontBufferSource, soundfontList };
|
export { loadSoundfont, startPresetNote, getFontBufferSource, soundfontList, registerSoundfonts };
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,5 @@
|
|||||||
import { Pattern, getPlayableNoteValue, toMidi } from '@strudel.cycles/core';
|
import { Pattern, getPlayableNoteValue, toMidi } from '@strudel.cycles/core';
|
||||||
import { getAudioContext } from '@strudel.cycles/webaudio';
|
import { getAudioContext, registerSound } from '@strudel.cycles/webaudio';
|
||||||
import { loadSoundfont as _loadSoundfont, startPresetNote } from 'sfumato';
|
import { loadSoundfont as _loadSoundfont, startPresetNote } from 'sfumato';
|
||||||
|
|
||||||
Pattern.prototype.soundfont = function (sf, n = 0) {
|
Pattern.prototype.soundfont = function (sf, n = 0) {
|
||||||
@ -21,5 +21,29 @@ export function loadSoundfont(url) {
|
|||||||
}
|
}
|
||||||
const sf = _loadSoundfont(url);
|
const sf = _loadSoundfont(url);
|
||||||
soundfontCache.set(url, sf);
|
soundfontCache.set(url, sf);
|
||||||
|
/*sf.then((font) => {
|
||||||
|
font.presets.forEach((preset) => {
|
||||||
|
console.log('preset', preset.header.name);
|
||||||
|
registerSound(
|
||||||
|
preset.header.name.replaceAll(' ', '_'),
|
||||||
|
(time, value, onended) => {
|
||||||
|
const ctx = getAudioContext();
|
||||||
|
let { note } = value; // freq ?
|
||||||
|
|
||||||
|
const p = font.presets.find((p) => p.header.name === preset.header.name);
|
||||||
|
|
||||||
|
if (!p) {
|
||||||
|
throw new Error('preset not found');
|
||||||
|
}
|
||||||
|
const deadline = time; // - ctx.currentTime;
|
||||||
|
const args = [ctx, p, toMidi(note), deadline];
|
||||||
|
const stop = startPresetNote(...args);
|
||||||
|
return { node: undefined, stop };
|
||||||
|
},
|
||||||
|
{ type: 'soundfont' },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
//console.log('f', f);
|
||||||
|
});*/
|
||||||
return sf;
|
return sf;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -251,23 +251,3 @@ export async function onTriggerSample(t, value, onended, bank) {
|
|||||||
|
|
||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*const getSoundfontKey = (s) => {
|
|
||||||
if (!globalThis.soundfontList) {
|
|
||||||
// soundfont package not loaded
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (globalThis.soundfontList?.instruments?.includes(s)) {
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
// check if s is one of the soundfonts, which are loaded into globalThis, to avoid coupling both packages
|
|
||||||
const nameIndex = globalThis.soundfontList?.instrumentNames?.indexOf(s);
|
|
||||||
// convert number nameIndex (0-128) to 3 digit string (001-128)
|
|
||||||
const name = nameIndex < 10 ? `00${nameIndex}` : nameIndex < 100 ? `0${nameIndex}` : nameIndex;
|
|
||||||
if (nameIndex !== -1) {
|
|
||||||
// TODO: indices of instrumentNames do not seem to match instruments
|
|
||||||
return globalThis.soundfontList.instruments.find((instrument) => instrument.startsWith(name));
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
};*/
|
|
||||||
// bufferSource = await globalThis.getFontBufferSource(soundfont, note || n, ac, freq);
|
|
||||||
|
|||||||
@ -233,9 +233,18 @@ function SoundsTab() {
|
|||||||
if (!sounds) {
|
if (!sounds) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
if (soundsFilter === 'hideDefaults') {
|
if (soundsFilter === 'user') {
|
||||||
return Object.entries(sounds).filter(([_, { data }]) => !data.prebake);
|
return Object.entries(sounds).filter(([_, { data }]) => !data.prebake);
|
||||||
}
|
}
|
||||||
|
if (soundsFilter === 'samples') {
|
||||||
|
return Object.entries(sounds).filter(([_, { data }]) => data.type === 'sample');
|
||||||
|
}
|
||||||
|
if (soundsFilter === 'synths') {
|
||||||
|
return Object.entries(sounds).filter(([_, { data }]) => data.type === 'synth');
|
||||||
|
}
|
||||||
|
if (soundsFilter === 'soundfonts') {
|
||||||
|
return Object.entries(sounds).filter(([_, { data }]) => data.type === 'soundfont');
|
||||||
|
}
|
||||||
return Object.entries(sounds);
|
return Object.entries(sounds);
|
||||||
}, [sounds, soundsFilter]);
|
}, [sounds, soundsFilter]);
|
||||||
// holds mutable ref to current triggered sound
|
// holds mutable ref to current triggered sound
|
||||||
@ -249,16 +258,21 @@ function SoundsTab() {
|
|||||||
<ButtonGroup
|
<ButtonGroup
|
||||||
value={soundsFilter}
|
value={soundsFilter}
|
||||||
onChange={(value) => settingsMap.setKey('soundsFilter', value)}
|
onChange={(value) => settingsMap.setKey('soundsFilter', value)}
|
||||||
items={{ all: 'All', hideDefaults: 'Hide Defaults' }}
|
items={{ samples: 'Samples', synths: 'Synths', soundfonts: 'Soundfonts', user: 'Custom' }}
|
||||||
></ButtonGroup>
|
></ButtonGroup>
|
||||||
<div className="pt-4 select-none">
|
<div className="pt-4 ">
|
||||||
{soundEntries.map(([name, { data, onTrigger }]) => (
|
{soundEntries.map(([name, { data, onTrigger }]) => (
|
||||||
<span
|
<span
|
||||||
key={name}
|
key={name}
|
||||||
className="cursor-pointer hover:opacity-50"
|
className="cursor-pointer hover:opacity-50"
|
||||||
onMouseDown={async () => {
|
onMouseDown={async () => {
|
||||||
const ctx = getAudioContext();
|
const ctx = getAudioContext();
|
||||||
const params = { freq: 220, s: name, clip: 1, release: 0.5 };
|
const params = {
|
||||||
|
note: ['synth', 'soundfont'].includes(data.type) ? 'a3' : undefined,
|
||||||
|
s: name,
|
||||||
|
clip: 1,
|
||||||
|
release: 0.5,
|
||||||
|
};
|
||||||
const time = ctx.currentTime + 0.05;
|
const time = ctx.currentTime + 0.05;
|
||||||
const onended = () => trigRef.current?.node?.disconnect();
|
const onended = () => trigRef.current?.node?.disconnect();
|
||||||
trigRef.current = Promise.resolve(onTrigger(time, params, onended));
|
trigRef.current = Promise.resolve(onTrigger(time, params, onended));
|
||||||
@ -272,7 +286,7 @@ function SoundsTab() {
|
|||||||
{data?.type === 'sample' ? `(${getSamples(data.samples)})` : ''}
|
{data?.type === 'sample' ? `(${getSamples(data.samples)})` : ''}
|
||||||
</span>
|
</span>
|
||||||
))}
|
))}
|
||||||
{!soundEntries.length ? 'No Sounds' : ''}
|
{!soundEntries.length ? 'No custom sounds loaded in this pattern (yet).' : ''}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
import { Pattern, toMidi, valueToMidi } from '@strudel.cycles/core';
|
import { Pattern, toMidi, valueToMidi } from '@strudel.cycles/core';
|
||||||
|
import { registerSoundfonts } from '@strudel.cycles/soundfonts';
|
||||||
import { registerSynthSounds, samples } from '@strudel.cycles/webaudio';
|
import { registerSynthSounds, samples } from '@strudel.cycles/webaudio';
|
||||||
|
|
||||||
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
|
||||||
registerSynthSounds();
|
registerSynthSounds();
|
||||||
|
registerSoundfonts();
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
samples(`./piano.json`, `./piano/`, { prebake: true }),
|
samples(`./piano.json`, `./piano/`, { prebake: true }),
|
||||||
// https://github.com/sgossner/VCSL/
|
// https://github.com/sgossner/VCSL/
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user