mirror of
https://codeberg.org/uzu/strudel
synced 2026-07-24 05:57:17 -04:00
Merge branch 'main' into ableton-link
This commit is contained in:
@@ -15,3 +15,4 @@ vite.config.js
|
||||
!**/*.mjs
|
||||
**/*.tsx
|
||||
**/*.ts
|
||||
**/*.json
|
||||
+3
-1
@@ -37,4 +37,6 @@ talk/public/EmuSP12
|
||||
talk/public/samples
|
||||
server/samples/old
|
||||
repl/stats.html
|
||||
coverage
|
||||
coverage
|
||||
public/icons/apple-splash-*
|
||||
dev-dist
|
||||
@@ -7,6 +7,7 @@ An experiment in making a [Tidal](https://github.com/tidalcycles/tidal/) using w
|
||||
- Try it here: <https://strudel.tidalcycles.org/>
|
||||
- Docs: <https://strudel.tidalcycles.org/learn/>
|
||||
- Technical Blog Post: <https://loophole-letters.vercel.app/strudel>
|
||||
- 1 Year of Strudel Blog Post: <https://loophole-letters.vercel.app/strudel1year>
|
||||
|
||||
## Running Locally
|
||||
|
||||
|
||||
+45
-41
@@ -83,8 +83,7 @@ const generic_params = [
|
||||
*/
|
||||
['f', 'amp', 'like @gain@, but linear.'],
|
||||
/**
|
||||
* A pattern of numbers to specify the attack time of an envelope applied to each sample.
|
||||
* [More info about envelopes](/learn/synths-samples-effects/#envelope)
|
||||
* Amplitude envelope attack time: Specifies how long it takes for the sound to reach its peak value, relative to the onset.
|
||||
*
|
||||
* @name attack
|
||||
* @param {number | Pattern} attack time in seconds.
|
||||
@@ -92,11 +91,7 @@ const generic_params = [
|
||||
* note("c3 e3").attack("<0 .1 .5>")
|
||||
*
|
||||
*/
|
||||
[
|
||||
'f',
|
||||
'attack',
|
||||
'a pattern of numbers to specify the attack time (in seconds) of an envelope applied to each sample.',
|
||||
],
|
||||
['f', 'attack'],
|
||||
|
||||
/**
|
||||
* Select the sound bank to use. To be used together with `s`. The bank name (+ "_") will be prepended to the value of `s`.
|
||||
@@ -110,8 +105,8 @@ const generic_params = [
|
||||
['f', 'bank', 'selects sound bank to use'],
|
||||
|
||||
/**
|
||||
* Gain envelope decay time = the time it takes after the attack time to reach the sustain level.
|
||||
* [More info about envelopes](/learn/synths-samples-effects/#envelope)
|
||||
* Amplitude envelope decay time: the time it takes after the attack time to reach the sustain level.
|
||||
* Note that the decay is only audible if the sustain value is lower than 1.
|
||||
*
|
||||
* @name decay
|
||||
* @param {number | Pattern} time decay time in seconds
|
||||
@@ -121,7 +116,7 @@ const generic_params = [
|
||||
*/
|
||||
['f', 'decay', ''],
|
||||
/**
|
||||
* Gain envelope sustain level. [More info about envelopes](/learn/synths-samples-effects/#envelope)
|
||||
* Amplitude envelope sustain level: The level which is reached after attack / decay, being sustained until the offset.
|
||||
*
|
||||
* @name sustain
|
||||
* @param {number | Pattern} gain sustain level between 0 and 1
|
||||
@@ -131,7 +126,7 @@ const generic_params = [
|
||||
*/
|
||||
['f', 'sustain', ''],
|
||||
/**
|
||||
* Gain envelope release time. [More info about envelopes](/learn/synths-samples-effects/#envelope)
|
||||
* Amplitude envelope release time: The time it takes after the offset to go from sustain level to zero.
|
||||
*
|
||||
* @name release
|
||||
* @param {number | Pattern} time release time in seconds
|
||||
@@ -151,30 +146,30 @@ const generic_params = [
|
||||
],
|
||||
// TODO: in tidal, it seems to be normalized
|
||||
/**
|
||||
* Sets the center frequency of the band-pass filter.
|
||||
* Sets the center frequency of the **b**and-**p**ass **f**ilter.
|
||||
*
|
||||
* @name bandf
|
||||
* @name bpf
|
||||
* @param {number | Pattern} frequency center frequency
|
||||
* @synonyms bpf
|
||||
* @synonyms bandf
|
||||
* @example
|
||||
* s("bd sd,hh*3").bandf("<1000 2000 4000 8000>")
|
||||
* s("bd sd,hh*3").bpf("<1000 2000 4000 8000>")
|
||||
*
|
||||
*/
|
||||
['f', 'bandf', 'A pattern of numbers from 0 to 1. Sets the center frequency of the band-pass filter.'],
|
||||
['f', 'bpf', ''],
|
||||
['f', 'bandf', 'A pattern of numbers from 0 to 1. Sets the center frequency of the band-pass filter.'],
|
||||
// TODO: in tidal, it seems to be normalized
|
||||
/**
|
||||
* Sets the q-factor of the band-pass filter
|
||||
* Sets the **b**and-**p**ass **q**-factor (resonance)
|
||||
*
|
||||
* @name bandq
|
||||
* @name bpq
|
||||
* @param {number | Pattern} q q factor
|
||||
* @synonyms bpq
|
||||
* @synonyms bandq
|
||||
* @example
|
||||
* s("bd sd").bandf(500).bandq("<0 1 2 3>")
|
||||
* s("bd sd").bpf(500).bpq("<0 1 2 3>")
|
||||
*
|
||||
*/
|
||||
['f', 'bandq', 'a pattern of anumbers from 0 to 1. Sets the q-factor of the band-pass filter.'],
|
||||
['f', 'bpq', ''],
|
||||
['f', 'bandq', 'a pattern of anumbers from 0 to 1. Sets the q-factor of the band-pass filter.'],
|
||||
/**
|
||||
* a pattern of numbers from 0 to 1. Skips the beginning of each sample, e.g. `0.25` to cut off the first quarter from each sample.
|
||||
*
|
||||
@@ -282,50 +277,49 @@ const generic_params = [
|
||||
'In the style of classic drum-machines, `cut` will stop a playing sample as soon as another samples with in same cutgroup is to be played. An example would be an open hi-hat followed by a closed one, essentially muting the open.',
|
||||
],
|
||||
/**
|
||||
* Applies the cutoff frequency of the low-pass filter.
|
||||
* Applies the cutoff frequency of the **l**ow-**p**ass **f**ilter.
|
||||
*
|
||||
* @name cutoff
|
||||
* @name lpf
|
||||
* @param {number | Pattern} frequency audible between 0 and 20000
|
||||
* @synonyms lpf
|
||||
* @synonyms cutoff
|
||||
* @example
|
||||
* s("bd sd,hh*3").cutoff("<4000 2000 1000 500 200 100>")
|
||||
* s("bd sd,hh*3").lpf("<4000 2000 1000 500 200 100>")
|
||||
*
|
||||
*/
|
||||
['f', 'cutoff', 'a pattern of numbers from 0 to 1. Applies the cutoff frequency of the low-pass filter.'],
|
||||
['f', 'lpf'],
|
||||
['f', 'cutoff', 'a pattern of numbers from 0 to 1. Applies the cutoff frequency of the low-pass filter.'],
|
||||
/**
|
||||
* Applies the cutoff frequency of the high-pass filter.
|
||||
* Applies the cutoff frequency of the **h**igh-**p**ass **f**ilter.
|
||||
*
|
||||
* @name hcutoff
|
||||
* @name hpf
|
||||
* @param {number | Pattern} frequency audible between 0 and 20000
|
||||
* @synonyms hpf
|
||||
* @synonyms hcutoff
|
||||
* @example
|
||||
* s("bd sd,hh*4").hcutoff("<4000 2000 1000 500 200 100>")
|
||||
* s("bd sd,hh*4").hpf("<4000 2000 1000 500 200 100>")
|
||||
*
|
||||
*/
|
||||
['f', 'hcutoff', ''],
|
||||
['f', 'hpf', ''],
|
||||
['f', 'hcutoff', ''],
|
||||
/**
|
||||
* Applies the resonance of the high-pass filter.
|
||||
* Controls the **h**igh-**p**ass **q**-value.
|
||||
*
|
||||
* @name hresonance
|
||||
* @name hpq
|
||||
* @param {number | Pattern} q resonance factor between 0 and 50
|
||||
* @synonyms hpq
|
||||
* @synonyms hresonance
|
||||
* @example
|
||||
* s("bd sd,hh*4").hcutoff(2000).hresonance("<0 10 20 30>")
|
||||
* s("bd sd,hh*4").hpf(2000).hpq("<0 10 20 30>")
|
||||
*
|
||||
*/
|
||||
['f', 'hpq', ''],
|
||||
['f', 'hresonance', ''],
|
||||
// TODO: add hpq synonym
|
||||
['f', 'hpq', ''],
|
||||
/**
|
||||
* Applies the cutoff frequency of the low-pass filter.
|
||||
* Controls the **l**ow-**p**ass **q**-value.
|
||||
*
|
||||
* @name resonance
|
||||
* @name lpq
|
||||
* @param {number | Pattern} q resonance factor between 0 and 50
|
||||
* @synonyms lpq
|
||||
* @synonyms resonance
|
||||
* @example
|
||||
* s("bd sd,hh*4").cutoff(2000).resonance("<0 10 20 30>")
|
||||
* s("bd sd,hh*4").lpf(2000).lpq("<0 10 20 30>")
|
||||
*
|
||||
*/
|
||||
['f', 'lpq'],
|
||||
@@ -804,6 +798,16 @@ const generic_params = [
|
||||
['f', 'uid', ''],
|
||||
['f', 'val', ''],
|
||||
['f', 'cps', ''],
|
||||
/**
|
||||
* If set to 1, samples will be cut to the duration of their event.
|
||||
* In tidal, this would be done with legato, which [is about to land in strudel too](https://github.com/tidalcycles/strudel/issues/111)
|
||||
*
|
||||
* @name clip
|
||||
* @param {number | Pattern} active 1 or 0
|
||||
* @example
|
||||
* note("c a f e ~").s("piano").clip(1)
|
||||
*
|
||||
*/
|
||||
['f', 'clip', ''],
|
||||
];
|
||||
|
||||
|
||||
@@ -92,6 +92,7 @@ async function load() {
|
||||
['message'].forEach((k) => _csound.on(k, (...args) => eventLogger(k, args)));
|
||||
await _csound.setOption('-m0d'); // see -m flag https://csound.com/docs/manual/CommandFlags.html
|
||||
await _csound.setOption('--sample-accurate');
|
||||
await _csound.setOption('-odac');
|
||||
await _csound.compileCsdText(csd);
|
||||
// await _csound.compileOrc(livecodeOrc);
|
||||
await _csound.compileOrc(presetsOrc);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/csound",
|
||||
"version": "0.6.0",
|
||||
"version": "0.6.2",
|
||||
"description": "csound bindings for strudel",
|
||||
"main": "index.mjs",
|
||||
"publishConfig": {
|
||||
@@ -32,7 +32,7 @@
|
||||
},
|
||||
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
||||
"dependencies": {
|
||||
"@csound/browser": "^6.18.3",
|
||||
"@csound/browser": "6.18.5",
|
||||
"@strudel.cycles/core": "workspace:*",
|
||||
"@strudel.cycles/webaudio": "workspace:*"
|
||||
},
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
**Submission ID:** 8
|
||||
|
||||
**Title:** Strudel: live coding patterns on the Web
|
||||
|
||||
**Status:** Accept
|
||||
|
||||
**Reviewer 1:**
|
||||
|
||||
Author Comments: * Summary of the contribution:\
|
||||
This paper describes Strudel, a Web-based implementation of Tidal Cycles. It is generally a good overview of the system which could help prospective users understand how it works beyond the manual, and it provides many code examples. The most interesting contribution is the comparison between the Haskell and JavaScript features for this case.
|
||||
|
||||
* On theme (1-5):\
|
||||
1
|
||||
|
||||
* Citation quality (1-5):\
|
||||
5
|
||||
|
||||
* Missing references:\
|
||||
not answered
|
||||
|
||||
* Suggested improvements:\
|
||||
This paper is a good introduction to Strudel. The style is mostly illustrative, but it is hard to understand where the Tidal syntax is being introduced vs where the specific Strudel features are used. Thus, the best way to improve it would be to focus on Strudel. Also, it would be good to give a better hint on what output method would be preferable. Also the title of Section 9 should compare Strudel to Tidal or Haskell to JavaScript.
|
||||
|
||||
The paper is driven by examples, but many are a bit difficult to follow, here are some issues:\
|
||||
Section 4 - the mini-notation example does not look terser despite the text.\
|
||||
Section 5 - echo funciton is not used.\
|
||||
Sections 7.11 and 7.12 - the same (mini-notation) example is used so it is not clear what relation there is between transpilation and using mini-notation. Also please reference peggy\
|
||||
Sectin 7.3.2 The sawtooth waveform has been used in all examples, so it would be clearer if it was used here (maybe cutoff as well).
|
||||
|
||||
Generally, beyond the language differences, what does Strudel contribute with respect to Haskell-based Tidal? I would say that there is a lot to be gained in terms of installation, availability and potential for collaboration.
|
||||
|
||||
* Clarity (1-5):\
|
||||
4
|
||||
|
||||
* Corrections required:\
|
||||
No contentious / incorrect claims
|
||||
|
||||
* Other Changes if Published:\
|
||||
not answered
|
||||
|
||||
* Correct Template used?\
|
||||
Yes
|
||||
|
||||
* All materials provided?\
|
||||
Yes
|
||||
|
||||
* Wordcount respected?\
|
||||
Yes
|
||||
|
||||
* Have any deviations, errors or omissions impacted your ability to give a fair review?\
|
||||
No
|
||||
|
||||
**Reviewer 2:**
|
||||
|
||||
Author Comments: * Summary of the contribution:\
|
||||
This proposal presents Strudel, an online, JavaScript-native approach to algorithmic pattern composition that in many ways mirrors TidalCycles. TidalCycles, developed by one of the authors, is a key software piece for live coding. Perhaps the most difficult aspects of Tidal are related to Haskell and Haskell's dependency system that makes it very difficult to install. Haskell in itself is a difficult language to grasp; if Tidal users would like to expand and 'personalise' the possibilities of the software, they would have to get familiar with the particularities of Haskell. JavaScript is a multi-paradigm language that is used for web development can make pattern-based, algorithmic composition more popular. The process of bringing Tidal's paradigm to a dynamic type system seems to be an important part of this research. I enjoy and find very interesting the dialectical process that occurs between Strudel and Tidal, where the implementation of Tidal concepts in Strudel simultaneously transforms Tidal, either by introspection (finding bugs, etc.) or by bringing back to Tidal new insights that are clear in JavaScript. I think moving to TypeScript makes sense; perhaps a small section where the decision to use JavaScript is discussed a little further could be helpful for readers (beyond accessibility). Overall, this paper is clear and presents software that is a key development for live coding and will be a great addition to the next ICLC.
|
||||
|
||||
* On theme (1-5):\
|
||||
4
|
||||
|
||||
* Citation quality (1-5):\
|
||||
5
|
||||
|
||||
* Missing references:\
|
||||
not answered
|
||||
|
||||
* Suggested improvements:\
|
||||
Tidal-Strudel parity seems to be on the immediate horizon, but I wonder if this parity is transient and if the ultimate goal of Strudel is to ultimately diverge from Tidal. Perhaps the project is in its early stages, and this is not clear, but it would be interesting to get some insights on the authors' intentions. Personally, I think that a key discussion that needs to happen in live coding communities and development nodes is that of what is understood as multilingual live coding. Tidal's paradigm is implemented in a number of programming languages (as well as platforms such as Estuary), each with its own set of features and challenges: some are functional reactive programming, type strict oriented, while others are multi-paradigm, dynamic type oriented, etc. This seems to suggest that it is possible for many programming languages to adopt one single understanding of how to make music. Perhaps further considerations should be made as to whether we need one single grammar to name and express all music structures or whether we need environments and software that help difference to proliferate in terms of music/sound art/art idiosyncrasies. Perhaps questions in this direction are beyond the scope of this paper and I hope this comment is taken as productive feedback for the authors.
|
||||
|
||||
* Clarity (1-5):\
|
||||
4
|
||||
|
||||
* Corrections required:\
|
||||
Nothing contentious or incorrect to note.
|
||||
|
||||
* Other Changes if Published:\
|
||||
The example shown on page 3 under "Pattern Example" seems to be different from its explanation. The explanations do not mention scale but mention echo, which is not present in the example. I would double check all the code examples inc ase I missed something like this.
|
||||
|
||||
* Correct Template used?\
|
||||
Yes
|
||||
|
||||
* All materials provided?\
|
||||
Yes
|
||||
|
||||
* Wordcount respected?\
|
||||
Yes
|
||||
|
||||
* Have any deviations, errors or omissions impacted your ability to give a fair review?\
|
||||
no
|
||||
|
||||
* General remarks:\
|
||||
This proposal presents Strudel, an online, JavaScript-native approach to algorithmic pattern composition that in many ways mirrors TidalCycles. TidalCycles, developed by one of the authors, is a key software piece for live coding. Perhaps the most difficult aspects of Tidal are related to Haskell and Haskell's dependency system that makes it very difficult to install. Haskell in itself is a difficult language to grasp; if Tidal users would like to expand and 'personalise' the possibilities of the software, they would have to get familiar with the particularities of Haskell. JavaScript is a multi-paradigm language that can make pattern-based, algorithmic composition more popular. The process of bringing Tidal's paradigm to a dynamic type system seems to be an important part of this research. I enjoy and find very interesting the dialectical process that occurs between Strudel and Tidal, where the implementation of Tidal concepts in Strudel simultaneously transforms Tidal, either by introspection (finding bugs, etc.) or by bringing back to Tidal new insights that are clear in JavaScript. I think moving to TypeScript makes sense; perhaps a small section where the decision to use JavaScript is discussed a little further could be helpful for readers (beyond accessibility). I am curious, did the authors consider PureScript? I can see how, in terms of popularity, PureScript is a lateral move coming from Haskell, but an explicit explanation about the (maybe) obvious candidates would be interesting.\
|
||||
Tidal-Strudel parity seems to be on the immediate horizon, but I wonder if this parity is transient and if the ultimate goal of Strudel is to ultimately diverge from Tidal. Perhaps the project is in its early stages, and this is not clear, but it would be interesting to get some insights on the authors' intentions. Personally, I think that a key discussion that needs to happen in live coding communities and development nodes is that of what is understood as multilingual live coding. Tidal's paradigm is implemented in a number of programming languages (as well as platforms such as Estuary), each with its own set of features and challenges: some are functional reactive programming, type strict oriented, while others are multi-paradigm, dynamic type oriented, etc. This seems to suggest that it is possible for many programming languages to adopt one single understanding of how to make music. Perhaps further considerations should be made as to whether we need one single grammar to name and express all music structures or whether we need environments and software that help difference to proliferate in terms of music/sound art/art idiosyncrasies. Perhaps questions in this direction are beyond the scope of this paper and I hope this last comment is taken as productive feedback for the authors.
|
||||
|
||||
Overall, this paper is clear and presents software that is a key development for live coding and will be a great addition to the next ICLC.
|
||||
|
||||
PS. The example shown on page 3 under "Pattern Example" seems to be different from its explanation. The explanations do not mention scale but mention echo, which is not present in the example.
|
||||
Generated
+1590
-65
File diff suppressed because it is too large
Load Diff
@@ -861,44 +861,6 @@ exports[`runs examples > example "attack" example index 0 1`] = `
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "bandf" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/2 | s:bd bandf:1000 ]",
|
||||
"[ 1/2 → 1/1 | s:sd bandf:1000 ]",
|
||||
"[ 0/1 → 1/3 | s:hh bandf:1000 ]",
|
||||
"[ 1/3 → 2/3 | s:hh bandf:1000 ]",
|
||||
"[ 2/3 → 1/1 | s:hh bandf:1000 ]",
|
||||
"[ 1/1 → 3/2 | s:bd bandf:2000 ]",
|
||||
"[ 3/2 → 2/1 | s:sd bandf:2000 ]",
|
||||
"[ 1/1 → 4/3 | s:hh bandf:2000 ]",
|
||||
"[ 4/3 → 5/3 | s:hh bandf:2000 ]",
|
||||
"[ 5/3 → 2/1 | s:hh bandf:2000 ]",
|
||||
"[ 2/1 → 5/2 | s:bd bandf:4000 ]",
|
||||
"[ 5/2 → 3/1 | s:sd bandf:4000 ]",
|
||||
"[ 2/1 → 7/3 | s:hh bandf:4000 ]",
|
||||
"[ 7/3 → 8/3 | s:hh bandf:4000 ]",
|
||||
"[ 8/3 → 3/1 | s:hh bandf:4000 ]",
|
||||
"[ 3/1 → 7/2 | s:bd bandf:8000 ]",
|
||||
"[ 7/2 → 4/1 | s:sd bandf:8000 ]",
|
||||
"[ 3/1 → 10/3 | s:hh bandf:8000 ]",
|
||||
"[ 10/3 → 11/3 | s:hh bandf:8000 ]",
|
||||
"[ 11/3 → 4/1 | s:hh bandf:8000 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "bandq" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/2 | s:bd bandf:500 bandq:0 ]",
|
||||
"[ 1/2 → 1/1 | s:sd bandf:500 bandq:0 ]",
|
||||
"[ 1/1 → 3/2 | s:bd bandf:500 bandq:1 ]",
|
||||
"[ 3/2 → 2/1 | s:sd bandf:500 bandq:1 ]",
|
||||
"[ 2/1 → 5/2 | s:bd bandf:500 bandq:2 ]",
|
||||
"[ 5/2 → 3/1 | s:sd bandf:500 bandq:2 ]",
|
||||
"[ 3/1 → 7/2 | s:bd bandf:500 bandq:3 ]",
|
||||
"[ 7/2 → 4/1 | s:sd bandf:500 bandq:3 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "bank" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/2 | s:bd bank:RolandTR909 ]",
|
||||
@@ -921,6 +883,44 @@ exports[`runs examples > example "begin" example index 0 1`] = `
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "bpf" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/2 | s:bd bpf:1000 ]",
|
||||
"[ 1/2 → 1/1 | s:sd bpf:1000 ]",
|
||||
"[ 0/1 → 1/3 | s:hh bpf:1000 ]",
|
||||
"[ 1/3 → 2/3 | s:hh bpf:1000 ]",
|
||||
"[ 2/3 → 1/1 | s:hh bpf:1000 ]",
|
||||
"[ 1/1 → 3/2 | s:bd bpf:2000 ]",
|
||||
"[ 3/2 → 2/1 | s:sd bpf:2000 ]",
|
||||
"[ 1/1 → 4/3 | s:hh bpf:2000 ]",
|
||||
"[ 4/3 → 5/3 | s:hh bpf:2000 ]",
|
||||
"[ 5/3 → 2/1 | s:hh bpf:2000 ]",
|
||||
"[ 2/1 → 5/2 | s:bd bpf:4000 ]",
|
||||
"[ 5/2 → 3/1 | s:sd bpf:4000 ]",
|
||||
"[ 2/1 → 7/3 | s:hh bpf:4000 ]",
|
||||
"[ 7/3 → 8/3 | s:hh bpf:4000 ]",
|
||||
"[ 8/3 → 3/1 | s:hh bpf:4000 ]",
|
||||
"[ 3/1 → 7/2 | s:bd bpf:8000 ]",
|
||||
"[ 7/2 → 4/1 | s:sd bpf:8000 ]",
|
||||
"[ 3/1 → 10/3 | s:hh bpf:8000 ]",
|
||||
"[ 10/3 → 11/3 | s:hh bpf:8000 ]",
|
||||
"[ 11/3 → 4/1 | s:hh bpf:8000 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "bpq" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/2 | s:bd bpf:500 bpq:0 ]",
|
||||
"[ 1/2 → 1/1 | s:sd bpf:500 bpq:0 ]",
|
||||
"[ 1/1 → 3/2 | s:bd bpf:500 bpq:1 ]",
|
||||
"[ 3/2 → 2/1 | s:sd bpf:500 bpq:1 ]",
|
||||
"[ 2/1 → 5/2 | s:bd bpf:500 bpq:2 ]",
|
||||
"[ 5/2 → 3/1 | s:sd bpf:500 bpq:2 ]",
|
||||
"[ 3/1 → 7/2 | s:bd bpf:500 bpq:3 ]",
|
||||
"[ 7/2 → 4/1 | s:sd bpf:500 bpq:3 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "cat" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/2 | s:hh ]",
|
||||
@@ -1060,6 +1060,27 @@ exports[`runs examples > example "chunkBack" example index 0 1`] = `
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "clip" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/5 | note:c s:piano clip:1 ]",
|
||||
"[ 1/5 → 2/5 | note:a s:piano clip:1 ]",
|
||||
"[ 2/5 → 3/5 | note:f s:piano clip:1 ]",
|
||||
"[ 3/5 → 4/5 | note:e s:piano clip:1 ]",
|
||||
"[ 1/1 → 6/5 | note:c s:piano clip:1 ]",
|
||||
"[ 6/5 → 7/5 | note:a s:piano clip:1 ]",
|
||||
"[ 7/5 → 8/5 | note:f s:piano clip:1 ]",
|
||||
"[ 8/5 → 9/5 | note:e s:piano clip:1 ]",
|
||||
"[ 2/1 → 11/5 | note:c s:piano clip:1 ]",
|
||||
"[ 11/5 → 12/5 | note:a s:piano clip:1 ]",
|
||||
"[ 12/5 → 13/5 | note:f s:piano clip:1 ]",
|
||||
"[ 13/5 → 14/5 | note:e s:piano clip:1 ]",
|
||||
"[ 3/1 → 16/5 | note:c s:piano clip:1 ]",
|
||||
"[ 16/5 → 17/5 | note:a s:piano clip:1 ]",
|
||||
"[ 17/5 → 18/5 | note:f s:piano clip:1 ]",
|
||||
"[ 18/5 → 19/5 | note:e s:piano clip:1 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "coarse" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/2 | s:bd coarse:1 ]",
|
||||
@@ -1254,31 +1275,6 @@ exports[`runs examples > example "cut" example index 0 1`] = `
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "cutoff" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/2 | s:bd cutoff:4000 ]",
|
||||
"[ 1/2 → 1/1 | s:sd cutoff:4000 ]",
|
||||
"[ 0/1 → 1/3 | s:hh cutoff:4000 ]",
|
||||
"[ 1/3 → 2/3 | s:hh cutoff:4000 ]",
|
||||
"[ 2/3 → 1/1 | s:hh cutoff:4000 ]",
|
||||
"[ 1/1 → 3/2 | s:bd cutoff:2000 ]",
|
||||
"[ 3/2 → 2/1 | s:sd cutoff:2000 ]",
|
||||
"[ 1/1 → 4/3 | s:hh cutoff:2000 ]",
|
||||
"[ 4/3 → 5/3 | s:hh cutoff:2000 ]",
|
||||
"[ 5/3 → 2/1 | s:hh cutoff:2000 ]",
|
||||
"[ 2/1 → 5/2 | s:bd cutoff:1000 ]",
|
||||
"[ 5/2 → 3/1 | s:sd cutoff:1000 ]",
|
||||
"[ 2/1 → 7/3 | s:hh cutoff:1000 ]",
|
||||
"[ 7/3 → 8/3 | s:hh cutoff:1000 ]",
|
||||
"[ 8/3 → 3/1 | s:hh cutoff:1000 ]",
|
||||
"[ 3/1 → 7/2 | s:bd cutoff:500 ]",
|
||||
"[ 7/2 → 4/1 | s:sd cutoff:500 ]",
|
||||
"[ 3/1 → 10/3 | s:hh cutoff:500 ]",
|
||||
"[ 10/3 → 11/3 | s:hh cutoff:500 ]",
|
||||
"[ 11/3 → 4/1 | s:hh cutoff:500 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "decay" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/2 | note:c3 decay:0.1 sustain:0 ]",
|
||||
@@ -1898,61 +1894,61 @@ exports[`runs examples > example "gain" example index 0 1`] = `
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "hcutoff" example index 0 1`] = `
|
||||
exports[`runs examples > example "hpf" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/2 | s:bd hcutoff:4000 ]",
|
||||
"[ 1/2 → 1/1 | s:sd hcutoff:4000 ]",
|
||||
"[ 0/1 → 1/4 | s:hh hcutoff:4000 ]",
|
||||
"[ 1/4 → 1/2 | s:hh hcutoff:4000 ]",
|
||||
"[ 1/2 → 3/4 | s:hh hcutoff:4000 ]",
|
||||
"[ 3/4 → 1/1 | s:hh hcutoff:4000 ]",
|
||||
"[ 1/1 → 3/2 | s:bd hcutoff:2000 ]",
|
||||
"[ 3/2 → 2/1 | s:sd hcutoff:2000 ]",
|
||||
"[ 1/1 → 5/4 | s:hh hcutoff:2000 ]",
|
||||
"[ 5/4 → 3/2 | s:hh hcutoff:2000 ]",
|
||||
"[ 3/2 → 7/4 | s:hh hcutoff:2000 ]",
|
||||
"[ 7/4 → 2/1 | s:hh hcutoff:2000 ]",
|
||||
"[ 2/1 → 5/2 | s:bd hcutoff:1000 ]",
|
||||
"[ 5/2 → 3/1 | s:sd hcutoff:1000 ]",
|
||||
"[ 2/1 → 9/4 | s:hh hcutoff:1000 ]",
|
||||
"[ 9/4 → 5/2 | s:hh hcutoff:1000 ]",
|
||||
"[ 5/2 → 11/4 | s:hh hcutoff:1000 ]",
|
||||
"[ 11/4 → 3/1 | s:hh hcutoff:1000 ]",
|
||||
"[ 3/1 → 7/2 | s:bd hcutoff:500 ]",
|
||||
"[ 7/2 → 4/1 | s:sd hcutoff:500 ]",
|
||||
"[ 3/1 → 13/4 | s:hh hcutoff:500 ]",
|
||||
"[ 13/4 → 7/2 | s:hh hcutoff:500 ]",
|
||||
"[ 7/2 → 15/4 | s:hh hcutoff:500 ]",
|
||||
"[ 15/4 → 4/1 | s:hh hcutoff:500 ]",
|
||||
"[ 0/1 → 1/2 | s:bd hpf:4000 ]",
|
||||
"[ 1/2 → 1/1 | s:sd hpf:4000 ]",
|
||||
"[ 0/1 → 1/4 | s:hh hpf:4000 ]",
|
||||
"[ 1/4 → 1/2 | s:hh hpf:4000 ]",
|
||||
"[ 1/2 → 3/4 | s:hh hpf:4000 ]",
|
||||
"[ 3/4 → 1/1 | s:hh hpf:4000 ]",
|
||||
"[ 1/1 → 3/2 | s:bd hpf:2000 ]",
|
||||
"[ 3/2 → 2/1 | s:sd hpf:2000 ]",
|
||||
"[ 1/1 → 5/4 | s:hh hpf:2000 ]",
|
||||
"[ 5/4 → 3/2 | s:hh hpf:2000 ]",
|
||||
"[ 3/2 → 7/4 | s:hh hpf:2000 ]",
|
||||
"[ 7/4 → 2/1 | s:hh hpf:2000 ]",
|
||||
"[ 2/1 → 5/2 | s:bd hpf:1000 ]",
|
||||
"[ 5/2 → 3/1 | s:sd hpf:1000 ]",
|
||||
"[ 2/1 → 9/4 | s:hh hpf:1000 ]",
|
||||
"[ 9/4 → 5/2 | s:hh hpf:1000 ]",
|
||||
"[ 5/2 → 11/4 | s:hh hpf:1000 ]",
|
||||
"[ 11/4 → 3/1 | s:hh hpf:1000 ]",
|
||||
"[ 3/1 → 7/2 | s:bd hpf:500 ]",
|
||||
"[ 7/2 → 4/1 | s:sd hpf:500 ]",
|
||||
"[ 3/1 → 13/4 | s:hh hpf:500 ]",
|
||||
"[ 13/4 → 7/2 | s:hh hpf:500 ]",
|
||||
"[ 7/2 → 15/4 | s:hh hpf:500 ]",
|
||||
"[ 15/4 → 4/1 | s:hh hpf:500 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "hresonance" example index 0 1`] = `
|
||||
exports[`runs examples > example "hpq" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/2 | s:bd hcutoff:2000 hresonance:0 ]",
|
||||
"[ 1/2 → 1/1 | s:sd hcutoff:2000 hresonance:0 ]",
|
||||
"[ 0/1 → 1/4 | s:hh hcutoff:2000 hresonance:0 ]",
|
||||
"[ 1/4 → 1/2 | s:hh hcutoff:2000 hresonance:0 ]",
|
||||
"[ 1/2 → 3/4 | s:hh hcutoff:2000 hresonance:0 ]",
|
||||
"[ 3/4 → 1/1 | s:hh hcutoff:2000 hresonance:0 ]",
|
||||
"[ 1/1 → 3/2 | s:bd hcutoff:2000 hresonance:10 ]",
|
||||
"[ 3/2 → 2/1 | s:sd hcutoff:2000 hresonance:10 ]",
|
||||
"[ 1/1 → 5/4 | s:hh hcutoff:2000 hresonance:10 ]",
|
||||
"[ 5/4 → 3/2 | s:hh hcutoff:2000 hresonance:10 ]",
|
||||
"[ 3/2 → 7/4 | s:hh hcutoff:2000 hresonance:10 ]",
|
||||
"[ 7/4 → 2/1 | s:hh hcutoff:2000 hresonance:10 ]",
|
||||
"[ 2/1 → 5/2 | s:bd hcutoff:2000 hresonance:20 ]",
|
||||
"[ 5/2 → 3/1 | s:sd hcutoff:2000 hresonance:20 ]",
|
||||
"[ 2/1 → 9/4 | s:hh hcutoff:2000 hresonance:20 ]",
|
||||
"[ 9/4 → 5/2 | s:hh hcutoff:2000 hresonance:20 ]",
|
||||
"[ 5/2 → 11/4 | s:hh hcutoff:2000 hresonance:20 ]",
|
||||
"[ 11/4 → 3/1 | s:hh hcutoff:2000 hresonance:20 ]",
|
||||
"[ 3/1 → 7/2 | s:bd hcutoff:2000 hresonance:30 ]",
|
||||
"[ 7/2 → 4/1 | s:sd hcutoff:2000 hresonance:30 ]",
|
||||
"[ 3/1 → 13/4 | s:hh hcutoff:2000 hresonance:30 ]",
|
||||
"[ 13/4 → 7/2 | s:hh hcutoff:2000 hresonance:30 ]",
|
||||
"[ 7/2 → 15/4 | s:hh hcutoff:2000 hresonance:30 ]",
|
||||
"[ 15/4 → 4/1 | s:hh hcutoff:2000 hresonance:30 ]",
|
||||
"[ 0/1 → 1/2 | s:bd hpf:2000 hpq:0 ]",
|
||||
"[ 1/2 → 1/1 | s:sd hpf:2000 hpq:0 ]",
|
||||
"[ 0/1 → 1/4 | s:hh hpf:2000 hpq:0 ]",
|
||||
"[ 1/4 → 1/2 | s:hh hpf:2000 hpq:0 ]",
|
||||
"[ 1/2 → 3/4 | s:hh hpf:2000 hpq:0 ]",
|
||||
"[ 3/4 → 1/1 | s:hh hpf:2000 hpq:0 ]",
|
||||
"[ 1/1 → 3/2 | s:bd hpf:2000 hpq:10 ]",
|
||||
"[ 3/2 → 2/1 | s:sd hpf:2000 hpq:10 ]",
|
||||
"[ 1/1 → 5/4 | s:hh hpf:2000 hpq:10 ]",
|
||||
"[ 5/4 → 3/2 | s:hh hpf:2000 hpq:10 ]",
|
||||
"[ 3/2 → 7/4 | s:hh hpf:2000 hpq:10 ]",
|
||||
"[ 7/4 → 2/1 | s:hh hpf:2000 hpq:10 ]",
|
||||
"[ 2/1 → 5/2 | s:bd hpf:2000 hpq:20 ]",
|
||||
"[ 5/2 → 3/1 | s:sd hpf:2000 hpq:20 ]",
|
||||
"[ 2/1 → 9/4 | s:hh hpf:2000 hpq:20 ]",
|
||||
"[ 9/4 → 5/2 | s:hh hpf:2000 hpq:20 ]",
|
||||
"[ 5/2 → 11/4 | s:hh hpf:2000 hpq:20 ]",
|
||||
"[ 11/4 → 3/1 | s:hh hpf:2000 hpq:20 ]",
|
||||
"[ 3/1 → 7/2 | s:bd hpf:2000 hpq:30 ]",
|
||||
"[ 7/2 → 4/1 | s:sd hpf:2000 hpq:30 ]",
|
||||
"[ 3/1 → 13/4 | s:hh hpf:2000 hpq:30 ]",
|
||||
"[ 13/4 → 7/2 | s:hh hpf:2000 hpq:30 ]",
|
||||
"[ 7/2 → 15/4 | s:hh hpf:2000 hpq:30 ]",
|
||||
"[ 15/4 → 4/1 | s:hh hpf:2000 hpq:30 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
@@ -2385,6 +2381,60 @@ exports[`runs examples > example "loopAtCps" example index 0 1`] = `
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "lpf" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/2 | s:bd lpf:4000 ]",
|
||||
"[ 1/2 → 1/1 | s:sd lpf:4000 ]",
|
||||
"[ 0/1 → 1/3 | s:hh lpf:4000 ]",
|
||||
"[ 1/3 → 2/3 | s:hh lpf:4000 ]",
|
||||
"[ 2/3 → 1/1 | s:hh lpf:4000 ]",
|
||||
"[ 1/1 → 3/2 | s:bd lpf:2000 ]",
|
||||
"[ 3/2 → 2/1 | s:sd lpf:2000 ]",
|
||||
"[ 1/1 → 4/3 | s:hh lpf:2000 ]",
|
||||
"[ 4/3 → 5/3 | s:hh lpf:2000 ]",
|
||||
"[ 5/3 → 2/1 | s:hh lpf:2000 ]",
|
||||
"[ 2/1 → 5/2 | s:bd lpf:1000 ]",
|
||||
"[ 5/2 → 3/1 | s:sd lpf:1000 ]",
|
||||
"[ 2/1 → 7/3 | s:hh lpf:1000 ]",
|
||||
"[ 7/3 → 8/3 | s:hh lpf:1000 ]",
|
||||
"[ 8/3 → 3/1 | s:hh lpf:1000 ]",
|
||||
"[ 3/1 → 7/2 | s:bd lpf:500 ]",
|
||||
"[ 7/2 → 4/1 | s:sd lpf:500 ]",
|
||||
"[ 3/1 → 10/3 | s:hh lpf:500 ]",
|
||||
"[ 10/3 → 11/3 | s:hh lpf:500 ]",
|
||||
"[ 11/3 → 4/1 | s:hh lpf:500 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "lpq" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/2 | s:bd lpf:2000 lpq:0 ]",
|
||||
"[ 1/2 → 1/1 | s:sd lpf:2000 lpq:0 ]",
|
||||
"[ 0/1 → 1/4 | s:hh lpf:2000 lpq:0 ]",
|
||||
"[ 1/4 → 1/2 | s:hh lpf:2000 lpq:0 ]",
|
||||
"[ 1/2 → 3/4 | s:hh lpf:2000 lpq:0 ]",
|
||||
"[ 3/4 → 1/1 | s:hh lpf:2000 lpq:0 ]",
|
||||
"[ 1/1 → 3/2 | s:bd lpf:2000 lpq:10 ]",
|
||||
"[ 3/2 → 2/1 | s:sd lpf:2000 lpq:10 ]",
|
||||
"[ 1/1 → 5/4 | s:hh lpf:2000 lpq:10 ]",
|
||||
"[ 5/4 → 3/2 | s:hh lpf:2000 lpq:10 ]",
|
||||
"[ 3/2 → 7/4 | s:hh lpf:2000 lpq:10 ]",
|
||||
"[ 7/4 → 2/1 | s:hh lpf:2000 lpq:10 ]",
|
||||
"[ 2/1 → 5/2 | s:bd lpf:2000 lpq:20 ]",
|
||||
"[ 5/2 → 3/1 | s:sd lpf:2000 lpq:20 ]",
|
||||
"[ 2/1 → 9/4 | s:hh lpf:2000 lpq:20 ]",
|
||||
"[ 9/4 → 5/2 | s:hh lpf:2000 lpq:20 ]",
|
||||
"[ 5/2 → 11/4 | s:hh lpf:2000 lpq:20 ]",
|
||||
"[ 11/4 → 3/1 | s:hh lpf:2000 lpq:20 ]",
|
||||
"[ 3/1 → 7/2 | s:bd lpf:2000 lpq:30 ]",
|
||||
"[ 7/2 → 4/1 | s:sd lpf:2000 lpq:30 ]",
|
||||
"[ 3/1 → 13/4 | s:hh lpf:2000 lpq:30 ]",
|
||||
"[ 13/4 → 7/2 | s:hh lpf:2000 lpq:30 ]",
|
||||
"[ 7/2 → 15/4 | s:hh lpf:2000 lpq:30 ]",
|
||||
"[ 15/4 → 4/1 | s:hh lpf:2000 lpq:30 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "lrate" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/1 | n:0 s:supersquare leslie:1 lrate:1 ]",
|
||||
@@ -3048,35 +3098,6 @@ exports[`runs examples > example "reset" example index 0 1`] = `
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "resonance" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/2 | s:bd cutoff:2000 resonance:0 ]",
|
||||
"[ 1/2 → 1/1 | s:sd cutoff:2000 resonance:0 ]",
|
||||
"[ 0/1 → 1/4 | s:hh cutoff:2000 resonance:0 ]",
|
||||
"[ 1/4 → 1/2 | s:hh cutoff:2000 resonance:0 ]",
|
||||
"[ 1/2 → 3/4 | s:hh cutoff:2000 resonance:0 ]",
|
||||
"[ 3/4 → 1/1 | s:hh cutoff:2000 resonance:0 ]",
|
||||
"[ 1/1 → 3/2 | s:bd cutoff:2000 resonance:10 ]",
|
||||
"[ 3/2 → 2/1 | s:sd cutoff:2000 resonance:10 ]",
|
||||
"[ 1/1 → 5/4 | s:hh cutoff:2000 resonance:10 ]",
|
||||
"[ 5/4 → 3/2 | s:hh cutoff:2000 resonance:10 ]",
|
||||
"[ 3/2 → 7/4 | s:hh cutoff:2000 resonance:10 ]",
|
||||
"[ 7/4 → 2/1 | s:hh cutoff:2000 resonance:10 ]",
|
||||
"[ 2/1 → 5/2 | s:bd cutoff:2000 resonance:20 ]",
|
||||
"[ 5/2 → 3/1 | s:sd cutoff:2000 resonance:20 ]",
|
||||
"[ 2/1 → 9/4 | s:hh cutoff:2000 resonance:20 ]",
|
||||
"[ 9/4 → 5/2 | s:hh cutoff:2000 resonance:20 ]",
|
||||
"[ 5/2 → 11/4 | s:hh cutoff:2000 resonance:20 ]",
|
||||
"[ 11/4 → 3/1 | s:hh cutoff:2000 resonance:20 ]",
|
||||
"[ 3/1 → 7/2 | s:bd cutoff:2000 resonance:30 ]",
|
||||
"[ 7/2 → 4/1 | s:sd cutoff:2000 resonance:30 ]",
|
||||
"[ 3/1 → 13/4 | s:hh cutoff:2000 resonance:30 ]",
|
||||
"[ 13/4 → 7/2 | s:hh cutoff:2000 resonance:30 ]",
|
||||
"[ 7/2 → 15/4 | s:hh cutoff:2000 resonance:30 ]",
|
||||
"[ 15/4 → 4/1 | s:hh cutoff:2000 resonance:30 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "restart" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/2 | s:bd ]",
|
||||
|
||||
@@ -19,6 +19,13 @@ npm run build # <- builds repl + tutorial to ../docs
|
||||
npm run preview # <- test static build
|
||||
```
|
||||
|
||||
## Generate PWA icons
|
||||
|
||||
```sh
|
||||
cd website/public
|
||||
npx pwa-asset-generator icon.png icons
|
||||
```
|
||||
|
||||
# Standard Readme of Astro Starter Kit: Docs Site
|
||||
|
||||
```bash
|
||||
|
||||
@@ -9,6 +9,7 @@ import rehypeSlug from 'rehype-slug';
|
||||
import rehypeAutolinkHeadings from 'rehype-autolink-headings';
|
||||
|
||||
import tailwind from '@astrojs/tailwind';
|
||||
import AstroPWA from '@vite-pwa/astro';
|
||||
// import { visualizer } from 'rollup-plugin-visualizer';
|
||||
|
||||
const options = {
|
||||
@@ -29,6 +30,66 @@ export default defineConfig({
|
||||
react(),
|
||||
mdx(options),
|
||||
tailwind(),
|
||||
AstroPWA({
|
||||
registerType: 'autoUpdate',
|
||||
injectRegister: 'auto',
|
||||
workbox: {
|
||||
globPatterns: ['**/*.{js,css,html,ico,png,svg,json,wav,mp3,ogg}'],
|
||||
runtimeCaching: [
|
||||
{
|
||||
urlPattern: /^https:\/\/raw\.githubusercontent\.com\/.*/i,
|
||||
handler: 'CacheFirst',
|
||||
options: {
|
||||
cacheName: 'github-files',
|
||||
expiration: {
|
||||
maxEntries: 200,
|
||||
maxAgeSeconds: 60 * 60 * 24 * 30, // <== 14 days
|
||||
},
|
||||
cacheableResponse: {
|
||||
statuses: [0, 200],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
devOptions: {
|
||||
enabled: true,
|
||||
},
|
||||
manifest: {
|
||||
includeAssets: ['favicon.ico', 'icons/apple-icon-180.png', 'favicon.svg'],
|
||||
name: 'Strudel REPL',
|
||||
short_name: 'Strudel',
|
||||
description:
|
||||
'Strudel is a music live coding environment for the browser, porting the TidalCycles pattern language to JavaScript.',
|
||||
theme_color: '#222222',
|
||||
icons: [
|
||||
{
|
||||
src: 'icons/manifest-icon-192.maskable.png',
|
||||
sizes: '192x192',
|
||||
type: 'image/png',
|
||||
purpose: 'any',
|
||||
},
|
||||
{
|
||||
src: 'icons/manifest-icon-192.maskable.png',
|
||||
sizes: '192x192',
|
||||
type: 'image/png',
|
||||
purpose: 'maskable',
|
||||
},
|
||||
{
|
||||
src: 'icons/manifest-icon-512.maskable.png',
|
||||
sizes: '512x512',
|
||||
type: 'image/png',
|
||||
purpose: 'any',
|
||||
},
|
||||
{
|
||||
src: 'icons/manifest-icon-512.maskable.png',
|
||||
sizes: '512x512',
|
||||
type: 'image/png',
|
||||
purpose: 'maskable',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
],
|
||||
site: `https://strudel.tidalcycles.org`,
|
||||
base: '/',
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
"@types/react": "^18.0.26",
|
||||
"@types/react-dom": "^18.0.9",
|
||||
"astro": "^1.7.2",
|
||||
"canvas": "^2.11.0",
|
||||
"fraction.js": "^4.2.0",
|
||||
"nanoid": "^4.0.0",
|
||||
"preact": "^10.7.3",
|
||||
@@ -47,10 +48,12 @@
|
||||
"rehype-autolink-headings": "^6.1.1",
|
||||
"rehype-slug": "^5.0.1",
|
||||
"remark-toc": "^8.0.1",
|
||||
"tailwindcss": "^3.2.4",
|
||||
"canvas": "^2.11.0"
|
||||
"tailwindcss": "^3.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"html-escaper": "^3.0.3"
|
||||
"@vite-pwa/astro": "^0.0.1",
|
||||
"html-escaper": "^3.0.3",
|
||||
"vite-plugin-pwa": "^0.14.1",
|
||||
"workbox-window": "^6.5.4"
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 35 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 44 KiB |
@@ -1,4 +1,5 @@
|
||||
---
|
||||
import { pwaInfo } from 'virtual:pwa-info';
|
||||
import '../styles/index.css';
|
||||
|
||||
const { BASE_URL } = import.meta.env;
|
||||
@@ -12,34 +13,18 @@ const base = BASE_URL;
|
||||
|
||||
<link rel="icon" type="image/svg+xml" href="favicon.ico" />
|
||||
|
||||
<link rel="sitemap" href="./sitemap.xml" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Strudel is a music live coding environment for the browser, porting the TidalCycles pattern language to JavaScript."
|
||||
/>
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<link rel="apple-touch-icon" href="/icons/apple-icon-180.png" sizes="180x180" />
|
||||
<meta name="theme-color" content="#222222" />
|
||||
|
||||
<base href={base} />
|
||||
|
||||
<!-- Preload Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital@0;1&display=swap" rel="stylesheet" />
|
||||
|
||||
<!-- Scrollable a11y code helper -->
|
||||
<script src="./make-scrollable-code-focusable.js" is:inline></script>
|
||||
|
||||
<!-- This is intentionally inlined to avoid FOUC -->
|
||||
<script is:inline>
|
||||
/* const root = document.documentElement;
|
||||
const theme = localStorage.getItem('theme');
|
||||
if (theme === 'dark' || (!theme && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
|
||||
root.classList.add('theme-dark');
|
||||
} else {
|
||||
root.classList.remove('theme-dark');
|
||||
} */
|
||||
</script>
|
||||
|
||||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||||
<!-- <script async src="https://www.googletagmanager.com/gtag/js?id=G-TEL60V1WM9" is:inline></script>
|
||||
<script>
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
gtag('config', 'G-TEL60V1WM9');
|
||||
</script> -->
|
||||
<script src="/src/pwa.ts"></script>
|
||||
{pwaInfo && <Fragment set:html={pwaInfo.webManifest.linkTag} />}
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
import { SITE, OPEN_GRAPH, Frontmatter } from '../config';
|
||||
|
||||
export interface Props {
|
||||
frontmatter: Frontmatter;
|
||||
canonicalUrl: URL;
|
||||
frontmatter: Frontmatter;
|
||||
canonicalUrl: URL;
|
||||
}
|
||||
|
||||
const { frontmatter, canonicalUrl } = Astro.props as Props;
|
||||
@@ -23,11 +23,7 @@ const imageAlt = frontmatter.image?.alt ?? OPEN_GRAPH.image.alt;
|
||||
<meta property="og:locale" content={frontmatter.ogLocale ?? SITE.defaultLanguage} />
|
||||
<meta property="og:image" content={canonicalImageSrc} />
|
||||
<meta property="og:image:alt" content={imageAlt} />
|
||||
<meta
|
||||
name="description"
|
||||
property="og:description"
|
||||
content={frontmatter.description ?? SITE.description}
|
||||
/>
|
||||
<meta name="description" property="og:description" content={frontmatter.description ?? SITE.description} />
|
||||
<meta property="og:site_name" content={SITE.title} />
|
||||
|
||||
<!-- Twitter Tags -->
|
||||
@@ -37,10 +33,3 @@ const imageAlt = frontmatter.image?.alt ?? OPEN_GRAPH.image.alt;
|
||||
<meta name="twitter:description" content={frontmatter.description ?? SITE.description} />
|
||||
<meta name="twitter:image" content={canonicalImageSrc} />
|
||||
<meta name="twitter:image:alt" content={imageAlt} />
|
||||
|
||||
<!--
|
||||
TODO: Add json+ld data, maybe https://schema.org/APIReference makes sense?
|
||||
Docs: https://developers.google.com/search/docs/advanced/structured-data/intro-structured-data
|
||||
https://www.npmjs.com/package/schema-dts seems like a great resource for implementing this.
|
||||
Even better, there's a React component that integrates with `schema-dts`: https://github.com/google/react-schemaorg
|
||||
-->
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
export const SITE = {
|
||||
title: 'Strudel Docs',
|
||||
description: 'Documentation for the Strudel Live Coding Language',
|
||||
defaultLanguage: 'en_US',
|
||||
title: 'Strudel',
|
||||
description: 'Strudel is a music live coding editor that brings TidalCycles to the browser.',
|
||||
defaultLanguage: 'en',
|
||||
};
|
||||
|
||||
export const OPEN_GRAPH = {
|
||||
image: {
|
||||
src: 'https://github.com/withastro/astro/blob/main/assets/social/banner-minimal.png?raw=true',
|
||||
alt:
|
||||
'astro logo on a starry expanse of space,' + ' with a purple saturn-like planet floating in the right foreground',
|
||||
src: 'https://strudel.tidalcycles.org/icon.png',
|
||||
alt: 'Strudel Logo',
|
||||
},
|
||||
twitter: 'astrodotbuild',
|
||||
};
|
||||
|
||||
// This is the type of the frontmatter you put in the docs markdown files.
|
||||
@@ -76,6 +74,7 @@ export const SIDEBAR: Sidebar = {
|
||||
{ text: 'REPL', link: 'technical-manual/repl' },
|
||||
{ text: 'Docs', link: 'technical-manual/docs' },
|
||||
{ text: 'Testing', link: 'technical-manual/testing' },
|
||||
{ text: 'Packages', link: 'technical-manual/packages' },
|
||||
// { text: 'Internals', link: 'technical-manual/internals' },
|
||||
],
|
||||
},
|
||||
|
||||
Vendored
+2
@@ -1 +1,3 @@
|
||||
/// <reference types="astro/client" />
|
||||
/// <reference types="vite-plugin-pwa/info" />
|
||||
/// <reference types="vite-plugin-pwa/client" />
|
||||
|
||||
@@ -22,12 +22,12 @@ const currentFile = `src/pages${currentPage.replace(/\/$/, '')}.mdx`;
|
||||
const githubEditUrl = `${CONFIG.GITHUB_EDIT_URL}/${currentFile}`;
|
||||
---
|
||||
|
||||
<html dir={frontmatter.dir ?? 'ltr'} lang={frontmatter.lang ?? 'en-us'} class="initial">
|
||||
<html dir={frontmatter.dir ?? 'ltr'} lang={frontmatter.lang ?? 'en'} class="initial">
|
||||
<head>
|
||||
<HeadCommon />
|
||||
<HeadSEO frontmatter={frontmatter} canonicalUrl={canonicalURL} />
|
||||
<title>
|
||||
{frontmatter.title ? `${frontmatter.title} 🚀 ${CONFIG.SITE.title}` : CONFIG.SITE.title}
|
||||
{frontmatter.title ? `${frontmatter.title} 🌀 ${CONFIG.SITE.title}` : CONFIG.SITE.title}
|
||||
</title>
|
||||
</head>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import HeadCommon from '../components/HeadCommon.astro';
|
||||
import { Repl } from '../repl/Repl.jsx';
|
||||
---
|
||||
|
||||
<html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<HeadCommon />
|
||||
<title>Strudel REPL</title>
|
||||
|
||||
@@ -9,16 +9,101 @@ import { JsDoc } from '../../docs/JsDoc';
|
||||
|
||||
# Audio Effects
|
||||
|
||||
Wether you're using a synth or a sample, you can apply any of the following built-in audio effects.
|
||||
Whether you're using a synth or a sample, you can apply any of the following built-in audio effects.
|
||||
As you might suspect, the effects can be chained together, and they accept a pattern string as their argument.
|
||||
|
||||
## bandf
|
||||
# Filters
|
||||
|
||||
<JsDoc client:idle name="bandf" h={0} />
|
||||
Filters are an essential building block of [subtractive synthesis](https://en.wikipedia.org/wiki/Subtractive_synthesis).
|
||||
Strudel comes with 3 types of filters:
|
||||
|
||||
## bandq
|
||||
- low-pass filter: low frequencies may _pass_, high frequencies are cut off
|
||||
- high-pass filter: high frequencies may _pass_, low frequencies are cut off
|
||||
- band-pass filters: only a frequency band may _pass_, low and high frequencies around are cut off
|
||||
|
||||
<JsDoc client:idle name="bandq" h={0} />
|
||||
Each filter has 2 parameters:
|
||||
|
||||
- cutoff: the frequency at which the filter starts to work. e.g. a low-pass filter with a cutoff of 1000Hz allows frequencies below 1000Hz to pass.
|
||||
- q-value: Controls the resonance of the filter. Higher values sound more aggressive. Also see [Q-Factor](https://en.wikipedia.org/wiki/Q_factor)
|
||||
|
||||
## lpf
|
||||
|
||||
<JsDoc client:idle name="lpf" h={0} />
|
||||
|
||||
## lpq
|
||||
|
||||
<JsDoc client:idle name="lpq" h={0} />
|
||||
|
||||
## hpf
|
||||
|
||||
<JsDoc client:idle name="hpf" h={0} />
|
||||
|
||||
## hpq
|
||||
|
||||
<JsDoc client:idle name="hpq" h={0} />
|
||||
|
||||
## bpf
|
||||
|
||||
<JsDoc client:idle name="bpf" h={0} />
|
||||
|
||||
## bpq
|
||||
|
||||
<JsDoc client:idle name="bpq" h={0} />
|
||||
|
||||
## vowel
|
||||
|
||||
<JsDoc client:idle name="vowel" h={0} />
|
||||
|
||||
# Amplitude Envelope
|
||||
|
||||
The amplitude [envelope](<https://en.wikipedia.org/wiki/Envelope_(music)>) controls the dynamic contour of a sound.
|
||||
Strudel uses ADSR envelopes, which are probably the most common way to describe an envelope:
|
||||
|
||||

|
||||
|
||||
[image link](https://commons.wikimedia.org/wiki/File:ADSR_parameter.svg)
|
||||
|
||||
## attack
|
||||
|
||||
<JsDoc client:idle name="attack" h={0} />
|
||||
|
||||
## decay
|
||||
|
||||
<JsDoc client:idle name="decay" h={0} />
|
||||
|
||||
## sustain
|
||||
|
||||
<JsDoc client:idle name="sustain" h={0} />
|
||||
|
||||
## release
|
||||
|
||||
<JsDoc client:idle name="release" h={0} />
|
||||
|
||||
# Dynamics
|
||||
|
||||
## gain
|
||||
|
||||
<JsDoc client:idle name="gain" h={0} />
|
||||
|
||||
## velocity
|
||||
|
||||
<JsDoc client:idle name="velocity" h={0} />
|
||||
|
||||
# Panning
|
||||
|
||||
## jux
|
||||
|
||||
<JsDoc client:idle name="jux" h={0} />
|
||||
|
||||
## juxBy
|
||||
|
||||
<JsDoc client:idle name="juxBy" h={0} />
|
||||
|
||||
## pan
|
||||
|
||||
<JsDoc client:idle name="pan" h={0} />
|
||||
|
||||
# Waveshaping
|
||||
|
||||
## coarse
|
||||
|
||||
@@ -28,50 +113,10 @@ As you might suspect, the effects can be chained together, and they accept a pat
|
||||
|
||||
<JsDoc client:idle name="crush" h={0} />
|
||||
|
||||
## cutoff
|
||||
|
||||
<JsDoc client:idle name="cutoff" h={0} />
|
||||
|
||||
## gain
|
||||
|
||||
<JsDoc client:idle name="gain" h={0} />
|
||||
|
||||
## hcutoff
|
||||
|
||||
<JsDoc client:idle name="hcutoff" h={0} />
|
||||
|
||||
## hresonance
|
||||
|
||||
<JsDoc client:idle name="hresonance" h={0} />
|
||||
|
||||
## pan
|
||||
|
||||
<JsDoc client:idle name="pan" h={0} />
|
||||
|
||||
## resonance
|
||||
|
||||
<JsDoc client:idle name="resonance" h={0} />
|
||||
|
||||
## shape
|
||||
|
||||
<JsDoc client:idle name="shape" h={0} />
|
||||
|
||||
## velocity
|
||||
|
||||
<JsDoc client:idle name="velocity" h={0} />
|
||||
|
||||
## vowel
|
||||
|
||||
<JsDoc client:idle name="vowel" h={0} />
|
||||
|
||||
## jux
|
||||
|
||||
<JsDoc client:idle name="jux" h={0} />
|
||||
|
||||
## juxBy
|
||||
|
||||
<JsDoc client:idle name="juxBy" h={0} />
|
||||
|
||||
# Global Effects
|
||||
|
||||
## Local vs Global Effects
|
||||
|
||||
@@ -9,16 +9,90 @@ import { JsDoc } from '../../docs/JsDoc';
|
||||
|
||||
# Samples
|
||||
|
||||
# Default Sample Map
|
||||
Samples are the most common way to make sound with tidal and strudel.
|
||||
A sample is a (commonly short) piece of audio that is used as a basis for sound generation, undergoing various transformations.
|
||||
Music that is based on samples can be thought of as a collage of sound. [Read more about Sampling](<https://en.wikipedia.org/wiki/Sampling_(music)>)
|
||||
|
||||
As we have seen, `s` can play back audio samples:
|
||||
Strudel allows loading samples in the form of audio files of various formats (wav, mp3, ogg) from any publicly available URL.
|
||||
|
||||
<MiniRepl client:idle client:idle tune={`s("bd sd,hh*8,misc/2")`} />
|
||||
# Default Samples
|
||||
|
||||
These sounds come from Strudel's in-built default "sample map".
|
||||
To know which sounds are available, open the [default sample map](https://strudel.tidalcycles.org/EmuSP12.json).
|
||||
By default, strudel comes with a built-in "sample map", providing a solid base to play with.
|
||||
|
||||
# Custom Sample Maps
|
||||
<MiniRepl client:idle tune={`s("bd sd,hh*8, misc/2")`} />
|
||||
|
||||
Here, we are using the `s` function to play back different default samples (`bd`, `sd`, `hh` and `misc`) to get a drum beat.
|
||||
|
||||
For drum sounds, strudel uses the comprehensive [tidal-drum-machines](https://github.com/ritchse/tidal-drum-machines) library, with the following naming convention:
|
||||
|
||||
| Drum | Abbreviation |
|
||||
| ----------------------------------- | ------------ |
|
||||
| Bass drum, Kick drum | bd |
|
||||
| Snare drum | sd |
|
||||
| Rimshot | rim |
|
||||
| Clap | cp |
|
||||
| Closed hi-hat | hh |
|
||||
| Open hi-hat | oh |
|
||||
| Crash | cr |
|
||||
| Ride | rd |
|
||||
| Shakers (and maracas, cabasas, etc) | sh |
|
||||
| High tom | ht |
|
||||
| Medium tom | mt |
|
||||
| Low tom | lt |
|
||||
| Cowbell | cb |
|
||||
| Tambourine | tb |
|
||||
| Other percussions | perc |
|
||||
| Miscellaneous samples | misc |
|
||||
| Effects | fx |
|
||||
|
||||
Furthermore, strudel also loads instrument samples from [VCSL](https://github.com/sgossner/VCSL) by default.
|
||||
|
||||
To see which sample names are available, open the `samples` tab in the [REPL](https://strudel.tidalcycles.org/).
|
||||
|
||||
Note that only the sample maps (mapping names to URLs) are loaded initially, while the audio samples itself are not loaded until they are actually played.
|
||||
This behaviour of loading things only when they are needed is also called `lazy loading`.
|
||||
While it saves resources, it can also lead to sounds not being audible the first time they are triggered, because the sound is still loading.
|
||||
[This might be fixed in the future](https://github.com/tidalcycles/strudel/issues/187)
|
||||
|
||||
# Sound Banks
|
||||
|
||||
If we look at the `samples` tab, we can see that the drum samples are all prefixed with drum machine names: `RolandTR808_bd`, `RolandTR808_sd`, `RolandTR808_hh` etc..
|
||||
|
||||
We _could_ use them like this:
|
||||
|
||||
<MiniRepl client:idle tune={`s("RolandTR808_bd RolandTR808_sd,RolandTR808_hh*8")`} />
|
||||
|
||||
... but thats obviously a bit much to write. Using the `bank` function, we can shorten this to:
|
||||
|
||||
<MiniRepl client:idle tune={`s("bd sd,hh*8").bank("RolandTR808")`} />
|
||||
|
||||
You could even pattern the bank to switch between different drum machines:
|
||||
|
||||
<MiniRepl client:idle tune={`s("bd sd,hh*8").bank("<RolandTR808 RolandTR909>")`} />
|
||||
|
||||
Behind the scenes, `bank` will just prepend the drum machine name to the sample name with `_` to get the full name.
|
||||
This of course only works because the name after `_` (`bd`, `sd` etc..) is standardized.
|
||||
Also note that some banks won't have samples for all sounds!
|
||||
|
||||
# Selecting Sounds
|
||||
|
||||
If we look again at the `samples` tab, there is also a number behind each name, indicating how many individual samples are available.
|
||||
For example `RolandTR909_hh(4)` means there are 4 samples of a TR909 hihat available.
|
||||
By default, `s` will play the first sample, but we can selecting the other ones using `n`, starting from 0:
|
||||
|
||||
<MiniRepl client:idle tune={`s("hh*4").bank("RolandTR909").n("<0 1 2 3>")`} />
|
||||
|
||||
Numbers that are too high will just wrap around to the beginning
|
||||
|
||||
<MiniRepl client:idle tune={`s("hh*4").bank("RolandTR909").n("<0 1 2 3 4 5 6 7>")`} />
|
||||
|
||||
Here, 0-3 will play the same sounds as 4-7, because `RolandTR909_hh` only has 4 sounds.
|
||||
|
||||
Selecting sounds also works inside the mini notation, using "`:`" like this:
|
||||
|
||||
<MiniRepl client:idle tune={`s("bd:1 bd:2,hh:0 hh:1 hh:2 hh:3").bank("RolandTR909")`} />
|
||||
|
||||
# Loading Custom Samples
|
||||
|
||||
You can load your own sample map using the `samples` function.
|
||||
In this example we create a map using sounds from the default sample map:
|
||||
@@ -48,7 +122,7 @@ s("bassdrum snaredrum, hihat*8")`}
|
||||
|
||||
Here we have changed the "map" to include longer sample names.
|
||||
|
||||
# Loading Custom Samples
|
||||
## The `samples` function
|
||||
|
||||
The `samples` function has two arguments:
|
||||
|
||||
@@ -87,7 +161,7 @@ We can see there are some guitar samples inside the `/samples` folder, so let's
|
||||
s("[g0 g1 g2 g3 g4]/5")`}
|
||||
/>
|
||||
|
||||
# Loading Multiple Samples per Sound
|
||||
## Multiple Samples per Sound
|
||||
|
||||
It is also possible, to declare multiple files for one sound, using the array notation:
|
||||
|
||||
@@ -146,7 +220,7 @@ And as above, we can choose the sample number using `n` for even more flexibilit
|
||||
n("<0 1 2 3 4>").s("guitar")`}
|
||||
/>
|
||||
|
||||
# Pitched Sounds
|
||||
## Pitched Sounds
|
||||
|
||||
For pitched sounds, you can use `note`, just like with synths:
|
||||
|
||||
@@ -170,7 +244,7 @@ note("g3 [bb3 c4] <g4 f4 eb4 f3>@2").s('gtr').clip(1)
|
||||
.gain(.5)`}
|
||||
/>
|
||||
|
||||
# Base Pitch
|
||||
## Base Pitch
|
||||
|
||||
If we have 2 samples with different base pitches, we can make them in tune by specifying the pitch like this:
|
||||
|
||||
@@ -203,32 +277,49 @@ note("g2!2 <bb2 c3>!2, <c4@3 [<eb4 bb3> g4 f4]>")
|
||||
|
||||
The sampler will always pick the closest matching sample for the current note!
|
||||
|
||||
## Shabda
|
||||
|
||||
If you don't want to select samples by hand, there is also the wonderful tool called [shabda](https://shabda.ndre.gr/).
|
||||
With it, you can enter any sample name(s) to query from [freesound.org](https://freesound.org/). Example:
|
||||
|
||||
<MiniRepl
|
||||
client:idle
|
||||
tune={`await samples('https://shabda.ndre.gr/bass:4,hihat:4,rimshot:2.json?strudel=1')
|
||||
stack(
|
||||
n("0 1 2 3").s('bass').slow(2),
|
||||
n("0 1*2 2 3*2").s('hihat'),
|
||||
n("~ 0 ~ 1").s('rimshot')
|
||||
).clip(1)`}
|
||||
/>
|
||||
|
||||
# Sampler Effects
|
||||
|
||||
Below are four different examples of sampler "effects" which are functions that can be used to change the behaviour of sample playback.
|
||||
Note that most of what you've learned already about Tidal mini-notation can be used with these functions too.
|
||||
Almost everything in Tidal can be patterned using strings!
|
||||
Sampler effects are functions that can be used to change the behaviour of sample playback.
|
||||
|
||||
### `begin`
|
||||
### begin
|
||||
|
||||
<JsDoc client:idle name="Pattern.begin" h={0} />
|
||||
|
||||
### `end`
|
||||
### end
|
||||
|
||||
<JsDoc client:idle name="Pattern.end" h={0} />
|
||||
|
||||
### `cut`
|
||||
### cut
|
||||
|
||||
<JsDoc client:idle name="cut" h={0} />
|
||||
|
||||
### `loopAt`
|
||||
### clip
|
||||
|
||||
<JsDoc client:idle name="clip" h={0} />
|
||||
|
||||
### loopAt
|
||||
|
||||
<JsDoc client:idle name="Pattern.loopAt" h={0} />
|
||||
|
||||
### `chop`
|
||||
### chop
|
||||
|
||||
<JsDoc client:idle name="Pattern.chop" h={0} />
|
||||
|
||||
### `speed`
|
||||
### speed
|
||||
|
||||
<JsDoc client:idle name="speed" h={0} />
|
||||
|
||||
@@ -28,15 +28,3 @@ The power of patterns allows us to sequence any _param_ independently:
|
||||
|
||||
Now we not only pattern the notes, but the sound as well!
|
||||
`sawtooth` `square` and `triangle` are the basic waveforms available in `s`.
|
||||
|
||||
## Ampltude Envelope
|
||||
|
||||
You can control the envelope of a synth using the `attack`, `decay`, `sustain` and `release` functions:
|
||||
|
||||
<MiniRepl
|
||||
client:idle
|
||||
tune={`note("c2 <eb2 <g2 g1>>").s('sawtooth')
|
||||
.attack(.1).decay(.1).sustain(.2).release(.1)`}
|
||||
/>
|
||||
|
||||
<br />
|
||||
|
||||
@@ -1,10 +1,79 @@
|
||||
---
|
||||
title: Strudel Packages
|
||||
layout: ../../layouts/MainLayout.astro
|
||||
---
|
||||
|
||||
import { MiniRepl } from '../../docs/MiniRepl';
|
||||
|
||||
## Strudel Packages
|
||||
|
||||
The [strudel repo](github.com/tidalcycles/strudel) is organized into packages, using [npm workspaces](https://docs.npmjs.com/cli/v7/using-npm/workspaces).
|
||||
Publishing packages is done with [lerna](https://lerna.js.org/).
|
||||
|
||||
There are different packages for different purposes. They..
|
||||
|
||||
- split up the code into smaller chunks
|
||||
- can be selectively used to implement some sort of time based system
|
||||
|
||||
Please refer to the individual README files in the [packages folder](https://github.com/tidalcycles/strudel/tree/main/packages)
|
||||
[See the latest published packages on npm](https://www.npmjs.com/search?q=%40strudel.cycles).
|
||||
|
||||
TODO
|
||||
### Important bits
|
||||
|
||||
- The [root package.json](https://github.com/tidalcycles/strudel/blob/main/package.json) specifies `packages/*` as `workspaces`
|
||||
- Each folder in `packages` comes with its own `package.json`, defining a package name of the format `@strudel.cycles/<name>`
|
||||
- Running `npm i` from the root folder will symlink all packages to the `node_modules` folder, e.g. `node_modules/@strudel.cycles/core` symlinks `packages/core`
|
||||
- These symlinks allow importing the packages with their package name, instead of a relative path, e.g. `import { seq } from '@strudel.cycles/core'`, instead of `import { seq } from '../core/`.
|
||||
This works because the [bare module import](https://vitejs.dev/guide/features.html#npm-dependency-resolving-and-pre-bundling) `@strudel.cycles/core` is resolved to `node_modules/@strudel.cycles/core`.
|
||||
In a project that installs the published packages from npm, these imports will still work, whereas relative ones might not.
|
||||
- When a strudel package is importing from another strudel package, the package that is imported from should be listed in the `dependencies` field of the `package.json`.
|
||||
For example, [@strudel.cycles/mini lists `@strudel.cycles/core` as a dependency](https://github.com/tidalcycles/strudel/blob/main/packages/mini/package.json).
|
||||
- In development, files in any package can be changed and saved to instantly update the dev server via [hot module replacement](https://vitejs.dev/guide/features.html#hot-module-replacement)
|
||||
- To publish packages, `npx lerna publish` will check which packages were changed since the last publish and publish only those.
|
||||
The version numbers in the dependencies of each packages will be updated automatically to the latest version.
|
||||
|
||||
### Building & Publishing
|
||||
|
||||
Currently, all packages are only published as ESM with vite flavour.
|
||||
To build standardized ESM and CJS files, a `vite.config.js` like that is needed:
|
||||
|
||||
```js
|
||||
import { defineConfig } from 'vite';
|
||||
import { dependencies } from './package.json';
|
||||
import { resolve } from 'path';
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [],
|
||||
build: {
|
||||
lib: {
|
||||
entry: resolve(__dirname, 'index.mjs'),
|
||||
formats: ['es', 'cjs'],
|
||||
fileName: (ext) => ({ es: 'index.mjs', cjs: 'index.js' }[ext]),
|
||||
},
|
||||
rollupOptions: {
|
||||
external: [...Object.keys(dependencies)],
|
||||
},
|
||||
target: 'esnext',
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
This will build `index.mjs` (ESM) and `index.js` (CJS) to the dist folder.
|
||||
|
||||
### What's the main file?
|
||||
|
||||
Currently, each package uses the unbundled `index.mjs` as its main file, which must change for the published version.
|
||||
There are 2 ways to handle this:
|
||||
|
||||
1. `main` = `dist/index.js`, `module` = `dist/index.mjs`. The built files are used. This means that changing a source file won't take effect in the dev server without a rebuild.
|
||||
2. Use different `package.json` files for dev vs publish. So the unbuilt `index.mjs` could be used in dev, while the built files can be used when publishing.
|
||||
|
||||
Option 1 could be done with [workspace watching](https://lerna.js.org/docs/features/workspace-watching), although it might make the dev server less snappy..
|
||||
Option 2 can be done by [publishing just the dist folder and copying over the `package.json` file](https://stackoverflow.com/questions/37862712/how-to-publish-contents-only-of-a-specific-folder).
|
||||
Sadly, [this does not fit into how lerna works](https://github.com/lerna/lerna/issues/91).
|
||||
|
||||
https://github.com/changesets/changesets
|
||||
|
||||
https://turbo.build/repo/docs/handbook/publishing-packages/versioning-and-publishing
|
||||
|
||||
https://pnpm.io/workspaces
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import { registerSW } from 'virtual:pwa-register';
|
||||
|
||||
registerSW({
|
||||
immediate: true,
|
||||
onRegisteredSW(swScriptUrl) {
|
||||
// console.log('SW registered: ', swScriptUrl)
|
||||
},
|
||||
onOfflineReady() {
|
||||
// console.log('PWA application ready to work offline')
|
||||
},
|
||||
});
|
||||
@@ -76,7 +76,7 @@ export function Footer({ context }) {
|
||||
<FooterTab name="reference" />
|
||||
</div>
|
||||
{activeFooter !== '' && (
|
||||
<button onClick={() => setActiveFooter('')} className="text-white">
|
||||
<button onClick={() => setActiveFooter('')} className="text-white" aria-label="Close Panel">
|
||||
<XMarkIcon className="w-5 h-5" />
|
||||
</button>
|
||||
)}
|
||||
|
||||
@@ -222,10 +222,10 @@ export function Repl({ embedded = false }) {
|
||||
}
|
||||
// generate uuid in the browser
|
||||
const hash = nanoid(12);
|
||||
const shareUrl = window.location.origin + window.location.pathname + '?' + hash;
|
||||
const { data, error } = await supabase.from('code').insert([{ code: codeToShare, hash }]);
|
||||
if (!error) {
|
||||
setLastShared(activeCode || code);
|
||||
const shareUrl = window.location.origin + '?' + hash;
|
||||
// copy shareUrl to clipboard
|
||||
await navigator.clipboard.writeText(shareUrl);
|
||||
const message = `Link copied to clipboard: ${shareUrl}`;
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"compilerOptions": {
|
||||
"jsx": "react-jsx",
|
||||
"skipLibCheck": true,
|
||||
"jsxImportSource": "react"
|
||||
"jsxImportSource": "react",
|
||||
"noImplicitAny": false,
|
||||
"types": [
|
||||
"vite-plugin-pwa/client"
|
||||
]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user