diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index f6fcc89f..69c43bfa 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -299,16 +299,52 @@ const generic_params = [ */ ['end'], /** - * Loops the sample (from `begin` to `end`) the specified number of times. + * Loops the sample. * Note that the tempo of the loop is not synced with the cycle tempo. + * To change the loop region, use loopBegin / loopEnd. * * @name loop - * @param {number | Pattern} times How often the sample is looped + * @param {number | Pattern} on If 1, the sample is looped * @example - * s("bd").loop("<1 2 3 4>").osc() + * s("casio").loop(1) * */ ['loop'], + /** + * Begin to loop at a specific point in the sample (inbetween `begin` and `end`). + * Note that the loop point must be inbetween `begin` and `end`, and before `loopEnd`! + * Note: Samples starting with wt_ will automatically loop! (wt = wavetable) + * + * @name loopBegin + * @param {number | Pattern} time between 0 and 1, where 1 is the length of the sample + * @synonyms loopb + * @example + * s("space").loop(1) + * .loopBegin("<0 .125 .25>").scope() + */ + ['loopBegin', 'loopb'], + /** + * + * End the looping section at a specific point in the sample (inbetween `begin` and `end`). + * Note that the loop point must be inbetween `begin` and `end`, and after `loopBegin`! + * + * @name loopEnd + * @param {number | Pattern} time between 0 and 1, where 1 is the length of the sample + * @synonyms loope + * @example + * s("space").loop(1) + * .loopEnd("<1 .75 .5 .25>").scope() + */ + ['loopEnd', 'loope'], + /** + * bit crusher effect. + * + * @name crush + * @param {number | Pattern} depth between 1 (for drastic reduction in bit-depth) to 16 (for barely no reduction). + * @example + * s(",hh*3").fast(2).crush("<16 8 7 6 5 4 3 2>") + * + */ // TODO: currently duplicated with "native" legato // TODO: superdirt legato will do more: https://youtu.be/dQPmE1WaD1k?t=419 /** @@ -323,15 +359,6 @@ const generic_params = [ */ // ['legato'], // ['clhatdecay'], - /** - * bit crusher effect. - * - * @name crush - * @param {number | Pattern} depth between 1 (for drastic reduction in bit-depth) to 16 (for barely no reduction). - * @example - * s(",hh*3").fast(2).crush("<16 8 7 6 5 4 3 2>") - * - */ ['crush'], /** * fake-resampling for lowering the sample rate. Caution: This effect seems to only work in chromium based browsers @@ -343,7 +370,6 @@ const generic_params = [ * */ ['coarse'], - /** * choose the channel the pattern is sent to in superdirt * @@ -377,6 +403,227 @@ const generic_params = [ * */ [['cutoff', 'resonance'], 'ctf', 'lpf', 'lp'], + + /** + * Sets the lowpass filter envelope modulation depth. + * @name lpenv + * @param {number | Pattern} modulation depth of the lowpass filter envelope between 0 and _n_ + * @synonyms lpe + * @example + * note("") + * .sound('sawtooth') + * .lpf(500) + * .lpa(.5) + * .lpenv("<4 2 1 0 -1 -2 -4>/4") + */ + ['lpenv', 'lpe'], + /** + * Sets the highpass filter envelope modulation depth. + * @name hpenv + * @param {number | Pattern} modulation depth of the highpass filter envelope between 0 and _n_ + * @synonyms hpe + * @example + * note("") + * .sound('sawtooth') + * .hpf(500) + * .hpa(.5) + * .hpenv("<4 2 1 0 -1 -2 -4>/4") + */ + ['hpenv', 'hpe'], + /** + * Sets the bandpass filter envelope modulation depth. + * @name bpenv + * @param {number | Pattern} modulation depth of the bandpass filter envelope between 0 and _n_ + * @synonyms bpe + * @example + * note("") + * .sound('sawtooth') + * .bpf(500) + * .bpa(.5) + * .bpenv("<4 2 1 0 -1 -2 -4>/4") + */ + ['bpenv', 'bpe'], + /** + * Sets the attack duration for the lowpass filter envelope. + * @name lpattack + * @param {number | Pattern} attack time of the filter envelope + * @synonyms lpa + * @example + * note("") + * .sound('sawtooth') + * .lpf(500) + * .lpa("<.5 .25 .1 .01>/4") + * .lpenv(4) + */ + ['lpattack', 'lpa'], + /** + * Sets the attack duration for the highpass filter envelope. + * @name hpattack + * @param {number | Pattern} attack time of the highpass filter envelope + * @synonyms hpa + * @example + * note("") + * .sound('sawtooth') + * .hpf(500) + * .hpa("<.5 .25 .1 .01>/4") + * .hpenv(4) + */ + ['hpattack', 'hpa'], + /** + * Sets the attack duration for the bandpass filter envelope. + * @name bpattack + * @param {number | Pattern} attack time of the bandpass filter envelope + * @synonyms bpa + * @example + * note("") + * .sound('sawtooth') + * .bpf(500) + * .bpa("<.5 .25 .1 .01>/4") + * .bpenv(4) + */ + ['bpattack', 'bpa'], + /** + * Sets the decay duration for the lowpass filter envelope. + * @name lpdecay + * @param {number | Pattern} decay time of the filter envelope + * @synonyms lpd + * @example + * note("") + * .sound('sawtooth') + * .lpf(500) + * .lpd("<.5 .25 .1 0>/4") + * .lps(0.2) + * .lpenv(4) + */ + ['lpdecay', 'lpd'], + /** + * Sets the decay duration for the highpass filter envelope. + * @name hpdecay + * @param {number | Pattern} decay time of the highpass filter envelope + * @synonyms hpd + * @example + * note("") + * .sound('sawtooth') + * .hpf(500) + * .hpd("<.5 .25 .1 0>/4") + * .hps(0.2) + * .hpenv(4) + */ + ['hpdecay', 'hpd'], + /** + * Sets the decay duration for the bandpass filter envelope. + * @name bpdecay + * @param {number | Pattern} decay time of the bandpass filter envelope + * @synonyms bpd + * @example + * note("") + * .sound('sawtooth') + * .bpf(500) + * .bpd("<.5 .25 .1 0>/4") + * .bps(0.2) + * .bpenv(4) + */ + ['bpdecay', 'bpd'], + /** + * Sets the sustain amplitude for the lowpass filter envelope. + * @name lpsustain + * @param {number | Pattern} sustain amplitude of the lowpass filter envelope + * @synonyms lps + * @example + * note("") + * .sound('sawtooth') + * .lpf(500) + * .lpd(.5) + * .lps("<0 .25 .5 1>/4") + * .lpenv(4) + */ + ['lpsustain', 'lps'], + /** + * Sets the sustain amplitude for the highpass filter envelope. + * @name hpsustain + * @param {number | Pattern} sustain amplitude of the highpass filter envelope + * @synonyms hps + * @example + * note("") + * .sound('sawtooth') + * .hpf(500) + * .hpd(.5) + * .hps("<0 .25 .5 1>/4") + * .hpenv(4) + */ + ['hpsustain', 'hps'], + /** + * Sets the sustain amplitude for the bandpass filter envelope. + * @name bpsustain + * @param {number | Pattern} sustain amplitude of the bandpass filter envelope + * @synonyms bps + * @example + * note("") + * .sound('sawtooth') + * .bpf(500) + * .bpd(.5) + * .bps("<0 .25 .5 1>/4") + * .bpenv(4) + */ + ['bpsustain', 'bps'], + /** + * Sets the release time for the lowpass filter envelope. + * @name lprelease + * @param {number | Pattern} release time of the filter envelope + * @synonyms lpr + * @example + * note("") + * .sound('sawtooth') + * .clip(.5) + * .lpf(500) + * .lpenv(4) + * .lpr("<.5 .25 .1 0>/4") + * .release(.5) + */ + ['lprelease', 'lpr'], + /** + * Sets the release time for the highpass filter envelope. + * @name hprelease + * @param {number | Pattern} release time of the highpass filter envelope + * @synonyms hpr + * @example + * note("") + * .sound('sawtooth') + * .clip(.5) + * .hpf(500) + * .hpenv(4) + * .hpr("<.5 .25 .1 0>/4") + * .release(.5) + */ + ['hprelease', 'hpr'], + /** + * Sets the release time for the bandpass filter envelope. + * @name bprelease + * @param {number | Pattern} release time of the bandpass filter envelope + * @synonyms bpr + * @example + * note("") + * .sound('sawtooth') + * .clip(.5) + * .bpf(500) + * .bpenv(4) + * .bpr("<.5 .25 .1 0>/4") + * .release(.5) + */ + ['bprelease', 'bpr'], + /** + * Sets the filter type. The 24db filter is more aggressive. More types might be added in the future. + * @name ftype + * @param {number | Pattern} type 12db (default) or 24db + * @example + * note("") + * .sound('sawtooth') + * .lpf(500) + * .bpenv(4) + * .ftype("<12db 24db>") + */ + ['ftype'], + ['fanchor'], /** * Applies the cutoff frequency of the **h**igh-**p**ass **f**ilter. * diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index 7e694f49..e3efb427 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -2273,14 +2273,14 @@ export const slice = register( false, // turns off auto-patternification ); -/* +/** * Works the same as slice, but changes the playback speed of each slice to match the duration of its step. * @name splice - * @memberof Pattern - * @returns Pattern * @example * await samples('github:tidalcycles/Dirt-Samples/master') - * s("breaks165").splice(8, "0 1 [2 3 0]@2 3 0@2 7").hurry(0.65) + * s("breaks165") + * .splice(8, "0 1 [2 3 0]@2 3 0@2 7") + * .hurry(0.65) */ export const splice = register( @@ -2307,9 +2307,16 @@ export const { loopAt, loopat } = register(['loopAt', 'loopat'], function (facto return _loopAt(factor, pat, 1); }); -// this function will be redefined in repl.mjs to use the correct cps value. +// the fit function will be redefined in repl.mjs to use the correct cps value. // It is still here to work in cases where repl.mjs is not used - +/** + * Makes the sample fit its event duration. Good for rhythmical loops like drum breaks. + * Similar to loopAt. + * @name fit + * @example + * samples({ rhodes: 'https://cdn.freesound.org/previews/132/132051_316502-lq.mp3' }) + * s("rhodes/4").fit() + */ export const fit = register('fit', (pat) => pat.withHap((hap) => hap.withValue((v) => ({ diff --git a/packages/desktopbridge/index.mjs b/packages/desktopbridge/index.mjs index 2cc9db3d..591bbe34 100644 --- a/packages/desktopbridge/index.mjs +++ b/packages/desktopbridge/index.mjs @@ -6,3 +6,4 @@ This program is free software: you can redistribute it and/or modify it under th export * from './midibridge.mjs'; export * from './utils.mjs'; +export * from './oscbridge.mjs'; diff --git a/packages/desktopbridge/loggerbridge.mjs b/packages/desktopbridge/loggerbridge.mjs new file mode 100644 index 00000000..6ab16dc5 --- /dev/null +++ b/packages/desktopbridge/loggerbridge.mjs @@ -0,0 +1,11 @@ +import { listen } from '@tauri-apps/api/event'; +import { logger } from '../core/logger.mjs'; + +// listen for log events from the Tauri backend and log in the UI +await listen('log-event', (e) => { + if (e.payload == null) { + return; + } + const { message, message_type } = e.payload; + logger(message, message_type); +}); diff --git a/packages/desktopbridge/oscbridge.mjs b/packages/desktopbridge/oscbridge.mjs new file mode 100644 index 00000000..135a2b02 --- /dev/null +++ b/packages/desktopbridge/oscbridge.mjs @@ -0,0 +1,43 @@ +import { parseNumeral, Pattern } from '@strudel.cycles/core'; +import { Invoke } from './utils.mjs'; + +Pattern.prototype.osc = function () { + return this.onTrigger(async (time, hap, currentTime, cps = 1) => { + hap.ensureObjectValue(); + const cycle = hap.wholeOrPart().begin.valueOf(); + const delta = hap.duration.valueOf(); + const controls = Object.assign({}, { cps, cycle, delta }, hap.value); + // make sure n and note are numbers + controls.n && (controls.n = parseNumeral(controls.n)); + controls.note && (controls.note = parseNumeral(controls.note)); + + const params = []; + + const timestamp = Math.round(Date.now() + (time - currentTime) * 1000); + + Object.keys(controls).forEach((key) => { + const val = controls[key]; + const value = typeof val === 'number' ? val.toString() : val; + + if (value == null) { + return; + } + params.push({ + name: key, + value, + valueisnumber: typeof val === 'number', + }); + }); + + const messagesfromjs = []; + if (params.length) { + messagesfromjs.push({ target: '/dirt/play', timestamp, params }); + } + + if (messagesfromjs.length) { + setTimeout(() => { + Invoke('sendosc', { messagesfromjs }); + }); + } + }); +}; diff --git a/packages/desktopbridge/package.json b/packages/desktopbridge/package.json index bdeda2d9..c2acba9e 100644 --- a/packages/desktopbridge/package.json +++ b/packages/desktopbridge/package.json @@ -1,7 +1,7 @@ { "name": "@strudel/desktopbridge", "version": "0.1.0", - "description": "send midi messages between the JS and Tauri (Rust) sides of the Studel desktop app", + "description": "tools/shims for communicating between the JS and Tauri (Rust) sides of the Studel desktop app", "main": "index.mjs", "type": "module", "repository": { diff --git a/packages/superdough/helpers.mjs b/packages/superdough/helpers.mjs index 07e2b121..32ca0bb2 100644 --- a/packages/superdough/helpers.mjs +++ b/packages/superdough/helpers.mjs @@ -1,4 +1,5 @@ import { getAudioContext } from './superdough.mjs'; +import { clamp } from './util.mjs'; export function gainNode(value) { const node = getAudioContext().createGain(); @@ -66,10 +67,48 @@ export const getADSR = (attack, decay, sustain, release, velocity, begin, end) = return gainNode; }; -export const getFilter = (type, frequency, Q) => { - const filter = getAudioContext().createBiquadFilter(); - filter.type = type; - filter.frequency.value = frequency; - filter.Q.value = Q; - return filter; +export const getParamADSR = (param, attack, decay, sustain, release, min, max, begin, end) => { + const range = max - min; + const peak = min + range; + const sustainLevel = min + sustain * range; + param.setValueAtTime(min, begin); + param.linearRampToValueAtTime(peak, begin + attack); + param.linearRampToValueAtTime(sustainLevel, begin + attack + decay); + param.setValueAtTime(sustainLevel, end); + param.linearRampToValueAtTime(min, end + Math.max(release, 0.1)); }; + +export function createFilter( + context, + type, + frequency, + Q, + attack, + decay, + sustain, + release, + fenv, + start, + end, + fanchor = 0.5, +) { + const filter = context.createBiquadFilter(); + filter.type = type; + filter.Q.value = Q; + filter.frequency.value = frequency; + + // Apply ADSR to filter frequency + if (!isNaN(fenv) && fenv !== 0) { + const offset = fenv * fanchor; + + const min = clamp(2 ** -offset * frequency, 0, 20000); + const max = clamp(2 ** (fenv - offset) * frequency, 0, 20000); + + // console.log('min', min, 'max', max); + + getParamADSR(filter.frequency, attack, decay, sustain, release, min, max, start, end); + return filter; + } + + return filter; +} diff --git a/packages/superdough/package.json b/packages/superdough/package.json index bab58658..22b58cc5 100644 --- a/packages/superdough/package.json +++ b/packages/superdough/package.json @@ -1,6 +1,6 @@ { "name": "superdough", - "version": "0.9.6", + "version": "0.9.7", "description": "simple web audio synth and sampler intended for live coding. inspired by superdirt and webdirt.", "main": "index.mjs", "type": "module", diff --git a/packages/superdough/sampler.mjs b/packages/superdough/sampler.mjs index 02e5eada..76b6a542 100644 --- a/packages/superdough/sampler.mjs +++ b/packages/superdough/sampler.mjs @@ -196,7 +196,7 @@ export const samples = async (sampleMap, baseUrl = sampleMap._base || '', option const cutGroups = []; export async function onTriggerSample(t, value, onended, bank, resolveUrl) { - const { + let { s, freq, unit, @@ -207,7 +207,9 @@ export async function onTriggerSample(t, value, onended, bank, resolveUrl) { n = 0, note, speed = 1, // sample playback speed + loopBegin = 0, begin = 0, + loopEnd = 1, end = 1, } = value; // load sample @@ -215,6 +217,7 @@ export async function onTriggerSample(t, value, onended, bank, resolveUrl) { // no playback return; } + loop = s.startsWith('wt_') ? 1 : value.loop; const ac = getAudioContext(); // destructure adsr here, because the default should be different for synths and samples const { attack = 0.001, decay = 0.001, sustain = 1, release = 0.001 } = value; @@ -242,19 +245,12 @@ export async function onTriggerSample(t, value, onended, bank, resolveUrl) { // rather than the current playback rate, so even if the sound is playing at twice its normal speed, // the midway point through a 10-second audio buffer is still 5." const offset = begin * bufferSource.buffer.duration; - bufferSource.start(time, offset); - const bufferDuration = bufferSource.buffer.duration / bufferSource.playbackRate.value; - /*if (loop) { - // TODO: idea for loopBegin / loopEnd - // if one of [loopBegin,loopEnd] is <= 1, interpret it as normlized - // if [loopBegin,loopEnd] is bigger >= 1, interpret it as sample number - // this will simplify perfectly looping things, while still keeping the normalized option - // the only drawback is that looping between samples 0 and 1 is not possible (which is not real use case) + if (loop) { bufferSource.loop = true; - bufferSource.loopStart = offset; - bufferSource.loopEnd = offset + duration; - duration = loop * duration; - }*/ + bufferSource.loopStart = loopBegin * bufferSource.buffer.duration - offset; + bufferSource.loopEnd = loopEnd * bufferSource.buffer.duration - offset; + } + bufferSource.start(time, offset); const { node: envelope, stop: releaseEnvelope } = getEnvelope(attack, decay, sustain, release, 1, t); bufferSource.connect(envelope); const out = ac.createGain(); // we need a separate gain for the cutgroups because firefox... @@ -265,9 +261,10 @@ export async function onTriggerSample(t, value, onended, bank, resolveUrl) { out.disconnect(); onended(); }; - const stop = (endTime, playWholeBuffer = clip === undefined) => { + const stop = (endTime, playWholeBuffer = clip === undefined && loop === undefined) => { let releaseTime = endTime; if (playWholeBuffer) { + const bufferDuration = bufferSource.buffer.duration / bufferSource.playbackRate.value; releaseTime = t + (end - begin) * bufferDuration; } bufferSource.stop(releaseTime + release); diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index af22355f..289e8d97 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -9,7 +9,7 @@ import './reverb.mjs'; import './vowel.mjs'; import { clamp } from './util.mjs'; import workletsUrl from './worklets.mjs?url'; -import { getFilter, gainNode } from './helpers.mjs'; +import { createFilter, gainNode } from './helpers.mjs'; import { map } from 'nanostores'; import { logger } from './logger.mjs'; @@ -177,14 +177,32 @@ export const superdough = async (value, deadline, hapDuration) => { bank, source, gain = 0.8, + // filters + ftype = '12db', + fanchor = 0.5, // low pass cutoff, + lpenv, + lpattack = 0.01, + lpdecay = 0.01, + lpsustain = 1, + lprelease = 0.01, resonance = 1, // high pass + hpenv, hcutoff, + hpattack = 0.01, + hpdecay = 0.01, + hpsustain = 1, + hprelease = 0.01, hresonance = 1, // band pass + bpenv, bandf, + bpattack = 0.01, + bpdecay = 0.01, + bpsustain = 1, + bprelease = 0.01, bandq = 1, // coarse, @@ -239,11 +257,76 @@ export const superdough = async (value, deadline, hapDuration) => { // gain stage chain.push(gainNode(gain)); - // filters - cutoff !== undefined && chain.push(getFilter('lowpass', cutoff, resonance)); - hcutoff !== undefined && chain.push(getFilter('highpass', hcutoff, hresonance)); - bandf !== undefined && chain.push(getFilter('bandpass', bandf, bandq)); - vowel !== undefined && chain.push(ac.createVowelFilter(vowel)); + if (cutoff !== undefined) { + let lp = () => + createFilter( + ac, + 'lowpass', + cutoff, + resonance, + lpattack, + lpdecay, + lpsustain, + lprelease, + lpenv, + t, + t + hapDuration, + fanchor, + ); + chain.push(lp()); + if (ftype === '24db') { + chain.push(lp()); + } + } + + if (hcutoff !== undefined) { + let hp = () => + createFilter( + ac, + 'highpass', + hcutoff, + hresonance, + hpattack, + hpdecay, + hpsustain, + hprelease, + hpenv, + t, + t + hapDuration, + fanchor, + ); + chain.push(hp()); + if (ftype === '24db') { + chain.push(hp()); + } + } + + if (bandf !== undefined) { + let bp = () => + createFilter( + ac, + 'bandpass', + bandf, + bandq, + bpattack, + bpdecay, + bpsustain, + bprelease, + bpenv, + t, + t + hapDuration, + fanchor, + ); + chain.push(bp()); + if (ftype === '24db') { + chain.push(bp()); + } + } + + if (vowel !== undefined) { + const vowelFilter = ac.createVowelFilter(vowel); + chain.push(vowelFilter); + } // effects coarse !== undefined && chain.push(getWorklet(ac, 'coarse-processor', { coarse })); diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 90767fec..38fce20b 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -98,6 +98,7 @@ name = "app" version = "0.1.0" dependencies = [ "midir", + "rosc", "serde", "serde_json", "tauri", @@ -1557,6 +1558,12 @@ dependencies = [ "windows 0.43.0", ] +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.7.1" @@ -1629,6 +1636,16 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -2173,6 +2190,16 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" +[[package]] +name = "rosc" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2e63d9e6b0d090be1485cf159b1e04c3973d2d3e1614963544ea2ff47a4a981" +dependencies = [ + "byteorder", + "nom", +] + [[package]] name = "rustc-demangle" version = "0.1.23" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 22170cac..35aab489 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -20,6 +20,7 @@ serde = { version = "1.0", features = ["derive"] } tauri = { version = "1.4.0", features = ["fs-all"] } midir = "0.9.1" tokio = { version = "1.29.0", features = ["full"] } +rosc = "0.10.1" [features] # this feature is used for production builds or when `devPath` points to the filesystem and the built-in dev server is disabled. diff --git a/src-tauri/src/loggerbridge.rs b/src-tauri/src/loggerbridge.rs new file mode 100644 index 00000000..f7fa9acf --- /dev/null +++ b/src-tauri/src/loggerbridge.rs @@ -0,0 +1,20 @@ +use std::sync::Arc; +use tauri::Window; + +#[derive(Clone, serde::Serialize)] +pub struct LoggerPayload { + pub message: String, + pub message_type: String, +} + +#[derive(Clone)] +pub struct Logger { + pub window: Arc, +} + +impl Logger { + pub fn log(&self, message: String, message_type: String) { + println!("{}", message); + let _ = self.window.emit("log-event", LoggerPayload { message, message_type }); + } +} diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 0643cefd..6f97b8c9 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -2,20 +2,44 @@ #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] mod midibridge; +mod oscbridge; +mod loggerbridge; +use std::sync::Arc; + +use loggerbridge::Logger; +use tauri::Manager; use tokio::sync::mpsc; use tokio::sync::Mutex; - +// the payload type must implement `Serialize` and `Clone`. +#[derive(Clone, serde::Serialize)] +struct Payload { + message: String, + message_type: String, +} fn main() { - let (async_input_transmitter, async_input_receiver) = mpsc::channel(1); - let (async_output_transmitter, async_output_receiver) = mpsc::channel(1); + let (async_input_transmitter_midi, async_input_receiver_midi) = mpsc::channel(1); + let (async_output_transmitter_midi, async_output_receiver_midi) = mpsc::channel(1); + let (async_input_transmitter_osc, async_input_receiver_osc) = mpsc::channel(1); + let (async_output_transmitter_osc, async_output_receiver_osc) = mpsc::channel(1); tauri::Builder ::default() .manage(midibridge::AsyncInputTransmit { - inner: Mutex::new(async_input_transmitter), + inner: Mutex::new(async_input_transmitter_midi), }) - .invoke_handler(tauri::generate_handler![midibridge::sendmidi]) - .setup(|_app| { - midibridge::init(async_input_receiver, async_output_receiver, async_output_transmitter); + .manage(oscbridge::AsyncInputTransmit { + inner: Mutex::new(async_input_transmitter_osc), + }) + .invoke_handler(tauri::generate_handler![midibridge::sendmidi, oscbridge::sendosc]) + .setup(|app| { + let window = Arc::new(app.get_window("main").unwrap()); + let logger = Logger { window }; + midibridge::init( + logger.clone(), + async_input_receiver_midi, + async_output_receiver_midi, + async_output_transmitter_midi + ); + oscbridge::init(logger, async_input_receiver_osc, async_output_receiver_osc, async_output_transmitter_osc); Ok(()) }) .run(tauri::generate_context!()) diff --git a/src-tauri/src/midibridge.rs b/src-tauri/src/midibridge.rs index c049e406..5c1149e7 100644 --- a/src-tauri/src/midibridge.rs +++ b/src-tauri/src/midibridge.rs @@ -2,10 +2,13 @@ use std::collections::HashMap; use std::sync::Arc; use std::time::Duration; use midir::MidiOutput; + use tokio::sync::{ mpsc, Mutex }; use tokio::time::Instant; use serde::Deserialize; use std::thread::sleep; + +use crate::loggerbridge::Logger; pub struct MidiMessage { pub message: Vec, pub instant: Instant, @@ -18,6 +21,7 @@ pub struct AsyncInputTransmit { } pub fn init( + logger: Logger, async_input_receiver: mpsc::Receiver>, mut async_output_receiver: mpsc::Receiver>, async_output_transmitter: mpsc::Sender> @@ -50,10 +54,16 @@ pub fn init( let mut port_names = Vec::new(); //TODO: Send these print messages to the UI logger instead of the rust console so the user can see them if out_ports.len() == 0 { - println!(" No MIDI devices found. Connect a device or enable IAC Driver."); + logger.log( + " No MIDI devices found. Connect a device or enable IAC Driver to enable midi.".to_string(), + "".to_string() + ); + // logger(window, " No MIDI devices found. Connect a device or enable IAC Driver.".to_string(), None); return; } - println!("Found {} midi devices!", out_ports.len()); + // give the frontend couple seconds to load on start, or the log messages will get lost + sleep(Duration::from_secs(3)); + logger.log(format!("Found {} midi devices!", out_ports.len()), "".to_string()); // the user could reference any port at anytime during runtime, // so let's go ahead and open them all (same behavior as web app) @@ -63,7 +73,7 @@ pub fn init( let ports = midiout.ports(); let port = ports.get(i).unwrap(); let port_name = midiout.port_name(port).unwrap(); - println!("{}", port_name); + logger.log(port_name.clone(), "".to_string()); let out_con = midiout.connect(port, &port_name).unwrap(); port_names.insert(i, port_name.clone()); output_connections.insert(port_name, out_con); @@ -96,10 +106,10 @@ pub fn init( if out_con.is_some() { // process the message if let Err(err) = (&mut out_con.unwrap()).send(&message.message) { - println!("Midi message send error: {}", err); + logger.log(format!("Midi message send error: {}", err), "error".to_string()); } } else { - println!("failed to find midi device: {}", message.requestedport); + logger.log(format!("failed to find midi device: {}", message.requestedport), "error".to_string()); } return false; }); diff --git a/src-tauri/src/oscbridge.rs b/src-tauri/src/oscbridge.rs new file mode 100644 index 00000000..6060cd9d --- /dev/null +++ b/src-tauri/src/oscbridge.rs @@ -0,0 +1,157 @@ +use rosc::{ encoder, OscTime }; +use rosc::{ OscMessage, OscPacket, OscType, OscBundle }; + +use std::net::UdpSocket; + +use std::time::Duration; +use std::sync::Arc; +use tokio::sync::{ mpsc, Mutex }; +use serde::Deserialize; +use std::thread::sleep; + +use crate::loggerbridge::Logger; +pub struct OscMsg { + pub msg_buf: Vec, + pub timestamp: u64, +} + +pub struct AsyncInputTransmit { + pub inner: Mutex>>, +} + +const UNIX_OFFSET: u64 = 2_208_988_800; // 70 years in seconds +const TWO_POW_32: f64 = (u32::MAX as f64) + 1.0; // Number of bits in a `u32` +const NANOS_PER_SECOND: f64 = 1.0e9; +const SECONDS_PER_NANO: f64 = 1.0 / NANOS_PER_SECOND; + +pub fn init( + logger: Logger, + async_input_receiver: mpsc::Receiver>, + mut async_output_receiver: mpsc::Receiver>, + async_output_transmitter: mpsc::Sender> +) { + tauri::async_runtime::spawn(async move { async_process_model(async_input_receiver, async_output_transmitter).await }); + let message_queue: Arc>> = Arc::new(Mutex::new(Vec::new())); + /* ........................................................... + Listen For incoming messages and add to queue + ............................................................*/ + let message_queue_clone = Arc::clone(&message_queue); + tauri::async_runtime::spawn(async move { + loop { + if let Some(package) = async_output_receiver.recv().await { + let mut message_queue = message_queue_clone.lock().await; + let messages = package; + for message in messages { + (*message_queue).push(message); + } + } + } + }); + + let message_queue_clone = Arc::clone(&message_queue); + tauri::async_runtime::spawn(async move { + /* ........................................................... + Open OSC Ports + ............................................................*/ + let sock = UdpSocket::bind("127.0.0.1:57122").unwrap(); + let to_addr = String::from("127.0.0.1:57120"); + sock.set_nonblocking(true).unwrap(); + sock.connect(to_addr).expect("could not connect to OSC address"); + + /* ........................................................... + Process queued messages + ............................................................*/ + + loop { + let mut message_queue = message_queue_clone.lock().await; + + message_queue.retain(|message| { + let result = sock.send(&message.msg_buf); + if result.is_err() { + logger.log( + format!("OSC Message failed to send, the server might no longer be available"), + "error".to_string() + ); + } + return false; + }); + + sleep(Duration::from_millis(1)); + } + }); +} + +pub async fn async_process_model( + mut input_reciever: mpsc::Receiver>, + output_transmitter: mpsc::Sender> +) -> Result<(), Box> { + while let Some(input) = input_reciever.recv().await { + let output = input; + output_transmitter.send(output).await?; + } + Ok(()) +} + +#[derive(Deserialize)] +pub struct Param { + name: String, + value: String, + valueisnumber: bool, +} +#[derive(Deserialize)] +pub struct MessageFromJS { + params: Vec, + timestamp: u64, + target: String, +} +// Called from JS +#[tauri::command] +pub async fn sendosc( + messagesfromjs: Vec, + state: tauri::State<'_, AsyncInputTransmit> +) -> Result<(), String> { + let async_proc_input_tx = state.inner.lock().await; + let mut messages_to_process: Vec = Vec::new(); + for m in messagesfromjs { + let mut args = Vec::new(); + for p in m.params { + args.push(OscType::String(p.name)); + if p.valueisnumber { + args.push(OscType::Float(p.value.parse().unwrap())); + } else { + args.push(OscType::String(p.value)); + } + } + + let duration_since_epoch = Duration::from_millis(m.timestamp) + Duration::new(UNIX_OFFSET, 0); + + let seconds = u32 + ::try_from(duration_since_epoch.as_secs()) + .map_err(|_| "bit conversion failed for osc message timetag")?; + + let nanos = duration_since_epoch.subsec_nanos() as f64; + let fractional = (nanos * SECONDS_PER_NANO * TWO_POW_32).round() as u32; + + let timetag = OscTime::from((seconds, fractional)); + + let packet = OscPacket::Message(OscMessage { + addr: m.target, + args, + }); + + let bundle = OscBundle { + content: vec![packet], + timetag, + }; + + let msg_buf = encoder::encode(&OscPacket::Bundle(bundle)).unwrap(); + + let message_to_process = OscMsg { + msg_buf, + timestamp: m.timestamp, + }; + messages_to_process.push(message_to_process); + } + + async_proc_input_tx.send(messages_to_process).await.map_err(|e| e.to_string()) +} diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index 76a7cf11..d146a065 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -900,6 +900,33 @@ exports[`runs examples > example "begin" example index 0 1`] = ` ] `; +exports[`runs examples > example "bpattack" example index 0 1`] = ` +[ + "[ 0/1 → 1/1 | note:c2 s:sawtooth bandf:500 bpattack:0.5 bpenv:4 ]", + "[ 1/1 → 2/1 | note:e2 s:sawtooth bandf:500 bpattack:0.5 bpenv:4 ]", + "[ 2/1 → 3/1 | note:f2 s:sawtooth bandf:500 bpattack:0.5 bpenv:4 ]", + "[ 3/1 → 4/1 | note:g2 s:sawtooth bandf:500 bpattack:0.5 bpenv:4 ]", +] +`; + +exports[`runs examples > example "bpdecay" example index 0 1`] = ` +[ + "[ 0/1 → 1/1 | note:c2 s:sawtooth bandf:500 bpdecay:0.5 bpsustain:0.2 bpenv:4 ]", + "[ 1/1 → 2/1 | note:e2 s:sawtooth bandf:500 bpdecay:0.5 bpsustain:0.2 bpenv:4 ]", + "[ 2/1 → 3/1 | note:f2 s:sawtooth bandf:500 bpdecay:0.5 bpsustain:0.2 bpenv:4 ]", + "[ 3/1 → 4/1 | note:g2 s:sawtooth bandf:500 bpdecay:0.5 bpsustain:0.2 bpenv:4 ]", +] +`; + +exports[`runs examples > example "bpenv" example index 0 1`] = ` +[ + "[ 0/1 → 1/1 | note:c2 s:sawtooth bandf:500 bpattack:0.5 bpenv:4 ]", + "[ 1/1 → 2/1 | note:e2 s:sawtooth bandf:500 bpattack:0.5 bpenv:4 ]", + "[ 2/1 → 3/1 | note:f2 s:sawtooth bandf:500 bpattack:0.5 bpenv:4 ]", + "[ 3/1 → 4/1 | note:g2 s:sawtooth bandf:500 bpattack:0.5 bpenv:4 ]", +] +`; + exports[`runs examples > example "bpf" example index 0 1`] = ` [ "[ 0/1 → 1/3 | s:hh bandf:1000 ]", @@ -938,6 +965,24 @@ exports[`runs examples > example "bpq" example index 0 1`] = ` ] `; +exports[`runs examples > example "bprelease" example index 0 1`] = ` +[ + "[ 0/1 → 1/1 | note:c2 s:sawtooth clip:0.5 bandf:500 bpenv:4 bprelease:0.5 release:0.5 ]", + "[ 1/1 → 2/1 | note:e2 s:sawtooth clip:0.5 bandf:500 bpenv:4 bprelease:0.5 release:0.5 ]", + "[ 2/1 → 3/1 | note:f2 s:sawtooth clip:0.5 bandf:500 bpenv:4 bprelease:0.5 release:0.5 ]", + "[ 3/1 → 4/1 | note:g2 s:sawtooth clip:0.5 bandf:500 bpenv:4 bprelease:0.5 release:0.5 ]", +] +`; + +exports[`runs examples > example "bpsustain" example index 0 1`] = ` +[ + "[ 0/1 → 1/1 | note:c2 s:sawtooth bandf:500 bpdecay:0.5 bpsustain:0 bpenv:4 ]", + "[ 1/1 → 2/1 | note:e2 s:sawtooth bandf:500 bpdecay:0.5 bpsustain:0 bpenv:4 ]", + "[ 2/1 → 3/1 | note:f2 s:sawtooth bandf:500 bpdecay:0.5 bpsustain:0 bpenv:4 ]", + "[ 3/1 → 4/1 | note:g2 s:sawtooth bandf:500 bpdecay:0.5 bpsustain:0 bpenv:4 ]", +] +`; + exports[`runs examples > example "cat" example index 0 1`] = ` [ "[ 0/1 → 1/2 | s:hh ]", @@ -1783,6 +1828,15 @@ exports[`runs examples > example "firstOf" example index 0 1`] = ` ] `; +exports[`runs examples > example "fit" example index 0 1`] = ` +[ + "[ (0/1 → 1/1) ⇝ 4/1 | s:rhodes speed:0.25 unit:c ]", + "[ 0/1 ⇜ (1/1 → 2/1) ⇝ 4/1 | s:rhodes speed:0.25 unit:c ]", + "[ 0/1 ⇜ (2/1 → 3/1) ⇝ 4/1 | s:rhodes speed:0.25 unit:c ]", + "[ 0/1 ⇜ (3/1 → 4/1) | s:rhodes speed:0.25 unit:c ]", +] +`; + exports[`runs examples > example "floor" example index 0 1`] = ` [ "[ 0/1 → 1/4 | note:42 ]", @@ -2013,6 +2067,15 @@ exports[`runs examples > example "freq" example index 1 1`] = ` ] `; +exports[`runs examples > example "ftype" example index 0 1`] = ` +[ + "[ 0/1 → 1/1 | note:c2 s:sawtooth cutoff:500 bpenv:4 ftype:12db ]", + "[ 1/1 → 2/1 | note:e2 s:sawtooth cutoff:500 bpenv:4 ftype:24db ]", + "[ 2/1 → 3/1 | note:f2 s:sawtooth cutoff:500 bpenv:4 ftype:12db ]", + "[ 3/1 → 4/1 | note:g2 s:sawtooth cutoff:500 bpenv:4 ftype:24db ]", +] +`; + exports[`runs examples > example "gain" example index 0 1`] = ` [ "[ 0/1 → 1/8 | s:hh gain:0.4 ]", @@ -2050,6 +2113,33 @@ exports[`runs examples > example "gain" example index 0 1`] = ` ] `; +exports[`runs examples > example "hpattack" example index 0 1`] = ` +[ + "[ 0/1 → 1/1 | note:c2 s:sawtooth hcutoff:500 hpattack:0.5 hpenv:4 ]", + "[ 1/1 → 2/1 | note:e2 s:sawtooth hcutoff:500 hpattack:0.5 hpenv:4 ]", + "[ 2/1 → 3/1 | note:f2 s:sawtooth hcutoff:500 hpattack:0.5 hpenv:4 ]", + "[ 3/1 → 4/1 | note:g2 s:sawtooth hcutoff:500 hpattack:0.5 hpenv:4 ]", +] +`; + +exports[`runs examples > example "hpdecay" example index 0 1`] = ` +[ + "[ 0/1 → 1/1 | note:c2 s:sawtooth hcutoff:500 hpdecay:0.5 hpsustain:0.2 hpenv:4 ]", + "[ 1/1 → 2/1 | note:e2 s:sawtooth hcutoff:500 hpdecay:0.5 hpsustain:0.2 hpenv:4 ]", + "[ 2/1 → 3/1 | note:f2 s:sawtooth hcutoff:500 hpdecay:0.5 hpsustain:0.2 hpenv:4 ]", + "[ 3/1 → 4/1 | note:g2 s:sawtooth hcutoff:500 hpdecay:0.5 hpsustain:0.2 hpenv:4 ]", +] +`; + +exports[`runs examples > example "hpenv" example index 0 1`] = ` +[ + "[ 0/1 → 1/1 | note:c2 s:sawtooth hcutoff:500 hpattack:0.5 hpenv:4 ]", + "[ 1/1 → 2/1 | note:e2 s:sawtooth hcutoff:500 hpattack:0.5 hpenv:4 ]", + "[ 2/1 → 3/1 | note:f2 s:sawtooth hcutoff:500 hpattack:0.5 hpenv:4 ]", + "[ 3/1 → 4/1 | note:g2 s:sawtooth hcutoff:500 hpattack:0.5 hpenv:4 ]", +] +`; + exports[`runs examples > example "hpf" example index 0 1`] = ` [ "[ 0/1 → 1/4 | s:hh hcutoff:4000 ]", @@ -2137,6 +2227,24 @@ exports[`runs examples > example "hpq" example index 0 1`] = ` ] `; +exports[`runs examples > example "hprelease" example index 0 1`] = ` +[ + "[ 0/1 → 1/1 | note:c2 s:sawtooth clip:0.5 hcutoff:500 hpenv:4 hprelease:0.5 release:0.5 ]", + "[ 1/1 → 2/1 | note:e2 s:sawtooth clip:0.5 hcutoff:500 hpenv:4 hprelease:0.5 release:0.5 ]", + "[ 2/1 → 3/1 | note:f2 s:sawtooth clip:0.5 hcutoff:500 hpenv:4 hprelease:0.5 release:0.5 ]", + "[ 3/1 → 4/1 | note:g2 s:sawtooth clip:0.5 hcutoff:500 hpenv:4 hprelease:0.5 release:0.5 ]", +] +`; + +exports[`runs examples > example "hpsustain" example index 0 1`] = ` +[ + "[ 0/1 → 1/1 | note:c2 s:sawtooth hcutoff:500 hpdecay:0.5 hpsustain:0 hpenv:4 ]", + "[ 1/1 → 2/1 | note:e2 s:sawtooth hcutoff:500 hpdecay:0.5 hpsustain:0 hpenv:4 ]", + "[ 2/1 → 3/1 | note:f2 s:sawtooth hcutoff:500 hpdecay:0.5 hpsustain:0 hpenv:4 ]", + "[ 3/1 → 4/1 | note:g2 s:sawtooth hcutoff:500 hpdecay:0.5 hpsustain:0 hpenv:4 ]", +] +`; + exports[`runs examples > example "hurry" example index 0 1`] = ` [ "[ 0/1 → 3/4 | s:bd speed:1 ]", @@ -2541,10 +2649,10 @@ exports[`runs examples > example "linger" example index 0 1`] = ` exports[`runs examples > example "loop" example index 0 1`] = ` [ - "[ 0/1 → 1/1 | s:bd loop:1 ]", - "[ 1/1 → 2/1 | s:bd loop:2 ]", - "[ 2/1 → 3/1 | s:bd loop:3 ]", - "[ 3/1 → 4/1 | s:bd loop:4 ]", + "[ 0/1 → 1/1 | s:casio loop:1 ]", + "[ 1/1 → 2/1 | s:casio loop:1 ]", + "[ 2/1 → 3/1 | s:casio loop:1 ]", + "[ 3/1 → 4/1 | s:casio loop:1 ]", ] `; @@ -2566,6 +2674,51 @@ exports[`runs examples > example "loopAtCps" example index 0 1`] = ` ] `; +exports[`runs examples > example "loopBegin" example index 0 1`] = ` +[ + "[ 0/1 → 1/1 | s:space loop:1 loopBegin:0 analyze:1 ]", + "[ 1/1 → 2/1 | s:space loop:1 loopBegin:0.125 analyze:1 ]", + "[ 2/1 → 3/1 | s:space loop:1 loopBegin:0.25 analyze:1 ]", + "[ 3/1 → 4/1 | s:space loop:1 loopBegin:0 analyze:1 ]", +] +`; + +exports[`runs examples > example "loopEnd" example index 0 1`] = ` +[ + "[ 0/1 → 1/1 | s:space loop:1 loopEnd:1 analyze:1 ]", + "[ 1/1 → 2/1 | s:space loop:1 loopEnd:0.75 analyze:1 ]", + "[ 2/1 → 3/1 | s:space loop:1 loopEnd:0.5 analyze:1 ]", + "[ 3/1 → 4/1 | s:space loop:1 loopEnd:0.25 analyze:1 ]", +] +`; + +exports[`runs examples > example "lpattack" example index 0 1`] = ` +[ + "[ 0/1 → 1/1 | note:c2 s:sawtooth cutoff:500 lpattack:0.5 lpenv:4 ]", + "[ 1/1 → 2/1 | note:e2 s:sawtooth cutoff:500 lpattack:0.5 lpenv:4 ]", + "[ 2/1 → 3/1 | note:f2 s:sawtooth cutoff:500 lpattack:0.5 lpenv:4 ]", + "[ 3/1 → 4/1 | note:g2 s:sawtooth cutoff:500 lpattack:0.5 lpenv:4 ]", +] +`; + +exports[`runs examples > example "lpdecay" example index 0 1`] = ` +[ + "[ 0/1 → 1/1 | note:c2 s:sawtooth cutoff:500 lpdecay:0.5 lpsustain:0.2 lpenv:4 ]", + "[ 1/1 → 2/1 | note:e2 s:sawtooth cutoff:500 lpdecay:0.5 lpsustain:0.2 lpenv:4 ]", + "[ 2/1 → 3/1 | note:f2 s:sawtooth cutoff:500 lpdecay:0.5 lpsustain:0.2 lpenv:4 ]", + "[ 3/1 → 4/1 | note:g2 s:sawtooth cutoff:500 lpdecay:0.5 lpsustain:0.2 lpenv:4 ]", +] +`; + +exports[`runs examples > example "lpenv" example index 0 1`] = ` +[ + "[ 0/1 → 1/1 | note:c2 s:sawtooth cutoff:500 lpattack:0.5 lpenv:4 ]", + "[ 1/1 → 2/1 | note:e2 s:sawtooth cutoff:500 lpattack:0.5 lpenv:4 ]", + "[ 2/1 → 3/1 | note:f2 s:sawtooth cutoff:500 lpattack:0.5 lpenv:4 ]", + "[ 3/1 → 4/1 | note:g2 s:sawtooth cutoff:500 lpattack:0.5 lpenv:4 ]", +] +`; + exports[`runs examples > example "lpf" example index 0 1`] = ` [ "[ 0/1 → 1/3 | s:hh cutoff:4000 ]", @@ -2657,6 +2810,24 @@ exports[`runs examples > example "lpq" example index 0 1`] = ` ] `; +exports[`runs examples > example "lprelease" example index 0 1`] = ` +[ + "[ 0/1 → 1/1 | note:c2 s:sawtooth clip:0.5 cutoff:500 lpenv:4 lprelease:0.5 release:0.5 ]", + "[ 1/1 → 2/1 | note:e2 s:sawtooth clip:0.5 cutoff:500 lpenv:4 lprelease:0.5 release:0.5 ]", + "[ 2/1 → 3/1 | note:f2 s:sawtooth clip:0.5 cutoff:500 lpenv:4 lprelease:0.5 release:0.5 ]", + "[ 3/1 → 4/1 | note:g2 s:sawtooth clip:0.5 cutoff:500 lpenv:4 lprelease:0.5 release:0.5 ]", +] +`; + +exports[`runs examples > example "lpsustain" example index 0 1`] = ` +[ + "[ 0/1 → 1/1 | note:c2 s:sawtooth cutoff:500 lpdecay:0.5 lpsustain:0 lpenv:4 ]", + "[ 1/1 → 2/1 | note:e2 s:sawtooth cutoff:500 lpdecay:0.5 lpsustain:0 lpenv:4 ]", + "[ 2/1 → 3/1 | note:f2 s:sawtooth cutoff:500 lpdecay:0.5 lpsustain:0 lpenv:4 ]", + "[ 3/1 → 4/1 | note:g2 s:sawtooth cutoff:500 lpdecay:0.5 lpsustain:0 lpenv:4 ]", +] +`; + exports[`runs examples > example "lrate" example index 0 1`] = ` [ "[ 0/1 → 1/1 | n:0 s:supersquare leslie:1 lrate:1 ]", @@ -4179,6 +4350,36 @@ exports[`runs examples > example "speed" example index 1 1`] = ` ] `; +exports[`runs examples > example "splice" example index 0 1`] = ` +[ + "[ 0/1 → 5/26 | speed:0.65 unit:c begin:0 end:0.125 _slices:8 s:breaks165 ]", + "[ 5/26 → 5/13 | speed:0.65 unit:c begin:0.125 end:0.25 _slices:8 s:breaks165 ]", + "[ 5/13 → 20/39 | speed:0.9750000000000001 unit:c begin:0.25 end:0.375 _slices:8 s:breaks165 ]", + "[ 20/39 → 25/39 | speed:0.9750000000000001 unit:c begin:0.375 end:0.5 _slices:8 s:breaks165 ]", + "[ 25/39 → 10/13 | speed:0.9750000000000001 unit:c begin:0 end:0.125 _slices:8 s:breaks165 ]", + "[ 10/13 → 25/26 | speed:0.65 unit:c begin:0.375 end:0.5 _slices:8 s:breaks165 ]", + "[ (25/26 → 1/1) ⇝ 35/26 | speed:0.325 unit:c begin:0 end:0.125 _slices:8 s:breaks165 ]", + "[ 25/26 ⇜ (1/1 → 35/26) | speed:0.325 unit:c begin:0 end:0.125 _slices:8 s:breaks165 ]", + "[ 35/26 → 20/13 | speed:0.65 unit:c begin:0.875 end:1 _slices:8 s:breaks165 ]", + "[ 20/13 → 45/26 | speed:0.65 unit:c begin:0 end:0.125 _slices:8 s:breaks165 ]", + "[ 45/26 → 25/13 | speed:0.65 unit:c begin:0.125 end:0.25 _slices:8 s:breaks165 ]", + "[ (25/13 → 2/1) ⇝ 80/39 | speed:0.9750000000000001 unit:c begin:0.25 end:0.375 _slices:8 s:breaks165 ]", + "[ 25/13 ⇜ (2/1 → 80/39) | speed:0.9750000000000001 unit:c begin:0.25 end:0.375 _slices:8 s:breaks165 ]", + "[ 80/39 → 85/39 | speed:0.9750000000000001 unit:c begin:0.375 end:0.5 _slices:8 s:breaks165 ]", + "[ 85/39 → 30/13 | speed:0.9750000000000001 unit:c begin:0 end:0.125 _slices:8 s:breaks165 ]", + "[ 30/13 → 5/2 | speed:0.65 unit:c begin:0.375 end:0.5 _slices:8 s:breaks165 ]", + "[ 5/2 → 75/26 | speed:0.325 unit:c begin:0 end:0.125 _slices:8 s:breaks165 ]", + "[ (75/26 → 3/1) ⇝ 40/13 | speed:0.65 unit:c begin:0.875 end:1 _slices:8 s:breaks165 ]", + "[ 75/26 ⇜ (3/1 → 40/13) | speed:0.65 unit:c begin:0.875 end:1 _slices:8 s:breaks165 ]", + "[ 40/13 → 85/26 | speed:0.65 unit:c begin:0 end:0.125 _slices:8 s:breaks165 ]", + "[ 85/26 → 45/13 | speed:0.65 unit:c begin:0.125 end:0.25 _slices:8 s:breaks165 ]", + "[ 45/13 → 140/39 | speed:0.9750000000000001 unit:c begin:0.25 end:0.375 _slices:8 s:breaks165 ]", + "[ 140/39 → 145/39 | speed:0.9750000000000001 unit:c begin:0.375 end:0.5 _slices:8 s:breaks165 ]", + "[ 145/39 → 50/13 | speed:0.9750000000000001 unit:c begin:0 end:0.125 _slices:8 s:breaks165 ]", + "[ (50/13 → 4/1) ⇝ 105/26 | speed:0.65 unit:c begin:0.375 end:0.5 _slices:8 s:breaks165 ]", +] +`; + exports[`runs examples > example "square" example index 0 1`] = ` [ "[ 0/1 → 1/2 | note:C3 ]", diff --git a/test/__snapshots__/tunes.test.mjs.snap b/test/__snapshots__/tunes.test.mjs.snap index 3c072ca2..aca92b55 100644 --- a/test/__snapshots__/tunes.test.mjs.snap +++ b/test/__snapshots__/tunes.test.mjs.snap @@ -3,23 +3,23 @@ exports[`renders tunes > tune: amensister 1`] = ` [ "[ 0/1 → 1/16 | s:breath room:1 shape:0.6 begin:0.9375 end:1 ]", - "[ 0/1 → 1/8 | note:Eb1 s:sawtooth decay:0.1 sustain:0 gain:0.4 cutoff:300.0066107586751 resonance:10 ]", - "[ 0/1 → 1/8 | note:F1 s:sawtooth decay:0.1 sustain:0 gain:0.4 cutoff:300.0066107586751 resonance:10 ]", + "[ 0/1 → 1/8 | note:Eb1 s:sawtooth decay:0.1 sustain:0 lpattack:0.1 lpenv:4 gain:0.4 cutoff:300.0066107586751 resonance:10 ]", + "[ 0/1 → 1/8 | note:F1 s:sawtooth decay:0.1 sustain:0 lpattack:0.1 lpenv:4 gain:0.4 cutoff:300.0066107586751 resonance:10 ]", "[ 0/1 → 1/4 | n:0 s:amencutup room:0.5 ]", "[ 1/16 → 1/8 | s:breath room:1 shape:0.6 begin:0.875 end:0.9375 ]", "[ 1/8 → 3/16 | s:breath room:1 shape:0.6 begin:0.8125 end:0.875 ]", - "[ 1/8 → 1/4 | note:45 s:sawtooth decay:0.1 sustain:0 gain:0.4 cutoff:300.174310575404 resonance:10 ]", - "[ 1/8 → 1/4 | note:45 s:sawtooth decay:0.1 sustain:0 gain:0.4 cutoff:300.174310575404 resonance:10 ]", + "[ 1/8 → 1/4 | note:45 s:sawtooth decay:0.1 sustain:0 lpattack:0.1 lpenv:4 gain:0.4 cutoff:300.174310575404 resonance:10 ]", + "[ 1/8 → 1/4 | note:45 s:sawtooth decay:0.1 sustain:0 lpattack:0.1 lpenv:4 gain:0.4 cutoff:300.174310575404 resonance:10 ]", "[ 3/16 → 1/4 | s:breath room:1 shape:0.6 begin:0.75 end:0.8125 ]", "[ 1/4 → 5/16 | s:breath room:1 shape:0.6 begin:0.6875 end:0.75 ]", "[ 1/4 → 3/8 | n:1 speed:2 delay:0.5 s:amencutup room:0.5 ]", - "[ 1/4 → 3/8 | note:A1 s:sawtooth decay:0.1 sustain:0 gain:0.4 cutoff:300.7878869297153 resonance:10 ]", - "[ 1/4 → 3/8 | note:A1 s:sawtooth decay:0.1 sustain:0 gain:0.4 cutoff:300.7878869297153 resonance:10 ]", + "[ 1/4 → 3/8 | note:A1 s:sawtooth decay:0.1 sustain:0 lpattack:0.1 lpenv:4 gain:0.4 cutoff:300.7878869297153 resonance:10 ]", + "[ 1/4 → 3/8 | note:A1 s:sawtooth decay:0.1 sustain:0 lpattack:0.1 lpenv:4 gain:0.4 cutoff:300.7878869297153 resonance:10 ]", "[ 5/16 → 3/8 | s:breath room:1 shape:0.6 begin:0.625 end:0.6875 ]", "[ 3/8 → 7/16 | s:breath room:1 shape:0.6 begin:0.5625 end:0.625 ]", "[ 3/8 → 1/2 | n:1 s:amencutup room:0.5 ]", - "[ 3/8 → 1/2 | note:F1 s:sawtooth decay:0.1 sustain:0 gain:0.4 cutoff:302.11020572391345 resonance:10 ]", - "[ 3/8 → 1/2 | note:F1 s:sawtooth decay:0.1 sustain:0 gain:0.4 cutoff:302.11020572391345 resonance:10 ]", + "[ 3/8 → 1/2 | note:F1 s:sawtooth decay:0.1 sustain:0 lpattack:0.1 lpenv:4 gain:0.4 cutoff:302.11020572391345 resonance:10 ]", + "[ 3/8 → 1/2 | note:F1 s:sawtooth decay:0.1 sustain:0 lpattack:0.1 lpenv:4 gain:0.4 cutoff:302.11020572391345 resonance:10 ]", "[ 7/16 → 1/2 | s:breath room:1 shape:0.6 begin:0.5 end:0.5625 ]", "[ 1/2 → 9/16 | s:breath room:1 shape:0.6 begin:0.4375 end:0.5 ]", "[ 1/2 → 3/4 | n:2 s:amencutup room:0.5 ]", @@ -28,13 +28,13 @@ exports[`renders tunes > tune: amensister 1`] = ` "[ 11/16 → 3/4 | s:breath room:1 shape:0.6 begin:0.25 end:0.3125 ]", "[ 3/4 → 13/16 | s:breath room:1 shape:0.6 begin:0.1875 end:0.25 ]", "[ 3/4 → 7/8 | n:3 s:amencutup room:0.5 ]", - "[ 3/4 → 7/8 | note:Bb0 s:sawtooth decay:0.1 sustain:0 gain:0.4 cutoff:312.54769231985796 resonance:10 ]", - "[ 3/4 → 7/8 | note:Bb0 s:sawtooth decay:0.1 sustain:0 gain:0.4 cutoff:312.54769231985796 resonance:10 ]", + "[ 3/4 → 7/8 | note:Bb0 s:sawtooth decay:0.1 sustain:0 lpattack:0.1 lpenv:4 gain:0.4 cutoff:312.54769231985796 resonance:10 ]", + "[ 3/4 → 7/8 | note:Bb0 s:sawtooth decay:0.1 sustain:0 lpattack:0.1 lpenv:4 gain:0.4 cutoff:312.54769231985796 resonance:10 ]", "[ 13/16 → 7/8 | s:breath room:1 shape:0.6 begin:0.125 end:0.1875 ]", "[ 7/8 → 15/16 | s:breath room:1 shape:0.6 begin:0.0625 end:0.125 ]", "[ 7/8 → 1/1 | n:3 s:amencutup room:0.5 ]", - "[ 7/8 → 1/1 | note:D1 s:sawtooth decay:0.1 sustain:0 gain:0.4 cutoff:318.7927796831686 resonance:10 ]", - "[ 7/8 → 1/1 | note:D1 s:sawtooth decay:0.1 sustain:0 gain:0.4 cutoff:318.7927796831686 resonance:10 ]", + "[ 7/8 → 1/1 | note:D1 s:sawtooth decay:0.1 sustain:0 lpattack:0.1 lpenv:4 gain:0.4 cutoff:318.7927796831686 resonance:10 ]", + "[ 7/8 → 1/1 | note:D1 s:sawtooth decay:0.1 sustain:0 lpattack:0.1 lpenv:4 gain:0.4 cutoff:318.7927796831686 resonance:10 ]", "[ 15/16 → 1/1 | s:breath room:1 shape:0.6 begin:0 end:0.0625 ]", ] `; @@ -323,8 +323,8 @@ exports[`renders tunes > tune: blippyRhodes 1`] = ` [ "[ 0/1 → 1/6 | note:G4 clip:0.3 s:rhodes room:0.5 delay:0.3 delayfeedback:0.4 delaytime:0.08333333333333333 gain:0.5 ]", "[ 0/1 → 1/3 | s:bd ]", - "[ (0/1 → 2/3) ⇝ 4/3 | note:36 gain:0.3 clip:1 s:sawtooth cutoff:600 ]", - "[ (0/1 → 2/3) ⇝ 4/3 | note:36.02 gain:0.3 clip:1 s:sawtooth cutoff:600 ]", + "[ (0/1 → 2/3) ⇝ 4/3 | note:36 gain:0.3 clip:1 cutoff:600 lpattack:0.2 lpenv:-4 s:sawtooth ]", + "[ (0/1 → 2/3) ⇝ 4/3 | note:36.02 gain:0.3 clip:1 cutoff:600 lpattack:0.2 lpenv:-4 s:sawtooth ]", "[ 1/6 → 1/3 | note:G4 clip:0.3 s:rhodes room:0.5 delay:0.3 delayfeedback:0.4 delaytime:0.08333333333333333 gain:0.5 ]", "[ 1/3 → 1/2 | note:B3 clip:0.3 s:rhodes room:0.5 delay:0.3 delayfeedback:0.4 delaytime:0.08333333333333333 gain:0.5 ]", "[ 1/3 → 1/2 | note:E4 clip:0.3 s:rhodes room:0.5 delay:0.3 delayfeedback:0.4 delaytime:0.08333333333333333 gain:0.5 ]", @@ -332,8 +332,8 @@ exports[`renders tunes > tune: blippyRhodes 1`] = ` "[ 1/2 → 2/3 | note:B3 clip:0.3 s:rhodes room:0.5 delay:0.3 delayfeedback:0.4 delaytime:0.08333333333333333 gain:0.5 ]", "[ 1/2 → 2/3 | note:E4 clip:0.3 s:rhodes room:0.5 delay:0.3 delayfeedback:0.4 delaytime:0.08333333333333333 gain:0.5 ]", "[ 2/3 → 5/6 | note:G3 clip:0.3 s:rhodes room:0.5 delay:0.3 delayfeedback:0.4 delaytime:0.08333333333333333 gain:0.5 ]", - "[ 0/1 ⇜ (2/3 → 1/1) ⇝ 4/3 | note:36 gain:0.3 clip:1 s:sawtooth cutoff:600 ]", - "[ 0/1 ⇜ (2/3 → 1/1) ⇝ 4/3 | note:36.02 gain:0.3 clip:1 s:sawtooth cutoff:600 ]", + "[ 0/1 ⇜ (2/3 → 1/1) ⇝ 4/3 | note:36 gain:0.3 clip:1 cutoff:600 lpattack:0.2 lpenv:-4 s:sawtooth ]", + "[ 0/1 ⇜ (2/3 → 1/1) ⇝ 4/3 | note:36.02 gain:0.3 clip:1 cutoff:600 lpattack:0.2 lpenv:-4 s:sawtooth ]", "[ 2/3 → 1/1 | s:sn ]", "[ 5/6 → 1/1 | note:G3 clip:0.3 s:rhodes room:0.5 delay:0.3 delayfeedback:0.4 delaytime:0.08333333333333333 gain:0.5 ]", ] @@ -7722,8 +7722,8 @@ exports[`renders tunes > tune: hyperpop 1`] = ` "[ -1/8 ⇜ (0/1 → 3/8) | note:B3 s:square gain:0.7 attack:0.01 decay:0.1 sustain:0 cutoff:1699.6897509708342 ]", "[ -1/4 ⇜ (1/12 → 1/8) | note:A5 s:sawtooth gain:0.2536811842784369 attack:0.001 decay:0.2 sustain:0 hcutoff:5999.785818935017 cutoff:4000 ]", "[ -1/4 ⇜ (1/12 → 1/8) | note:C#5 s:sawtooth gain:0.2536811842784369 attack:0.001 decay:0.2 sustain:0 hcutoff:5999.785818935017 cutoff:4000 ]", - "[ 1/8 → 1/4 | note:D1 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:1699.6897509708342 ]", - "[ 1/8 → 1/4 | note:D1 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:1699.6897509708342 ]", + "[ 1/8 → 1/4 | note:D1 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:1699.6897509708342 lpattack:0.1 lpenv:2 ftype:24db ]", + "[ 1/8 → 1/4 | note:D1 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:1699.6897509708342 lpattack:0.1 lpenv:2 ftype:24db ]", "[ (1/8 → 5/12) ⇝ 1/2 | note:A5 s:sawtooth gain:0.26836160127988246 attack:0.001 decay:0.2 sustain:0 hcutoff:5994.647308096509 cutoff:4000 ]", "[ (1/8 → 5/12) ⇝ 1/2 | note:C#5 s:sawtooth gain:0.26836160127988246 attack:0.001 decay:0.2 sustain:0 hcutoff:5994.647308096509 cutoff:4000 ]", "[ -1/8 ⇜ (1/6 → 1/4) | note:F#5 s:sawtooth gain:0.2573601511491127 attack:0.001 decay:0.2 sustain:0 hcutoff:5999.143312438893 cutoff:4000 ]", @@ -7735,14 +7735,14 @@ exports[`renders tunes > tune: hyperpop 1`] = ` "[ (1/4 → 7/12) ⇝ 5/8 | note:E5 s:sawtooth gain:0.2756442833140452 attack:0.001 decay:0.2 sustain:0 hcutoff:5989.512318936654 cutoff:4000 ]", "[ 0/1 ⇜ (1/3 → 3/8) | note:A5 s:sawtooth gain:0.26103468453995016 attack:0.001 decay:0.2 sustain:0 hcutoff:5998.072590601808 cutoff:4000 ]", "[ 0/1 ⇜ (1/3 → 3/8) | note:C#5 s:sawtooth gain:0.26103468453995016 attack:0.001 decay:0.2 sustain:0 hcutoff:5998.072590601808 cutoff:4000 ]", - "[ 3/8 → 1/2 | note:D2 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:1765.826371664994 ]", - "[ 3/8 → 1/2 | note:D2 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:1765.826371664994 ]", + "[ 3/8 → 1/2 | note:D2 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:1765.826371664994 lpattack:0.1 lpenv:2 ftype:24db ]", + "[ 3/8 → 1/2 | note:D2 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:1765.826371664994 lpattack:0.1 lpenv:2 ftype:24db ]", "[ (3/8 → 2/3) ⇝ 3/4 | note:A5 s:sawtooth gain:0.2828651860235305 attack:0.001 decay:0.2 sustain:0 hcutoff:5982.671142387316 cutoff:4000 ]", "[ (3/8 → 2/3) ⇝ 3/4 | note:C#5 s:sawtooth gain:0.2828651860235305 attack:0.001 decay:0.2 sustain:0 hcutoff:5982.671142387316 cutoff:4000 ]", "[ 1/8 ⇜ (5/12 → 1/2) | note:F#5 s:sawtooth gain:0.26836160127988246 attack:0.001 decay:0.2 sustain:0 hcutoff:5994.647308096509 cutoff:4000 ]", "[ 1/8 ⇜ (5/12 → 1/2) | note:A4 s:sawtooth gain:0.26836160127988246 attack:0.001 decay:0.2 sustain:0 hcutoff:5994.647308096509 cutoff:4000 ]", - "[ 1/2 → 5/8 | note:D1 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:1798.799979846742 ]", - "[ 1/2 → 5/8 | note:D1 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:1798.799979846742 ]", + "[ 1/2 → 5/8 | note:D1 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:1798.799979846742 lpattack:0.1 lpenv:2 ftype:24db ]", + "[ 1/2 → 5/8 | note:D1 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:1798.799979846742 lpattack:0.1 lpenv:2 ftype:24db ]", "[ 1/2 → 3/4 | note:F#5 s:sawtooth gain:0.28644702698548963 attack:0.001 decay:0.2 sustain:0 hcutoff:5978.612153434527 cutoff:4000 ]", "[ 1/2 → 3/4 | note:A4 s:sawtooth gain:0.28644702698548963 attack:0.001 decay:0.2 sustain:0 hcutoff:5978.612153434527 cutoff:4000 ]", "[ 1/2 → 3/4 | s:bd gain:0.7 ]", @@ -7755,8 +7755,8 @@ exports[`renders tunes > tune: hyperpop 1`] = ` "[ (5/8 → 11/12) ⇝ 1/1 | note:C#5 s:sawtooth gain:0.29705226105983373 attack:0.001 decay:0.2 sustain:0 hcutoff:5963.890147645195 cutoff:4000 ]", "[ 3/8 ⇜ (2/3 → 3/4) | note:F#5 s:sawtooth gain:0.2828651860235305 attack:0.001 decay:0.2 sustain:0 hcutoff:5982.671142387316 cutoff:4000 ]", "[ 3/8 ⇜ (2/3 → 3/4) | note:A4 s:sawtooth gain:0.2828651860235305 attack:0.001 decay:0.2 sustain:0 hcutoff:5982.671142387316 cutoff:4000 ]", - "[ 3/4 → 7/8 | note:D3 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:1864.4584935007128 ]", - "[ 3/4 → 7/8 | note:D3 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:1864.4584935007128 ]", + "[ 3/4 → 7/8 | note:D3 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:1864.4584935007128 lpattack:0.1 lpenv:2 ftype:24db ]", + "[ 3/4 → 7/8 | note:D3 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:1864.4584935007128 lpattack:0.1 lpenv:2 ftype:24db ]", "[ 3/4 → 1/1 | note:F#5 s:sawtooth gain:0.300533478008833 attack:0.001 decay:0.2 sustain:0 hcutoff:5958.137268909887 cutoff:4000 ]", "[ 3/4 → 1/1 | note:A4 s:sawtooth gain:0.300533478008833 attack:0.001 decay:0.2 sustain:0 hcutoff:5958.137268909887 cutoff:4000 ]", "[ 3/4 → 1/1 | s:hh3 gain:0.7 ]", @@ -7764,8 +7764,8 @@ exports[`renders tunes > tune: hyperpop 1`] = ` "[ (3/4 → 1/1) ⇝ 9/8 | note:E5 s:sawtooth gain:0.30398425548024827 attack:0.001 decay:0.2 sustain:0 hcutoff:5951.963201008076 cutoff:4000 ]", "[ 1/2 ⇜ (5/6 → 7/8) | note:A5 s:sawtooth gain:0.29000691362123476 attack:0.001 decay:0.2 sustain:0 hcutoff:5974.128467049176 cutoff:4000 ]", "[ 1/2 ⇜ (5/6 → 7/8) | note:C#5 s:sawtooth gain:0.29000691362123476 attack:0.001 decay:0.2 sustain:0 hcutoff:5974.128467049176 cutoff:4000 ]", - "[ 7/8 → 1/1 | note:D3 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:1897.1038487394403 ]", - "[ 7/8 → 1/1 | note:D3 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:1897.1038487394403 ]", + "[ 7/8 → 1/1 | note:D3 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:1897.1038487394403 lpattack:0.1 lpenv:2 ftype:24db ]", + "[ 7/8 → 1/1 | note:D3 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:1897.1038487394403 lpattack:0.1 lpenv:2 ftype:24db ]", "[ (7/8 → 1/1) ⇝ 5/4 | note:A5 s:sawtooth gain:0.3107861971007485 attack:0.001 decay:0.2 sustain:0 hcutoff:5938.355801271282 cutoff:4000 ]", "[ (7/8 → 1/1) ⇝ 5/4 | note:C#5 s:sawtooth gain:0.3107861971007485 attack:0.001 decay:0.2 sustain:0 hcutoff:5938.355801271282 cutoff:4000 ]", "[ 5/8 ⇜ (11/12 → 1/1) | note:F#5 s:sawtooth gain:0.29705226105983373 attack:0.001 decay:0.2 sustain:0 hcutoff:5963.890147645195 cutoff:4000 ]", @@ -7781,8 +7781,8 @@ exports[`renders tunes > tune: hyperpop 1`] = ` "[ (1/1 → 4/3) ⇝ 11/8 | note:E5 s:sawtooth gain:0.317441699448191 attack:0.001 decay:0.2 sustain:0 hcutoff:5923.077274266886 cutoff:4000 ]", "[ 3/4 ⇜ (13/12 → 9/8) | note:A5 s:sawtooth gain:0.30398425548024827 attack:0.001 decay:0.2 sustain:0 hcutoff:5951.963201008076 cutoff:4000 ]", "[ 3/4 ⇜ (13/12 → 9/8) | note:C#5 s:sawtooth gain:0.30398425548024827 attack:0.001 decay:0.2 sustain:0 hcutoff:5951.963201008076 cutoff:4000 ]", - "[ 9/8 → 5/4 | note:D1 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:1961.928446178906 ]", - "[ 9/8 → 5/4 | note:D1 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:1961.928446178906 ]", + "[ 9/8 → 5/4 | note:D1 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:1961.928446178906 lpattack:0.1 lpenv:2 ftype:24db ]", + "[ 9/8 → 5/4 | note:D1 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:1961.928446178906 lpattack:0.1 lpenv:2 ftype:24db ]", "[ (9/8 → 17/12) ⇝ 3/2 | note:A5 s:sawtooth gain:0.3239347288344676 attack:0.001 decay:0.2 sustain:0 hcutoff:5906.1380911341175 cutoff:4000 ]", "[ (9/8 → 17/12) ⇝ 3/2 | note:C#5 s:sawtooth gain:0.3239347288344676 attack:0.001 decay:0.2 sustain:0 hcutoff:5906.1380911341175 cutoff:4000 ]", "[ 7/8 ⇜ (7/6 → 5/4) | note:F#5 s:sawtooth gain:0.3107861971007485 attack:0.001 decay:0.2 sustain:0 hcutoff:5938.355801271282 cutoff:4000 ]", @@ -7794,14 +7794,14 @@ exports[`renders tunes > tune: hyperpop 1`] = ` "[ (5/4 → 19/12) ⇝ 13/8 | note:E5 s:sawtooth gain:0.3302496429830646 attack:0.001 decay:0.2 sustain:0 hcutoff:5887.549861142967 cutoff:4000 ]", "[ 1/1 ⇜ (4/3 → 11/8) | note:A5 s:sawtooth gain:0.317441699448191 attack:0.001 decay:0.2 sustain:0 hcutoff:5923.077274266886 cutoff:4000 ]", "[ 1/1 ⇜ (4/3 → 11/8) | note:C#5 s:sawtooth gain:0.317441699448191 attack:0.001 decay:0.2 sustain:0 hcutoff:5923.077274266886 cutoff:4000 ]", - "[ 11/8 → 3/2 | note:D2 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2026.0015806698216 ]", - "[ 11/8 → 3/2 | note:D2 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2026.0015806698216 ]", + "[ 11/8 → 3/2 | note:D2 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2026.0015806698216 lpattack:0.1 lpenv:2 ftype:24db ]", + "[ 11/8 → 3/2 | note:D2 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2026.0015806698216 lpattack:0.1 lpenv:2 ftype:24db ]", "[ (11/8 → 5/3) ⇝ 7/4 | note:A5 s:sawtooth gain:0.3363712287126769 attack:0.001 decay:0.2 sustain:0 hcutoff:5867.325323737765 cutoff:4000 ]", "[ (11/8 → 5/3) ⇝ 7/4 | note:C#5 s:sawtooth gain:0.3363712287126769 attack:0.001 decay:0.2 sustain:0 hcutoff:5867.325323737765 cutoff:4000 ]", "[ 9/8 ⇜ (17/12 → 3/2) | note:F#5 s:sawtooth gain:0.3239347288344676 attack:0.001 decay:0.2 sustain:0 hcutoff:5906.1380911341175 cutoff:4000 ]", "[ 9/8 ⇜ (17/12 → 3/2) | note:A4 s:sawtooth gain:0.3239347288344676 attack:0.001 decay:0.2 sustain:0 hcutoff:5906.1380911341175 cutoff:4000 ]", - "[ 3/2 → 13/8 | note:D1 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2057.708031580958 ]", - "[ 3/2 → 13/8 | note:D1 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2057.708031580958 ]", + "[ 3/2 → 13/8 | note:D1 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2057.708031580958 lpattack:0.1 lpenv:2 ftype:24db ]", + "[ 3/2 → 13/8 | note:D1 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2057.708031580958 lpattack:0.1 lpenv:2 ftype:24db ]", "[ 3/2 → 7/4 | note:F#5 s:sawtooth gain:0.339354895673865 attack:0.001 decay:0.2 sustain:0 hcutoff:5856.603727730447 cutoff:4000 ]", "[ 3/2 → 7/4 | note:A4 s:sawtooth gain:0.339354895673865 attack:0.001 decay:0.2 sustain:0 hcutoff:5856.603727730447 cutoff:4000 ]", "[ 3/2 → 7/4 | s:bd gain:0.7 ]", @@ -7818,8 +7818,8 @@ exports[`renders tunes > tune: hyperpop 1`] = ` "[ (13/8 → 2/1) ⇝ 17/8 | note:A3 s:square gain:0.7 attack:0.01 decay:0.1 sustain:0 cutoff:2120.3652183367367 ]", "[ 11/8 ⇜ (5/3 → 7/4) | note:F#5 s:sawtooth gain:0.3363712287126769 attack:0.001 decay:0.2 sustain:0 hcutoff:5867.325323737765 cutoff:4000 ]", "[ 11/8 ⇜ (5/3 → 7/4) | note:A4 s:sawtooth gain:0.3363712287126769 attack:0.001 decay:0.2 sustain:0 hcutoff:5867.325323737765 cutoff:4000 ]", - "[ 7/4 → 15/8 | note:D3 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2120.3652183367367 ]", - "[ 7/4 → 15/8 | note:D3 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2120.3652183367367 ]", + "[ 7/4 → 15/8 | note:D3 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2120.3652183367367 lpattack:0.1 lpenv:2 ftype:24db ]", + "[ 7/4 → 15/8 | note:D3 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2120.3652183367367 lpattack:0.1 lpenv:2 ftype:24db ]", "[ 7/4 → 2/1 | note:F#5 s:sawtooth gain:0.3507338432270528 attack:0.001 decay:0.2 sustain:0 hcutoff:5809.698831278217 cutoff:4000 ]", "[ 7/4 → 2/1 | note:A4 s:sawtooth gain:0.3507338432270528 attack:0.001 decay:0.2 sustain:0 hcutoff:5809.698831278217 cutoff:4000 ]", "[ 7/4 → 2/1 | s:hh3 gain:0.7 ]", @@ -7829,8 +7829,8 @@ exports[`renders tunes > tune: hyperpop 1`] = ` "[ (7/4 → 2/1) ⇝ 9/4 | note:A3 s:square gain:0.7 attack:0.01 decay:0.1 sustain:0 cutoff:2135.8582993222344 ]", "[ 3/2 ⇜ (11/6 → 15/8) | note:A5 s:sawtooth gain:0.3422847385870941 attack:0.001 decay:0.2 sustain:0 hcutoff:5845.47833980621 cutoff:4000 ]", "[ 3/2 ⇜ (11/6 → 15/8) | note:C#5 s:sawtooth gain:0.3422847385870941 attack:0.001 decay:0.2 sustain:0 hcutoff:5845.47833980621 cutoff:4000 ]", - "[ 15/8 → 2/1 | note:D3 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2151.2782118349805 ]", - "[ 15/8 → 2/1 | note:D3 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2151.2782118349805 ]", + "[ 15/8 → 2/1 | note:D3 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2151.2782118349805 lpattack:0.1 lpenv:2 ftype:24db ]", + "[ 15/8 → 2/1 | note:D3 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2151.2782118349805 lpattack:0.1 lpenv:2 ftype:24db ]", "[ (15/8 → 2/1) ⇝ 9/4 | note:A5 s:sawtooth gain:0.35343108171056004 attack:0.001 decay:0.2 sustain:0 hcutoff:5796.978025372246 cutoff:4000 ]", "[ (15/8 → 2/1) ⇝ 9/4 | note:C#5 s:sawtooth gain:0.35343108171056004 attack:0.001 decay:0.2 sustain:0 hcutoff:5796.978025372246 cutoff:4000 ]", "[ (15/8 → 2/1) ⇝ 19/8 | note:F#3 s:square gain:0.7 attack:0.01 decay:0.1 sustain:0 cutoff:2151.2782118349805 ]", @@ -7854,8 +7854,8 @@ exports[`renders tunes > tune: hyperpop 1`] = ` "[ 15/8 ⇜ (2/1 → 19/8) | note:A3 s:square gain:0.7 attack:0.01 decay:0.1 sustain:0 cutoff:2212.17990613181 ]", "[ 7/4 ⇜ (25/12 → 17/8) | note:A5 s:sawtooth gain:0.3586370624427201 attack:0.001 decay:0.2 sustain:0 hcutoff:5770.357934562703 cutoff:4000 ]", "[ 7/4 ⇜ (25/12 → 17/8) | note:C#5 s:sawtooth gain:0.3586370624427201 attack:0.001 decay:0.2 sustain:0 hcutoff:5770.357934562703 cutoff:4000 ]", - "[ 17/8 → 9/4 | note:D1 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2212.17990613181 ]", - "[ 17/8 → 9/4 | note:D1 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2212.17990613181 ]", + "[ 17/8 → 9/4 | note:D1 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2212.17990613181 lpattack:0.1 lpenv:2 ftype:24db ]", + "[ 17/8 → 9/4 | note:D1 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2212.17990613181 lpattack:0.1 lpenv:2 ftype:24db ]", "[ (17/8 → 29/12) ⇝ 5/2 | note:A5 s:sawtooth gain:0.368251964143991 attack:0.001 decay:0.2 sustain:0 hcutoff:5712.469093657604 cutoff:4000 ]", "[ (17/8 → 29/12) ⇝ 5/2 | note:C#5 s:sawtooth gain:0.368251964143991 attack:0.001 decay:0.2 sustain:0 hcutoff:5712.469093657604 cutoff:4000 ]", "[ 15/8 ⇜ (13/6 → 9/4) | note:F#5 s:sawtooth gain:0.36114266880324397 attack:0.001 decay:0.2 sustain:0 hcutoff:5756.463210874651 cutoff:4000 ]", @@ -7867,14 +7867,14 @@ exports[`renders tunes > tune: hyperpop 1`] = ` "[ (9/4 → 31/12) ⇝ 21/8 | note:E5 s:sawtooth gain:0.3726377219727376 attack:0.001 decay:0.2 sustain:0 hcutoff:5681.240017681994 cutoff:4000 ]", "[ 2/1 ⇜ (7/3 → 19/8) | note:A5 s:sawtooth gain:0.3635813269759728 attack:0.001 decay:0.2 sustain:0 hcutoff:5742.18185383172 cutoff:4000 ]", "[ 2/1 ⇜ (7/3 → 19/8) | note:C#5 s:sawtooth gain:0.3635813269759728 attack:0.001 decay:0.2 sustain:0 hcutoff:5742.18185383172 cutoff:4000 ]", - "[ 19/8 → 5/2 | note:D2 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2271.727259793624 ]", - "[ 19/8 → 5/2 | note:D2 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2271.727259793624 ]", + "[ 19/8 → 5/2 | note:D2 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2271.727259793624 lpattack:0.1 lpenv:2 ftype:24db ]", + "[ 19/8 → 5/2 | note:D2 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2271.727259793624 lpattack:0.1 lpenv:2 ftype:24db ]", "[ (19/8 → 8/3) ⇝ 11/4 | note:A5 s:sawtooth gain:0.3767280347874561 attack:0.001 decay:0.2 sustain:0 hcutoff:5648.516028753632 cutoff:4000 ]", "[ (19/8 → 8/3) ⇝ 11/4 | note:C#5 s:sawtooth gain:0.3767280347874561 attack:0.001 decay:0.2 sustain:0 hcutoff:5648.516028753632 cutoff:4000 ]", "[ 17/8 ⇜ (29/12 → 5/2) | note:F#5 s:sawtooth gain:0.368251964143991 attack:0.001 decay:0.2 sustain:0 hcutoff:5712.469093657604 cutoff:4000 ]", "[ 17/8 ⇜ (29/12 → 5/2) | note:A4 s:sawtooth gain:0.368251964143991 attack:0.001 decay:0.2 sustain:0 hcutoff:5712.469093657604 cutoff:4000 ]", - "[ 5/2 → 21/8 | note:D1 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2300.948092306816 ]", - "[ 5/2 → 21/8 | note:D1 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2300.948092306816 ]", + "[ 5/2 → 21/8 | note:D1 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2300.948092306816 lpattack:0.1 lpenv:2 ftype:24db ]", + "[ 5/2 → 21/8 | note:D1 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2300.948092306816 lpattack:0.1 lpenv:2 ftype:24db ]", "[ 5/2 → 11/4 | note:F#5 s:sawtooth gain:0.37865929150004085 attack:0.001 decay:0.2 sustain:0 hcutoff:5631.60041088523 cutoff:4000 ]", "[ 5/2 → 11/4 | note:A4 s:sawtooth gain:0.37865929150004085 attack:0.001 decay:0.2 sustain:0 hcutoff:5631.60041088523 cutoff:4000 ]", "[ 5/2 → 11/4 | s:bd gain:0.7 ]", @@ -7887,8 +7887,8 @@ exports[`renders tunes > tune: hyperpop 1`] = ` "[ (21/8 → 35/12) ⇝ 3/1 | note:C#5 s:sawtooth gain:0.38398364517932737 attack:0.001 decay:0.2 sustain:0 hcutoff:5578.674030756363 cutoff:4000 ]", "[ 19/8 ⇜ (8/3 → 11/4) | note:F#5 s:sawtooth gain:0.3767280347874561 attack:0.001 decay:0.2 sustain:0 hcutoff:5648.516028753632 cutoff:4000 ]", "[ 19/8 ⇜ (8/3 → 11/4) | note:A4 s:sawtooth gain:0.3767280347874561 attack:0.001 decay:0.2 sustain:0 hcutoff:5648.516028753632 cutoff:4000 ]", - "[ 11/4 → 23/8 | note:D3 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2358.1960716159333 ]", - "[ 11/4 → 23/8 | note:D3 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2358.1960716159333 ]", + "[ 11/4 → 23/8 | note:D3 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2358.1960716159333 lpattack:0.1 lpenv:2 ftype:24db ]", + "[ 11/4 → 23/8 | note:D3 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2358.1960716159333 lpattack:0.1 lpenv:2 ftype:24db ]", "[ 11/4 → 3/1 | note:F#5 s:sawtooth gain:0.3855983939685166 attack:0.001 decay:0.2 sustain:0 hcutoff:5560.31547155504 cutoff:4000 ]", "[ 11/4 → 3/1 | note:A4 s:sawtooth gain:0.3855983939685166 attack:0.001 decay:0.2 sustain:0 hcutoff:5560.31547155504 cutoff:4000 ]", "[ 11/4 → 3/1 | s:hh3 gain:0.7 ]", @@ -7896,8 +7896,8 @@ exports[`renders tunes > tune: hyperpop 1`] = ` "[ (11/4 → 3/1) ⇝ 25/8 | note:E5 s:sawtooth gain:0.3871314633555296 attack:0.001 decay:0.2 sustain:0 hcutoff:5541.603887904197 cutoff:4000 ]", "[ 5/2 ⇜ (17/6 → 23/8) | note:A5 s:sawtooth gain:0.38051304866630675 attack:0.001 decay:0.2 sustain:0 hcutoff:5614.319554259933 cutoff:4000 ]", "[ 5/2 ⇜ (17/6 → 23/8) | note:C#5 s:sawtooth gain:0.38051304866630675 attack:0.001 decay:0.2 sustain:0 hcutoff:5614.319554259933 cutoff:4000 ]", - "[ 23/8 → 3/1 | note:D3 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2386.1887343697626 ]", - "[ 23/8 → 3/1 | note:D3 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2386.1887343697626 ]", + "[ 23/8 → 3/1 | note:D3 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2386.1887343697626 lpattack:0.1 lpenv:2 ftype:24db ]", + "[ 23/8 → 3/1 | note:D3 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2386.1887343697626 lpattack:0.1 lpenv:2 ftype:24db ]", "[ (23/8 → 3/1) ⇝ 13/4 | note:A5 s:sawtooth gain:0.38994891982521085 attack:0.001 decay:0.2 sustain:0 hcutoff:5503.134531727652 cutoff:4000 ]", "[ (23/8 → 3/1) ⇝ 13/4 | note:C#5 s:sawtooth gain:0.38994891982521085 attack:0.001 decay:0.2 sustain:0 hcutoff:5503.134531727652 cutoff:4000 ]", "[ 21/8 ⇜ (35/12 → 3/1) | note:F#5 s:sawtooth gain:0.38398364517932737 attack:0.001 decay:0.2 sustain:0 hcutoff:5578.674030756363 cutoff:4000 ]", @@ -7913,8 +7913,8 @@ exports[`renders tunes > tune: hyperpop 1`] = ` "[ (3/1 → 10/3) ⇝ 27/8 | note:E5 s:sawtooth gain:0.39242922708895556 attack:0.001 decay:0.2 sustain:0 hcutoff:5463.2923272018625 cutoff:4000 ]", "[ 11/4 ⇜ (37/12 → 25/8) | note:A5 s:sawtooth gain:0.3871314633555296 attack:0.001 decay:0.2 sustain:0 hcutoff:5541.603887904197 cutoff:4000 ]", "[ 11/4 ⇜ (37/12 → 25/8) | note:C#5 s:sawtooth gain:0.3871314633555296 attack:0.001 decay:0.2 sustain:0 hcutoff:5541.603887904197 cutoff:4000 ]", - "[ 25/8 → 13/4 | note:D1 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2440.8271075661924 ]", - "[ 25/8 → 13/4 | note:D1 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2440.8271075661924 ]", + "[ 25/8 → 13/4 | note:D1 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2440.8271075661924 lpattack:0.1 lpenv:2 ftype:24db ]", + "[ 25/8 → 13/4 | note:D1 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2440.8271075661924 lpattack:0.1 lpenv:2 ftype:24db ]", "[ (25/8 → 41/12) ⇝ 7/2 | note:A5 s:sawtooth gain:0.394566409869316 attack:0.001 decay:0.2 sustain:0 hcutoff:5422.104580183649 cutoff:4000 ]", "[ (25/8 → 41/12) ⇝ 7/2 | note:C#5 s:sawtooth gain:0.394566409869316 attack:0.001 decay:0.2 sustain:0 hcutoff:5422.104580183649 cutoff:4000 ]", "[ 23/8 ⇜ (19/6 → 13/4) | note:F#5 s:sawtooth gain:0.38994891982521085 attack:0.001 decay:0.2 sustain:0 hcutoff:5503.134531727652 cutoff:4000 ]", @@ -7926,14 +7926,14 @@ exports[`renders tunes > tune: hyperpop 1`] = ` "[ (13/4 → 43/12) ⇝ 29/8 | note:E5 s:sawtooth gain:0.3963553195057793 attack:0.001 decay:0.2 sustain:0 hcutoff:5379.599518697443 cutoff:4000 ]", "[ 3/1 ⇜ (10/3 → 27/8) | note:A5 s:sawtooth gain:0.39242922708895556 attack:0.001 decay:0.2 sustain:0 hcutoff:5463.2923272018625 cutoff:4000 ]", "[ 3/1 ⇜ (10/3 → 27/8) | note:C#5 s:sawtooth gain:0.39242922708895556 attack:0.001 decay:0.2 sustain:0 hcutoff:5463.2923272018625 cutoff:4000 ]", - "[ 27/8 → 7/2 | note:D2 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2493.5603089922215 ]", - "[ 27/8 → 7/2 | note:D2 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2493.5603089922215 ]", + "[ 27/8 → 7/2 | note:D2 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2493.5603089922215 lpattack:0.1 lpenv:2 ftype:24db ]", + "[ 27/8 → 7/2 | note:D2 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2493.5603089922215 lpattack:0.1 lpenv:2 ftype:24db ]", "[ (27/8 → 11/3) ⇝ 15/4 | note:A5 s:sawtooth gain:0.3977916463583412 attack:0.001 decay:0.2 sustain:0 hcutoff:5335.806273589214 cutoff:4000 ]", "[ (27/8 → 11/3) ⇝ 15/4 | note:C#5 s:sawtooth gain:0.3977916463583412 attack:0.001 decay:0.2 sustain:0 hcutoff:5335.806273589214 cutoff:4000 ]", "[ 25/8 ⇜ (41/12 → 7/2) | note:F#5 s:sawtooth gain:0.394566409869316 attack:0.001 decay:0.2 sustain:0 hcutoff:5422.104580183649 cutoff:4000 ]", "[ 25/8 ⇜ (41/12 → 7/2) | note:A4 s:sawtooth gain:0.394566409869316 attack:0.001 decay:0.2 sustain:0 hcutoff:5422.104580183649 cutoff:4000 ]", - "[ 7/2 → 29/8 | note:D1 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2519.1725829012184 ]", - "[ 7/2 → 29/8 | note:D1 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2519.1725829012184 ]", + "[ 7/2 → 29/8 | note:D1 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2519.1725829012184 lpattack:0.1 lpenv:2 ftype:24db ]", + "[ 7/2 → 29/8 | note:D1 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2519.1725829012184 lpattack:0.1 lpenv:2 ftype:24db ]", "[ 7/2 → 15/4 | note:F#5 s:sawtooth gain:0.3983764764947172 attack:0.001 decay:0.2 sustain:0 hcutoff:5313.435927530719 cutoff:4000 ]", "[ 7/2 → 15/4 | note:A4 s:sawtooth gain:0.3983764764947172 attack:0.001 decay:0.2 sustain:0 hcutoff:5313.435927530719 cutoff:4000 ]", "[ 7/2 → 15/4 | s:bd gain:0.7 ]", @@ -7950,8 +7950,8 @@ exports[`renders tunes > tune: hyperpop 1`] = ` "[ (29/8 → 4/1) ⇝ 33/8 | note:B3 s:square gain:0.7 attack:0.01 decay:0.1 sustain:0 cutoff:2568.811347023862 ]", "[ 27/8 ⇜ (11/3 → 15/4) | note:F#5 s:sawtooth gain:0.3977916463583412 attack:0.001 decay:0.2 sustain:0 hcutoff:5335.806273589214 cutoff:4000 ]", "[ 27/8 ⇜ (11/3 → 15/4) | note:A4 s:sawtooth gain:0.3977916463583412 attack:0.001 decay:0.2 sustain:0 hcutoff:5335.806273589214 cutoff:4000 ]", - "[ 15/4 → 31/8 | note:D3 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2568.811347023862 ]", - "[ 15/4 → 31/8 | note:D3 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2568.811347023862 ]", + "[ 15/4 → 31/8 | note:D3 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2568.811347023862 lpattack:0.1 lpenv:2 ftype:24db ]", + "[ 15/4 → 31/8 | note:D3 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2568.811347023862 lpattack:0.1 lpenv:2 ftype:24db ]", "[ 15/4 → 4/1 | note:F#5 s:sawtooth gain:0.3998193184307759 attack:0.001 decay:0.2 sustain:0 hcutoff:5220.886439234386 cutoff:4000 ]", "[ 15/4 → 4/1 | note:A4 s:sawtooth gain:0.3998193184307759 attack:0.001 decay:0.2 sustain:0 hcutoff:5220.886439234386 cutoff:4000 ]", "[ 15/4 → 4/1 | s:hh3 gain:0.7 ]", @@ -7961,8 +7961,8 @@ exports[`renders tunes > tune: hyperpop 1`] = ` "[ (15/4 → 4/1) ⇝ 17/4 | note:B3 s:square gain:0.7 attack:0.01 decay:0.1 sustain:0 cutoff:2580.8797353950404 ]", "[ 7/2 ⇜ (23/6 → 31/8) | note:A5 s:sawtooth gain:0.3988719301898066 attack:0.001 decay:0.2 sustain:0 hcutoff:5290.754858561636 cutoff:4000 ]", "[ 7/2 ⇜ (23/6 → 31/8) | note:C#5 s:sawtooth gain:0.3988719301898066 attack:0.001 decay:0.2 sustain:0 hcutoff:5290.754858561636 cutoff:4000 ]", - "[ 31/8 → 4/1 | note:D3 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2592.8079367021132 ]", - "[ 31/8 → 4/1 | note:D3 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2592.8079367021132 ]", + "[ 31/8 → 4/1 | note:D3 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2592.8079367021132 lpattack:0.1 lpenv:2 ftype:24db ]", + "[ 31/8 → 4/1 | note:D3 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2592.8079367021132 lpattack:0.1 lpenv:2 ftype:24db ]", "[ 31/8 → 4/1 | s:bd gain:0.7 ]", "[ (31/8 → 4/1) ⇝ 17/4 | note:A5 s:sawtooth gain:0.3999548228044306 attack:0.001 decay:0.2 sustain:0 hcutoff:5197.0018638323545 cutoff:4000 ]", "[ (31/8 → 4/1) ⇝ 17/4 | note:C#5 s:sawtooth gain:0.3999548228044306 attack:0.001 decay:0.2 sustain:0 hcutoff:5197.0018638323545 cutoff:4000 ]", @@ -7987,8 +7987,8 @@ exports[`renders tunes > tune: hyperpop 1`] = ` "[ 31/8 ⇜ (4/1 → 35/8) | note:B3 s:square gain:0.7 attack:0.01 decay:0.1 sustain:0 cutoff:2639.083266757757 ]", "[ 15/4 ⇜ (49/12 → 33/8) | note:A5 s:sawtooth gain:0.3999548228044306 attack:0.001 decay:0.2 sustain:0 hcutoff:5148.3645377501725 cutoff:4000 ]", "[ 15/4 ⇜ (49/12 → 33/8) | note:C#5 s:sawtooth gain:0.3999548228044306 attack:0.001 decay:0.2 sustain:0 hcutoff:5148.3645377501725 cutoff:4000 ]", - "[ 33/8 → 17/4 | note:D1 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2639.083266757757 ]", - "[ 33/8 → 17/4 | note:D1 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2639.083266757757 ]", + "[ 33/8 → 17/4 | note:D1 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2639.083266757757 lpattack:0.1 lpenv:2 ftype:24db ]", + "[ 33/8 → 17/4 | note:D1 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2639.083266757757 lpattack:0.1 lpenv:2 ftype:24db ]", "[ (33/8 → 53/12) ⇝ 9/2 | note:A5 s:sawtooth gain:0.3988719301898066 attack:0.001 decay:0.2 sustain:0 hcutoff:5047.734873274585 cutoff:4000 ]", "[ (33/8 → 53/12) ⇝ 9/2 | note:C#5 s:sawtooth gain:0.3988719301898066 attack:0.001 decay:0.2 sustain:0 hcutoff:5047.734873274585 cutoff:4000 ]", "[ 31/8 ⇜ (25/6 → 17/4) | note:F#5 s:sawtooth gain:0.3998193184307759 attack:0.001 decay:0.2 sustain:0 hcutoff:5123.62012082546 cutoff:4000 ]", @@ -8000,14 +8000,14 @@ exports[`renders tunes > tune: hyperpop 1`] = ` "[ (17/4 → 55/12) ⇝ 37/8 | note:E5 s:sawtooth gain:0.3977916463583412 attack:0.001 decay:0.2 sustain:0 hcutoff:4995.811501426648 cutoff:4000 ]", "[ 4/1 ⇜ (13/3 → 35/8) | note:A5 s:sawtooth gain:0.3995935685018036 attack:0.001 decay:0.2 sustain:0 hcutoff:5098.597504951462 cutoff:4000 ]", "[ 4/1 ⇜ (13/3 → 35/8) | note:C#5 s:sawtooth gain:0.3995935685018036 attack:0.001 decay:0.2 sustain:0 hcutoff:5098.597504951462 cutoff:4000 ]", - "[ 35/8 → 9/2 | note:D2 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2682.97580859032 ]", - "[ 35/8 → 9/2 | note:D2 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2682.97580859032 ]", + "[ 35/8 → 9/2 | note:D2 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2682.97580859032 lpattack:0.1 lpenv:2 ftype:24db ]", + "[ 35/8 → 9/2 | note:D2 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2682.97580859032 lpattack:0.1 lpenv:2 ftype:24db ]", "[ (35/8 → 14/3) ⇝ 19/4 | note:A5 s:sawtooth gain:0.3963553195057793 attack:0.001 decay:0.2 sustain:0 hcutoff:4942.862975093085 cutoff:4000 ]", "[ (35/8 → 14/3) ⇝ 19/4 | note:C#5 s:sawtooth gain:0.3963553195057793 attack:0.001 decay:0.2 sustain:0 hcutoff:4942.862975093085 cutoff:4000 ]", "[ 33/8 ⇜ (53/12 → 9/2) | note:F#5 s:sawtooth gain:0.3988719301898066 attack:0.001 decay:0.2 sustain:0 hcutoff:5047.734873274585 cutoff:4000 ]", "[ 33/8 ⇜ (53/12 → 9/2) | note:A4 s:sawtooth gain:0.3988719301898066 attack:0.001 decay:0.2 sustain:0 hcutoff:5047.734873274585 cutoff:4000 ]", - "[ 9/2 → 37/8 | note:D1 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2703.995258572327 ]", - "[ 9/2 → 37/8 | note:D1 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2703.995258572327 ]", + "[ 9/2 → 37/8 | note:D1 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2703.995258572327 lpattack:0.1 lpenv:2 ftype:24db ]", + "[ 9/2 → 37/8 | note:D1 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2703.995258572327 lpattack:0.1 lpenv:2 ftype:24db ]", "[ 9/2 → 19/4 | note:F#5 s:sawtooth gain:0.3955046879791817 attack:0.001 decay:0.2 sustain:0 hcutoff:4916.015592312082 cutoff:4000 ]", "[ 9/2 → 19/4 | note:A4 s:sawtooth gain:0.3955046879791817 attack:0.001 decay:0.2 sustain:0 hcutoff:4916.015592312082 cutoff:4000 ]", "[ 9/2 → 19/4 | s:bd gain:0.7 ]", @@ -8020,8 +8020,8 @@ exports[`renders tunes > tune: hyperpop 1`] = ` "[ (37/8 → 59/12) ⇝ 5/1 | note:C#5 s:sawtooth gain:0.39242922708895556 attack:0.001 decay:0.2 sustain:0 hcutoff:4834.036289789029 cutoff:4000 ]", "[ 35/8 ⇜ (14/3 → 19/4) | note:F#5 s:sawtooth gain:0.3963553195057793 attack:0.001 decay:0.2 sustain:0 hcutoff:4942.862975093085 cutoff:4000 ]", "[ 35/8 ⇜ (14/3 → 19/4) | note:A4 s:sawtooth gain:0.3963553195057793 attack:0.001 decay:0.2 sustain:0 hcutoff:4942.862975093085 cutoff:4000 ]", - "[ 19/4 → 39/8 | note:D3 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2744.1172176410028 ]", - "[ 19/4 → 39/8 | note:D3 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2744.1172176410028 ]", + "[ 19/4 → 39/8 | note:D3 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2744.1172176410028 lpattack:0.1 lpenv:2 ftype:24db ]", + "[ 19/4 → 39/8 | note:D3 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2744.1172176410028 lpattack:0.1 lpenv:2 ftype:24db ]", "[ 19/4 → 5/1 | note:F#5 s:sawtooth gain:0.3912316097774532 attack:0.001 decay:0.2 sustain:0 hcutoff:4806.246411789873 cutoff:4000 ]", "[ 19/4 → 5/1 | note:A4 s:sawtooth gain:0.3912316097774532 attack:0.001 decay:0.2 sustain:0 hcutoff:4806.246411789873 cutoff:4000 ]", "[ 19/4 → 5/1 | s:hh3 gain:0.7 ]", @@ -8029,8 +8029,8 @@ exports[`renders tunes > tune: hyperpop 1`] = ` "[ (19/4 → 5/1) ⇝ 41/8 | note:E5 s:sawtooth gain:0.38994891982521085 attack:0.001 decay:0.2 sustain:0 hcutoff:4778.23271519263 cutoff:4000 ]", "[ 9/2 ⇜ (29/6 → 39/8) | note:A5 s:sawtooth gain:0.394566409869316 attack:0.001 decay:0.2 sustain:0 hcutoff:4888.925582549005 cutoff:4000 ]", "[ 9/2 ⇜ (29/6 → 39/8) | note:C#5 s:sawtooth gain:0.394566409869316 attack:0.001 decay:0.2 sustain:0 hcutoff:4888.925582549005 cutoff:4000 ]", - "[ 39/8 → 5/1 | note:D3 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2763.195558759784 ]", - "[ 39/8 → 5/1 | note:D3 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2763.195558759784 ]", + "[ 39/8 → 5/1 | note:D3 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2763.195558759784 lpattack:0.1 lpenv:2 ftype:24db ]", + "[ 39/8 → 5/1 | note:D3 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2763.195558759784 lpattack:0.1 lpenv:2 ftype:24db ]", "[ (39/8 → 5/1) ⇝ 21/4 | note:A5 s:sawtooth gain:0.3871314633555296 attack:0.001 decay:0.2 sustain:0 hcutoff:4721.553103742387 cutoff:4000 ]", "[ (39/8 → 5/1) ⇝ 21/4 | note:C#5 s:sawtooth gain:0.3871314633555296 attack:0.001 decay:0.2 sustain:0 hcutoff:4721.553103742387 cutoff:4000 ]", "[ 37/8 ⇜ (59/12 → 5/1) | note:F#5 s:sawtooth gain:0.39242922708895556 attack:0.001 decay:0.2 sustain:0 hcutoff:4834.036289789029 cutoff:4000 ]", @@ -8046,8 +8046,8 @@ exports[`renders tunes > tune: hyperpop 1`] = ` "[ (5/1 → 16/3) ⇝ 43/8 | note:E5 s:sawtooth gain:0.38398364517932737 attack:0.001 decay:0.2 sustain:0 hcutoff:4664.036300812779 cutoff:4000 ]", "[ 19/4 ⇜ (61/12 → 41/8) | note:A5 s:sawtooth gain:0.38994891982521085 attack:0.001 decay:0.2 sustain:0 hcutoff:4778.23271519263 cutoff:4000 ]", "[ 19/4 ⇜ (61/12 → 41/8) | note:C#5 s:sawtooth gain:0.38994891982521085 attack:0.001 decay:0.2 sustain:0 hcutoff:4778.23271519263 cutoff:4000 ]", - "[ 41/8 → 21/4 | note:D1 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2799.329510692108 ]", - "[ 41/8 → 21/4 | note:D1 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2799.329510692108 ]", + "[ 41/8 → 21/4 | note:D1 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2799.329510692108 lpattack:0.1 lpenv:2 ftype:24db ]", + "[ 41/8 → 21/4 | note:D1 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2799.329510692108 lpattack:0.1 lpenv:2 ftype:24db ]", "[ (41/8 → 65/12) ⇝ 11/2 | note:A5 s:sawtooth gain:0.38051304866630675 attack:0.001 decay:0.2 sustain:0 hcutoff:4605.721725547503 cutoff:4000 ]", "[ (41/8 → 65/12) ⇝ 11/2 | note:C#5 s:sawtooth gain:0.38051304866630675 attack:0.001 decay:0.2 sustain:0 hcutoff:4605.721725547503 cutoff:4000 ]", "[ 39/8 ⇜ (31/6 → 21/4) | note:F#5 s:sawtooth gain:0.3871314633555296 attack:0.001 decay:0.2 sustain:0 hcutoff:4721.553103742387 cutoff:4000 ]", @@ -8059,14 +8059,14 @@ exports[`renders tunes > tune: hyperpop 1`] = ` "[ (21/4 → 67/12) ⇝ 45/8 | note:E5 s:sawtooth gain:0.3767280347874561 attack:0.001 decay:0.2 sustain:0 hcutoff:4546.64934384357 cutoff:4000 ]", "[ 5/1 ⇜ (16/3 → 43/8) | note:A5 s:sawtooth gain:0.38398364517932737 attack:0.001 decay:0.2 sustain:0 hcutoff:4664.036300812779 cutoff:4000 ]", "[ 5/1 ⇜ (16/3 → 43/8) | note:C#5 s:sawtooth gain:0.38398364517932737 attack:0.001 decay:0.2 sustain:0 hcutoff:4664.036300812779 cutoff:4000 ]", - "[ 43/8 → 11/2 | note:D2 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2832.694627163799 ]", - "[ 43/8 → 11/2 | note:D2 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2832.694627163799 ]", + "[ 43/8 → 11/2 | note:D2 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2832.694627163799 lpattack:0.1 lpenv:2 ftype:24db ]", + "[ 43/8 → 11/2 | note:D2 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2832.694627163799 lpattack:0.1 lpenv:2 ftype:24db ]", "[ (43/8 → 17/3) ⇝ 23/4 | note:A5 s:sawtooth gain:0.3726377219727376 attack:0.001 decay:0.2 sustain:0 hcutoff:4486.859640960669 cutoff:4000 ]", "[ (43/8 → 17/3) ⇝ 23/4 | note:C#5 s:sawtooth gain:0.3726377219727376 attack:0.001 decay:0.2 sustain:0 hcutoff:4486.859640960669 cutoff:4000 ]", "[ 41/8 ⇜ (65/12 → 11/2) | note:F#5 s:sawtooth gain:0.38051304866630675 attack:0.001 decay:0.2 sustain:0 hcutoff:4605.721725547503 cutoff:4000 ]", "[ 41/8 ⇜ (65/12 → 11/2) | note:A4 s:sawtooth gain:0.38051304866630675 attack:0.001 decay:0.2 sustain:0 hcutoff:4605.721725547503 cutoff:4000 ]", - "[ 11/2 → 45/8 | note:D1 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2848.313487543853 ]", - "[ 11/2 → 45/8 | note:D1 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2848.313487543853 ]", + "[ 11/2 → 45/8 | note:D1 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2848.313487543853 lpattack:0.1 lpenv:2 ftype:24db ]", + "[ 11/2 → 45/8 | note:D1 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2848.313487543853 lpattack:0.1 lpenv:2 ftype:24db ]", "[ 11/2 → 23/4 | note:F#5 s:sawtooth gain:0.3704811297220968 attack:0.001 decay:0.2 sustain:0 hcutoff:4456.708580912725 cutoff:4000 ]", "[ 11/2 → 23/4 | note:A4 s:sawtooth gain:0.3704811297220968 attack:0.001 decay:0.2 sustain:0 hcutoff:4456.708580912725 cutoff:4000 ]", "[ 11/2 → 23/4 | s:bd gain:0.7 ]", @@ -8083,8 +8083,8 @@ exports[`renders tunes > tune: hyperpop 1`] = ` "[ (45/8 → 6/1) ⇝ 49/8 | note:A3 s:square gain:0.7 attack:0.01 decay:0.1 sustain:0 cutoff:2877.376777172205 ]", "[ 43/8 ⇜ (17/3 → 23/4) | note:F#5 s:sawtooth gain:0.3726377219727376 attack:0.001 decay:0.2 sustain:0 hcutoff:4486.859640960669 cutoff:4000 ]", "[ 43/8 ⇜ (17/3 → 23/4) | note:A4 s:sawtooth gain:0.3726377219727376 attack:0.001 decay:0.2 sustain:0 hcutoff:4486.859640960669 cutoff:4000 ]", - "[ 23/4 → 47/8 | note:D3 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2877.376777172205 ]", - "[ 23/4 → 47/8 | note:D3 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2877.376777172205 ]", + "[ 23/4 → 47/8 | note:D3 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2877.376777172205 lpattack:0.1 lpenv:2 ftype:24db ]", + "[ 23/4 → 47/8 | note:D3 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2877.376777172205 lpattack:0.1 lpenv:2 ftype:24db ]", "[ 23/4 → 6/1 | note:F#5 s:sawtooth gain:0.36114266880324386 attack:0.001 decay:0.2 sustain:0 hcutoff:4334.517148084427 cutoff:4000 ]", "[ 23/4 → 6/1 | note:A4 s:sawtooth gain:0.36114266880324386 attack:0.001 decay:0.2 sustain:0 hcutoff:4334.517148084427 cutoff:4000 ]", "[ 23/4 → 6/1 | s:hh3 gain:0.7 ]", @@ -8094,8 +8094,8 @@ exports[`renders tunes > tune: hyperpop 1`] = ` "[ (23/4 → 6/1) ⇝ 25/4 | note:A3 s:square gain:0.7 attack:0.01 decay:0.1 sustain:0 cutoff:2884.183170199766 ]", "[ 11/2 ⇜ (35/6 → 47/8) | note:A5 s:sawtooth gain:0.368251964143991 attack:0.001 decay:0.2 sustain:0 hcutoff:4426.39359377459 cutoff:4000 ]", "[ 11/2 ⇜ (35/6 → 47/8) | note:C#5 s:sawtooth gain:0.368251964143991 attack:0.001 decay:0.2 sustain:0 hcutoff:4426.39359377459 cutoff:4000 ]", - "[ 47/8 → 6/1 | note:D3 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2890.803699781578 ]", - "[ 47/8 → 6/1 | note:D3 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2890.803699781578 ]", + "[ 47/8 → 6/1 | note:D3 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2890.803699781578 lpattack:0.1 lpenv:2 ftype:24db ]", + "[ 47/8 → 6/1 | note:D3 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2890.803699781578 lpattack:0.1 lpenv:2 ftype:24db ]", "[ (47/8 → 6/1) ⇝ 25/4 | note:A5 s:sawtooth gain:0.3586370624427201 attack:0.001 decay:0.2 sustain:0 hcutoff:4303.598663257904 cutoff:4000 ]", "[ (47/8 → 6/1) ⇝ 25/4 | note:C#5 s:sawtooth gain:0.3586370624427201 attack:0.001 decay:0.2 sustain:0 hcutoff:4303.598663257904 cutoff:4000 ]", "[ (47/8 → 6/1) ⇝ 51/8 | note:F#3 s:square gain:0.7 attack:0.01 decay:0.1 sustain:0 cutoff:2890.803699781578 ]", @@ -8119,8 +8119,8 @@ exports[`renders tunes > tune: hyperpop 1`] = ` "[ 47/8 ⇜ (6/1 → 51/8) | note:A3 s:square gain:0.7 attack:0.01 decay:0.1 sustain:0 cutoff:2915.4076660819765 ]", "[ 23/4 ⇜ (73/12 → 49/8) | note:A5 s:sawtooth gain:0.35343108171056015 attack:0.001 decay:0.2 sustain:0 hcutoff:4241.3539374389275 cutoff:4000 ]", "[ 23/4 ⇜ (73/12 → 49/8) | note:C#5 s:sawtooth gain:0.35343108171056015 attack:0.001 decay:0.2 sustain:0 hcutoff:4241.3539374389275 cutoff:4000 ]", - "[ 49/8 → 25/4 | note:D1 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2915.4076660819765 ]", - "[ 49/8 → 25/4 | note:D1 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2915.4076660819765 ]", + "[ 49/8 → 25/4 | note:D1 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2915.4076660819765 lpattack:0.1 lpenv:2 ftype:24db ]", + "[ 49/8 → 25/4 | note:D1 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2915.4076660819765 lpattack:0.1 lpenv:2 ftype:24db ]", "[ (49/8 → 77/12) ⇝ 13/2 | note:A5 s:sawtooth gain:0.3422847385870941 attack:0.001 decay:0.2 sustain:0 hcutoff:4115.383232572483 cutoff:4000 ]", "[ (49/8 → 77/12) ⇝ 13/2 | note:C#5 s:sawtooth gain:0.3422847385870941 attack:0.001 decay:0.2 sustain:0 hcutoff:4115.383232572483 cutoff:4000 ]", "[ 47/8 ⇜ (37/6 → 25/4) | note:F#5 s:sawtooth gain:0.3507338432270528 attack:0.001 decay:0.2 sustain:0 hcutoff:4210.038361759807 cutoff:4000 ]", @@ -8132,14 +8132,14 @@ exports[`renders tunes > tune: hyperpop 1`] = ` "[ (25/4 → 79/12) ⇝ 53/8 | note:E5 s:sawtooth gain:0.3363712287126769 attack:0.001 decay:0.2 sustain:0 hcutoff:4051.743587553753 cutoff:4000 ]", "[ 6/1 ⇜ (19/3 → 51/8) | note:A5 s:sawtooth gain:0.3479759264430665 attack:0.001 decay:0.2 sustain:0 hcutoff:4178.601124662687 cutoff:4000 ]", "[ 6/1 ⇜ (19/3 → 51/8) | note:C#5 s:sawtooth gain:0.3479759264430665 attack:0.001 decay:0.2 sustain:0 hcutoff:4178.601124662687 cutoff:4000 ]", - "[ 51/8 → 13/2 | note:D2 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2936.9631544781614 ]", - "[ 51/8 → 13/2 | note:D2 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2936.9631544781614 ]", + "[ 51/8 → 13/2 | note:D2 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2936.9631544781614 lpattack:0.1 lpenv:2 ftype:24db ]", + "[ 51/8 → 13/2 | note:D2 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2936.9631544781614 lpattack:0.1 lpenv:2 ftype:24db ]", "[ (51/8 → 20/3) ⇝ 27/4 | note:A5 s:sawtooth gain:0.3302496429830646 attack:0.001 decay:0.2 sustain:0 hcutoff:3987.7258050403216 cutoff:4000 ]", "[ (51/8 → 20/3) ⇝ 27/4 | note:C#5 s:sawtooth gain:0.3302496429830646 attack:0.001 decay:0.2 sustain:0 hcutoff:3987.7258050403216 cutoff:4000 ]", "[ 49/8 ⇜ (77/12 → 13/2) | note:F#5 s:sawtooth gain:0.3422847385870941 attack:0.001 decay:0.2 sustain:0 hcutoff:4115.383232572483 cutoff:4000 ]", "[ 49/8 ⇜ (77/12 → 13/2) | note:A4 s:sawtooth gain:0.3422847385870941 attack:0.001 decay:0.2 sustain:0 hcutoff:4115.383232572483 cutoff:4000 ]", - "[ 13/2 → 53/8 | note:D1 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2946.5812012110136 ]", - "[ 13/2 → 53/8 | note:D1 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2946.5812012110136 ]", + "[ 13/2 → 53/8 | note:D1 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2946.5812012110136 lpattack:0.1 lpenv:2 ftype:24db ]", + "[ 13/2 → 53/8 | note:D1 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2946.5812012110136 lpattack:0.1 lpenv:2 ftype:24db ]", "[ 13/2 → 27/4 | note:F#5 s:sawtooth gain:0.3271154116289833 attack:0.001 decay:0.2 sustain:0 hcutoff:3955.588813730369 cutoff:4000 ]", "[ 13/2 → 27/4 | note:A4 s:sawtooth gain:0.3271154116289833 attack:0.001 decay:0.2 sustain:0 hcutoff:3955.588813730369 cutoff:4000 ]", "[ 13/2 → 27/4 | s:bd gain:0.7 ]", @@ -8152,8 +8152,8 @@ exports[`renders tunes > tune: hyperpop 1`] = ` "[ (53/8 → 83/12) ⇝ 7/1 | note:C#5 s:sawtooth gain:0.3174416994481911 attack:0.001 decay:0.2 sustain:0 hcutoff:3858.7315549779487 cutoff:4000 ]", "[ 51/8 ⇜ (20/3 → 27/4) | note:F#5 s:sawtooth gain:0.3302496429830646 attack:0.001 decay:0.2 sustain:0 hcutoff:3987.7258050403216 cutoff:4000 ]", "[ 51/8 ⇜ (20/3 → 27/4) | note:A4 s:sawtooth gain:0.3302496429830646 attack:0.001 decay:0.2 sustain:0 hcutoff:3987.7258050403216 cutoff:4000 ]", - "[ 27/4 → 55/8 | note:D3 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2963.468935477506 ]", - "[ 27/4 → 55/8 | note:D3 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2963.468935477506 ]", + "[ 27/4 → 55/8 | note:D3 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2963.468935477506 lpattack:0.1 lpenv:2 ftype:24db ]", + "[ 27/4 → 55/8 | note:D3 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2963.468935477506 lpattack:0.1 lpenv:2 ftype:24db ]", "[ 27/4 → 7/1 | note:F#5 s:sawtooth gain:0.31413326401454233 attack:0.001 decay:0.2 sustain:0 hcutoff:3826.315480550129 cutoff:4000 ]", "[ 27/4 → 7/1 | note:A4 s:sawtooth gain:0.31413326401454233 attack:0.001 decay:0.2 sustain:0 hcutoff:3826.315480550129 cutoff:4000 ]", "[ 27/4 → 7/1 | s:hh3 gain:0.7 ]", @@ -8161,8 +8161,8 @@ exports[`renders tunes > tune: hyperpop 1`] = ` "[ (27/4 → 7/1) ⇝ 57/8 | note:E5 s:sawtooth gain:0.3107861971007485 attack:0.001 decay:0.2 sustain:0 hcutoff:3793.8434936445938 cutoff:4000 ]", "[ 13/2 ⇜ (41/6 → 55/8) | note:A5 s:sawtooth gain:0.32393472883446767 attack:0.001 decay:0.2 sustain:0 hcutoff:3923.373759622562 cutoff:4000 ]", "[ 13/2 ⇜ (41/6 → 55/8) | note:C#5 s:sawtooth gain:0.32393472883446767 attack:0.001 decay:0.2 sustain:0 hcutoff:3923.373759622562 cutoff:4000 ]", - "[ 55/8 → 7/1 | note:D3 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2970.728450471497 ]", - "[ 55/8 → 7/1 | note:D3 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2970.728450471497 ]", + "[ 55/8 → 7/1 | note:D3 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2970.728450471497 lpattack:0.1 lpenv:2 ftype:24db ]", + "[ 55/8 → 7/1 | note:D3 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2970.728450471497 lpattack:0.1 lpenv:2 ftype:24db ]", "[ (55/8 → 7/1) ⇝ 29/4 | note:A5 s:sawtooth gain:0.30398425548024827 attack:0.001 decay:0.2 sustain:0 hcutoff:3728.7540466585065 cutoff:4000 ]", "[ (55/8 → 7/1) ⇝ 29/4 | note:C#5 s:sawtooth gain:0.30398425548024827 attack:0.001 decay:0.2 sustain:0 hcutoff:3728.7540466585065 cutoff:4000 ]", "[ 53/8 ⇜ (83/12 → 7/1) | note:F#5 s:sawtooth gain:0.3174416994481911 attack:0.001 decay:0.2 sustain:0 hcutoff:3858.7315549779487 cutoff:4000 ]", @@ -8178,8 +8178,8 @@ exports[`renders tunes > tune: hyperpop 1`] = ` "[ (7/1 → 22/3) ⇝ 59/8 | note:E5 s:sawtooth gain:0.29705226105983373 attack:0.001 decay:0.2 sustain:0 hcutoff:3663.507823075358 cutoff:4000 ]", "[ 27/4 ⇜ (85/12 → 57/8) | note:A5 s:sawtooth gain:0.3107861971007485 attack:0.001 decay:0.2 sustain:0 hcutoff:3793.8434936445938 cutoff:4000 ]", "[ 27/4 ⇜ (85/12 → 57/8) | note:C#5 s:sawtooth gain:0.3107861971007485 attack:0.001 decay:0.2 sustain:0 hcutoff:3793.8434936445938 cutoff:4000 ]", - "[ 57/8 → 29/4 | note:D1 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2982.856914513109 ]", - "[ 57/8 → 29/4 | note:D1 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2982.856914513109 ]", + "[ 57/8 → 29/4 | note:D1 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2982.856914513109 lpattack:0.1 lpenv:2 ftype:24db ]", + "[ 57/8 → 29/4 | note:D1 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2982.856914513109 lpattack:0.1 lpenv:2 ftype:24db ]", "[ (57/8 → 89/12) ⇝ 15/2 | note:A5 s:sawtooth gain:0.29000691362123476 attack:0.001 decay:0.2 sustain:0 hcutoff:3598.149539397671 cutoff:4000 ]", "[ (57/8 → 89/12) ⇝ 15/2 | note:C#5 s:sawtooth gain:0.29000691362123476 attack:0.001 decay:0.2 sustain:0 hcutoff:3598.149539397671 cutoff:4000 ]", "[ 55/8 ⇜ (43/6 → 29/4) | note:F#5 s:sawtooth gain:0.30398425548024827 attack:0.001 decay:0.2 sustain:0 hcutoff:3728.7540466585065 cutoff:4000 ]", @@ -8191,14 +8191,14 @@ exports[`renders tunes > tune: hyperpop 1`] = ` "[ (29/4 → 91/12) ⇝ 61/8 | note:E5 s:sawtooth gain:0.28286518602353056 attack:0.001 decay:0.2 sustain:0 hcutoff:3532.7239889283615 cutoff:4000 ]", "[ 7/1 ⇜ (22/3 → 59/8) | note:A5 s:sawtooth gain:0.29705226105983373 attack:0.001 decay:0.2 sustain:0 hcutoff:3663.507823075358 cutoff:4000 ]", "[ 7/1 ⇜ (22/3 → 59/8) | note:C#5 s:sawtooth gain:0.29705226105983373 attack:0.001 decay:0.2 sustain:0 hcutoff:3663.507823075358 cutoff:4000 ]", - "[ 59/8 → 15/2 | note:D2 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2991.774409503181 ]", - "[ 59/8 → 15/2 | note:D2 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2991.774409503181 ]", + "[ 59/8 → 15/2 | note:D2 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2991.774409503181 lpattack:0.1 lpenv:2 ftype:24db ]", + "[ 59/8 → 15/2 | note:D2 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2991.774409503181 lpattack:0.1 lpenv:2 ftype:24db ]", "[ (59/8 → 23/3) ⇝ 31/4 | note:A5 s:sawtooth gain:0.2756442833140452 attack:0.001 decay:0.2 sustain:0 hcutoff:3467.276011071639 cutoff:4000 ]", "[ (59/8 → 23/3) ⇝ 31/4 | note:C#5 s:sawtooth gain:0.2756442833140452 attack:0.001 decay:0.2 sustain:0 hcutoff:3467.276011071639 cutoff:4000 ]", "[ 57/8 ⇜ (89/12 → 15/2) | note:F#5 s:sawtooth gain:0.29000691362123476 attack:0.001 decay:0.2 sustain:0 hcutoff:3598.149539397671 cutoff:4000 ]", "[ 57/8 ⇜ (89/12 → 15/2) | note:A4 s:sawtooth gain:0.29000691362123476 attack:0.001 decay:0.2 sustain:0 hcutoff:3598.149539397671 cutoff:4000 ]", - "[ 15/2 → 61/8 | note:D1 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2995.0220264467503 ]", - "[ 15/2 → 61/8 | note:D1 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2995.0220264467503 ]", + "[ 15/2 → 61/8 | note:D1 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2995.0220264467503 lpattack:0.1 lpenv:2 ftype:24db ]", + "[ 15/2 → 61/8 | note:D1 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2995.0220264467503 lpattack:0.1 lpenv:2 ftype:24db ]", "[ 15/2 → 31/4 | note:F#5 s:sawtooth gain:0.2720095711683043 attack:0.001 decay:0.2 sustain:0 hcutoff:3434.557629230318 cutoff:4000 ]", "[ 15/2 → 31/4 | note:A4 s:sawtooth gain:0.2720095711683043 attack:0.001 decay:0.2 sustain:0 hcutoff:3434.557629230318 cutoff:4000 ]", "[ 15/2 → 31/4 | s:bd gain:0.7 ]", @@ -8215,8 +8215,8 @@ exports[`renders tunes > tune: hyperpop 1`] = ` "[ (61/8 → 8/1) ⇝ 65/8 | note:B3 s:square gain:0.7 attack:0.01 decay:0.1 sustain:0 cutoff:2999.0852191942718 ]", "[ 59/8 ⇜ (23/3 → 31/4) | note:F#5 s:sawtooth gain:0.2756442833140452 attack:0.001 decay:0.2 sustain:0 hcutoff:3467.276011071639 cutoff:4000 ]", "[ 59/8 ⇜ (23/3 → 31/4) | note:A4 s:sawtooth gain:0.2756442833140452 attack:0.001 decay:0.2 sustain:0 hcutoff:3467.276011071639 cutoff:4000 ]", - "[ 31/4 → 63/8 | note:D3 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2999.0852191942718 ]", - "[ 31/4 → 63/8 | note:D3 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2999.0852191942718 ]", + "[ 31/4 → 63/8 | note:D3 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2999.0852191942718 lpattack:0.1 lpenv:2 ftype:24db ]", + "[ 31/4 → 63/8 | note:D3 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2999.0852191942718 lpattack:0.1 lpenv:2 ftype:24db ]", "[ 31/4 → 8/1 | note:F#5 s:sawtooth gain:0.2573601511491127 attack:0.001 decay:0.2 sustain:0 hcutoff:3303.852260680389 cutoff:4000 ]", "[ 31/4 → 8/1 | note:A4 s:sawtooth gain:0.2573601511491127 attack:0.001 decay:0.2 sustain:0 hcutoff:3303.852260680389 cutoff:4000 ]", "[ 31/4 → 8/1 | s:hh3 gain:0.7 ]", @@ -8226,8 +8226,8 @@ exports[`renders tunes > tune: hyperpop 1`] = ` "[ (31/4 → 8/1) ⇝ 33/4 | note:B3 s:square gain:0.7 attack:0.01 decay:0.1 sustain:0 cutoff:2999.5934052398757 ]", "[ 15/2 ⇜ (47/6 → 63/8) | note:A5 s:sawtooth gain:0.2683616012798825 attack:0.001 decay:0.2 sustain:0 hcutoff:3401.8504606023293 cutoff:4000 ]", "[ 15/2 ⇜ (47/6 → 63/8) | note:C#5 s:sawtooth gain:0.2683616012798825 attack:0.001 decay:0.2 sustain:0 hcutoff:3401.8504606023293 cutoff:4000 ]", - "[ 63/8 → 8/1 | note:D3 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2999.898347482845 ]", - "[ 63/8 → 8/1 | note:D3 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2999.898347482845 ]", + "[ 63/8 → 8/1 | note:D3 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2999.898347482845 lpattack:0.1 lpenv:2 ftype:24db ]", + "[ 63/8 → 8/1 | note:D3 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2999.898347482845 lpattack:0.1 lpenv:2 ftype:24db ]", "[ 63/8 → 8/1 | s:bd gain:0.7 ]", "[ (63/8 → 8/1) ⇝ 33/4 | note:A5 s:sawtooth gain:0.2536811842784369 attack:0.001 decay:0.2 sustain:0 hcutoff:3271.2459533414954 cutoff:4000 ]", "[ (63/8 → 8/1) ⇝ 33/4 | note:C#5 s:sawtooth gain:0.2536811842784369 attack:0.001 decay:0.2 sustain:0 hcutoff:3271.2459533414954 cutoff:4000 ]", @@ -8252,8 +8252,8 @@ exports[`renders tunes > tune: hyperpop 1`] = ` "[ 63/8 ⇜ (8/1 → 67/8) | note:B3 s:square gain:0.7 attack:0.01 decay:0.1 sustain:0 cutoff:2999.0852191942718 ]", "[ 31/4 ⇜ (97/12 → 65/8) | note:A5 s:sawtooth gain:0.24631881572156322 attack:0.001 decay:0.2 sustain:0 hcutoff:3206.156506355406 cutoff:4000 ]", "[ 31/4 ⇜ (97/12 → 65/8) | note:C#5 s:sawtooth gain:0.24631881572156322 attack:0.001 decay:0.2 sustain:0 hcutoff:3206.156506355406 cutoff:4000 ]", - "[ 65/8 → 33/4 | note:D1 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2999.0852191942718 ]", - "[ 65/8 → 33/4 | note:D1 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2999.0852191942718 ]", + "[ 65/8 → 33/4 | note:D1 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2999.0852191942718 lpattack:0.1 lpenv:2 ftype:24db ]", + "[ 65/8 → 33/4 | note:D1 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2999.0852191942718 lpattack:0.1 lpenv:2 ftype:24db ]", "[ (65/8 → 101/12) ⇝ 17/2 | note:A5 s:sawtooth gain:0.2316383987201176 attack:0.001 decay:0.2 sustain:0 hcutoff:3076.6262403774385 cutoff:4000 ]", "[ (65/8 → 101/12) ⇝ 17/2 | note:C#5 s:sawtooth gain:0.2316383987201176 attack:0.001 decay:0.2 sustain:0 hcutoff:3076.6262403774385 cutoff:4000 ]", "[ 63/8 ⇜ (49/6 → 33/4) | note:F#5 s:sawtooth gain:0.24263984885088735 attack:0.001 decay:0.2 sustain:0 hcutoff:3173.6845194498705 cutoff:4000 ]", @@ -8265,14 +8265,14 @@ exports[`renders tunes > tune: hyperpop 1`] = ` "[ (33/4 → 103/12) ⇝ 69/8 | note:E5 s:sawtooth gain:0.2243557166859549 attack:0.001 decay:0.2 sustain:0 hcutoff:3012.274194959679 cutoff:4000 ]", "[ 8/1 ⇜ (25/3 → 67/8) | note:A5 s:sawtooth gain:0.2389653154600499 attack:0.001 decay:0.2 sustain:0 hcutoff:3141.2684450220513 cutoff:4000 ]", "[ 8/1 ⇜ (25/3 → 67/8) | note:C#5 s:sawtooth gain:0.2389653154600499 attack:0.001 decay:0.2 sustain:0 hcutoff:3141.2684450220513 cutoff:4000 ]", - "[ 67/8 → 17/2 | note:D2 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2995.0220264467503 ]", - "[ 67/8 → 17/2 | note:D2 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2995.0220264467503 ]", + "[ 67/8 → 17/2 | note:D2 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2995.0220264467503 lpattack:0.1 lpenv:2 ftype:24db ]", + "[ 67/8 → 17/2 | note:D2 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2995.0220264467503 lpattack:0.1 lpenv:2 ftype:24db ]", "[ (67/8 → 26/3) ⇝ 35/4 | note:A5 s:sawtooth gain:0.21713481397646955 attack:0.001 decay:0.2 sustain:0 hcutoff:2948.256412446248 cutoff:4000 ]", "[ (67/8 → 26/3) ⇝ 35/4 | note:C#5 s:sawtooth gain:0.21713481397646955 attack:0.001 decay:0.2 sustain:0 hcutoff:2948.256412446248 cutoff:4000 ]", "[ 65/8 ⇜ (101/12 → 17/2) | note:F#5 s:sawtooth gain:0.2316383987201176 attack:0.001 decay:0.2 sustain:0 hcutoff:3076.6262403774385 cutoff:4000 ]", "[ 65/8 ⇜ (101/12 → 17/2) | note:A4 s:sawtooth gain:0.2316383987201176 attack:0.001 decay:0.2 sustain:0 hcutoff:3076.6262403774385 cutoff:4000 ]", - "[ 17/2 → 69/8 | note:D1 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2991.774409503181 ]", - "[ 17/2 → 69/8 | note:D1 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2991.774409503181 ]", + "[ 17/2 → 69/8 | note:D1 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2991.774409503181 lpattack:0.1 lpenv:2 ftype:24db ]", + "[ 17/2 → 69/8 | note:D1 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2991.774409503181 lpattack:0.1 lpenv:2 ftype:24db ]", "[ 17/2 → 35/4 | note:F#5 s:sawtooth gain:0.21355297301451046 attack:0.001 decay:0.2 sustain:0 hcutoff:2916.386590360237 cutoff:4000 ]", "[ 17/2 → 35/4 | note:A4 s:sawtooth gain:0.21355297301451046 attack:0.001 decay:0.2 sustain:0 hcutoff:2916.386590360237 cutoff:4000 ]", "[ 17/2 → 35/4 | s:bd gain:0.7 ]", @@ -8285,8 +8285,8 @@ exports[`renders tunes > tune: hyperpop 1`] = ` "[ (69/8 → 107/12) ⇝ 9/1 | note:C#5 s:sawtooth gain:0.20294773894016632 attack:0.001 decay:0.2 sustain:0 hcutoff:2821.398875337315 cutoff:4000 ]", "[ 67/8 ⇜ (26/3 → 35/4) | note:F#5 s:sawtooth gain:0.21713481397646955 attack:0.001 decay:0.2 sustain:0 hcutoff:2948.256412446248 cutoff:4000 ]", "[ 67/8 ⇜ (26/3 → 35/4) | note:A4 s:sawtooth gain:0.21713481397646955 attack:0.001 decay:0.2 sustain:0 hcutoff:2948.256412446248 cutoff:4000 ]", - "[ 35/4 → 71/8 | note:D3 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2982.856914513109 ]", - "[ 35/4 → 71/8 | note:D3 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2982.856914513109 ]", + "[ 35/4 → 71/8 | note:D3 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2982.856914513109 lpattack:0.1 lpenv:2 ftype:24db ]", + "[ 35/4 → 71/8 | note:D3 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2982.856914513109 lpattack:0.1 lpenv:2 ftype:24db ]", "[ 35/4 → 9/1 | note:F#5 s:sawtooth gain:0.19946652199116702 attack:0.001 decay:0.2 sustain:0 hcutoff:2789.9616382401937 cutoff:4000 ]", "[ 35/4 → 9/1 | note:A4 s:sawtooth gain:0.19946652199116702 attack:0.001 decay:0.2 sustain:0 hcutoff:2789.9616382401937 cutoff:4000 ]", "[ 35/4 → 9/1 | s:hh3 gain:0.7 ]", @@ -8294,8 +8294,8 @@ exports[`renders tunes > tune: hyperpop 1`] = ` "[ (35/4 → 9/1) ⇝ 73/8 | note:E5 s:sawtooth gain:0.1960157445197518 attack:0.001 decay:0.2 sustain:0 hcutoff:2758.6460625610725 cutoff:4000 ]", "[ 17/2 ⇜ (53/6 → 71/8) | note:A5 s:sawtooth gain:0.2099930863787653 attack:0.001 decay:0.2 sustain:0 hcutoff:2884.6167674275184 cutoff:4000 ]", "[ 17/2 ⇜ (53/6 → 71/8) | note:C#5 s:sawtooth gain:0.2099930863787653 attack:0.001 decay:0.2 sustain:0 hcutoff:2884.6167674275184 cutoff:4000 ]", - "[ 71/8 → 9/1 | note:D3 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2977.1924080321423 ]", - "[ 71/8 → 9/1 | note:D3 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2977.1924080321423 ]", + "[ 71/8 → 9/1 | note:D3 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2977.1924080321423 lpattack:0.1 lpenv:2 ftype:24db ]", + "[ 71/8 → 9/1 | note:D3 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2977.1924080321423 lpattack:0.1 lpenv:2 ftype:24db ]", "[ (71/8 → 9/1) ⇝ 37/4 | note:A5 s:sawtooth gain:0.18921380289925155 attack:0.001 decay:0.2 sustain:0 hcutoff:2696.4013367420957 cutoff:4000 ]", "[ (71/8 → 9/1) ⇝ 37/4 | note:C#5 s:sawtooth gain:0.18921380289925155 attack:0.001 decay:0.2 sustain:0 hcutoff:2696.4013367420957 cutoff:4000 ]", "[ 69/8 ⇜ (107/12 → 9/1) | note:F#5 s:sawtooth gain:0.20294773894016632 attack:0.001 decay:0.2 sustain:0 hcutoff:2821.398875337315 cutoff:4000 ]", @@ -8311,8 +8311,8 @@ exports[`renders tunes > tune: hyperpop 1`] = ` "[ (9/1 → 28/3) ⇝ 75/8 | note:E5 s:sawtooth gain:0.182558300551809 attack:0.001 decay:0.2 sustain:0 hcutoff:2634.707357306267 cutoff:4000 ]", "[ 35/4 ⇜ (109/12 → 73/8) | note:A5 s:sawtooth gain:0.1960157445197518 attack:0.001 decay:0.2 sustain:0 hcutoff:2758.6460625610725 cutoff:4000 ]", "[ 35/4 ⇜ (109/12 → 73/8) | note:C#5 s:sawtooth gain:0.1960157445197518 attack:0.001 decay:0.2 sustain:0 hcutoff:2758.6460625610725 cutoff:4000 ]", - "[ 73/8 → 37/4 | note:D1 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2963.4689354775064 ]", - "[ 73/8 → 37/4 | note:D1 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2963.4689354775064 ]", + "[ 73/8 → 37/4 | note:D1 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2963.4689354775064 lpattack:0.1 lpenv:2 ftype:24db ]", + "[ 73/8 → 37/4 | note:D1 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2963.4689354775064 lpattack:0.1 lpenv:2 ftype:24db ]", "[ (73/8 → 113/12) ⇝ 19/2 | note:A5 s:sawtooth gain:0.17606527116553244 attack:0.001 decay:0.2 sustain:0 hcutoff:2573.60640622541 cutoff:4000 ]", "[ (73/8 → 113/12) ⇝ 19/2 | note:C#5 s:sawtooth gain:0.17606527116553244 attack:0.001 decay:0.2 sustain:0 hcutoff:2573.60640622541 cutoff:4000 ]", "[ 71/8 ⇜ (55/6 → 37/4) | note:F#5 s:sawtooth gain:0.18921380289925155 attack:0.001 decay:0.2 sustain:0 hcutoff:2696.4013367420957 cutoff:4000 ]", @@ -8324,14 +8324,14 @@ exports[`renders tunes > tune: hyperpop 1`] = ` "[ (37/4 → 115/12) ⇝ 77/8 | note:E5 s:sawtooth gain:0.16975035701693547 attack:0.001 decay:0.2 sustain:0 hcutoff:2513.140359039332 cutoff:4000 ]", "[ 9/1 ⇜ (28/3 → 75/8) | note:A5 s:sawtooth gain:0.182558300551809 attack:0.001 decay:0.2 sustain:0 hcutoff:2634.707357306267 cutoff:4000 ]", "[ 9/1 ⇜ (28/3 → 75/8) | note:C#5 s:sawtooth gain:0.182558300551809 attack:0.001 decay:0.2 sustain:0 hcutoff:2634.707357306267 cutoff:4000 ]", - "[ 75/8 → 19/2 | note:D2 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2946.5812012110136 ]", - "[ 75/8 → 19/2 | note:D2 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2946.5812012110136 ]", + "[ 75/8 → 19/2 | note:D2 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2946.5812012110136 lpattack:0.1 lpenv:2 ftype:24db ]", + "[ 75/8 → 19/2 | note:D2 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2946.5812012110136 lpattack:0.1 lpenv:2 ftype:24db ]", "[ (75/8 → 29/3) ⇝ 39/4 | note:A5 s:sawtooth gain:0.16362877128732323 attack:0.001 decay:0.2 sustain:0 hcutoff:2453.350656156431 cutoff:4000 ]", "[ (75/8 → 29/3) ⇝ 39/4 | note:C#5 s:sawtooth gain:0.16362877128732323 attack:0.001 decay:0.2 sustain:0 hcutoff:2453.350656156431 cutoff:4000 ]", "[ 73/8 ⇜ (113/12 → 19/2) | note:F#5 s:sawtooth gain:0.17606527116553244 attack:0.001 decay:0.2 sustain:0 hcutoff:2573.60640622541 cutoff:4000 ]", "[ 73/8 ⇜ (113/12 → 19/2) | note:A4 s:sawtooth gain:0.17606527116553244 attack:0.001 decay:0.2 sustain:0 hcutoff:2573.60640622541 cutoff:4000 ]", - "[ 19/2 → 77/8 | note:D1 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2936.9631544781614 ]", - "[ 19/2 → 77/8 | note:D1 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2936.9631544781614 ]", + "[ 19/2 → 77/8 | note:D1 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2936.9631544781614 lpattack:0.1 lpenv:2 ftype:24db ]", + "[ 19/2 → 77/8 | note:D1 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2936.9631544781614 lpattack:0.1 lpenv:2 ftype:24db ]", "[ 19/2 → 39/4 | note:F#5 s:sawtooth gain:0.16064510432613502 attack:0.001 decay:0.2 sustain:0 hcutoff:2423.7222579792624 cutoff:4000 ]", "[ 19/2 → 39/4 | note:A4 s:sawtooth gain:0.16064510432613502 attack:0.001 decay:0.2 sustain:0 hcutoff:2423.7222579792624 cutoff:4000 ]", "[ 19/2 → 39/4 | s:bd gain:0.7 ]", @@ -8348,8 +8348,8 @@ exports[`renders tunes > tune: hyperpop 1`] = ` "[ (77/8 → 10/1) ⇝ 81/8 | note:A3 s:square gain:0.7 attack:0.01 decay:0.1 sustain:0 cutoff:2915.4076660819765 ]", "[ 75/8 ⇜ (29/3 → 39/4) | note:F#5 s:sawtooth gain:0.16362877128732323 attack:0.001 decay:0.2 sustain:0 hcutoff:2453.350656156431 cutoff:4000 ]", "[ 75/8 ⇜ (29/3 → 39/4) | note:A4 s:sawtooth gain:0.16362877128732323 attack:0.001 decay:0.2 sustain:0 hcutoff:2453.350656156431 cutoff:4000 ]", - "[ 39/4 → 79/8 | note:D3 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2915.4076660819765 ]", - "[ 39/4 → 79/8 | note:D3 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2915.4076660819765 ]", + "[ 39/4 → 79/8 | note:D3 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2915.4076660819765 lpattack:0.1 lpenv:2 ftype:24db ]", + "[ 39/4 → 79/8 | note:D3 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2915.4076660819765 lpattack:0.1 lpenv:2 ftype:24db ]", "[ 39/4 → 10/1 | note:F#5 s:sawtooth gain:0.14926615677294724 attack:0.001 decay:0.2 sustain:0 hcutoff:2307.1030993509794 cutoff:4000 ]", "[ 39/4 → 10/1 | note:A4 s:sawtooth gain:0.14926615677294724 attack:0.001 decay:0.2 sustain:0 hcutoff:2307.1030993509794 cutoff:4000 ]", "[ 39/4 → 10/1 | s:hh3 gain:0.7 ]", @@ -8359,8 +8359,8 @@ exports[`renders tunes > tune: hyperpop 1`] = ` "[ (39/4 → 10/1) ⇝ 41/4 | note:A3 s:square gain:0.7 attack:0.01 decay:0.1 sustain:0 cutoff:2909.5402784268977 ]", "[ 19/2 ⇜ (59/6 → 79/8) | note:A5 s:sawtooth gain:0.157715261412906 attack:0.001 decay:0.2 sustain:0 hcutoff:2394.2782744524975 cutoff:4000 ]", "[ 19/2 ⇜ (59/6 → 79/8) | note:C#5 s:sawtooth gain:0.157715261412906 attack:0.001 decay:0.2 sustain:0 hcutoff:2394.2782744524975 cutoff:4000 ]", - "[ 79/8 → 10/1 | note:D3 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2903.483208638841 ]", - "[ 79/8 → 10/1 | note:D3 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2903.483208638841 ]", + "[ 79/8 → 10/1 | note:D3 s:sawtooth gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2903.483208638841 lpattack:0.1 lpenv:2 ftype:24db ]", + "[ 79/8 → 10/1 | note:D3 s:square gain:0.3 attack:0.01 decay:0.1 sustain:0.5 cutoff:2903.483208638841 lpattack:0.1 lpenv:2 ftype:24db ]", "[ (79/8 → 10/1) ⇝ 41/4 | note:A5 s:sawtooth gain:0.14656891828944 attack:0.001 decay:0.2 sustain:0 hcutoff:2278.446896257612 cutoff:4000 ]", "[ (79/8 → 10/1) ⇝ 41/4 | note:C#5 s:sawtooth gain:0.14656891828944 attack:0.001 decay:0.2 sustain:0 hcutoff:2278.446896257612 cutoff:4000 ]", "[ (79/8 → 10/1) ⇝ 83/8 | note:F#3 s:square gain:0.7 attack:0.01 decay:0.1 sustain:0 cutoff:2903.483208638841 ]", @@ -8484,7 +8484,7 @@ exports[`renders tunes > tune: orbit 1`] = ` exports[`renders tunes > tune: outroMusic 1`] = ` [ "[ 0/1 → 1/2 | s:hh speed:0.9036881079621337 n:3 ]", - "[ 0/1 → 3/4 | n:-7 note:C2 s:sawtooth attack:0.05 decay:0.1 sustain:0.7 cutoff:864.536878321087 gain:0.3 ]", + "[ 0/1 → 3/4 | note:C2 s:sawtooth attack:0.05 decay:0.1 sustain:0.7 cutoff:864.536878321087 gain:0.3 ]", "[ 0/1 → 3/4 | s:bd speed:0.9107561463868479 n:3 ]", "[ (0/1 → 1/1) ⇝ 3/1 | note:B3 s:gm_epiano1 n:1 attack:0.05 decay:0.1 sustain:0.7 cutoff:1111.7252990603447 gain:0.3 ]", "[ (0/1 → 1/1) ⇝ 3/1 | note:D4 s:gm_epiano1 n:1 attack:0.05 decay:0.1 sustain:0.7 cutoff:1111.7252990603447 gain:0.3 ]", diff --git a/website/src/docs/JsDoc.jsx b/website/src/docs/JsDoc.jsx index 88a775a5..1ba80e0a 100644 --- a/website/src/docs/JsDoc.jsx +++ b/website/src/docs/JsDoc.jsx @@ -12,10 +12,11 @@ export function JsDoc({ name, h = 3, hideDescription, punchcard, canvasHeight }) } const synonyms = getTag('synonyms', item)?.split(', ') || []; const CustomHeading = `h${h}`; - const description = item.description.replaceAll(/\{@link ([a-zA-Z\.]+)?#?([a-zA-Z]*)\}/g, (_, a, b) => { - // console.log(_, 'a', a, 'b', b); - return `${a}${b ? `#${b}` : ''}`; - }); + const description = + item.description?.replaceAll(/\{@link ([a-zA-Z\.]+)?#?([a-zA-Z]*)\}/g, (_, a, b) => { + // console.log(_, 'a', a, 'b', b); + return `${a}${b ? `#${b}` : ''}`; + }) || ''; return ( <> {!!h && {item.longname}} diff --git a/website/src/pages/learn/effects.mdx b/website/src/pages/learn/effects.mdx index 12e57846..f77ab4c4 100644 --- a/website/src/pages/learn/effects.mdx +++ b/website/src/pages/learn/effects.mdx @@ -49,6 +49,10 @@ Each filter has 2 parameters: +## ftype + + + ## vowel @@ -78,6 +82,58 @@ Strudel uses ADSR envelopes, which are probably the most common way to describe +# Filter Envelope + +Each filter can receive an additional filter envelope controlling the cutoff value dynamically. It uses an ADSR envelope similar to the one used for amplitude. There is an additional parameter to control the depth of the filter modulation: `lpenv`|`hpenv`|`bpenv`. This allows you to play subtle or huge filter modulations just the same by only increasing or decreasing the depth. + +](3,8,<0 1>)".sub(12)) + .s("/64") + .lpf(sine.range(500,3000).slow(16)) + .lpa(0.005) + .lpd(perlin.range(.02,.2)) + .lps(perlin.range(0,.5).slow(3)) + .lpq(sine.range(2,10).slow(32)) + .release(.5) + .lpenv(perlin.range(1,8).slow(2)) + .ftype('24db') + .room(1) + .juxBy(.5,rev) + .sometimes(add(note(12))) + .stack(s("bd*2").bank('RolandTR909')) + .gain(.5)`} +/> + +There is one filter envelope for each filter type and thus one set of envelope filter parameters preceded either by `lp`, `hp` or `bp`: + +- `lpattack`, `lpdecay`, `lpsustain`, `lprelease`, `lpenv`: filter envelope for the lowpass filter. + - alternatively: `lpa`, `lpd`, `lps`, `lpr` and `lpe`. +- `hpattack`, `hpdecay`, `hpsustain`, `hprelease`, `hpenv`: filter envelope for the highpass filter. + - alternatively: `hpa`, `hpd`, `hps`, `hpr` and `hpe`. +- `bpattack`, `bpdecay`, `bpsustain`, `bprelease`, `bpenv`: filter envelope for the bandpass filter. + - alternatively: `bpa`, `bpd`, `bps`, `bpr` and `bpe`. + +## lpattack + + + +## lpdecay + + + +## lpsustain + + + +## lprelease + + + +## lpenv + + + # Dynamics ## gain diff --git a/website/src/pages/learn/samples.mdx b/website/src/pages/learn/samples.mdx index 9f79e54a..10d8c730 100644 --- a/website/src/pages/learn/samples.mdx +++ b/website/src/pages/learn/samples.mdx @@ -303,6 +303,18 @@ Sampler effects are functions that can be used to change the behaviour of sample +### loop + + + +### loopBegin + + + +### loopEnd + + + ### cut @@ -315,6 +327,10 @@ Sampler effects are functions that can be used to change the behaviour of sample +### fit + + + ### chop @@ -323,6 +339,10 @@ Sampler effects are functions that can be used to change the behaviour of sample +### splice + + + ### speed diff --git a/website/src/pages/learn/synths.mdx b/website/src/pages/learn/synths.mdx index 68bbee34..a9cf094c 100644 --- a/website/src/pages/learn/synths.mdx +++ b/website/src/pages/learn/synths.mdx @@ -88,6 +88,23 @@ You can use fm with any of the above waveforms, although the below examples all +## Wavetable Synthesis + +Strudel can also use the sampler to load custom waveforms as a replacement of the default waveforms used by WebAudio for the base synth. A default set of more than 1000 wavetables is accessible by default (coming from the [AKWF](https://www.adventurekid.se/akrt/waveforms/adventure-kid-waveforms/) set). You can also import/use your own. A wavetable is a one-cycle waveform, which is then repeated to create a sound at the desired frequency. It is a classic but very effective synthesis technique. + +Any sample preceded by the `wt_` prefix will be loaded as a wavetable. This means that the `loop` argument will be set to `1` by defalt. You can scan over the wavetable by using `loopBegin` and `loopEnd` as well. + +") +.n("<1 2 3 4 5 6 7 8 9 10>/2").room(0.5).size(0.9) +.s('wt_flute').velocity(0.25).often(n => n.ply(2)) +.release(0.125).decay("<0.1 0.25 0.3 0.4>").sustain(0) +.cutoff(2000).scope({}).cutoff("<1000 2000 4000>").fast(2)`} +/> + ## ZZFX The "Zuper Zmall Zound Zynth" [ZZFX](https://github.com/KilledByAPixel/ZzFX) is also integrated in strudel. diff --git a/website/src/repl/Repl.jsx b/website/src/repl/Repl.jsx index 4434ed7a..173bb455 100644 --- a/website/src/repl/Repl.jsx +++ b/website/src/repl/Repl.jsx @@ -37,14 +37,22 @@ const modules = [ import('@strudel.cycles/core'), import('@strudel.cycles/tonal'), import('@strudel.cycles/mini'), - isTauri() ? import('@strudel/desktopbridge') : import('@strudel.cycles/midi'), import('@strudel.cycles/xen'), import('@strudel.cycles/webaudio'), - import('@strudel.cycles/osc'), + import('@strudel.cycles/serial'), import('@strudel.cycles/soundfonts'), import('@strudel.cycles/csound'), ]; +if (isTauri()) { + modules.concat([ + import('@strudel/desktopbridge/loggerbridge.mjs'), + import('@strudel/desktopbridge/midibridge.mjs'), + import('@strudel/desktopbridge/oscbridge.mjs'), + ]); +} else { + modules.concat([import('@strudel.cycles/midi'), import('@strudel.cycles/osc')]); +} const modulesLoading = evalScope( controls, // sadly, this cannot be exported from core direclty diff --git a/website/src/repl/tunes.mjs b/website/src/repl/tunes.mjs index dd524283..e33482cf 100644 --- a/website/src/repl/tunes.mjs +++ b/website/src/repl/tunes.mjs @@ -229,7 +229,9 @@ stack( .add("0,.02") .note().gain(.3) .clip("<1@3 [.3 1]>/2") - .s('sawtooth').cutoff(600).color('#F8E71C'), + .cutoff(600) + .lpa(.2).lpenv(-4) + .s('sawtooth').color('#F8E71C'), ).fast(3/2) //.pianoroll({fold:1})`; @@ -468,7 +470,9 @@ stack( .note() .s("sawtooth,square") .gain(.3).attack(0.01).decay(0.1).sustain(.5) - .apply(filter1), + .apply(filter1) + .lpa(.1).lpenv(2).ftype('24db') + , "~@3 [<2 3>,<4 5>]" .echo(4,1/16,.7) .scale(scales) @@ -576,8 +580,8 @@ chord("*2").dict('lefthand').anchor("G4").voicing() .s("gm_epiano1:1") .color('steelblue') .stack( - n("<-7 ~@2 [~@2 -7] -9 ~@2 [~@2 -9] -10!2 ~ [~@2 -10] -5 ~ [-3 -2 -10]@2>*2") - .scale('C3 major') + "<-7 ~@2 [~@2 -7] -9 ~@2 [~@2 -9] -10!2 ~ [~@2 -10] -5 ~ [-3 -2 -10]@2>*2" + .scale('C3 major').note() .s('sawtooth').color('brown') ) .attack(0.05).decay(.1).sustain(.7) @@ -803,14 +807,14 @@ stack( sine.add(saw.slow(4)).range(0,7).segment(8) .superimpose(x=>x.add(.1)) .scale('G0 minor').note() - .s("sawtooth").decay(.1).sustain(0) + .s("sawtooth").decay(.1).sustain(0).lpa(.1).lpenv(4) .gain(.4).cutoff(perlin.range(300,3000).slow(8)).resonance(10) .degradeBy("0 0.1 .5 .1") .rarely(add(note("12"))) , // chord note("Bb3,D4".superimpose(x=>x.add(.2))) - .s('sawtooth').cutoff(1000).struct("<~@3 [~ x]>") + .s('sawtooth').lpf(1000).struct("<~@3 [~ x]>") .decay(.05).sustain(.0).delay(.8).delaytime(.125).room(.8) , // alien