Compare commits

..

2 Commits

Author SHA1 Message Date
Jade (Rose) Rowland d6045c943b working 2025-07-29 18:20:13 -04:00
Jade (Rose) Rowland 30c80119e9 works 2025-07-28 01:20:42 -04:00
10 changed files with 145 additions and 170 deletions
+13 -23
View File
@@ -551,28 +551,6 @@ export const { tremoloshape } = registerControl('tremoloshape', 'tremshape');
export const { drive } = registerControl('drive');
/**
* waveshaping distortion with selectable algorithms
*
* @name shape
* @param {number | Pattern} amount
* @example
* $: s("909bd").beat("0,4,8,11,14",16).shape("<2 4 1>").shapetype("<0 1 2>*2")
*
*/
export const { shape } = registerControl(['shape', 'shapevol', 'shapetype']);
/**
* filter overdrive for supported filter types
*
* @name shapetype
* @param {number | Pattern} type
* @example
* $: s("909bd").beat("0,4,8,11,14",16).shape("2").shapetype("<0 1 2>")
*
*/
export const { shapeType } = registerControl('shapetype');
/**
* Create byte beats with custom expressions
*
@@ -1532,7 +1510,19 @@ export const { roomsize, size, sz, rsize } = registerControl('roomsize', 'size',
// ['sclap'],
// ['sclaves'],
// ['scrash'],
/**
* (Deprecated) Wave shaping distortion. WARNING: can suddenly get unpredictably loud.
* Please use distort instead, which has a more predictable response curve
* second option in optional array syntax (ex: ".9:.5") applies a postgain to the output
*
*
* @name shape
* @param {number | Pattern} distortion between 0 and 1
* @example
* s("bd sd [~ bd] sd,hh*8").shape("<0 .2 .4 .6 .8>")
*
*/
export const { shape } = registerControl(['shape', 'shapevol']);
/**
* Wave shaping distortion. CAUTION: it can get loud.
* Second option in optional array syntax (ex: ".9:.5") applies a postgain to the output.
+1 -1
View File
@@ -36,7 +36,7 @@ export async function prebake() {
samples(`${ds}/tidal-drum-machines.json`),
samples(`${ds}/piano.json`),
samples(`${ds}/Dirt-Samples.json`),
samples(`https://raw.githubusercontent.com/tidalcycles/uzu-drumkit/main/strudel.json`),
samples(`${ds}/uzu-drumkit.json`),
samples(`${ds}/vcsl.json`),
samples(`${ds}/mridangam.json`),
]);
-15
View File
@@ -277,18 +277,3 @@ export function applyFM(param, value, begin) {
}
return { stop };
}
export const saturationAlgos = [
(x, k) => ((1 + k) * x) / (1 + k * Math.abs(x)), //S Curve sourced from: https://www.nickwritesablog.com/sound-design-in-web-audio-neurofunk-bass-part-1/
(x, k) => Math.tanh(x * k),
];
export function makeSaturationCurve(amount, n_samples, algo = 0) {
const k = typeof amount === 'number' ? amount : 50;
const curve = new Float32Array(n_samples);
for (let i = 0; i < n_samples; i++) {
const x = (i * 2) / n_samples - 1;
curve[i] = saturationAlgos[algo % saturationAlgos.length]?.(x, amount);
}
return curve;
}
+2 -11
View File
@@ -9,7 +9,7 @@ import './reverb.mjs';
import './vowel.mjs';
import { clamp, nanFallback, _mod, cycleToSeconds, secondsToCycle } from './util.mjs';
import workletsUrl from './worklets.mjs?audioworklet';
import { createFilter, gainNode, getCompressor, getWorklet, makeSaturationCurve } from './helpers.mjs';
import { createFilter, gainNode, getCompressor, getWorklet } from './helpers.mjs';
import { map } from 'nanostores';
import { logger } from './logger.mjs';
import { loadBuffer } from './sampler.mjs';
@@ -147,7 +147,6 @@ let defaultDefaultValues = {
phaserdepth: 0.75,
shapevol: 1,
distortvol: 1,
saturatevol: 1,
delay: 0,
byteBeatExpression: '0',
delayfeedback: 0.5,
@@ -574,7 +573,6 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5)
crush,
shape,
shapevol = getDefaultValue('shapevol'),
shapetype,
distort,
distortvol = getDefaultValue('distortvol'),
pan,
@@ -678,14 +676,6 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5)
// gain stage
chain.push(gainNode(gain));
if (shape != null) {
const shaper = new WaveShaperNode(ac);
shaper.curve = makeSaturationCurve(shape, ac.sampleRate, shapetype);
const shapeGain = new GainNode(ac, { gain: shapevol });
chain.push(shaper);
chain.push(shapeGain);
}
//filter
const ftype = getFilterType(value.ftype);
if (cutoff !== undefined) {
@@ -751,6 +741,7 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5)
// effects
coarse !== undefined && chain.push(getWorklet(ac, 'coarse-processor', { coarse }));
crush !== undefined && chain.push(getWorklet(ac, 'crush-processor', { crush }));
shape !== undefined && chain.push(getWorklet(ac, 'shape-processor', { shape, postgain: shapevol }));
distort !== undefined && chain.push(getWorklet(ac, 'distort-processor', { distort, postgain: distortvol }));
if (tremolosync != null) {
+2 -72
View File
@@ -9,13 +9,12 @@ import {
getVibratoOscillator,
webAudioTimeout,
getWorklet,
makeSaturationCurve,
} from './helpers.mjs';
import { getNoiseMix, getNoiseOscillator } from './noise.mjs';
const getFrequencyFromValue = (value, defaultNote = 36) => {
const getFrequencyFromValue = (value) => {
let { note, freq } = value;
note = note || defaultNote;
note = note || 36;
if (typeof note === 'string') {
note = noteToMidi(note); // e.g. c3 => 48
}
@@ -85,75 +84,6 @@ export function registerSynthSounds() {
{ type: 'synth', prebake: true },
);
});
registerSound(
'909bd',
(t, value, onended) => {
const { duration, decay = 0.5, pdecay = 0.5, penv = 36, clip } = value;
const ctx = getAudioContext();
const attackhold = 0.02;
const noiselvl = 1.2;
const noisedecay = 0.025;
const mixGain = 1;
const o = ctx.createOscillator();
o.type = 'triangle';
o.frequency.value = getFrequencyFromValue(value, 29);
o.detune.setValueAtTime(penv * 100, 0);
o.detune.setValueAtTime(penv * 100, t);
o.detune.exponentialRampToValueAtTime(0.001, t + pdecay);
const g = gainNode(1);
g.gain.setValueAtTime(1, t + attackhold);
g.gain.exponentialRampToValueAtTime(0.001, t + attackhold + decay);
o.start(t);
const noise = getNoiseOscillator('brown', t, 2);
const noiseGain = gainNode(1);
noiseGain.gain.setValueAtTime(noiselvl, t);
noiseGain.gain.exponentialRampToValueAtTime(0.001, t + noisedecay);
const sat = new WaveShaperNode(ctx);
// tri to sine diode shaper emulation
sat.curve = makeSaturationCurve(2, ctx.sampleRate, 1);
const mix = gainNode(mixGain);
o.onended = () => {
o.disconnect();
g.disconnect();
sat.disconnect();
noise.node.disconnect();
noiseGain.disconnect();
mix.disconnect();
onended();
};
const node = o.connect(sat).connect(g).connect(mix);
noise.node.connect(noiseGain).connect(mix);
const holdEnd = t + decay;
let end = holdEnd + 0.01;
if (clip != null) {
end = Math.min(t + clip * duration, end);
}
// prevent clicking
mix.gain.setValueAtTime(mixGain, end - 0.01);
mix.gain.linearRampToValueAtTime(0, end);
o.stop(end);
noise.stop(end);
return {
node,
stop: (endTime) => {
o.stop(endTime);
},
};
},
{ type: 'synth', prebake: true },
);
registerSound(
'supersaw',
(begin, value, onended) => {
+48 -45
View File
@@ -8612,51 +8612,54 @@ exports[`runs examples > example "setcpm" example index 0 1`] = `
exports[`runs examples > example "shape" example index 0 1`] = `
[
"[ 0/1 → 1/16 | s:909bd shape:2 shapetype:0 ]",
"[ 1/4 → 5/16 | s:909bd shape:2 shapetype:0 ]",
"[ 1/29/16 | s:909bd shape:2 shapetype:1 ]",
"[ 11/16 → 3/4 | s:909bd shape:2 shapetype:1 ]",
"[ 7/8 → 15/16 | s:909bd shape:2 shapetype:1 ]",
"[ 1/1 → 17/16 | s:909bd shape:4 shapetype:2 ]",
"[ 5/4 → 21/16 | s:909bd shape:4 shapetype:2 ]",
"[ 3/2 → 25/16 | s:909bd shape:4 shapetype:0 ]",
"[ 27/167/4 | s:909bd shape:4 shapetype:0 ]",
"[ 15/8 → 31/16 | s:909bd shape:4 shapetype:0 ]",
"[ 2/133/16 | s:909bd shape:1 shapetype:1 ]",
"[ 9/437/16 | s:909bd shape:1 shapetype:1 ]",
"[ 5/2 → 41/16 | s:909bd shape:1 shapetype:2 ]",
"[ 43/1611/4 | s:909bd shape:1 shapetype:2 ]",
"[ 23/8 → 47/16 | s:909bd shape:1 shapetype:2 ]",
"[ 3/1 → 49/16 | s:909bd shape:2 shapetype:0 ]",
"[ 13/4 → 53/16 | s:909bd shape:2 shapetype:0 ]",
"[ 7/2 → 57/16 | s:909bd shape:2 shapetype:1 ]",
"[ 59/16 → 15/4 | s:909bd shape:2 shapetype:1 ]",
"[ 31/8 → 63/16 | s:909bd shape:2 shapetype:1 ]",
]
`;
exports[`runs examples > example "shapetype" example index 0 1`] = `
[
"[ 0/1 → 1/16 | s:909bd shape:2 shapetype:0 ]",
"[ 1/4 → 5/16 | s:909bd shape:2 shapetype:0 ]",
"[ 1/2 → 9/16 | s:909bd shape:2 shapetype:0 ]",
"[ 11/16 → 3/4 | s:909bd shape:2 shapetype:0 ]",
"[ 7/8 → 15/16 | s:909bd shape:2 shapetype:0 ]",
"[ 1/1 → 17/16 | s:909bd shape:2 shapetype:1 ]",
"[ 5/421/16 | s:909bd shape:2 shapetype:1 ]",
"[ 3/2 → 25/16 | s:909bd shape:2 shapetype:1 ]",
"[ 27/16 → 7/4 | s:909bd shape:2 shapetype:1 ]",
"[ 15/8 → 31/16 | s:909bd shape:2 shapetype:1 ]",
"[ 2/133/16 | s:909bd shape:2 shapetype:2 ]",
"[ 9/4 → 37/16 | s:909bd shape:2 shapetype:2 ]",
"[ 5/2 → 41/16 | s:909bd shape:2 shapetype:2 ]",
"[ 43/16 → 11/4 | s:909bd shape:2 shapetype:2 ]",
"[ 23/847/16 | s:909bd shape:2 shapetype:2 ]",
"[ 3/149/16 | s:909bd shape:2 shapetype:0 ]",
"[ 13/4 → 53/16 | s:909bd shape:2 shapetype:0 ]",
"[ 7/2 → 57/16 | s:909bd shape:2 shapetype:0 ]",
"[ 59/16 → 15/4 | s:909bd shape:2 shapetype:0 ]",
"[ 31/8 → 63/16 | s:909bd shape:2 shapetype:0 ]",
"[ 0/1 → 1/8 | s:hh shape:0 ]",
"[ 0/1 → 1/4 | s:bd shape:0 ]",
"[ 1/81/4 | s:hh shape:0 ]",
"[ 1/4 → 3/8 | s:hh shape:0 ]",
"[ 1/4 → 1/2 | s:sd shape:0 ]",
"[ 3/8 → 1/2 | s:hh shape:0 ]",
"[ 1/2 → 5/8 | s:hh shape:0 ]",
"[ 5/8 → 3/4 | s:bd shape:0 ]",
"[ 5/83/4 | s:hh shape:0 ]",
"[ 3/4 → 7/8 | s:hh shape:0 ]",
"[ 3/41/1 | s:sd shape:0 ]",
"[ 7/81/1 | s:hh shape:0 ]",
"[ 1/1 → 9/8 | s:hh shape:0.2 ]",
"[ 1/1 → 5/4 | s:bd shape:0.2 ]",
"[ 9/8 → 5/4 | s:hh shape:0.2 ]",
"[ 5/4 → 11/8 | s:hh shape:0.2 ]",
"[ 5/4 → 3/2 | s:sd shape:0.2 ]",
"[ 11/8 → 3/2 | s:hh shape:0.2 ]",
"[ 3/2 → 13/8 | s:hh shape:0.2 ]",
"[ 13/8 → 7/4 | s:bd shape:0.2 ]",
"[ 13/8 → 7/4 | s:hh shape:0.2 ]",
"[ 7/4 → 15/8 | s:hh shape:0.2 ]",
"[ 7/4 → 2/1 | s:sd shape:0.2 ]",
"[ 15/8 → 2/1 | s:hh shape:0.2 ]",
"[ 2/1 → 17/8 | s:hh shape:0.4 ]",
"[ 2/1 → 9/4 | s:bd shape:0.4 ]",
"[ 17/8 → 9/4 | s:hh shape:0.4 ]",
"[ 9/419/8 | s:hh shape:0.4 ]",
"[ 9/4 → 5/2 | s:sd shape:0.4 ]",
"[ 19/8 → 5/2 | s:hh shape:0.4 ]",
"[ 5/2 → 21/8 | s:hh shape:0.4 ]",
"[ 21/811/4 | s:bd shape:0.4 ]",
"[ 21/8 → 11/4 | s:hh shape:0.4 ]",
"[ 11/4 → 23/8 | s:hh shape:0.4 ]",
"[ 11/4 → 3/1 | s:sd shape:0.4 ]",
"[ 23/8 → 3/1 | s:hh shape:0.4 ]",
"[ 3/1 → 25/8 | s:hh shape:0.6 ]",
"[ 3/1 → 13/4 | s:bd shape:0.6 ]",
"[ 25/8 → 13/4 | s:hh shape:0.6 ]",
"[ 13/427/8 | s:hh shape:0.6 ]",
"[ 13/47/2 | s:sd shape:0.6 ]",
"[ 27/8 → 7/2 | s:hh shape:0.6 ]",
"[ 7/2 → 29/8 | s:hh shape:0.6 ]",
"[ 29/8 → 15/4 | s:bd shape:0.6 ]",
"[ 29/8 → 15/4 | s:hh shape:0.6 ]",
"[ 15/4 → 31/8 | s:hh shape:0.6 ]",
"[ 15/4 → 4/1 | s:sd shape:0.6 ]",
"[ 31/8 → 4/1 | s:hh shape:0.6 ]",
]
`;
+76
View File
@@ -0,0 +1,76 @@
{
"_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"
]
}
@@ -125,7 +125,7 @@ function UserPatterns({ context }) {
style={{ display: 'none' }}
type="file"
multiple
accept="text/plain,application/json"
accept="text/plain,text/x-markdown,application/json"
onChange={(e) => importPatterns(e.target.files)}
/>
import
+1 -1
View File
@@ -28,7 +28,7 @@ export async function prebake() {
prebake: true,
tag: 'drum-machines',
}),
samples(`https://raw.githubusercontent.com/tidalcycles/uzu-drumkit/main/strudel.json`, undefined, {
samples(`${baseNoTrailing}/uzu-drumkit.json`, undefined, {
prebake: true,
tag: 'drum-machines',
}),
+1 -1
View File
@@ -197,7 +197,7 @@ export async function importPatterns(fileList) {
if (file.type === 'application/json') {
const userPatterns = userPattern.getAll();
setUserPatterns({ ...userPatterns, ...parseJSON(content) });
} else if (file.type === 'text/plain') {
} else if (['text/x-markdown', 'text/plain'].includes(file.type)) {
const id = file.name.replace(/\.[^/.]+$/, '');
userPattern.update(id, { code: content });
}