From 95ae5191ceedfdaae1f85b8e5ed34926129bb9fa Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 16 May 2024 03:36:35 +0200 Subject: [PATCH 01/12] begin "understanding voicings" --- website/src/config.ts | 1 + website/src/pages/understand/voicings.mdx | 196 ++++++++++++++++++++++ 2 files changed, 197 insertions(+) create mode 100644 website/src/pages/understand/voicings.mdx diff --git a/website/src/config.ts b/website/src/config.ts index 9dee2aac..f85d60d8 100644 --- a/website/src/config.ts +++ b/website/src/config.ts @@ -99,6 +99,7 @@ export const SIDEBAR: Sidebar = { { text: 'Coding syntax', link: 'learn/code' }, { text: 'Pitch', link: 'understand/pitch' }, { text: 'Cycles', link: 'understand/cycles' }, + { text: 'Voicings', link: 'understand/voicings' }, { text: 'Pattern Alignment', link: 'technical-manual/alignment' }, { text: 'Strudel vs Tidal', link: 'learn/strudel-vs-tidal' }, ], diff --git a/website/src/pages/understand/voicings.mdx b/website/src/pages/understand/voicings.mdx new file mode 100644 index 00000000..a36ecd8a --- /dev/null +++ b/website/src/pages/understand/voicings.mdx @@ -0,0 +1,196 @@ +--- +title: Understanding Chod Voicings +layout: ../../layouts/MainLayout.astro +--- + +import { MiniRepl } from '../../docs/MiniRepl'; +import { PitchSlider } from '../../components/PitchSlider'; +import Box from '@components/Box.astro'; + +# Understanding Chords and Voicings + +Let's dig deeper into how chords and voicings work. +I'll try to keep theory jargon to a minimum, so hopefully this is approachable for anyone interested. + +## What is a chord + +Playing more than one note at a time is generally called a chord. Here's an example: + +").room(.5)`} /> + +Here's the same with midi numbers: + +").room(.5)`} /> + +Here, we have two 3-note chords played in a loop. +You could already stop here and write chords in this style, which is totally fine and gives you control over individual notes. +One downside is that it can be difficult to find good sounding chords and maybe you're yearning for a way to organize chords in some other way.. + +## Labeling Chords + +Chords are typically given different labels depending on the relationship of the notes within. +In the number example above, we have `48,51,55` and `53,57,60`. + +To analyze the relationship of those notes, they are typically compared to some `root`, which is often the lowest note. +In our case, the `roots` would be `48` (= `c3`) and `53` (= `f3`). +We can express the same chords relative to those `roots` like this: + +".add("<48 53>")).room(.5)`} /> + +Now within each chord, each number represents the distance from the root. +A distance between pitches is typically called `interval`, but let's stick to distance for now. + +Now we can see that our 2 chords are actually quite similar, as the only difference is the middle note (and the root of course). +They are part of a group of chords called `triads` which are chords with 3 notes. + +### Triads + +These 4 shapes are the most common types of `triads` you will encounter: + +| shape | label | +| ----- | ---------- | +| 0,3,6 | diminished | +| 0,3,7 | minor | +| 0,4,7 | major | +| 0,4,8 | augmented | + +Here they are in succession: + +".add("60")) +.room(.5)._pitchwheel()`} +/> + +Many types of music often only use minor and major chords, so we already have the knowledge to accompany songs. Here's one: + +\`.add(\`< +a c d f +a e a e +>\`)).room(.5)`} +/> + +These are the chords for "The house of the rising sun" by The Animals. +So far it doesn't sound too exiting but at least it's recognizable.. + +## Voicings + +A `voicing` is one of many ways a certain chord shape could be played. +The term comes from choral music, where chords can be sung in different ways by changing which voice sings which note. +For example we could add 12 to one or more notes in the chord: + +".add("48")) +.room(.5)`} +/> + +Notes that are 12 steps apart (= 1 `octave`) are considered to be equal in a harmonic sense, which is why they get the same note letter. +Here's the same example with note letterns: + +") +.room(.5)`} +/> + +This type of voicings are also called `inversions`. There are many other ways we could `voice` this minor chord: + +".add("48")) +.room(.5)`} +/> + +Here we are changing the flavour of the chord slightly by + +1. doubling notes 12 steps higher, +2. using very wide distances +3. omitting notes + +## Voice Leading + +Let's revisit "The House of the Rising Sun", this time using our newly acquired voicing techniques: + +\`.add(\`< +a c d f +a e a e +>\`)).room(.5)`} + punchcard +/> + +These voicings make the chords sound more connected and less jumpy, compared to the version without voicings. +The way chords interact is also called voice leading, reminiscent of how a choir voice would move through a sequence of chords. + +For example, try singing the top voice in the above example. Then try the same on the example without voice leading. Which one's easier? + +Naturally, there are many ways a progression of chords could be voiced and there is no clear right or wrong. + +## Chord Symbols + +Musicians playing chord-based music often rely on a so called lead sheet, which is a simplified notation of a music piece. +The chords in those lead sheets are notated with symbols that allow a piece to be notated in a very concise manner. +A common way to write the chords "The House of the Rising Sun" would be: + +``` +Am | C | D | F +Am | E | Am | E +``` + +Here, each symbol consists of the `root` of the chord and optionally an `m` to signal it's a minor chord (just the root note means it's major). +We could mirror that notation in strudel using the `pick` function: + +" + .pick({ + Am: "57,60,64", + C: "55,60,64", + D: "50,57,66", + F: "57,60,65", + E: "56,59,64", + }) + .note().room(.5)`} + punchcard +/> + +## The voicing function + +Coming up with good sounding voicings that connect well can be a difficult and time consuming process. +The `chord` and `voicing` functions can be used to automate that: + +").voicing().room(.5)`} punchcard /> + +Here we're also using chord symbols but the voicings will be automatically generated with smooth voice leading. + +## Voicing Dictionaries + +The voicing function internally uses so called `voicing dictionaries`, which can also be customized: + +") + .dict('house').anchor(66) + .voicing().room(.5)`} + punchcard +/> + +In a `voicing dictionary`, each chord symbol is assigned one or more voicings. +The `voicing` function then picks the voicing that is closest to the `anchor` (defaults to `c5`). + +The handy thing about this approach is that a `voicing dictionary` can be used to play any chord progression with automated voice leading! From 83e46037d2b0156e6270ab2f46d533e4bb61d091 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 16 May 2024 08:59:07 +0200 Subject: [PATCH 02/12] typo --- website/src/pages/understand/timbre.mdx | 12 ++++++++++++ website/src/pages/understand/voicings.mdx | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 website/src/pages/understand/timbre.mdx diff --git a/website/src/pages/understand/timbre.mdx b/website/src/pages/understand/timbre.mdx new file mode 100644 index 00000000..47a8e299 --- /dev/null +++ b/website/src/pages/understand/timbre.mdx @@ -0,0 +1,12 @@ +--- +title: Understanding Timbre +layout: ../../layouts/MainLayout.astro +--- + +import { MiniRepl } from '../../docs/MiniRepl'; +import { PitchSlider } from '../../components/PitchSlider'; +import Box from '@components/Box.astro'; + +# Understanding Timbre + +Let's learn what timbre is! diff --git a/website/src/pages/understand/voicings.mdx b/website/src/pages/understand/voicings.mdx index a36ecd8a..d66e075e 100644 --- a/website/src/pages/understand/voicings.mdx +++ b/website/src/pages/understand/voicings.mdx @@ -1,5 +1,5 @@ --- -title: Understanding Chod Voicings +title: Understanding Chord Voicings layout: ../../layouts/MainLayout.astro --- From e12dadb33fb9ec489df91fb76f87d7250deac9a1 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Tue, 24 Dec 2024 00:59:03 +0100 Subject: [PATCH 03/12] write more --- website/src/pages/understand/voicings.mdx | 127 +++++++++++++++++++++- 1 file changed, 126 insertions(+), 1 deletion(-) diff --git a/website/src/pages/understand/voicings.mdx b/website/src/pages/understand/voicings.mdx index d66e075e..d255d876 100644 --- a/website/src/pages/understand/voicings.mdx +++ b/website/src/pages/understand/voicings.mdx @@ -9,7 +9,7 @@ import Box from '@components/Box.astro'; # Understanding Chords and Voicings -Let's dig deeper into how chords and voicings work. +Let's dig deeper into how chords and voicings work in strudel. I'll try to keep theory jargon to a minimum, so hopefully this is approachable for anyone interested. ## What is a chord @@ -194,3 +194,128 @@ In a `voicing dictionary`, each chord symbol is assigned one or more voicings. The `voicing` function then picks the voicing that is closest to the `anchor` (defaults to `c5`). The handy thing about this approach is that a `voicing dictionary` can be used to play any chord progression with automated voice leading! + +## The default dictionary + +When using the default dictionary, you can use these chord symbols: + +``` +2 5 6 7 9 11 13 69 add9 +o h sus ^ - ^7 -7 7sus +h7 o7 ^9 ^13 ^7#11 ^9#11 +^7#5 -6 -69 -^7 -^9 -9 +-add9 -11 -7b5 h9 -b6 -#5 +7b9 7#9 7#11 7b5 7#5 9#11 +9b5 9#5 7b13 7#9#5 7#9b5 +7#9#11 7b9#11 7b9b5 7b9#5 +7b9#9 7b9b13 7alt 13#11 +13b9 13#9 7b9sus 7susadd3 +9sus 13sus 7b13sus +aug M m M7 m7 M9 M13 +M7#11 M9#11 M7#5 m6 m69 +m^7 -M7 m^9 -M9 m9 madd9 +m11 m7b5 mb6 m#5 mM7 mM9 +``` + +The available chords and the format is very much inspired by [ireal pro chords](https://technimo.helpshift.com/hc/en/3-ireal-pro/faq/88-chord-symbols-used-in-ireal-pro/). +Some symbols are synonymous: + +- "-" is the same as "m", for example C-7 = Cm7 +- "^" is the same as "M", for example C^7 = CM7 +- "+" is the same as "aug" + +You can decide which one's you prefer. There is no international standard for these symbols. +To get a full chord, the symbols have to be prefixed with a root pitch, e.g. D7#11 is the 7#11 chord relative to the pitch D. + +Here are all possible chords with root C: + +\`).voicing().room(.5)`} + punchcard +/> + +Note that the default dictionary contains multiple ways (= `voicings`) to play each chord symbol. +By default, the `voicing` function tries to minimize jumps. +You can alter the picked voicings in various ways, which are now explained in further detail: + +## anchor + +The `anchor` is a note that is used to align the voicings to: + +").chord("C").voicing().room(.5)`} punchcard /> + +By default, the anchor is the highest possible note the voicing can contain. +When deciding which voicing of the dictionary to pick for a certain chord, the voicing with a top note closest to the anchor wins. + +Note that the anchors in the above example match up with the top notes in the pianoroll. +Like `note`, anchor accepts either midi numbers or note names. + +## mode + +With `mode`, you can change the way the voicing relates to the `anchor`: + +").chord("C").anchor("c5").voicing().room(.5)`} + punchcard +/> + +The modes are: + +- `below`: the top note of the voicing is lower than or equal to the anchor (default) +- `above`: the bottom note of the voicing is higher than or equal to the anchor +- `duck`: the top note of the voicing is lower than the anchor +- `root`: the bottom note of the voicing is always the root note closest to the anchor + +The `anchor` can also be set from within the `mode` function: + +:c5").chord("C").voicing().room(.5)`} punchcard /> + +## n + +The `n` control can be used with `voicing` to select individual notes: + +>").voicing() +.clip("4 3 2 1").room(.5)`} + punchcard +/> + +## Example + +Here's an example of a Jazz Blues in F: + +\`) +$: n("7 8 [10 9] 8").set(chords).voicing().dec(.2) +$: chords.struct("- x - x").voicing().room(.5) +$: n("0 - 1 -").set(chords).mode("root:g2").voicing() +`} + punchcard +/> + +The chords are reused for melody, chords and bassline of the tune. From e4f89f072e3b1ed451d3b6133f2ef3d15c01defd Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Tue, 24 Dec 2024 01:05:01 +0100 Subject: [PATCH 04/12] delete timbre draft --- website/src/pages/understand/timbre.mdx | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 website/src/pages/understand/timbre.mdx diff --git a/website/src/pages/understand/timbre.mdx b/website/src/pages/understand/timbre.mdx deleted file mode 100644 index 47a8e299..00000000 --- a/website/src/pages/understand/timbre.mdx +++ /dev/null @@ -1,12 +0,0 @@ ---- -title: Understanding Timbre -layout: ../../layouts/MainLayout.astro ---- - -import { MiniRepl } from '../../docs/MiniRepl'; -import { PitchSlider } from '../../components/PitchSlider'; -import Box from '@components/Box.astro'; - -# Understanding Timbre - -Let's learn what timbre is! From 49d4fb499f64eddc0a2f42a015164a6afeefff2b Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 26 Dec 2024 01:29:48 +0100 Subject: [PATCH 05/12] some voicing doc edits --- website/src/pages/understand/voicings.mdx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/website/src/pages/understand/voicings.mdx b/website/src/pages/understand/voicings.mdx index d255d876..ba23246d 100644 --- a/website/src/pages/understand/voicings.mdx +++ b/website/src/pages/understand/voicings.mdx @@ -92,7 +92,7 @@ For example we could add 12 to one or more notes in the chord: /> Notes that are 12 steps apart (= 1 `octave`) are considered to be equal in a harmonic sense, which is why they get the same note letter. -Here's the same example with note letterns: +Here's the same example with note letters: These voicings make the chords sound more connected and less jumpy, compared to the version without voicings. -The way chords interact is also called voice leading, reminiscent of how a choir voice would move through a sequence of chords. +The way chords interact is also called `voice leading`, reminiscent of how a choir voice would move through a sequence of chords. For example, try singing the top voice in the above example. Then try the same on the example without voice leading. Which one's easier? @@ -172,7 +173,8 @@ The `chord` and `voicing` functions can be used to automate that: ").voicing().room(.5)`} punchcard /> -Here we're also using chord symbols but the voicings will be automatically generated with smooth voice leading. +Here we're also using chord symbols but the voicings will be automatically generated with smooth `voice leading`, minimizing jumps. +It is inspired by the way a piano or guitar player would pick chords to accompany a song. ## Voicing Dictionaries From d30623dad47a36f7ab821dec873efee3bd3ebf90 Mon Sep 17 00:00:00 2001 From: Bernhard Wagner Date: Wed, 25 Dec 2024 23:01:52 +0100 Subject: [PATCH 06/12] minor amendments --- website/src/pages/understand/voicings.mdx | 42 ++++++++++++----------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/website/src/pages/understand/voicings.mdx b/website/src/pages/understand/voicings.mdx index ba23246d..7d9c13d9 100644 --- a/website/src/pages/understand/voicings.mdx +++ b/website/src/pages/understand/voicings.mdx @@ -14,7 +14,7 @@ I'll try to keep theory jargon to a minimum, so hopefully this is approachable f ## What is a chord -Playing more than one note at a time is generally called a chord. Here's an example: +Playing more than one note at a time is generally called a `chord`. Here's an example: ").room(.5)`} /> @@ -24,7 +24,7 @@ Here's the same with midi numbers: Here, we have two 3-note chords played in a loop. You could already stop here and write chords in this style, which is totally fine and gives you control over individual notes. -One downside is that it can be difficult to find good sounding chords and maybe you're yearning for a way to organize chords in some other way.. +One downside is that it can be difficult to find good sounding chords and maybe you're yearning for a way to organize chords in some other way. ## Labeling Chords @@ -49,16 +49,16 @@ These 4 shapes are the most common types of `triads` you will encounter: | shape | label | | ----- | ---------- | -| 0,3,6 | diminished | -| 0,3,7 | minor | | 0,4,7 | major | +| 0,3,7 | minor | +| 0,3,6 | diminished | | 0,4,8 | augmented | Here they are in succession: ".add("60")) + tune={`note("<[0,4,7] [0,3,7] [0,3,6] [0,4,8]>".add("60")) .room(.5)._pitchwheel()`} /> @@ -76,14 +76,14 @@ a e a e >\`)).room(.5)`} /> -These are the chords for "The house of the rising sun" by The Animals. -So far it doesn't sound too exiting but at least it's recognizable.. +These are the chords for "The House of the Rising Sun" by The Animals. +So far, it doesn't sound too exciting, but at least it's recognizable. ## Voicings -A `voicing` is one of many ways a certain chord shape could be played. -The term comes from choral music, where chords can be sung in different ways by changing which voice sings which note. -For example we could add 12 to one or more notes in the chord: +A `voicing` is one of many ways a certain chord shape can be arranged. +The term comes from choral music, where chords can be sung in different ways by assigning different notes to each voice. +For example we could add 12 semitones to one or more notes in the chord: -Notes that are 12 steps apart (= 1 `octave`) are considered to be equal in a harmonic sense, which is why they get the same note letter. +Notes that are 12 semitone steps apart (= 1 `octave`) are considered to be equal in a harmonic sense, which is why they get the same note letter. Here's the same example with note letters: -This type of voicings are also called `inversions`. There are many other ways we could `voice` this minor chord: +These types of voicings are also called `inversions`. There are many other ways we could `voice` this minor chord: -These voicings make the chords sound more connected and less jumpy, compared to the version without voicings. -The way chords interact is also called `voice leading`, reminiscent of how a choir voice would move through a sequence of chords. +These voicings make the chords sound more connected and less jumpy, compared to the earlier version, which didn't focus on voicing. +The way chords interact is also called `voice leading`, reminiscent of how an +individual choir voice would move through a sequence of chords. -For example, try singing the top voice in the above example. Then try the same on the example without voice leading. Which one's easier? +For example, try singing the top voice in the above example. Then try the same +on the example not focusing on voice leading. Which one's easier? -Naturally, there are many ways a progression of chords could be voiced and there is no clear right or wrong. +Naturally, there are many ways a progression of chords could be voiced and there is no definitive right or wrong. ## Chord Symbols -Musicians playing chord-based music often rely on a so called lead sheet, which is a simplified notation of a music piece. -The chords in those lead sheets are notated with symbols that allow a piece to be notated in a very concise manner. -A common way to write the chords "The House of the Rising Sun" would be: +Musicians playing chord-based music often use a `lead sheet`, which is a simplified notation for a piece of music. +These sheets condense the essential elements, such as chords, into symbols that make the music easy to read and follow. +For example, a lead sheet for "The House of the Rising Sun" might include chords written like this: ``` Am | C | D | F @@ -226,7 +228,7 @@ Some symbols are synonymous: - "^" is the same as "M", for example C^7 = CM7 - "+" is the same as "aug" -You can decide which one's you prefer. There is no international standard for these symbols. +You can decide which ones you prefer. There is no international standard for these symbols. To get a full chord, the symbols have to be prefixed with a root pitch, e.g. D7#11 is the 7#11 chord relative to the pitch D. Here are all possible chords with root C: From 188d3181250fb4c81c7e753a2cbf1ba9098f3754 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Tue, 21 Jan 2025 00:20:55 -0500 Subject: [PATCH 07/12] fixed --- packages/core/pattern.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index 34a63c93..72d3b100 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -3119,9 +3119,9 @@ Pattern.prototype.xfade = function (pos, b) { * especially useful for creating rhythms * @name beat * @example - * s("bd").beat("0:7:10", 16) + * s("bd").beat("0,7,10", 16) * @example - * s("sd").beat("4:12", 16) + * s("sd").beat("4,12", 16) */ const __beat = (join) => (t, div, pat) => { t = Fraction(t).mod(div); From 6cde970e0daecbf4e2300a55213f524cf14e5e2a Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Tue, 21 Jan 2025 00:22:31 -0500 Subject: [PATCH 08/12] snapshot --- test/__snapshots__/examples.test.mjs.snap | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index c071b536..c8d53858 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -957,18 +957,30 @@ exports[`runs examples > example "bank" example index 0 1`] = ` exports[`runs examples > example "beat" example index 0 1`] = ` [ "[ 0/1 → 1/16 | s:bd ]", + "[ 7/16 → 1/2 | s:bd ]", + "[ 5/8 → 11/16 | s:bd ]", "[ 1/1 → 17/16 | s:bd ]", + "[ 23/16 → 3/2 | s:bd ]", + "[ 13/8 → 27/16 | s:bd ]", "[ 2/1 → 33/16 | s:bd ]", + "[ 39/16 → 5/2 | s:bd ]", + "[ 21/8 → 43/16 | s:bd ]", "[ 3/1 → 49/16 | s:bd ]", + "[ 55/16 → 7/2 | s:bd ]", + "[ 29/8 → 59/16 | s:bd ]", ] `; exports[`runs examples > example "beat" example index 1 1`] = ` [ - "[ 1/48 → 1/12 | s:sd ]", - "[ 49/48 → 13/12 | s:sd ]", - "[ 97/48 → 25/12 | s:sd ]", - "[ 145/48 → 37/12 | s:sd ]", + "[ 1/4 → 5/16 | s:sd ]", + "[ 3/4 → 13/16 | s:sd ]", + "[ 5/4 → 21/16 | s:sd ]", + "[ 7/4 → 29/16 | s:sd ]", + "[ 9/4 → 37/16 | s:sd ]", + "[ 11/4 → 45/16 | s:sd ]", + "[ 13/4 → 53/16 | s:sd ]", + "[ 15/4 → 61/16 | s:sd ]", ] `; From 9432f780f26cee135a0d70f26f116a4ce6f213a7 Mon Sep 17 00:00:00 2001 From: Alex McLean Date: Fri, 24 Jan 2025 11:13:49 +0000 Subject: [PATCH 09/12] MQTT - if password isn't provided, prompt for one Helpful for cases where you don't want to risk putting a password in the code. --- packages/mqtt/mqtt.mjs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/packages/mqtt/mqtt.mjs b/packages/mqtt/mqtt.mjs index 75f7904e..0d02a37c 100644 --- a/packages/mqtt/mqtt.mjs +++ b/packages/mqtt/mqtt.mjs @@ -23,6 +23,9 @@ function onMessageArrived(message) { function onFailure(err) { console.error('Connection failed: ', err); + if (typeof window !== 'undefined') { + document.cookie = 'mqtt_pass='; + } } Pattern.prototype.mqtt = function ( @@ -35,12 +38,17 @@ Pattern.prototype.mqtt = function ( ) { const key = host + '-' + client; let connected = false; + let password_entered = false; + if (!client) { client = 'strudel-' + String(Math.floor(Math.random() * 1000000)); } function onConnect() { console.log('Connected to mqtt broker'); connected = true; + if (password_entered) { + document.cookie = 'mqtt_pass=' + password; + } } let cx; @@ -58,6 +66,17 @@ Pattern.prototype.mqtt = function ( if (username) { props.userName = username; + if (typeof password === 'undefined' && typeof window !== 'undefined') { + const cookie = /mqtt_pass=(\w+)/.exec(window.document.cookie); + if (cookie) { + password = cookie[1]; + } + if (typeof password === 'undefined') { + password = prompt('Please enter MQTT server password'); + password_entered = true; + } + } + props.password = password; } cx.connect(props); From 5d562e8cd60ccca2aac37d2a4a9d07873579ef78 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Fri, 24 Jan 2025 14:31:29 +0100 Subject: [PATCH 10/12] add reference package --- packages/reference/index.mjs | 2 ++ packages/reference/package.json | 39 +++++++++++++++++++++++++++++++ packages/reference/vite.config.js | 19 +++++++++++++++ pnpm-lock.yaml | 7 +++++- 4 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 packages/reference/index.mjs create mode 100644 packages/reference/package.json create mode 100644 packages/reference/vite.config.js diff --git a/packages/reference/index.mjs b/packages/reference/index.mjs new file mode 100644 index 00000000..deeb6039 --- /dev/null +++ b/packages/reference/index.mjs @@ -0,0 +1,2 @@ +import jsdoc from '../../doc.json'; +export const reference = jsdoc; diff --git a/packages/reference/package.json b/packages/reference/package.json new file mode 100644 index 00000000..1782fca9 --- /dev/null +++ b/packages/reference/package.json @@ -0,0 +1,39 @@ +{ + "name": "@strudel/reference", + "version": "1.1.0", + "description": "Headless reference of all strudel functions", + "main": "index.mjs", + "type": "module", + "publishConfig": { + "main": "dist/index.mjs" + }, + "scripts": { + "build": "vite build", + "prepublishOnly": "npm run build" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/tidalcycles/strudel.git" + }, + "keywords": [ + "tidalcycles", + "strudel", + "pattern", + "livecoding", + "algorave" + ], + "author": "Felix Roos ", + "contributors": [ + "Alex McLean " + ], + "license": "AGPL-3.0-or-later", + "bugs": { + "url": "https://github.com/tidalcycles/strudel/issues" + }, + "homepage": "https://github.com/tidalcycles/strudel#readme", + "dependencies": { + }, + "devDependencies": { + "vite": "^5.0.10" + } +} diff --git a/packages/reference/vite.config.js b/packages/reference/vite.config.js new file mode 100644 index 00000000..5df3edc1 --- /dev/null +++ b/packages/reference/vite.config.js @@ -0,0 +1,19 @@ +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'], + fileName: (ext) => ({ es: 'index.mjs' })[ext], + }, + rollupOptions: { + external: [...Object.keys(dependencies)], + }, + target: 'esnext', + }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6cb259a9..24529652 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -360,6 +360,12 @@ importers: specifier: ^5.0.10 version: 5.4.9(@types/node@22.7.6)(terser@5.36.0) + packages/reference: + devDependencies: + vite: + specifier: ^5.0.10 + version: 5.4.9(@types/node@22.7.6)(terser@5.36.0) + packages/repl: dependencies: '@strudel/codemirror': @@ -7759,7 +7765,6 @@ packages: workbox-google-analytics@7.0.0: resolution: {integrity: sha512-MEYM1JTn/qiC3DbpvP2BVhyIH+dV/5BjHk756u9VbwuAhu0QHyKscTnisQuz21lfRpOwiS9z4XdqeVAKol0bzg==} - deprecated: It is not compatible with newer versions of GA starting with v4, as long as you are using GAv3 it should be ok, but the package is not longer being maintained workbox-navigation-preload@7.0.0: resolution: {integrity: sha512-juWCSrxo/fiMz3RsvDspeSLGmbgC0U9tKqcUPZBCf35s64wlaLXyn2KdHHXVQrb2cqF7I0Hc9siQalainmnXJA==} From 5731ae0cdcd434e899f6bbc67941c527727f9382 Mon Sep 17 00:00:00 2001 From: Alex McLean Date: Fri, 24 Jan 2025 14:16:55 +0000 Subject: [PATCH 11/12] support `all(pianoroll)` and `all(pianoroll({labels: true}))` (#1234) --- packages/draw/pianoroll.mjs | 78 +++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 34 deletions(-) diff --git a/packages/draw/pianoroll.mjs b/packages/draw/pianoroll.mjs index 2c6742cd..d874c968 100644 --- a/packages/draw/pianoroll.mjs +++ b/packages/draw/pianoroll.mjs @@ -4,7 +4,7 @@ Copyright (C) 2022 Strudel contributors - see . */ -import { Pattern, noteToMidi, freqToMidi } from '@strudel/core'; +import { Pattern, noteToMidi, freqToMidi, isPattern } from '@strudel/core'; import { getTheme, getDrawContext } from './draw.mjs'; const scale = (normalized, min, max) => normalized * (max - min) + min; @@ -36,35 +36,9 @@ const getValue = (e) => { return value; }; -Pattern.prototype.pianoroll = function (options = {}) { - let { cycles = 4, playhead = 0.5, overscan = 0, hideNegative = false, ctx = getDrawContext(), id = 1 } = options; - - let from = -cycles * playhead; - let to = cycles * (1 - playhead); - const inFrame = (hap, t) => (!hideNegative || hap.whole.begin >= 0) && hap.isWithinTime(t + from, t + to); - this.draw( - (haps, time) => { - pianoroll({ - ...options, - time, - ctx, - haps: haps.filter((hap) => inFrame(hap, time)), - }); - }, - { - lookbehind: from - overscan, - lookahead: to + overscan, - id, - }, - ); - return this; -}; - -// this function allows drawing a pianoroll without ties to Pattern.prototype -// it will probably replace the above in the future - /** - * Displays a midi-style piano roll + * Visualises a pattern as a scrolling 'pianoroll', displayed in the background of the editor. To show a pianoroll for all running patterns, use `all(pianoroll)`. To have a pianoroll appear below + * a pattern instead, prefix with `_`, e.g.: `sound("bd sd")._pianoroll()`. * * @name pianoroll * @synonyms punchcard @@ -93,15 +67,51 @@ Pattern.prototype.pianoroll = function (options = {}) { * @param {integer} minMidi minimum note value to display on the value axis - defaults to 10 * @param {integer} maxMidi maximum note value to display on the value axis - defaults to 90 * @param {boolean} autorange automatically calculate the minMidi and maxMidi parameters - 0 by default - * + * @see _pianoroll * @example * note("c2 a2 eb2") * .euclid(5,8) * .s('sawtooth') * .lpenv(4).lpf(300) - * ._pianoroll({ labels: 1 }) + * .pianoroll({ labels: 1 }) */ -export function pianoroll({ + +Pattern.prototype.pianoroll = function (options = {}) { + let { cycles = 4, playhead = 0.5, overscan = 0, hideNegative = false, ctx = getDrawContext(), id = 1 } = options; + + let from = -cycles * playhead; + let to = cycles * (1 - playhead); + const inFrame = (hap, t) => (!hideNegative || hap.whole.begin >= 0) && hap.isWithinTime(t + from, t + to); + this.draw( + (haps, time) => { + __pianoroll({ + ...options, + time, + ctx, + haps: haps.filter((hap) => inFrame(hap, time)), + }); + }, + { + lookbehind: from - overscan, + lookahead: to + overscan, + id, + }, + ); + return this; +}; + +export function pianoroll(arg) { + if (isPattern(arg)) { + // Single argument as a pattern + // (to support `all(pianoroll)`) + return arg.pianoroll(); + } + // Single argument with option - return function to get the pattern + // (to support `all(pianoroll(options))`) + return (pat) => pat.pianoroll(arg); +} + +export function __pianoroll({ time, haps, cycles = 4, @@ -278,7 +288,7 @@ export function getDrawOptions(drawTime, options = {}) { export const getPunchcardPainter = (options = {}) => (ctx, time, haps, drawTime) => - pianoroll({ ctx, time, haps, ...getDrawOptions(drawTime, options) }); + __pianoroll({ ctx, time, haps, ...getDrawOptions(drawTime, options) }); Pattern.prototype.punchcard = function (options) { return this.onPaint(getPunchcardPainter(options)); @@ -302,5 +312,5 @@ Pattern.prototype.wordfall = function (options) { export function drawPianoroll(options) { const { drawTime, ...rest } = options; - pianoroll({ ...getDrawOptions(drawTime), ...rest }); + __pianoroll({ ...getDrawOptions(drawTime), ...rest }); } From 0ca540b671cfbf9d1fc3985089f5235d798e3ccd Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Fri, 24 Jan 2025 15:22:23 +0100 Subject: [PATCH 12/12] add reference readme --- packages/reference/README.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 packages/reference/README.md diff --git a/packages/reference/README.md b/packages/reference/README.md new file mode 100644 index 00000000..8ff16259 --- /dev/null +++ b/packages/reference/README.md @@ -0,0 +1,8 @@ +# @strudel/reference + +this package contains metadata for all documented strudel functions, useful to implement a reference. + +```js +import { reference } from '@strudel/reference'; +console.log(reference) +```