mirror of
https://codeberg.org/uzu/strudel
synced 2026-07-14 14:53:45 -04:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 301fb5e59a | |||
| 9ac691c19e | |||
| 6e7512016c | |||
| 3bbd808783 | |||
| 7b245abd1a | |||
| 9e5c9c7539 | |||
| d14194db6a |
@@ -1,88 +0,0 @@
|
||||
# changelog
|
||||
|
||||
NOTE: you can generate this with `node warm.js`. it might still not be perfectly sorted...
|
||||
|
||||
## november 2025
|
||||
|
||||
- 2025-11-27T22:03:53+01:00 [perf] in `noise`, let noiseMix do the disconnect when it exists by jeromew in: [#1783](https://codeberg.org/uzu/strudel/pulls/1783)
|
||||
- 2025-11-27T22:01:01+01:00 Bug Fix: Retries for sounds tab by glossing in: [#1754](https://codeberg.org/uzu/strudel/pulls/1754)
|
||||
- 2025-11-27T20:36:46+01:00 [hydra] return the hydra object when await initHydra(..) is called by jeromew in: [#1784](https://codeberg.org/uzu/strudel/pulls/1784)
|
||||
- 2025-11-27T20:36:14+01:00 Use errorLogger for query and tonal errors by glossing in: [#1782](https://codeberg.org/uzu/strudel/pulls/1782)
|
||||
- 2025-11-27T20:27:10+01:00 [perf] level 5 `connect-leak` on `vowel` by jeromew in: [#1779](https://codeberg.org/uzu/strudel/pulls/1779)
|
||||
- 2025-11-26T08:23:38+01:00 feat: add prebake script import button for loading .strudel files by daslyfe in: [#1774](https://codeberg.org/uzu/strudel/pulls/1774)
|
||||
- 2025-11-26T07:39:28+01:00 Fix Sampler port trampling by Dayglo in: [#1717](https://codeberg.org/uzu/strudel/pulls/1717)
|
||||
- 2025-11-25T20:59:09+01:00 Feat: Hook up octave and fix example by glossing in: [#1773](https://codeberg.org/uzu/strudel/pulls/1773)
|
||||
- 2025-11-25T20:48:09+01:00 [perf] fix `connect-leak` in `tremolo` param by jeromew in: [#1780](https://codeberg.org/uzu/strudel/pulls/1780)
|
||||
- 2025-11-24T17:51:20+01:00 filter modulation improvements! by daslyfe in: [#1769](https://codeberg.org/uzu/strudel/pulls/1769)
|
||||
- 2025-11-24T01:20:53+01:00 Add o as a synonym for orbit by daslyfe in: [#1766](https://codeberg.org/uzu/strudel/pulls/1766)
|
||||
- 2025-11-23T22:23:26+01:00 [perf] fix `connect-leak` in `fm` modulation by jeromew in: [#1758](https://codeberg.org/uzu/strudel/pulls/1758)
|
||||
- 2025-11-23T09:47:24+01:00 Fix interoperability issue between `all` and `await initHydra()` by jeromew in: [#1663](https://codeberg.org/uzu/strudel/pulls/1663)
|
||||
- 2025-11-23T04:23:06+01:00 Bug fix: Swap l/r gains with temp variable by glossing in: [#1768](https://codeberg.org/uzu/strudel/pulls/1768)
|
||||
- 2025-11-23T00:52:28+01:00 FIX: prevent LFO filter modulation from popping from negative values by daslyfe in: [#1767](https://codeberg.org/uzu/strudel/pulls/1767)
|
||||
- 2025-11-21T01:56:11+01:00 Bug Fix: Use frac due to negative frequencies from FM by glossing in: [#1759](https://codeberg.org/uzu/strudel/pulls/1759)
|
||||
- 2025-11-20T19:48:16+01:00 [perf] fix `connect-leak` added by #1742 when noise() is not used by jeromew in: [#1757](https://codeberg.org/uzu/strudel/pulls/1757)
|
||||
- 2025-11-20T14:51:00+01:00 [perf] fix `connect-leak` in `delay` effect by jeromew in: [#1755](https://codeberg.org/uzu/strudel/pulls/1755)
|
||||
- 2025-11-20T14:49:41+01:00 [perf] fix `connect leak` when .noise() is in the mix by jeromew in: [#1742](https://codeberg.org/uzu/strudel/pulls/1742)
|
||||
- 2025-11-18T23:52:45+01:00 wchooseCycles has now notes in an example by scrappy_fiddler in: [#1748](https://codeberg.org/uzu/strudel/pulls/1748)
|
||||
- 2025-11-17T05:31:54+01:00 Feature: Partials by glossing in: [#1659](https://codeberg.org/uzu/strudel/pulls/1659)
|
||||
- 2025-11-16T22:06:14+01:00 README: update superdough documentation by TristanMlct in: [#1741](https://codeberg.org/uzu/strudel/pulls/1741)
|
||||
- 2025-11-16T21:08:54+01:00 Worklet optimizations by glossing in: [#1730](https://codeberg.org/uzu/strudel/pulls/1730)
|
||||
- 2025-11-15T14:59:30+01:00 [perf] disconnect Offline AudioNode connections in generateReverb by jeromew in: [#1740](https://codeberg.org/uzu/strudel/pulls/1740)
|
||||
- 2025-11-15T14:57:47+01:00 [perf] disconnect `send` effect AudioNode when `room` is used by jeromew in: [#1736](https://codeberg.org/uzu/strudel/pulls/1736)
|
||||
- 2025-11-12T21:22:34+01:00 [supradough] fix: unstable filter by froos in: [#1593](https://codeberg.org/uzu/strudel/pulls/1593)
|
||||
- 2025-11-12T21:12:07+01:00 Fix link syntax in `project-start` by Kissaki in: [#1724](https://codeberg.org/uzu/strudel/pulls/1724)
|
||||
- 2025-11-12T21:06:13+01:00 Fix web README sample code by Kissaki in: [#1725](https://codeberg.org/uzu/strudel/pulls/1725)
|
||||
- 2025-11-12T20:59:39+01:00 Fix typo: 'studel' -> 'strudel' by drhayes in: [#1726](https://codeberg.org/uzu/strudel/pulls/1726)
|
||||
- 2025-11-12T15:23:41+01:00 fix for node 24 support in tests - #1718 by ausav in: [#1719](https://codeberg.org/uzu/strudel/pulls/1719)
|
||||
- 2025-11-11T09:00:49+01:00 soundAlias example fix by PepsiiMan in: [#1720](https://codeberg.org/uzu/strudel/pulls/1720)
|
||||
- 2025-11-10T08:18:36+01:00 fix: Make Supradough package build by munshkr in: [#1711](https://codeberg.org/uzu/strudel/pulls/1711)
|
||||
- 2025-11-10T08:18:00+01:00 added docs for spaces in scale names by ondras in: [#1715](https://codeberg.org/uzu/strudel/pulls/1715)
|
||||
- 2025-11-10T02:44:01+01:00 Feature: LFOs for Filters by glossing in: [#1636](https://codeberg.org/uzu/strudel/pulls/1636)
|
||||
- 2025-11-05T17:07:00+01:00 improvement: sync midi messages to audio context clock by daslyfe in: [#1708](https://codeberg.org/uzu/strudel/pulls/1708)
|
||||
- 2025-11-04T19:36:14+01:00 Add stick button mappings to gamepad implementation and improve docs by kaosuryoko in: [#1696](https://codeberg.org/uzu/strudel/pulls/1696)
|
||||
- 2025-11-04T19:29:35+01:00 Refactor sound stopping and triggering logic in SoundsTab component by IJOL in: [#1688](https://codeberg.org/uzu/strudel/pulls/1688)
|
||||
- 2025-11-04T19:13:21+01:00 Add setting to toggle pattern auto-start on pattern change by moumar in: [#1690](https://codeberg.org/uzu/strudel/pulls/1690)
|
||||
- 2025-11-04T18:58:50+01:00 Voicings JSDoc by sharkeys_lunchbox in: [#1686](https://codeberg.org/uzu/strudel/pulls/1686)
|
||||
- 2025-11-01T07:50:40+01:00 FIX: Loading local samples uses too much memory by daslyfe in: [#1706](https://codeberg.org/uzu/strudel/pulls/1706)
|
||||
|
||||
|
||||
## october 2025
|
||||
|
||||
- 2025-10-28T22:58:24+01:00 Bug Fix: Handle scale-for-notes when n also supplied by glossing in: [#1625](https://codeberg.org/uzu/strudel/pulls/1625)
|
||||
- 2025-10-28T22:51:16+01:00 Repurpose vim shortcuts for usability by dtricks in: [#1624](https://codeberg.org/uzu/strudel/pulls/1624)
|
||||
- 2025-10-28T22:35:21+01:00 Add support for euclidian in mondo with `bd&3:8` by TristanCacqueray in: [#1630](https://codeberg.org/uzu/strudel/pulls/1630)
|
||||
- 2025-10-27T10:42:07+01:00 Use bunny cdn for all samples and json files by yaxu in: [#1701](https://codeberg.org/uzu/strudel/pulls/1701)
|
||||
- 2025-10-26T22:52:54+01:00 degithub - switch some samples to bunnycdn by yaxu in: [#1697](https://codeberg.org/uzu/strudel/pulls/1697)
|
||||
- 2025-10-26T17:09:22+01:00 Fix ZZFX example by moumar in: [#1685](https://codeberg.org/uzu/strudel/pulls/1685)
|
||||
- 2025-10-23T15:56:04+02:00 Make osc port and host configurable. Changes dependencies. by yaxu in: [#1682](https://codeberg.org/uzu/strudel/pulls/1682)
|
||||
- 2025-10-23T03:47:31+02:00 Adds back shape to superdough by glossing in: [#1672](https://codeberg.org/uzu/strudel/pulls/1672)
|
||||
- 2025-10-22T22:15:19+02:00 Fix sampler.mjs githubPath by jeromew in: [#1684](https://codeberg.org/uzu/strudel/pulls/1684)
|
||||
- 2025-10-22T16:20:50+02:00 Fix onPaint for widgets by yaxu in: [#1658](https://codeberg.org/uzu/strudel/pulls/1658)
|
||||
- 2025-10-22T15:19:12+02:00 Fix a bug introduced by #4e17cfbdd6 by milliganf in: [#1679](https://codeberg.org/uzu/strudel/pulls/1679)
|
||||
- 2025-10-18T04:29:20+02:00 Bug Fix: Wavetable: phase wrapping at 1 and detune by glossing in: [#1620](https://codeberg.org/uzu/strudel/pulls/1620)
|
||||
- 2025-10-16T20:26:28+02:00 github samples: default to "samples" if repository is not specified by prezmop in: [#1644](https://codeberg.org/uzu/strudel/pulls/1644)
|
||||
- 2025-10-16T17:33:39+02:00 Docs: add example of custom chained function by dariusk in: [#1642](https://codeberg.org/uzu/strudel/pulls/1642)
|
||||
- 2025-10-14T12:39:48+02:00 textbox by yaxu in: [#1650](https://codeberg.org/uzu/strudel/pulls/1650)
|
||||
- 2025-10-13T07:05:57+02:00 Feature: Wavetable FM by glossing in: [#1623](https://codeberg.org/uzu/strudel/pulls/1623)
|
||||
- 2025-10-12T20:43:42+02:00 fix: repair REPL sample sources and URL concat (#1640) by erikfox in: [#1646](https://codeberg.org/uzu/strudel/pulls/1646)
|
||||
- 2025-10-10T11:25:02+02:00 Add control-metadata by yaxu in: [#1634](https://codeberg.org/uzu/strudel/pulls/1634)
|
||||
- 2025-10-07T22:59:39+02:00 Fix MQTT: change the trigger handler to match new hap by jurrchen in: [#1629](https://codeberg.org/uzu/strudel/pulls/1629)
|
||||
- 2025-10-05T21:42:19+02:00 Fix case sensitivity for synonym search by vvolhejn in: [#1618](https://codeberg.org/uzu/strudel/pulls/1618)
|
||||
- 2025-10-01T08:56:35+02:00 Optimize wavetable synth by glossing in: [#1613](https://codeberg.org/uzu/strudel/pulls/1613)
|
||||
- 2025-10-01T02:02:33+02:00 fix: pattern switching by daslyfe in: [#1616](https://codeberg.org/uzu/strudel/pulls/1616)
|
||||
|
||||
## September 2025
|
||||
|
||||
- 2025-09-29T02:10:54+02:00 fix wavetable JSON by daslyfe in: [#1611](https://codeberg.org/uzu/strudel/pulls/1611)
|
||||
- 2025-09-28T21:28:16+02:00 rename wavetable controls to match existing naming conventions by daslyfe in: [#1610](https://codeberg.org/uzu/strudel/pulls/1610)
|
||||
- 2025-09-27T23:28:13+02:00 Feature: Wavetable synth improvements by glossing in: [#1607](https://codeberg.org/uzu/strudel/pulls/1607)
|
||||
- 2025-09-27T21:43:10+02:00 wavetable improvements by daslyfe in: [#1608](https://codeberg.org/uzu/strudel/pulls/1608)
|
||||
- 2025-09-26T08:48:46+02:00 create orbit based DJ filter by daslyfe in: [#1603](https://codeberg.org/uzu/strudel/pulls/1603)
|
||||
- 2025-09-23T13:28:20+02:00 fix: time signal by daslyfe in: [#1583](https://codeberg.org/uzu/strudel/pulls/1583)
|
||||
- 2025-09-21T16:16:06+02:00 mondo: fix all + setcpm + setcps by froos in: [#1600](https://codeberg.org/uzu/strudel/pulls/1600)
|
||||
- 2025-09-19T21:59:43+02:00 fix: osc error message by froos in: [#1597](https://codeberg.org/uzu/strudel/pulls/1597)
|
||||
- 2025-09-18T21:09:56+02:00 osc: --debug flag to see incoming messages by froos in: [#1595](https://codeberg.org/uzu/strudel/pulls/1595)
|
||||
- 2025-09-17T14:12:10+02:00 simplify osc usage by froos in: [#1588](https://codeberg.org/uzu/strudel/pulls/1588)
|
||||
- 2025-09-16T16:59:58+02:00 dev: logger errors to console in dev mode by daslyfe in: [#1585](https://codeberg.org/uzu/strudel/pulls/1585)
|
||||
- 2025-09-15T22:52:14+02:00 add tic80 font by froos in: [#1579](https://codeberg.org/uzu/strudel/pulls/1579)
|
||||
- 2025-09-15T00:00:19+02:00 supradough: fix delay + add some jsdoc types by froos in: [#1578](https://codeberg.org/uzu/strudel/pulls/1578)
|
||||
@@ -1,4 +1,4 @@
|
||||
<script src="https://unpkg.com/@strudel/web@1.0.3"></script>
|
||||
<script src="https://unpkg.com/@strudel/web@1.2.6"></script>
|
||||
<button id="play">PLAY</button>
|
||||
<script>
|
||||
initStrudel({
|
||||
|
||||
@@ -12,14 +12,14 @@
|
||||
"vite": "^6.0.11"
|
||||
},
|
||||
"dependencies": {
|
||||
"@strudel/codemirror": "workspace:*",
|
||||
"@strudel/core": "workspace:*",
|
||||
"@strudel/draw": "workspace:*",
|
||||
"@strudel/mini": "workspace:*",
|
||||
"@strudel/soundfonts": "workspace:*",
|
||||
"@strudel/tonal": "workspace:*",
|
||||
"@strudel/transpiler": "workspace:*",
|
||||
"@strudel/webaudio": "workspace:*"
|
||||
"@strudel/codemirror": "1.2.6",
|
||||
"@strudel/core": "1.2.5",
|
||||
"@strudel/draw": "1.2.5",
|
||||
"@strudel/mini": "1.2.5",
|
||||
"@strudel/soundfonts": "1.2.6",
|
||||
"@strudel/tonal": "1.2.5",
|
||||
"@strudel/transpiler": "1.2.5",
|
||||
"@strudel/webaudio": "1.2.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export const bumpStreet = `// froos - "22 bump street", licensed with CC BY-NC-SA 4.0
|
||||
samples('github:felixroos/samples')
|
||||
samples('https://strudel.cc/tidal-drum-machines.json', 'github:ritchse/tidal-drum-machines/main/machines/')
|
||||
samples('https://strudel.b-cdn.net/tidal-drum-machines.json', 'github:ritchse/tidal-drum-machines/main/machines/')
|
||||
|
||||
"<[0,<6 7 9>,13,<17 20 22 26>]!2>/2"
|
||||
// make it 22 edo
|
||||
@@ -28,13 +28,12 @@ samples('https://strudel.cc/tidal-drum-machines.json', 'github:ritchse/tidal-dru
|
||||
.mask("<0@4 1@12>/4")
|
||||
.reset("<x@15 [x(3,8) x*[4 8]]>")
|
||||
// wait for it...
|
||||
).fast(2/3)
|
||||
).fast(4/3)
|
||||
//.crush(6) // remove "//" if you dare`;
|
||||
|
||||
export const trafficFlam = `// froos - "traffic flam", licensed with CC BY-NC-SA 4.0
|
||||
|
||||
samples('github:felixroos/samples')
|
||||
samples('https://strudel.cc/tidal-drum-machines.json', 'github:ritchse/tidal-drum-machines/main/machines/')
|
||||
samples('https://strudel.b-cdn.net/tidal-drum-machines.json', 'github:ritchse/tidal-drum-machines/main/machines/')
|
||||
|
||||
addVoicings('hip', {
|
||||
m11: ['2M 3m 4P 7m'],
|
||||
@@ -61,16 +60,14 @@ stack(
|
||||
s("rim*4").end(.05).bank('RolandTR808').speed(.8).room(.2)
|
||||
)
|
||||
)
|
||||
.late("[0 .05]*2").late(12)
|
||||
|
||||
.late("[0 .05]*2").late(12).fast(2)
|
||||
`;
|
||||
|
||||
export const funk42 = `// froos - how to funk in 42 lines of code
|
||||
// adapted from "how to funk in two minutes" by marc rebillet https://www.youtube.com/watch?v=3vBwRfQbXkg
|
||||
// thanks to peach for the transcription: https://www.youtube.com/watch?v=8eiPXvIgda4
|
||||
|
||||
samples('github:felixroos/samples')
|
||||
samples('https://strudel.cc/tidal-drum-machines.json', 'github:ritchse/tidal-drum-machines/main/machines/')
|
||||
samples('https://strudel.b-cdn.net/tidal-drum-machines.json', 'github:ritchse/tidal-drum-machines/main/machines/')
|
||||
|
||||
setcps(.5)
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<button id="c">C</button>
|
||||
<button id="stop">stop</button>
|
||||
<script type="module">
|
||||
import { initStrudel } from '@strudel/web';
|
||||
import { initStrudel, hush } from '@strudel/web';
|
||||
initStrudel({
|
||||
prebake: () => samples('github:tidalcycles/dirt-samples'),
|
||||
});
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
"vite": "^6.0.11"
|
||||
},
|
||||
"dependencies": {
|
||||
"@strudel/web": "workspace:*"
|
||||
"@strudel/web": "1.2.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
|
||||
@@ -1294,8 +1294,6 @@ export const { fanchor } = registerControl('fanchor');
|
||||
*
|
||||
* @name lprate
|
||||
* @param {number | Pattern} rate rate in hertz
|
||||
* @example
|
||||
* note("<c c c# c c c4>*16").s("sawtooth").lpf(600).lprate("<4 8 2 1>")
|
||||
*/
|
||||
export const { lprate } = registerControl('lprate');
|
||||
|
||||
@@ -1304,8 +1302,6 @@ export const { lprate } = registerControl('lprate');
|
||||
*
|
||||
* @name lpsync
|
||||
* @param {number | Pattern} rate rate in cycles
|
||||
* @example
|
||||
* note("<c c c# c c c4>*16").s("sawtooth").lpf(600).lpsync("<4 8 2 1>")
|
||||
*/
|
||||
export const { lpsync } = registerControl('lpsync');
|
||||
|
||||
@@ -1314,23 +1310,8 @@ export const { lpsync } = registerControl('lpsync');
|
||||
*
|
||||
* @name lpdepth
|
||||
* @param {number | Pattern} depth depth of modulation
|
||||
* @example
|
||||
* note("<c c c# c c c4>*16").s("sawtooth").lpf(600).lpdepth("<1 .5 1.8 0>")
|
||||
*/
|
||||
|
||||
export const { lpdepth } = registerControl('lpdepth');
|
||||
/**
|
||||
* Depth of the LFO for the lowpass filter, in HZ
|
||||
*
|
||||
* @name lpdepthfrequency
|
||||
* @synonyms
|
||||
* lpdethfreq
|
||||
* @param {number | Pattern} depth depth of modulation
|
||||
* @example
|
||||
* note("<c c c# c c c4>*16").s("sawtooth").lpf(600).lpdepthfrequency("<200 500 100 0>")
|
||||
*/
|
||||
|
||||
export const { lpdepthfrequency } = registerControl('lpdepthfrequency', 'lpdepthfreq');
|
||||
|
||||
/**
|
||||
* Shape of the LFO for the lowpass filter
|
||||
@@ -1380,19 +1361,6 @@ export const { bpsync } = registerControl('bpsync');
|
||||
*/
|
||||
export const { bpdepth } = registerControl('bpdepth');
|
||||
|
||||
/**
|
||||
* Depth of the LFO for the bandpass filter, in HZ
|
||||
*
|
||||
* @name bpdepthfrequency
|
||||
* @synonyms
|
||||
* bpdethfreq
|
||||
* @param {number | Pattern} depth depth of modulation
|
||||
* @example
|
||||
* note("<c c c# c c c4>*16").s("sawtooth").lpf(600).bpdepthfrequency("<200 500 100 0>")
|
||||
*/
|
||||
|
||||
export const { bpdepthfrequency } = registerControl('bpdepthfrequency', 'bpdepthfreq');
|
||||
|
||||
/**
|
||||
* Shape of the LFO for the bandpass filter
|
||||
*
|
||||
@@ -1439,20 +1407,7 @@ export const { hpsync } = registerControl('hpsync');
|
||||
* @name hpdepth
|
||||
* @param {number | Pattern} depth depth of modulation
|
||||
*/
|
||||
export const { hpdepth, hpdepthfreq } = registerControl('hpdepth');
|
||||
|
||||
/**
|
||||
* Depth of the LFO for the hipass filter, in hz
|
||||
*
|
||||
* @name hpdepthfrequency
|
||||
* @synonyms
|
||||
* hpdethfreq
|
||||
* @param {number | Pattern} depth depth of modulation
|
||||
* @example
|
||||
* note("<c c c# c c c4>*16").s("sawtooth").lpf(600).hpdepthfrequency("<200 500 100 0>")
|
||||
*/
|
||||
|
||||
export const { hpdepthfrequency } = registerControl('hpdepthfrequency', 'hpdepthfreq');
|
||||
export const { hpdepth } = registerControl('hpdepth');
|
||||
|
||||
/**
|
||||
* Shape of the LFO for the highpass filter
|
||||
@@ -1865,12 +1820,12 @@ export const { nudge } = registerControl('nudge');
|
||||
* Sets the default octave of a synth.
|
||||
*
|
||||
* @name octave
|
||||
* @synonyms oct
|
||||
* @param {number | Pattern} octave octave number
|
||||
* @example
|
||||
* n("0,4,7").scale("F:minor").s('supersaw').octave("<0 1 2 3>")
|
||||
* n("0,4,7").s('supersquare').octave("<3 4 5 6>").osc()
|
||||
* @superDirtOnly
|
||||
*/
|
||||
export const { octave, oct } = registerControl('octave', 'oct');
|
||||
export const { octave } = registerControl('octave');
|
||||
|
||||
// ['ophatdecay'],
|
||||
// TODO: example
|
||||
@@ -1878,7 +1833,6 @@ export const { octave, oct } = registerControl('octave', 'oct');
|
||||
* An `orbit` is a global parameter context for patterns. Patterns with the same orbit will share the same global effects.
|
||||
*
|
||||
* @name orbit
|
||||
* @synonyms o
|
||||
* @param {number | Pattern} number
|
||||
* @example
|
||||
* stack(
|
||||
@@ -1886,7 +1840,7 @@ export const { octave, oct } = registerControl('octave', 'oct');
|
||||
* s("~ sd ~ sd").delay(.5).delaytime(.125).orbit(2)
|
||||
* )
|
||||
*/
|
||||
export const { orbit } = registerControl('orbit', 'o');
|
||||
export const { orbit } = registerControl('orbit');
|
||||
// TODO: what is this? not found in tidal doc Answer: gain is limited to maximum of 2. This allows you to go over that
|
||||
export const { overgain } = registerControl('overgain');
|
||||
// TODO: what is this? not found in tidal doc. Similar to above, but limited to 1
|
||||
@@ -2310,6 +2264,7 @@ export const { tsdelay } = registerControl('tsdelay');
|
||||
export const { real } = registerControl('real');
|
||||
export const { imag } = registerControl('imag');
|
||||
export const { enhance } = registerControl('enhance');
|
||||
export const { partials } = registerControl('partials');
|
||||
export const { comb } = registerControl('comb');
|
||||
export const { smear } = registerControl('smear');
|
||||
export const { scram } = registerControl('scram');
|
||||
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
stringifyValues,
|
||||
} from './util.mjs';
|
||||
import drawLine from './drawLine.mjs';
|
||||
import { errorLogger, logger } from './logger.mjs';
|
||||
import { logger } from './logger.mjs';
|
||||
|
||||
let stringParser;
|
||||
|
||||
@@ -414,7 +414,7 @@ export class Pattern {
|
||||
try {
|
||||
return this.query(new State(new TimeSpan(begin, end), controls));
|
||||
} catch (err) {
|
||||
errorLogger(err, 'query');
|
||||
logger(`[query]: ${err.message}`, 'error');
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -3624,66 +3624,3 @@ for (const name of distAlgoNames) {
|
||||
return this.distort(argsPat);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns a list of patterns into a single pattern which outputs list-values
|
||||
*
|
||||
* @name parray
|
||||
* @returns Pattern
|
||||
*/
|
||||
export const parray = (pats) => {
|
||||
const pack = (...xs) => xs;
|
||||
let acc = pure(curry(pack, null, pats.length));
|
||||
for (const p of pats) acc = acc.appBoth(reify(p));
|
||||
return acc;
|
||||
};
|
||||
|
||||
const _ensureListPattern = (list) => {
|
||||
if (Array.isArray(list)) {
|
||||
return parray(list);
|
||||
}
|
||||
return reify(list);
|
||||
};
|
||||
|
||||
/**
|
||||
* Scale the magnitude of the harmonics of one of the core synths ('sine', 'tri', 'saw', ..)
|
||||
*
|
||||
* Can also be used to create a new synth via `s('user').partials(...)`
|
||||
*
|
||||
* @name partials
|
||||
* @param {number[] | Pattern} magnitudes List of [0, 1] magnitudes for partials. 0th entry is the fundamental harmonic (i.e. DC offset is skipped)
|
||||
* @example
|
||||
* s("user").seg(16).n(irand(8)).scale("A:major")
|
||||
* .partials([1, 0, 1, 0, 0, 1])
|
||||
* @example
|
||||
* s("saw").seg(8).n(irand(12)).scale("G#:minor")
|
||||
* .partials(binaryL(irand(256).add("1")))
|
||||
*/
|
||||
Pattern.prototype.partials = function (list) {
|
||||
return this.withValue((v) => (l) => ({ ...v, partials: l })).appLeft(_ensureListPattern(list));
|
||||
};
|
||||
|
||||
// Also create a top-level function
|
||||
export const partials = (list) => {
|
||||
return _ensureListPattern(list).as('partials');
|
||||
};
|
||||
|
||||
/**
|
||||
* Rotates the harmonics of one of the core synths ('sine', 'tri', 'saw', 'user', ..) by a list of phases
|
||||
*
|
||||
* @name phases
|
||||
* @param {number[] | Pattern} phases List of [0, 1) phases for partials. 0th entry is the fundamental phase (i.e. DC offset is skipped)
|
||||
* @example
|
||||
* // Phase cancellation
|
||||
* s("saw").seg(8).n(irand(12)).scale("G#1:minor")
|
||||
* .partials(partials([1, 1, 1]))
|
||||
* .superimpose(x => x.phases([0.5, 0.5, 0.5]))
|
||||
*/
|
||||
Pattern.prototype.phases = function (list) {
|
||||
return this.withValue((v) => (l) => ({ ...v, phases: l })).appLeft(_ensureListPattern(list));
|
||||
};
|
||||
|
||||
// Also create a top-level function
|
||||
export const phases = (list) => {
|
||||
return _ensureListPattern(list).as('phases');
|
||||
};
|
||||
|
||||
+7
-17
@@ -152,9 +152,9 @@ export function repl({
|
||||
// allows muting a pattern x with x_ or _x
|
||||
return silence;
|
||||
}
|
||||
if (id.includes('$')) {
|
||||
if (id === '$') {
|
||||
// allows adding anonymous patterns with $:
|
||||
id = `${id}${anonymousIndex}`;
|
||||
id = `$${anonymousIndex}`;
|
||||
anonymousIndex++;
|
||||
}
|
||||
pPatterns[id] = this;
|
||||
@@ -215,19 +215,8 @@ export function repl({
|
||||
let { pattern, meta } = await _evaluate(code, transpiler, transpilerOptions);
|
||||
if (Object.keys(pPatterns).length) {
|
||||
let patterns = [];
|
||||
let soloActive = false;
|
||||
for (const [key, value] of Object.entries(pPatterns)) {
|
||||
// handle soloed patterns ex: S$: s("bd!4")
|
||||
const isSolod = key.length > 1 && key.startsWith('S');
|
||||
if (isSolod && soloActive === false) {
|
||||
// first time we see a soloed pattern, clear existing patterns
|
||||
patterns = [];
|
||||
soloActive = true;
|
||||
}
|
||||
if (!soloActive || (soloActive && isSolod)) {
|
||||
const valWithState = value.withState((state) => state.setControls({ id: key }));
|
||||
patterns.push(valWithState);
|
||||
}
|
||||
patterns.push(value.withState((state) => state.setControls({ id: key })));
|
||||
}
|
||||
if (eachTransform) {
|
||||
// Explicit lambda so only element (not index and array) are passed
|
||||
@@ -238,13 +227,14 @@ export function repl({
|
||||
pattern = eachTransform(pattern);
|
||||
}
|
||||
if (allTransforms.length) {
|
||||
for (const transform of allTransforms) {
|
||||
pattern = transform(pattern);
|
||||
for (let i in allTransforms) {
|
||||
pattern = allTransforms[i](pattern);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isPattern(pattern)) {
|
||||
pattern = silence;
|
||||
const message = `got "${typeof evaluated}" instead of pattern`;
|
||||
throw new Error(message + (typeof evaluated === 'function' ? ', did you forget to call a function?' : '.'));
|
||||
}
|
||||
logger(`[eval] code updated`);
|
||||
pattern = await setPattern(pattern, autostart);
|
||||
|
||||
@@ -228,7 +228,7 @@ const timeToRands = (t, n) => timeToRandsPrime(timeToIntSeed(t), n);
|
||||
export const run = (n) => saw.range(0, n).round().segment(n);
|
||||
|
||||
/**
|
||||
* Creates a binary pattern from a number.
|
||||
* Creates a pattern from a binary number.
|
||||
*
|
||||
* @name binary
|
||||
* @param {number} n - input number to convert to binary
|
||||
@@ -242,7 +242,7 @@ export const binary = (n) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a binary pattern from a number, padded to n bits long.
|
||||
* Creates a pattern from a binary number, padded to n bits long.
|
||||
*
|
||||
* @name binaryN
|
||||
* @param {number} n - input number to convert to binary
|
||||
@@ -258,51 +258,6 @@ export const binaryN = (n, nBits = 16) => {
|
||||
return reify(n).segment(nBits).brshift(bitPos).band(pure(1));
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a binary list pattern from a number.
|
||||
*
|
||||
* @name binaryL
|
||||
* @param {number} n - input number to convert to binary
|
||||
* s("saw").seg(8)
|
||||
* .partials(binaryL(irand(4096).add(1)))
|
||||
*/
|
||||
export const binaryL = (n) => {
|
||||
const nBits = reify(n).log2(0).floor().add(1);
|
||||
return binaryNL(n, nBits);
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a binary list pattern from a number, padded to n bits long.
|
||||
*
|
||||
* @name binaryNL
|
||||
* @param {number} n - input number to convert to binary
|
||||
* @param {number} nBits - pattern length, defaults to 16
|
||||
*/
|
||||
export const binaryNL = (n, nBits = 16) => {
|
||||
return reify(n)
|
||||
.withValue((v) => (bits) => {
|
||||
const bList = [];
|
||||
for (let i = bits - 1; i >= 0; i--) {
|
||||
bList.push((v >> i) & 1);
|
||||
}
|
||||
return bList;
|
||||
})
|
||||
.appLeft(reify(nBits));
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a list of random numbers of the given length
|
||||
*
|
||||
* @name randL
|
||||
* @param {number} n Number of random numbers to sample
|
||||
* @example
|
||||
* s("saw").seg(16).n(irand(12)).scale("F1:minor")
|
||||
* .partials(randL(8))
|
||||
*/
|
||||
export const randL = (n) => {
|
||||
return signal((t) => (nVal) => timeToRands(t, nVal).map(Math.abs)).appLeft(reify(n));
|
||||
};
|
||||
|
||||
export const randrun = (n) => {
|
||||
return signal((t) => {
|
||||
// Without adding 0.5, the first cycle is always 0,1,2,3,...
|
||||
@@ -524,7 +479,7 @@ export const wchoose = (...pairs) => wchooseWith(rand, ...pairs);
|
||||
* @example
|
||||
* wchooseCycles(["bd",10], ["hh",1], ["sd",1]).s().fast(8)
|
||||
* @example
|
||||
* wchooseCycles(["c c c",5], ["a a a",3], ["f f f",1]).fast(4).note()
|
||||
* wchooseCycles(["bd bd bd",5], ["hh hh hh",3], ["sd sd sd",1]).fast(4).s()
|
||||
* @example
|
||||
* // The probability can itself be a pattern
|
||||
* wchooseCycles(["bd(3,8)","<5 0>"], ["hh hh hh",3]).fast(4).s()
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# @strudel/canvas
|
||||
# @strudel/draw
|
||||
|
||||
Helpers for drawing with the Canvas API and Strudel
|
||||
|
||||
## Install
|
||||
|
||||
```sh
|
||||
npm i @strudel/canvas --save
|
||||
npm i @strudel/draw --save
|
||||
```
|
||||
|
||||
@@ -35,7 +35,6 @@ export async function initHydra(options = {}) {
|
||||
hydra.synth.s0.init({ src: canvas });
|
||||
}
|
||||
}
|
||||
return hydra;
|
||||
}
|
||||
|
||||
export function clearHydra() {
|
||||
|
||||
@@ -9,7 +9,6 @@ import readline from 'readline';
|
||||
import os from 'os';
|
||||
|
||||
const LOG = !!process.env.LOG || false;
|
||||
const PORT = process.env.PORT || 5432;
|
||||
const VALID_AUDIO_EXTENSIONS = ['wav', 'mp3', 'ogg'];
|
||||
|
||||
const isAudioFile = (f) => {
|
||||
@@ -55,6 +54,7 @@ async function getBanks(directory, flat = false) {
|
||||
banks[bank].push(subDir);
|
||||
return subDir;
|
||||
});
|
||||
banks._base = `http://localhost:5432`;
|
||||
return { banks, files };
|
||||
}
|
||||
|
||||
@@ -134,6 +134,8 @@ const server = http.createServer(async (req, res) => {
|
||||
readStream.pipe(res);
|
||||
});
|
||||
|
||||
// eslint-disable-next-line
|
||||
const PORT = process.env.PORT || 5432;
|
||||
const IP_ADDRESS = '0.0.0.0';
|
||||
let IP;
|
||||
const networkInterfaces = os.networkInterfaces();
|
||||
|
||||
@@ -89,7 +89,7 @@ superdough({ s: 'bd', delay: 0.5 }, 0, 1);
|
||||
- `decay`: seconds of decay phase
|
||||
- `sustain`: gain of sustain phase
|
||||
- `release`: seconds of release phase
|
||||
- `deadline`: seconds from audio context initialization before playing the sound (getAudioContextCurrentTime() = immediate)
|
||||
- `deadline`: seconds until the sound should play (0 = immediate)
|
||||
- `duration`: seconds the sound should last. optional for one shot samples, required for synth sounds
|
||||
|
||||
### registerSynthSounds()
|
||||
|
||||
@@ -191,7 +191,8 @@ export function applyParameterModulators(audioContext, param, start, end, envelo
|
||||
const lfo = getParamLfo(audioContext, param, start, end, lfoValues);
|
||||
return { lfo, disconnect: () => lfo?.disconnect() };
|
||||
}
|
||||
export function createFilter(context, start, end, params, cps, cycle) {
|
||||
|
||||
export function createFilter(context, start, end, params, cps) {
|
||||
let {
|
||||
frequency,
|
||||
anchor,
|
||||
@@ -201,14 +202,12 @@ export function createFilter(context, start, end, params, cps, cycle) {
|
||||
q = 1,
|
||||
drive = 0.69,
|
||||
depth,
|
||||
depthfrequency,
|
||||
dcoffset = -0.5,
|
||||
skew,
|
||||
shape,
|
||||
rate,
|
||||
sync,
|
||||
} = params;
|
||||
|
||||
let frequencyParam, filter;
|
||||
if (model === 'ladder') {
|
||||
filter = getWorklet(context, 'ladder-processor', { frequency, q, drive });
|
||||
@@ -239,25 +238,8 @@ export function createFilter(context, start, end, params, cps, cycle) {
|
||||
if (sync != null) {
|
||||
rate = cps * sync;
|
||||
}
|
||||
const hasLFO = [depth, depthfrequency, skew, shape, rate].some((v) => v !== undefined);
|
||||
if (hasLFO) {
|
||||
depth = depth ?? 1;
|
||||
const time = cycle / cps;
|
||||
const modDepth = depthfrequency ?? (depth ?? 1) * frequency;
|
||||
const lfoValues = {
|
||||
depth: modDepth,
|
||||
dcoffset,
|
||||
skew,
|
||||
shape,
|
||||
frequency: rate ?? cps,
|
||||
min: -frequency + 30,
|
||||
max: 20000 - frequency,
|
||||
time,
|
||||
curve: 1,
|
||||
};
|
||||
getParamLfo(context, frequencyParam, start, end, lfoValues);
|
||||
}
|
||||
|
||||
const lfoValues = { depth, dcoffset, skew, shape, frequency: rate };
|
||||
getParamLfo(context, frequencyParam, start, end, lfoValues);
|
||||
return filter;
|
||||
}
|
||||
|
||||
@@ -280,15 +262,7 @@ export function drywet(dry, wet, wetAmount = 0) {
|
||||
let mix = ac.createGain();
|
||||
dry_gain.connect(mix);
|
||||
wet_gain.connect(mix);
|
||||
return {
|
||||
node: mix,
|
||||
onended: () => {
|
||||
dry_gain.disconnect(mix);
|
||||
wet_gain.disconnect(mix);
|
||||
dry.disconnect(dry_gain);
|
||||
wet.disconnect(wet_gain);
|
||||
},
|
||||
};
|
||||
return mix;
|
||||
}
|
||||
|
||||
let curves = ['linear', 'exponential'];
|
||||
@@ -323,10 +297,6 @@ export function getVibratoOscillator(param, value, t) {
|
||||
gain.gain.value = vibmod * 100;
|
||||
vibratoOscillator.connect(gain);
|
||||
gain.connect(param);
|
||||
vibratoOscillator.onended = () => {
|
||||
gain.disconnect(param);
|
||||
vibratoOscillator.disconnect(gain);
|
||||
};
|
||||
vibratoOscillator.start(t);
|
||||
return vibratoOscillator;
|
||||
}
|
||||
@@ -380,9 +350,9 @@ const mod = (freq, range = 1, type = 'sine') => {
|
||||
}
|
||||
|
||||
osc.start();
|
||||
const g = gainNode(range);
|
||||
const g = new GainNode(ctx, { gain: range });
|
||||
osc.connect(g); // -range, range
|
||||
return { node: g, stop: (t) => osc.stop(t), osc: osc };
|
||||
return { node: g, stop: (t) => osc.stop(t) };
|
||||
};
|
||||
const fm = (frequencyparam, harmonicityRatio, modulationIndex, wave = 'sine') => {
|
||||
const carrfreq = frequencyparam.value;
|
||||
@@ -434,11 +404,6 @@ export function applyFM(param, value, begin) {
|
||||
modulator.connect(envGain);
|
||||
envGain.connect(param);
|
||||
}
|
||||
fmmod.osc.onended = () => {
|
||||
envGain.disconnect();
|
||||
modulator.disconnect();
|
||||
fmmod.osc.disconnect();
|
||||
};
|
||||
}
|
||||
return { stop };
|
||||
}
|
||||
@@ -540,7 +505,7 @@ export const getDistortion = (distort, postgain, algorithm) => {
|
||||
};
|
||||
|
||||
export const getFrequencyFromValue = (value, defaultNote = 36) => {
|
||||
let { note, freq, octave = 0 } = value;
|
||||
let { note, freq } = value;
|
||||
note = note || defaultNote;
|
||||
if (typeof note === 'string') {
|
||||
note = noteToMidi(note); // e.g. c3 => 48
|
||||
@@ -549,7 +514,7 @@ export const getFrequencyFromValue = (value, defaultNote = 36) => {
|
||||
if (!freq && typeof note === 'number') {
|
||||
freq = midiToFreq(note); // + 48);
|
||||
}
|
||||
freq *= Math.pow(2, octave);
|
||||
|
||||
return Number(freq);
|
||||
};
|
||||
|
||||
|
||||
@@ -65,9 +65,8 @@ export function getNoiseOscillator(type = 'white', t, density = 0.02) {
|
||||
export function getNoiseMix(inputNode, wet, t) {
|
||||
const noiseOscillator = getNoiseOscillator('pink', t);
|
||||
const noiseMix = drywet(inputNode, noiseOscillator.node, wet);
|
||||
noiseOscillator.node.onended = () => noiseMix.onended();
|
||||
return {
|
||||
node: noiseMix.node,
|
||||
node: noiseMix,
|
||||
stop: (time) => noiseOscillator?.stop(time),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -104,8 +104,6 @@ var applyGradualLowpass = function (input, lpFreqStart, lpFreqEnd, lpFreqEndAt,
|
||||
player.start();
|
||||
context.oncomplete = function (event) {
|
||||
callback(event.renderedBuffer);
|
||||
filter.disconnect();
|
||||
player.disconnect();
|
||||
};
|
||||
context.startRendering();
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getBaseURL, getCommonSampleInfo } from './util.mjs';
|
||||
import { getCommonSampleInfo } from './util.mjs';
|
||||
import { registerSound, registerWaveTable } from './index.mjs';
|
||||
import { getAudioContext } from './audioContext.mjs';
|
||||
import { getADSRValues, getParamADSR, getPitchEnvelope, getVibratoOscillator } from './helpers.mjs';
|
||||
@@ -211,7 +211,7 @@ export async function fetchSampleMap(url) {
|
||||
// not a browser
|
||||
return;
|
||||
}
|
||||
const base = getBaseURL(url);
|
||||
const base = url.split('/').slice(0, -1).join('/');
|
||||
if (typeof fetch === 'undefined') {
|
||||
// skip fetch when in node / testing
|
||||
return;
|
||||
|
||||
@@ -263,8 +263,8 @@ let audioReady;
|
||||
export async function initAudioOnFirstClick(options) {
|
||||
if (!audioReady) {
|
||||
audioReady = new Promise((resolve) => {
|
||||
document.addEventListener('mousedown', async function listener() {
|
||||
document.removeEventListener('mousedown', listener);
|
||||
document.addEventListener('click', async function listener() {
|
||||
document.removeEventListener('click', listener);
|
||||
await initAudio(options);
|
||||
resolve();
|
||||
});
|
||||
@@ -481,7 +481,7 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5)
|
||||
// oldest audio nodes will be destroyed if maximum polyphony is exceeded
|
||||
for (let i = 0; i <= activeSoundSources.size - maxPolyphony; i++) {
|
||||
const ch = activeSoundSources.entries().next();
|
||||
const source = ch.value[1].deref();
|
||||
const source = ch.value[1];
|
||||
const chainID = ch.value[0];
|
||||
const endTime = t + 0.25;
|
||||
source?.node?.gain?.linearRampToValueAtTime(0, endTime);
|
||||
@@ -513,7 +513,7 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5)
|
||||
|
||||
if (soundHandle) {
|
||||
sourceNode = soundHandle.node;
|
||||
activeSoundSources.set(chainID, new WeakRef(soundHandle)); // allow GC
|
||||
activeSoundSources.set(chainID, soundHandle);
|
||||
}
|
||||
} else {
|
||||
throw new Error(`sound ${s} not found! Is it loaded?`);
|
||||
@@ -553,14 +553,13 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5)
|
||||
rate: 'lprate',
|
||||
sync: 'lpsync',
|
||||
depth: 'lpdepth',
|
||||
depthfrequency: 'lpdepthfrequency',
|
||||
shape: 'lpshape',
|
||||
dcoffset: 'lpdc',
|
||||
skew: 'lpskew',
|
||||
};
|
||||
const lpParams = pickAndRename(value, lpMap);
|
||||
lpParams.type = 'lowpass';
|
||||
let lp = () => createFilter(ac, t, end, lpParams, cps, cycle);
|
||||
let lp = () => createFilter(ac, t, end, lpParams, cps);
|
||||
chain.push(lp());
|
||||
if (ftype === '24db') {
|
||||
chain.push(lp());
|
||||
@@ -582,14 +581,13 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5)
|
||||
rate: 'hprate',
|
||||
sync: 'hpsync',
|
||||
depth: 'hpdepth',
|
||||
depthfrequency: 'hpdepthfrequency',
|
||||
shape: 'hpshape',
|
||||
dcoffset: 'hpdc',
|
||||
skew: 'hpskew',
|
||||
};
|
||||
const hpParams = pickAndRename(value, hpMap);
|
||||
hpParams.type = 'highpass';
|
||||
let hp = () => createFilter(ac, t, end, hpParams, cps, cycle);
|
||||
let hp = () => createFilter(ac, t, end, hpParams, cps);
|
||||
chain.push(hp());
|
||||
if (ftype === '24db') {
|
||||
chain.push(hp());
|
||||
@@ -611,14 +609,13 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5)
|
||||
rate: 'bprate',
|
||||
sync: 'bpsync',
|
||||
depth: 'bpdepth',
|
||||
depthfrequency: 'bpdepthfrequency',
|
||||
shape: 'bpshape',
|
||||
dcoffset: 'bpdc',
|
||||
skew: 'bpskew',
|
||||
};
|
||||
const bpParams = pickAndRename(value, bpMap);
|
||||
bpParams.type = 'bandpass';
|
||||
let bp = () => createFilter(ac, t, end, bpParams, cps, cycle);
|
||||
let bp = () => createFilter(ac, t, end, bpParams, cps);
|
||||
chain.push(bp());
|
||||
if (ftype === '24db') {
|
||||
chain.push(bp());
|
||||
@@ -668,7 +665,6 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5)
|
||||
curve: 1.5,
|
||||
});
|
||||
lfo.connect(amGain.gain);
|
||||
audioNodes.push(lfo);
|
||||
chain.push(amGain);
|
||||
}
|
||||
|
||||
@@ -696,8 +692,7 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5)
|
||||
// delay
|
||||
if (delay > 0 && delaytime > 0 && delayfeedback > 0) {
|
||||
orbitBus.getDelay(delaytime, delayfeedback, t);
|
||||
const send = orbitBus.sendDelay(post, delay);
|
||||
audioNodes.push(send);
|
||||
orbitBus.sendDelay(post, delay);
|
||||
}
|
||||
// reverb
|
||||
if (room > 0) {
|
||||
@@ -713,8 +708,7 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5)
|
||||
roomIR = await loadBuffer(url, ac, ir, 0);
|
||||
}
|
||||
orbitBus.getReverb(roomsize, roomfade, roomlp, roomdim, roomIR, irspeed, irbegin);
|
||||
const send = orbitBus.sendReverb(post, room);
|
||||
audioNodes.push(send);
|
||||
orbitBus.sendReverb(post, room);
|
||||
}
|
||||
|
||||
if (djf != null) {
|
||||
|
||||
@@ -78,11 +78,11 @@ export class Orbit {
|
||||
return this.reverbNode;
|
||||
}
|
||||
sendReverb(node, amount) {
|
||||
return effectSend(node, this.reverbNode, amount);
|
||||
effectSend(node, this.reverbNode, amount);
|
||||
}
|
||||
|
||||
sendDelay(node, amount) {
|
||||
return effectSend(node, this.delayNode, amount);
|
||||
effectSend(node, this.delayNode, amount);
|
||||
}
|
||||
|
||||
duck(t, onsettime = 0, attacktime = 0.1, depth = 1) {
|
||||
|
||||
@@ -15,10 +15,9 @@ import {
|
||||
noises,
|
||||
webAudioTimeout,
|
||||
} from './helpers.mjs';
|
||||
import { logger } from './logger.mjs';
|
||||
import { getNoiseMix, getNoiseOscillator } from './noise.mjs';
|
||||
|
||||
const waveforms = ['triangle', 'square', 'sawtooth', 'sine', 'user'];
|
||||
const waveforms = ['triangle', 'square', 'sawtooth', 'sine'];
|
||||
const waveformAliases = [
|
||||
['tri', 'triangle'],
|
||||
['sqr', 'square'],
|
||||
@@ -48,17 +47,19 @@ export function registerSynthSounds() {
|
||||
[0.001, 0.05, 0.6, 0.01],
|
||||
);
|
||||
|
||||
let sound = getOscillator(s, t, value);
|
||||
let { node: o, stop, triggerRelease } = sound;
|
||||
|
||||
// turn down
|
||||
const g = gainNode(0.3);
|
||||
|
||||
let sound = getOscillator(s, t, value, () => {
|
||||
const { duration } = value;
|
||||
|
||||
o.onended = () => {
|
||||
o.disconnect();
|
||||
g.disconnect();
|
||||
onended();
|
||||
});
|
||||
|
||||
let { node: o, stop, triggerRelease } = sound;
|
||||
|
||||
const { duration } = value;
|
||||
};
|
||||
|
||||
const envGain = gainNode(1);
|
||||
let node = o.connect(g).connect(envGain);
|
||||
@@ -413,13 +414,9 @@ export function registerSynthSounds() {
|
||||
waveformAliases.forEach(([alias, actual]) => soundMap.set({ ...soundMap.get(), [alias]: soundMap.get()[actual] }));
|
||||
}
|
||||
|
||||
const PI2 = 2 * Math.PI;
|
||||
export function waveformN(partials, phases, type) {
|
||||
const isList = typeof partials === 'object';
|
||||
partials = isList ? partials : new Float32Array(partials).fill(1);
|
||||
const len = partials.length;
|
||||
const real = new Float32Array(len + 1);
|
||||
const imag = new Float32Array(len + 1);
|
||||
export function waveformN(partials, type) {
|
||||
const real = new Float32Array(partials + 1);
|
||||
const imag = new Float32Array(partials + 1);
|
||||
const ac = getAudioContext();
|
||||
const osc = ac.createOscillator();
|
||||
|
||||
@@ -427,29 +424,20 @@ export function waveformN(partials, phases, type) {
|
||||
sawtooth: (n) => [0, -1 / n],
|
||||
square: (n) => [0, n % 2 === 0 ? 0 : 1 / n],
|
||||
triangle: (n) => [n % 2 === 0 ? 0 : 1 / (n * n), 0],
|
||||
user: (_n) => [0, 1],
|
||||
};
|
||||
|
||||
if (!terms[type]) {
|
||||
throw new Error(`unknown wave type ${type}`);
|
||||
}
|
||||
|
||||
for (let n = 0; n < len; n++) {
|
||||
const mag = partials[n];
|
||||
const [r, i] = terms[type](n + 1); // we skip n === 0 as this is dc offset
|
||||
const phase = phases?.[n] ?? 0;
|
||||
// Scale by `partials`
|
||||
let R = r * mag;
|
||||
let I = i * mag;
|
||||
// Apply rotation by the phase
|
||||
if (phase !== 0) {
|
||||
const c = Math.cos(PI2 * phase);
|
||||
const s = Math.sin(PI2 * phase);
|
||||
R = c * R - s * I;
|
||||
I = s * R + c * I;
|
||||
}
|
||||
real[n + 1] = R;
|
||||
imag[n + 1] = I;
|
||||
real[0] = 0; // dc offset
|
||||
imag[0] = 0;
|
||||
let n = 1;
|
||||
while (n <= partials) {
|
||||
const [r, i] = terms[type](n);
|
||||
real[n] = r;
|
||||
imag[n] = i;
|
||||
n++;
|
||||
}
|
||||
|
||||
const wave = ac.createPeriodicWave(real, imag);
|
||||
@@ -458,28 +446,21 @@ export function waveformN(partials, phases, type) {
|
||||
}
|
||||
|
||||
// expects one of waveforms as s
|
||||
export function getOscillator(s, t, value, onended) {
|
||||
const { duration, noise = 0 } = value;
|
||||
const partials = value.partials ?? value.n;
|
||||
export function getOscillator(s, t, value) {
|
||||
let { n: partials, duration, noise = 0 } = value;
|
||||
let o;
|
||||
if (s === 'user' && !partials) {
|
||||
logger(
|
||||
`[superdough] Synth 'user' was selected, but partials not specified. Defaulting to triangle. Use pat.partials to setup custom waveform`,
|
||||
);
|
||||
s = 'triangle';
|
||||
}
|
||||
s = s === 'user' && !partials ? 'triangle' : s;
|
||||
// If no partials are given, use stock waveforms
|
||||
if (!partials || partials?.length === 0 || s === 'sine') {
|
||||
if (!partials || s === 'sine') {
|
||||
o = getAudioContext().createOscillator();
|
||||
o.type = s || 'triangle';
|
||||
}
|
||||
// generate custom waveform if partials are given
|
||||
else {
|
||||
o = waveformN(partials, value.phases, s);
|
||||
o = waveformN(partials, s);
|
||||
}
|
||||
// set frequency
|
||||
o.frequency.value = getFrequencyFromValue(value);
|
||||
o.start(t);
|
||||
|
||||
let vibratoOscillator = getVibratoOscillator(o.detune, value, t);
|
||||
|
||||
@@ -492,13 +473,6 @@ export function getOscillator(s, t, value, onended) {
|
||||
noiseMix = getNoiseMix(o, noise, t);
|
||||
}
|
||||
|
||||
o.onended = () => {
|
||||
noiseMix || o.disconnect();
|
||||
noiseMix?.node.disconnect();
|
||||
onended();
|
||||
};
|
||||
o.start(t);
|
||||
|
||||
return {
|
||||
node: noiseMix?.node || o,
|
||||
stop: (time) => {
|
||||
|
||||
@@ -114,13 +114,3 @@ export function getCommonSampleInfo(hapValue, bank) {
|
||||
export const pickAndRename = (source, map) => {
|
||||
return Object.fromEntries(Object.entries(map).map(([newKey, oldKey]) => [newKey, source[oldKey]]));
|
||||
};
|
||||
|
||||
export const getBaseURL = (url) => {
|
||||
try {
|
||||
// For real URLs
|
||||
return new URL('.', new URL(url)).href.replace(/\/$/, ''); // removes trailing slash
|
||||
} catch {
|
||||
// For pseudo URLS
|
||||
return url.split('/').slice(0, -1).join('/');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -45,8 +45,7 @@ if (typeof GainNode !== 'undefined') {
|
||||
throw new Error('vowel: unknown vowel ' + letter);
|
||||
}
|
||||
const { gains, qs, freqs } = vowelFormant[letter];
|
||||
this.makeupGain = ac.createGain();
|
||||
this.audioNodes = [];
|
||||
const makeupGain = ac.createGain();
|
||||
for (let i = 0; i < 5; i++) {
|
||||
const gain = ac.createGain();
|
||||
gain.gain.value = gains[i];
|
||||
@@ -54,25 +53,14 @@ if (typeof GainNode !== 'undefined') {
|
||||
filter.type = 'bandpass';
|
||||
filter.Q.value = qs[i];
|
||||
filter.frequency.value = freqs[i];
|
||||
super.connect(filter);
|
||||
this.connect(filter);
|
||||
filter.connect(gain);
|
||||
this.audioNodes.push(filter);
|
||||
gain.connect(this.makeupGain);
|
||||
this.audioNodes.push(gain);
|
||||
gain.connect(makeupGain);
|
||||
}
|
||||
this.makeupGain.gain.value = 8; // how much makeup gain to add?
|
||||
makeupGain.gain.value = 8; // how much makeup gain to add?
|
||||
this.connect = (target) => makeupGain.connect(target);
|
||||
return this;
|
||||
}
|
||||
connect(target) {
|
||||
this.makeupGain.connect(target);
|
||||
}
|
||||
disconnect() {
|
||||
this.makeupGain.disconnect();
|
||||
this.audioNodes.forEach((n) => n.disconnect());
|
||||
super.disconnect();
|
||||
this.makeupGain = null;
|
||||
this.audioNodes = null;
|
||||
}
|
||||
}
|
||||
|
||||
AudioContext.prototype.createVowelFilter = function (letter) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { getAudioContext, registerSound } from './index.mjs';
|
||||
import { getBaseURL, getCommonSampleInfo } from './util.mjs';
|
||||
import { getCommonSampleInfo } from './util.mjs';
|
||||
import {
|
||||
applyFM,
|
||||
applyParameterModulators,
|
||||
@@ -190,7 +190,6 @@ export const tables = async (url, frameLen, json, options = {}) => {
|
||||
if (url.startsWith('local:')) {
|
||||
url = `http://localhost:5432`;
|
||||
}
|
||||
const base = getBaseURL(url);
|
||||
if (typeof fetch !== 'function') {
|
||||
// not a browser
|
||||
return;
|
||||
@@ -201,7 +200,7 @@ export const tables = async (url, frameLen, json, options = {}) => {
|
||||
}
|
||||
return fetch(url)
|
||||
.then((res) => res.json())
|
||||
.then((json) => _processTables(json, base, frameLen, options))
|
||||
.then((json) => _processTables(json, url, frameLen, options))
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
throw new Error(`error loading "${url}"`);
|
||||
|
||||
+173
-237
@@ -6,57 +6,51 @@ import OLAProcessor from './ola-processor';
|
||||
import FFT from './fft.js';
|
||||
import { getDistortionAlgorithm } from './helpers.mjs';
|
||||
|
||||
const blockSize = 128;
|
||||
const PI = Math.PI;
|
||||
const TWO_PI = 2 * PI;
|
||||
const INVSR = 1 / sampleRate;
|
||||
|
||||
const clamp = (num, min, max) => Math.min(Math.max(num, min), max);
|
||||
const mod = (n, m) => ((n % m) + m) % m;
|
||||
const lerp = (a, b, n) => n * (b - a) + a;
|
||||
const pv = (arr, n) => arr[n] ?? arr[0];
|
||||
const frac = (x) => x - Math.floor(x);
|
||||
const ffloor = (x) => x | 0; // fast floor for non-negative
|
||||
|
||||
// Fast integer ops for non-negative values
|
||||
const ffloor = (x) => x | 0;
|
||||
const fround = (x) => ffloor(x + 0.5);
|
||||
const fceil = (x) => ffloor(x + 1);
|
||||
const ffrac = (x) => x - ffloor(x);
|
||||
|
||||
const fast_tanh = (x) => {
|
||||
const x2 = x ** 2;
|
||||
return (x * (27.0 + x2)) / (27.0 + 9.0 * x2);
|
||||
};
|
||||
|
||||
// Optimized per-voice detuner which precomputes constants
|
||||
const getDetuner = (unison, detune) => {
|
||||
const getUnisonDetune = (unison, detune, voiceIndex) => {
|
||||
if (unison < 2) {
|
||||
return (_voiceIdx) => 0;
|
||||
return 0;
|
||||
}
|
||||
const scale = detune / (unison - 1);
|
||||
const center = detune * 0.5;
|
||||
return (voiceIdx) => voiceIdx * scale - center;
|
||||
return lerp(-detune * 0.5, detune * 0.5, voiceIndex / (unison - 1));
|
||||
};
|
||||
|
||||
const applySemitoneDetuneToFrequency = (frequency, detune) => {
|
||||
return frequency * Math.pow(2, detune / 12);
|
||||
};
|
||||
|
||||
// Restrict phase to the range [0, maxPhase) via wrapping
|
||||
function wrapPhase(phase, maxPhase = 1) {
|
||||
if (phase >= maxPhase) {
|
||||
phase -= maxPhase;
|
||||
} else if (phase < 0) {
|
||||
phase += maxPhase;
|
||||
}
|
||||
return phase;
|
||||
}
|
||||
const blockSize = 128;
|
||||
// Smooth waveshape near discontinuities to remove frequencies above Nyquist and prevent aliasing
|
||||
// referenced from https://www.kvraudio.com/forum/viewtopic.php?t=375517
|
||||
function polyBlep(phase, dt) {
|
||||
dt = Math.min(dt, 1 - dt);
|
||||
const invdt = 1 / dt;
|
||||
// Start of cycle
|
||||
if (phase < dt) {
|
||||
phase *= invdt;
|
||||
return 2 * phase - phase ** 2 - 1;
|
||||
phase /= dt;
|
||||
// 2 * (phase - phase^2/2 - 0.5)
|
||||
return phase + phase - phase * phase - 1;
|
||||
}
|
||||
|
||||
// End of cycle
|
||||
else if (phase > 1 - dt) {
|
||||
phase = (phase - 1) * invdt;
|
||||
return phase ** 2 + 2 * phase + 1;
|
||||
phase = (phase - 1) / dt;
|
||||
// 2 * (phase^2/2 + phase + 0.5)
|
||||
return phase * phase + phase + phase + 1;
|
||||
}
|
||||
|
||||
// 0 otherwise
|
||||
else {
|
||||
return 0;
|
||||
@@ -72,7 +66,7 @@ const waveshapes = {
|
||||
return phase / skew;
|
||||
},
|
||||
sine(phase) {
|
||||
return Math.sin(TWO_PI * phase) * 0.5 + 0.5;
|
||||
return Math.sin(Math.PI * 2 * phase) * 0.5 + 0.5;
|
||||
},
|
||||
ramp(phase) {
|
||||
return phase;
|
||||
@@ -106,6 +100,12 @@ const waveshapes = {
|
||||
return v - polyBlep(phase, dt);
|
||||
},
|
||||
};
|
||||
function getParamValue(block, param) {
|
||||
if (param.length > 1) {
|
||||
return param[block];
|
||||
}
|
||||
return param[0];
|
||||
}
|
||||
|
||||
const waveShapeNames = Object.keys(waveshapes);
|
||||
class LFOProcessor extends AudioWorkletProcessor {
|
||||
@@ -165,9 +165,9 @@ class LFOProcessor extends AudioWorkletProcessor {
|
||||
const blockSize = output[0].length ?? 0;
|
||||
|
||||
if (this.phase == null) {
|
||||
this.phase = ffrac(time * frequency + phaseoffset);
|
||||
this.phase = mod(time * frequency + phaseoffset, 1);
|
||||
}
|
||||
const dt = frequency * INVSR;
|
||||
const dt = frequency / sampleRate;
|
||||
for (let n = 0; n < blockSize; n++) {
|
||||
for (let i = 0; i < output.length; i++) {
|
||||
let modval = (waveshapes[shape](this.phase, skew) + dcoffset) * depth;
|
||||
@@ -293,8 +293,8 @@ class TwoPoleFilter {
|
||||
// Out of bound values can produce NaNs
|
||||
resonance = clamp(resonance, 0, 1);
|
||||
cutoff = clamp(cutoff, 0, sampleRate / 2 - 1);
|
||||
const c = clamp(2 * Math.sin(cutoff * PI * INVSR), 0, 1.14);
|
||||
const r = Math.pow(0.5, 8 * resonance + 1);
|
||||
const c = clamp(2 * Math.sin(cutoff * (_PI / sampleRate)), 0, 1.14);
|
||||
const r = Math.pow(0.5, (resonance + 0.125) / 0.125);
|
||||
const mrc = 1 - r * c;
|
||||
this.s0 = mrc * this.s0 - c * this.s1 + c * s; // bpf
|
||||
this.s1 = mrc * this.s1 + c * this.s0; // lpf
|
||||
@@ -353,6 +353,11 @@ class DJFProcessor extends AudioWorkletProcessor {
|
||||
}
|
||||
registerProcessor('djf-processor', DJFProcessor);
|
||||
|
||||
function fast_tanh(x) {
|
||||
const x2 = x * x;
|
||||
return (x * (27.0 + x2)) / (27.0 + 9.0 * x2);
|
||||
}
|
||||
const _PI = 3.14159265359;
|
||||
//adapted from https://github.com/TheBouteillacBear/webaudioworklet-wasm?tab=MIT-1-ov-file
|
||||
class LadderProcessor extends AudioWorkletProcessor {
|
||||
static get parameterDescriptors() {
|
||||
@@ -390,7 +395,7 @@ class LadderProcessor extends AudioWorkletProcessor {
|
||||
const drive = clamp(Math.exp(parameters.drive[0]), 0.1, 2000);
|
||||
|
||||
let cutoff = parameters.frequency[0];
|
||||
cutoff = cutoff * TWO_PI * INVSR;
|
||||
cutoff = (cutoff * 2 * _PI) / sampleRate;
|
||||
cutoff = cutoff > 1 ? 1 : cutoff;
|
||||
|
||||
const k = Math.min(8, resonance * 0.13);
|
||||
@@ -503,7 +508,6 @@ class SuperSawOscillatorProcessor extends AudioWorkletProcessor {
|
||||
name: 'voices',
|
||||
defaultValue: 5,
|
||||
min: 1,
|
||||
automationRate: 'k-rate',
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -515,37 +519,40 @@ class SuperSawOscillatorProcessor extends AudioWorkletProcessor {
|
||||
// this.port.postMessage({ type: 'onended' });
|
||||
return false;
|
||||
}
|
||||
|
||||
const output = outputs[0];
|
||||
const voices = params.voices[0]; // k-rate
|
||||
|
||||
for (let i = 0; i < output[0].length; i++) {
|
||||
const detune = pv(params.detune, i);
|
||||
const voices = pv(params.voices, i);
|
||||
const freqspread = pv(params.freqspread, i);
|
||||
const panspread = pv(params.panspread, i) * 0.5 + 0.5;
|
||||
let gainL = Math.sqrt(1 - panspread);
|
||||
let gainR = Math.sqrt(panspread);
|
||||
const gain1 = Math.sqrt(1 - panspread);
|
||||
const gain2 = Math.sqrt(panspread);
|
||||
let freq = pv(params.frequency, i);
|
||||
// Main detuning
|
||||
freq = applySemitoneDetuneToFrequency(freq, detune / 100);
|
||||
const detuner = getDetuner(voices, freqspread);
|
||||
for (let n = 0; n < voices; n++) {
|
||||
const isOdd = (n & 1) == 1;
|
||||
let gainL = gain1;
|
||||
let gainR = gain2;
|
||||
// invert right and left gain
|
||||
if (isOdd) {
|
||||
gainL = gain2;
|
||||
gainR = gain1;
|
||||
}
|
||||
// Individual voice detuning
|
||||
const freqVoice = applySemitoneDetuneToFrequency(freq, detuner(n));
|
||||
const freqVoice = applySemitoneDetuneToFrequency(freq, getUnisonDetune(voices, freqspread, n));
|
||||
// We must wrap this here because it is passed into sawblep below which
|
||||
// has domain [0, 1]
|
||||
const dt = frac(freqVoice * INVSR);
|
||||
const dt = mod(freqVoice / sampleRate, 1);
|
||||
this.phase[n] = this.phase[n] ?? Math.random();
|
||||
const v = waveshapes.sawblep(this.phase[n], dt);
|
||||
|
||||
output[0][i] += v * gainL;
|
||||
output[1][i] += v * gainR;
|
||||
output[0][i] = output[0][i] + v * gainL;
|
||||
output[1][i] = output[1][i] + v * gainR;
|
||||
|
||||
let pn = this.phase[n] + dt;
|
||||
if (pn >= 1.0) pn -= 1.0;
|
||||
this.phase[n] = pn;
|
||||
// invert right and left gain
|
||||
const tmp = gainL;
|
||||
gainL = gainR;
|
||||
gainR = tmp;
|
||||
this.phase[n] = wrapPhase(this.phase[n] + dt);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@@ -557,16 +564,12 @@ registerProcessor('supersaw-oscillator', SuperSawOscillatorProcessor);
|
||||
// Phase Vocoder sourced from https://github.com/olvb/phaze/tree/master?tab=readme-ov-file
|
||||
const BUFFERED_BLOCK_SIZE = 2048;
|
||||
|
||||
const hannCache = new Map();
|
||||
function genHannWindow(length) {
|
||||
if (!hannCache.has(length)) {
|
||||
const win = new Float32Array(length);
|
||||
for (let i = 0; i < length; i++) {
|
||||
win[i] = 0.5 * (1 - Math.cos((TWO_PI * i) / length));
|
||||
}
|
||||
hannCache.set(length, win);
|
||||
let win = new Float32Array(length);
|
||||
for (var i = 0; i < length; i++) {
|
||||
win[i] = 0.5 * (1 - Math.cos((2 * Math.PI * i) / length));
|
||||
}
|
||||
return hannCache.get(length);
|
||||
return win;
|
||||
}
|
||||
|
||||
class PhaseVocoderProcessor extends OLAProcessor {
|
||||
@@ -584,10 +587,11 @@ class PhaseVocoderProcessor extends OLAProcessor {
|
||||
blockSize: BUFFERED_BLOCK_SIZE,
|
||||
};
|
||||
super(options);
|
||||
this.timeCursor = 0;
|
||||
|
||||
this.fftSize = this.blockSize;
|
||||
this.invfftSize = 1 / this.fftSize;
|
||||
this.hannWindow = genHannWindow(this.fftSize);
|
||||
this.timeCursor = 0;
|
||||
|
||||
this.hannWindow = genHannWindow(this.blockSize);
|
||||
// prepare FFT and pre-allocate buffers
|
||||
this.fft = new FFT(this.fftSize);
|
||||
this.freqComplexBuffer = this.fft.createComplexArray();
|
||||
@@ -600,43 +604,52 @@ class PhaseVocoderProcessor extends OLAProcessor {
|
||||
|
||||
processOLA(inputs, outputs, parameters) {
|
||||
// no automation, take last value
|
||||
|
||||
let pitchFactor = parameters.pitchFactor[parameters.pitchFactor.length - 1];
|
||||
|
||||
if (pitchFactor < 0) {
|
||||
pitchFactor = pitchFactor * 0.25;
|
||||
}
|
||||
pitchFactor = Math.max(0, pitchFactor + 1);
|
||||
for (let i = 0; i < this.nbInputs; i++) {
|
||||
for (let j = 0; j < inputs[i].length; j++) {
|
||||
const input = inputs[i][j];
|
||||
const output = outputs[i][j];
|
||||
|
||||
for (var i = 0; i < this.nbInputs; i++) {
|
||||
for (var j = 0; j < inputs[i].length; j++) {
|
||||
// big assumption here: output is symetric to input
|
||||
var input = inputs[i][j];
|
||||
var output = outputs[i][j];
|
||||
|
||||
this.applyHannWindow(input);
|
||||
|
||||
this.fft.realTransform(this.freqComplexBuffer, input);
|
||||
|
||||
this.computeMagnitudes();
|
||||
this.findPeaks();
|
||||
this.shiftPeaks(pitchFactor);
|
||||
|
||||
this.fft.completeSpectrum(this.freqComplexBufferShifted);
|
||||
this.fft.inverseTransform(this.timeComplexBuffer, this.freqComplexBufferShifted);
|
||||
this.fft.fromComplexArray(this.timeComplexBuffer, output);
|
||||
this.applyHannWindow(output);
|
||||
}
|
||||
}
|
||||
|
||||
this.timeCursor += this.hopSize;
|
||||
}
|
||||
|
||||
/** Apply Hann window in-place */
|
||||
applyHannWindow(input) {
|
||||
for (let i = 0; i < this.blockSize; i++) {
|
||||
input[i] *= this.hannWindow[i] * 1.62;
|
||||
for (var i = 0; i < this.blockSize; i++) {
|
||||
input[i] = input[i] * this.hannWindow[i] * 1.62;
|
||||
}
|
||||
}
|
||||
|
||||
/** Compute squared magnitudes for peak finding **/
|
||||
computeMagnitudes() {
|
||||
let i = 0,
|
||||
var i = 0,
|
||||
j = 0;
|
||||
while (i < this.magnitudes.length) {
|
||||
const real = this.freqComplexBuffer[j];
|
||||
const imag = this.freqComplexBuffer[j + 1];
|
||||
let real = this.freqComplexBuffer[j];
|
||||
let imag = this.freqComplexBuffer[j + 1];
|
||||
// no need to sqrt for peak finding
|
||||
this.magnitudes[i] = real ** 2 + imag ** 2;
|
||||
i += 1;
|
||||
@@ -647,10 +660,12 @@ class PhaseVocoderProcessor extends OLAProcessor {
|
||||
/** Find peaks in spectrum magnitudes **/
|
||||
findPeaks() {
|
||||
this.nbPeaks = 0;
|
||||
let i = 2;
|
||||
const end = this.magnitudes.length - 2;
|
||||
var i = 2;
|
||||
let end = this.magnitudes.length - 2;
|
||||
|
||||
while (i < end) {
|
||||
const mag = this.magnitudes[i];
|
||||
let mag = this.magnitudes[i];
|
||||
|
||||
if (this.magnitudes[i - 1] >= mag || this.magnitudes[i - 2] >= mag) {
|
||||
i++;
|
||||
continue;
|
||||
@@ -659,6 +674,7 @@ class PhaseVocoderProcessor extends OLAProcessor {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
this.peakIndexes[this.nbPeaks] = i;
|
||||
this.nbPeaks++;
|
||||
i += 2;
|
||||
@@ -669,44 +685,53 @@ class PhaseVocoderProcessor extends OLAProcessor {
|
||||
shiftPeaks(pitchFactor) {
|
||||
// zero-fill new spectrum
|
||||
this.freqComplexBufferShifted.fill(0);
|
||||
for (let i = 0; i < this.nbPeaks; i++) {
|
||||
const peakIndex = this.peakIndexes[i];
|
||||
const peakIndexShifted = fround(peakIndex * pitchFactor);
|
||||
|
||||
for (var i = 0; i < this.nbPeaks; i++) {
|
||||
let peakIndex = this.peakIndexes[i];
|
||||
let peakIndexShifted = Math.round(peakIndex * pitchFactor);
|
||||
|
||||
if (peakIndexShifted > this.magnitudes.length) {
|
||||
break;
|
||||
}
|
||||
|
||||
// find region of influence
|
||||
let startIndex = 0;
|
||||
let endIndex = this.fftSize;
|
||||
var startIndex = 0;
|
||||
var endIndex = this.fftSize;
|
||||
if (i > 0) {
|
||||
startIndex = peakIndex - fround((peakIndex - this.peakIndexes[i - 1]) / 2);
|
||||
let peakIndexBefore = this.peakIndexes[i - 1];
|
||||
startIndex = peakIndex - Math.floor((peakIndex - peakIndexBefore) / 2);
|
||||
}
|
||||
if (i < this.nbPeaks - 1) {
|
||||
endIndex = peakIndex + fceil((this.peakIndexes[i + 1] - peakIndex) / 2);
|
||||
let peakIndexAfter = this.peakIndexes[i + 1];
|
||||
endIndex = peakIndex + Math.ceil((peakIndexAfter - peakIndex) / 2);
|
||||
}
|
||||
|
||||
// shift whole region of influence around peak to shifted peak
|
||||
const startOffset = startIndex - peakIndex;
|
||||
const endOffset = endIndex - peakIndex;
|
||||
const omegaDelta = TWO_PI * this.invfftSize * (peakIndexShifted - peakIndex);
|
||||
const phaseShiftReal = Math.cos(omegaDelta * this.timeCursor);
|
||||
const phaseShiftImag = Math.sin(omegaDelta * this.timeCursor);
|
||||
for (let j = startOffset; j < endOffset; j++) {
|
||||
const binIndex = peakIndex + j;
|
||||
const binIndexShifted = peakIndexShifted + j;
|
||||
let startOffset = startIndex - peakIndex;
|
||||
let endOffset = endIndex - peakIndex;
|
||||
for (var j = startOffset; j < endOffset; j++) {
|
||||
let binIndex = peakIndex + j;
|
||||
let binIndexShifted = peakIndexShifted + j;
|
||||
|
||||
if (binIndexShifted >= this.magnitudes.length) {
|
||||
break;
|
||||
}
|
||||
|
||||
// apply phase correction
|
||||
const indexReal = 2 * binIndex;
|
||||
const indexImag = indexReal + 1;
|
||||
const valueReal = this.freqComplexBuffer[indexReal];
|
||||
const valueImag = this.freqComplexBuffer[indexImag];
|
||||
let omegaDelta = (2 * Math.PI * (binIndexShifted - binIndex)) / this.fftSize;
|
||||
let phaseShiftReal = Math.cos(omegaDelta * this.timeCursor);
|
||||
let phaseShiftImag = Math.sin(omegaDelta * this.timeCursor);
|
||||
|
||||
const valueShiftedReal = valueReal * phaseShiftReal - valueImag * phaseShiftImag;
|
||||
const valueShiftedImag = valueReal * phaseShiftImag + valueImag * phaseShiftReal;
|
||||
let indexReal = binIndex * 2;
|
||||
let indexImag = indexReal + 1;
|
||||
let valueReal = this.freqComplexBuffer[indexReal];
|
||||
let valueImag = this.freqComplexBuffer[indexImag];
|
||||
|
||||
const indexShiftedReal = 2 * binIndexShifted;
|
||||
const indexShiftedImag = indexShiftedReal + 1;
|
||||
let valueShiftedReal = valueReal * phaseShiftReal - valueImag * phaseShiftImag;
|
||||
let valueShiftedImag = valueReal * phaseShiftImag + valueImag * phaseShiftReal;
|
||||
|
||||
let indexShiftedReal = binIndexShifted * 2;
|
||||
let indexShiftedImag = indexShiftedReal + 1;
|
||||
this.freqComplexBufferShifted[indexShiftedReal] += valueShiftedReal;
|
||||
this.freqComplexBufferShifted[indexShiftedImag] += valueShiftedImag;
|
||||
}
|
||||
@@ -720,10 +745,11 @@ registerProcessor('phase-vocoder-processor', PhaseVocoderProcessor);
|
||||
class PulseOscillatorProcessor extends AudioWorkletProcessor {
|
||||
constructor() {
|
||||
super();
|
||||
this.phi = -PI; // phase
|
||||
this.pi = _PI;
|
||||
this.phi = -this.pi; // phase
|
||||
this.Y0 = 0; // feedback memories
|
||||
this.Y1 = 0;
|
||||
this.PW = PI; // pulse width
|
||||
this.PW = this.pi; // pulse width
|
||||
this.B = 2.3; // feedback coefficient
|
||||
this.dphif = 0; // filtered phase increment
|
||||
this.envf = 0; // filtered envelope
|
||||
@@ -780,11 +806,11 @@ class PulseOscillatorProcessor extends AudioWorkletProcessor {
|
||||
dphi;
|
||||
|
||||
for (let i = 0; i < (output[0].length ?? 0); i++) {
|
||||
const pw = (1 - clamp(pv(params.pulsewidth, i), -0.99, 0.99)) * PI;
|
||||
const detune = pv(params.detune, i);
|
||||
const freq = applySemitoneDetuneToFrequency(pv(params.frequency, i), detune / 100);
|
||||
const pw = (1 - clamp(getParamValue(i, params.pulsewidth), -0.99, 0.99)) * this.pi;
|
||||
const detune = getParamValue(i, params.detune);
|
||||
const freq = applySemitoneDetuneToFrequency(getParamValue(i, params.frequency), detune / 100);
|
||||
|
||||
dphi = freq * TWO_PI * INVSR; // phase increment
|
||||
dphi = freq * (this.pi / (sampleRate * 0.5)); // phase increment
|
||||
this.dphif += 0.1 * (dphi - this.dphif);
|
||||
|
||||
env *= 0.9998; // exponential decay envelope
|
||||
@@ -796,7 +822,7 @@ class PulseOscillatorProcessor extends AudioWorkletProcessor {
|
||||
|
||||
// Waveform generation (half-Tomisawa oscillators)
|
||||
this.phi += this.dphif; // phase increment
|
||||
if (this.phi >= PI) this.phi -= TWO_PI; // phase wrapping
|
||||
if (this.phi >= this.pi) this.phi -= 2 * this.pi; // phase wrapping
|
||||
|
||||
// First half-Tomisawa generator
|
||||
let out0 = Math.cos(this.phi + this.B * this.Y0); // self-phase modulation
|
||||
@@ -826,23 +852,24 @@ const chyx = {
|
||||
/*bit reverse*/ br: function (x, size = 8) {
|
||||
if (size > 32) {
|
||||
throw new Error('br() Size cannot be greater than 32');
|
||||
} else {
|
||||
let result = 0;
|
||||
for (let idx = 0; idx < size - 0; idx++) {
|
||||
result += chyx.bitC(x, 2 ** idx, 2 ** (size - (idx + 1)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
let result = 0;
|
||||
for (let idx = 0; idx < size; idx++) {
|
||||
result |= chyx.bitC(x, 1 << idx, 1 << (size - (idx + 1)));
|
||||
}
|
||||
return result;
|
||||
},
|
||||
/*sin that loops every 128 "steps", instead of every pi steps*/ sinf: function (x) {
|
||||
return Math.sin((x * PI) / 128);
|
||||
return Math.sin(x / (128 / Math.PI));
|
||||
},
|
||||
/*cos that loops every 128 "steps", instead of every pi steps*/ cosf: function (x) {
|
||||
return Math.cos((x * PI) / 128);
|
||||
return Math.cos(x / (128 / Math.PI));
|
||||
},
|
||||
/*tan that loops every 128 "steps", instead of every pi steps*/ tanf: function (x) {
|
||||
return Math.tan((x * PI) / 128);
|
||||
return Math.tan(x / (128 / Math.PI));
|
||||
},
|
||||
/*converts t into a string composed of its bits; regexes that*/ regG: function (t, X) {
|
||||
/*converts t into a string composed of it's bits, regex's that*/ regG: function (t, X) {
|
||||
return X.test(t.toString(2));
|
||||
},
|
||||
};
|
||||
@@ -850,7 +877,7 @@ const chyx = {
|
||||
// Create shortened Math functions
|
||||
let mathParams, byteBeatHelperFuncs;
|
||||
function getByteBeatFunc(codetext) {
|
||||
if (mathParams == null) {
|
||||
if ((mathParams || byteBeatHelperFuncs) == null) {
|
||||
mathParams = Object.getOwnPropertyNames(Math);
|
||||
byteBeatHelperFuncs = mathParams.map((k) => Math[k]);
|
||||
const chyxNames = Object.getOwnPropertyNames(chyx);
|
||||
@@ -883,7 +910,7 @@ class ByteBeatProcessor extends AudioWorkletProcessor {
|
||||
|
||||
this.func = getByteBeatFunc(codeText);
|
||||
};
|
||||
this.initialOffset = 0;
|
||||
this.initialOffset = null;
|
||||
this.t = null;
|
||||
this.func = null;
|
||||
}
|
||||
@@ -930,19 +957,18 @@ class ByteBeatProcessor extends AudioWorkletProcessor {
|
||||
this.t = params.begin[0] * sampleRate;
|
||||
}
|
||||
const output = outputs[0];
|
||||
const scale = 256 * INVSR;
|
||||
for (let i = 0; i < output[0].length; i++) {
|
||||
const detune = pv(params.detune, i);
|
||||
const freq = applySemitoneDetuneToFrequency(pv(params.frequency, i), detune / 100);
|
||||
const local_t = scale * freq * this.t + this.initialOffset;
|
||||
const detune = getParamValue(i, params.detune);
|
||||
const freq = applySemitoneDetuneToFrequency(getParamValue(i, params.frequency), detune / 100);
|
||||
let local_t = (this.t / (sampleRate / 256)) * freq + this.initialOffset;
|
||||
const funcValue = this.func(local_t);
|
||||
const signal = (funcValue & 255) / 127.5 - 1;
|
||||
//prevent speaker blowout via clipping if threshold exceeds
|
||||
const out = clamp(signal * 0.2, -0.4, 0.4);
|
||||
let signal = (funcValue & 255) / 127.5 - 1;
|
||||
const out = signal * 0.2;
|
||||
for (let c = 0; c < output.length; c++) {
|
||||
output[c][i] = out;
|
||||
//prevent speaker blowout via clipping if threshold exceeds
|
||||
output[c][i] = clamp(out, -0.4, 0.4);
|
||||
}
|
||||
this.t++;
|
||||
this.t = this.t + 1;
|
||||
}
|
||||
|
||||
return true; // keep the audio processing going
|
||||
@@ -951,102 +977,6 @@ class ByteBeatProcessor extends AudioWorkletProcessor {
|
||||
|
||||
registerProcessor('byte-beat-processor', ByteBeatProcessor);
|
||||
|
||||
class EnvelopeProcessor extends AudioWorkletProcessor {
|
||||
static get parameterDescriptors() {
|
||||
return [
|
||||
{ name: 'begin', defaultValue: 0 },
|
||||
{ name: 'end', defaultValue: 0 },
|
||||
{ name: 'attack', defaultValue: 0.005, minValue: 0 },
|
||||
{ name: 'decay', defaultValue: 0.14, minValue: 0 },
|
||||
{ name: 'sustain', defaultValue: 0, minValue: 0, maxValue: 1 },
|
||||
{ name: 'release', defaultValue: 0.1, minValue: 0 },
|
||||
{ name: 'attackCurve', defaultValue: 0, minValue: -1, maxValue: 1 },
|
||||
{ name: 'decayCurve', defaultValue: 0, minValue: -1, maxValue: 1 },
|
||||
{ name: 'releaseCurve', defaultValue: 0, minValue: -1, maxValue: 1 },
|
||||
{ name: 'peak', defaultValue: 1 },
|
||||
{ name: 'retrigger', defaultValue: 1, minValue: 0, maxValue: 1 },
|
||||
];
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.val = 0;
|
||||
this.segIdx = 0;
|
||||
this.state = 0;
|
||||
this.beginTime = 0;
|
||||
this.endTime = 0;
|
||||
this.attackStart = 0;
|
||||
}
|
||||
|
||||
_warp(phase, curvature, strength = 8) {
|
||||
if (phase === 0 || phase === 1) return phase; // fast exit
|
||||
if (curvature > 0) {
|
||||
// snappier
|
||||
const exp = 1 + strength * curvature;
|
||||
return 1 - Math.pow(1 - phase, exp);
|
||||
} else {
|
||||
// more calm
|
||||
const exp = 1 - strength * curvature;
|
||||
return Math.pow(phase, exp);
|
||||
}
|
||||
}
|
||||
|
||||
_advance(start, target, time, curvature) {
|
||||
if (time === 0 || start === target) {
|
||||
this.val = target;
|
||||
} else {
|
||||
// We compute our progress through this section of the envelope in time
|
||||
// as a `phase` value, which is warped by the curvature, and then used
|
||||
// to compute the value of the envelope at that time
|
||||
const phase = Math.min(1, (currentTime - this.beginTime) / time);
|
||||
const phaseWarped = this._warp(phase, curvature);
|
||||
this.val = start + (target - start) * phaseWarped;
|
||||
}
|
||||
}
|
||||
|
||||
process(_inputs, outputs, params) {
|
||||
const out = outputs[0][0];
|
||||
if (!out) return true;
|
||||
const begin = pv(params.begin, 0);
|
||||
const retrigger = pv(params.retrigger, 0) >= 0.5; // convert to bool
|
||||
if (begin !== this.beginTime && (this.state === 0 || retrigger)) {
|
||||
// triggered
|
||||
this.beginTime = begin;
|
||||
this.state = 1;
|
||||
this.endTime = pv(params.end, 0);
|
||||
this.attackStart = this.val;
|
||||
}
|
||||
const susTime = this.endTime - this.beginTime;
|
||||
for (let i = 0; i < out.length; i++) {
|
||||
const attack = pv(params.attack, i);
|
||||
const decay = pv(params.decay, i);
|
||||
const sustain = pv(params.sustain, i);
|
||||
const release = pv(params.release, i);
|
||||
const aCurve = pv(params.attackCurve, i);
|
||||
const dCurve = pv(params.decayCurve, i);
|
||||
const rCurve = pv(params.releaseCurve, i);
|
||||
const peak = pv(params.peak, i);
|
||||
const states = [
|
||||
{ time: Number.POSITIVE_INFINITY, start: 0, target: 0 }, // idle
|
||||
{ time: attack, start: this.attackStart, target: 1, curve: aCurve },
|
||||
{ time: attack + decay, start: 1, target: sustain, curve: dCurve },
|
||||
{ time: susTime, start: sustain, target: sustain },
|
||||
{ time: susTime + release, start: sustain, target: 0, curve: rCurve },
|
||||
];
|
||||
let { time, start, target, curve } = states[this.state];
|
||||
this._advance(start, target, time, curve);
|
||||
while (currentTime - this.beginTime >= time) {
|
||||
this.state = (this.state + 1) % states.length;
|
||||
time = states[this.state].time;
|
||||
}
|
||||
out[i] = this.val * peak;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
registerProcessor('envelope-processor', EnvelopeProcessor);
|
||||
|
||||
export const WarpMode = Object.freeze({
|
||||
NONE: 0,
|
||||
ASYM: 1,
|
||||
@@ -1126,7 +1056,7 @@ class WavetableOscillatorProcessor extends AudioWorkletProcessor {
|
||||
{ name: 'position', defaultValue: 0, min: 0, max: 1 },
|
||||
{ name: 'warp', defaultValue: 0, min: 0, max: 1 },
|
||||
{ name: 'warpMode', defaultValue: 0 },
|
||||
{ name: 'voices', defaultValue: 1, min: 1, automationRate: 'k-rate' },
|
||||
{ name: 'voices', defaultValue: 1, min: 1 },
|
||||
{ name: 'panspread', defaultValue: 0.7, min: 0, max: 1 },
|
||||
{ name: 'phaserand', defaultValue: 0, min: 0, max: 1 },
|
||||
];
|
||||
@@ -1137,6 +1067,7 @@ class WavetableOscillatorProcessor extends AudioWorkletProcessor {
|
||||
this.frameLen = 0;
|
||||
this.numFrames = 0;
|
||||
this.phase = [];
|
||||
this.invSR = 1 / sampleRate;
|
||||
|
||||
this.port.onmessage = (e) => {
|
||||
const { type, payload } = e.data || {};
|
||||
@@ -1173,7 +1104,7 @@ class WavetableOscillatorProcessor extends AudioWorkletProcessor {
|
||||
|
||||
_toBits(amt, min = 2, max = 12) {
|
||||
const b = max + (min - max) * amt;
|
||||
return { b, n: fround(Math.pow(2, b)) };
|
||||
return { b, n: Math.round(Math.pow(2, b)) };
|
||||
}
|
||||
|
||||
_warpPhase(phase, amt, mode) {
|
||||
@@ -1199,7 +1130,7 @@ class WavetableOscillatorProcessor extends AudioWorkletProcessor {
|
||||
return amt < 0.5 ? this._warpPhase(phase, 1 - 2 * amt, 3) : this._warpPhase(phase, 2 * amt - 1, 2);
|
||||
}
|
||||
case WarpMode.SYNC: {
|
||||
const syncRatio = Math.pow(16, amt ** 2);
|
||||
const syncRatio = Math.pow(16, amt * amt);
|
||||
return (phase * syncRatio) % 1;
|
||||
}
|
||||
case WarpMode.QUANT: {
|
||||
@@ -1208,8 +1139,8 @@ class WavetableOscillatorProcessor extends AudioWorkletProcessor {
|
||||
}
|
||||
case WarpMode.FOLD: {
|
||||
const K = 7;
|
||||
const k = 1 + Math.max(1, fround(K * amt));
|
||||
return Math.abs(ffrac(k * phase) - 0.5) * 2;
|
||||
const k = 1 + Math.max(1, Math.round(K * amt));
|
||||
return Math.abs(frac(k * phase) - 0.5) * 2;
|
||||
}
|
||||
case WarpMode.PWM: {
|
||||
const w = clamp(0.5 + 0.49 * (2 * amt - 1), 0, 1);
|
||||
@@ -1219,12 +1150,12 @@ class WavetableOscillatorProcessor extends AudioWorkletProcessor {
|
||||
case WarpMode.ORBIT: {
|
||||
const depth = 0.5 * amt;
|
||||
const n = 3;
|
||||
return frac(phase + depth * Math.sin(TWO_PI * n * phase));
|
||||
return frac(phase + depth * Math.sin(2 * Math.PI * n * phase));
|
||||
}
|
||||
case WarpMode.SPIN: {
|
||||
const depth = 0.5 * amt;
|
||||
const { n } = this._toBits(amt, 1, 6);
|
||||
return frac(phase + depth * Math.sin(TWO_PI * n * phase));
|
||||
return frac(phase + depth * Math.sin(2 * Math.PI * n * phase));
|
||||
}
|
||||
case WarpMode.CHAOS: {
|
||||
const r = 3.7 + 0.3 * amt;
|
||||
@@ -1235,7 +1166,7 @@ class WavetableOscillatorProcessor extends AudioWorkletProcessor {
|
||||
const isPrime = (n) => {
|
||||
if (n < 2) return false;
|
||||
if (n % 2 === 0) return n === 2;
|
||||
for (let d = 3; d ** 2 <= n; d += 2) if (n % d === 0) return false;
|
||||
for (let d = 3; d * d <= n; d += 2) if (n % d === 0) return false;
|
||||
return true;
|
||||
};
|
||||
let { n } = this._toBits(amt, 3);
|
||||
@@ -1244,12 +1175,18 @@ class WavetableOscillatorProcessor extends AudioWorkletProcessor {
|
||||
}
|
||||
case WarpMode.BINARY: {
|
||||
let { b } = this._toBits(amt, 3);
|
||||
b = fround(b);
|
||||
b = Math.round(b);
|
||||
const n = 1 << b;
|
||||
const idx = ffloor(phase * n);
|
||||
const ridx = bitReverse(idx, b);
|
||||
return ridx / n;
|
||||
}
|
||||
case WarpMode.MODULAR: {
|
||||
const { n } = this._toBits(amt);
|
||||
const depth = 0.5 * amt;
|
||||
const jump = frac(phase * n) / n;
|
||||
return frac(phase + depth * jump);
|
||||
}
|
||||
case WarpMode.BROWNIAN: {
|
||||
const disp = 0.25 * amt * brownian(64 * phase, 4);
|
||||
return frac(phase + disp);
|
||||
@@ -1272,7 +1209,7 @@ class WavetableOscillatorProcessor extends AudioWorkletProcessor {
|
||||
case WarpMode.LOGISTIC: {
|
||||
let x = phase;
|
||||
const r = 3.6 + 0.4 * amt;
|
||||
const iters = 1 + fround(2 * amt);
|
||||
const iters = 1 + Math.round(2 * amt);
|
||||
for (let i = 0; i < iters; i++) x = r * x * (1 - x);
|
||||
return clamp(x, 0, 1);
|
||||
}
|
||||
@@ -1285,7 +1222,7 @@ class WavetableOscillatorProcessor extends AudioWorkletProcessor {
|
||||
return (y - y0) / (y1 - y0);
|
||||
}
|
||||
case WarpMode.FRACTAL: {
|
||||
const d = 0.5 * Math.sin(TWO_PI * phase) * amt;
|
||||
const d = 0.5 * Math.sin(2 * Math.PI * phase) * amt;
|
||||
return frac(phase + d);
|
||||
}
|
||||
case WarpMode.FLIP: {
|
||||
@@ -1332,16 +1269,16 @@ class WavetableOscillatorProcessor extends AudioWorkletProcessor {
|
||||
if (outR !== outL) outR.set(outL);
|
||||
return true;
|
||||
}
|
||||
const voices = parameters.voices[0]; // k-rate
|
||||
for (let i = 0; i < outL.length; i++) {
|
||||
const detune = pv(parameters.detune, i);
|
||||
const freqspread = pv(parameters.freqspread, i);
|
||||
const tablePos = clamp(pv(parameters.position, i), 0, 1);
|
||||
const idx = tablePos * (this.numFrames - 1);
|
||||
const fIdx = idx | 0;
|
||||
const interpT = idx - fIdx;
|
||||
const frac = idx - fIdx;
|
||||
const warpAmount = clamp(pv(parameters.warp, i), 0, 1);
|
||||
const warpMode = pv(parameters.warpMode, i);
|
||||
const voices = pv(parameters.voices, i);
|
||||
const phaseRand = clamp(pv(parameters.phaserand, i), 0, 1);
|
||||
const panspread = voices > 1 ? clamp(pv(parameters.panspread, i), 0, 1) : 0;
|
||||
const gain1 = Math.sqrt(0.5 - 0.5 * panspread);
|
||||
@@ -1349,7 +1286,6 @@ class WavetableOscillatorProcessor extends AudioWorkletProcessor {
|
||||
let f = pv(parameters.frequency, i);
|
||||
f = applySemitoneDetuneToFrequency(f, detune / 100); // overall detune
|
||||
const normalizer = 1 / Math.sqrt(voices);
|
||||
const detuner = getDetuner(voices, freqspread);
|
||||
for (let n = 0; n < voices; n++) {
|
||||
const isOdd = (n & 1) == 1;
|
||||
let gainL = gain1;
|
||||
@@ -1359,8 +1295,8 @@ class WavetableOscillatorProcessor extends AudioWorkletProcessor {
|
||||
gainL = gain2;
|
||||
gainR = gain1;
|
||||
}
|
||||
const fVoice = applySemitoneDetuneToFrequency(f, detuner(n)); // voice detune
|
||||
const dPhase = fVoice * INVSR;
|
||||
const fVoice = applySemitoneDetuneToFrequency(f, getUnisonDetune(voices, freqspread, n)); // voice detune
|
||||
const dPhase = fVoice * this.invSR;
|
||||
const level = this._chooseMip(dPhase);
|
||||
const table = this.tables[level];
|
||||
|
||||
@@ -1369,13 +1305,13 @@ class WavetableOscillatorProcessor extends AudioWorkletProcessor {
|
||||
const ph = this._warpPhase(this.phase[n], warpAmount, warpMode);
|
||||
const s0 = this._sampleFrame(table[fIdx], ph);
|
||||
const s1 = this._sampleFrame(table[Math.min(this.numFrames - 1, fIdx + 1)], ph);
|
||||
let s = lerp(s0, s1, interpT);
|
||||
let s = s0 + (s1 - s0) * frac;
|
||||
if (warpMode === WarpMode.FLIP && this.phase[n] < warpAmount) {
|
||||
s = -s;
|
||||
}
|
||||
outL[i] += s * gainL * normalizer;
|
||||
outR[i] += s * gainR * normalizer;
|
||||
this.phase[n] = frac(this.phase[n] + dPhase);
|
||||
this.phase[n] = wrapPhase(this.phase[n] + dPhase);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -5,16 +5,7 @@ This program is free software: you can redistribute it and/or modify it under th
|
||||
*/
|
||||
|
||||
import { Note, Interval, Scale } from '@tonaljs/tonal';
|
||||
import {
|
||||
_mod,
|
||||
errorLogger,
|
||||
getAccidentalsOffset,
|
||||
isNote,
|
||||
logger,
|
||||
noteToMidi,
|
||||
register,
|
||||
removeUndefineds,
|
||||
} from '@strudel/core';
|
||||
import { register, _mod, logger, isNote, noteToMidi, removeUndefineds, getAccidentalsOffset } from '@strudel/core';
|
||||
import { stepInNamedScale, nearestNumberIndex } from './tonleiter.mjs';
|
||||
|
||||
const octavesInterval = (octaves) => (octaves <= 0 ? -1 : 1) + octaves * 7 + 'P';
|
||||
@@ -303,7 +294,7 @@ export const scale = register(
|
||||
}
|
||||
if (offset != 0) scaleNote = Note.transpose(scaleNote, Interval.fromSemitones(offset));
|
||||
} catch (err) {
|
||||
errorLogger(err, 'tonal');
|
||||
logger(`[tonal] ${err.message}`, 'error');
|
||||
return; // will be removed
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+132
-20
@@ -41,7 +41,7 @@ importers:
|
||||
version: 2.2.7
|
||||
'@vitest/coverage-v8':
|
||||
specifier: 3.0.4
|
||||
version: 3.0.4(vitest@3.0.4(@types/debug@4.1.12)(@types/node@22.10.10)(@vitest/ui@3.0.4)(jiti@2.4.2)(lightningcss@1.29.1)(terser@5.37.0)(yaml@2.7.0))
|
||||
version: 3.0.4(vitest@3.0.4)
|
||||
'@vitest/ui':
|
||||
specifier: ^3.0.4
|
||||
version: 3.0.4(vitest@3.0.4)
|
||||
@@ -85,29 +85,29 @@ importers:
|
||||
examples/codemirror-repl:
|
||||
dependencies:
|
||||
'@strudel/codemirror':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/codemirror
|
||||
specifier: 1.2.6
|
||||
version: 1.2.6(@codemirror/lint@6.8.4)
|
||||
'@strudel/core':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/core
|
||||
specifier: 1.2.5
|
||||
version: 1.2.5
|
||||
'@strudel/draw':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/draw
|
||||
specifier: 1.2.5
|
||||
version: 1.2.5
|
||||
'@strudel/mini':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/mini
|
||||
specifier: 1.2.5
|
||||
version: 1.2.5
|
||||
'@strudel/soundfonts':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/soundfonts
|
||||
specifier: 1.2.6
|
||||
version: 1.2.6
|
||||
'@strudel/tonal':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/tonal
|
||||
specifier: 1.2.5
|
||||
version: 1.2.5
|
||||
'@strudel/transpiler':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/transpiler
|
||||
specifier: 1.2.5
|
||||
version: 1.2.5
|
||||
'@strudel/webaudio':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/webaudio
|
||||
specifier: 1.2.6
|
||||
version: 1.2.6
|
||||
devDependencies:
|
||||
vite:
|
||||
specifier: ^6.0.11
|
||||
@@ -116,8 +116,8 @@ importers:
|
||||
examples/headless-repl:
|
||||
dependencies:
|
||||
'@strudel/web':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/web
|
||||
specifier: 1.2.6
|
||||
version: 1.2.6
|
||||
devDependencies:
|
||||
vite:
|
||||
specifier: ^6.0.11
|
||||
@@ -2544,6 +2544,33 @@ packages:
|
||||
'@sinclair/typebox@0.27.8':
|
||||
resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==}
|
||||
|
||||
'@strudel/codemirror@1.2.6':
|
||||
resolution: {integrity: sha512-zjKbnbnOY40dlIAQu1poq28rcngYliLsuDwXXVnJsaXzCZSVuXEOMq91LqY4IGz/2QUOIDGm663ziz6Nb4IXeg==}
|
||||
|
||||
'@strudel/core@1.2.5':
|
||||
resolution: {integrity: sha512-XO6ICbfuGfvaQ4sZmfIFsPDcjFez/+AVpNgJnmks5gtMMQNXpr0reuee61tvMiu2k5QYKzxWgzDl2FF9pJeIgA==}
|
||||
|
||||
'@strudel/draw@1.2.5':
|
||||
resolution: {integrity: sha512-ZI4WPxUsVsxgDoeZMHo6VfFf+S1tsj8ubKBeSLcf6lH29JqPp7Nz6tPmbomOV1fzazZ7+xpkC7z772MdtgWy2g==}
|
||||
|
||||
'@strudel/mini@1.2.5':
|
||||
resolution: {integrity: sha512-GmTWSBt49C3ogBy0hjM3bmf6K12PaJ1JJLsEvQu7j5J+lUd2n7GmLYR1UL6fntY4yVqT8ZwMhfUf6vIgUCTNbw==}
|
||||
|
||||
'@strudel/soundfonts@1.2.6':
|
||||
resolution: {integrity: sha512-F7eBhohkCXCMo8WiufSAhD3JCfL5GTgCh9RlOJVT+1swiFeIss1vUAWJR3WjonB6moL8BiuNpcYOqO6i6cFy4A==}
|
||||
|
||||
'@strudel/tonal@1.2.5':
|
||||
resolution: {integrity: sha512-mX3uW0nWH8Sa52HTLwnxlR5BrejFX77abeaiMbp+kQCMrX9I+XRdf8rEr7gdeInB6f/y2TzTVFpnsNsB5jLdoA==}
|
||||
|
||||
'@strudel/transpiler@1.2.5':
|
||||
resolution: {integrity: sha512-mBZWfQBzknvnIYsjjzR8D67A+uIZ1iZ60U2WoXT21q5luXKz2c95RcTpqdx3oOGpLK9K00lDzcKjhQDJtj4OKw==}
|
||||
|
||||
'@strudel/web@1.2.6':
|
||||
resolution: {integrity: sha512-e6VltuX90zZz8JVe1FtN23DCaXA9p8zGDakp5OuUb5kZ5hC6cySFkkOsHdGC6dJ0EAsZSCKXMihRxgk9GhBwKA==}
|
||||
|
||||
'@strudel/webaudio@1.2.6':
|
||||
resolution: {integrity: sha512-4LnyNvyeAXYNmXQGGw2f0chIkDr9Rbwo+SK6Y0p8C3W8juDiOfKq2ihSVph95yaBBPRYFJ5Kttj1kLknRHYsog==}
|
||||
|
||||
'@supabase/auth-js@2.67.3':
|
||||
resolution: {integrity: sha512-NJDaW8yXs49xMvWVOkSIr8j46jf+tYHV0wHhrwOaLLMZSFO4g6kKAf+MfzQ2RaD06OCUkUHIzctLAxjTgEVpzw==}
|
||||
|
||||
@@ -7094,6 +7121,9 @@ packages:
|
||||
engines: {node: '>=16 || 14 >=14.17'}
|
||||
hasBin: true
|
||||
|
||||
superdough@1.2.6:
|
||||
resolution: {integrity: sha512-zHEFfG+N4zhxJ9lUDf8p4Oj4sdGGQ5SrOPksdc0cKv5Yn0hzlNlMehEa5TkeIfDCiR27Y4H81ks7S8/CGSByhQ==}
|
||||
|
||||
supports-color@7.2.0:
|
||||
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -7102,6 +7132,9 @@ packages:
|
||||
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
supradough@1.2.4:
|
||||
resolution: {integrity: sha512-/v3cL9fq05QCQVwbttLHNZcjyyFFz5wdKvg3j6V6moRXpDlQG89l8dQtUIpQvHsxYI5sXNod9rh5bf+Y4NMGeA==}
|
||||
|
||||
tabbable@6.2.0:
|
||||
resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==}
|
||||
|
||||
@@ -10019,6 +10052,79 @@ snapshots:
|
||||
|
||||
'@sinclair/typebox@0.27.8': {}
|
||||
|
||||
'@strudel/codemirror@1.2.6(@codemirror/lint@6.8.4)':
|
||||
dependencies:
|
||||
'@codemirror/autocomplete': 6.18.4
|
||||
'@codemirror/commands': 6.8.0
|
||||
'@codemirror/lang-javascript': 6.2.2
|
||||
'@codemirror/language': 6.10.8
|
||||
'@codemirror/search': 6.5.8
|
||||
'@codemirror/state': 6.5.1
|
||||
'@codemirror/view': 6.36.2
|
||||
'@lezer/highlight': 1.2.1
|
||||
'@nanostores/persistent': 0.10.2(nanostores@0.11.3)
|
||||
'@replit/codemirror-emacs': 6.1.0(@codemirror/autocomplete@6.18.4)(@codemirror/commands@6.8.0)(@codemirror/search@6.5.8)(@codemirror/state@6.5.1)(@codemirror/view@6.36.2)
|
||||
'@replit/codemirror-vim': 6.3.0(@codemirror/commands@6.8.0)(@codemirror/language@6.10.8)(@codemirror/search@6.5.8)(@codemirror/state@6.5.1)(@codemirror/view@6.36.2)
|
||||
'@replit/codemirror-vscode-keymap': 6.0.2(@codemirror/autocomplete@6.18.4)(@codemirror/commands@6.8.0)(@codemirror/language@6.10.8)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.8)(@codemirror/state@6.5.1)(@codemirror/view@6.36.2)
|
||||
'@strudel/core': 1.2.5
|
||||
'@strudel/draw': 1.2.5
|
||||
'@strudel/tonal': 1.2.5
|
||||
'@strudel/transpiler': 1.2.5
|
||||
'@tonaljs/tonal': 4.10.0
|
||||
nanostores: 0.11.3
|
||||
superdough: 1.2.6
|
||||
transitivePeerDependencies:
|
||||
- '@codemirror/lint'
|
||||
|
||||
'@strudel/core@1.2.5':
|
||||
dependencies:
|
||||
fraction.js: 5.2.1
|
||||
|
||||
'@strudel/draw@1.2.5':
|
||||
dependencies:
|
||||
'@strudel/core': 1.2.5
|
||||
|
||||
'@strudel/mini@1.2.5':
|
||||
dependencies:
|
||||
'@strudel/core': 1.2.5
|
||||
|
||||
'@strudel/soundfonts@1.2.6':
|
||||
dependencies:
|
||||
'@strudel/core': 1.2.5
|
||||
'@strudel/webaudio': 1.2.6
|
||||
sfumato: 0.1.2
|
||||
soundfont2: 0.5.0
|
||||
|
||||
'@strudel/tonal@1.2.5':
|
||||
dependencies:
|
||||
'@strudel/core': 1.2.5
|
||||
'@tonaljs/tonal': 4.10.0
|
||||
chord-voicings: 0.0.1
|
||||
webmidi: 3.1.12
|
||||
|
||||
'@strudel/transpiler@1.2.5':
|
||||
dependencies:
|
||||
'@strudel/core': 1.2.5
|
||||
'@strudel/mini': 1.2.5
|
||||
acorn: 8.14.0
|
||||
escodegen: 2.1.0
|
||||
estree-walker: 3.0.3
|
||||
|
||||
'@strudel/web@1.2.6':
|
||||
dependencies:
|
||||
'@strudel/core': 1.2.5
|
||||
'@strudel/mini': 1.2.5
|
||||
'@strudel/tonal': 1.2.5
|
||||
'@strudel/transpiler': 1.2.5
|
||||
'@strudel/webaudio': 1.2.6
|
||||
|
||||
'@strudel/webaudio@1.2.6':
|
||||
dependencies:
|
||||
'@strudel/core': 1.2.5
|
||||
'@strudel/draw': 1.2.5
|
||||
superdough: 1.2.6
|
||||
supradough: 1.2.4
|
||||
|
||||
'@supabase/auth-js@2.67.3':
|
||||
dependencies:
|
||||
'@supabase/node-fetch': 2.6.15
|
||||
@@ -10554,7 +10660,7 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@vitest/coverage-v8@3.0.4(vitest@3.0.4(@types/debug@4.1.12)(@types/node@22.10.10)(@vitest/ui@3.0.4)(jiti@2.4.2)(lightningcss@1.29.1)(terser@5.37.0)(yaml@2.7.0))':
|
||||
'@vitest/coverage-v8@3.0.4(vitest@3.0.4)':
|
||||
dependencies:
|
||||
'@ampproject/remapping': 2.3.0
|
||||
'@bcoe/v8-coverage': 1.0.2
|
||||
@@ -15665,12 +15771,18 @@ snapshots:
|
||||
pirates: 4.0.6
|
||||
ts-interface-checker: 0.1.13
|
||||
|
||||
superdough@1.2.6:
|
||||
dependencies:
|
||||
nanostores: 0.11.3
|
||||
|
||||
supports-color@7.2.0:
|
||||
dependencies:
|
||||
has-flag: 4.0.0
|
||||
|
||||
supports-preserve-symlinks-flag@1.0.0: {}
|
||||
|
||||
supradough@1.2.4: {}
|
||||
|
||||
tabbable@6.2.0: {}
|
||||
|
||||
tailwindcss@3.4.17:
|
||||
|
||||
@@ -1391,75 +1391,6 @@ exports[`runs examples > example "bpdecay" example index 0 1`] = `
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "bpdepthfrequency" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/16 | note:c s:sawtooth cutoff:600 bpdepthfrequency:200 ]",
|
||||
"[ 1/16 → 1/8 | note:c s:sawtooth cutoff:600 bpdepthfrequency:200 ]",
|
||||
"[ 1/8 → 3/16 | note:c# s:sawtooth cutoff:600 bpdepthfrequency:200 ]",
|
||||
"[ 3/16 → 1/4 | note:c s:sawtooth cutoff:600 bpdepthfrequency:200 ]",
|
||||
"[ 1/4 → 5/16 | note:c s:sawtooth cutoff:600 bpdepthfrequency:200 ]",
|
||||
"[ 5/16 → 3/8 | note:c4 s:sawtooth cutoff:600 bpdepthfrequency:200 ]",
|
||||
"[ 3/8 → 7/16 | note:c s:sawtooth cutoff:600 bpdepthfrequency:200 ]",
|
||||
"[ 7/16 → 1/2 | note:c s:sawtooth cutoff:600 bpdepthfrequency:200 ]",
|
||||
"[ 1/2 → 9/16 | note:c# s:sawtooth cutoff:600 bpdepthfrequency:200 ]",
|
||||
"[ 9/16 → 5/8 | note:c s:sawtooth cutoff:600 bpdepthfrequency:200 ]",
|
||||
"[ 5/8 → 11/16 | note:c s:sawtooth cutoff:600 bpdepthfrequency:200 ]",
|
||||
"[ 11/16 → 3/4 | note:c4 s:sawtooth cutoff:600 bpdepthfrequency:200 ]",
|
||||
"[ 3/4 → 13/16 | note:c s:sawtooth cutoff:600 bpdepthfrequency:200 ]",
|
||||
"[ 13/16 → 7/8 | note:c s:sawtooth cutoff:600 bpdepthfrequency:200 ]",
|
||||
"[ 7/8 → 15/16 | note:c# s:sawtooth cutoff:600 bpdepthfrequency:200 ]",
|
||||
"[ 15/16 → 1/1 | note:c s:sawtooth cutoff:600 bpdepthfrequency:200 ]",
|
||||
"[ 1/1 → 17/16 | note:c s:sawtooth cutoff:600 bpdepthfrequency:500 ]",
|
||||
"[ 17/16 → 9/8 | note:c4 s:sawtooth cutoff:600 bpdepthfrequency:500 ]",
|
||||
"[ 9/8 → 19/16 | note:c s:sawtooth cutoff:600 bpdepthfrequency:500 ]",
|
||||
"[ 19/16 → 5/4 | note:c s:sawtooth cutoff:600 bpdepthfrequency:500 ]",
|
||||
"[ 5/4 → 21/16 | note:c# s:sawtooth cutoff:600 bpdepthfrequency:500 ]",
|
||||
"[ 21/16 → 11/8 | note:c s:sawtooth cutoff:600 bpdepthfrequency:500 ]",
|
||||
"[ 11/8 → 23/16 | note:c s:sawtooth cutoff:600 bpdepthfrequency:500 ]",
|
||||
"[ 23/16 → 3/2 | note:c4 s:sawtooth cutoff:600 bpdepthfrequency:500 ]",
|
||||
"[ 3/2 → 25/16 | note:c s:sawtooth cutoff:600 bpdepthfrequency:500 ]",
|
||||
"[ 25/16 → 13/8 | note:c s:sawtooth cutoff:600 bpdepthfrequency:500 ]",
|
||||
"[ 13/8 → 27/16 | note:c# s:sawtooth cutoff:600 bpdepthfrequency:500 ]",
|
||||
"[ 27/16 → 7/4 | note:c s:sawtooth cutoff:600 bpdepthfrequency:500 ]",
|
||||
"[ 7/4 → 29/16 | note:c s:sawtooth cutoff:600 bpdepthfrequency:500 ]",
|
||||
"[ 29/16 → 15/8 | note:c4 s:sawtooth cutoff:600 bpdepthfrequency:500 ]",
|
||||
"[ 15/8 → 31/16 | note:c s:sawtooth cutoff:600 bpdepthfrequency:500 ]",
|
||||
"[ 31/16 → 2/1 | note:c s:sawtooth cutoff:600 bpdepthfrequency:500 ]",
|
||||
"[ 2/1 → 33/16 | note:c# s:sawtooth cutoff:600 bpdepthfrequency:100 ]",
|
||||
"[ 33/16 → 17/8 | note:c s:sawtooth cutoff:600 bpdepthfrequency:100 ]",
|
||||
"[ 17/8 → 35/16 | note:c s:sawtooth cutoff:600 bpdepthfrequency:100 ]",
|
||||
"[ 35/16 → 9/4 | note:c4 s:sawtooth cutoff:600 bpdepthfrequency:100 ]",
|
||||
"[ 9/4 → 37/16 | note:c s:sawtooth cutoff:600 bpdepthfrequency:100 ]",
|
||||
"[ 37/16 → 19/8 | note:c s:sawtooth cutoff:600 bpdepthfrequency:100 ]",
|
||||
"[ 19/8 → 39/16 | note:c# s:sawtooth cutoff:600 bpdepthfrequency:100 ]",
|
||||
"[ 39/16 → 5/2 | note:c s:sawtooth cutoff:600 bpdepthfrequency:100 ]",
|
||||
"[ 5/2 → 41/16 | note:c s:sawtooth cutoff:600 bpdepthfrequency:100 ]",
|
||||
"[ 41/16 → 21/8 | note:c4 s:sawtooth cutoff:600 bpdepthfrequency:100 ]",
|
||||
"[ 21/8 → 43/16 | note:c s:sawtooth cutoff:600 bpdepthfrequency:100 ]",
|
||||
"[ 43/16 → 11/4 | note:c s:sawtooth cutoff:600 bpdepthfrequency:100 ]",
|
||||
"[ 11/4 → 45/16 | note:c# s:sawtooth cutoff:600 bpdepthfrequency:100 ]",
|
||||
"[ 45/16 → 23/8 | note:c s:sawtooth cutoff:600 bpdepthfrequency:100 ]",
|
||||
"[ 23/8 → 47/16 | note:c s:sawtooth cutoff:600 bpdepthfrequency:100 ]",
|
||||
"[ 47/16 → 3/1 | note:c4 s:sawtooth cutoff:600 bpdepthfrequency:100 ]",
|
||||
"[ 3/1 → 49/16 | note:c s:sawtooth cutoff:600 bpdepthfrequency:0 ]",
|
||||
"[ 49/16 → 25/8 | note:c s:sawtooth cutoff:600 bpdepthfrequency:0 ]",
|
||||
"[ 25/8 → 51/16 | note:c# s:sawtooth cutoff:600 bpdepthfrequency:0 ]",
|
||||
"[ 51/16 → 13/4 | note:c s:sawtooth cutoff:600 bpdepthfrequency:0 ]",
|
||||
"[ 13/4 → 53/16 | note:c s:sawtooth cutoff:600 bpdepthfrequency:0 ]",
|
||||
"[ 53/16 → 27/8 | note:c4 s:sawtooth cutoff:600 bpdepthfrequency:0 ]",
|
||||
"[ 27/8 → 55/16 | note:c s:sawtooth cutoff:600 bpdepthfrequency:0 ]",
|
||||
"[ 55/16 → 7/2 | note:c s:sawtooth cutoff:600 bpdepthfrequency:0 ]",
|
||||
"[ 7/2 → 57/16 | note:c# s:sawtooth cutoff:600 bpdepthfrequency:0 ]",
|
||||
"[ 57/16 → 29/8 | note:c s:sawtooth cutoff:600 bpdepthfrequency:0 ]",
|
||||
"[ 29/8 → 59/16 | note:c s:sawtooth cutoff:600 bpdepthfrequency:0 ]",
|
||||
"[ 59/16 → 15/4 | note:c4 s:sawtooth cutoff:600 bpdepthfrequency:0 ]",
|
||||
"[ 15/4 → 61/16 | note:c s:sawtooth cutoff:600 bpdepthfrequency:0 ]",
|
||||
"[ 61/16 → 31/8 | note:c s:sawtooth cutoff:600 bpdepthfrequency:0 ]",
|
||||
"[ 31/8 → 63/16 | note:c# s:sawtooth cutoff:600 bpdepthfrequency:0 ]",
|
||||
"[ 63/16 → 4/1 | note:c s:sawtooth cutoff:600 bpdepthfrequency:0 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "bpenv" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/4 | note:c2 s:sawtooth bandf:500 bpattack:0.5 bpenv:4 ]",
|
||||
@@ -4855,75 +4786,6 @@ exports[`runs examples > example "hpdecay" example index 0 1`] = `
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "hpdepthfrequency" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/16 | note:c s:sawtooth cutoff:600 hpdepthfrequency:200 ]",
|
||||
"[ 1/16 → 1/8 | note:c s:sawtooth cutoff:600 hpdepthfrequency:200 ]",
|
||||
"[ 1/8 → 3/16 | note:c# s:sawtooth cutoff:600 hpdepthfrequency:200 ]",
|
||||
"[ 3/16 → 1/4 | note:c s:sawtooth cutoff:600 hpdepthfrequency:200 ]",
|
||||
"[ 1/4 → 5/16 | note:c s:sawtooth cutoff:600 hpdepthfrequency:200 ]",
|
||||
"[ 5/16 → 3/8 | note:c4 s:sawtooth cutoff:600 hpdepthfrequency:200 ]",
|
||||
"[ 3/8 → 7/16 | note:c s:sawtooth cutoff:600 hpdepthfrequency:200 ]",
|
||||
"[ 7/16 → 1/2 | note:c s:sawtooth cutoff:600 hpdepthfrequency:200 ]",
|
||||
"[ 1/2 → 9/16 | note:c# s:sawtooth cutoff:600 hpdepthfrequency:200 ]",
|
||||
"[ 9/16 → 5/8 | note:c s:sawtooth cutoff:600 hpdepthfrequency:200 ]",
|
||||
"[ 5/8 → 11/16 | note:c s:sawtooth cutoff:600 hpdepthfrequency:200 ]",
|
||||
"[ 11/16 → 3/4 | note:c4 s:sawtooth cutoff:600 hpdepthfrequency:200 ]",
|
||||
"[ 3/4 → 13/16 | note:c s:sawtooth cutoff:600 hpdepthfrequency:200 ]",
|
||||
"[ 13/16 → 7/8 | note:c s:sawtooth cutoff:600 hpdepthfrequency:200 ]",
|
||||
"[ 7/8 → 15/16 | note:c# s:sawtooth cutoff:600 hpdepthfrequency:200 ]",
|
||||
"[ 15/16 → 1/1 | note:c s:sawtooth cutoff:600 hpdepthfrequency:200 ]",
|
||||
"[ 1/1 → 17/16 | note:c s:sawtooth cutoff:600 hpdepthfrequency:500 ]",
|
||||
"[ 17/16 → 9/8 | note:c4 s:sawtooth cutoff:600 hpdepthfrequency:500 ]",
|
||||
"[ 9/8 → 19/16 | note:c s:sawtooth cutoff:600 hpdepthfrequency:500 ]",
|
||||
"[ 19/16 → 5/4 | note:c s:sawtooth cutoff:600 hpdepthfrequency:500 ]",
|
||||
"[ 5/4 → 21/16 | note:c# s:sawtooth cutoff:600 hpdepthfrequency:500 ]",
|
||||
"[ 21/16 → 11/8 | note:c s:sawtooth cutoff:600 hpdepthfrequency:500 ]",
|
||||
"[ 11/8 → 23/16 | note:c s:sawtooth cutoff:600 hpdepthfrequency:500 ]",
|
||||
"[ 23/16 → 3/2 | note:c4 s:sawtooth cutoff:600 hpdepthfrequency:500 ]",
|
||||
"[ 3/2 → 25/16 | note:c s:sawtooth cutoff:600 hpdepthfrequency:500 ]",
|
||||
"[ 25/16 → 13/8 | note:c s:sawtooth cutoff:600 hpdepthfrequency:500 ]",
|
||||
"[ 13/8 → 27/16 | note:c# s:sawtooth cutoff:600 hpdepthfrequency:500 ]",
|
||||
"[ 27/16 → 7/4 | note:c s:sawtooth cutoff:600 hpdepthfrequency:500 ]",
|
||||
"[ 7/4 → 29/16 | note:c s:sawtooth cutoff:600 hpdepthfrequency:500 ]",
|
||||
"[ 29/16 → 15/8 | note:c4 s:sawtooth cutoff:600 hpdepthfrequency:500 ]",
|
||||
"[ 15/8 → 31/16 | note:c s:sawtooth cutoff:600 hpdepthfrequency:500 ]",
|
||||
"[ 31/16 → 2/1 | note:c s:sawtooth cutoff:600 hpdepthfrequency:500 ]",
|
||||
"[ 2/1 → 33/16 | note:c# s:sawtooth cutoff:600 hpdepthfrequency:100 ]",
|
||||
"[ 33/16 → 17/8 | note:c s:sawtooth cutoff:600 hpdepthfrequency:100 ]",
|
||||
"[ 17/8 → 35/16 | note:c s:sawtooth cutoff:600 hpdepthfrequency:100 ]",
|
||||
"[ 35/16 → 9/4 | note:c4 s:sawtooth cutoff:600 hpdepthfrequency:100 ]",
|
||||
"[ 9/4 → 37/16 | note:c s:sawtooth cutoff:600 hpdepthfrequency:100 ]",
|
||||
"[ 37/16 → 19/8 | note:c s:sawtooth cutoff:600 hpdepthfrequency:100 ]",
|
||||
"[ 19/8 → 39/16 | note:c# s:sawtooth cutoff:600 hpdepthfrequency:100 ]",
|
||||
"[ 39/16 → 5/2 | note:c s:sawtooth cutoff:600 hpdepthfrequency:100 ]",
|
||||
"[ 5/2 → 41/16 | note:c s:sawtooth cutoff:600 hpdepthfrequency:100 ]",
|
||||
"[ 41/16 → 21/8 | note:c4 s:sawtooth cutoff:600 hpdepthfrequency:100 ]",
|
||||
"[ 21/8 → 43/16 | note:c s:sawtooth cutoff:600 hpdepthfrequency:100 ]",
|
||||
"[ 43/16 → 11/4 | note:c s:sawtooth cutoff:600 hpdepthfrequency:100 ]",
|
||||
"[ 11/4 → 45/16 | note:c# s:sawtooth cutoff:600 hpdepthfrequency:100 ]",
|
||||
"[ 45/16 → 23/8 | note:c s:sawtooth cutoff:600 hpdepthfrequency:100 ]",
|
||||
"[ 23/8 → 47/16 | note:c s:sawtooth cutoff:600 hpdepthfrequency:100 ]",
|
||||
"[ 47/16 → 3/1 | note:c4 s:sawtooth cutoff:600 hpdepthfrequency:100 ]",
|
||||
"[ 3/1 → 49/16 | note:c s:sawtooth cutoff:600 hpdepthfrequency:0 ]",
|
||||
"[ 49/16 → 25/8 | note:c s:sawtooth cutoff:600 hpdepthfrequency:0 ]",
|
||||
"[ 25/8 → 51/16 | note:c# s:sawtooth cutoff:600 hpdepthfrequency:0 ]",
|
||||
"[ 51/16 → 13/4 | note:c s:sawtooth cutoff:600 hpdepthfrequency:0 ]",
|
||||
"[ 13/4 → 53/16 | note:c s:sawtooth cutoff:600 hpdepthfrequency:0 ]",
|
||||
"[ 53/16 → 27/8 | note:c4 s:sawtooth cutoff:600 hpdepthfrequency:0 ]",
|
||||
"[ 27/8 → 55/16 | note:c s:sawtooth cutoff:600 hpdepthfrequency:0 ]",
|
||||
"[ 55/16 → 7/2 | note:c s:sawtooth cutoff:600 hpdepthfrequency:0 ]",
|
||||
"[ 7/2 → 57/16 | note:c# s:sawtooth cutoff:600 hpdepthfrequency:0 ]",
|
||||
"[ 57/16 → 29/8 | note:c s:sawtooth cutoff:600 hpdepthfrequency:0 ]",
|
||||
"[ 29/8 → 59/16 | note:c s:sawtooth cutoff:600 hpdepthfrequency:0 ]",
|
||||
"[ 59/16 → 15/4 | note:c4 s:sawtooth cutoff:600 hpdepthfrequency:0 ]",
|
||||
"[ 15/4 → 61/16 | note:c s:sawtooth cutoff:600 hpdepthfrequency:0 ]",
|
||||
"[ 61/16 → 31/8 | note:c s:sawtooth cutoff:600 hpdepthfrequency:0 ]",
|
||||
"[ 31/8 → 63/16 | note:c# s:sawtooth cutoff:600 hpdepthfrequency:0 ]",
|
||||
"[ 63/16 → 4/1 | note:c s:sawtooth cutoff:600 hpdepthfrequency:0 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "hpenv" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/4 | note:c2 s:sawtooth hcutoff:500 hpattack:0.5 hpenv:4 ]",
|
||||
@@ -6107,144 +5969,6 @@ exports[`runs examples > example "lpdecay" example index 0 1`] = `
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "lpdepth" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/16 | note:c s:sawtooth cutoff:600 lpdepth:1 ]",
|
||||
"[ 1/16 → 1/8 | note:c s:sawtooth cutoff:600 lpdepth:1 ]",
|
||||
"[ 1/8 → 3/16 | note:c# s:sawtooth cutoff:600 lpdepth:1 ]",
|
||||
"[ 3/16 → 1/4 | note:c s:sawtooth cutoff:600 lpdepth:1 ]",
|
||||
"[ 1/4 → 5/16 | note:c s:sawtooth cutoff:600 lpdepth:1 ]",
|
||||
"[ 5/16 → 3/8 | note:c4 s:sawtooth cutoff:600 lpdepth:1 ]",
|
||||
"[ 3/8 → 7/16 | note:c s:sawtooth cutoff:600 lpdepth:1 ]",
|
||||
"[ 7/16 → 1/2 | note:c s:sawtooth cutoff:600 lpdepth:1 ]",
|
||||
"[ 1/2 → 9/16 | note:c# s:sawtooth cutoff:600 lpdepth:1 ]",
|
||||
"[ 9/16 → 5/8 | note:c s:sawtooth cutoff:600 lpdepth:1 ]",
|
||||
"[ 5/8 → 11/16 | note:c s:sawtooth cutoff:600 lpdepth:1 ]",
|
||||
"[ 11/16 → 3/4 | note:c4 s:sawtooth cutoff:600 lpdepth:1 ]",
|
||||
"[ 3/4 → 13/16 | note:c s:sawtooth cutoff:600 lpdepth:1 ]",
|
||||
"[ 13/16 → 7/8 | note:c s:sawtooth cutoff:600 lpdepth:1 ]",
|
||||
"[ 7/8 → 15/16 | note:c# s:sawtooth cutoff:600 lpdepth:1 ]",
|
||||
"[ 15/16 → 1/1 | note:c s:sawtooth cutoff:600 lpdepth:1 ]",
|
||||
"[ 1/1 → 17/16 | note:c s:sawtooth cutoff:600 lpdepth:0.5 ]",
|
||||
"[ 17/16 → 9/8 | note:c4 s:sawtooth cutoff:600 lpdepth:0.5 ]",
|
||||
"[ 9/8 → 19/16 | note:c s:sawtooth cutoff:600 lpdepth:0.5 ]",
|
||||
"[ 19/16 → 5/4 | note:c s:sawtooth cutoff:600 lpdepth:0.5 ]",
|
||||
"[ 5/4 → 21/16 | note:c# s:sawtooth cutoff:600 lpdepth:0.5 ]",
|
||||
"[ 21/16 → 11/8 | note:c s:sawtooth cutoff:600 lpdepth:0.5 ]",
|
||||
"[ 11/8 → 23/16 | note:c s:sawtooth cutoff:600 lpdepth:0.5 ]",
|
||||
"[ 23/16 → 3/2 | note:c4 s:sawtooth cutoff:600 lpdepth:0.5 ]",
|
||||
"[ 3/2 → 25/16 | note:c s:sawtooth cutoff:600 lpdepth:0.5 ]",
|
||||
"[ 25/16 → 13/8 | note:c s:sawtooth cutoff:600 lpdepth:0.5 ]",
|
||||
"[ 13/8 → 27/16 | note:c# s:sawtooth cutoff:600 lpdepth:0.5 ]",
|
||||
"[ 27/16 → 7/4 | note:c s:sawtooth cutoff:600 lpdepth:0.5 ]",
|
||||
"[ 7/4 → 29/16 | note:c s:sawtooth cutoff:600 lpdepth:0.5 ]",
|
||||
"[ 29/16 → 15/8 | note:c4 s:sawtooth cutoff:600 lpdepth:0.5 ]",
|
||||
"[ 15/8 → 31/16 | note:c s:sawtooth cutoff:600 lpdepth:0.5 ]",
|
||||
"[ 31/16 → 2/1 | note:c s:sawtooth cutoff:600 lpdepth:0.5 ]",
|
||||
"[ 2/1 → 33/16 | note:c# s:sawtooth cutoff:600 lpdepth:1.8 ]",
|
||||
"[ 33/16 → 17/8 | note:c s:sawtooth cutoff:600 lpdepth:1.8 ]",
|
||||
"[ 17/8 → 35/16 | note:c s:sawtooth cutoff:600 lpdepth:1.8 ]",
|
||||
"[ 35/16 → 9/4 | note:c4 s:sawtooth cutoff:600 lpdepth:1.8 ]",
|
||||
"[ 9/4 → 37/16 | note:c s:sawtooth cutoff:600 lpdepth:1.8 ]",
|
||||
"[ 37/16 → 19/8 | note:c s:sawtooth cutoff:600 lpdepth:1.8 ]",
|
||||
"[ 19/8 → 39/16 | note:c# s:sawtooth cutoff:600 lpdepth:1.8 ]",
|
||||
"[ 39/16 → 5/2 | note:c s:sawtooth cutoff:600 lpdepth:1.8 ]",
|
||||
"[ 5/2 → 41/16 | note:c s:sawtooth cutoff:600 lpdepth:1.8 ]",
|
||||
"[ 41/16 → 21/8 | note:c4 s:sawtooth cutoff:600 lpdepth:1.8 ]",
|
||||
"[ 21/8 → 43/16 | note:c s:sawtooth cutoff:600 lpdepth:1.8 ]",
|
||||
"[ 43/16 → 11/4 | note:c s:sawtooth cutoff:600 lpdepth:1.8 ]",
|
||||
"[ 11/4 → 45/16 | note:c# s:sawtooth cutoff:600 lpdepth:1.8 ]",
|
||||
"[ 45/16 → 23/8 | note:c s:sawtooth cutoff:600 lpdepth:1.8 ]",
|
||||
"[ 23/8 → 47/16 | note:c s:sawtooth cutoff:600 lpdepth:1.8 ]",
|
||||
"[ 47/16 → 3/1 | note:c4 s:sawtooth cutoff:600 lpdepth:1.8 ]",
|
||||
"[ 3/1 → 49/16 | note:c s:sawtooth cutoff:600 lpdepth:0 ]",
|
||||
"[ 49/16 → 25/8 | note:c s:sawtooth cutoff:600 lpdepth:0 ]",
|
||||
"[ 25/8 → 51/16 | note:c# s:sawtooth cutoff:600 lpdepth:0 ]",
|
||||
"[ 51/16 → 13/4 | note:c s:sawtooth cutoff:600 lpdepth:0 ]",
|
||||
"[ 13/4 → 53/16 | note:c s:sawtooth cutoff:600 lpdepth:0 ]",
|
||||
"[ 53/16 → 27/8 | note:c4 s:sawtooth cutoff:600 lpdepth:0 ]",
|
||||
"[ 27/8 → 55/16 | note:c s:sawtooth cutoff:600 lpdepth:0 ]",
|
||||
"[ 55/16 → 7/2 | note:c s:sawtooth cutoff:600 lpdepth:0 ]",
|
||||
"[ 7/2 → 57/16 | note:c# s:sawtooth cutoff:600 lpdepth:0 ]",
|
||||
"[ 57/16 → 29/8 | note:c s:sawtooth cutoff:600 lpdepth:0 ]",
|
||||
"[ 29/8 → 59/16 | note:c s:sawtooth cutoff:600 lpdepth:0 ]",
|
||||
"[ 59/16 → 15/4 | note:c4 s:sawtooth cutoff:600 lpdepth:0 ]",
|
||||
"[ 15/4 → 61/16 | note:c s:sawtooth cutoff:600 lpdepth:0 ]",
|
||||
"[ 61/16 → 31/8 | note:c s:sawtooth cutoff:600 lpdepth:0 ]",
|
||||
"[ 31/8 → 63/16 | note:c# s:sawtooth cutoff:600 lpdepth:0 ]",
|
||||
"[ 63/16 → 4/1 | note:c s:sawtooth cutoff:600 lpdepth:0 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "lpdepthfrequency" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/16 | note:c s:sawtooth cutoff:600 lpdepthfrequency:200 ]",
|
||||
"[ 1/16 → 1/8 | note:c s:sawtooth cutoff:600 lpdepthfrequency:200 ]",
|
||||
"[ 1/8 → 3/16 | note:c# s:sawtooth cutoff:600 lpdepthfrequency:200 ]",
|
||||
"[ 3/16 → 1/4 | note:c s:sawtooth cutoff:600 lpdepthfrequency:200 ]",
|
||||
"[ 1/4 → 5/16 | note:c s:sawtooth cutoff:600 lpdepthfrequency:200 ]",
|
||||
"[ 5/16 → 3/8 | note:c4 s:sawtooth cutoff:600 lpdepthfrequency:200 ]",
|
||||
"[ 3/8 → 7/16 | note:c s:sawtooth cutoff:600 lpdepthfrequency:200 ]",
|
||||
"[ 7/16 → 1/2 | note:c s:sawtooth cutoff:600 lpdepthfrequency:200 ]",
|
||||
"[ 1/2 → 9/16 | note:c# s:sawtooth cutoff:600 lpdepthfrequency:200 ]",
|
||||
"[ 9/16 → 5/8 | note:c s:sawtooth cutoff:600 lpdepthfrequency:200 ]",
|
||||
"[ 5/8 → 11/16 | note:c s:sawtooth cutoff:600 lpdepthfrequency:200 ]",
|
||||
"[ 11/16 → 3/4 | note:c4 s:sawtooth cutoff:600 lpdepthfrequency:200 ]",
|
||||
"[ 3/4 → 13/16 | note:c s:sawtooth cutoff:600 lpdepthfrequency:200 ]",
|
||||
"[ 13/16 → 7/8 | note:c s:sawtooth cutoff:600 lpdepthfrequency:200 ]",
|
||||
"[ 7/8 → 15/16 | note:c# s:sawtooth cutoff:600 lpdepthfrequency:200 ]",
|
||||
"[ 15/16 → 1/1 | note:c s:sawtooth cutoff:600 lpdepthfrequency:200 ]",
|
||||
"[ 1/1 → 17/16 | note:c s:sawtooth cutoff:600 lpdepthfrequency:500 ]",
|
||||
"[ 17/16 → 9/8 | note:c4 s:sawtooth cutoff:600 lpdepthfrequency:500 ]",
|
||||
"[ 9/8 → 19/16 | note:c s:sawtooth cutoff:600 lpdepthfrequency:500 ]",
|
||||
"[ 19/16 → 5/4 | note:c s:sawtooth cutoff:600 lpdepthfrequency:500 ]",
|
||||
"[ 5/4 → 21/16 | note:c# s:sawtooth cutoff:600 lpdepthfrequency:500 ]",
|
||||
"[ 21/16 → 11/8 | note:c s:sawtooth cutoff:600 lpdepthfrequency:500 ]",
|
||||
"[ 11/8 → 23/16 | note:c s:sawtooth cutoff:600 lpdepthfrequency:500 ]",
|
||||
"[ 23/16 → 3/2 | note:c4 s:sawtooth cutoff:600 lpdepthfrequency:500 ]",
|
||||
"[ 3/2 → 25/16 | note:c s:sawtooth cutoff:600 lpdepthfrequency:500 ]",
|
||||
"[ 25/16 → 13/8 | note:c s:sawtooth cutoff:600 lpdepthfrequency:500 ]",
|
||||
"[ 13/8 → 27/16 | note:c# s:sawtooth cutoff:600 lpdepthfrequency:500 ]",
|
||||
"[ 27/16 → 7/4 | note:c s:sawtooth cutoff:600 lpdepthfrequency:500 ]",
|
||||
"[ 7/4 → 29/16 | note:c s:sawtooth cutoff:600 lpdepthfrequency:500 ]",
|
||||
"[ 29/16 → 15/8 | note:c4 s:sawtooth cutoff:600 lpdepthfrequency:500 ]",
|
||||
"[ 15/8 → 31/16 | note:c s:sawtooth cutoff:600 lpdepthfrequency:500 ]",
|
||||
"[ 31/16 → 2/1 | note:c s:sawtooth cutoff:600 lpdepthfrequency:500 ]",
|
||||
"[ 2/1 → 33/16 | note:c# s:sawtooth cutoff:600 lpdepthfrequency:100 ]",
|
||||
"[ 33/16 → 17/8 | note:c s:sawtooth cutoff:600 lpdepthfrequency:100 ]",
|
||||
"[ 17/8 → 35/16 | note:c s:sawtooth cutoff:600 lpdepthfrequency:100 ]",
|
||||
"[ 35/16 → 9/4 | note:c4 s:sawtooth cutoff:600 lpdepthfrequency:100 ]",
|
||||
"[ 9/4 → 37/16 | note:c s:sawtooth cutoff:600 lpdepthfrequency:100 ]",
|
||||
"[ 37/16 → 19/8 | note:c s:sawtooth cutoff:600 lpdepthfrequency:100 ]",
|
||||
"[ 19/8 → 39/16 | note:c# s:sawtooth cutoff:600 lpdepthfrequency:100 ]",
|
||||
"[ 39/16 → 5/2 | note:c s:sawtooth cutoff:600 lpdepthfrequency:100 ]",
|
||||
"[ 5/2 → 41/16 | note:c s:sawtooth cutoff:600 lpdepthfrequency:100 ]",
|
||||
"[ 41/16 → 21/8 | note:c4 s:sawtooth cutoff:600 lpdepthfrequency:100 ]",
|
||||
"[ 21/8 → 43/16 | note:c s:sawtooth cutoff:600 lpdepthfrequency:100 ]",
|
||||
"[ 43/16 → 11/4 | note:c s:sawtooth cutoff:600 lpdepthfrequency:100 ]",
|
||||
"[ 11/4 → 45/16 | note:c# s:sawtooth cutoff:600 lpdepthfrequency:100 ]",
|
||||
"[ 45/16 → 23/8 | note:c s:sawtooth cutoff:600 lpdepthfrequency:100 ]",
|
||||
"[ 23/8 → 47/16 | note:c s:sawtooth cutoff:600 lpdepthfrequency:100 ]",
|
||||
"[ 47/16 → 3/1 | note:c4 s:sawtooth cutoff:600 lpdepthfrequency:100 ]",
|
||||
"[ 3/1 → 49/16 | note:c s:sawtooth cutoff:600 lpdepthfrequency:0 ]",
|
||||
"[ 49/16 → 25/8 | note:c s:sawtooth cutoff:600 lpdepthfrequency:0 ]",
|
||||
"[ 25/8 → 51/16 | note:c# s:sawtooth cutoff:600 lpdepthfrequency:0 ]",
|
||||
"[ 51/16 → 13/4 | note:c s:sawtooth cutoff:600 lpdepthfrequency:0 ]",
|
||||
"[ 13/4 → 53/16 | note:c s:sawtooth cutoff:600 lpdepthfrequency:0 ]",
|
||||
"[ 53/16 → 27/8 | note:c4 s:sawtooth cutoff:600 lpdepthfrequency:0 ]",
|
||||
"[ 27/8 → 55/16 | note:c s:sawtooth cutoff:600 lpdepthfrequency:0 ]",
|
||||
"[ 55/16 → 7/2 | note:c s:sawtooth cutoff:600 lpdepthfrequency:0 ]",
|
||||
"[ 7/2 → 57/16 | note:c# s:sawtooth cutoff:600 lpdepthfrequency:0 ]",
|
||||
"[ 57/16 → 29/8 | note:c s:sawtooth cutoff:600 lpdepthfrequency:0 ]",
|
||||
"[ 29/8 → 59/16 | note:c s:sawtooth cutoff:600 lpdepthfrequency:0 ]",
|
||||
"[ 59/16 → 15/4 | note:c4 s:sawtooth cutoff:600 lpdepthfrequency:0 ]",
|
||||
"[ 15/4 → 61/16 | note:c s:sawtooth cutoff:600 lpdepthfrequency:0 ]",
|
||||
"[ 61/16 → 31/8 | note:c s:sawtooth cutoff:600 lpdepthfrequency:0 ]",
|
||||
"[ 31/8 → 63/16 | note:c# s:sawtooth cutoff:600 lpdepthfrequency:0 ]",
|
||||
"[ 63/16 → 4/1 | note:c s:sawtooth cutoff:600 lpdepthfrequency:0 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "lpenv" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/4 | note:c2 s:sawtooth cutoff:300 lpattack:0.5 lpenv:4 ]",
|
||||
@@ -6433,75 +6157,6 @@ exports[`runs examples > example "lpq" example index 0 1`] = `
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "lprate" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/16 | note:c s:sawtooth cutoff:600 lprate:4 ]",
|
||||
"[ 1/16 → 1/8 | note:c s:sawtooth cutoff:600 lprate:4 ]",
|
||||
"[ 1/8 → 3/16 | note:c# s:sawtooth cutoff:600 lprate:4 ]",
|
||||
"[ 3/16 → 1/4 | note:c s:sawtooth cutoff:600 lprate:4 ]",
|
||||
"[ 1/4 → 5/16 | note:c s:sawtooth cutoff:600 lprate:4 ]",
|
||||
"[ 5/16 → 3/8 | note:c4 s:sawtooth cutoff:600 lprate:4 ]",
|
||||
"[ 3/8 → 7/16 | note:c s:sawtooth cutoff:600 lprate:4 ]",
|
||||
"[ 7/16 → 1/2 | note:c s:sawtooth cutoff:600 lprate:4 ]",
|
||||
"[ 1/2 → 9/16 | note:c# s:sawtooth cutoff:600 lprate:4 ]",
|
||||
"[ 9/16 → 5/8 | note:c s:sawtooth cutoff:600 lprate:4 ]",
|
||||
"[ 5/8 → 11/16 | note:c s:sawtooth cutoff:600 lprate:4 ]",
|
||||
"[ 11/16 → 3/4 | note:c4 s:sawtooth cutoff:600 lprate:4 ]",
|
||||
"[ 3/4 → 13/16 | note:c s:sawtooth cutoff:600 lprate:4 ]",
|
||||
"[ 13/16 → 7/8 | note:c s:sawtooth cutoff:600 lprate:4 ]",
|
||||
"[ 7/8 → 15/16 | note:c# s:sawtooth cutoff:600 lprate:4 ]",
|
||||
"[ 15/16 → 1/1 | note:c s:sawtooth cutoff:600 lprate:4 ]",
|
||||
"[ 1/1 → 17/16 | note:c s:sawtooth cutoff:600 lprate:8 ]",
|
||||
"[ 17/16 → 9/8 | note:c4 s:sawtooth cutoff:600 lprate:8 ]",
|
||||
"[ 9/8 → 19/16 | note:c s:sawtooth cutoff:600 lprate:8 ]",
|
||||
"[ 19/16 → 5/4 | note:c s:sawtooth cutoff:600 lprate:8 ]",
|
||||
"[ 5/4 → 21/16 | note:c# s:sawtooth cutoff:600 lprate:8 ]",
|
||||
"[ 21/16 → 11/8 | note:c s:sawtooth cutoff:600 lprate:8 ]",
|
||||
"[ 11/8 → 23/16 | note:c s:sawtooth cutoff:600 lprate:8 ]",
|
||||
"[ 23/16 → 3/2 | note:c4 s:sawtooth cutoff:600 lprate:8 ]",
|
||||
"[ 3/2 → 25/16 | note:c s:sawtooth cutoff:600 lprate:8 ]",
|
||||
"[ 25/16 → 13/8 | note:c s:sawtooth cutoff:600 lprate:8 ]",
|
||||
"[ 13/8 → 27/16 | note:c# s:sawtooth cutoff:600 lprate:8 ]",
|
||||
"[ 27/16 → 7/4 | note:c s:sawtooth cutoff:600 lprate:8 ]",
|
||||
"[ 7/4 → 29/16 | note:c s:sawtooth cutoff:600 lprate:8 ]",
|
||||
"[ 29/16 → 15/8 | note:c4 s:sawtooth cutoff:600 lprate:8 ]",
|
||||
"[ 15/8 → 31/16 | note:c s:sawtooth cutoff:600 lprate:8 ]",
|
||||
"[ 31/16 → 2/1 | note:c s:sawtooth cutoff:600 lprate:8 ]",
|
||||
"[ 2/1 → 33/16 | note:c# s:sawtooth cutoff:600 lprate:2 ]",
|
||||
"[ 33/16 → 17/8 | note:c s:sawtooth cutoff:600 lprate:2 ]",
|
||||
"[ 17/8 → 35/16 | note:c s:sawtooth cutoff:600 lprate:2 ]",
|
||||
"[ 35/16 → 9/4 | note:c4 s:sawtooth cutoff:600 lprate:2 ]",
|
||||
"[ 9/4 → 37/16 | note:c s:sawtooth cutoff:600 lprate:2 ]",
|
||||
"[ 37/16 → 19/8 | note:c s:sawtooth cutoff:600 lprate:2 ]",
|
||||
"[ 19/8 → 39/16 | note:c# s:sawtooth cutoff:600 lprate:2 ]",
|
||||
"[ 39/16 → 5/2 | note:c s:sawtooth cutoff:600 lprate:2 ]",
|
||||
"[ 5/2 → 41/16 | note:c s:sawtooth cutoff:600 lprate:2 ]",
|
||||
"[ 41/16 → 21/8 | note:c4 s:sawtooth cutoff:600 lprate:2 ]",
|
||||
"[ 21/8 → 43/16 | note:c s:sawtooth cutoff:600 lprate:2 ]",
|
||||
"[ 43/16 → 11/4 | note:c s:sawtooth cutoff:600 lprate:2 ]",
|
||||
"[ 11/4 → 45/16 | note:c# s:sawtooth cutoff:600 lprate:2 ]",
|
||||
"[ 45/16 → 23/8 | note:c s:sawtooth cutoff:600 lprate:2 ]",
|
||||
"[ 23/8 → 47/16 | note:c s:sawtooth cutoff:600 lprate:2 ]",
|
||||
"[ 47/16 → 3/1 | note:c4 s:sawtooth cutoff:600 lprate:2 ]",
|
||||
"[ 3/1 → 49/16 | note:c s:sawtooth cutoff:600 lprate:1 ]",
|
||||
"[ 49/16 → 25/8 | note:c s:sawtooth cutoff:600 lprate:1 ]",
|
||||
"[ 25/8 → 51/16 | note:c# s:sawtooth cutoff:600 lprate:1 ]",
|
||||
"[ 51/16 → 13/4 | note:c s:sawtooth cutoff:600 lprate:1 ]",
|
||||
"[ 13/4 → 53/16 | note:c s:sawtooth cutoff:600 lprate:1 ]",
|
||||
"[ 53/16 → 27/8 | note:c4 s:sawtooth cutoff:600 lprate:1 ]",
|
||||
"[ 27/8 → 55/16 | note:c s:sawtooth cutoff:600 lprate:1 ]",
|
||||
"[ 55/16 → 7/2 | note:c s:sawtooth cutoff:600 lprate:1 ]",
|
||||
"[ 7/2 → 57/16 | note:c# s:sawtooth cutoff:600 lprate:1 ]",
|
||||
"[ 57/16 → 29/8 | note:c s:sawtooth cutoff:600 lprate:1 ]",
|
||||
"[ 29/8 → 59/16 | note:c s:sawtooth cutoff:600 lprate:1 ]",
|
||||
"[ 59/16 → 15/4 | note:c4 s:sawtooth cutoff:600 lprate:1 ]",
|
||||
"[ 15/4 → 61/16 | note:c s:sawtooth cutoff:600 lprate:1 ]",
|
||||
"[ 61/16 → 31/8 | note:c s:sawtooth cutoff:600 lprate:1 ]",
|
||||
"[ 31/8 → 63/16 | note:c# s:sawtooth cutoff:600 lprate:1 ]",
|
||||
"[ 63/16 → 4/1 | note:c s:sawtooth cutoff:600 lprate:1 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "lprelease" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/4 | note:c2 s:sawtooth clip:0.5 cutoff:300 lpenv:4 lprelease:0.5 release:0.5 ]",
|
||||
@@ -6544,75 +6199,6 @@ exports[`runs examples > example "lpsustain" example index 0 1`] = `
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "lpsync" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/16 | note:c s:sawtooth cutoff:600 lpsync:4 ]",
|
||||
"[ 1/16 → 1/8 | note:c s:sawtooth cutoff:600 lpsync:4 ]",
|
||||
"[ 1/8 → 3/16 | note:c# s:sawtooth cutoff:600 lpsync:4 ]",
|
||||
"[ 3/16 → 1/4 | note:c s:sawtooth cutoff:600 lpsync:4 ]",
|
||||
"[ 1/4 → 5/16 | note:c s:sawtooth cutoff:600 lpsync:4 ]",
|
||||
"[ 5/16 → 3/8 | note:c4 s:sawtooth cutoff:600 lpsync:4 ]",
|
||||
"[ 3/8 → 7/16 | note:c s:sawtooth cutoff:600 lpsync:4 ]",
|
||||
"[ 7/16 → 1/2 | note:c s:sawtooth cutoff:600 lpsync:4 ]",
|
||||
"[ 1/2 → 9/16 | note:c# s:sawtooth cutoff:600 lpsync:4 ]",
|
||||
"[ 9/16 → 5/8 | note:c s:sawtooth cutoff:600 lpsync:4 ]",
|
||||
"[ 5/8 → 11/16 | note:c s:sawtooth cutoff:600 lpsync:4 ]",
|
||||
"[ 11/16 → 3/4 | note:c4 s:sawtooth cutoff:600 lpsync:4 ]",
|
||||
"[ 3/4 → 13/16 | note:c s:sawtooth cutoff:600 lpsync:4 ]",
|
||||
"[ 13/16 → 7/8 | note:c s:sawtooth cutoff:600 lpsync:4 ]",
|
||||
"[ 7/8 → 15/16 | note:c# s:sawtooth cutoff:600 lpsync:4 ]",
|
||||
"[ 15/16 → 1/1 | note:c s:sawtooth cutoff:600 lpsync:4 ]",
|
||||
"[ 1/1 → 17/16 | note:c s:sawtooth cutoff:600 lpsync:8 ]",
|
||||
"[ 17/16 → 9/8 | note:c4 s:sawtooth cutoff:600 lpsync:8 ]",
|
||||
"[ 9/8 → 19/16 | note:c s:sawtooth cutoff:600 lpsync:8 ]",
|
||||
"[ 19/16 → 5/4 | note:c s:sawtooth cutoff:600 lpsync:8 ]",
|
||||
"[ 5/4 → 21/16 | note:c# s:sawtooth cutoff:600 lpsync:8 ]",
|
||||
"[ 21/16 → 11/8 | note:c s:sawtooth cutoff:600 lpsync:8 ]",
|
||||
"[ 11/8 → 23/16 | note:c s:sawtooth cutoff:600 lpsync:8 ]",
|
||||
"[ 23/16 → 3/2 | note:c4 s:sawtooth cutoff:600 lpsync:8 ]",
|
||||
"[ 3/2 → 25/16 | note:c s:sawtooth cutoff:600 lpsync:8 ]",
|
||||
"[ 25/16 → 13/8 | note:c s:sawtooth cutoff:600 lpsync:8 ]",
|
||||
"[ 13/8 → 27/16 | note:c# s:sawtooth cutoff:600 lpsync:8 ]",
|
||||
"[ 27/16 → 7/4 | note:c s:sawtooth cutoff:600 lpsync:8 ]",
|
||||
"[ 7/4 → 29/16 | note:c s:sawtooth cutoff:600 lpsync:8 ]",
|
||||
"[ 29/16 → 15/8 | note:c4 s:sawtooth cutoff:600 lpsync:8 ]",
|
||||
"[ 15/8 → 31/16 | note:c s:sawtooth cutoff:600 lpsync:8 ]",
|
||||
"[ 31/16 → 2/1 | note:c s:sawtooth cutoff:600 lpsync:8 ]",
|
||||
"[ 2/1 → 33/16 | note:c# s:sawtooth cutoff:600 lpsync:2 ]",
|
||||
"[ 33/16 → 17/8 | note:c s:sawtooth cutoff:600 lpsync:2 ]",
|
||||
"[ 17/8 → 35/16 | note:c s:sawtooth cutoff:600 lpsync:2 ]",
|
||||
"[ 35/16 → 9/4 | note:c4 s:sawtooth cutoff:600 lpsync:2 ]",
|
||||
"[ 9/4 → 37/16 | note:c s:sawtooth cutoff:600 lpsync:2 ]",
|
||||
"[ 37/16 → 19/8 | note:c s:sawtooth cutoff:600 lpsync:2 ]",
|
||||
"[ 19/8 → 39/16 | note:c# s:sawtooth cutoff:600 lpsync:2 ]",
|
||||
"[ 39/16 → 5/2 | note:c s:sawtooth cutoff:600 lpsync:2 ]",
|
||||
"[ 5/2 → 41/16 | note:c s:sawtooth cutoff:600 lpsync:2 ]",
|
||||
"[ 41/16 → 21/8 | note:c4 s:sawtooth cutoff:600 lpsync:2 ]",
|
||||
"[ 21/8 → 43/16 | note:c s:sawtooth cutoff:600 lpsync:2 ]",
|
||||
"[ 43/16 → 11/4 | note:c s:sawtooth cutoff:600 lpsync:2 ]",
|
||||
"[ 11/4 → 45/16 | note:c# s:sawtooth cutoff:600 lpsync:2 ]",
|
||||
"[ 45/16 → 23/8 | note:c s:sawtooth cutoff:600 lpsync:2 ]",
|
||||
"[ 23/8 → 47/16 | note:c s:sawtooth cutoff:600 lpsync:2 ]",
|
||||
"[ 47/16 → 3/1 | note:c4 s:sawtooth cutoff:600 lpsync:2 ]",
|
||||
"[ 3/1 → 49/16 | note:c s:sawtooth cutoff:600 lpsync:1 ]",
|
||||
"[ 49/16 → 25/8 | note:c s:sawtooth cutoff:600 lpsync:1 ]",
|
||||
"[ 25/8 → 51/16 | note:c# s:sawtooth cutoff:600 lpsync:1 ]",
|
||||
"[ 51/16 → 13/4 | note:c s:sawtooth cutoff:600 lpsync:1 ]",
|
||||
"[ 13/4 → 53/16 | note:c s:sawtooth cutoff:600 lpsync:1 ]",
|
||||
"[ 53/16 → 27/8 | note:c4 s:sawtooth cutoff:600 lpsync:1 ]",
|
||||
"[ 27/8 → 55/16 | note:c s:sawtooth cutoff:600 lpsync:1 ]",
|
||||
"[ 55/16 → 7/2 | note:c s:sawtooth cutoff:600 lpsync:1 ]",
|
||||
"[ 7/2 → 57/16 | note:c# s:sawtooth cutoff:600 lpsync:1 ]",
|
||||
"[ 57/16 → 29/8 | note:c s:sawtooth cutoff:600 lpsync:1 ]",
|
||||
"[ 29/8 → 59/16 | note:c s:sawtooth cutoff:600 lpsync:1 ]",
|
||||
"[ 59/16 → 15/4 | note:c4 s:sawtooth cutoff:600 lpsync:1 ]",
|
||||
"[ 15/4 → 61/16 | note:c s:sawtooth cutoff:600 lpsync:1 ]",
|
||||
"[ 61/16 → 31/8 | note:c s:sawtooth cutoff:600 lpsync:1 ]",
|
||||
"[ 31/8 → 63/16 | note:c# s:sawtooth cutoff:600 lpsync:1 ]",
|
||||
"[ 63/16 → 4/1 | note:c s:sawtooth cutoff:600 lpsync:1 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "lrate" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/1 | n:0 s:supersquare leslie:1 lrate:1 ]",
|
||||
@@ -7302,18 +6888,18 @@ exports[`runs examples > example "nrpv" example index 0 1`] = `
|
||||
|
||||
exports[`runs examples > example "octave" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/1 | note:F3 s:supersaw octave:0 ]",
|
||||
"[ 0/1 → 1/1 | note:C4 s:supersaw octave:0 ]",
|
||||
"[ 0/1 → 1/1 | note:F4 s:supersaw octave:0 ]",
|
||||
"[ 1/1 → 2/1 | note:F3 s:supersaw octave:1 ]",
|
||||
"[ 1/1 → 2/1 | note:C4 s:supersaw octave:1 ]",
|
||||
"[ 1/1 → 2/1 | note:F4 s:supersaw octave:1 ]",
|
||||
"[ 2/1 → 3/1 | note:F3 s:supersaw octave:2 ]",
|
||||
"[ 2/1 → 3/1 | note:C4 s:supersaw octave:2 ]",
|
||||
"[ 2/1 → 3/1 | note:F4 s:supersaw octave:2 ]",
|
||||
"[ 3/1 → 4/1 | note:F3 s:supersaw octave:3 ]",
|
||||
"[ 3/1 → 4/1 | note:C4 s:supersaw octave:3 ]",
|
||||
"[ 3/1 → 4/1 | note:F4 s:supersaw octave:3 ]",
|
||||
"[ 0/1 → 1/1 | n:0 s:supersquare octave:3 ]",
|
||||
"[ 0/1 → 1/1 | n:4 s:supersquare octave:3 ]",
|
||||
"[ 0/1 → 1/1 | n:7 s:supersquare octave:3 ]",
|
||||
"[ 1/1 → 2/1 | n:0 s:supersquare octave:4 ]",
|
||||
"[ 1/1 → 2/1 | n:4 s:supersquare octave:4 ]",
|
||||
"[ 1/1 → 2/1 | n:7 s:supersquare octave:4 ]",
|
||||
"[ 2/1 → 3/1 | n:0 s:supersquare octave:5 ]",
|
||||
"[ 2/1 → 3/1 | n:4 s:supersquare octave:5 ]",
|
||||
"[ 2/1 → 3/1 | n:7 s:supersquare octave:5 ]",
|
||||
"[ 3/1 → 4/1 | n:0 s:supersquare octave:6 ]",
|
||||
"[ 3/1 → 4/1 | n:4 s:supersquare octave:6 ]",
|
||||
"[ 3/1 → 4/1 | n:7 s:supersquare octave:6 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
@@ -7633,112 +7219,6 @@ exports[`runs examples > example "panchor" example index 0 1`] = `
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "partials" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/16 | s:user note:A3 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 1/16 → 1/8 | s:user note:G#4 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 1/8 → 3/16 | s:user note:F#4 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 3/16 → 1/4 | s:user note:B3 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 1/4 → 5/16 | s:user note:C#4 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 5/16 → 3/8 | s:user note:E4 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 3/8 → 7/16 | s:user note:D4 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 7/16 → 1/2 | s:user note:C#4 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 1/2 → 9/16 | s:user note:C#4 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 9/16 → 5/8 | s:user note:B3 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 5/8 → 11/16 | s:user note:B3 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 11/16 → 3/4 | s:user note:D4 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 3/4 → 13/16 | s:user note:B3 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 13/16 → 7/8 | s:user note:G#4 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 7/8 → 15/16 | s:user note:D4 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 15/16 → 1/1 | s:user note:F#4 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 1/1 → 17/16 | s:user note:E4 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 17/16 → 9/8 | s:user note:E4 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 9/8 → 19/16 | s:user note:F#4 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 19/16 → 5/4 | s:user note:A3 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 5/4 → 21/16 | s:user note:F#4 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 21/16 → 11/8 | s:user note:A3 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 11/8 → 23/16 | s:user note:B3 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 23/16 → 3/2 | s:user note:E4 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 3/2 → 25/16 | s:user note:E4 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 25/16 → 13/8 | s:user note:B3 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 13/8 → 27/16 | s:user note:D4 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 27/16 → 7/4 | s:user note:C#4 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 7/4 → 29/16 | s:user note:B3 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 29/16 → 15/8 | s:user note:D4 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 15/8 → 31/16 | s:user note:E4 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 31/16 → 2/1 | s:user note:G#4 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 2/1 → 33/16 | s:user note:A4 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 33/16 → 17/8 | s:user note:C#4 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 17/8 → 35/16 | s:user note:A4 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 35/16 → 9/4 | s:user note:A3 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 9/4 → 37/16 | s:user note:C#4 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 37/16 → 19/8 | s:user note:F#4 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 19/8 → 39/16 | s:user note:G#4 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 39/16 → 5/2 | s:user note:B3 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 5/2 → 41/16 | s:user note:D4 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 41/16 → 21/8 | s:user note:E4 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 21/8 → 43/16 | s:user note:B3 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 43/16 → 11/4 | s:user note:E4 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 11/4 → 45/16 | s:user note:F#4 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 45/16 → 23/8 | s:user note:A4 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 23/8 → 47/16 | s:user note:A3 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 47/16 → 3/1 | s:user note:C#4 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 3/1 → 49/16 | s:user note:B3 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 49/16 → 25/8 | s:user note:A3 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 25/8 → 51/16 | s:user note:C#4 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 51/16 → 13/4 | s:user note:A4 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 13/4 → 53/16 | s:user note:A4 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 53/16 → 27/8 | s:user note:C#4 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 27/8 → 55/16 | s:user note:D4 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 55/16 → 7/2 | s:user note:G#4 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 7/2 → 57/16 | s:user note:D4 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 57/16 → 29/8 | s:user note:G#4 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 29/8 → 59/16 | s:user note:A4 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 59/16 → 15/4 | s:user note:A4 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 15/4 → 61/16 | s:user note:G#4 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 61/16 → 31/8 | s:user note:A3 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 31/8 → 63/16 | s:user note:F#4 partials:[1 0 1 0 0 1] ]",
|
||||
"[ 63/16 → 4/1 | s:user note:C#4 partials:[1 0 1 0 0 1] ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "partials" example index 1 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/8 | s:saw note:G#3 partials:[1] ]",
|
||||
"[ 1/8 → 1/4 | s:saw note:A#4 partials:[1 0 1 1 0 0 0 0] ]",
|
||||
"[ 1/4 → 3/8 | s:saw note:D#4 partials:[1 0 1 1 1 1 1] ]",
|
||||
"[ 3/8 → 1/2 | s:saw note:D#4 partials:[1 1 0 0 1 1 1] ]",
|
||||
"[ 1/2 → 5/8 | s:saw note:C#4 partials:[1 0 0 0 0 1 1] ]",
|
||||
"[ 5/8 → 3/4 | s:saw note:A#3 partials:[1 0 0 0 1 1] ]",
|
||||
"[ 3/4 → 7/8 | s:saw note:B3 partials:[1 1 0 0 1 1] ]",
|
||||
"[ 7/8 → 1/1 | s:saw note:D#4 partials:[1 1 0 0 1 1 0] ]",
|
||||
"[ 1/1 → 9/8 | s:saw note:F#4 partials:[1 0 0 0 0 1 1 0] ]",
|
||||
"[ 9/8 → 5/4 | s:saw note:A#4 partials:[1 0 1 0 1 1 0 1] ]",
|
||||
"[ 5/4 → 11/8 | s:saw note:A#4 partials:[1 0 1 1 1 0 1 1] ]",
|
||||
"[ 11/8 → 3/2 | s:saw note:B3 partials:[1 0 1 1 1 1] ]",
|
||||
"[ 3/2 → 13/8 | s:saw note:G#4 partials:[1 0 0 1 1 1 0 0] ]",
|
||||
"[ 13/8 → 7/4 | s:saw note:E4 partials:[1 0 0 0 0 0 0 0] ]",
|
||||
"[ 7/4 → 15/8 | s:saw note:B3 partials:[1 1 0 1 0 1] ]",
|
||||
"[ 15/8 → 2/1 | s:saw note:G#4 partials:[1 0 0 1 1 0 0 1] ]",
|
||||
"[ 2/1 → 17/8 | s:saw note:D#5 partials:[1 1 1 1 0 1 1 0] ]",
|
||||
"[ 17/8 → 9/4 | s:saw note:C#5 partials:[1 1 1 0 1 0 0 0] ]",
|
||||
"[ 9/4 → 19/8 | s:saw note:D#4 partials:[1 0 1 1 0 0 1] ]",
|
||||
"[ 19/8 → 5/2 | s:saw note:C#5 partials:[1 1 0 1 0 1 1 1] ]",
|
||||
"[ 5/2 → 21/8 | s:saw note:E4 partials:[1 1 1 0 1 1 0] ]",
|
||||
"[ 21/8 → 11/4 | s:saw note:A#3 partials:[1 0 1 0 1 0] ]",
|
||||
"[ 11/4 → 23/8 | s:saw note:G#4 partials:[1 0 1 0 0 0 1 1] ]",
|
||||
"[ 23/8 → 3/1 | s:saw note:G#3 partials:[1 0 1] ]",
|
||||
"[ 3/1 → 25/8 | s:saw note:B3 partials:[1 1 1 0 0 0] ]",
|
||||
"[ 25/8 → 13/4 | s:saw note:D#4 partials:[1 0 1 1 0 0 0] ]",
|
||||
"[ 13/4 → 27/8 | s:saw note:D#5 partials:[1 1 1 1 1 1 1 1] ]",
|
||||
"[ 27/8 → 7/2 | s:saw note:D#4 partials:[1 1 0 1 0 0 1] ]",
|
||||
"[ 7/2 → 29/8 | s:saw note:D#4 partials:[1 1 0 1 0 0 1] ]",
|
||||
"[ 29/8 → 15/4 | s:saw note:D#5 partials:[1 1 1 1 0 0 0 1] ]",
|
||||
"[ 15/4 → 31/8 | s:saw note:B4 partials:[1 1 0 1 0 0 0 0] ]",
|
||||
"[ 31/8 → 4/1 | s:saw note:A#4 partials:[1 0 1 1 1 1 0 1] ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "pattack" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/2 | note:c pattack:0 ]",
|
||||
@@ -7992,75 +7472,6 @@ exports[`runs examples > example "phasersweep" example index 0 1`] = `
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "phases" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/8 | s:saw note:G#1 partials:{partials:1} ]",
|
||||
"[ 0/1 → 1/8 | s:saw note:G#1 partials:{partials:1} phases:[0.5 0.5 0.5] ]",
|
||||
"[ 1/8 → 1/4 | s:saw note:A#2 partials:{partials:1} ]",
|
||||
"[ 1/8 → 1/4 | s:saw note:A#2 partials:{partials:1} phases:[0.5 0.5 0.5] ]",
|
||||
"[ 1/4 → 3/8 | s:saw note:D#2 partials:{partials:1} ]",
|
||||
"[ 1/4 → 3/8 | s:saw note:D#2 partials:{partials:1} phases:[0.5 0.5 0.5] ]",
|
||||
"[ 3/8 → 1/2 | s:saw note:D#2 partials:{partials:1} ]",
|
||||
"[ 3/8 → 1/2 | s:saw note:D#2 partials:{partials:1} phases:[0.5 0.5 0.5] ]",
|
||||
"[ 1/2 → 5/8 | s:saw note:C#2 partials:{partials:1} ]",
|
||||
"[ 1/2 → 5/8 | s:saw note:C#2 partials:{partials:1} phases:[0.5 0.5 0.5] ]",
|
||||
"[ 5/8 → 3/4 | s:saw note:A#1 partials:{partials:1} ]",
|
||||
"[ 5/8 → 3/4 | s:saw note:A#1 partials:{partials:1} phases:[0.5 0.5 0.5] ]",
|
||||
"[ 3/4 → 7/8 | s:saw note:B1 partials:{partials:1} ]",
|
||||
"[ 3/4 → 7/8 | s:saw note:B1 partials:{partials:1} phases:[0.5 0.5 0.5] ]",
|
||||
"[ 7/8 → 1/1 | s:saw note:D#2 partials:{partials:1} ]",
|
||||
"[ 7/8 → 1/1 | s:saw note:D#2 partials:{partials:1} phases:[0.5 0.5 0.5] ]",
|
||||
"[ 1/1 → 9/8 | s:saw note:F#2 partials:{partials:1} ]",
|
||||
"[ 1/1 → 9/8 | s:saw note:F#2 partials:{partials:1} phases:[0.5 0.5 0.5] ]",
|
||||
"[ 9/8 → 5/4 | s:saw note:A#2 partials:{partials:1} ]",
|
||||
"[ 9/8 → 5/4 | s:saw note:A#2 partials:{partials:1} phases:[0.5 0.5 0.5] ]",
|
||||
"[ 5/4 → 11/8 | s:saw note:A#2 partials:{partials:1} ]",
|
||||
"[ 5/4 → 11/8 | s:saw note:A#2 partials:{partials:1} phases:[0.5 0.5 0.5] ]",
|
||||
"[ 11/8 → 3/2 | s:saw note:B1 partials:{partials:1} ]",
|
||||
"[ 11/8 → 3/2 | s:saw note:B1 partials:{partials:1} phases:[0.5 0.5 0.5] ]",
|
||||
"[ 3/2 → 13/8 | s:saw note:G#2 partials:{partials:1} ]",
|
||||
"[ 3/2 → 13/8 | s:saw note:G#2 partials:{partials:1} phases:[0.5 0.5 0.5] ]",
|
||||
"[ 13/8 → 7/4 | s:saw note:E2 partials:{partials:1} ]",
|
||||
"[ 13/8 → 7/4 | s:saw note:E2 partials:{partials:1} phases:[0.5 0.5 0.5] ]",
|
||||
"[ 7/4 → 15/8 | s:saw note:B1 partials:{partials:1} ]",
|
||||
"[ 7/4 → 15/8 | s:saw note:B1 partials:{partials:1} phases:[0.5 0.5 0.5] ]",
|
||||
"[ 15/8 → 2/1 | s:saw note:G#2 partials:{partials:1} ]",
|
||||
"[ 15/8 → 2/1 | s:saw note:G#2 partials:{partials:1} phases:[0.5 0.5 0.5] ]",
|
||||
"[ 2/1 → 17/8 | s:saw note:D#3 partials:{partials:1} ]",
|
||||
"[ 2/1 → 17/8 | s:saw note:D#3 partials:{partials:1} phases:[0.5 0.5 0.5] ]",
|
||||
"[ 17/8 → 9/4 | s:saw note:C#3 partials:{partials:1} ]",
|
||||
"[ 17/8 → 9/4 | s:saw note:C#3 partials:{partials:1} phases:[0.5 0.5 0.5] ]",
|
||||
"[ 9/4 → 19/8 | s:saw note:D#2 partials:{partials:1} ]",
|
||||
"[ 9/4 → 19/8 | s:saw note:D#2 partials:{partials:1} phases:[0.5 0.5 0.5] ]",
|
||||
"[ 19/8 → 5/2 | s:saw note:C#3 partials:{partials:1} ]",
|
||||
"[ 19/8 → 5/2 | s:saw note:C#3 partials:{partials:1} phases:[0.5 0.5 0.5] ]",
|
||||
"[ 5/2 → 21/8 | s:saw note:E2 partials:{partials:1} ]",
|
||||
"[ 5/2 → 21/8 | s:saw note:E2 partials:{partials:1} phases:[0.5 0.5 0.5] ]",
|
||||
"[ 21/8 → 11/4 | s:saw note:A#1 partials:{partials:1} ]",
|
||||
"[ 21/8 → 11/4 | s:saw note:A#1 partials:{partials:1} phases:[0.5 0.5 0.5] ]",
|
||||
"[ 11/4 → 23/8 | s:saw note:G#2 partials:{partials:1} ]",
|
||||
"[ 11/4 → 23/8 | s:saw note:G#2 partials:{partials:1} phases:[0.5 0.5 0.5] ]",
|
||||
"[ 23/8 → 3/1 | s:saw note:G#1 partials:{partials:1} ]",
|
||||
"[ 23/8 → 3/1 | s:saw note:G#1 partials:{partials:1} phases:[0.5 0.5 0.5] ]",
|
||||
"[ 3/1 → 25/8 | s:saw note:B1 partials:{partials:1} ]",
|
||||
"[ 3/1 → 25/8 | s:saw note:B1 partials:{partials:1} phases:[0.5 0.5 0.5] ]",
|
||||
"[ 25/8 → 13/4 | s:saw note:D#2 partials:{partials:1} ]",
|
||||
"[ 25/8 → 13/4 | s:saw note:D#2 partials:{partials:1} phases:[0.5 0.5 0.5] ]",
|
||||
"[ 13/4 → 27/8 | s:saw note:D#3 partials:{partials:1} ]",
|
||||
"[ 13/4 → 27/8 | s:saw note:D#3 partials:{partials:1} phases:[0.5 0.5 0.5] ]",
|
||||
"[ 27/8 → 7/2 | s:saw note:D#2 partials:{partials:1} ]",
|
||||
"[ 27/8 → 7/2 | s:saw note:D#2 partials:{partials:1} phases:[0.5 0.5 0.5] ]",
|
||||
"[ 7/2 → 29/8 | s:saw note:D#2 partials:{partials:1} ]",
|
||||
"[ 7/2 → 29/8 | s:saw note:D#2 partials:{partials:1} phases:[0.5 0.5 0.5] ]",
|
||||
"[ 29/8 → 15/4 | s:saw note:D#3 partials:{partials:1} ]",
|
||||
"[ 29/8 → 15/4 | s:saw note:D#3 partials:{partials:1} phases:[0.5 0.5 0.5] ]",
|
||||
"[ 15/4 → 31/8 | s:saw note:B2 partials:{partials:1} ]",
|
||||
"[ 15/4 → 31/8 | s:saw note:B2 partials:{partials:1} phases:[0.5 0.5 0.5] ]",
|
||||
"[ 31/8 → 4/1 | s:saw note:A#2 partials:{partials:1} ]",
|
||||
"[ 31/8 → 4/1 | s:saw note:A#2 partials:{partials:1} phases:[0.5 0.5 0.5] ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "pianoroll" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/8 | note:c2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
@@ -8782,75 +8193,6 @@ exports[`runs examples > example "rand" example index 0 1`] = `
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "randL" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/16 | s:saw note:F1 partials:[0 0 0 0 0 0 0 0] ]",
|
||||
"[ 1/16 → 1/8 | s:saw note:Bb2 partials:[0.8426077850162983 0.0886186733841896 0.9342893119901419 0.06848422810435295 0.6248505394905806 0.5372848063707352 0.8283301629126072 0.7969411127269268] ]",
|
||||
"[ 1/8 → 3/16 | s:saw note:G2 partials:[0.6852155700325966 0.17723728902637959 0.8521428182721138 0.5022399611771107 0.9780306946486235 0.3186125475913286 0.1851645242422819 0.8495976086705923] ]",
|
||||
"[ 3/16 → 1/4 | s:saw note:Ab1 partials:[0.20066574029624462 0.17195586115121841 0.2159541044384241 0.17005567252635956 0.6841662060469389 0.07906394638121128 0.004815371707081795 0.027107616886496544] ]",
|
||||
"[ 1/4 → 5/16 | s:saw note:C2 partials:[0.36975969187915325 0.18273563869297504 0.012781793251633644 0.5423613861203194 0.12467948533594608 0.7188410349190235 0.856887087225914 0.8818203993141651] ]",
|
||||
"[ 5/16 → 3/8 | s:saw note:Eb2 partials:[0.5675661638379097 0.15932588279247284 0.31376867927610874 0.32359412126243114 0.3281281068921089 0.011714376509189606 0.4433113746345043 0.68459103256464] ]",
|
||||
"[ 3/8 → 7/16 | s:saw note:C2 partials:[0.40139251574873924 0.15799915604293346 0.9604704882949591 0.03873417526483536 0.4157217647880316 0.27756719291210175 0.8800551909953356 0.5692523363977671] ]",
|
||||
"[ 7/16 → 1/2 | s:saw note:Bb1 partials:[0.3015887886285782 0.22311350144445896 0.47645803540945053 0.001545613631606102 0.53841289319098 0.330710094422102 0.2747743520885706 0.9471044968813658] ]",
|
||||
"[ 1/2 → 9/16 | s:saw note:Bb1 partials:[0.2604806162416935 0.3654713351279497 0.9900747090578079 0.22572625242173672 0.9328999016433954 0.5273839123547077 0.7047769222408533 0.7311522178351879] ]",
|
||||
"[ 9/16 → 5/8 | s:saw note:G1 partials:[0.16399178467690945 0.9559339117258787 0.21830322034657001 0.4903192054480314 0.2504696864634752 0.7302105724811554 0.6580934692174196 0.9855979979038239] ]",
|
||||
"[ 5/8 → 11/16 | s:saw note:G1 partials:[0.1356358677148819 0.5403102282434702 0.07321739941835403 0.9646544624119997 0.7707359045743942 0.46888123638927937 0.9489036612212658 0.366748945787549] ]",
|
||||
"[ 11/16 → 3/4 | s:saw note:C2 partials:[0.408811716362834 0.9613851886242628 0.7683626655489206 0.5862949229776859 0.9568102769553661 0.7154356613755226 0.6283384170383215 0.2193678840994835] ]",
|
||||
"[ 3/4 → 13/16 | s:saw note:Ab1 partials:[0.19582648016512394 0.0024610888212919235 0.08272589556872845 0.09592138417065144 0.9678201265633106 0.3249557167291641 0.9463537875562906 0.20766610652208328] ]",
|
||||
"[ 13/16 → 7/8 | s:saw note:Ab2 partials:[0.7516226731240749 0.15610851533710957 0.9039078876376152 0.8602268267422915 0.928601048886776 0.4479671400040388 0.5080656576901674 0.562442347407341] ]",
|
||||
"[ 7/8 → 15/16 | s:saw note:C2 partials:[0.3976310808211565 0.13476407527923584 0.963376859202981 0.6579397786408663 0.5203975513577461 0.865439185872674 0.5583905670791864 0.2736878804862499] ]",
|
||||
"[ 15/16 → 1/1 | s:saw note:G2 partials:[0.7029578909277916 0.6351367030292749 0.26143294386565685 0.6411824338138103 0.5676082745194435 0.4803342465311289 0.5140306428074837 0.9477859679609537] ]",
|
||||
"[ 1/1 → 17/16 | s:saw note:Eb2 partials:[0.5195421651005745 0.9349692352116108 0.1718774326145649 0.9601866770535707 0.2802200373262167 0.8732441551983356 0.01810968853533268 0.42737805284559727] ]",
|
||||
"[ 17/16 → 9/8 | s:saw note:Eb2 partials:[0.5486328881233931 0.7088070474565029 0.07877279818058014 0.9422610364854336 0.7696988433599472 0.8289324026554823 0.5925844628363848 0.3920764606446028] ]",
|
||||
"[ 9/8 → 19/16 | s:saw note:G2 partials:[0.6724895145744085 0.21410975232720375 0.5229047331959009 0.20957534946501255 0.7402758821845055 0.2681974694132805 0.4059371296316385 0.9422509074211121] ]",
|
||||
"[ 19/16 → 5/4 | s:saw note:F1 partials:[0.08174665085971355 0.2762963864952326 0.5428560189902782 0.29452037811279297 0.17946280725300312 0.366960596293211 0.26327736489474773 0.5002023074775934] ]",
|
||||
"[ 5/4 → 21/16 | s:saw note:G2 partials:[0.7287282031029463 0.9341024849563837 0.5098182689398527 0.5608645845204592 0.6812942810356617 0.5015129633247852 0.4602120481431484 0.927369873970747] ]",
|
||||
"[ 21/16 → 11/8 | s:saw note:F1 partials:[0.08104278706014156 0.5200720727443695 0.5363391060382128 0.9824271816760302 0.10652091167867184 0.23473932035267353 0.7656102329492569 0.4850195776671171] ]",
|
||||
"[ 11/8 → 23/16 | s:saw note:Ab1 partials:[0.18280375190079212 0.40527783520519733 0.30504362285137177 0.7622128054499626 0.842598931863904 0.053796686232089996 0.5124214049428701 0.9178454764187336] ]",
|
||||
"[ 23/16 → 3/2 | s:saw note:Eb2 partials:[0.5081270858645439 0.1421387754380703 0.8852288406342268 0.9066353775560856 0.7700740322470665 0.35437702015042305 0.20032598450779915 0.963155921548605] ]",
|
||||
"[ 3/2 → 25/16 | s:saw note:F2 partials:[0.6084080748260021 0.4424846563488245 0.755185678601265 0.04328125901520252 0.04244415462017059 0.3994515985250473 0.787989066913724 0.3180440980941057] ]",
|
||||
"[ 25/16 → 13/8 | s:saw note:Ab1 partials:[0.17092766426503658 0.7808954659849405 0.8234915658831596 0.8643151633441448 0.9781719036400318 0.5102015696465969 0.018259897828102112 0.25844234600663185] ]",
|
||||
"[ 13/8 → 27/16 | s:saw note:Db2 partials:[0.49675471149384975 0.2965749856084585 0.09848833456635475 0.19172007404267788 0.22677889838814735 0.9841996673494577 0.11156083643436432 0.6079028844833374] ]",
|
||||
"[ 27/16 → 7/4 | s:saw note:Bb1 partials:[0.29589061066508293 0.13011129200458527 0.8021425940096378 0.38040103763341904 0.33274981752038 0.29745868407189846 0.9898250997066498 0.743482418358326] ]",
|
||||
"[ 7/4 → 29/16 | s:saw note:Ab1 partials:[0.20473789609968662 0.7480021081864834 0.30757393687963486 0.5827204789966345 0.22094514779746532 0.6370698790997267 0.16573002003133297 0.0638319905847311] ]",
|
||||
"[ 29/16 → 15/8 | s:saw note:Db2 partials:[0.4695742893964052 0.6690364442765713 0.6216684486716986 0.41342394426465034 0.9508822970092297 0.8403679896146059 0.91759910620749 0.6591395419090986] ]",
|
||||
"[ 15/8 → 31/16 | s:saw note:F2 partials:[0.5945571791380644 0.3962489552795887 0.563431927934289 0.4742323160171509 0.4185752831399441 0.9703940469771624 0.5282467231154442 0.6896266285330057] ]",
|
||||
"[ 31/16 → 2/1 | s:saw note:Bb2 partials:[0.8672592639923096 0.3438429981470108 0.9795673936605453 0.46547594852745533 0.9770395755767822 0.36851615831255913 0.7725539114326239 0.876962523907423] ]",
|
||||
"[ 2/1 → 33/16 | s:saw note:C3 partials:[0.9595271199941635 0.5427457969635725 0.45864437520504 0.8057199101895094 0.18388565629720688 0.17221237532794476 0.8838487900793552 0.4976818822324276] ]",
|
||||
"[ 33/16 → 17/8 | s:saw note:Bb1 partials:[0.3324784208089113 0.6005446836352348 0.1564352661371231 0.765217661857605 0.12899602390825748 0.20880493707954884 0.9156261626631021 0.5550615340471268] ]",
|
||||
"[ 17/8 → 35/16 | s:saw note:Bb2 partials:[0.9033902939409018 0.22919894196093082 0.8388008493930101 0.9108077362179756 0.32246968522667885 0.16122018359601498 0.31932324543595314 0.18524732999503613] ]",
|
||||
"[ 35/16 → 9/4 | s:saw note:F1 partials:[0.037000780925154686 0.30360451713204384 0.7516817897558212 0.17065968923270702 0.24669718369841576 0.978821286931634 0.9620356522500515 0.12417634017765522] ]",
|
||||
"[ 9/4 → 37/16 | s:saw note:C2 partials:[0.34548251144587994 0.30419893004000187 0.10205957666039467 0.9906709585338831 0.3156223688274622 0.3745057098567486 0.7668170686811209 0.9379972033202648] ]",
|
||||
"[ 37/16 → 19/8 | s:saw note:F2 partials:[0.6649543270468712 0.41613837145268917 0.9078915324062109 0.2232209537178278 0.2918607946485281 0.4931973237544298 0.7035325299948454 0.29280414804816246] ]",
|
||||
"[ 19/8 → 39/16 | s:saw note:Bb2 partials:[0.8365066405385733 0.5369812995195389 0.2263860274106264 0.21759207546710968 0.2172116208821535 0.09454222768545151 0.8075542915612459 0.9017798062413931] ]",
|
||||
"[ 39/16 → 5/2 | s:saw note:G1 partials:[0.14231421053409576 0.6801821701228619 0.7958894111216068 0.9494249671697617 0.179213372990489 0.22531690262258053 0.7458387855440378 0.3595987129956484] ]",
|
||||
"[ 5/2 → 41/16 | s:saw note:Db2 partials:[0.45861607417464256 0.6807645913213491 0.07009582966566086 0.1450389064848423 0.2579921595752239 0.612881500273943 0.5503941811621189 0.7027011960744858] ]",
|
||||
"[ 41/16 → 21/8 | s:saw note:F2 partials:[0.6068673655390739 0.8489279896020889 0.027898555621504784 0.90520747192204 0.18605150654911995 0.30939464271068573 0.16843407973647118 0.5127317048609257] ]",
|
||||
"[ 21/8 → 43/16 | s:saw note:G1 partials:[0.16019348427653313 0.47758268006145954 0.9749126061797142 0.8025561925023794 0.3600110989063978 0.324309878051281 0.5655391272157431 0.13364790193736553] ]",
|
||||
"[ 43/16 → 11/4 | s:saw note:Eb2 partials:[0.538035349920392 0.4103843830525875 0.6443832442164421 0.7532152868807316 0.32128027081489563 0.29064710810780525 0.5972099266946316 0.8252892810851336] ]",
|
||||
"[ 11/4 → 45/16 | s:saw note:F2 partials:[0.6332327704876661 0.7342763151973486 0.16935866326093674 0.4754706546664238 0.528122790157795 0.21934260986745358 0.7476693484932184 0.2995396424084902] ]",
|
||||
"[ 45/16 → 23/8 | s:saw note:Bb2 partials:[0.8912940509617329 0.5256196465343237 0.25639201514422894 0.46860250271856785 0.08611978217959404 0.6175916157662868 0.990638293325901 0.11189854890108109] ]",
|
||||
"[ 23/8 → 47/16 | s:saw note:F1 partials:[0.01625417172908783 0.28409438766539097 0.3075305689126253 0.40343056432902813 0.5556836389005184 0.6926821582019329 0.5635769125074148 0.017873913049697876] ]",
|
||||
"[ 47/16 → 3/1 | s:saw note:Bb1 partials:[0.2830589488148689 0.3613663297146559 0.6216250211000443 0.6083405166864395 0.27202711440622807 0.771099541336298 0.3156145606189966 0.736228458583355] ]",
|
||||
"[ 3/1 → 49/16 | s:saw note:Ab1 partials:[0.21728911064565182 0.7605195846408606 0.11240843310952187 0.4372697602957487 0.15630115941166878 0.1653979979455471 0.5303416550159454 0.07218753732740879] ]",
|
||||
"[ 49/16 → 25/8 | s:saw note:F1 partials:[0.012518879026174545 0.6565517522394657 0.783835593611002 0.2481316588819027 0.27786846831440926 0.6153149046003819 0.39914070069789886 0.2175555843859911] ]",
|
||||
"[ 25/8 → 51/16 | s:saw note:C2 partials:[0.34324387833476067 0.14098095521330833 0.012934491038322449 0.827436288818717 0.5283997394144535 0.8942463714629412 0.4924008697271347 0.9298017006367445] ]",
|
||||
"[ 51/16 → 13/4 | s:saw note:Bb2 partials:[0.9057559575885534 0.5731389932334423 0.06561482697725296 0.02156665176153183 0.4685337021946907 0.000788738951086998 0.6995994579046965 0.9893404543399811] ]",
|
||||
"[ 13/4 → 53/16 | s:saw note:C3 partials:[0.9923650715500116 0.7797339502722025 0.1992435697466135 0.9005183521658182 0.718992218375206 0.23745290748775005 0.28872333094477654 0.35448253713548183] ]",
|
||||
"[ 53/16 → 27/8 | s:saw note:Bb1 partials:[0.3235324025154114 0.5165992900729179 0.6231792941689491 0.8034896682947874 0.6663950439542532 0.4207208137959242 0.9748435858637094 0.27676148526370525] ]",
|
||||
"[ 27/8 → 55/16 | s:saw note:C2 partials:[0.40869180113077164 0.4018078800290823 0.34696186147630215 0.9572948440909386 0.400035472586751 0.3531211093068123 0.7240961641073227 0.8125612027943134] ]",
|
||||
"[ 55/16 → 7/2 | s:saw note:Ab2 partials:[0.775677714496851 0.0502743236720562 0.3246541116386652 0.503994770348072 0.8252716399729252 0.89872563816607 0.060319963842630386 0.25497629307210445] ]",
|
||||
"[ 7/2 → 57/16 | s:saw note:C2 partials:[0.40994881466031075 0.6455810181796551 0.35704658553004265 0.2732649203389883 0.31587288342416286 0.23243548162281513 0.22569947130978107 0.17241661623120308] ]",
|
||||
"[ 57/16 → 29/8 | s:saw note:Ab2 partials:[0.8133465722203255 0.8327706772834063 0.32914630882442 0.23131784796714783 0.7526696771383286 0.1361286472529173 0.11441296897828579 0.36366880126297474] ]",
|
||||
"[ 29/8 → 59/16 | s:saw note:C3 partials:[0.9391485787928104 0.3380728308111429 0.7723016366362572 0.998674126341939 0.7193406894803047 0.6963680125772953 0.9260678570717573 0.7829763628542423] ]",
|
||||
"[ 59/16 → 15/4 | s:saw note:C3 partials:[0.9731374885886908 0.0494034755975008 0.990720022469759 0.9819309785962105 0.22877203114330769 0.7265191469341516 0.4922411348670721 0.7549834884703159] ]",
|
||||
"[ 15/4 → 61/16 | s:saw note:Ab2 partials:[0.8121673222631216 0.25548945367336273 0.5759696904569864 0.6668333616107702 0.9233786761760712 0.11143362522125244 0.9734930321574211 0.369325939565897] ]",
|
||||
"[ 61/16 → 31/8 | s:saw note:G1 partials:[0.1008925698697567 0.7558664139360189 0.1523460578173399 0.6523381881415844 0.9690635204315186 0.7343240566551685 0.9544064309448004 0.9150135722011328] ]",
|
||||
"[ 31/8 → 63/16 | s:saw note:G2 partials:[0.7361665386706591 0.7657648548483849 0.21474426984786987 0.8228576295077801 0.5647660344839096 0.08148551359772682 0.9714624118059874 0.13992334716022015] ]",
|
||||
"[ 63/16 → 4/1 | s:saw note:Bb1 partials:[0.3056422360241413 0.8881660811603069 0.7683485243469477 0.08641545102000237 0.5427133180201054 0.20803124643862247 0.5347504671663046 0.5977730695158243] ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "range" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/8 | s:hh cutoff:2250 ]",
|
||||
@@ -12898,54 +12240,54 @@ exports[`runs examples > example "wchooseCycles" example index 0 1`] = `
|
||||
|
||||
exports[`runs examples > example "wchooseCycles" example index 1 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/12 | note:c ]",
|
||||
"[ 1/12 → 1/6 | note:c ]",
|
||||
"[ 1/6 → 1/4 | note:c ]",
|
||||
"[ 1/4 → 1/3 | note:c ]",
|
||||
"[ 1/3 → 5/12 | note:c ]",
|
||||
"[ 5/12 → 1/2 | note:c ]",
|
||||
"[ 1/2 → 7/12 | note:f ]",
|
||||
"[ 7/12 → 2/3 | note:f ]",
|
||||
"[ 2/3 → 3/4 | note:f ]",
|
||||
"[ 3/4 → 5/6 | note:c ]",
|
||||
"[ 5/6 → 11/12 | note:c ]",
|
||||
"[ 11/12 → 1/1 | note:c ]",
|
||||
"[ 1/1 → 13/12 | note:c ]",
|
||||
"[ 13/12 → 7/6 | note:c ]",
|
||||
"[ 7/6 → 5/4 | note:c ]",
|
||||
"[ 5/4 → 4/3 | note:c ]",
|
||||
"[ 4/3 → 17/12 | note:c ]",
|
||||
"[ 17/12 → 3/2 | note:c ]",
|
||||
"[ 3/2 → 19/12 | note:a ]",
|
||||
"[ 19/12 → 5/3 | note:a ]",
|
||||
"[ 5/3 → 7/4 | note:a ]",
|
||||
"[ 7/4 → 11/6 | note:c ]",
|
||||
"[ 11/6 → 23/12 | note:c ]",
|
||||
"[ 23/12 → 2/1 | note:c ]",
|
||||
"[ 2/1 → 25/12 | note:a ]",
|
||||
"[ 25/12 → 13/6 | note:a ]",
|
||||
"[ 13/6 → 9/4 | note:a ]",
|
||||
"[ 9/4 → 7/3 | note:a ]",
|
||||
"[ 7/3 → 29/12 | note:a ]",
|
||||
"[ 29/12 → 5/2 | note:a ]",
|
||||
"[ 5/2 → 31/12 | note:c ]",
|
||||
"[ 31/12 → 8/3 | note:c ]",
|
||||
"[ 8/3 → 11/4 | note:c ]",
|
||||
"[ 11/4 → 17/6 | note:c ]",
|
||||
"[ 17/6 → 35/12 | note:c ]",
|
||||
"[ 35/12 → 3/1 | note:c ]",
|
||||
"[ 3/1 → 37/12 | note:c ]",
|
||||
"[ 37/12 → 19/6 | note:c ]",
|
||||
"[ 19/6 → 13/4 | note:c ]",
|
||||
"[ 13/4 → 10/3 | note:f ]",
|
||||
"[ 10/3 → 41/12 | note:f ]",
|
||||
"[ 41/12 → 7/2 | note:f ]",
|
||||
"[ 7/2 → 43/12 | note:a ]",
|
||||
"[ 43/12 → 11/3 | note:a ]",
|
||||
"[ 11/3 → 15/4 | note:a ]",
|
||||
"[ 15/4 → 23/6 | note:c ]",
|
||||
"[ 23/6 → 47/12 | note:c ]",
|
||||
"[ 47/12 → 4/1 | note:c ]",
|
||||
"[ 0/1 → 1/12 | s:bd ]",
|
||||
"[ 1/12 → 1/6 | s:bd ]",
|
||||
"[ 1/6 → 1/4 | s:bd ]",
|
||||
"[ 1/4 → 1/3 | s:bd ]",
|
||||
"[ 1/3 → 5/12 | s:bd ]",
|
||||
"[ 5/12 → 1/2 | s:bd ]",
|
||||
"[ 1/2 → 7/12 | s:sd ]",
|
||||
"[ 7/12 → 2/3 | s:sd ]",
|
||||
"[ 2/3 → 3/4 | s:sd ]",
|
||||
"[ 3/4 → 5/6 | s:bd ]",
|
||||
"[ 5/6 → 11/12 | s:bd ]",
|
||||
"[ 11/12 → 1/1 | s:bd ]",
|
||||
"[ 1/1 → 13/12 | s:bd ]",
|
||||
"[ 13/12 → 7/6 | s:bd ]",
|
||||
"[ 7/6 → 5/4 | s:bd ]",
|
||||
"[ 5/4 → 4/3 | s:bd ]",
|
||||
"[ 4/3 → 17/12 | s:bd ]",
|
||||
"[ 17/12 → 3/2 | s:bd ]",
|
||||
"[ 3/2 → 19/12 | s:hh ]",
|
||||
"[ 19/12 → 5/3 | s:hh ]",
|
||||
"[ 5/3 → 7/4 | s:hh ]",
|
||||
"[ 7/4 → 11/6 | s:bd ]",
|
||||
"[ 11/6 → 23/12 | s:bd ]",
|
||||
"[ 23/12 → 2/1 | s:bd ]",
|
||||
"[ 2/1 → 25/12 | s:hh ]",
|
||||
"[ 25/12 → 13/6 | s:hh ]",
|
||||
"[ 13/6 → 9/4 | s:hh ]",
|
||||
"[ 9/4 → 7/3 | s:hh ]",
|
||||
"[ 7/3 → 29/12 | s:hh ]",
|
||||
"[ 29/12 → 5/2 | s:hh ]",
|
||||
"[ 5/2 → 31/12 | s:bd ]",
|
||||
"[ 31/12 → 8/3 | s:bd ]",
|
||||
"[ 8/3 → 11/4 | s:bd ]",
|
||||
"[ 11/4 → 17/6 | s:bd ]",
|
||||
"[ 17/6 → 35/12 | s:bd ]",
|
||||
"[ 35/12 → 3/1 | s:bd ]",
|
||||
"[ 3/1 → 37/12 | s:bd ]",
|
||||
"[ 37/12 → 19/6 | s:bd ]",
|
||||
"[ 19/6 → 13/4 | s:bd ]",
|
||||
"[ 13/4 → 10/3 | s:sd ]",
|
||||
"[ 10/3 → 41/12 | s:sd ]",
|
||||
"[ 41/12 → 7/2 | s:sd ]",
|
||||
"[ 7/2 → 43/12 | s:hh ]",
|
||||
"[ 43/12 → 11/3 | s:hh ]",
|
||||
"[ 11/3 → 15/4 | s:hh ]",
|
||||
"[ 15/4 → 23/6 | s:bd ]",
|
||||
"[ 23/6 → 47/12 | s:bd ]",
|
||||
"[ 47/12 → 4/1 | s:bd ]",
|
||||
]
|
||||
`;
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
{
|
||||
"name": "dbpatch",
|
||||
"dependencies": {
|
||||
"csv": "^6.3.11"
|
||||
},
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
fetch('https://codeberg.org/api/v1/repos/uzu/strudel/pulls?state=closed&page=1')
|
||||
.then((res) => res.json())
|
||||
.then((pulls) => {
|
||||
const r = pulls
|
||||
.filter((pull) => pull.merged)
|
||||
.sort((a, b) => new Date(b.closed_at) - new Date(a.closed_at))
|
||||
.map((pull) => `${pull.closed_at} ${pull.title} by ${pull.user.login || '?'} in: [#${pull.number}](${pull.url}) `)
|
||||
.join('\n');
|
||||
console.log(r);
|
||||
});
|
||||
|
||||
/*
|
||||
|
||||
*/
|
||||
@@ -48,94 +48,28 @@ You can also use the `crackle` type to play some subtle noise crackles. You can
|
||||
|
||||
### Additive Synthesis
|
||||
|
||||
Periodic waveforms are composed of several [harmonics](https://en.wikipedia.org/wiki/Harmonic) above a fundamental frequency, lying at integer multiples. These overtones combine to give a sound its unique timbral quality.
|
||||
|
||||
For the basic waveforms, we offer you control over these harmonics with the `partials` and `phases` functions.
|
||||
|
||||
#### Partials
|
||||
|
||||
`partials` refers to the magnitude of each harmonic relative to the fundamental frequency. They can thus be used to spectrally filter these waveforms and tame some of their harshness:
|
||||
To tame the harsh sound of the basic waveforms, we can set the `n` control to limit the overtones of the waveform:
|
||||
|
||||
<MiniRepl
|
||||
client:idle
|
||||
tune={`note("c2 <eb2 <g2 g1>>".fast(2))
|
||||
.sound("sawtooth")
|
||||
.partials([1, 1, "<1 0>", "<1 0>", "<1 0>", "<1 0>", "<1 0>"])
|
||||
.n("<32 16 8 4>")
|
||||
._scope()`}
|
||||
/>
|
||||
|
||||
`partials` can also be used to construct _new_ waveforms not present in our basic set with the 'user' sound source:
|
||||
When the `n` control is used on a basic waveform, it defines the number of harmonic partials the sound is getting.
|
||||
You can also set `n` directly in mini notation with `sound`:
|
||||
|
||||
<MiniRepl
|
||||
client:idle
|
||||
tune={`note("c2 <eb2 <g2 g1>>".fast(2))
|
||||
.sound("user")
|
||||
.partials([1, 0, 0.3, 0, 0.1, 0, 0, 0.3])
|
||||
.sound("sawtooth:<32 16 8 4>")
|
||||
._scope()`}
|
||||
/>
|
||||
|
||||
We may algorithmically construct lists of magnitudes with Javascript code like:
|
||||
|
||||
<MiniRepl
|
||||
client:idle
|
||||
tune={`const numHarmonics = 22;
|
||||
note("c2 <eb2 <g2 g1>>".fast(2))
|
||||
.sound("saw")
|
||||
.partials(new Array(numHarmonics).fill(1))
|
||||
._scope()`}
|
||||
/>
|
||||
|
||||
which acts as a spectral filter. Or:
|
||||
|
||||
<MiniRepl
|
||||
client:idle
|
||||
tune={`note("c2 <eb2 <g2 g1>>").fast(2)
|
||||
.sound("user")
|
||||
.partials(new Array(50).fill(0)
|
||||
.map((_, idx) => ((-1) ** (idx + 1)) / (idx + 1))
|
||||
)
|
||||
._scope()`}
|
||||
/>
|
||||
|
||||
which recovers a familiar waveform.
|
||||
|
||||
`partials` is also compatible with pattern functions designed to produce lists, like `randL` or `binaryL`:
|
||||
|
||||
<MiniRepl
|
||||
client:idle
|
||||
tune={`note("c2 <eb2 <g2 g1>>").fast(2)
|
||||
.sound("user")
|
||||
.partials(randL(10))
|
||||
._scope()`}
|
||||
/>
|
||||
|
||||
and with lists _of_ patterns:
|
||||
|
||||
<MiniRepl
|
||||
client:idle
|
||||
tune={`note("c2 <eb2 <g2 g1>>".fast(4))
|
||||
.sound("user")
|
||||
.partials([1, 0, "0 1", "0 1 0.3", rand])
|
||||
._scope()`}
|
||||
/>
|
||||
|
||||
Note that the first value in the `partials` array controls the magnitude of the fundamental harmonic rather than the DC offset, which is fixed at 0.
|
||||
|
||||
#### Phases
|
||||
|
||||
Earlier, we mentioned that periodic waveforms can be broken into a set of harmonics above a fundamental frequency. Each harmonic has two defining properties: its magnitude (how loud it is) and its phase, which determines where in its cycle that sine wave starts when the waveform is built.
|
||||
|
||||
These phases too can be declared in Strudel and can give your sounds interesting depth.
|
||||
|
||||
<MiniRepl
|
||||
client:idle
|
||||
tune={`s("saw").seg(16).n(irand(12)).scale("F1:minor")
|
||||
.penv(48).panchor(0).pdec(0.05)
|
||||
.delay(0.25).room(0.25)
|
||||
.compressor(-20).vib(0.3)
|
||||
.partials(randL(200))
|
||||
.phases(randL(200))`}
|
||||
/>
|
||||
Note for tidal users: `n` in tidal is synonymous to `note` for synths only.
|
||||
In strudel, this is not the case, where `n` will always change timbre, be it though different samples or different waveforms.
|
||||
|
||||
## Vibrato
|
||||
|
||||
|
||||
@@ -8,34 +8,3 @@ export function ActionButton({ children, label, labelIsHidden, className, ...but
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
export function SpecialActionButton(props) {
|
||||
const { className, ...buttonProps } = props;
|
||||
|
||||
return (
|
||||
<ActionButton
|
||||
{...buttonProps}
|
||||
className={cx('bg-background p-2 max-w-[300px] rounded-md hover:opacity-50', className)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function ActionInput({ label, className, ...props }) {
|
||||
return (
|
||||
<label className={cx('inline-flex items-center cursor-pointer', className)}>
|
||||
<input {...props} className="sr-only peer" />
|
||||
|
||||
<span className="inline-flex items-center peer-hover:opacity-50">{label}</span>
|
||||
</label>
|
||||
);
|
||||
}
|
||||
|
||||
export function SpecialActionInput({ className, ...props }) {
|
||||
return (
|
||||
<ActionInput
|
||||
{...props}
|
||||
className={className}
|
||||
label={<span className="bg-background p-2 max-w-[300px] rounded-md">{props.label}</span>}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
import { errorLogger } from '@strudel/core';
|
||||
import { useSettings, storePrebakeScript } from '../../../settings.mjs';
|
||||
import { SpecialActionInput } from '../button/action-button';
|
||||
|
||||
async function importScript(script) {
|
||||
const reader = new FileReader();
|
||||
reader.readAsText(script);
|
||||
|
||||
reader.onload = () => {
|
||||
const text = reader.result;
|
||||
storePrebakeScript(text);
|
||||
};
|
||||
|
||||
reader.onerror = () => {
|
||||
errorLogger(new Error('failed to import prebake script'), 'importScript');
|
||||
};
|
||||
}
|
||||
export function ImportPrebakeScriptButton() {
|
||||
const settings = useSettings();
|
||||
|
||||
return (
|
||||
<SpecialActionInput
|
||||
type="file"
|
||||
label="import prebake script"
|
||||
accept=".strudel"
|
||||
onChange={(e) => importScript(e.target.files[0])}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -7,8 +7,6 @@ import { AudioDeviceSelector } from './AudioDeviceSelector.jsx';
|
||||
import { AudioEngineTargetSelector } from './AudioEngineTargetSelector.jsx';
|
||||
import { confirmDialog } from '../../util.mjs';
|
||||
import { DEFAULT_MAX_POLYPHONY, setMaxPolyphony, setMultiChannelOrbits } from '@strudel/webaudio';
|
||||
import { ActionButton, SpecialActionButton } from '../button/action-button.jsx';
|
||||
import { ImportPrebakeScriptButton } from './ImportPrebakeScriptButton.jsx';
|
||||
|
||||
function Checkbox({ label, value, onChange, disabled = false }) {
|
||||
return (
|
||||
@@ -115,7 +113,6 @@ export function SettingsTab({ started }) {
|
||||
isTabIndentationEnabled,
|
||||
isMultiCursorEnabled,
|
||||
patternAutoStart,
|
||||
includePrebakeScriptInShare,
|
||||
} = useSettings();
|
||||
const shouldAlwaysSync = isUdels();
|
||||
const canChangeAudioDevice = AudioContext.prototype.setSinkId != null;
|
||||
@@ -207,15 +204,6 @@ export function SettingsTab({ started }) {
|
||||
/>
|
||||
</FormItem>
|
||||
</div>
|
||||
<FormItem label="Prebake">
|
||||
<ImportPrebakeScriptButton />
|
||||
<Checkbox
|
||||
label="Include prebake script in share"
|
||||
onChange={(cbEvent) => settingsMap.setKey('includePrebakeScriptInShare', cbEvent.target.checked)}
|
||||
value={includePrebakeScriptInShare}
|
||||
/>
|
||||
</FormItem>
|
||||
|
||||
<FormItem label="Keybindings">
|
||||
<ButtonGroup
|
||||
value={keybindings}
|
||||
@@ -325,7 +313,8 @@ export function SettingsTab({ started }) {
|
||||
</FormItem>
|
||||
<FormItem label="Zen Mode">Try clicking the logo in the top left!</FormItem>
|
||||
<FormItem label="Reset Settings">
|
||||
<SpecialActionButton
|
||||
<button
|
||||
className="bg-background p-2 max-w-[300px] rounded-md hover:opacity-50"
|
||||
onClick={() => {
|
||||
confirmDialog('Sure?').then((r) => {
|
||||
if (r) {
|
||||
@@ -336,7 +325,7 @@ export function SettingsTab({ started }) {
|
||||
}}
|
||||
>
|
||||
restore default settings
|
||||
</SpecialActionButton>
|
||||
</button>
|
||||
</FormItem>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -14,10 +14,6 @@ import { prebake } from '@src/repl/prebake.mjs';
|
||||
const getSamples = (samples) =>
|
||||
Array.isArray(samples) ? samples.length : typeof samples === 'object' ? Object.values(samples).length : 1;
|
||||
|
||||
function wait(ms) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
export function SoundsTab() {
|
||||
const sounds = useStore(soundMap);
|
||||
|
||||
@@ -60,7 +56,9 @@ export function SoundsTab() {
|
||||
|
||||
// holds mutable ref to current triggered sound
|
||||
const trigRef = useRef();
|
||||
const numRef = useRef(0);
|
||||
|
||||
// Used to cycle through sound previews on banks with multiple sounds
|
||||
let soundPreviewIdx = 0;
|
||||
|
||||
// stop current sound on mouseup
|
||||
useEvent('mouseup', () => {
|
||||
@@ -68,14 +66,6 @@ export function SoundsTab() {
|
||||
trigRef.current = undefined;
|
||||
ref?.stop?.(getAudioContext().currentTime + 0.01);
|
||||
});
|
||||
useEvent('keydown', (e) => {
|
||||
if (!isNaN(Number(e.key))) {
|
||||
numRef.current = Number(e.key);
|
||||
}
|
||||
});
|
||||
useEvent('keyup', (e) => {
|
||||
numRef.current = 0;
|
||||
});
|
||||
return (
|
||||
<div id="sounds-tab" className="px-4 flex gap-2 flex-col w-full h-full text-foreground">
|
||||
<Textbox placeholder="Search" value={search} onChange={(v) => setSearch(v)} />
|
||||
@@ -125,34 +115,25 @@ export function SoundsTab() {
|
||||
const params = {
|
||||
note: ['synth', 'soundfont'].includes(data.type) ? 'a3' : undefined,
|
||||
s: name,
|
||||
n: numRef.current,
|
||||
n: soundPreviewIdx,
|
||||
clip: 1,
|
||||
release: 0.5,
|
||||
sustain: 1,
|
||||
duration: 0.5,
|
||||
};
|
||||
soundPreviewIdx++;
|
||||
const onended = () => trigRef.current?.node?.disconnect();
|
||||
// Attempt to play the sample and retry every 200ms until 10 attempts have been reached
|
||||
let errMsg;
|
||||
for (let attempt = 0; attempt < 10; attempt++) {
|
||||
try {
|
||||
// Pre-load the sample by calling onTrigger with a future time
|
||||
// This triggers the loading but schedules playback for later
|
||||
const time = ctx.currentTime + 0.05; // Give 50ms for loading
|
||||
const ref = await onTrigger(time, params, onended);
|
||||
trigRef.current = ref;
|
||||
if (ref?.node) {
|
||||
connectToDestination(ref.node);
|
||||
break;
|
||||
}
|
||||
} catch (err) {
|
||||
errMsg = err;
|
||||
}
|
||||
if (attempt == 9) {
|
||||
console.warn('Failed to trigger sound after 10 attempts' + (errMsg ? `: ${errMsg}` : ''));
|
||||
} else {
|
||||
await wait(200);
|
||||
try {
|
||||
// Pre-load the sample by calling onTrigger with a future time
|
||||
// This triggers the loading but schedules playback for later
|
||||
const time = ctx.currentTime + 0.5; // Give 500ms for loading
|
||||
const ref = await onTrigger(time, params, onended);
|
||||
trigRef.current = ref;
|
||||
if (ref?.node) {
|
||||
connectToDestination(ref.node);
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('Failed to trigger sound:', err);
|
||||
}
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -3,8 +3,6 @@ import { aliasBank, registerSynthSounds, registerZZFXSounds, samples } from '@st
|
||||
import { registerSamplesFromDB } from './idbutils.mjs';
|
||||
import './piano.mjs';
|
||||
import './files.mjs';
|
||||
import { settingsMap } from '@src/settings.mjs';
|
||||
import { evaluate } from '@strudel/transpiler';
|
||||
|
||||
const { BASE_URL } = import.meta.env;
|
||||
const baseNoTrailing = BASE_URL.endsWith('/') ? BASE_URL.slice(0, -1) : BASE_URL;
|
||||
|
||||
@@ -6,7 +6,7 @@ This program is free software: you can redistribute it and/or modify it under th
|
||||
|
||||
import { code2hash, getPerformanceTimeSeconds, logger, silence } from '@strudel/core';
|
||||
import { getDrawContext } from '@strudel/draw';
|
||||
import { evaluate, transpiler } from '@strudel/transpiler';
|
||||
import { transpiler } from '@strudel/transpiler';
|
||||
import {
|
||||
getAudioContextCurrentTime,
|
||||
webaudioOutput,
|
||||
@@ -63,10 +63,11 @@ async function getModule(name) {
|
||||
const initialCode = `// LOADING`;
|
||||
|
||||
export function useReplContext() {
|
||||
const { isSyncEnabled, audioEngineTarget, prebakeScript, includePrebakeScriptInShare } = useSettings();
|
||||
const { isSyncEnabled, audioEngineTarget } = useSettings();
|
||||
const shouldUseWebaudio = audioEngineTarget !== audioEngineTargets.osc;
|
||||
const defaultOutput = shouldUseWebaudio ? webaudioOutput : superdirtOutput;
|
||||
const getTime = shouldUseWebaudio ? getAudioContextCurrentTime : getPerformanceTimeSeconds;
|
||||
|
||||
const init = useCallback(() => {
|
||||
const drawTime = [-2, 2];
|
||||
const drawContext = getDrawContext();
|
||||
@@ -83,12 +84,7 @@ export function useReplContext() {
|
||||
pattern: silence,
|
||||
drawTime,
|
||||
drawContext,
|
||||
prebake: async () =>
|
||||
Promise.all([modulesLoading, presets]).then(() => {
|
||||
if (prebakeScript?.length) {
|
||||
return evaluate(prebakeScript ?? '');
|
||||
}
|
||||
}),
|
||||
prebake: async () => Promise.all([modulesLoading, presets]),
|
||||
onUpdateState: (state) => {
|
||||
setReplState({ ...state });
|
||||
},
|
||||
@@ -218,13 +214,7 @@ export function useReplContext() {
|
||||
editorRef.current.repl.evaluate(code);
|
||||
};
|
||||
|
||||
const handleShare = async () => {
|
||||
let code = replState.code;
|
||||
if (includePrebakeScriptInShare) {
|
||||
code = prebakeScript + '\n' + code;
|
||||
}
|
||||
shareCode(code);
|
||||
};
|
||||
const handleShare = async () => shareCode(replState.code);
|
||||
const context = {
|
||||
started,
|
||||
pending,
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { code2hash, evalScope, hash2code, logger } from '@strudel/core';
|
||||
import { evalScope, hash2code, logger } from '@strudel/core';
|
||||
import { settingPatterns } from '../settings.mjs';
|
||||
import { setVersionDefaults } from '@strudel/webaudio';
|
||||
import { getMetadata } from '../metadata_parser';
|
||||
import { isTauri } from '../tauri.mjs';
|
||||
import './Repl.css';
|
||||
import { createClient } from '@supabase/supabase-js';
|
||||
import { nanoid } from 'nanoid';
|
||||
import { writeText } from '@tauri-apps/plugin-clipboard-manager';
|
||||
import { $featuredPatterns /* , loadDBPatterns */ } from '@src/user_pattern_utils.mjs';
|
||||
|
||||
@@ -108,8 +109,9 @@ export function confirmDialog(msg) {
|
||||
});
|
||||
}
|
||||
|
||||
let lastShared;
|
||||
|
||||
//RIP due to SPAM
|
||||
// let lastShared;
|
||||
// export async function shareCode(codeToShare) {
|
||||
// // const codeToShare = activeCode || code;
|
||||
// if (lastShared === codeToShare) {
|
||||
@@ -144,10 +146,9 @@ export function confirmDialog(msg) {
|
||||
// });
|
||||
// }
|
||||
|
||||
export async function shareCode(codeToShare) {
|
||||
export async function shareCode() {
|
||||
try {
|
||||
const hash = '#' + code2hash(codeToShare);
|
||||
const shareUrl = window.location.origin + window.location.pathname + hash;
|
||||
const shareUrl = window.location.href;
|
||||
if (isTauri()) {
|
||||
await writeText(shareUrl);
|
||||
} else {
|
||||
|
||||
@@ -45,13 +45,11 @@ export const defaultSettings = {
|
||||
isPanelOpen: true,
|
||||
togglePanelTrigger: 'click', //click | hover
|
||||
userPatterns: '{}',
|
||||
prebakeScript: '',
|
||||
audioEngineTarget: audioEngineTargets.webaudio,
|
||||
isButtonRowHidden: false,
|
||||
isCSSAnimationDisabled: false,
|
||||
maxPolyphony: 128,
|
||||
multiChannelOrbits: false,
|
||||
includePrebakeScriptInShare: true,
|
||||
};
|
||||
|
||||
let search = null;
|
||||
@@ -98,7 +96,6 @@ export function useSettings() {
|
||||
isPanelOpen: parseBoolean(state.isPanelOpen),
|
||||
userPatterns: userPatterns,
|
||||
multiChannelOrbits: parseBoolean(state.multiChannelOrbits),
|
||||
includePrebakeScriptInShare: parseBoolean(state.includePrebakeScriptInShare),
|
||||
patternAutoStart: isUdels()
|
||||
? false
|
||||
: state.patternAutoStart === undefined
|
||||
@@ -111,8 +108,6 @@ export const setActiveFooter = (tab) => settingsMap.setKey('activeFooter', tab);
|
||||
export const setPanelPinned = (bool) => settingsMap.setKey('isPanelPinned', bool);
|
||||
export const setIsPanelOpened = (bool) => settingsMap.setKey('isPanelOpen', bool);
|
||||
|
||||
export const storePrebakeScript = (script) => settingsMap.setKey('prebakeScript', script);
|
||||
|
||||
export const setIsZen = (active) => settingsMap.setKey('isZen', !!active);
|
||||
|
||||
const patternSetting = (key) =>
|
||||
|
||||
Reference in New Issue
Block a user