From 3db1c7a52122cc0eb180c764ea87447b5b88a3bc Mon Sep 17 00:00:00 2001 From: Roipoussiere Date: Mon, 5 Jun 2023 19:04:56 +0200 Subject: [PATCH 01/11] swatch: look for song titles in code metadata --- website/src/pages/swatch/list.json.js | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/website/src/pages/swatch/list.json.js b/website/src/pages/swatch/list.json.js index 6d86e384..ecc1d41a 100644 --- a/website/src/pages/swatch/list.json.js +++ b/website/src/pages/swatch/list.json.js @@ -1,9 +1,26 @@ +export function getMetadata(raw_code) { + // https://stackoverflow.com/a/15123777 + const comment_regexp = /\/\*([\s\S]*?)\*\/|([^\\:]|^)\/\/(.*)$/gm; + + const tag_regexp = /@([a-z]*):? (.*)/gm + const tags = {}; + + for (const match of raw_code.matchAll(comment_regexp)) { + const comment = match[1] ? match[1] : '' + match[3] ? match[3] : ''; + for (const tag_match of comment.trim().matchAll(tag_regexp)) { + tags[tag_match[1]] = tag_match[2].trim() + } + } + + return tags; +} + export function getMyPatterns() { const my = import.meta.glob('../../../../my-patterns/**', { as: 'raw', eager: true }); return Object.fromEntries( - Object.entries(my) // - .filter(([name]) => name.endsWith('.txt')) // - .map(([name, raw]) => [name.split('/').slice(-1), raw]), // + Object.entries(my) + .filter(([name]) => name.endsWith('.txt')) + .map(([name, raw]) => [ getMetadata(raw)['title'] || name.split('/').slice(-1), raw ]), ); } From f9ad5f0d01af70d3d633f08b4b20412d86c61b94 Mon Sep 17 00:00:00 2001 From: Roipoussiere Date: Mon, 5 Jun 2023 19:35:41 +0200 Subject: [PATCH 02/11] add tunes metadata --- website/src/repl/tunes.mjs | 170 ++++++++++++++++++++++++------------- 1 file changed, 112 insertions(+), 58 deletions(-) diff --git a/website/src/repl/tunes.mjs b/website/src/repl/tunes.mjs index 9a564609..0e60ab26 100644 --- a/website/src/repl/tunes.mjs +++ b/website/src/repl/tunes.mjs @@ -121,8 +121,10 @@ stack( .room(1) //.pianoroll({fold:1})`; -export const caverave = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ -// by Felix Roos +export const caverave = `// @title Caverave +// @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ +// @by Felix Roos + const keys = x => x.s('sawtooth').cutoff(1200).gain(.5) .attack(0).decay(.16).sustain(.3).release(.1); @@ -184,8 +186,10 @@ export const barryHarris = `// adapted from a Barry Harris excercise .color('#00B8D4') `; -export const blippyRhodes = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ -// by Felix Roos +export const blippyRhodes = `// @title Blippy Rhodes +// @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ +// @by Felix Roos + samples({ bd: 'samples/tidal/bd/BT0A0D0.wav', sn: 'samples/tidal/sn/ST0T0S3.wav', @@ -226,8 +230,10 @@ stack( ).fast(3/2) //.pianoroll({fold:1})`; -export const wavyKalimba = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ -// by Felix Roos +export const wavyKalimba = `// @title Wavy kalimba +// @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ +// @by Felix Roos + samples({ 'kalimba': { c5:'https://freesound.org/data/previews/536/536549_11935698-lq.mp3' } }) @@ -256,8 +262,10 @@ stack( .delay(.2) `; -export const festivalOfFingers = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ -// by Felix Roos +export const festivalOfFingers = `// @title Festival of fingers +// @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ +// @by Felix Roos + const chords = ""; stack( chords.voicings('lefthand').struct("x(3,8,-1)").velocity(.5).off(1/7,x=>x.transpose(12).velocity(.2)), @@ -271,8 +279,10 @@ stack( .note().piano()`; // iter, echo, echoWith -export const undergroundPlumber = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ -// by Felix Roos, inspired by Friendship - Let's not talk about it (1979) :) +export const undergroundPlumber = `// @title Underground plumber +// @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ +// @by Felix Roos +// @details inspired by Friendship - Let's not talk about it (1979) :) samples({ bd: 'bd/BT0A0D0.wav', sn: 'sn/ST0T0S3.wav', hh: 'hh/000_hh3closedhh.wav', cp: 'cp/HANDCLP0.wav', }, 'https://loophole-letters.vercel.app/samples/tidal/') @@ -297,8 +307,10 @@ stack( .fast(2/3) .pianoroll({})`; -export const bridgeIsOver = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ -// by Felix Roos, bassline by BDP - The Bridge Is Over +export const bridgeIsOver = `// @title Bridge is over +// @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ +// @by Felix Roos, bassline by BDP - The Bridge Is Over + samples({mad:'https://freesound.org/data/previews/22/22274_109943-lq.mp3'}) stack( stack( @@ -318,8 +330,10 @@ stack( .pianoroll() `; -export const goodTimes = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ -// by Felix Roos +export const goodTimes = `// @title Good times +// @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ +// @by Felix Roos + const scale = cat('C3 dorian','Bb2 major').slow(4); stack( "2*4".add(12).scale(scale) @@ -361,8 +375,10 @@ stack( .pianoroll({maxMidi:100,minMidi:20})`; */ -export const echoPiano = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ -// by Felix Roos +export const echoPiano = `// @title Echo piano +// @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ +// @by Felix Roos + "<0 2 [4 6](3,4,2) 3*2>" .scale('D minor') .color('salmon') @@ -408,8 +424,10 @@ stack( .legato(.5) ).fast(2).note()`; -export const randomBells = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ -// by Felix Roos +export const randomBells = `// @title Random bells +// @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ +// @by Felix Roos + samples({ bell: { c6: 'https://freesound.org/data/previews/411/411089_5121236-lq.mp3' }, bass: { d2: 'https://freesound.org/data/previews/608/608286_13074022-lq.mp3' } @@ -431,8 +449,10 @@ stack( .pianoroll({minMidi:20,maxMidi:120,background:'transparent'}) `; -export const waa2 = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ -// by Felix Roos +export const waa2 = `// @title Waa2 +// @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ +// @by Felix Roos + n( "a4 [a3 c3] a3 c3" .sub("<7 12 5 12>".slow(2)) @@ -447,8 +467,10 @@ n( .room(.5) `; -export const hyperpop = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ -// by Felix Roos +export const hyperpop = `// @title Hyperpop +// @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ +// @by Felix Roos + const lfo = cosine.slow(15); const lfo2 = sine.slow(16); const filter1 = x=>x.cutoff(lfo2.range(300,3000)); @@ -498,8 +520,10 @@ stack( ).slow(2) // strudel disable-highlighting`; -export const festivalOfFingers3 = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ -// by Felix Roos +export const festivalOfFingers3 = `// @title Festival of fingers 3 +// @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ +// @by Felix Roos + "[-7*3],0,2,6,[8 7]" .echoWith(4,1/4, (x,n)=>x .add(n*7) @@ -516,8 +540,10 @@ export const festivalOfFingers3 = `// licensed with CC BY-NC-SA 4.0 https://crea .note().piano() //.pianoroll({maxMidi:160})`; -export const meltingsubmarine = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ -// by Felix Roos +export const meltingsubmarine = `// @title Melting submarine +// @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ +// @by Felix Roos + await samples('github:tidalcycles/Dirt-Samples/master/') stack( s("bd:5,[~ ],hh27(3,4,1)") // drums @@ -554,8 +580,10 @@ stack( ) .slow(3/2)`; -export const outroMusic = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ -// by Felix Roos +export const outroMusic = `// @title Outro music +// @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ +// @by Felix Roos + samples({ bd: ['bd/BT0AADA.wav','bd/BT0AAD0.wav','bd/BT0A0DA.wav','bd/BT0A0D3.wav','bd/BT0A0D0.wav','bd/BT0A0A7.wav'], sd: ['sd/rytm-01-classic.wav','sd/rytm-00-hard.wav'], @@ -583,8 +611,10 @@ samples({ //.pianoroll({autorange:1,vertical:1,fold:0}) `; -export const bassFuge = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ -// by Felix Roos +export const bassFuge = `// @title Bass fuge +// @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ +// @by Felix Roos + samples({ flbass: ['00_c2_finger_long_neck.wav','01_c2_finger_short_neck.wav','02_c2_finger_long_bridge.wav','03_c2_finger_short_bridge.wav','04_c2_pick_long.wav','05_c2_pick_short.wav','06_c2_palm_mute.wav'] }, 'github:cleary/samples-flbass/main/') samples({ @@ -609,8 +639,10 @@ x=>x.add(7).color('steelblue') .stack(s("bd:1*2,~ sd:0,[~ hh:0]*2")) .pianoroll({vertical:1})`; -export const chop = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ -// by Felix Roos +export const chop = `// @title Chop +// @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ +// @by Felix Roos + samples({ p: 'https://cdn.freesound.org/previews/648/648433_11943129-lq.mp3' }) s("p") @@ -622,8 +654,10 @@ s("p") .sustain(.6) `; -export const delay = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ -// by Felix Roos +export const delay = `// @title Delay +// @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ +// @by Felix Roos + stack( s("bd ") .delay("<0 .5>") @@ -631,8 +665,10 @@ stack( .delayfeedback(".6 | .8") ).sometimes(x=>x.speed("-1"))`; -export const orbit = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ -// by Felix Roos +export const orbit = `// @title Orbit +// @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ +// @by Felix Roos + stack( s("bd ") .delay(.5) @@ -645,8 +681,10 @@ stack( .orbit(2) ).sometimes(x=>x.speed("-1"))`; -export const belldub = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ -// by Felix Roos +export const belldub = `// @title Belldub +// @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ +// @by Felix Roos + samples({ bell: {b4:'https://cdn.freesound.org/previews/339/339809_5121236-lq.mp3'}}) // "Hand Bells, B, Single.wav" by InspectorJ (www.jshaw.co.uk) of Freesound.org stack( @@ -678,8 +716,10 @@ stack( .mask("<1 0>/8") ).slow(5)`; -export const dinofunk = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ -// by Felix Roos +export const dinofunk = `// @title Dinofunk +// @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ +// @by Felix Roos + samples({bass:'https://cdn.freesound.org/previews/614/614637_2434927-hq.mp3', dino:{b4:'https://cdn.freesound.org/previews/316/316403_5123851-hq.mp3'}}) setVoicingRange('lefthand', ['c3','a4']) @@ -699,8 +739,10 @@ note("Abm7".voicings('lefthand').struct("x(3,8,1)".slow(2))), note("").s('dino').delay(.8).slow(8).room(.5) )`; -export const sampleDemo = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ -// by Felix Roos +export const sampleDemo = `// @title Sample demo +// @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ +// @by Felix Roos + stack( // percussion s("[woodblock:1 woodblock:2*2] snare_rim:0,gong/8,brakedrum:1(3,8),~@3 cowbell:3") @@ -715,8 +757,10 @@ stack( .release(.1).room(.5) )`; -export const holyflute = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ -// by Felix Roos +export const holyflute = `// @title Holy flute +// @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ +// @by Felix Roos + "c3 eb3(3,8) c4/2 g3*2" .superimpose( x=>x.slow(2).add(12), @@ -728,8 +772,10 @@ export const holyflute = `// licensed with CC BY-NC-SA 4.0 https://creativecommo .pianoroll({fold:0,autorange:0,vertical:0,cycles:12,smear:0,minMidi:40}) `; -export const flatrave = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ -// by Felix Roos +export const flatrave = `// @title Flatrave +// @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ +// @by Felix Roos + stack( s("bd*2,~ [cp,sd]").bank('RolandTR909'), @@ -751,8 +797,10 @@ stack( .decay(.05).sustain(0).delay(.2).degradeBy(.5).mask("<0 1>/16") )`; -export const amensister = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ -// by Felix Roos +export const amensister = `// @title Amensister +// @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ +// @by Felix Roos + await samples('github:tidalcycles/Dirt-Samples/master') stack( @@ -785,8 +833,10 @@ stack( n("0 1").s("east").delay(.5).degradeBy(.8).speed(rand.range(.5,1.5)) ).reset("")`; -export const juxUndTollerei = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ -// by Felix Roos +export const juxUndTollerei = `// @title Jux und tollerei +// @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ +// @by Felix Roos + note("c3 eb3 g3 bb3").palindrome() .s('sawtooth') .jux(x=>x.rev().color('green').s('sawtooth')) @@ -798,8 +848,10 @@ note("c3 eb3 g3 bb3").palindrome() .delay(.5).delaytime(.1).delayfeedback(.4) .pianoroll()`; -export const csoundDemo = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ -// by Felix Roos +export const csoundDemo = `// @title CSound demo +// @license with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ +// @by Felix Roos + await loadCsound\` instr CoolSynth iduration = p3 @@ -832,10 +884,10 @@ endin\` //.pianoroll() .csound('CoolSynth')`; -export const loungeSponge = ` -// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ -// by Felix Roos -// livecode.orc by Steven Yi +export const loungeSponge = `// @title Lounge sponge +// @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ +// @by Felix Roos, livecode.orc by Steven Yi + await loadOrc('github:kunstmusik/csound-live-code/master/livecode.orc') stack( @@ -852,8 +904,10 @@ stack( s("bd*2,[~ hh]*2,~ cp").bank('RolandTR909') )`; -export const arpoon = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ -// "Arpoon" by Felix Roos +export const arpoon = `// @title Arpoon +// @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ +// @by Felix Roos + await samples('github:tidalcycles/Dirt-Samples/master') "< C7 F^7 [Fm7 E7b9]>".voicings('lefthand') From 52641db590fd5652e89d958b0057695f5b4c6b04 Mon Sep 17 00:00:00 2001 From: Roipoussiere Date: Mon, 5 Jun 2023 20:42:41 +0200 Subject: [PATCH 03/11] load titles from metadata in examples page --- website/src/pages/examples/index.astro | 4 +++- website/src/pages/metadata_parser.js | 16 ++++++++++++++++ website/src/pages/swatch/list.json.js | 17 +---------------- 3 files changed, 20 insertions(+), 17 deletions(-) create mode 100644 website/src/pages/metadata_parser.js diff --git a/website/src/pages/examples/index.astro b/website/src/pages/examples/index.astro index b1ceae11..c973f2eb 100644 --- a/website/src/pages/examples/index.astro +++ b/website/src/pages/examples/index.astro @@ -1,6 +1,8 @@ --- import * as tunes from '../../../src/repl/tunes.mjs'; import HeadCommon from '../../components/HeadCommon.astro'; + +import { getMetadata } from '../metadata_parser'; --- @@ -12,7 +14,7 @@ import HeadCommon from '../../components/HeadCommon.astro'; Object.entries(tunes).map(([name, tune]) => (
- {name} + {getMetadata(tune)['title'] || name}
diff --git a/website/src/pages/metadata_parser.js b/website/src/pages/metadata_parser.js new file mode 100644 index 00000000..f6b8f628 --- /dev/null +++ b/website/src/pages/metadata_parser.js @@ -0,0 +1,16 @@ +export function getMetadata(raw_code) { + // https://stackoverflow.com/a/15123777 + const comment_regexp = /\/\*([\s\S]*?)\*\/|([^\\:]|^)\/\/(.*)$/gm; + + const tag_regexp = /@([a-z]*):? (.*)/gm + const tags = {}; + + for (const match of raw_code.matchAll(comment_regexp)) { + const comment = match[1] ? match[1] : '' + match[3] ? match[3] : ''; + for (const tag_match of comment.trim().matchAll(tag_regexp)) { + tags[tag_match[1]] = tag_match[2].trim() + } + } + + return tags; +} diff --git a/website/src/pages/swatch/list.json.js b/website/src/pages/swatch/list.json.js index ecc1d41a..febc52aa 100644 --- a/website/src/pages/swatch/list.json.js +++ b/website/src/pages/swatch/list.json.js @@ -1,19 +1,4 @@ -export function getMetadata(raw_code) { - // https://stackoverflow.com/a/15123777 - const comment_regexp = /\/\*([\s\S]*?)\*\/|([^\\:]|^)\/\/(.*)$/gm; - - const tag_regexp = /@([a-z]*):? (.*)/gm - const tags = {}; - - for (const match of raw_code.matchAll(comment_regexp)) { - const comment = match[1] ? match[1] : '' + match[3] ? match[3] : ''; - for (const tag_match of comment.trim().matchAll(tag_regexp)) { - tags[tag_match[1]] = tag_match[2].trim() - } - } - - return tags; -} +import { getMetadata } from '../metadata_parser'; export function getMyPatterns() { const my = import.meta.glob('../../../../my-patterns/**', { as: 'raw', eager: true }); From 372e33c06691476ce3e9cc451354979ae4865837 Mon Sep 17 00:00:00 2001 From: Roipoussiere Date: Mon, 5 Jun 2023 20:43:53 +0200 Subject: [PATCH 04/11] doc: add section about metadata --- website/src/config.ts | 1 + website/src/pages/learn/code.mdx | 3 +++ website/src/pages/learn/metadata.mdx | 30 ++++++++++++++++++++++++++++ 3 files changed, 34 insertions(+) create mode 100644 website/src/pages/learn/metadata.mdx diff --git a/website/src/config.ts b/website/src/config.ts index bf26fff1..f4dba71b 100644 --- a/website/src/config.ts +++ b/website/src/config.ts @@ -70,6 +70,7 @@ export const SIDEBAR: Sidebar = { { text: 'Patterns', link: 'technical-manual/patterns' }, { text: 'Pattern Alignment', link: 'technical-manual/alignment' }, { text: 'Strudel vs Tidal', link: 'learn/strudel-vs-tidal' }, + { text: 'Music metadata', link: 'learn/metadata' }, ], Development: [ { text: 'REPL', link: 'technical-manual/repl' }, diff --git a/website/src/pages/learn/code.mdx b/website/src/pages/learn/code.mdx index 678d4ccc..809e04ab 100644 --- a/website/src/pages/learn/code.mdx +++ b/website/src/pages/learn/code.mdx @@ -67,6 +67,9 @@ It is a handy way to quickly turn code on and off. Try uncommenting this line by deleting `//` and refreshing the pattern. You can also use the keyboard shortcut `cmd-/` to toggle comments on and off. +You might noticed that some comments in the REPL samples include some words starting with a "@", like `@title` or `@license`. +Those are just a convention to define some information about the music. We will talk about it in the *metadata* section. + # Strings Ok, so what about the content inside the quotes (e.g. `"a3 c#4 e4 a4"`)? diff --git a/website/src/pages/learn/metadata.mdx b/website/src/pages/learn/metadata.mdx new file mode 100644 index 00000000..663e82c0 --- /dev/null +++ b/website/src/pages/learn/metadata.mdx @@ -0,0 +1,30 @@ +--- +title: Music metadata +layout: ../../layouts/MainLayout.astro +--- + +import { MiniRepl } from '../../docs/MiniRepl'; +import { JsDoc } from '../../docs/JsDoc'; + +# Music metadata + +You can optionally add some music metadata in your Strudel code, by using tags in code comments like this: + +``` +// @license: CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ +// @author: Felix Roos +// @details: Inspired by Friendship - Let's not talk about it (1979) :) +``` + +Like other comments, those are ignored by Strudel, but it can be used by other tools to retrieve some information about the music. + +It is for instance used by the [swatch tool](https://github.com/tidalcycles/strudel/tree/main/my-patterns) to display pattern titles in the [examples page](https://strudel.tidalcycles.org/examples/). + +Available tags are: +- `@title`: music title +- `@by`: music author(s), separated with comma, eventually followed with a link in `<>` (ex: `@author john doe `) +- `@license`: music license +- `@details`: some additional information about the music +- `@url`: web page related to the music (git repo, soundcloud link, etc.) +- `@genre`: music genre (pop, jazz, etc) +- `@album`: music album name From 98a89aea11d9cf39a666867c40d111a83dc4bc5a Mon Sep 17 00:00:00 2001 From: Roipoussiere Date: Mon, 5 Jun 2023 21:15:59 +0200 Subject: [PATCH 05/11] lint: metadata parsing --- website/src/pages/learn/code.mdx | 2 +- website/src/pages/learn/metadata.mdx | 1 + website/src/pages/metadata_parser.js | 4 ++-- website/src/pages/swatch/list.json.js | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/website/src/pages/learn/code.mdx b/website/src/pages/learn/code.mdx index 809e04ab..8551a8a2 100644 --- a/website/src/pages/learn/code.mdx +++ b/website/src/pages/learn/code.mdx @@ -68,7 +68,7 @@ Try uncommenting this line by deleting `//` and refreshing the pattern. You can also use the keyboard shortcut `cmd-/` to toggle comments on and off. You might noticed that some comments in the REPL samples include some words starting with a "@", like `@title` or `@license`. -Those are just a convention to define some information about the music. We will talk about it in the *metadata* section. +Those are just a convention to define some information about the music. We will talk about it in the _metadata_ section. # Strings diff --git a/website/src/pages/learn/metadata.mdx b/website/src/pages/learn/metadata.mdx index 663e82c0..47a19c1e 100644 --- a/website/src/pages/learn/metadata.mdx +++ b/website/src/pages/learn/metadata.mdx @@ -21,6 +21,7 @@ Like other comments, those are ignored by Strudel, but it can be used by other t It is for instance used by the [swatch tool](https://github.com/tidalcycles/strudel/tree/main/my-patterns) to display pattern titles in the [examples page](https://strudel.tidalcycles.org/examples/). Available tags are: + - `@title`: music title - `@by`: music author(s), separated with comma, eventually followed with a link in `<>` (ex: `@author john doe `) - `@license`: music license diff --git a/website/src/pages/metadata_parser.js b/website/src/pages/metadata_parser.js index f6b8f628..9fadc30d 100644 --- a/website/src/pages/metadata_parser.js +++ b/website/src/pages/metadata_parser.js @@ -2,13 +2,13 @@ export function getMetadata(raw_code) { // https://stackoverflow.com/a/15123777 const comment_regexp = /\/\*([\s\S]*?)\*\/|([^\\:]|^)\/\/(.*)$/gm; - const tag_regexp = /@([a-z]*):? (.*)/gm + const tag_regexp = /@([a-z]*):? (.*)/gm; const tags = {}; for (const match of raw_code.matchAll(comment_regexp)) { const comment = match[1] ? match[1] : '' + match[3] ? match[3] : ''; for (const tag_match of comment.trim().matchAll(tag_regexp)) { - tags[tag_match[1]] = tag_match[2].trim() + tags[tag_match[1]] = tag_match[2].trim(); } } diff --git a/website/src/pages/swatch/list.json.js b/website/src/pages/swatch/list.json.js index febc52aa..4bf6bb4a 100644 --- a/website/src/pages/swatch/list.json.js +++ b/website/src/pages/swatch/list.json.js @@ -5,7 +5,7 @@ export function getMyPatterns() { return Object.fromEntries( Object.entries(my) .filter(([name]) => name.endsWith('.txt')) - .map(([name, raw]) => [ getMetadata(raw)['title'] || name.split('/').slice(-1), raw ]), + .map(([name, raw]) => [getMetadata(raw)['title'] || name.split('/').slice(-1), raw]), ); } From 2e221520895e8eda9ae1063c7f2e0c385e91ed20 Mon Sep 17 00:00:00 2001 From: Roipoussiere Date: Tue, 6 Jun 2023 19:15:46 +0200 Subject: [PATCH 06/11] metadata: allow several values and one-liners --- website/src/pages/metadata_parser.js | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/website/src/pages/metadata_parser.js b/website/src/pages/metadata_parser.js index 9fadc30d..b4448c2a 100644 --- a/website/src/pages/metadata_parser.js +++ b/website/src/pages/metadata_parser.js @@ -1,14 +1,26 @@ -export function getMetadata(raw_code) { - // https://stackoverflow.com/a/15123777 - const comment_regexp = /\/\*([\s\S]*?)\*\/|([^\\:]|^)\/\/(.*)$/gm; +const ALLOW_MANY = ['by', 'url', 'genre', 'license']; - const tag_regexp = /@([a-z]*):? (.*)/gm; +export function getMetadata(raw_code) { + const comment_regexp = /\/\*([\s\S]*?)\*\/|\/\/(.*)$/gm; const tags = {}; for (const match of raw_code.matchAll(comment_regexp)) { - const comment = match[1] ? match[1] : '' + match[3] ? match[3] : ''; - for (const tag_match of comment.trim().matchAll(tag_regexp)) { - tags[tag_match[1]] = tag_match[2].trim(); + const tag_matches = (match[1] || match[2] || '').trim().split('@').slice(1); + for (const tag_match of tag_matches) { + let [tag, tag_value] = tag_match.split(/ (.*)/s); + tag = tag.trim(); + tag_value = (tag_value || '').replaceAll(/ +/g, ' ').trim(); + + if (ALLOW_MANY.includes(tag)) { + const tag_list = tag_value + .split(/[,\n]/) + .map((t) => t.trim()) + .filter((t) => t !== ''); + tags[tag] = tag in tags ? tags[tag].concat(tag_list) : tag_list; + } else { + tag_value = tag_value.replaceAll(/\s+/g, ' '); + tags[tag] = tag in tags ? tags[tag] + ' ' + tag_value : tag_value; + } } } From bd7f4dd73f51fcee93d6d86555bfb1c947b5c390 Mon Sep 17 00:00:00 2001 From: Roipoussiere Date: Tue, 6 Jun 2023 21:01:09 +0200 Subject: [PATCH 07/11] metadata: add tests --- test/metadata.test.mjs | 196 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 test/metadata.test.mjs diff --git a/test/metadata.test.mjs b/test/metadata.test.mjs new file mode 100644 index 00000000..21646d8f --- /dev/null +++ b/test/metadata.test.mjs @@ -0,0 +1,196 @@ +import { describe, expect, it } from 'vitest'; +import { getMetadata } from '../website/src/pages/metadata_parser'; + +describe.concurrent('Metadata parser', () => { + it('loads a tag from inline comment', async () => { + const tune = `// @title Awesome song`; + expect(getMetadata(tune)).toStrictEqual({ + title: 'Awesome song', + }); + }); + + it('loads many tags from inline comments', async () => { + const tune = `// @title Awesome song +// @by Sam`; + expect(getMetadata(tune)).toStrictEqual({ + title: 'Awesome song', + by: ['Sam'], + }); + }); + + it('loads many tags from one inline comment', async () => { + const tune = `// @title Awesome song @by Sam`; + expect(getMetadata(tune)).toStrictEqual({ + title: 'Awesome song', + by: ['Sam'], + }); + }); + + it('loads a tag from a block comment', async () => { + const tune = `/* @title Awesome song */`; + expect(getMetadata(tune)).toStrictEqual({ + title: 'Awesome song', + }); + }); + + it('loads many tags from a block comment', async () => { + const tune = `/* +@title Awesome song +@by Sam +*/`; + expect(getMetadata(tune)).toStrictEqual({ + title: 'Awesome song', + by: ['Sam'], + }); + }); + + it('loads many tags from many block comments', async () => { + const tune = `/* @title Awesome song */ +/* @by Sam */`; + expect(getMetadata(tune)).toStrictEqual({ + title: 'Awesome song', + by: ['Sam'], + }); + }); + + it('loads many tags from mixed comments', async () => { + const tune = `/* @title Awesome song */ +// @by Sam +`; + expect(getMetadata(tune)).toStrictEqual({ + title: 'Awesome song', + by: ['Sam'], + }); + }); + + it('loads a tag list with comma-separated values syntax', async () => { + const tune = `// @by Sam, Sandy`; + expect(getMetadata(tune)).toStrictEqual({ + by: ['Sam', 'Sandy'], + }); + }); + + it('loads a tag list with duplicate keys syntax', async () => { + const tune = `// @by Sam +// @by Sandy`; + expect(getMetadata(tune)).toStrictEqual({ + by: ['Sam', 'Sandy'], + }); + }); + + it('loads a tag list with duplicate keys syntax, with prefixes', async () => { + const tune = `// song @by Sam +// samples @by Sandy`; + expect(getMetadata(tune)).toStrictEqual({ + by: ['Sam', 'Sandy'], + }); + }); + + it('loads many tag lists with duplicate keys syntax, within code', async () => { + const tune = `note("a3 c#4 e4 a4") // @by Sam @license CC0 + s("bd hh sd hh") // @by Sandy @license CC BY-NC-SA`; + expect(getMetadata(tune)).toStrictEqual({ + by: ['Sam', 'Sandy'], + license: ['CC0', 'CC BY-NC-SA'], + }); + }); + + it('loads a tag list with duplicate keys syntax from block comment', async () => { + const tune = `/* @by Sam +@by Sandy */`; + expect(getMetadata(tune)).toStrictEqual({ + by: ['Sam', 'Sandy'], + }); + }); + + it('loads a tag list with newline syntax', async () => { + const tune = `/* +@by Sam + Sandy */`; + expect(getMetadata(tune)).toStrictEqual({ + by: ['Sam', 'Sandy'], + }); + }); + + it('loads a multiline tag from block comment', async () => { + const tune = `/* +@details I wrote this song in February 19th, 2023. + It was around midnight and I was lying on + the sofa in the living room. +*/`; + expect(getMetadata(tune)).toStrictEqual({ + details: + 'I wrote this song in February 19th, 2023. ' + + 'It was around midnight and I was lying on the sofa in the living room.', + }); + }); + + it('loads a multiline tag from block comment with duplicate keys', async () => { + const tune = `/* +@details I wrote this song in February 19th, 2023. +@details It was around midnight and I was lying on + the sofa in the living room. +*/`; + expect(getMetadata(tune)).toStrictEqual({ + details: + 'I wrote this song in February 19th, 2023. ' + + 'It was around midnight and I was lying on the sofa in the living room.', + }); + }); + + it('loads a multiline tag from inline comments', async () => { + const tune = `// @details I wrote this song in February 19th, 2023. +// @details It was around midnight and I was lying on +// @details the sofa in the living room. +*/`; + expect(getMetadata(tune)).toStrictEqual({ + details: + 'I wrote this song in February 19th, 2023. ' + + 'It was around midnight and I was lying on the sofa in the living room.', + }); + }); + + it('loads empty tags from inline comments', async () => { + const tune = `// @title +// @by`; + expect(getMetadata(tune)).toStrictEqual({ + title: '', + by: [], + }); + }); + + it('loads empty tags from block comment', async () => { + const tune = `/* @title +@by */`; + expect(getMetadata(tune)).toStrictEqual({ + title: '', + by: [], + }); + }); + + it('loads tags with whitespaces from inline comments', async () => { + const tune = ` // @title Awesome song + // @by Sam Tagada `; + expect(getMetadata(tune)).toStrictEqual({ + title: 'Awesome song', + by: ['Sam Tagada'], + }); + }); + + it('loads tags with whitespaces from block comment', async () => { + const tune = ` /* @title Awesome song + @by Sam Tagada */ `; + expect(getMetadata(tune)).toStrictEqual({ + title: 'Awesome song', + by: ['Sam Tagada'], + }); + }); + + it('does not load code that looks like a metadata tag', async () => { + const tune = ` +const str1 = '@title Awesome song' +`; + expect(getMetadata(tune)).toStrictEqual({}); + }); + +}); From 96ac054392bca821f0d177d6ad3a5aef07a1e45b Mon Sep 17 00:00:00 2001 From: Roipoussiere Date: Tue, 6 Jun 2023 21:33:32 +0200 Subject: [PATCH 08/11] metadata: improve doc --- website/src/pages/learn/code.mdx | 2 +- website/src/pages/learn/metadata.mdx | 75 ++++++++++++++++++++++++---- 2 files changed, 67 insertions(+), 10 deletions(-) diff --git a/website/src/pages/learn/code.mdx b/website/src/pages/learn/code.mdx index 8551a8a2..5e2b37b4 100644 --- a/website/src/pages/learn/code.mdx +++ b/website/src/pages/learn/code.mdx @@ -68,7 +68,7 @@ Try uncommenting this line by deleting `//` and refreshing the pattern. You can also use the keyboard shortcut `cmd-/` to toggle comments on and off. You might noticed that some comments in the REPL samples include some words starting with a "@", like `@title` or `@license`. -Those are just a convention to define some information about the music. We will talk about it in the _metadata_ section. +Those are just a convention to define some information about the music. We will talk about it in the [Music metadata](/learn/metadata) section. # Strings diff --git a/website/src/pages/learn/metadata.mdx b/website/src/pages/learn/metadata.mdx index 47a19c1e..2f3be348 100644 --- a/website/src/pages/learn/metadata.mdx +++ b/website/src/pages/learn/metadata.mdx @@ -8,24 +8,81 @@ import { JsDoc } from '../../docs/JsDoc'; # Music metadata -You can optionally add some music metadata in your Strudel code, by using tags in code comments like this: +You can optionally add some music metadata in your Strudel code, by using tags in code comments: -``` -// @license: CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ -// @author: Felix Roos -// @details: Inspired by Friendship - Let's not talk about it (1979) :) +```js +// @title Hey Hoo +// @by Sam Tagada +// @license CC BY-NC-SA ``` Like other comments, those are ignored by Strudel, but it can be used by other tools to retrieve some information about the music. It is for instance used by the [swatch tool](https://github.com/tidalcycles/strudel/tree/main/my-patterns) to display pattern titles in the [examples page](https://strudel.tidalcycles.org/examples/). +## Alternative syntax + +You can also use comment blocks: + +```js +/* +@title Hey Hoo +@by Sam Tagada +@license CC BY-NC-SA +*/ +``` + +Or define multiple tags in one line: + +```js +// @title Hey Hoo @by Sam Tagada @license CC BY-NC-SA +``` + +## Tags list + Available tags are: - `@title`: music title -- `@by`: music author(s), separated with comma, eventually followed with a link in `<>` (ex: `@author john doe `) -- `@license`: music license +- `@by`: music author(s), separated by comma, eventually followed with a link in `<>` (ex: `@by John Doe `) +- `@license`: music license(s) - `@details`: some additional information about the music -- `@url`: web page related to the music (git repo, soundcloud link, etc.) -- `@genre`: music genre (pop, jazz, etc) +- `@url`: web page(s) related to the music (git repo, soundcloud link, etc.) +- `@genre`: music genre(s) (pop, jazz, etc) - `@album`: music album name + +## Multiple values + +Some of them accepts several values, using the comma or new line separator, or duplicating the tag: + +```js +/* +@by Sam Tagada + Jimmy +@genre pop, jazz +@url https://example.com +@url https://example.org +*/ +``` + +You can also add optinal prefixes and use tags where you want: + +```js +/* +song @by Sam Tagada +samples @by Jimmy +*/ +... +note("a3 c#4 e4 a4") // @by Sandy +``` + +## Multiline + +If a tag doesn't accept a list, it can take multi-line values: + +```js +/* +@details I wrote this song in February 19th, 2023. + It was around midnight and I was lying on + the sofa in the living room. +*/ +``` From 185318a70c75e33ad30e908001af442a16ee74d0 Mon Sep 17 00:00:00 2001 From: Roipoussiere Date: Wed, 7 Jun 2023 11:02:38 +0200 Subject: [PATCH 09/11] metadata: fix typos --- test/metadata.test.mjs | 8 ++++---- website/src/pages/learn/metadata.mdx | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/test/metadata.test.mjs b/test/metadata.test.mjs index 21646d8f..9487ebe2 100644 --- a/test/metadata.test.mjs +++ b/test/metadata.test.mjs @@ -187,10 +187,10 @@ describe.concurrent('Metadata parser', () => { }); it('does not load code that looks like a metadata tag', async () => { - const tune = ` -const str1 = '@title Awesome song' -`; + const tune = `const str1 = '@title Awesome song'`; + // need a lexer to avoid this one, but it's a pretty rare use case: + // const tune = `const str1 = '// @title Awesome song'`; + expect(getMetadata(tune)).toStrictEqual({}); }); - }); diff --git a/website/src/pages/learn/metadata.mdx b/website/src/pages/learn/metadata.mdx index 2f3be348..7038644d 100644 --- a/website/src/pages/learn/metadata.mdx +++ b/website/src/pages/learn/metadata.mdx @@ -64,7 +64,7 @@ Some of them accepts several values, using the comma or new line separator, or d */ ``` -You can also add optinal prefixes and use tags where you want: +You can also add optional prefixes and use tags where you want: ```js /* From 51e817468919045b10da51febaa6484f736295a1 Mon Sep 17 00:00:00 2001 From: Roipoussiere Date: Wed, 7 Jun 2023 12:26:23 +0200 Subject: [PATCH 10/11] metadata: add quotes syntax for titles --- test/metadata.test.mjs | 68 ++++++++++++++++++++++++---- website/src/pages/learn/code.mdx | 2 +- website/src/pages/learn/metadata.mdx | 6 +++ website/src/pages/metadata_parser.js | 10 +++- website/src/repl/tunes.mjs | 56 +++++++++++------------ 5 files changed, 102 insertions(+), 40 deletions(-) diff --git a/test/metadata.test.mjs b/test/metadata.test.mjs index 9487ebe2..cbd0f8a3 100644 --- a/test/metadata.test.mjs +++ b/test/metadata.test.mjs @@ -63,6 +63,51 @@ describe.concurrent('Metadata parser', () => { }); }); + it('loads a title tag with quotes syntax', async () => { + const tune = `// "Awesome song"`; + expect(getMetadata(tune)).toStrictEqual({ + title: 'Awesome song', + }); + }); + + it('loads a title tag with quotes syntax among other tags', async () => { + const tune = `// "Awesome song" made @by Sam`; + expect(getMetadata(tune)).toStrictEqual({ + title: 'Awesome song', + by: ['Sam'], + }); + }); + + it('loads a title tag with quotes syntax from block comment', async () => { + const tune = `/* "Awesome song" +@by Sam */`; + expect(getMetadata(tune)).toStrictEqual({ + title: 'Awesome song', + by: ['Sam'], + }); + }); + + it('does not load a title tag with quotes syntax after a prefix', async () => { + const tune = `// I don't care about those "metadata".`; + expect(getMetadata(tune)).toStrictEqual({}); + }); + + it('does not load a title tag with quotes syntax after an other comment', async () => { + const tune = `// I don't care about those +// "metadata"`; + expect(getMetadata(tune)).toStrictEqual({}); + }); + + it('does not load a title tag with quotes syntax after other tags', async () => { + const tune = `/* +@by Sam aka "Lady Strudel" + "Sandyyy" +*/`; + expect(getMetadata(tune)).toStrictEqual({ + by: ['Sam aka "Lady Strudel"', '"Sandyyy"'], + }); + }); + it('loads a tag list with comma-separated values syntax', async () => { const tune = `// @by Sam, Sandy`; expect(getMetadata(tune)).toStrictEqual({ @@ -159,15 +204,6 @@ describe.concurrent('Metadata parser', () => { }); }); - it('loads empty tags from block comment', async () => { - const tune = `/* @title -@by */`; - expect(getMetadata(tune)).toStrictEqual({ - title: '', - by: [], - }); - }); - it('loads tags with whitespaces from inline comments', async () => { const tune = ` // @title Awesome song // @by Sam Tagada `; @@ -186,6 +222,20 @@ describe.concurrent('Metadata parser', () => { }); }); + it('loads empty tags from block comment', async () => { + const tune = `/* @title +@by */`; + expect(getMetadata(tune)).toStrictEqual({ + title: '', + by: [], + }); + }); + + it('does not load tags if there is not', async () => { + const tune = `note("a3 c#4 e4 a4")`; + expect(getMetadata(tune)).toStrictEqual({}); + }); + it('does not load code that looks like a metadata tag', async () => { const tune = `const str1 = '@title Awesome song'`; // need a lexer to avoid this one, but it's a pretty rare use case: diff --git a/website/src/pages/learn/code.mdx b/website/src/pages/learn/code.mdx index 5e2b37b4..b74002aa 100644 --- a/website/src/pages/learn/code.mdx +++ b/website/src/pages/learn/code.mdx @@ -67,7 +67,7 @@ It is a handy way to quickly turn code on and off. Try uncommenting this line by deleting `//` and refreshing the pattern. You can also use the keyboard shortcut `cmd-/` to toggle comments on and off. -You might noticed that some comments in the REPL samples include some words starting with a "@", like `@title` or `@license`. +You might noticed that some comments in the REPL samples include some words starting with a "@", like `@by` or `@license`. Those are just a convention to define some information about the music. We will talk about it in the [Music metadata](/learn/metadata) section. # Strings diff --git a/website/src/pages/learn/metadata.mdx b/website/src/pages/learn/metadata.mdx index 7038644d..27f46921 100644 --- a/website/src/pages/learn/metadata.mdx +++ b/website/src/pages/learn/metadata.mdx @@ -38,6 +38,12 @@ Or define multiple tags in one line: // @title Hey Hoo @by Sam Tagada @license CC BY-NC-SA ``` +The `title` tag has an alternative syntax using quotes (must be defined at the very begining): + +```js +// "Hey Hoo" @by Sam Tagada +``` + ## Tags list Available tags are: diff --git a/website/src/pages/metadata_parser.js b/website/src/pages/metadata_parser.js index b4448c2a..6a92a8a6 100644 --- a/website/src/pages/metadata_parser.js +++ b/website/src/pages/metadata_parser.js @@ -2,10 +2,16 @@ const ALLOW_MANY = ['by', 'url', 'genre', 'license']; export function getMetadata(raw_code) { const comment_regexp = /\/\*([\s\S]*?)\*\/|\/\/(.*)$/gm; + const comments = [...raw_code.matchAll(comment_regexp)].map((c) => (c[1] || c[2] || '').trim()); const tags = {}; - for (const match of raw_code.matchAll(comment_regexp)) { - const tag_matches = (match[1] || match[2] || '').trim().split('@').slice(1); + const [prefix, title] = (comments[0] || '').split('"'); + if (prefix.trim() === '' && title !== undefined) { + tags['title'] = title; + } + + for (const comment of comments) { + const tag_matches = comment.split('@').slice(1); for (const tag_match of tag_matches) { let [tag, tag_value] = tag_match.split(/ (.*)/s); tag = tag.trim(); diff --git a/website/src/repl/tunes.mjs b/website/src/repl/tunes.mjs index 0e60ab26..f2d492e9 100644 --- a/website/src/repl/tunes.mjs +++ b/website/src/repl/tunes.mjs @@ -121,7 +121,7 @@ stack( .room(1) //.pianoroll({fold:1})`; -export const caverave = `// @title Caverave +export const caverave = `// "Caverave" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos @@ -186,7 +186,7 @@ export const barryHarris = `// adapted from a Barry Harris excercise .color('#00B8D4') `; -export const blippyRhodes = `// @title Blippy Rhodes +export const blippyRhodes = `// "Blippy Rhodes" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos @@ -230,7 +230,7 @@ stack( ).fast(3/2) //.pianoroll({fold:1})`; -export const wavyKalimba = `// @title Wavy kalimba +export const wavyKalimba = `// "Wavy kalimba" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos @@ -262,7 +262,7 @@ stack( .delay(.2) `; -export const festivalOfFingers = `// @title Festival of fingers +export const festivalOfFingers = `// "Festival of fingers" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos @@ -279,7 +279,7 @@ stack( .note().piano()`; // iter, echo, echoWith -export const undergroundPlumber = `// @title Underground plumber +export const undergroundPlumber = `// "Underground plumber" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos // @details inspired by Friendship - Let's not talk about it (1979) :) @@ -307,7 +307,7 @@ stack( .fast(2/3) .pianoroll({})`; -export const bridgeIsOver = `// @title Bridge is over +export const bridgeIsOver = `// "Bridge is over" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos, bassline by BDP - The Bridge Is Over @@ -330,7 +330,7 @@ stack( .pianoroll() `; -export const goodTimes = `// @title Good times +export const goodTimes = `// "Good times" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos @@ -375,7 +375,7 @@ stack( .pianoroll({maxMidi:100,minMidi:20})`; */ -export const echoPiano = `// @title Echo piano +export const echoPiano = `// "Echo piano" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos @@ -424,7 +424,7 @@ stack( .legato(.5) ).fast(2).note()`; -export const randomBells = `// @title Random bells +export const randomBells = `// "Random bells" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos @@ -449,7 +449,7 @@ stack( .pianoroll({minMidi:20,maxMidi:120,background:'transparent'}) `; -export const waa2 = `// @title Waa2 +export const waa2 = `// "Waa2" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos @@ -467,7 +467,7 @@ n( .room(.5) `; -export const hyperpop = `// @title Hyperpop +export const hyperpop = `// "Hyperpop" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos @@ -520,7 +520,7 @@ stack( ).slow(2) // strudel disable-highlighting`; -export const festivalOfFingers3 = `// @title Festival of fingers 3 +export const festivalOfFingers3 = `// "Festival of fingers 3" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos @@ -540,7 +540,7 @@ export const festivalOfFingers3 = `// @title Festival of fingers 3 .note().piano() //.pianoroll({maxMidi:160})`; -export const meltingsubmarine = `// @title Melting submarine +export const meltingsubmarine = `// "Melting submarine" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos @@ -580,7 +580,7 @@ stack( ) .slow(3/2)`; -export const outroMusic = `// @title Outro music +export const outroMusic = `// "Outro music" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos @@ -611,7 +611,7 @@ samples({ //.pianoroll({autorange:1,vertical:1,fold:0}) `; -export const bassFuge = `// @title Bass fuge +export const bassFuge = `// "Bass fuge" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos @@ -639,7 +639,7 @@ x=>x.add(7).color('steelblue') .stack(s("bd:1*2,~ sd:0,[~ hh:0]*2")) .pianoroll({vertical:1})`; -export const chop = `// @title Chop +export const chop = `// "Chop" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos @@ -654,7 +654,7 @@ s("p") .sustain(.6) `; -export const delay = `// @title Delay +export const delay = `// "Delay" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos @@ -665,7 +665,7 @@ stack( .delayfeedback(".6 | .8") ).sometimes(x=>x.speed("-1"))`; -export const orbit = `// @title Orbit +export const orbit = `// "Orbit" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos @@ -681,7 +681,7 @@ stack( .orbit(2) ).sometimes(x=>x.speed("-1"))`; -export const belldub = `// @title Belldub +export const belldub = `// "Belldub" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos @@ -716,7 +716,7 @@ stack( .mask("<1 0>/8") ).slow(5)`; -export const dinofunk = `// @title Dinofunk +export const dinofunk = `// "Dinofunk" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos @@ -739,7 +739,7 @@ note("Abm7".voicings('lefthand').struct("x(3,8,1)".slow(2))), note("").s('dino').delay(.8).slow(8).room(.5) )`; -export const sampleDemo = `// @title Sample demo +export const sampleDemo = `// "Sample demo" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos @@ -757,7 +757,7 @@ stack( .release(.1).room(.5) )`; -export const holyflute = `// @title Holy flute +export const holyflute = `// "Holy flute" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos @@ -772,7 +772,7 @@ export const holyflute = `// @title Holy flute .pianoroll({fold:0,autorange:0,vertical:0,cycles:12,smear:0,minMidi:40}) `; -export const flatrave = `// @title Flatrave +export const flatrave = `// "Flatrave" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos @@ -797,7 +797,7 @@ stack( .decay(.05).sustain(0).delay(.2).degradeBy(.5).mask("<0 1>/16") )`; -export const amensister = `// @title Amensister +export const amensister = `// "Amensister" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos @@ -833,7 +833,7 @@ stack( n("0 1").s("east").delay(.5).degradeBy(.8).speed(rand.range(.5,1.5)) ).reset("")`; -export const juxUndTollerei = `// @title Jux und tollerei +export const juxUndTollerei = `// "Jux und tollerei" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos @@ -848,7 +848,7 @@ note("c3 eb3 g3 bb3").palindrome() .delay(.5).delaytime(.1).delayfeedback(.4) .pianoroll()`; -export const csoundDemo = `// @title CSound demo +export const csoundDemo = `// "CSound demo" // @license with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos @@ -884,7 +884,7 @@ endin\` //.pianoroll() .csound('CoolSynth')`; -export const loungeSponge = `// @title Lounge sponge +export const loungeSponge = `// "Lounge sponge" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos, livecode.orc by Steven Yi @@ -904,7 +904,7 @@ stack( s("bd*2,[~ hh]*2,~ cp").bank('RolandTR909') )`; -export const arpoon = `// @title Arpoon +export const arpoon = `// "Arpoon" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @by Felix Roos From 799afd511a383172b578cadb751c9216f5b508d5 Mon Sep 17 00:00:00 2001 From: Stephan Renatus Date: Wed, 7 Jun 2023 13:22:00 +0200 Subject: [PATCH 11/11] learn/tonal: fix typo in "scaleTran[s]pose" --- website/src/pages/learn/tonal.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/src/pages/learn/tonal.mdx b/website/src/pages/learn/tonal.mdx index 5932d4a9..33121ab7 100644 --- a/website/src/pages/learn/tonal.mdx +++ b/website/src/pages/learn/tonal.mdx @@ -24,7 +24,7 @@ Instead of numbers, scientific interval notation can be used as well: ### scale(name) -Turns numbers into notes in the scale (zero indexed). Also sets scale for other scale operations, like scaleTranpose. +Turns numbers into notes in the scale (zero indexed). Also sets scale for other scale operations, like scaleTranspose.