mirror of
https://codeberg.org/uzu/strudel
synced 2026-07-31 21:27:07 -04:00
Merge remote-tracking branch 'origin/main' into notes-and-numbers
This commit is contained in:
+11
-11
@@ -32,12 +32,12 @@ Pattern.prototype.midi = function (output: string, channel = 1) {
|
||||
}')`
|
||||
);
|
||||
}
|
||||
return this.fmap((value: any) => ({
|
||||
...value,
|
||||
onTrigger: (time: number, event: any) => {
|
||||
value = value.value || value;
|
||||
if (!isNote(value)) {
|
||||
throw new Error('not a note: ' + value);
|
||||
return this._withEvent((event: any) => {
|
||||
const onTrigger = (time: number, event: any) => {
|
||||
let note = event.value;
|
||||
const velocity = event.context?.velocity ?? 0.9;
|
||||
if (!isNote(note)) {
|
||||
throw new Error('not a note: ' + note);
|
||||
}
|
||||
if (!WebMidi.enabled) {
|
||||
throw new Error(`🎹 WebMidi is not enabled. Supported Browsers: https://caniuse.com/?search=webmidi`);
|
||||
@@ -58,14 +58,14 @@ Pattern.prototype.midi = function (output: string, channel = 1) {
|
||||
time = time * 1000 + timingOffset;
|
||||
// const inMs = '+' + (time - Tone.context.currentTime) * 1000;
|
||||
// await enableWebMidi()
|
||||
device.playNote(value, channel, {
|
||||
device.playNote(note, channel, {
|
||||
time,
|
||||
duration: event.duration * 1000 - 5,
|
||||
// velocity: velocity ?? 0.5,
|
||||
velocity: 0.9,
|
||||
velocity,
|
||||
});
|
||||
},
|
||||
}));
|
||||
};
|
||||
return event.setContext({ ...event.context, onTrigger });
|
||||
});
|
||||
};
|
||||
|
||||
export function useWebMidi(props?: any) {
|
||||
|
||||
+44
-12
@@ -17,6 +17,7 @@ import {
|
||||
PluckSynth,
|
||||
Sampler,
|
||||
getDestination,
|
||||
Players,
|
||||
} from 'tone';
|
||||
import { Piano } from '@tonejs/piano';
|
||||
import { getPlayableNoteValue } from '../../util.mjs';
|
||||
@@ -31,17 +32,37 @@ Pattern.prototype.tone = function (instrument) {
|
||||
// instrument.toDestination();
|
||||
return this._withEvent((event) => {
|
||||
const onTrigger = (time, event) => {
|
||||
const note = getPlayableNoteValue(event);
|
||||
// TODO: test if all tonejs instruments can handle freqs
|
||||
if (instrument.constructor.name === 'PluckSynth') {
|
||||
instrument.triggerAttack(note, time);
|
||||
} else if (instrument.constructor.name === 'NoiseSynth') {
|
||||
instrument.triggerAttackRelease(event.duration, time); // noise has no value
|
||||
} else if (instrument.constructor.name === 'Piano') {
|
||||
instrument.keyDown({ note, time, velocity: 0.5 });
|
||||
instrument.keyUp({ note, time: time + event.duration });
|
||||
} else {
|
||||
instrument.triggerAttackRelease(event.value, event.duration, time);
|
||||
let note;
|
||||
let velocity = event.context?.velocity ?? 0.75;
|
||||
switch (instrument.constructor.name) {
|
||||
case 'PluckSynth':
|
||||
note = getPlayableNoteValue(event);
|
||||
instrument.triggerAttack(note, time);
|
||||
break;
|
||||
case 'NoiseSynth':
|
||||
instrument.triggerAttackRelease(event.duration, time); // noise has no value
|
||||
break;
|
||||
case 'Piano':
|
||||
note = getPlayableNoteValue(event);
|
||||
instrument.keyDown({ note, time, velocity: 0.5 });
|
||||
instrument.keyUp({ note, time: time + event.duration, velocity });
|
||||
break;
|
||||
case 'Sampler':
|
||||
note = getPlayableNoteValue(event);
|
||||
instrument.triggerAttackRelease(note, event.duration, time, velocity);
|
||||
break;
|
||||
case 'Players':
|
||||
if (!instrument.has(event.value)) {
|
||||
throw new Error(`name "${event.value}" not defined for players`);
|
||||
}
|
||||
const player = instrument.player(event.value);
|
||||
// velocity ?
|
||||
player.start(time);
|
||||
player.stop(time + event.duration);
|
||||
break;
|
||||
default:
|
||||
note = getPlayableNoteValue(event);
|
||||
instrument.triggerAttackRelease(note, event.duration, time, velocity);
|
||||
}
|
||||
};
|
||||
return event.setContext({ ...event.context, instrument, onTrigger });
|
||||
@@ -60,7 +81,18 @@ export const monosynth = (options) => new MonoSynth(options);
|
||||
export const noise = (options) => new NoiseSynth(options);
|
||||
export const pluck = (options) => new PluckSynth(options);
|
||||
export const polysynth = (options) => new PolySynth(options);
|
||||
export const sampler = (options) => new Sampler(options);
|
||||
export const sampler = (options, baseUrl) =>
|
||||
new Promise((resolve) => {
|
||||
const s = new Sampler(options, () => resolve(s), baseUrl);
|
||||
});
|
||||
export const players = (options, baseUrl = '') => {
|
||||
options = !baseUrl
|
||||
? options
|
||||
: Object.fromEntries(Object.entries(options).map(([key, value]: any) => [key, baseUrl + value]));
|
||||
return new Promise((resolve) => {
|
||||
const s = new Players(options, () => resolve(s));
|
||||
});
|
||||
};
|
||||
export const synth = (options) => new Synth(options);
|
||||
export const piano = async (options = { velocities: 1 }) => {
|
||||
const p = new Piano(options);
|
||||
|
||||
+62
-5
@@ -459,6 +459,63 @@ export const barryHarris = `piano()
|
||||
.tone(p.toDestination()))
|
||||
`;
|
||||
|
||||
export const blippyRhodes = `Promise.all([
|
||||
players({
|
||||
bd: 'samples/tidal/bd/BT0A0D0.wav',
|
||||
sn: 'samples/tidal/sn/ST0T0S3.wav',
|
||||
hh: 'samples/tidal/hh/000_hh3closedhh.wav'
|
||||
}, 'https://loophole-letters.vercel.app/'),
|
||||
sampler({
|
||||
E1: 'samples/rhodes/MK2Md2000.mp3',
|
||||
E2: 'samples/rhodes/MK2Md2012.mp3',
|
||||
E3: 'samples/rhodes/MK2Md2024.mp3',
|
||||
E4: 'samples/rhodes/MK2Md2036.mp3',
|
||||
E5: 'samples/rhodes/MK2Md2048.mp3',
|
||||
E6: 'samples/rhodes/MK2Md2060.mp3',
|
||||
E7: 'samples/rhodes/MK2Md2072.mp3'
|
||||
}, 'https://loophole-letters.vercel.app/')
|
||||
])
|
||||
.then(([drums, rhodes])=>{
|
||||
const delay = new FeedbackDelay(1/12, .4).chain(vol(0.3), out());
|
||||
rhodes = rhodes.chain(vol(0.5).connect(delay), out());
|
||||
const bass = synth(osc('sawtooth8')).chain(vol(.5),out());
|
||||
const scales = ['C major', 'C mixolydian', 'F lydian', ['F minor',slowcat('Db major','Db mixolydian')]];
|
||||
const t = x => x.scale(sequence(...scales).slow(4));
|
||||
return stack(
|
||||
"<bd sn> <hh hh*2 hh*3>".tone(drums.chain(out())),
|
||||
"<g4 c5 a4 [ab4 <eb5 f5>]>".apply(t).struct("x*8").apply(scaleTranspose("0 [-5,-2] -7 [-9,-2]")).legato(.2).slow(2).tone(rhodes),
|
||||
//"<C^7 C7 F^7 [Fm7 <Db^7 Db7>]>".slow(2).voicings().struct("~ x").legato(.25).tone(rhodes),
|
||||
"<c2 c3 f2 [[F2 C2] db2]>".legato("<1@3 [.3 1]>").slow(2).tone(bass),
|
||||
).fast(3/2)
|
||||
})`;
|
||||
|
||||
export const wavyKalimba = `sampler({
|
||||
C5: 'https://freesound.org/data/previews/536/536549_11935698-lq.mp3'
|
||||
}).then((kalimba)=>{
|
||||
const delay = new FeedbackDelay(1/3, .5).chain(vol(.2), out());
|
||||
kalimba = kalimba.chain(vol(0.6).connect(delay),out());
|
||||
const scales = sequence('C major', 'C mixolydian', 'F lydian', ['F minor', 'Db major']).slow(4);
|
||||
return stack(
|
||||
"[0 2 4 6 9 2 0 -2]*3"
|
||||
.add("<0 2>/4")
|
||||
.scale(scales)
|
||||
.struct("x*8")
|
||||
.velocity("<.8 .3 .6>*8")
|
||||
.slow(2)
|
||||
.tone(kalimba),
|
||||
"<c2 c2 f2 [[F2 C2] db2]>"
|
||||
.scale(scales)
|
||||
.scaleTranspose("[0 <2 4>]*2")
|
||||
.struct("x*4")
|
||||
.velocity("<.8 .5>*4")
|
||||
.velocity(0.8)
|
||||
.slow(2)
|
||||
.tone(kalimba)
|
||||
)
|
||||
.legato("<.4 .8 1 1.2 1.4 1.6 1.8 2>/8")
|
||||
.fast(1)
|
||||
})`;
|
||||
|
||||
export const jemblung = `() => {
|
||||
const delay = new FeedbackDelay(1/8, .6).chain(vol(0.15), out());
|
||||
const snare = noise({type:'white',...adsr(0,0.2,0)}).chain(lowpass(5000),vol(1.8),out());
|
||||
@@ -467,12 +524,12 @@ export const jemblung = `() => {
|
||||
stack(
|
||||
"0 1 4 [3!2 5]".edit(
|
||||
// chords
|
||||
x=>x.add("0,3").len("0.05!3 0.02"),
|
||||
x=>x.add("0,3").duration("0.05!3 0.02"),
|
||||
// bass
|
||||
x=>x.add("-8").struct("x*8").len(0.1)
|
||||
x=>x.add("-8").struct("x*8").duration(0.1)
|
||||
),
|
||||
// melody
|
||||
"12 11*3 12 ~".len(0.005)
|
||||
"12 11*3 12 ~".duration(0.005)
|
||||
)
|
||||
.add("<0 1>")
|
||||
.tune("jemblung2")
|
||||
@@ -480,10 +537,10 @@ export const jemblung = `() => {
|
||||
//.mul(12/5).round().xen("12edo")
|
||||
.tone(s),
|
||||
// kick
|
||||
"[c2 ~]*2".len(0.05).tone(membrane().chain(out())),
|
||||
"[c2 ~]*2".duration(0.05).tone(membrane().chain(out())),
|
||||
// snare
|
||||
"[~ c1]*2".early(0.001).tone(snare),
|
||||
// hihat
|
||||
"c2*8".tone(noise().chain(highpass(6000),vol(0.5).connect(delay),out())),
|
||||
).slow(3)
|
||||
}`;
|
||||
}`;
|
||||
+2
-2
@@ -61,11 +61,11 @@ function useRepl({ tune, defaultSynth, autolink = true, onEvent, onDraw }: any)
|
||||
(time, event) => {
|
||||
try {
|
||||
onEvent?.(event);
|
||||
const { onTrigger } = event.context;
|
||||
const { onTrigger, velocity } = event.context;
|
||||
if (!onTrigger) {
|
||||
if (defaultSynth) {
|
||||
const note = getPlayableNoteValue(event);
|
||||
defaultSynth.triggerAttackRelease(note, event.duration, time);
|
||||
defaultSynth.triggerAttackRelease(note, event.duration, time, velocity);
|
||||
} else {
|
||||
throw new Error('no defaultSynth passed to useRepl.');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user