From 811346e83d4a7f6cd014085baa494f6e9d5b59b1 Mon Sep 17 00:00:00 2001 From: Daria Cotocu Date: Wed, 24 May 2023 17:24:34 +0100 Subject: [PATCH 01/63] Solmization added --- packages/core/test/solmization.test.js | 17 +++++++++++++++++ packages/core/util.mjs | 21 ++++++++++++++++++--- 2 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 packages/core/test/solmization.test.js diff --git a/packages/core/test/solmization.test.js b/packages/core/test/solmization.test.js new file mode 100644 index 000000000..28528aef1 --- /dev/null +++ b/packages/core/test/solmization.test.js @@ -0,0 +1,17 @@ + +/*test for issue 302 support alternative solmization types */ +import { midi2note } from '../util.mjs'; + +console.log(midi2note(60, 'letters')); /* should be C4*/ +console.log(midi2note(60, 'solfeggio')); /* should be Do4*/ +console.log(midi2note(60, 'indian')); /* should be Sa4*/ +console.log(midi2note(60, 'german')); /* should be C4*/ +console.log(midi2note(60, 'byzantine')); /* should be Ni4*/ +console.log(midi2note(60, 'japanese')); /* should be I4*/ + +console.log(midi2note(70, 'letters')); /* should be Bb4*/ +console.log(midi2note(70, 'solfeggio')); /* should be Sib4*/ +console.log(midi2note(70, 'indian')); /* should be Ni4*/ +console.log(midi2note(70, 'german')); /* should be Hb4*/ +console.log(midi2note(70, 'byzantine')); /* should be Zob4*/ +console.log(midi2note(70, 'japanese')); /* should be To4*/ diff --git a/packages/core/util.mjs b/packages/core/util.mjs index 2b43cf0b6..6c20cbd76 100644 --- a/packages/core/util.mjs +++ b/packages/core/util.mjs @@ -71,11 +71,26 @@ export const getFreq = (noteOrMidi) => { * @deprecated does not appear to be referenced or invoked anywhere in the codebase * @noAutocomplete */ -export const midi2note = (n) => { +/* added code from here to solve issue 302*/ + +export const midi2note = (n,notation = 'letters') => { + const solfeggio = ['Do','Reb','Re', 'Mib','Mi','Fa', 'Solb','Sol', 'Lab', 'La', 'Sib','Si']; /*solffegio notes*/ + const indian = ['Sa', 'Re', 'Ga', 'Ma', 'Pa', 'Dha', 'Ni']; /*indian musical notes, seems like they do not use flats or sharps*/ + const german = ['C', 'Db', 'D', 'Eb', 'E', 'F', 'Gb', 'G', 'Ab', 'A','Hb','H']; /*german & dutch musical notes*/ + const byzantine = ['Ni', 'Pab', 'Pa', 'Voub', 'Vou', 'Ga', 'Dib', 'Di', 'Keb', 'Ke', 'Zob','Zo']; /*byzantine musical notes*/ + const japanese = [ 'I', 'Ro', 'Ha', 'Ni' , 'Ho' , 'He', 'To']; /*traditional japanese musical notes, seems like they do not use falts or sharps*/ + const pc = notation === 'solfeggio' ? solfeggio : /*check if its is any of the following*/ + notation === 'indian' ? indian : + notation === 'german' ? german : + notation === 'byzantine' ? byzantine : + notation === 'japanese' ? japanese : + ['C', 'Db', 'D', 'Eb', 'E', 'F', 'Gb', 'G', 'Ab', 'A', 'Bb', 'B']; /*if not use standard version*/ + const note = pc[n % 12]; /*calculating the midi value to the note*/ const oct = Math.floor(n / 12) - 1; - const pc = ['C', 'Db', 'D', 'Eb', 'E', 'F', 'Gb', 'G', 'Ab', 'A', 'Bb', 'B'][n % 12]; - return pc + oct; + return note + oct; }; +/*testing if the function works by using the file solmization.test.mjs + in the test folder*/ // modulo that works with negative numbers e.g. _mod(-1, 3) = 2. Works on numbers (rather than patterns of numbers, as @mod@ from pattern.mjs does) export const _mod = (n, m) => ((n % m) + m) % m; From ccf775e976767b950ce2477ae41bbea24abfb382 Mon Sep 17 00:00:00 2001 From: Daria Cotocu Date: Wed, 24 May 2023 17:57:05 +0100 Subject: [PATCH 02/63] Format code --- packages/core/test/solmization.test.js | 1 - packages/core/util.mjs | 59 ++++++++++++++++++++------ 2 files changed, 47 insertions(+), 13 deletions(-) diff --git a/packages/core/test/solmization.test.js b/packages/core/test/solmization.test.js index 28528aef1..0c04a1901 100644 --- a/packages/core/test/solmization.test.js +++ b/packages/core/test/solmization.test.js @@ -1,4 +1,3 @@ - /*test for issue 302 support alternative solmization types */ import { midi2note } from '../util.mjs'; diff --git a/packages/core/util.mjs b/packages/core/util.mjs index 6c20cbd76..1e6d9a7b6 100644 --- a/packages/core/util.mjs +++ b/packages/core/util.mjs @@ -73,18 +73,53 @@ export const getFreq = (noteOrMidi) => { */ /* added code from here to solve issue 302*/ -export const midi2note = (n,notation = 'letters') => { - const solfeggio = ['Do','Reb','Re', 'Mib','Mi','Fa', 'Solb','Sol', 'Lab', 'La', 'Sib','Si']; /*solffegio notes*/ - const indian = ['Sa', 'Re', 'Ga', 'Ma', 'Pa', 'Dha', 'Ni']; /*indian musical notes, seems like they do not use flats or sharps*/ - const german = ['C', 'Db', 'D', 'Eb', 'E', 'F', 'Gb', 'G', 'Ab', 'A','Hb','H']; /*german & dutch musical notes*/ - const byzantine = ['Ni', 'Pab', 'Pa', 'Voub', 'Vou', 'Ga', 'Dib', 'Di', 'Keb', 'Ke', 'Zob','Zo']; /*byzantine musical notes*/ - const japanese = [ 'I', 'Ro', 'Ha', 'Ni' , 'Ho' , 'He', 'To']; /*traditional japanese musical notes, seems like they do not use falts or sharps*/ - const pc = notation === 'solfeggio' ? solfeggio : /*check if its is any of the following*/ - notation === 'indian' ? indian : - notation === 'german' ? german : - notation === 'byzantine' ? byzantine : - notation === 'japanese' ? japanese : - ['C', 'Db', 'D', 'Eb', 'E', 'F', 'Gb', 'G', 'Ab', 'A', 'Bb', 'B']; /*if not use standard version*/ +export const midi2note = (n, notation = 'letters') => { + const solfeggio = ['Do', 'Reb', 'Re', 'Mib', 'Mi', 'Fa', 'Solb', 'Sol', 'Lab', 'La', 'Sib', 'Si']; /*solffegio notes*/ + const indian = [ + 'Sa', + 'Re', + 'Ga', + 'Ma', + 'Pa', + 'Dha', + 'Ni', + ]; /*indian musical notes, seems like they do not use flats or sharps*/ + const german = ['C', 'Db', 'D', 'Eb', 'E', 'F', 'Gb', 'G', 'Ab', 'A', 'Hb', 'H']; /*german & dutch musical notes*/ + const byzantine = [ + 'Ni', + 'Pab', + 'Pa', + 'Voub', + 'Vou', + 'Ga', + 'Dib', + 'Di', + 'Keb', + 'Ke', + 'Zob', + 'Zo', + ]; /*byzantine musical notes*/ + const japanese = [ + 'I', + 'Ro', + 'Ha', + 'Ni', + 'Ho', + 'He', + 'To', + ]; /*traditional japanese musical notes, seems like they do not use falts or sharps*/ + const pc = + notation === 'solfeggio' + ? solfeggio /*check if its is any of the following*/ + : notation === 'indian' + ? indian + : notation === 'german' + ? german + : notation === 'byzantine' + ? byzantine + : notation === 'japanese' + ? japanese + : ['C', 'Db', 'D', 'Eb', 'E', 'F', 'Gb', 'G', 'Ab', 'A', 'Bb', 'B']; /*if not use standard version*/ const note = pc[n % 12]; /*calculating the midi value to the note*/ const oct = Math.floor(n / 12) - 1; return note + oct; From cd7bc09f9c0d1b5ad263d1116144330d21fb82b1 Mon Sep 17 00:00:00 2001 From: Daria Cotocu Date: Wed, 24 May 2023 18:43:24 +0100 Subject: [PATCH 03/63] Update solmization.test.js --- packages/core/test/solmization.test.js | 52 ++++++++++++++++++++------ 1 file changed, 40 insertions(+), 12 deletions(-) diff --git a/packages/core/test/solmization.test.js b/packages/core/test/solmization.test.js index 0c04a1901..7a91c3b98 100644 --- a/packages/core/test/solmization.test.js +++ b/packages/core/test/solmization.test.js @@ -1,16 +1,44 @@ /*test for issue 302 support alternative solmization types */ import { midi2note } from '../util.mjs'; +import { test } from 'vitest'; +import assert from 'assert'; + +test('midi2note - letters', () => { + const result = midi2note(60, 'letters'); + const expected = 'C4'; + assert.equal(result, expected); +}); + +test('midi2note - solfeggio', () => { + const result = midi2note(60, 'solfeggio'); + const expected = 'Do4'; + assert.equal(result, expected); +}); + + +test('midi2note - indian', () => { + const result = midi2note(60, 'indian'); + const expected = 'Sa4'; + assert.equal(result, expected); + }); + +test('midi2note - german', () => { + const result = midi2note(60, 'german'); + const expected = 'C4'; + assert.equal(result, expected); + }); + +test('midi2note - byzantine', () => { + const result = midi2note(60, 'byzantine'); + const expected = 'Ni4'; + assert.equal(result, expected); + }); + +test('midi2note - japanese', () => { + const result = midi2note(60, 'japanese'); + const expected = 'I4'; + assert.equal(result, expected); + }); + -console.log(midi2note(60, 'letters')); /* should be C4*/ -console.log(midi2note(60, 'solfeggio')); /* should be Do4*/ -console.log(midi2note(60, 'indian')); /* should be Sa4*/ -console.log(midi2note(60, 'german')); /* should be C4*/ -console.log(midi2note(60, 'byzantine')); /* should be Ni4*/ -console.log(midi2note(60, 'japanese')); /* should be I4*/ -console.log(midi2note(70, 'letters')); /* should be Bb4*/ -console.log(midi2note(70, 'solfeggio')); /* should be Sib4*/ -console.log(midi2note(70, 'indian')); /* should be Ni4*/ -console.log(midi2note(70, 'german')); /* should be Hb4*/ -console.log(midi2note(70, 'byzantine')); /* should be Zob4*/ -console.log(midi2note(70, 'japanese')); /* should be To4*/ From b63f4eb503db9edd4182d5895a33677b5440dc84 Mon Sep 17 00:00:00 2001 From: Daria Cotocu Date: Wed, 24 May 2023 18:58:11 +0100 Subject: [PATCH 04/63] Update solmization.test.js --- package.json | 6 +-- packages/core/test/solmization.test.js | 36 ++++++++--------- pnpm-lock.yaml | 54 ++++++++++++++++++++++++-- 3 files changed, 70 insertions(+), 26 deletions(-) diff --git a/package.json b/package.json index 76d60cb34..ee26531c6 100644 --- a/package.json +++ b/package.json @@ -52,8 +52,7 @@ "@strudel.cycles/webaudio": "workspace:*", "@strudel.cycles/xen": "workspace:*", "acorn": "^8.8.1", - "dependency-tree": "^9.0.0", - "vitest": "^0.28.0" + "dependency-tree": "^9.0.0" }, "devDependencies": { "@vitest/ui": "^0.28.0", @@ -66,6 +65,7 @@ "jsdoc-to-markdown": "^8.0.0", "lerna": "^6.6.1", "prettier": "^2.8.8", - "rollup-plugin-visualizer": "^5.8.1" + "rollup-plugin-visualizer": "^5.8.1", + "vitest": "^0.28.0" } } diff --git a/packages/core/test/solmization.test.js b/packages/core/test/solmization.test.js index 7a91c3b98..fe66a4a8c 100644 --- a/packages/core/test/solmization.test.js +++ b/packages/core/test/solmization.test.js @@ -15,30 +15,26 @@ test('midi2note - solfeggio', () => { assert.equal(result, expected); }); - test('midi2note - indian', () => { - const result = midi2note(60, 'indian'); - const expected = 'Sa4'; - assert.equal(result, expected); - }); + const result = midi2note(60, 'indian'); + const expected = 'Sa4'; + assert.equal(result, expected); +}); test('midi2note - german', () => { - const result = midi2note(60, 'german'); - const expected = 'C4'; - assert.equal(result, expected); - }); + const result = midi2note(60, 'german'); + const expected = 'C4'; + assert.equal(result, expected); +}); test('midi2note - byzantine', () => { - const result = midi2note(60, 'byzantine'); - const expected = 'Ni4'; - assert.equal(result, expected); - }); + const result = midi2note(60, 'byzantine'); + const expected = 'Ni4'; + assert.equal(result, expected); +}); test('midi2note - japanese', () => { - const result = midi2note(60, 'japanese'); - const expected = 'I4'; - assert.equal(result, expected); - }); - - - + const result = midi2note(60, 'japanese'); + const expected = 'I4'; + assert.equal(result, expected); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a4df850b5..3731b000e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -28,9 +28,6 @@ importers: dependency-tree: specifier: ^9.0.0 version: 9.0.0 - vitest: - specifier: ^0.28.0 - version: 0.28.0(@vitest/ui@0.28.0) devDependencies: '@vitest/ui': specifier: ^0.28.0 @@ -65,6 +62,9 @@ importers: rollup-plugin-visualizer: specifier: ^5.8.1 version: 5.9.0 + vitest: + specifier: ^0.28.0 + version: 0.28.0(@vitest/ui@0.28.0) packages/core: dependencies: @@ -3443,6 +3443,7 @@ packages: /@polka/url@1.0.0-next.21: resolution: {integrity: sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==} + dev: true /@proload/core@0.3.3: resolution: {integrity: sha512-7dAFWsIK84C90AMl24+N/ProHKm4iw0akcnoKjRvbfHifJZBLhaDsDus1QJmhG12lXj4e/uB/8mB/0aduCW+NQ==} @@ -3890,9 +3891,11 @@ packages: resolution: {integrity: sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==} dependencies: '@types/chai': 4.3.4 + dev: true /@types/chai@4.3.4: resolution: {integrity: sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw==} + dev: true /@types/debug@4.1.7: resolution: {integrity: sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==} @@ -3969,6 +3972,7 @@ packages: /@types/node@18.11.18: resolution: {integrity: sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==} + dev: true /@types/node@18.16.3: resolution: {integrity: sha512-OPs5WnnT1xkCBiuQrZA4+YAV4HEJejmHneyraIaxsbev5yCEr6KMwINNFP9wQeFIw8FWcoTqF3vQsa5CDaI+8Q==} @@ -4422,6 +4426,7 @@ packages: '@vitest/spy': 0.28.0 '@vitest/utils': 0.28.0 chai: 4.3.7 + dev: true /@vitest/runner@0.28.0: resolution: {integrity: sha512-SXQO9aubp7Hg4DV4D5DP70wJ/4o0krH1gAPrSt+rhEZQbQvMaBJAHWOxEibwzLkklgoHreaMEvETFILkGQWXww==} @@ -4429,11 +4434,13 @@ packages: '@vitest/utils': 0.28.0 p-limit: 4.0.0 pathe: 1.1.0 + dev: true /@vitest/spy@0.28.0: resolution: {integrity: sha512-gYBDQIP0QDvxrscl2Id0BTbzLUbuAzFiFur3eHxH9Yt5cM6YCH/kxBrSHhmXTbu92UenLx53Gwq17u5N0zGNDQ==} dependencies: tinyspy: 1.0.2 + dev: true /@vitest/ui@0.28.0: resolution: {integrity: sha512-ihcVEx8t1gZXMboPGcIvoHk+PxiW5USxDMqnZOeUVIUm+XrRCtoJ96YDXdeR6MyPWeYLBPXfBWSxp5gMqoNSkw==} @@ -4443,6 +4450,7 @@ packages: pathe: 1.1.0 picocolors: 1.0.0 sirv: 2.0.2 + dev: true /@vitest/utils@0.28.0: resolution: {integrity: sha512-Dt+jDZbwriZWzJ5Hi9nAUnz9IPgNb+ACE96tWiXPp/u9NmCYWIWcuNoUOYS8HQyGFz31GiNYGvaZ4ZEDjAgi1g==} @@ -4452,6 +4460,7 @@ packages: loupe: 2.3.6 picocolors: 1.0.0 pretty-format: 27.5.1 + dev: true /@vscode/emmet-helper@2.8.6: resolution: {integrity: sha512-IIB8jbiKy37zN8bAIHx59YmnIelY78CGHtThnibD/d3tQOKRY83bYVi9blwmZVUZh6l9nfkYH3tvReaiNxY9EQ==} @@ -4517,6 +4526,7 @@ packages: /acorn-walk@8.2.0: resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} engines: {node: '>=0.4.0'} + dev: true /acorn@8.8.2: resolution: {integrity: sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==} @@ -4641,6 +4651,7 @@ packages: /ansi-styles@5.2.0: resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} engines: {node: '>=10'} + dev: true /ansi-styles@6.2.1: resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} @@ -4790,6 +4801,7 @@ packages: /assertion-error@1.1.0: resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} + dev: true /ast-module-types@3.0.0: resolution: {integrity: sha512-CMxMCOCS+4D+DkOQfuZf+vLrSEmY/7xtORwdxs4wtcC1wVgvk2MqFFTwQCFhvWsI4KPU9lcWXPI8DgRiz+xetQ==} @@ -5059,6 +5071,7 @@ packages: /buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + dev: true /buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} @@ -5109,6 +5122,7 @@ packages: /cac@6.7.14: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} + dev: true /cacache@16.1.3: resolution: {integrity: sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==} @@ -5238,6 +5252,7 @@ packages: loupe: 2.3.6 pathval: 1.1.1 type-detect: 4.0.8 + dev: true /chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} @@ -5285,6 +5300,7 @@ packages: /check-error@1.0.2: resolution: {integrity: sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==} + dev: true /chokidar@3.5.3: resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} @@ -5359,6 +5375,7 @@ packages: dependencies: slice-ansi: 5.0.0 string-width: 5.1.2 + dev: true /cli-width@3.0.0: resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==} @@ -5838,6 +5855,7 @@ packages: engines: {node: '>=6'} dependencies: type-detect: 4.0.8 + dev: true /deep-extend@0.6.0: resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} @@ -7036,6 +7054,7 @@ packages: /get-func-name@2.0.0: resolution: {integrity: sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==} + dev: true /get-intrinsic@1.2.0: resolution: {integrity: sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==} @@ -7867,6 +7886,7 @@ packages: /is-fullwidth-code-point@4.0.0: resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} engines: {node: '>=12'} + dev: true /is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} @@ -8510,6 +8530,7 @@ packages: /local-pkg@0.4.3: resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==} engines: {node: '>=14'} + dev: true /locate-path@2.0.0: resolution: {integrity: sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==} @@ -8602,6 +8623,7 @@ packages: resolution: {integrity: sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==} dependencies: get-func-name: 2.0.0 + dev: true /lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} @@ -9496,6 +9518,7 @@ packages: pathe: 1.1.0 pkg-types: 1.0.2 ufo: 1.1.1 + dev: true /modify-values@1.0.1: resolution: {integrity: sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==} @@ -9541,6 +9564,7 @@ packages: /mrmime@1.0.1: resolution: {integrity: sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==} engines: {node: '>=10'} + dev: true /ms@2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} @@ -10221,6 +10245,7 @@ packages: engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dependencies: yocto-queue: 1.0.0 + dev: true /p-locate@2.0.0: resolution: {integrity: sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==} @@ -10473,9 +10498,11 @@ packages: /pathe@1.1.0: resolution: {integrity: sha512-ODbEPR0KKHqECXW1GoxdDb+AZvULmXjVPy4rt+pGo2+TnjJTIPJQSVS6N63n8T2Ip+syHhbn52OewKicV0373w==} + dev: true /pathval@1.1.1: resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} + dev: true /peggy@3.0.2: resolution: {integrity: sha512-n7chtCbEoGYRwZZ0i/O3t1cPr6o+d9Xx4Zwy2LYfzv0vjchMBU0tO+qYYyvZloBPcgRgzYvALzGWHe609JjEpg==} @@ -10552,6 +10579,7 @@ packages: jsonc-parser: 3.2.0 mlly: 1.2.0 pathe: 1.1.0 + dev: true /pkg@5.8.1: resolution: {integrity: sha512-CjBWtFStCfIiT4Bde9QpJy0KeH19jCfwZRJqHFDFXfhUklCx8JoFmMj3wgnEYIwGmZVNkhsStPHEOnrtrQhEXA==} @@ -10753,6 +10781,7 @@ packages: ansi-regex: 5.0.1 ansi-styles: 5.2.0 react-is: 17.0.2 + dev: true /pretty-format@29.4.3: resolution: {integrity: sha512-cvpcHTc42lcsvOOAzd3XuNWTcvk1Jmnzqeu+WsOuiPmxUJTnkbAcFNsRKvEpBEUFVUgy/GTZLulZDcDEi+CIlA==} @@ -10912,6 +10941,7 @@ packages: /react-is@17.0.2: resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + dev: true /react-is@18.2.0: resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} @@ -11665,6 +11695,7 @@ packages: /siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + dev: true /signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} @@ -11712,6 +11743,7 @@ packages: '@polka/url': 1.0.0-next.21 mrmime: 1.0.1 totalist: 3.0.0 + dev: true /sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} @@ -11730,6 +11762,7 @@ packages: dependencies: ansi-styles: 6.2.1 is-fullwidth-code-point: 4.0.0 + dev: true /smart-buffer@4.2.0: resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} @@ -11788,6 +11821,7 @@ packages: dependencies: buffer-from: 1.1.2 source-map: 0.6.1 + dev: true /source-map@0.5.7: resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} @@ -11870,6 +11904,7 @@ packages: /stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + dev: true /standardized-audio-context@25.3.37: resolution: {integrity: sha512-lr0+RH/IJXYMts95oYKIJ+orTmstOZN3GXWVGmlkbMj8OLahREkRh7DhNGLYgBGDkBkhhc4ev5pYGSFN3gltHw==} @@ -11881,6 +11916,7 @@ packages: /std-env@3.3.2: resolution: {integrity: sha512-uUZI65yrV2Qva5gqE0+A7uVAvO40iPo6jGhs7s8keRfHCmtg+uB2X6EiLGCI9IgL1J17xGhvoOqSz79lzICPTA==} + dev: true /stdopt@2.2.0: resolution: {integrity: sha512-D/p41NgXOkcj1SeGhfXOwv9z1K6EV3sjAUY5aeepVbgEHv7DpKWLTjhjScyzMWAQCAgUQys1mjH0eArm4cjRGw==} @@ -12051,6 +12087,7 @@ packages: resolution: {integrity: sha512-QZTsipNpa2Ppr6v1AmJHESqJ3Uz247MUS0OjrnnZjFAvEoWqxuyFuXn2xLgMtRnijJShAa1HL0gtJyUs7u7n3Q==} dependencies: acorn: 8.8.2 + dev: true /strong-log-transformer@2.1.0: resolution: {integrity: sha512-B3Hgul+z0L9a236FAUC9iZsL+nVHgoCJnqCbN588DjYxvGXaXaaFbfmQ/JhvKjZwsOukuR72XbHv71Qkug0HxA==} @@ -12337,14 +12374,17 @@ packages: /tinybench@2.5.0: resolution: {integrity: sha512-kRwSG8Zx4tjF9ZiyH4bhaebu+EDz1BOx9hOigYHlUW4xxI/wKIUQUqo018UlU4ar6ATPBsaMrdbKZ+tmPdohFA==} + dev: true /tinypool@0.3.1: resolution: {integrity: sha512-zLA1ZXlstbU2rlpA4CIeVaqvWq41MTWqLY3FfsAXgC8+f7Pk7zroaJQxDgxn1xNudKW6Kmj4808rPFShUlIRmQ==} engines: {node: '>=14.0.0'} + dev: true /tinyspy@1.0.2: resolution: {integrity: sha512-bSGlgwLBYf7PnUsQ6WOc6SJ3pGOcd+d8AA6EUnLDDM0kWEstC1JIlSZA3UNliDXhd9ABoS7hiRBDCu+XP/sf1Q==} engines: {node: '>=14.0.0'} + dev: true /tmp@0.0.33: resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} @@ -12380,6 +12420,7 @@ packages: /totalist@3.0.0: resolution: {integrity: sha512-eM+pCBxXO/njtF7vdFsHuqb+ElbxqtI4r5EAvk6grfAFyJ6IvWlSkfZ5T9ozC6xWw3Fj1fGoSmrl0gUs46JVIw==} engines: {node: '>=6'} + dev: true /tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} @@ -12486,6 +12527,7 @@ packages: /type-detect@4.0.8: resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} engines: {node: '>=4'} + dev: true /type-fest@0.13.1: resolution: {integrity: sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==} @@ -12586,6 +12628,7 @@ packages: /ufo@1.1.1: resolution: {integrity: sha512-MvlCc4GHrmZdAllBc0iUDowff36Q9Ndw/UzqmEKyrfSzokTd9ZCy1i+IIk5hrYKkjoYVQyNbrw7/F8XJ2rEwTg==} + dev: true /uglify-js@3.17.4: resolution: {integrity: sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==} @@ -12961,6 +13004,7 @@ packages: - sugarss - supports-color - terser + dev: true /vite-plugin-pwa@0.14.7(vite@4.3.3)(workbox-build@6.5.4)(workbox-window@6.5.4): resolution: {integrity: sha512-dNJaf0fYOWncmjxv9HiSa2xrSjipjff7IkYE5oIUJ2x5HKu3cXgA8LRgzOwTc5MhwyFYRSU0xyN0Phbx3NsQYw==} @@ -13012,6 +13056,7 @@ packages: rollup: 3.21.0 optionalDependencies: fsevents: 2.3.2 + dev: true /vite@4.3.3(@types/node@18.16.3): resolution: {integrity: sha512-MwFlLBO4udZXd+VBcezo3u8mC77YQk+ik+fbc0GZWGgzfbPP+8Kf0fldhARqvSYmtIWoAJ5BXPClUbMTlqFxrA==} @@ -13109,6 +13154,7 @@ packages: - sugarss - supports-color - terser + dev: true /vscode-css-languageservice@6.2.3: resolution: {integrity: sha512-EAyhyIVHpEaf+GjtI+tVe7SekdoANfG0aubnspsQwak3Qkimn/97FpAufNyXk636ngW05pjNKAR9zyTCzo6avQ==} @@ -13296,6 +13342,7 @@ packages: dependencies: siginfo: 2.0.0 stackback: 0.0.2 + dev: true /wide-align@1.1.5: resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} @@ -13629,6 +13676,7 @@ packages: /yocto-queue@1.0.0: resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} engines: {node: '>=12.20'} + dev: true /zod@3.21.4: resolution: {integrity: sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==} From 82225f0b81a9d1f55310908923007e8fb4c43b18 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Fri, 26 May 2023 14:12:53 +0200 Subject: [PATCH 05/63] started workshop pages --- packages/react/src/components/MiniRepl.jsx | 3 +- website/src/components/Box.astro | 10 + website/src/components/QA.tsx | 18 ++ website/src/config.ts | 9 +- website/src/docs/MiniRepl.jsx | 3 +- website/src/pages/workshop/first-effects.mdx | 62 ++++++ website/src/pages/workshop/first-sounds.mdx | 207 +++++++++++++++++++ website/src/pages/workshop/index.astro | 3 + website/src/pages/workshop/intro.mdx | 6 + website/src/pages/workshop/langebank.mdx | 37 ++++ website/src/pages/workshop/mini-notation.mdx | 69 +++++++ website/src/repl/prebake.mjs | 2 +- website/tsconfig.json | 7 +- 13 files changed, 431 insertions(+), 5 deletions(-) create mode 100644 website/src/components/Box.astro create mode 100644 website/src/components/QA.tsx create mode 100644 website/src/pages/workshop/first-effects.mdx create mode 100644 website/src/pages/workshop/first-sounds.mdx create mode 100644 website/src/pages/workshop/index.astro create mode 100644 website/src/pages/workshop/intro.mdx create mode 100644 website/src/pages/workshop/langebank.mdx create mode 100644 website/src/pages/workshop/mini-notation.mdx diff --git a/packages/react/src/components/MiniRepl.jsx b/packages/react/src/components/MiniRepl.jsx index a8de79788..8c45f1ed9 100644 --- a/packages/react/src/components/MiniRepl.jsx +++ b/packages/react/src/components/MiniRepl.jsx @@ -20,10 +20,11 @@ export function MiniRepl({ enableKeyboard, drawTime, punchcard, + span, canvasHeight = 200, theme, }) { - drawTime = drawTime || (punchcard ? [0, 4] : undefined); + drawTime = drawTime || (punchcard ? span || [0, 4] : undefined); const evalOnMount = !!drawTime; const drawContext = useCallback( !!drawTime ? (canvasId) => document.querySelector('#' + canvasId)?.getContext('2d') : null, diff --git a/website/src/components/Box.astro b/website/src/components/Box.astro new file mode 100644 index 000000000..d27671eaf --- /dev/null +++ b/website/src/components/Box.astro @@ -0,0 +1,10 @@ +--- +import LightBulbIcon from '@heroicons/react/20/solid/LightBulbIcon'; +//import MusicalNoteIcon from '@heroicons/react/20/solid/MusicalNoteIcon'; +--- + +
+
+ + +
diff --git a/website/src/components/QA.tsx b/website/src/components/QA.tsx new file mode 100644 index 000000000..ef743bede --- /dev/null +++ b/website/src/components/QA.tsx @@ -0,0 +1,18 @@ +import ChevronDownIcon from '@heroicons/react/20/solid/ChevronDownIcon'; +import ChevronUpIcon from '@heroicons/react/20/solid/ChevronUpIcon'; +import { useState } from 'react'; + +export default function QA({ children, q }) { + const [visible, setVisible] = useState(false); + return ( +
+
setVisible((v) => !v)}> +
{q}
+ + {visible ? : } + +
+ {visible &&
{children}
} +
+ ); +} diff --git a/website/src/config.ts b/website/src/config.ts index bf26fff15..541703786 100644 --- a/website/src/config.ts +++ b/website/src/config.ts @@ -38,9 +38,16 @@ export const ALGOLIA = { apiKey: 'd5044f9d21b80e7721e5b0067a8730b1', }; -export type Sidebar = Record<(typeof KNOWN_LANGUAGE_CODES)[number], Record>; +export type SidebarLang = Record; +export type Sidebar = Record<(typeof KNOWN_LANGUAGE_CODES)[number], SidebarLang>; export const SIDEBAR: Sidebar = { en: { + Workshop: [ + { text: 'Intro', link: 'workshop/intro' }, + { text: 'First Sounds', link: 'workshop/first-sounds' }, + { text: 'First Effects', link: 'workshop/first-effects' }, + { text: 'Mini Notation', link: 'workshop/mini-notation' }, + ], Tutorial: [ { text: 'Getting Started', link: 'learn/getting-started' }, { text: 'Notes', link: 'learn/notes' }, diff --git a/website/src/docs/MiniRepl.jsx b/website/src/docs/MiniRepl.jsx index cce24623f..8b93a4461 100644 --- a/website/src/docs/MiniRepl.jsx +++ b/website/src/docs/MiniRepl.jsx @@ -27,7 +27,7 @@ if (typeof window !== 'undefined') { prebake(); } -export function MiniRepl({ tune, drawTime, punchcard, canvasHeight = 100 }) { +export function MiniRepl({ tune, drawTime, punchcard, span = [0, 4], canvasHeight = 100 }) { const [Repl, setRepl] = useState(); const { theme } = useSettings(); useEffect(() => { @@ -44,6 +44,7 @@ export function MiniRepl({ tune, drawTime, punchcard, canvasHeight = 100 }) { hideOutsideView={true} drawTime={drawTime} punchcard={punchcard} + span={span} canvasHeight={canvasHeight} theme={themes[theme]} /> diff --git a/website/src/pages/workshop/first-effects.mdx b/website/src/pages/workshop/first-effects.mdx new file mode 100644 index 000000000..56914dbc5 --- /dev/null +++ b/website/src/pages/workshop/first-effects.mdx @@ -0,0 +1,62 @@ +--- +title: First Effects +layout: ../../layouts/MainLayout.astro +--- + +import { MiniRepl } from '../../docs/MiniRepl'; + +# First Effects + +**vowel** + + + +You can probably think of more vowels :) + +**gain** + + + +**control the gain with a sine wave** + + + +Try also `saw`, `square`, `tri` + +**The 'structure' comes from the left - try swapping:** + + + +**speed of playback, e.g. 2 = double speed (up 1 octave)** + + + + + +**set note** + + + +**pan** + + + + + +**delay** + + + +**room** + + diff --git a/website/src/pages/workshop/first-sounds.mdx b/website/src/pages/workshop/first-sounds.mdx new file mode 100644 index 000000000..1a9df5f63 --- /dev/null +++ b/website/src/pages/workshop/first-sounds.mdx @@ -0,0 +1,207 @@ +--- +title: First Sounds +layout: ../../layouts/MainLayout.astro +--- + +import { MiniRepl } from '@src/docs/MiniRepl'; +import Box from '@components/Box.astro'; +import QA from '@components/QA'; + +# First Sounds + +## Make a Sound + +Let's start by making some noise: + + + + + +1. press play button to start +2. change `house` to `casio` +3. press refresh button to update +4. press stop button to stop + + + +Congratulations, you've played your first pattern! + +Instead of clicking update all the time, you can use keyboard shortcuts: + + + +1. click into the text field +2. press `ctrl`+`enter` to play +3. change `casio` to `crow` +4. press `ctrl`+`enter` to update +5. press `ctrl`+`.` to stop + + + +To play code like an instrument, these shortcuts should become second nature to you. + +**Try more Sounds** + +You can pick a different sample from the same set, with ':' + + + +Try changing `east:1` to `east:2` + +Here are some more sound sets to try + +``` +casio control crow techno house jazz +metal east jvbass juno insect space wind +bd sd rim hh oh +``` + + + +- `bd` = **b**ass **d**rum +- `sd` = **s**nare **d**rum +- `sn` = **sn**are +- `rim` = **rim**shot +- `hh` = **h**i**h**at +- `oh` = **o**pen **h**ihat + + + +## Sequences + +**Make a Sequence** + + + +Notice how the currently playing sound is highlighted in the code and also visualized below. + + + +Try adding more sounds to the sequence! + + + +**The longer the sequence, the faster it runs** + + + +The content of the sequence will be squished into one second, called a cycle. + +**One way to change the tempo is using `cpm`** + + + + + +cpm = cycles per minute + +By default, the tempo is 60 cycles per minute = 1 cycle per second. + + + +We will look at other ways to change the tempo later! + +**Add a rests in a sequence with '~'** + + + +**Sub-Sequences with [brackets]** + + + + + +Try adding more sounds inside a bracket! + + + +Similar to the whole sequence, the content of a sub-sequence will be squished to the its own length. + +**Multiplication: Speed things up** + + + +**Multiplication: Speeeeeeeeed things up** + + + + + +Pitch = Really fast Rhythm + + + +**Sub-Sub-Sequences with [[brackets]]** + + + +**Play Sounds in parallel with comma** + + + + + +**Multiple Lines with backticks** + + + +## Recap + +Now we've learned the basics of the so called Mini-Notation, the rhythm language of Tidal. +This is what we've leared so far: + +| Concept | Syntax | Example | +| ----------------- | ---------- | --------------------------------------------------------------------- | +| Sequence | space | | +| Sample Number | :x | | +| Rests | ~ | | +| Sub-Sequences | \[ \] | | +| Sub-Sub-Sequences | \[ \[ \]\] | | +| Speed up | \* | | +| Parallel | , | | + +## Examples + +Imitation of a step sequencer: + + + +Shorter variant: + + + +Another beat: + + diff --git a/website/src/pages/workshop/index.astro b/website/src/pages/workshop/index.astro new file mode 100644 index 000000000..9f79e4c22 --- /dev/null +++ b/website/src/pages/workshop/index.astro @@ -0,0 +1,3 @@ + diff --git a/website/src/pages/workshop/intro.mdx b/website/src/pages/workshop/intro.mdx new file mode 100644 index 000000000..e7b075b4c --- /dev/null +++ b/website/src/pages/workshop/intro.mdx @@ -0,0 +1,6 @@ +--- +title: Introduction +layout: ../../layouts/MainLayout.astro +--- + +# Introduction diff --git a/website/src/pages/workshop/langebank.mdx b/website/src/pages/workshop/langebank.mdx new file mode 100644 index 000000000..55fbdf4b7 --- /dev/null +++ b/website/src/pages/workshop/langebank.mdx @@ -0,0 +1,37 @@ +Everythings repeats once per second => 1 **c**ycle **p**er **s**econd (cps) + +**Change tempo** + + + +adding your own samples + + + +").slow(3)`} + punchcard +/> + + +n(run(8)).sound("east") \ No newline at end of file diff --git a/website/src/pages/workshop/mini-notation.mdx b/website/src/pages/workshop/mini-notation.mdx new file mode 100644 index 000000000..df1c8cbee --- /dev/null +++ b/website/src/pages/workshop/mini-notation.mdx @@ -0,0 +1,69 @@ +--- +title: First Sounds +layout: ../../layouts/MainLayout.astro +--- + +import { MiniRepl } from '../../docs/MiniRepl'; + +# Mini Notation + +Mini Notation is everything between the quotes. It the short rhythm language of Tidal. + +## Cycles + +**The longer the sequence, the faster it runs** + + + +**Play less sounds per cycle with \{curly braces\}** + + + +**Use \`backticks\` for multiple lines** + + + +**Play one sounds per cycle with \** + +")`} punchcard /> + +This is the same as `{...}%1` + +## Operators + +**Multiplication: Speed things up** + + + +**Division: Slow things down** + + + +`bd` will play only every second time + +## Combining it all + +**Speed up Sub-Sequences** + + + +**Slow down Sequences** + + + +**Parallel Sub-Sequences** + + + +**Sample Numbers on groups** + + diff --git a/website/src/repl/prebake.mjs b/website/src/repl/prebake.mjs index edf63f546..f8a4e4770 100644 --- a/website/src/repl/prebake.mjs +++ b/website/src/repl/prebake.mjs @@ -22,8 +22,8 @@ export async function prebake() { tag: 'drum-machines', }), samples(`./EmuSP12.json`, `./EmuSP12/`, { prebake: true, tag: 'drum-machines' }), - // samples('github:tidalcycles/Dirt-Samples/master'), ]); + await samples('github:tidalcycles/Dirt-Samples/master'); } const maxPan = noteToMidi('C8'); diff --git a/website/tsconfig.json b/website/tsconfig.json index 78017eafe..90aa524fd 100644 --- a/website/tsconfig.json +++ b/website/tsconfig.json @@ -7,6 +7,11 @@ "noImplicitAny": false, "types": [ "vite-plugin-pwa/client" - ] + ], + "baseUrl": ".", + "paths": { + "@components/*": ["src/components/*"], + "@src/*": ["src/*"], + } } } \ No newline at end of file From 0d6fcf78d83691ca324fb69acd63e14322924688 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Fri, 26 May 2023 16:05:53 +0200 Subject: [PATCH 06/63] hide mini repl headers + improve workshop --- packages/react/src/components/MiniRepl.jsx | 62 ++++--- website/src/docs/MiniRepl.jsx | 3 +- website/src/pages/workshop/first-sounds.mdx | 182 +++++++++++++------- website/src/pages/workshop/langebank.mdx | 23 ++- 4 files changed, 179 insertions(+), 91 deletions(-) diff --git a/packages/react/src/components/MiniRepl.jsx b/packages/react/src/components/MiniRepl.jsx index 8c45f1ed9..9c30dc29d 100644 --- a/packages/react/src/components/MiniRepl.jsx +++ b/packages/react/src/components/MiniRepl.jsx @@ -20,11 +20,12 @@ export function MiniRepl({ enableKeyboard, drawTime, punchcard, - span, canvasHeight = 200, + fontSize = 18, + hideHeader = false, theme, }) { - drawTime = drawTime || (punchcard ? span || [0, 4] : undefined); + drawTime = drawTime || (punchcard ? [0, 4] : undefined); const evalOnMount = !!drawTime; const drawContext = useCallback( !!drawTime ? (canvasId) => document.querySelector('#' + canvasId)?.getContext('2d') : null, @@ -48,7 +49,10 @@ export function MiniRepl({ } = useStrudel({ initialCode: tune, defaultOutput: webaudioOutput, - editPattern: (pat) => (punchcard ? pat.punchcard() : pat), + editPattern: (pat, id) => { + //pat = pat.withContext((ctx) => ({ ...ctx, id })); + return punchcard ? pat.punchcard() : pat; + }, getTime, evalOnMount, drawContext, @@ -102,7 +106,7 @@ export function MiniRepl({ // const logId = data?.pattern?.meta?.id; if (logId === replId) { setLog((l) => { - return l.concat([e.detail]).slice(-10); + return l.concat([e.detail]).slice(-8); }); } }, []), @@ -110,31 +114,35 @@ export function MiniRepl({ return (
-
-
- - + {!hideHeader && ( +
+
+ + +
- {error &&
{error.message}
} -
+ )}
- {show && } + {show && ( + + )} + {error &&
{error.message}
}
{drawTime && ( { @@ -47,6 +47,7 @@ export function MiniRepl({ tune, drawTime, punchcard, span = [0, 4], canvasHeigh span={span} canvasHeight={canvasHeight} theme={themes[theme]} + hideHeader={hideHeader} />
) : ( diff --git a/website/src/pages/workshop/first-sounds.mdx b/website/src/pages/workshop/first-sounds.mdx index 1a9df5f63..65a79093b 100644 --- a/website/src/pages/workshop/first-sounds.mdx +++ b/website/src/pages/workshop/first-sounds.mdx @@ -13,50 +13,51 @@ import QA from '@components/QA'; Let's start by making some noise: - + -1. press play button to start -2. change `house` to `casio` -3. press refresh button to update -4. press stop button to stop - - - -Congratulations, you've played your first pattern! - -Instead of clicking update all the time, you can use keyboard shortcuts: - - - -1. click into the text field +1. ⬆️ click into the text field above ⬆️ 2. press `ctrl`+`enter` to play -3. change `casio` to `crow` +3. change `house` to `casio` 4. press `ctrl`+`enter` to update 5. press `ctrl`+`.` to stop -To play code like an instrument, these shortcuts should become second nature to you. +Congratulations, you are now live coding! **Try more Sounds** You can pick a different sample from the same set, with ':' - + -Try changing `east:1` to `east:2` + -Here are some more sound sets to try +Try changing `east:1` to `east:2` to hear a different sound in the `east` set. + +You can try other numbers too! You might hear a little pause while the sound is loading + + + +Here are some more sound sets to try: ``` casio control crow techno house jazz metal east jvbass juno insect space wind -bd sd rim hh oh ``` - +Now you know how to use different sounds. +For now we'll stick to this little selection of sounds, but we'll find out how to load your own sounds later. + +## Drum Sounds + +By default, Strudel comes with a wide selection of drum sounds: + + + +These letter combinations stand for different parts of a drum set: - `bd` = **b**ass **d**rum - `sd` = **s**nare **d**rum @@ -65,13 +66,30 @@ bd sd rim hh oh - `hh` = **h**i**h**at - `oh` = **o**pen **h**ihat - +To change the sound character of our drums, we can use `bank` to change the drum machine: + + + +In this example `RolandTR909` is the name of the drum machine that we're using. +It is a famous drum machine for house and techno beats. + + + +Try changing `RolandTR909` to one of + +- `AkaiLinn` +- `RhythmAce` +- `RolandTR808` +- `RolandTR707` +- `ViscoSpaceDrum` + + ## Sequences -**Make a Sequence** +In the last example, we already saw that you can play multiple sounds in a sequence by separating them with a space: - + Notice how the currently playing sound is highlighted in the code and also visualized below. @@ -83,13 +101,13 @@ Try adding more sounds to the sequence! **The longer the sequence, the faster it runs** - + -The content of the sequence will be squished into one second, called a cycle. +The content of a sequence will be squished into what's called a cycle. **One way to change the tempo is using `cpm`** - + @@ -103,11 +121,11 @@ We will look at other ways to change the tempo later! **Add a rests in a sequence with '~'** - + **Sub-Sequences with [brackets]** - + @@ -119,31 +137,44 @@ Similar to the whole sequence, the content of a sub-sequence will be squished to **Multiplication: Speed things up** - + + +**Multiplication: Speed up sequences** + + **Multiplication: Speeeeeeeeed things up** - + -Pitch = Really fast Rhythm +Pitch = really fast rhythm **Sub-Sub-Sequences with [[brackets]]** - + -**Play Sounds in parallel with comma** + - +You can go as deep as you want! - + + +**Play sequences in parallel with comma** + + + +You can use as many commas as you want: + + **Multiple Lines with backticks** | -| Sample Number | :x | | -| Rests | ~ | | -| Sub-Sequences | \[ \] | | -| Sub-Sub-Sequences | \[ \[ \]\] | | -| Speed up | \* | | -| Parallel | , | | +| Concept | Syntax | Example | +| ----------------- | ---------- | -------------------------------------------------------------------------------- | +| Sequence | space | | +| Sample Number | :x | | +| Rests | ~ | | +| Sub-Sequences | \[ \] | | +| Sub-Sub-Sequences | \[ \[ \]\] | | +| Speed up | \* | | +| Parallel | , | | ## Examples -Imitation of a step sequencer: +**Basic rock beat** + + + +**Classic house** + + + +Notice that the house and rock beats are extremely similar. Besides their different tempos and minor differences in the hihat and kick drum lines, these patterns are the same. You'll find certain drum patterns reused in many styles. + +We Will Rock you + + + +**Yellow Magic Orchestra - Firecracker** + +**Imitation of a 16 step sequencer** + + -Shorter variant: - - - -Another beat: +**Another one** + +**Not your average drums** + + + +This was just the tip of the iceberg! diff --git a/website/src/pages/workshop/langebank.mdx b/website/src/pages/workshop/langebank.mdx index 55fbdf4b7..b41d96580 100644 --- a/website/src/pages/workshop/langebank.mdx +++ b/website/src/pages/workshop/langebank.mdx @@ -1,4 +1,11 @@ -Everythings repeats once per second => 1 **c**ycle **p**er **s**econd (cps) + + +1. press play button to start +2. change `house` to `casio` +3. press refresh button to update +4. press stop button to stop + + **Change tempo** @@ -33,5 +40,17 @@ adding your own samples punchcard /> +n(run(8)).sound("east") -n(run(8)).sound("east") \ No newline at end of file +Shorter variant: + + From fc0618121701b39c46d6bd4e3ab0c8fbcaff837f Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sat, 27 May 2023 13:30:57 +0200 Subject: [PATCH 07/63] - add claviature flag to minirepl - bring back option+dot on macos - consume more editor settings in minirepl --- packages/core/util.mjs | 3 +- packages/react/src/components/MiniRepl.jsx | 30 ++++++++++--- packages/react/src/hooks/useStrudel.mjs | 3 +- pnpm-lock.yaml | 51 ++++++++++++---------- website/package.json | 3 +- website/src/components/Claviature.jsx | 24 ++++++++++ website/src/docs/MiniRepl.jsx | 41 +++++++++++++++-- website/src/repl/Repl.jsx | 2 +- 8 files changed, 121 insertions(+), 36 deletions(-) create mode 100644 website/src/components/Claviature.jsx diff --git a/packages/core/util.mjs b/packages/core/util.mjs index 2b43cf0b6..37fe6b6c1 100644 --- a/packages/core/util.mjs +++ b/packages/core/util.mjs @@ -67,13 +67,14 @@ export const getFreq = (noteOrMidi) => { return midiToFreq(noteToMidi(noteOrMidi)); }; +const pcs = ['C', 'Db', 'D', 'Eb', 'E', 'F', 'Gb', 'G', 'Ab', 'A', 'Bb', 'B']; /** * @deprecated does not appear to be referenced or invoked anywhere in the codebase * @noAutocomplete */ export const midi2note = (n) => { const oct = Math.floor(n / 12) - 1; - const pc = ['C', 'Db', 'D', 'Eb', 'E', 'F', 'Gb', 'G', 'Ab', 'A', 'Bb', 'B'][n % 12]; + const pc = pcs[n % 12]; return pc + oct; }; diff --git a/packages/react/src/components/MiniRepl.jsx b/packages/react/src/components/MiniRepl.jsx index 9c30dc29d..8ff738e69 100644 --- a/packages/react/src/components/MiniRepl.jsx +++ b/packages/react/src/components/MiniRepl.jsx @@ -18,18 +18,21 @@ export function MiniRepl({ tune, hideOutsideView = false, enableKeyboard, + onTrigger, drawTime, punchcard, + onPaint, canvasHeight = 200, fontSize = 18, hideHeader = false, theme, + keybindings, }) { drawTime = drawTime || (punchcard ? [0, 4] : undefined); const evalOnMount = !!drawTime; const drawContext = useCallback( - !!drawTime ? (canvasId) => document.querySelector('#' + canvasId)?.getContext('2d') : null, - [drawTime], + punchcard ? (canvasId) => document.querySelector('#' + canvasId)?.getContext('2d') : null, + [punchcard], ); const { code, @@ -51,7 +54,15 @@ export function MiniRepl({ defaultOutput: webaudioOutput, editPattern: (pat, id) => { //pat = pat.withContext((ctx) => ({ ...ctx, id })); - return punchcard ? pat.punchcard() : pat; + if (onTrigger) { + pat = pat.onTrigger(onTrigger, false); + } + if (onPaint) { + pat = pat.onPaint(onPaint); + } else if (punchcard) { + pat = pat.punchcard(); + } + return pat; }, getTime, evalOnMount, @@ -87,7 +98,7 @@ export function MiniRepl({ e.preventDefault(); flash(view); await activateCode(); - } else if (e.key === '.') { + } else if (e.key === '.' || e.code === 'Period') { stop(); e.preventDefault(); } @@ -140,11 +151,18 @@ export function MiniRepl({ )}
{show && ( - + )} {error &&
{error.message}
}
- {drawTime && ( + {punchcard && ( !!(pat?.context?.onPaint && drawContext), [drawContext]); + //const shouldPaint = useCallback((pat) => !!(pat?.context?.onPaint && drawContext), [drawContext]); + const shouldPaint = useCallback((pat) => !!pat?.context?.onPaint, []); // TODO: make sure this hook reruns when scheduler.started changes const { scheduler, evaluate, start, stop, pause, setCps } = useMemo( diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3a135d893..6542a7072 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -92,7 +92,7 @@ importers: devDependencies: vite: specifier: ^4.3.3 - version: 4.3.3(@types/node@18.16.3) + version: 4.3.3(@types/node@18.11.18) packages/core: dependencies: @@ -102,7 +102,7 @@ importers: devDependencies: vite: specifier: ^4.3.3 - version: 4.3.3(@types/node@18.16.3) + version: 4.3.3(@types/node@18.11.18) vitest: specifier: ^0.28.0 version: 0.28.0(@vitest/ui@0.28.0) @@ -127,7 +127,7 @@ importers: devDependencies: vite: specifier: ^4.3.3 - version: 4.3.3(@types/node@18.16.3) + version: 4.3.3(@types/node@18.11.18) packages/core/examples/vite-vanilla-repl-cm6: dependencies: @@ -155,7 +155,7 @@ importers: devDependencies: vite: specifier: ^4.3.2 - version: 4.3.3(@types/node@18.16.3) + version: 4.3.3(@types/node@18.11.18) packages/csound: dependencies: @@ -171,7 +171,7 @@ importers: devDependencies: vite: specifier: ^4.3.3 - version: 4.3.3(@types/node@18.16.3) + version: 4.3.3(@types/node@18.11.18) packages/embed: {} @@ -204,7 +204,7 @@ importers: version: link:../mini vite: specifier: ^4.3.3 - version: 4.3.3(@types/node@18.16.3) + version: 4.3.3(@types/node@18.11.18) vitest: specifier: ^0.28.0 version: 0.28.0(@vitest/ui@0.28.0) @@ -223,7 +223,7 @@ importers: devDependencies: vite: specifier: ^4.3.3 - version: 4.3.3(@types/node@18.16.3) + version: 4.3.3(@types/node@18.11.18) packages/mini: dependencies: @@ -236,7 +236,7 @@ importers: version: 3.0.2 vite: specifier: ^4.3.3 - version: 4.3.3(@types/node@18.16.3) + version: 4.3.3(@types/node@18.11.18) vitest: specifier: ^0.28.0 version: 0.28.0(@vitest/ui@0.28.0) @@ -255,7 +255,7 @@ importers: version: 5.8.1 vite: specifier: ^4.3.3 - version: 4.3.3(@types/node@18.16.3) + version: 4.3.3(@types/node@18.11.18) packages/react: dependencies: @@ -325,7 +325,7 @@ importers: version: 3.3.2 vite: specifier: ^4.3.3 - version: 4.3.3(@types/node@18.16.3) + version: 4.3.3(@types/node@18.11.18) packages/react/examples/nano-repl: dependencies: @@ -380,7 +380,7 @@ importers: version: 3.3.2 vite: specifier: ^4.3.3 - version: 4.3.3(@types/node@18.16.3) + version: 4.3.3(@types/node@18.11.18) packages/serial: dependencies: @@ -390,7 +390,7 @@ importers: devDependencies: vite: specifier: ^4.3.3 - version: 4.3.3(@types/node@18.16.3) + version: 4.3.3(@types/node@18.11.18) packages/soundfonts: dependencies: @@ -412,7 +412,7 @@ importers: version: 3.3.1 vite: specifier: ^4.3.3 - version: 4.3.3(@types/node@18.16.3) + version: 4.3.3(@types/node@18.11.18) packages/tonal: dependencies: @@ -431,7 +431,7 @@ importers: devDependencies: vite: specifier: ^4.3.3 - version: 4.3.3(@types/node@18.16.3) + version: 4.3.3(@types/node@18.11.18) vitest: specifier: ^0.28.0 version: 0.28.0(@vitest/ui@0.28.0) @@ -447,7 +447,7 @@ importers: devDependencies: vite: specifier: ^4.3.3 - version: 4.3.3(@types/node@18.16.3) + version: 4.3.3(@types/node@18.11.18) vitest: specifier: ^0.28.0 version: 0.28.0(@vitest/ui@0.28.0) @@ -469,7 +469,7 @@ importers: devDependencies: vite: specifier: ^4.3.3 - version: 4.3.3(@types/node@18.16.3) + version: 4.3.3(@types/node@18.11.18) vitest: specifier: ^0.28.0 version: 0.28.0(@vitest/ui@0.28.0) @@ -494,7 +494,7 @@ importers: devDependencies: vite: specifier: ^4.3.3 - version: 4.3.3(@types/node@18.16.3) + version: 4.3.3(@types/node@18.11.18) packages/web/examples/repl-example: dependencies: @@ -504,7 +504,7 @@ importers: devDependencies: vite: specifier: ^4.3.2 - version: 4.3.3(@types/node@18.16.3) + version: 4.3.3(@types/node@18.11.18) packages/webaudio: dependencies: @@ -517,7 +517,7 @@ importers: devDependencies: vite: specifier: ^4.3.3 - version: 4.3.3(@types/node@18.16.3) + version: 4.3.3(@types/node@18.11.18) packages/webdirt: dependencies: @@ -533,7 +533,7 @@ importers: devDependencies: vite: specifier: ^4.3.3 - version: 4.3.3(@types/node@18.16.3) + version: 4.3.3(@types/node@18.11.18) vitest: specifier: ^0.28.0 version: 0.28.0(@vitest/ui@0.28.0) @@ -546,7 +546,7 @@ importers: devDependencies: vite: specifier: ^4.3.3 - version: 4.3.3(@types/node@18.16.3) + version: 4.3.3(@types/node@18.11.18) vitest: specifier: ^0.28.0 version: 0.28.0(@vitest/ui@0.28.0) @@ -646,6 +646,9 @@ importers: canvas: specifier: ^2.11.2 version: 2.11.2 + claviature: + specifier: ^0.1.0 + version: 0.1.0 fraction.js: specifier: ^4.2.0 version: 4.2.0 @@ -4502,7 +4505,7 @@ packages: '@babel/plugin-transform-react-jsx-self': 7.21.0(@babel/core@7.21.5) '@babel/plugin-transform-react-jsx-source': 7.19.6(@babel/core@7.21.5) react-refresh: 0.14.0 - vite: 4.3.3(@types/node@18.16.3) + vite: 4.3.3(@types/node@18.11.18) transitivePeerDependencies: - supports-color dev: true @@ -5413,6 +5416,10 @@ packages: resolution: {integrity: sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w==} engines: {node: '>=8'} + /claviature@0.1.0: + resolution: {integrity: sha512-Ai12axNwQ7x/F9QAj64RYKsgvi5Y33+X3GUSKAC/9s/adEws8TSSc0efeiqhKNGKBo6rT/c+CSCwSXzXxwxZzQ==} + dev: false + /clean-stack@2.2.0: resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} engines: {node: '>=6'} diff --git a/website/package.json b/website/package.json index 6b8b7a6cf..d6100625d 100644 --- a/website/package.json +++ b/website/package.json @@ -13,7 +13,7 @@ }, "dependencies": { "@algolia/client-search": "^4.17.0", - "@astrojs/mdx": "^0.19.0", + "@astrojs/mdx": "^0.19.0", "@astrojs/react": "^2.1.1", "@astrojs/tailwind": "^3.1.1", "@docsearch/css": "^3.3.4", @@ -43,6 +43,7 @@ "@uiw/codemirror-themes-all": "^4.19.16", "astro": "^2.3.2", "canvas": "^2.11.2", + "claviature": "^0.1.0", "fraction.js": "^4.2.0", "nanoid": "^4.0.2", "nanostores": "^0.8.1", diff --git a/website/src/components/Claviature.jsx b/website/src/components/Claviature.jsx new file mode 100644 index 000000000..e97facbc4 --- /dev/null +++ b/website/src/components/Claviature.jsx @@ -0,0 +1,24 @@ +import { getClaviature } from 'claviature'; +import React from 'react'; + +export default function Claviature({ options, onClick, onMouseDown, onMouseUp, onMouseLeave }) { + const svg = getClaviature({ + options, + onClick, + onMouseDown, + onMouseUp, + onMouseLeave, + }); + return ( + + {svg.children.map((el, i) => { + const TagName = el.name; + return ( + + {el.value} + + ); + })} + + ); +} diff --git a/website/src/docs/MiniRepl.jsx b/website/src/docs/MiniRepl.jsx index 6b434353a..651241a95 100644 --- a/website/src/docs/MiniRepl.jsx +++ b/website/src/docs/MiniRepl.jsx @@ -1,10 +1,11 @@ -import { evalScope, controls } from '@strudel.cycles/core'; +import { evalScope, controls, noteToMidi } from '@strudel.cycles/core'; import { initAudioOnFirstClick } from '@strudel.cycles/webaudio'; import { useEffect, useState } from 'react'; import { prebake } from '../repl/prebake'; import { themes, settings } from '../repl/themes.mjs'; import './MiniRepl.css'; import { useSettings } from '../settings.mjs'; +import Claviature from '@components/Claviature'; let modules; if (typeof window !== 'undefined') { @@ -27,9 +28,19 @@ if (typeof window !== 'undefined') { prebake(); } -export function MiniRepl({ tune, drawTime, punchcard, span = [0, 4], canvasHeight = 100, hideHeader }) { +export function MiniRepl({ + tune, + drawTime, + punchcard, + span = [0, 4], + canvasHeight = 100, + hideHeader, + claviature, + claviatureLabels, +}) { const [Repl, setRepl] = useState(); - const { theme } = useSettings(); + const { theme, keybindings, fontSize, fontFamily } = useSettings(); + const [activeNotes, setActiveNotes] = useState([]); useEffect(() => { // we have to load this package on the client // because codemirror throws an error on the server @@ -42,13 +53,35 @@ export function MiniRepl({ tune, drawTime, punchcard, span = [0, 4], canvasHeigh { + const active = haps + .map((hap) => hap.value.note) + .filter(Boolean) + .map((n) => (typeof n === 'string' ? noteToMidi(n) : n)); + setActiveNotes(active); + } + : undefined + } /> + {claviature && ( + + )}
) : (
{tune}
diff --git a/website/src/repl/Repl.jsx b/website/src/repl/Repl.jsx index 4ad387fee..1db8f8dd1 100644 --- a/website/src/repl/Repl.jsx +++ b/website/src/repl/Repl.jsx @@ -157,7 +157,7 @@ export function Repl({ embedded = false }) { e.preventDefault(); flash(view); await activateCode(); - } else if (e.key === '.') { + } else if (e.key === '.' || e.keyCode === 'Period') { stop(); e.preventDefault(); } From 4e575c44b3f2821d745303f90034a99fec0299c8 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sat, 27 May 2023 13:31:18 +0200 Subject: [PATCH 08/63] begin first notes page --- website/src/components/QA.tsx | 1 + website/src/config.ts | 1 + website/src/pages/workshop/first-notes.mdx | 227 ++++++++++++++++++++ website/src/pages/workshop/first-sounds.mdx | 4 +- website/src/pages/workshop/langebank.mdx | 35 +++ 5 files changed, 267 insertions(+), 1 deletion(-) create mode 100644 website/src/pages/workshop/first-notes.mdx diff --git a/website/src/components/QA.tsx b/website/src/components/QA.tsx index ef743bede..7d2ac53d9 100644 --- a/website/src/components/QA.tsx +++ b/website/src/components/QA.tsx @@ -1,5 +1,6 @@ import ChevronDownIcon from '@heroicons/react/20/solid/ChevronDownIcon'; import ChevronUpIcon from '@heroicons/react/20/solid/ChevronUpIcon'; +import React from 'react'; import { useState } from 'react'; export default function QA({ children, q }) { diff --git a/website/src/config.ts b/website/src/config.ts index 541703786..6fc7bb771 100644 --- a/website/src/config.ts +++ b/website/src/config.ts @@ -45,6 +45,7 @@ export const SIDEBAR: Sidebar = { Workshop: [ { text: 'Intro', link: 'workshop/intro' }, { text: 'First Sounds', link: 'workshop/first-sounds' }, + { text: 'First Notes', link: 'workshop/first-notes' }, { text: 'First Effects', link: 'workshop/first-effects' }, { text: 'Mini Notation', link: 'workshop/mini-notation' }, ], diff --git a/website/src/pages/workshop/first-notes.mdx b/website/src/pages/workshop/first-notes.mdx new file mode 100644 index 000000000..6e4f91102 --- /dev/null +++ b/website/src/pages/workshop/first-notes.mdx @@ -0,0 +1,227 @@ +--- +title: First Notes +layout: ../../layouts/MainLayout.astro +--- + +import { MiniRepl } from '@src/docs/MiniRepl'; +import { midi2note } from '@strudel.cycles/core/'; +import Box from '@components/Box.astro'; +import QA from '@components/QA'; + +# First Notes + +Let's look at how we can play notes + +## numbers and notes + +**play notes with numbers** + + [midi2note(i + 36), i + 36]), + )} +/> + + + +Try out different numbers! + +Try decimal numbers, like 55.5 + + + +**play notes with letters** + + [n, n.split('')[0]]))} +/> + + + +Try out different letters (a - g). + +Can you find melodies that are actual words? Hint: ☕ 😉 ⚪ + + + +**add flats or sharps to play the black keys** + + [n, n.split('').slice(0, 2).join('')]), + )} +/> + + [n, n.split('').slice(0, 2).join('')]), + )} +/> + +**play notes with letters in different octaves** + + [n, n]))} + claviatureLabels={Object.fromEntries( + Array(49) + .fill() + .map((_, i) => [midi2note(i + 36), midi2note(i + 36)]), + )} +/> + + + +Try out different octaves (1-8) + + + +## changing the sound + +Just like with unpitched sounds, we can change the sound of our notes with `sound`: + + + + + +Try out different sounds: + +- gm_electric_guitar_muted +- gm_acoustic_bass +- gm_voice_oohs +- gm_blown_bottle +- sawtooth +- square +- triangle +- how about bd, sd or hh? +- remove `.sound('...')` completely + + + +**switch between sounds** + + + +**stack multiple sounds** + + + + + +The `note` and `sound` patterns are combined! + +We will see more ways to combine patterns later.. + + + +## Longer Sequences + +**Divide sequences with `/` to slow them down** + +{/* [c2 bb1 f2 eb2] */} + + + + + +The `/4` plays the sequence in brackets over 4 cycles (=4s). + +Try adding more notes inside the brackets and notice how it gets faster. + + + +Because it is so common to just play one thing per cycle, you can.. + +**Play one per cycle with \< \>** + +").sound("gm_acoustic_bass")`} punchcard /> + + + +Try adding more notes inside the brackets and notice how it does **not** get faster. + + + +**Play one sequence per cycle** + +{/* <[c2 c3]*4 [bb1 bb2]*4 [f2 f3]*4 [eb2 eb3]*4>/2 */} + +/2") + .sound("gm_acoustic_bass")`} +/> + +**Play X per cycle with \{ \}** + + + + + +Try different numbers after `%` + +`{ ... }%1` is the same as `< ... >` + + + +## Examples + +Small Town Boy + +/2") +.sound("gm_synth_bass_1").lpf(1000)`} +/> + +/2" +.add.squeeze("[0 12]\*4") +.note() +.sound("gm_synth_bass_1")`} +/> diff --git a/website/src/pages/workshop/first-sounds.mdx b/website/src/pages/workshop/first-sounds.mdx index 65a79093b..b0aeb1fbb 100644 --- a/website/src/pages/workshop/first-sounds.mdx +++ b/website/src/pages/workshop/first-sounds.mdx @@ -83,6 +83,8 @@ Try changing `RolandTR909` to one of - `RolandTR707` - `ViscoSpaceDrum` +There are a lot more, but let's keep it simple for now + ## Sequences @@ -264,4 +266,4 @@ insect [crow metal] ~ ~, punchcard /> -This was just the tip of the iceberg! +Now that we know the basics of how to make beats, let's look at how we can play [notes](/workshop/first-notes) diff --git a/website/src/pages/workshop/langebank.mdx b/website/src/pages/workshop/langebank.mdx index b41d96580..44e5d6009 100644 --- a/website/src/pages/workshop/langebank.mdx +++ b/website/src/pages/workshop/langebank.mdx @@ -54,3 +54,38 @@ Shorter variant: bd [~ ~ ~ bd] [~ bd] [~ ~ ~ bd] \`).cpm(90/4)`} /> + +polyrythms & polymeters + +-- This can make for flexible time signatures: + +d1 $ sound "[bd bd sn:5] [bd sn:3]" + +-- You can put subsequences inside subsequences: +d1 $ sound "[[bd bd] bd sn:5] [bd sn:3]" + +-- Keep going.. +d1 $ sound "[[bd [bd bd bd bd]] bd sn:5] [bd sn:3]" + +-- * Polymetric / polyrhythmic sequences + +-- Play two subsequences at once by separating with a comma: + +d1 $ sound "[voodoo voodoo:3, arpy arpy:4 arpy:2]" + +-- compare how [,] and {,} work: + +d1 $ sound "[voodoo voodoo:3, arpy arpy:4 arpy:2]" + +d1 $ sound "{voodoo voodoo:3, arpy arpy:4 arpy:2}" + +d1 $ sound "[drum bd hh bd, can can:2 can:3 can:4 can:2]" + +d1 $ sound "{drum bd hh bd, can can:2 can:3 can:4 can:2}" + +d1 $ sound "[bd sn, can:2 can:3 can:1, arpy arpy:1 arpy:2 arpy:3 arpy:5]" + +d1 $ sound "{bd sn, can:2 can:3 can:1, arpy arpy:1 arpy:2 arpy:3 arpy:5}" + + + From ed792fc0d46a75b6836a5d74ca79079f004b2111 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sat, 27 May 2023 16:29:05 +0200 Subject: [PATCH 09/63] continue notes chapter --- website/src/pages/workshop/first-notes.mdx | 74 ++++++++++++++++------ website/src/pages/workshop/langebank.mdx | 30 +++++++++ 2 files changed, 86 insertions(+), 18 deletions(-) diff --git a/website/src/pages/workshop/first-notes.mdx b/website/src/pages/workshop/first-notes.mdx index 6e4f91102..cb40713fa 100644 --- a/website/src/pages/workshop/first-notes.mdx +++ b/website/src/pages/workshop/first-notes.mdx @@ -97,11 +97,17 @@ Try out different octaves (1-8) +If you are not comfortable with the note letter system, it should be easier to use numbers instead. +Most of the examples below will use numbers for that reason. +We will also look at ways to make it easier to play the right notes later. + ## changing the sound Just like with unpitched sounds, we can change the sound of our notes with `sound`: - + + +{/* c2 g2, e3 b3 d4 e4 */} @@ -157,6 +163,8 @@ We will see more ways to combine patterns later.. The `/4` plays the sequence in brackets over 4 cycles (=4s). +So each of the 4 notes is 1s long. + Try adding more notes inside the brackets and notice how it gets faster. @@ -181,47 +189,77 @@ Try adding more notes inside the brackets and notice how it does **not** get fas hideHeader client:visible tune={`note("<[36 48]*4 [34 46]*4 [41 53]*4 [39 51]*4>/2") - .sound("gm_acoustic_bass")`} +.sound("gm_acoustic_bass")`} /> -**Play X per cycle with \{ \}** +**Alternate between multiple things** ") +.sound("gm_xylophone")`} +/> + +This is also useful for unpitched sounds: + +, [~ hh]*2") +.bank("RolandTR909")`} +/> + +## Scales + +Finding the right notes can be difficult.. Scales are here to help: + +") +.scale("C:minor").sound("piano")`} /> -Try different numbers after `%` +Try out different numbers. Any number should sound good! -`{ ... }%1` is the same as `< ... >` +Try out different scales: + +- C:major +- A2:minor +- D:dorian +- G:mixolydian +- A2:minor:pentatonic +- F:major:pentatonic -## Examples +**automate scales** -Small Town Boy +Just like anything, we can automate the scale with a pattern: + +") +.scale("/2") +.sound("piano")`} +/> + +## Examples /2") -.sound("gm_synth_bass_1").lpf(1000)`} +.sound("gm_synth_bass_1")`} /> /2" -.add.squeeze("[0 12]\*4") -.note() + tune={`note("[0 12]*2".add("<36 34 41 39>/2")) .sound("gm_synth_bass_1")`} /> diff --git a/website/src/pages/workshop/langebank.mdx b/website/src/pages/workshop/langebank.mdx index 44e5d6009..9190afa66 100644 --- a/website/src/pages/workshop/langebank.mdx +++ b/website/src/pages/workshop/langebank.mdx @@ -89,3 +89,33 @@ d1 $ sound "{bd sn, can:2 can:3 can:1, arpy arpy:1 arpy:2 arpy:3 arpy:5}" + + + +**Play X per cycle with \{ \}** + + + + + +Try different numbers after `%` + +`{ ... }%1` is the same as `< ... >` + + + +## Bracket Recap + +- `[]` squeezes contents to 1 cycle +- `<>` plays one item per cycle +- `{}%x` plays x items per cycle From 1ba5d2e1ca09c5aa086130c57c87746e021e3081 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sat, 27 May 2023 21:52:44 +0200 Subject: [PATCH 10/63] finish notes chapter --- website/src/pages/workshop/first-effects.mdx | 22 ++-- website/src/pages/workshop/first-notes.mdx | 127 ++++++++++++++++++- website/src/pages/workshop/first-sounds.mdx | 23 +++- website/src/pages/workshop/langebank.mdx | 45 ++++++- 4 files changed, 191 insertions(+), 26 deletions(-) diff --git a/website/src/pages/workshop/first-effects.mdx b/website/src/pages/workshop/first-effects.mdx index 56914dbc5..1b718b166 100644 --- a/website/src/pages/workshop/first-effects.mdx +++ b/website/src/pages/workshop/first-effects.mdx @@ -9,54 +9,52 @@ import { MiniRepl } from '../../docs/MiniRepl'; **vowel** - + You can probably think of more vowels :) **gain** - + **control the gain with a sine wave** - + Try also `saw`, `square`, `tri` **The 'structure' comes from the left - try swapping:** - + **speed of playback, e.g. 2 = double speed (up 1 octave)** - + -**set note** - - - **pan** - + **delay** - + **room** - + diff --git a/website/src/pages/workshop/first-notes.mdx b/website/src/pages/workshop/first-notes.mdx index cb40713fa..b986ba791 100644 --- a/website/src/pages/workshop/first-notes.mdx +++ b/website/src/pages/workshop/first-notes.mdx @@ -190,6 +190,7 @@ Try adding more notes inside the brackets and notice how it does **not** get fas client:visible tune={`note("<[36 48]*4 [34 46]*4 [41 53]*4 [39 51]*4>/2") .sound("gm_acoustic_bass")`} + punchcard /> **Alternate between multiple things** @@ -199,6 +200,7 @@ Try adding more notes inside the brackets and notice how it does **not** get fas client:visible tune={`note("60 <63 62 65 63>") .sound("gm_xylophone")`} + punchcard /> This is also useful for unpitched sounds: @@ -208,6 +210,7 @@ This is also useful for unpitched sounds: client:visible tune={`sound("bd*2, ~ , [~ hh]*2") .bank("RolandTR909")`} + punchcard /> ## Scales @@ -219,6 +222,7 @@ Finding the right notes can be difficult.. Scales are here to help: client:visible tune={`n("0 2 4 <[6,8] [7,9]>") .scale("C:minor").sound("piano")`} + punchcard /> @@ -243,23 +247,136 @@ Just like anything, we can automate the scale with a pattern: ") -.scale("/2") + tune={`n("<0 -3>, 2 4 <[6,8] [7,9]>") +.scale("/4") .sound("piano")`} + punchcard /> + + +If you have no idea what these scale mean, don't worry. +These are just labels for different sets of notes that go well together. + +Take your time and you'll find scales you like! + + + +## Repeat & Elongate + +**Elongate with @** + + + + + +Not using `@` is like using `@1`. In the above example, c is 3 units long and eb is 1 unit long. + +Try changing that number! + + + +**Elongate within sub-sequences** + +*2") +.scale("/4") +.sound("gm_acoustic_bass")`} + punchcard +/> + + + +This groove is called a `shuffle`. +Each beat has two notes, where the first is twice as long as the second. +This is also sometimes called triplet swing. You'll often find it in blues and jazz. + + + +**Replicate** + +]").sound("piano")`} punchcard /> + + + +Try switching between `!`, `*` and `@` + +What's the difference? + + + +## Recap + +Let's recap what we've learned in this chapter: + +| Concept | Syntax | Example | +| --------- | ------ | ------------------------------------------------------------------- | +| Slow down | \/ | | +| Alternate | \<\> | ")`} /> | +| Elongate | @ | | +| Replicate | ! | | + ## Examples +**Classy Bassline** + /2") -.sound("gm_synth_bass_1")`} +.sound("gm_synth_bass_1") +.lpf(800) // <-- we'll learn about this soon`} /> +**Classy Melody** + /2")) -.sound("gm_synth_bass_1")`} + tune={`n(\`< +[~ 0] 2 [0 2] [~ 2] +[~ 0] 1 [0 1] [~ 1] +[~ 0] 3 [0 3] [~ 3] +[~ 0] 2 [0 2] [~ 2] +>*2\`).scale("C4:minor") +.sound("gm_synth_strings_1")`} /> + +**Classy Drums** + +, [~ hh]*2") +.bank("RolandTR909")`} +/> + +**If there just was a way to play all the above at the same time.......** + + + +It's called `stack` 😙 + + + +/2") + .sound("gm_synth_bass_1").lpf(800), + n(\`< + [~ 0] 2 [0 2] [~ 2] + [~ 0] 1 [0 1] [~ 1] + [~ 0] 3 [0 3] [~ 3] + [~ 0] 2 [0 2] [~ 2] + >*2\`).scale("C4:minor") + .sound("gm_synth_strings_1"), + sound("bd*2, ~ , [~ hh]*2") + .bank("RolandTR909") +)`} +/> + +This is starting to sound like actual music! We have sounds, we have notes, now the last piece of the puzzle is missing: [effects](/workshop/first-effects) diff --git a/website/src/pages/workshop/first-sounds.mdx b/website/src/pages/workshop/first-sounds.mdx index b0aeb1fbb..852977a3c 100644 --- a/website/src/pages/workshop/first-sounds.mdx +++ b/website/src/pages/workshop/first-sounds.mdx @@ -91,7 +91,7 @@ There are a lot more, but let's keep it simple for now In the last example, we already saw that you can play multiple sounds in a sequence by separating them with a space: - + Notice how the currently playing sound is highlighted in the code and also visualized below. @@ -173,6 +173,18 @@ You can use as many commas as you want: +Commas can also be used inside sub-sequences: + + + + + +Notice how the 2 above are the same? + +It is quite common that there are many ways to express the same idea. + + + **Multiple Lines with backticks** + **Classic house** -Notice that the house and rock beats are extremely similar. Besides their different tempos and minor differences in the hihat and kick drum lines, these patterns are the same. You'll find certain drum patterns reused in many styles. + + +Notice that the two patterns are extremely similar. +Certain drum patterns are reused across genres. + + We Will Rock you diff --git a/website/src/pages/workshop/langebank.mdx b/website/src/pages/workshop/langebank.mdx index 9190afa66..7a1ca6977 100644 --- a/website/src/pages/workshop/langebank.mdx +++ b/website/src/pages/workshop/langebank.mdx @@ -67,7 +67,7 @@ d1 $ sound "[[bd bd] bd sn:5] [bd sn:3]" -- Keep going.. d1 $ sound "[[bd [bd bd bd bd]] bd sn:5] [bd sn:3]" --- * Polymetric / polyrhythmic sequences +-- \* Polymetric / polyrhythmic sequences -- Play two subsequences at once by separating with a comma: @@ -87,11 +87,6 @@ d1 $ sound "[bd sn, can:2 can:3 can:1, arpy arpy:1 arpy:2 arpy:3 arpy:5]" d1 $ sound "{bd sn, can:2 can:3 can:1, arpy arpy:1 arpy:2 arpy:3 arpy:5}" - - - - - **Play X per cycle with \{ \}** ` plays one item per cycle - `{}%x` plays x items per cycle + +/2")) +.sound("gm_synth_bass_1")`} +/> + +vertical + + +< 4 4 4 3> +<[2,7] [2,6] [1,6] [1,6]> +< 4 4 4 3> +>*2\`) +.scale("/4") +.sound("piano")`} +/> + +horizontal + +*2\`) +.scale("/4") +.sound("piano")`} +/> From 8c93e578a062ee24fdc0edc2c356caa118bf5964 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 28 May 2023 12:41:46 +0200 Subject: [PATCH 11/63] clamp delayfeedback --- packages/webaudio/webaudio.mjs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/webaudio/webaudio.mjs b/packages/webaudio/webaudio.mjs index 6b069a859..c59223031 100644 --- a/packages/webaudio/webaudio.mjs +++ b/packages/webaudio/webaudio.mjs @@ -85,7 +85,12 @@ export async function initAudioOnFirstClick() { } let delays = {}; +const maxfeedback = 0.98; function getDelay(orbit, delaytime, delayfeedback, t) { + if (delayfeedback > maxfeedback) { + logger(`delayfeedback was clamped to ${maxfeedback} to save your ears`); + } + delayfeedback = strudel.clamp(delayfeedback, 0, 0.98); if (!delays[orbit]) { const ac = getAudioContext(); const dly = ac.createFeedbackDelay(1, delaytime, delayfeedback); From 9971867e2f2adfa9b93f244bccdf71875ab65913 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 28 May 2023 12:41:53 +0200 Subject: [PATCH 12/63] clamp function --- packages/core/util.mjs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/core/util.mjs b/packages/core/util.mjs index 37fe6b6c1..5dbf65fcb 100644 --- a/packages/core/util.mjs +++ b/packages/core/util.mjs @@ -213,3 +213,5 @@ export const splitAt = function (index, value) { }; export const zipWith = (f, xs, ys) => xs.map((n, i) => f(n, ys[i])); + +export const clamp = (num, min, max) => Math.min(Math.max(num, min), max); From d2dffe318685ec7d196d654ccb1a60b4cecc6acc Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 28 May 2023 12:42:15 +0200 Subject: [PATCH 13/63] MiniRepl: consume font settings --- packages/react/src/components/MiniRepl.jsx | 2 ++ website/src/docs/MiniRepl.css | 4 ++++ website/src/docs/MiniRepl.jsx | 2 ++ 3 files changed, 8 insertions(+) diff --git a/packages/react/src/components/MiniRepl.jsx b/packages/react/src/components/MiniRepl.jsx index 8ff738e69..c13ff28ed 100644 --- a/packages/react/src/components/MiniRepl.jsx +++ b/packages/react/src/components/MiniRepl.jsx @@ -24,6 +24,7 @@ export function MiniRepl({ onPaint, canvasHeight = 200, fontSize = 18, + fontFamily, hideHeader = false, theme, keybindings, @@ -156,6 +157,7 @@ export function MiniRepl({ onChange={setCode} onViewChanged={setView} theme={theme} + fontFamily={fontFamily} fontSize={fontSize} keybindings={keybindings} /> diff --git a/website/src/docs/MiniRepl.css b/website/src/docs/MiniRepl.css index e9b49af84..5e5206714 100644 --- a/website/src/docs/MiniRepl.css +++ b/website/src/docs/MiniRepl.css @@ -7,3 +7,7 @@ border: 1px solid var(--lineHighlight); padding: 2px; } + +.cm-scroller { + font-family: inherit !important; +} diff --git a/website/src/docs/MiniRepl.jsx b/website/src/docs/MiniRepl.jsx index 651241a95..6dfaf28e0 100644 --- a/website/src/docs/MiniRepl.jsx +++ b/website/src/docs/MiniRepl.jsx @@ -60,6 +60,8 @@ export function MiniRepl({ theme={themes[theme]} hideHeader={hideHeader} keybindings={keybindings} + fontFamily={fontFamily} + fontSize={fontSize} onPaint={ claviature ? (ctx, time, haps, drawTime) => { From 1e9979ae186b84d3c1f97f36b9167b7a7d5f5094 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 28 May 2023 12:42:24 +0200 Subject: [PATCH 14/63] start fleshing out effects chapter --- website/src/pages/workshop/first-effects.mdx | 166 ++++++++++++++++++- website/src/pages/workshop/first-sounds.mdx | 35 ++-- 2 files changed, 181 insertions(+), 20 deletions(-) diff --git a/website/src/pages/workshop/first-effects.mdx b/website/src/pages/workshop/first-effects.mdx index 1b718b166..3c0fc0fda 100644 --- a/website/src/pages/workshop/first-effects.mdx +++ b/website/src/pages/workshop/first-effects.mdx @@ -7,15 +7,175 @@ import { MiniRepl } from '../../docs/MiniRepl'; # First Effects +import Box from '@components/Box.astro'; + +We have sounds, we have notes, now let's look at effects! + +**low-pass filter with lpf** + +/2") +.sound("sawtooth").lpf(800)`} +/> + + + +- Change lpf to 200. Notice how it gets muffled. Think of it as standing in front of the club with the door closed 🚪. +- Now let's open the door... change it to 8000. Notice how it gets sharper 🪩 + + + +**automate the filter** + +/2") +.sound("sawtooth").lpf("200 1000")`} +/> + + + +- Try adding more values +- Notice how the pattern in lpf does not change the overall rhythm + + + **vowel** - +/2") +.sound("sawtooth").vowel("/2")`} +/> -You can probably think of more vowels :) + + +- Try adding more values +- Notice how the pattern in lpf does not change the overall rhythm + + **gain** - + + + + +Rhythm is all about dynamics! + +Remove the gain and notice how flat it sounds. + + + +**stacks within stacks** + +Let's combine all of the above into a little tune: + +/2") + .sound("sawtooth").lpf("200 1000"), + note("<[c3,g3,e4] [bb2,f3,d4] [a2,f3,c4] [bb2,g3,eb4]>/2") + .sound("sawtooth").vowel("/2") +) `} +/> + + + +Pay attention to where the commas are and identify the individual parts of the stacks. +The 3 parts (drums, bassline, chords) are exactly as earlier, just stacked together. + + + +**fast and slow** + +We can use `fast` and `slow` to change the tempo of a pattern outside of Mini-Notation: + + + + + +Change the `slow` value. Try replacing it with `fast`. + +What happens if you use a pattern like `"<1 [2 4]>"`? + + + +By the way, inside Mini-Notation, `fast` is `*` and slow is `/`. + +")`} /> + +**delay** + + ~]") + .sound("gm_electric_guitar_muted"), + sound("").bank("RolandTR707") +).delay(".5")`} +/> + + + +Try some `delay` values between 0 and 1. Btw, `.5` is short for `0.5` + +What happens if you use `.delay(".8:.125")` ? Can you guess what the second number does? + +What happens if you use `.delay(".8:.06:.8")` ? Can you guess what the third number does? + + + +**room** + + ~@16] ~>/2") +.scale("D4:minor").sound("gm_accordion:2") +.room(2)`} +/> + + + +Try different values! + +Add a delay too! + + + +**little dub tune** + + ~]") + .sound("gm_electric_guitar_muted"), + sound("").bank("RolandTR707"), + n("<4 [3@3 4] [<2 0> ~@16] ~>/2") + .scale("D4:minor").sound("gm_accordion:2") + .room(2).gain(.5) +).delay(.5)`} +/> **control the gain with a sine wave** diff --git a/website/src/pages/workshop/first-sounds.mdx b/website/src/pages/workshop/first-sounds.mdx index 852977a3c..5b87d10f6 100644 --- a/website/src/pages/workshop/first-sounds.mdx +++ b/website/src/pages/workshop/first-sounds.mdx @@ -190,9 +190,10 @@ It is quite common that there are many ways to express the same idea. @@ -201,25 +202,25 @@ bd*2, [~ casio], Now we've learned the basics of the so called Mini-Notation, the rhythm language of Tidal. This is what we've leared so far: -| Concept | Syntax | Example | -| ----------------- | ---------- | -------------------------------------------------------------------------------- | -| Sequence | space | | -| Sample Number | :x | | -| Rests | ~ | | -| Sub-Sequences | \[ \] | | -| Sub-Sub-Sequences | \[ \[ \]\] | | -| Speed up | \* | | -| Parallel | , | | +| Concept | Syntax | Example | +| ----------------- | -------- | -------------------------------------------------------------------------------- | +| Sequence | space | | +| Sample Number | :x | | +| Rests | ~ | | +| Sub-Sequences | \[\] | | +| Sub-Sub-Sequences | \[\[\]\] | | +| Speed up | \* | | +| Parallel | , | | ## Examples **Basic rock beat** - + **Classic house** - + @@ -230,15 +231,15 @@ Certain drum patterns are reused across genres. We Will Rock you - + **Yellow Magic Orchestra - Firecracker** From 8679dc63beee41032e0e228994b2c5b3785eefb3 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Mon, 29 May 2023 02:18:27 +0200 Subject: [PATCH 15/63] pianoroll: also reflect gain in transparency --- packages/core/pianoroll.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/pianoroll.mjs b/packages/core/pianoroll.mjs index 594760155..2b5af1a79 100644 --- a/packages/core/pianoroll.mjs +++ b/packages/core/pianoroll.mjs @@ -87,7 +87,7 @@ Pattern.prototype.pianoroll = function ({ const isActive = event.whole.begin <= t && event.whole.end > t; ctx.fillStyle = event.context?.color || inactive; ctx.strokeStyle = event.context?.color || active; - ctx.globalAlpha = event.context.velocity ?? 1; + ctx.globalAlpha = event.context.velocity ?? event.value?.gain ?? 1; const timePx = scale((event.whole.begin - (flipTime ? to : from)) / timeExtent, ...timeRange); let durationPx = scale(event.duration / timeExtent, 0, timeAxis); const value = getValue(event); @@ -240,7 +240,7 @@ export function pianoroll({ const color = event.value?.color || event.context?.color; ctx.fillStyle = color || inactive; ctx.strokeStyle = color || active; - ctx.globalAlpha = event.context.velocity ?? 1; + ctx.globalAlpha = event.context.velocity ?? event.value?.gain ?? 1; const timePx = scale((event.whole.begin - (flipTime ? to : from)) / timeExtent, ...timeRange); let durationPx = scale(event.duration / timeExtent, 0, timeAxis); const value = getValue(event); From 536327f403dd3d79b3b0b00a088a4c8dcf9adf17 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Mon, 29 May 2023 02:18:36 +0200 Subject: [PATCH 16/63] effects chapter mostly finished --- website/src/pages/workshop/first-effects.mdx | 170 ++++++++++++------- 1 file changed, 112 insertions(+), 58 deletions(-) diff --git a/website/src/pages/workshop/first-effects.mdx b/website/src/pages/workshop/first-effects.mdx index 3c0fc0fda..f1162ef09 100644 --- a/website/src/pages/workshop/first-effects.mdx +++ b/website/src/pages/workshop/first-effects.mdx @@ -11,7 +11,9 @@ import Box from '@components/Box.astro'; We have sounds, we have notes, now let's look at effects! -**low-pass filter with lpf** +## Some basic effects + +**low-pass filter** +lpf = **l**ow **p**ass **f**ilter + - Change lpf to 200. Notice how it gets muffled. Think of it as standing in front of the club with the door closed 🚪. -- Now let's open the door... change it to 8000. Notice how it gets sharper 🪩 +- Now let's open the door... change it to 5000. Notice how it gets brighter ✨🪩 -**automate the filter** +**pattern the filter** **vowel** @@ -52,13 +58,6 @@ We have sounds, we have notes, now let's look at effects! .sound("sawtooth").vowel("/2")`} /> - - -- Try adding more values -- Notice how the pattern in lpf does not change the overall rhythm - - - **gain** Rhythm is all about dynamics! -Remove the gain and notice how flat it sounds. +- Remove `.gain(...)` and notice how flat it sounds. +- Bring it back by undoing (ctrl+z) @@ -88,7 +89,7 @@ Let's combine all of the above into a little tune: tune={`stack( stack( sound("hh*8").gain("[.25 1]*2"), - sound("bd*2,~ rim") + sound("bd*2,~ sd:1") ), note("<[c2 c3]*4 [bb1 bb2]*4 [f2 f3]*4 [eb2 eb3]*4>/2") .sound("sawtooth").lpf("200 1000"), @@ -99,29 +100,11 @@ Let's combine all of the above into a little tune: -Pay attention to where the commas are and identify the individual parts of the stacks. -The 3 parts (drums, bassline, chords) are exactly as earlier, just stacked together. +Try to identify the individual parts of the stacks, pay attention to where the commas are. +The 3 parts (drums, bassline, chords) are exactly as earlier, just stacked together, separated by comma. -**fast and slow** - -We can use `fast` and `slow` to change the tempo of a pattern outside of Mini-Notation: - - - - - -Change the `slow` value. Try replacing it with `fast`. - -What happens if you use a pattern like `"<1 [2 4]>"`? - - - -By the way, inside Mini-Notation, `fast` is `*` and slow is `/`. - -")`} /> - **delay** -**room** +**room aka reverb** ~]") - .sound("gm_electric_guitar_muted"), - sound("").bank("RolandTR707"), + .sound("gm_electric_guitar_muted").delay(.5), + sound("").bank("RolandTR707").delay(.5), n("<4 [3@3 4] [<2 0> ~@16] ~>/2") .scale("D4:minor").sound("gm_accordion:2") .room(2).gain(.5) -).delay(.5)`} +)`} /> -**control the gain with a sine wave** - - - -Try also `saw`, `square`, `tri` - -**The 'structure' comes from the left - try swapping:** - - - -**speed of playback, e.g. 2 = double speed (up 1 octave)** - - +Let's add a bass to make this complete: ~]") + .sound("gm_electric_guitar_muted").delay(.5), + sound("").bank("RolandTR707").delay(.5), + n("<4 [3@3 4] [<2 0> ~@16] ~>/2") + .scale("D4:minor").sound("gm_accordion:2") + .room(2).gain(.4), + n("<0 [~ 0] 4 [3 2] [0 ~] [0 ~] <0 2> ~>*2") + .scale("D2:minor") + .sound("sawtooth,triangle").lpf(800) +)`} /> + + +Try adding `.hush()` at the end of one of the patterns in the stack... + + + **pan** - +**speed** -**delay** +").room(.2)`} /> - +**fast and slow** -**room** +We can use `fast` and `slow` to change the tempo of a pattern outside of Mini-Notation: - + + + + +Change the `slow` value. Try replacing it with `fast`. + +What happens if you use a pattern like `.fast("<1 [2 4]>")`? + + + +By the way, inside Mini-Notation, `fast` is `*` and slow is `/`. + +")`} /> + +## automation with signals + +Instead of changing values stepwise, we can also control them with signals: + + + + + +The basic waveforms for signals are `sine`, `saw`, `square`, `tri` 🌊 + +Try also random signals `rand` and `perlin`! + +The gain is visualized as transparency in the pianoroll. + + + +**setting a range** + + + + + +What happens if you flip the range values? + + + +**setting a range** + +By default, waves oscillate between 0 to 1. We can change that with `range`: + +/2") +.sound("sawtooth") +.lpf(sine.range(100, 2000).slow(8))`} +/> + + + +Notice how the wave is slowed down. The whole automation will take 8 cycles to repeat. + + + +## Recap + +| name | example | +| ----- | ----------------------------------------------------------------------------------------------- | +| lpf | ")`} /> | +| vowel | ")`} /> | +| gain | | +| delay | | +| room | | +| pan | | +| speed | ")`} /> | +| range | | From 39d5955e587f70e136a09267540755dfcef66aa6 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Mon, 29 May 2023 12:39:05 +0200 Subject: [PATCH 17/63] add function recap to sounds chapter --- website/src/pages/workshop/first-sounds.mdx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/website/src/pages/workshop/first-sounds.mdx b/website/src/pages/workshop/first-sounds.mdx index 5b87d10f6..b651c0ba4 100644 --- a/website/src/pages/workshop/first-sounds.mdx +++ b/website/src/pages/workshop/first-sounds.mdx @@ -212,6 +212,14 @@ This is what we've leared so far: | Speed up | \* | | | Parallel | , | | +The Mini-Notation is usually used inside some function. These are the functions we've seen so far: + +| Name | Description | Example | +| ----- | ----------------------------------- | ---------------------------------------------------------------------------------- | +| sound | plays the sound of the given name | | +| bank | selects the sound bank | | +| cpm | sets the tempo in cycles per minute | | + ## Examples **Basic rock beat** From 8b7bb7b6ae482458872971ac9d63d0d473b72d1b Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Mon, 29 May 2023 12:39:17 +0200 Subject: [PATCH 18/63] add adsr section to effects chapter --- website/src/pages/workshop/first-effects.mdx | 49 ++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/website/src/pages/workshop/first-effects.mdx b/website/src/pages/workshop/first-effects.mdx index f1162ef09..c9b6dc569 100644 --- a/website/src/pages/workshop/first-effects.mdx +++ b/website/src/pages/workshop/first-effects.mdx @@ -4,6 +4,7 @@ layout: ../../layouts/MainLayout.astro --- import { MiniRepl } from '../../docs/MiniRepl'; +import QA from '@components/QA'; # First Effects @@ -105,6 +106,54 @@ The 3 parts (drums, bassline, chords) are exactly as earlier, just stacked toget +**adsr envelope** + +") +.sound("sawtooth").lpf(600) +.attack(.1) +.decay(.1) +.sustain(.25) +.release(.2)`} +/> + + + +Try to find out what the numbers do.. Compare the following + +- attack: `.5` vs `0` +- decay: `.5` vs `0` +- sustain: `1` vs `.25` vs `0` +- release: `0` vs `.5` vs `1` + +Can you guess what they do? + + + + + +- attack: time it takes to fade in +- decay: time it takes to fade to sustain +- sustain: level after decay +- release: time it takes to fade out after note is finished + +![ADSR](https://upload.wikimedia.org/wikipedia/commons/thumb/e/ea/ADSR_parameter.svg/1920px-ADSR_parameter.svg.png) + + + +**adsr short notation** + +") +.sound("sawtooth").lpf(600) +.adsr(".1:.1:.5:.2") +`} +/> + **delay** Date: Mon, 29 May 2023 12:39:36 +0200 Subject: [PATCH 19/63] add compound adsr + ds controls --- packages/core/controls.mjs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index ef5bd33f9..c4044d4a4 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -4,7 +4,7 @@ Copyright (C) 2022 Strudel contributors - see . */ -import { Pattern, sequence } from './pattern.mjs'; +import { Pattern, register, sequence } from './pattern.mjs'; import { zipWith } from './util.mjs'; const controls = {}; @@ -810,4 +810,15 @@ generic_params.forEach(([names, ...aliases]) => { controls.createParams = (...names) => names.reduce((acc, name) => Object.assign(acc, { [name]: controls.createParam(name) }), {}); +controls.adsr = register('adsr', (adsr, pat) => { + adsr = !Array.isArray(adsr) ? [adsr] : adsr; + const [attack, decay, sustain, release] = adsr; + return pat.set({ attack, decay, sustain, release }); +}); +controls.ds = register('ds', (ds, pat) => { + ds = !Array.isArray(ds) ? [ds] : ds; + const [decay, sustain] = ds; + return pat.set({ decay, sustain }); +}); + export default controls; From c4a38d9008fd1cc81de288c171286aafa0716bcc Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Tue, 30 May 2023 06:16:02 +0200 Subject: [PATCH 20/63] + pattern effects chapter + recap page + only load mini repls when visible --- website/src/config.ts | 3 +- website/src/pages/workshop/first-effects.mdx | 66 +++--- website/src/pages/workshop/first-notes.mdx | 8 + website/src/pages/workshop/first-sounds.mdx | 11 + website/src/pages/workshop/intro.mdx | 16 ++ .../src/pages/workshop/pattern-effects.mdx | 194 ++++++++++++++++++ website/src/pages/workshop/recap.mdx | 98 +++++++++ 7 files changed, 362 insertions(+), 34 deletions(-) create mode 100644 website/src/pages/workshop/pattern-effects.mdx create mode 100644 website/src/pages/workshop/recap.mdx diff --git a/website/src/config.ts b/website/src/config.ts index 6fc7bb771..a3629fe34 100644 --- a/website/src/config.ts +++ b/website/src/config.ts @@ -47,7 +47,8 @@ export const SIDEBAR: Sidebar = { { text: 'First Sounds', link: 'workshop/first-sounds' }, { text: 'First Notes', link: 'workshop/first-notes' }, { text: 'First Effects', link: 'workshop/first-effects' }, - { text: 'Mini Notation', link: 'workshop/mini-notation' }, + { text: 'Pattern Effects', link: 'workshop/pattern-effects' }, + { text: 'Recap', link: 'workshop/recap' }, ], Tutorial: [ { text: 'Getting Started', link: 'learn/getting-started' }, diff --git a/website/src/pages/workshop/first-effects.mdx b/website/src/pages/workshop/first-effects.mdx index c9b6dc569..1428e2bdf 100644 --- a/website/src/pages/workshop/first-effects.mdx +++ b/website/src/pages/workshop/first-effects.mdx @@ -18,7 +18,7 @@ We have sounds, we have notes, now let's look at effects! /2") .sound("sawtooth").lpf(800)`} /> @@ -36,7 +36,7 @@ lpf = **l**ow **p**ass **f**ilter /2") .sound("sawtooth").lpf("200 1000")`} /> @@ -54,7 +54,7 @@ We will learn how to automate with waves later... /2") .sound("sawtooth").vowel("/2")`} /> @@ -63,7 +63,7 @@ We will learn how to automate with waves later... -**adsr envelope** +**shape the sound with an adsr envelope** ") .sound("sawtooth").lpf(600) .attack(.1) @@ -147,7 +147,7 @@ Can you guess what they do? ") .sound("sawtooth").lpf(600) .adsr(".1:.1:.5:.2") @@ -158,7 +158,7 @@ Can you guess what they do? ~]") .sound("gm_electric_guitar_muted"), @@ -180,7 +180,7 @@ What happens if you use `.delay(".8:.06:.8")` ? Can you guess what the third num ~@16] ~>/2") .scale("D4:minor").sound("gm_accordion:2") .room(2)`} @@ -198,7 +198,7 @@ Add a delay too! ~]") .sound("gm_electric_guitar_muted").delay(.5), @@ -213,7 +213,7 @@ Let's add a bass to make this complete: ~]") .sound("gm_electric_guitar_muted").delay(.5), @@ -237,7 +237,7 @@ Try adding `.hush()` at the end of one of the patterns in the stack... ").room(.2)`} /> +").room(.2)`} /> **fast and slow** We can use `fast` and `slow` to change the tempo of a pattern outside of Mini-Notation: - + @@ -263,13 +263,13 @@ What happens if you use a pattern like `.fast("<1 [2 4]>")`? By the way, inside Mini-Notation, `fast` is `*` and slow is `/`. -")`} /> +")`} /> ## automation with signals Instead of changing values stepwise, we can also control them with signals: - + @@ -283,7 +283,9 @@ The gain is visualized as transparency in the pianoroll. **setting a range** - +By default, waves oscillate between 0 to 1. We can change that with `range`: + + @@ -291,13 +293,11 @@ What happens if you flip the range values? -**setting a range** - -By default, waves oscillate between 0 to 1. We can change that with `range`: +We can change the automation speed with slow / fast: /2") .sound("sawtooth") .lpf(sine.range(100, 2000).slow(8))`} @@ -305,19 +305,19 @@ By default, waves oscillate between 0 to 1. We can change that with `range`: -Notice how the wave is slowed down. The whole automation will take 8 cycles to repeat. +The whole automation will now take 8 cycles to repeat. ## Recap -| name | example | -| ----- | ----------------------------------------------------------------------------------------------- | -| lpf | ")`} /> | -| vowel | ")`} /> | -| gain | | -| delay | | -| room | | -| pan | | -| speed | ")`} /> | -| range | | +| name | example | +| ----- | -------------------------------------------------------------------------------------------------- | +| lpf | ")`} /> | +| vowel | ")`} /> | +| gain | | +| delay | | +| room | | +| pan | | +| speed | ")`} /> | +| range | | diff --git a/website/src/pages/workshop/first-notes.mdx b/website/src/pages/workshop/first-notes.mdx index b986ba791..330dd0721 100644 --- a/website/src/pages/workshop/first-notes.mdx +++ b/website/src/pages/workshop/first-notes.mdx @@ -318,6 +318,14 @@ Let's recap what we've learned in this chapter: | Elongate | @ | | | Replicate | ! | | +New functions: + +| Name | Description | Example | +| ----- | ----------------------------------- | -------------------------------------------------------------------------------------------- | +| note | set pitch as number or letter | | +| scale | interpret `n` as scale degree | | +| stack | play patterns in parallel (read on) | | + ## Examples **Classy Bassline** diff --git a/website/src/pages/workshop/first-sounds.mdx b/website/src/pages/workshop/first-sounds.mdx index b651c0ba4..618943339 100644 --- a/website/src/pages/workshop/first-sounds.mdx +++ b/website/src/pages/workshop/first-sounds.mdx @@ -197,6 +197,16 @@ It is quite common that there are many ways to express the same idea. punchcard /> +**selecting sample numbers separately** + +Instead of using ":", we can also use the `n` function to select sample numbers: + + + +This is shorter and more readable than: + + + ## Recap Now we've learned the basics of the so called Mini-Notation, the rhythm language of Tidal. @@ -219,6 +229,7 @@ The Mini-Notation is usually used inside some function. These are the functions | sound | plays the sound of the given name | | | bank | selects the sound bank | | | cpm | sets the tempo in cycles per minute | | +| n | select sample number | | ## Examples diff --git a/website/src/pages/workshop/intro.mdx b/website/src/pages/workshop/intro.mdx index e7b075b4c..9e18bbfec 100644 --- a/website/src/pages/workshop/intro.mdx +++ b/website/src/pages/workshop/intro.mdx @@ -4,3 +4,19 @@ layout: ../../layouts/MainLayout.astro --- # Introduction + +## goals + +- be beginner friendly +- teach a representative subset of strudel / tidal +- introduce one new thing at a time +- give practical / musical examples +- encourage self-experimentation +- hands on learning > walls of text +- maintain flow state +- no setup required + +## inspired by + +- https://github.com/tidalcycles/tidal-workshop/blob/master/workshop.tidal +- https://learningmusic.ableton.com diff --git a/website/src/pages/workshop/pattern-effects.mdx b/website/src/pages/workshop/pattern-effects.mdx new file mode 100644 index 000000000..7e028b6f8 --- /dev/null +++ b/website/src/pages/workshop/pattern-effects.mdx @@ -0,0 +1,194 @@ +--- +title: Pattern Effects +layout: ../../layouts/MainLayout.astro +--- + +import { MiniRepl } from '@src/docs/MiniRepl'; +import Box from '@components/Box.astro'; +import QA from '@components/QA'; + +# Pattern Effects + +Up until now, most of the functions we've seen are what other music programs are typically capable of: sequencing sounds, playing notes, controlling effects. + +In this chapter, we are going to look at functions that are more unique to tidal. + +**reverse patterns with rev** + + + +**play pattern left and modify it right with jux** + + + +This is the same as: + + + +Let's visualize what happens here: + + + + + +Try commenting out one of the two by adding `//` before a line + + + +**multiple tempos** + + + +This is like doing + + + + + +Try commenting out one or more by adding `//` before a line + + + +**add** + +>")) +.color(">").adsr("[.1 0]:.2:[1 0]") +.sound("gm_acoustic_bass").room(.5)`} + punchcard +/> + + + +If you add a number to a note, the note will be treated as if it was a number + + + +We can add as often as we like: + +>").add("0,7")) +.color(">").adsr("[.1 0]:.2:[1 0]") +.sound("gm_acoustic_bass").room(.5)`} + punchcard +/> + +**add with scale** + + [~ <4 1>]>*2".add("<0 [0,2,4]>/4")) +.scale("C5:minor").release(.5) +.sound("gm_xylophone").room(.5)`} + punchcard +/> + +**time to stack** + + [~ <4 1>]>*2".add("<0 [0,2,4]>/4")) + .scale("C5:minor") + .sound("gm_xylophone") + .room(.4).delay(.125), + note("c2 [eb3,g3]".add("<0 <1 -1>>")) + .adsr("[.1 0]:.2:[1 0]") + .sound("gm_acoustic_bass") + .room(.5), + n("0 1 [2 3] 2").sound("jazz").jux(rev).slow(2) +)`} +/> + +**ply** + + + +this is like writing: + + + + + +Try patterning the `ply` function, for example using `"<1 2 1 3>"` + + + +**off** + +] <2 3> [~ 1]>" + .off(1/8, x=>x.add(4)) + //.off(1/4, x=>x.add(7)) +).scale("/4") +.s("triangle").room(.5).ds(".1:0").delay(.5)`} + punchcard +/> + + + +In the notation `x=>x.`, the `x` is the shifted pattern, which where modifying. + + + +The above is like writing: + +] <2 3> [~ 1]>*2").color("cyan"), + n("<0 [4 <3 2>] <2 3> [~ 1]>*2".add(7).late(1/8)).color("magenta") +).scale("/2") +.s("triangle").adsr(".01:.1:0").room(.5)`} + punchcard +/> + +off is also useful for sounds: + +x.speed(1.5).gain(.25))`} +/> + +| name | description | example | +| ---- | ------------------------------ | ---------------------------------------------------------------------------------------------- | +| rev | reverse | | +| jux | split left/right, modify right | | +| add | add numbers / notes | ")).scale("C:minor")`} /> | +| ply | speed up each event n times | ")`} /> | +| off | copy, shift time & modify | x.speed(2))`} /> | diff --git a/website/src/pages/workshop/recap.mdx b/website/src/pages/workshop/recap.mdx new file mode 100644 index 000000000..bc9c4648c --- /dev/null +++ b/website/src/pages/workshop/recap.mdx @@ -0,0 +1,98 @@ +--- +title: Recap +layout: ../../layouts/MainLayout.astro +--- + +import { MiniRepl } from '../../docs/MiniRepl'; + +# Workshop Recap + +This page is just a listing of all functions covered in the workshop! + +## Mini Notation + +| Concept | Syntax | Example | +| ----------------- | -------- | -------------------------------------------------------------------------------- | +| Sequence | space | | +| Sample Number | :x | | +| Rests | ~ | | +| Sub-Sequences | \[\] | | +| Sub-Sub-Sequences | \[\[\]\] | | +| Speed up | \* | | +| Parallel | , | | +| Slow down | \/ | | +| Alternate | \<\> | ")`} /> | +| Elongate | @ | | +| Replicate | ! | | + +## Sounds + +| Name | Description | Example | +| ----- | --------------------------------- | ---------------------------------------------------------------------------------- | +| sound | plays the sound of the given name | | +| bank | selects the sound bank | | +| n | select sample number | | + +## Notes + +| Name | Description | Example | +| --------- | ----------------------------------- | -------------------------------------------------------------------------------------------- | +| note | set pitch as number or letter | | +| n + scale | set note in scale | | +| stack | play patterns in parallel (read on) | | + +## Audio Effects + +| name | example | +| ----- | -------------------------------------------------------------------------------------------------- | +| lpf | ")`} /> | +| vowel | ")`} /> | +| gain | | +| delay | | +| room | | +| pan | | +| speed | ")`} /> | +| range | | + +## Pattern Effects + +| name | description | example | +| ---- | ----------------------------------- | ---------------------------------------------------------------------------------------------- | +| cpm | sets the tempo in cycles per minute | | +| fast | speed up | | +| slow | slow down | | +| rev | reverse | | +| jux | split left/right, modify right | | +| add | add numbers / notes | ")).scale("C:minor")`} /> | +| ply | speed up each event n times | ")`} /> | +| off | copy, shift time & modify | x.speed(2))`} /> | + +## Samples + +``` +casio control crow techno house jazz +metal east jvbass juno insect space wind +bd sd sn cp hh +piano +``` + +## Synths + +``` +gm_electric_guitar_muted gm_acoustic_bass +gm_voice_oohs gm_blown_bottle sawtooth square triangle +gm_xylophone gm_synth_bass_1 gm_synth_strings_1 +``` + +## Banks + +``` +RolandTR909 +``` + +## Scales + +``` +major minor dorian mixolydian +minor:pentatonic major:pentatonic +``` From 61a9b01062f9952eabc8b178c27f8d8d1795ccd8 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Mon, 5 Jun 2023 23:48:04 +0200 Subject: [PATCH 21/63] started german translation of workshop --- .../components/LeftSidebar/LeftSidebar.astro | 4 +- website/src/config.ts | 11 + .../src/pages/de/workshop/first-effects.mdx | 323 +++++++++++++++ website/src/pages/de/workshop/first-notes.mdx | 388 ++++++++++++++++++ .../src/pages/de/workshop/first-sounds.mdx | 310 ++++++++++++++ website/src/pages/de/workshop/index.astro | 3 + website/src/pages/de/workshop/intro.mdx | 22 + website/src/pages/de/workshop/langebank.mdx | 154 +++++++ .../src/pages/de/workshop/mini-notation.mdx | 69 ++++ .../src/pages/de/workshop/pattern-effects.mdx | 194 +++++++++ website/src/pages/de/workshop/recap.mdx | 98 +++++ 11 files changed, 1574 insertions(+), 2 deletions(-) create mode 100644 website/src/pages/de/workshop/first-effects.mdx create mode 100644 website/src/pages/de/workshop/first-notes.mdx create mode 100644 website/src/pages/de/workshop/first-sounds.mdx create mode 100644 website/src/pages/de/workshop/index.astro create mode 100644 website/src/pages/de/workshop/intro.mdx create mode 100644 website/src/pages/de/workshop/langebank.mdx create mode 100644 website/src/pages/de/workshop/mini-notation.mdx create mode 100644 website/src/pages/de/workshop/pattern-effects.mdx create mode 100644 website/src/pages/de/workshop/recap.mdx diff --git a/website/src/components/LeftSidebar/LeftSidebar.astro b/website/src/components/LeftSidebar/LeftSidebar.astro index 18327ebed..ce4dbb800 100644 --- a/website/src/components/LeftSidebar/LeftSidebar.astro +++ b/website/src/components/LeftSidebar/LeftSidebar.astro @@ -1,5 +1,5 @@ --- -// import { getLanguageFromURL } from '../../languages'; +import { getLanguageFromURL } from '../../languages'; import { SIDEBAR } from '../../config'; type Props = { @@ -10,7 +10,7 @@ const { currentPage } = Astro.props as Props; const { BASE_URL } = import.meta.env; let currentPageMatch = currentPage.slice(BASE_URL.length, currentPage.endsWith('/') ? -1 : undefined); -const langCode = 'en'; // getLanguageFromURL(currentPage); +const langCode = getLanguageFromURL(currentPage) || 'en'; const sidebar = SIDEBAR[langCode]; --- diff --git a/website/src/config.ts b/website/src/config.ts index a3629fe34..e824f35b4 100644 --- a/website/src/config.ts +++ b/website/src/config.ts @@ -24,6 +24,7 @@ export type Frontmatter = { export const KNOWN_LANGUAGES = { English: 'en', + German: 'de', } as const; export const KNOWN_LANGUAGE_CODES = Object.values(KNOWN_LANGUAGES); @@ -41,6 +42,16 @@ export const ALGOLIA = { export type SidebarLang = Record; export type Sidebar = Record<(typeof KNOWN_LANGUAGE_CODES)[number], SidebarLang>; export const SIDEBAR: Sidebar = { + de: { + Workshop: [ + { text: 'Intro', link: 'de/workshop/intro' }, + { text: 'Erste Sounds', link: 'de/workshop/first-sounds' }, + { text: 'Erste Töne', link: 'de/workshop/first-notes' }, + { text: 'Erste Effekte', link: 'de/workshop/first-effects' }, + { text: 'Pattern Effekte', link: 'de/workshop/pattern-effects' }, + { text: 'Rückblick', link: 'de/workshop/recap' }, + ], + }, en: { Workshop: [ { text: 'Intro', link: 'workshop/intro' }, diff --git a/website/src/pages/de/workshop/first-effects.mdx b/website/src/pages/de/workshop/first-effects.mdx new file mode 100644 index 000000000..d547548db --- /dev/null +++ b/website/src/pages/de/workshop/first-effects.mdx @@ -0,0 +1,323 @@ +--- +title: First Effects +layout: ../../../layouts/MainLayout.astro +--- + +import { MiniRepl } from '../../../docs/MiniRepl'; +import QA from '@components/QA'; + +# First Effects + +import Box from '@components/Box.astro'; + +We have sounds, we have notes, now let's look at effects! + +## Some basic effects + +**low-pass filter** + +/2") +.sound("sawtooth").lpf(800)`} +/> + + + +lpf = **l**ow **p**ass **f**ilter + +- Change lpf to 200. Notice how it gets muffled. Think of it as standing in front of the club with the door closed 🚪. +- Now let's open the door... change it to 5000. Notice how it gets brighter ✨🪩 + + + +**pattern the filter** + +/2") +.sound("sawtooth").lpf("200 1000")`} +/> + + + +- Try adding more values +- Notice how the pattern in lpf does not change the overall rhythm + +We will learn how to automate with waves later... + + + +**vowel** + +/2") +.sound("sawtooth").vowel("/2")`} +/> + +**gain** + + + + + +Rhythm is all about dynamics! + +- Remove `.gain(...)` and notice how flat it sounds. +- Bring it back by undoing (ctrl+z) + + + +**stacks within stacks** + +Let's combine all of the above into a little tune: + +/2") + .sound("sawtooth").lpf("200 1000"), + note("<[c3,g3,e4] [bb2,f3,d4] [a2,f3,c4] [bb2,g3,eb4]>/2") + .sound("sawtooth").vowel("/2") +) `} +/> + + + +Try to identify the individual parts of the stacks, pay attention to where the commas are. +The 3 parts (drums, bassline, chords) are exactly as earlier, just stacked together, separated by comma. + + + +**shape the sound with an adsr envelope** + +") +.sound("sawtooth").lpf(600) +.attack(.1) +.decay(.1) +.sustain(.25) +.release(.2)`} +/> + + + +Try to find out what the numbers do.. Compare the following + +- attack: `.5` vs `0` +- decay: `.5` vs `0` +- sustain: `1` vs `.25` vs `0` +- release: `0` vs `.5` vs `1` + +Can you guess what they do? + + + + + +- attack: time it takes to fade in +- decay: time it takes to fade to sustain +- sustain: level after decay +- release: time it takes to fade out after note is finished + +![ADSR](https://upload.wikimedia.org/wikipedia/commons/thumb/e/ea/ADSR_parameter.svg/1920px-ADSR_parameter.svg.png) + + + +**adsr short notation** + +") +.sound("sawtooth").lpf(600) +.adsr(".1:.1:.5:.2") +`} +/> + +**delay** + + ~]") + .sound("gm_electric_guitar_muted"), + sound("").bank("RolandTR707") +).delay(".5")`} +/> + + + +Try some `delay` values between 0 and 1. Btw, `.5` is short for `0.5` + +What happens if you use `.delay(".8:.125")` ? Can you guess what the second number does? + +What happens if you use `.delay(".8:.06:.8")` ? Can you guess what the third number does? + + + +**room aka reverb** + + ~@16] ~>/2") +.scale("D4:minor").sound("gm_accordion:2") +.room(2)`} +/> + + + +Try different values! + +Add a delay too! + + + +**little dub tune** + + ~]") + .sound("gm_electric_guitar_muted").delay(.5), + sound("").bank("RolandTR707").delay(.5), + n("<4 [3@3 4] [<2 0> ~@16] ~>/2") + .scale("D4:minor").sound("gm_accordion:2") + .room(2).gain(.5) +)`} +/> + +Let's add a bass to make this complete: + + ~]") + .sound("gm_electric_guitar_muted").delay(.5), + sound("").bank("RolandTR707").delay(.5), + n("<4 [3@3 4] [<2 0> ~@16] ~>/2") + .scale("D4:minor").sound("gm_accordion:2") + .room(2).gain(.4), + n("<0 [~ 0] 4 [3 2] [0 ~] [0 ~] <0 2> ~>*2") + .scale("D2:minor") + .sound("sawtooth,triangle").lpf(800) +)`} +/> + + + +Try adding `.hush()` at the end of one of the patterns in the stack... + + + +**pan** + + + +**speed** + +").room(.2)`} /> + +**fast and slow** + +We can use `fast` and `slow` to change the tempo of a pattern outside of Mini-Notation: + + + + + +Change the `slow` value. Try replacing it with `fast`. + +What happens if you use a pattern like `.fast("<1 [2 4]>")`? + + + +By the way, inside Mini-Notation, `fast` is `*` and slow is `/`. + +")`} /> + +## automation with signals + +Instead of changing values stepwise, we can also control them with signals: + + + + + +The basic waveforms for signals are `sine`, `saw`, `square`, `tri` 🌊 + +Try also random signals `rand` and `perlin`! + +The gain is visualized as transparency in the pianoroll. + + + +**setting a range** + +By default, waves oscillate between 0 to 1. We can change that with `range`: + + + + + +What happens if you flip the range values? + + + +We can change the automation speed with slow / fast: + +/2") +.sound("sawtooth") +.lpf(sine.range(100, 2000).slow(8))`} +/> + + + +The whole automation will now take 8 cycles to repeat. + + + +## Recap + +| name | example | +| ----- | -------------------------------------------------------------------------------------------------- | +| lpf | ")`} /> | +| vowel | ")`} /> | +| gain | | +| delay | | +| room | | +| pan | | +| speed | ")`} /> | +| range | | diff --git a/website/src/pages/de/workshop/first-notes.mdx b/website/src/pages/de/workshop/first-notes.mdx new file mode 100644 index 000000000..c33fbb1a8 --- /dev/null +++ b/website/src/pages/de/workshop/first-notes.mdx @@ -0,0 +1,388 @@ +--- +title: First Notes +layout: ../../../layouts/MainLayout.astro +--- + +import { MiniRepl } from '@src/docs/MiniRepl'; +import { midi2note } from '@strudel.cycles/core/'; +import Box from '@components/Box.astro'; +import QA from '@components/QA'; + +# Erste Töne + +Jetzt schauen wir uns an wie man mit Tönen mit der `note` Funktion spielt. + +## Zahlen und Noten + +**Töne mit Zahlen** + + [midi2note(i + 36), i + 36]), + )} +/> + + + +Probiere verschiedene Zahlen aus! + +Versuch auch mal Kommazahlen, z.B. 55.5 (beachte die englische Schreibweise von Kommazahlen mit "." anstatt ",") + + + +**Töne mit Buchstaben** + + [n, n.split('')[0]]))} +/> + + + +Versuch verschiedene Buchstaben aus (a - g). + +Findest du Melodien die auch gleichzeitig ein Wort sind? Tipp: ☕ 🙈 🧚 + + + +**Vorzeichen** + + [n, n.split('').slice(0, 2).join('')]), + )} +/> + + [n, n.split('').slice(0, 2).join('')]), + )} +/> + +**Andere Oktaven** + + [n, n]))} + claviatureLabels={Object.fromEntries( + Array(49) + .fill() + .map((_, i) => [midi2note(i + 36), midi2note(i + 36)]), + )} +/> + + + +Probiere verschiedene Oktaven aus (1-8) + + + +Normalerweise kommen Leute die keine Noten besser mit Zahlen anstatt mit Buchstaben zurecht. +Daher benutzen die folgenden Beispiele meistens Zahlen. +Später sehen wir auch noch ein paar Tricks die es uns erleichtern Töne zu spielen die zueinander passen. + +## Den Sound verändern + +Genau wie bei geräuschhaften Sounds können wir den Klang unserer Töne mit `sound` verändern: + + + + + +Probier ein paar sounds aus: + +- gm_electric_guitar_muted - E-Gitarre +- gm_acoustic_bass - Kontrabass +- gm_voice_oohs - Chords +- gm_blown_bottle - Flasche +- sawtooth - Sägezahn-Welle +- square - Rechteck-Welle +- triangle - Dreieck-Welle +- Was ist mit bd, sd oder hh? +- Entferne `.sound('...')` komplett + + + +**Zwischen Sounds hin und her wechseln** + + + +**Gleichzeitige Sounds** + + + + + +Die patterns in `note` und `sound` werden kombiniert! + +Wir schauen uns später noch mehr Möglichkeiten an wie man patterns kombiniert. + + + +## Längere Sequenzen + +**Divide sequences with `/` to slow them down** + +{/* [c2 bb1 f2 eb2] */} + + + + + +The `/4` plays the sequence in brackets over 4 cycles (=4s). + +So each of the 4 notes is 1s long. + +Try adding more notes inside the brackets and notice how it gets faster. + + + +Because it is so common to just play one thing per cycle, you can.. + +**Play one per cycle with \< \>** + +").sound("gm_acoustic_bass")`} punchcard /> + + + +Try adding more notes inside the brackets and notice how it does **not** get faster. + + + +**Play one sequence per cycle** + +{/* <[c2 c3]*4 [bb1 bb2]*4 [f2 f3]*4 [eb2 eb3]*4>/2 */} + +/2") +.sound("gm_acoustic_bass")`} + punchcard +/> + +**Alternate between multiple things** + +") +.sound("gm_xylophone")`} + punchcard +/> + +This is also useful for unpitched sounds: + +, [~ hh]*2") +.bank("RolandTR909")`} + punchcard +/> + +## Scales + +Finding the right notes can be difficult.. Scales are here to help: + +") +.scale("C:minor").sound("piano")`} + punchcard +/> + + + +Try out different numbers. Any number should sound good! + +Try out different scales: + +- C:major +- A2:minor +- D:dorian +- G:mixolydian +- A2:minor:pentatonic +- F:major:pentatonic + + + +**automate scales** + +Just like anything, we can automate the scale with a pattern: + +, 2 4 <[6,8] [7,9]>") +.scale("/4") +.sound("piano")`} + punchcard +/> + + + +If you have no idea what these scale mean, don't worry. +These are just labels for different sets of notes that go well together. + +Take your time and you'll find scales you like! + + + +## Repeat & Elongate + +**Elongate with @** + + + + + +Not using `@` is like using `@1`. In the above example, c is 3 units long and eb is 1 unit long. + +Try changing that number! + + + +**Elongate within sub-sequences** + +*2") +.scale("/4") +.sound("gm_acoustic_bass")`} + punchcard +/> + + + +This groove is called a `shuffle`. +Each beat has two notes, where the first is twice as long as the second. +This is also sometimes called triplet swing. You'll often find it in blues and jazz. + + + +**Replicate** + +]").sound("piano")`} punchcard /> + + + +Try switching between `!`, `*` and `@` + +What's the difference? + + + +## Recap + +Let's recap what we've learned in this chapter: + +| Concept | Syntax | Example | +| --------- | ------ | ------------------------------------------------------------------- | +| Slow down | \/ | | +| Alternate | \<\> | ")`} /> | +| Elongate | @ | | +| Replicate | ! | | + +New functions: + +| Name | Description | Example | +| ----- | ----------------------------------- | -------------------------------------------------------------------------------------------- | +| note | set pitch as number or letter | | +| scale | interpret `n` as scale degree | | +| stack | play patterns in parallel (read on) | | + +## Examples + +**Classy Bassline** + +/2") +.sound("gm_synth_bass_1") +.lpf(800) // <-- we'll learn about this soon`} +/> + +**Classy Melody** + +*2\`).scale("C4:minor") +.sound("gm_synth_strings_1")`} +/> + +**Classy Drums** + +, [~ hh]*2") +.bank("RolandTR909")`} +/> + +**If there just was a way to play all the above at the same time.......** + + + +It's called `stack` 😙 + + + +/2") + .sound("gm_synth_bass_1").lpf(800), + n(\`< + [~ 0] 2 [0 2] [~ 2] + [~ 0] 1 [0 1] [~ 1] + [~ 0] 3 [0 3] [~ 3] + [~ 0] 2 [0 2] [~ 2] + >*2\`).scale("C4:minor") + .sound("gm_synth_strings_1"), + sound("bd*2, ~ , [~ hh]*2") + .bank("RolandTR909") +)`} +/> + +This is starting to sound like actual music! We have sounds, we have notes, now the last piece of the puzzle is missing: [effects](/workshop/first-effects) diff --git a/website/src/pages/de/workshop/first-sounds.mdx b/website/src/pages/de/workshop/first-sounds.mdx new file mode 100644 index 000000000..0f132bec0 --- /dev/null +++ b/website/src/pages/de/workshop/first-sounds.mdx @@ -0,0 +1,310 @@ +--- +title: Erste Sounds +layout: ../../../layouts/MainLayout.astro +--- + +import { MiniRepl } from '@src/docs/MiniRepl'; +import Box from '@components/Box.astro'; +import QA from '@components/QA'; + +## Erste Sounds + +{/* Let's start by making some noise: */} + +Los geht's mit ein paar Sounds: + + + + + +1. ⬆️ Klicke in das obige Textfeld ⬆️ +2. Drücke `Strg`+`Enter` zum Abspielen +3. Ändere `house` in `casio` +4. Drücke `Strg`+`Enter` zum Aktualisieren +5. Drücke `Strg`+`.` zum Stoppen + + + +Glückwunsch, du bist nun am live coden! + +**Probiere mehr Sounds aus** + +Mit ":" kannst du einen anderen Sound aus dem Set wählen: + + + + + +Ändere `east:1` in `east:2` um einen anderen Sound aus dem Set `east` zu hören. + +Du kannst auch andere Zahlen ausprobieren! Es kann sein dass du kurz nichts hörst während ein neuer Sound lädt. + + + +Hier sind ein paar mehr Sounds zum ausprobieren: + +``` +casio control crow techno house jazz +metal east jvbass juno insect space wind +``` + +Jetzt weißt du wie man verschiedene Sounds benutzt. +Vorerst bleiben wir bei den voreingestellten Sounds, später erfahren wir noch wie man eigene benutzt. + +## Drum Sounds + +Strudel kommt von Haus aus mit einer breiten Auswahl an Drum Sounds: + + + +Diese 2-Buchstaben Kombinationen stehen für verschiedene Teile eines Drumsets: + +- `bd` = **b**ass **d**rum - Basstrommel +- `sd` = **s**nare **d**rum - Schnarrtrommel +- `sn` = **sn**are +- `rim` = **rim**shot - Rahmenschlag +- `hh` = **h**i**h**at +- `oh` = **o**pen **h**ihat - Offenes Hi-Hat + +Wir können den Charakter des Drum Sounds verändern, indem wir mit `bank` die Drum Machine auswählen: + +{/* To change the sound character of our drums, we can use `bank` to change the drum machine: */} + + + +In diesem Beispiel ist `RolandTR909` der Name der Drum Machine, welche eine prägende Rolle für House und Techno Musik spielte. + + + +Ändere `RolandTR909` in + +- `AkaiLinn` +- `RhythmAce` +- `RolandTR808` +- `RolandTR707` +- `ViscoSpaceDrum` + +Es gibt noch viel mehr, aber das sollte fürs Erste reichen.. + + + +## Sequenzen / Sequences + +Im letzten Beispiel haben wir schon gesehen dass man mehrere Sounds hintereinander abspielen kann wenn man sie durch Leerzeichen trennt: + + + +Beachte wie der aktuell gespielte Sound im Code markiert und auch darunter visualisiert wird. + + + +Versuch noch mehr Sounds hinzuzfügen! + + + +**Je länger die Sequence, desto schneller** + + + +Der Inhalt einer Sequence wird in einen sogenannten Cycle (=Zyklus) zusammengequetscht. + +**Tempo ändern mit `cpm`** + + + + + +cpm = **c**ycles per **m**inute = Cycles pro Minute + +Das Tempo ist standardmäßig auf 60cpm eingestellt, also 1 Cycle pro Sekunde. + + + +Wir werden später noch mehr Möglichkeiten kennen lernen wie man das Tempo verändert. + +**Pausen mit '~'** + + + +**Unter-Sequenzen mit [Klammern]** + + + + + +Füge noch mehr Sounds in die Klammern ein! + + + +Genau wie bei der ganzen Sequence wird der eine Unter-Sequence schneller je mehr drin ist. + +**Multiplikation: Dinge schneller machen** + + + +**Multiplikation: Unter-Sequences schneller machen** + + + +**Multiplikation: Vieeeeeeel schneller** + + + + + +Tonhöhe = sehr schneller Rhythmus + + + +**Unter-Unter-Sequenzen mit [[Klammern]]** + + + + + +Du kannst so tief verschachteln wie du willst! + + + +**Parallele Sequenzen mit Komma** + + + +Du kannst soviele Kommas benutzen wie du magst: + + + +Kommas können auch in Unter-Sequenzen verwendet werden: + + + + + +Ist dir aufgefallen dass sich die letzten beiden Beispiele gleich anhören? + +Es kommt öfter vor dass man die gleiche Idee auf verschiedene Arten ausdrücken kann. + + + +**Mehrere Zeilen mit \`** + + + +**Sound Nummer separat auswählen** + +Anstatt mit ":" kann man die Sound Nummer auch separat mir der `n` Funktion steuern: + + + +Das ist kürzer und lesbarer als: + + + +## Rückblick + +Wir haben jetzt die Grundlagen der sogenannten Mini-Notation gelernt, der Rhythmus-Sprache von Tidal. + +Das haben wir bisher gelernt: + +| Concept | Syntax | Example | +| --------------------- | ----------- | -------------------------------------------------------------------------------- | +| Sequenz | Leerzeichen | | +| Sound Nummer | :x | | +| Pausen | ~ | | +| Unter-Sequenzen | \[\] | | +| Unter-Unter-Sequenzen | \[\[\]\] | | +| Schneller | \* | | +| Parallel | , | | + +Die mit Apostrophen umgebene Mini-Notation benutzt man normalerweise in eine sogenannten Funktion. +Die folgenden Funktionen haben wir bereits gesehen: + +| Name | Description | Example | +| ----- | -------------------------------------- | ---------------------------------------------------------------------------------- | +| sound | Spielt den Sound mit dem Namen | | +| bank | Wählt die Soundbank / Drum Machine | | +| cpm | Tempo in **C**ycles **p**ro **M**inute | | +| n | Sample Nummer | | + +## Beispiele + +**Einfacher Rock Beat** + + + +**Klassischer House** + + + + + +Ist die aufgefallen dass die letzten 2 Patterns extrem ähnlich sind? +Bestimmte Drum Patterns werden oft genreübergreifend wiederverwendet. + + + +We Will Rock you + + + +**Yellow Magic Orchestra - Firecracker** + + + +**Nachahmung eines 16 step sequencers** + + + +**Noch eins** + + + +**Nicht so typische Drums** + + + +Jetzt haben wir eine grundlegende Ahnung davon wie man mit Strudel Beats baut! +Im nächsten Kapitel werden wir ein paar [Töne spielen](/workshop/first-notes). diff --git a/website/src/pages/de/workshop/index.astro b/website/src/pages/de/workshop/index.astro new file mode 100644 index 000000000..9f79e4c22 --- /dev/null +++ b/website/src/pages/de/workshop/index.astro @@ -0,0 +1,3 @@ + diff --git a/website/src/pages/de/workshop/intro.mdx b/website/src/pages/de/workshop/intro.mdx new file mode 100644 index 000000000..5688476be --- /dev/null +++ b/website/src/pages/de/workshop/intro.mdx @@ -0,0 +1,22 @@ +--- +title: Introduction +layout: ../../../layouts/MainLayout.astro +--- + +# Introduction + +## goals + +- be beginner friendly +- teach a representative subset of strudel / tidal +- introduce one new thing at a time +- give practical / musical examples +- encourage self-experimentation +- hands on learning > walls of text +- maintain flow state +- no setup required + +## inspired by + +- https://github.com/tidalcycles/tidal-workshop/blob/master/workshop.tidal +- https://learningmusic.ableton.com diff --git a/website/src/pages/de/workshop/langebank.mdx b/website/src/pages/de/workshop/langebank.mdx new file mode 100644 index 000000000..7a1ca6977 --- /dev/null +++ b/website/src/pages/de/workshop/langebank.mdx @@ -0,0 +1,154 @@ + + +1. press play button to start +2. change `house` to `casio` +3. press refresh button to update +4. press stop button to stop + + + +**Change tempo** + + + +adding your own samples + + + +").slow(3)`} + punchcard +/> + +n(run(8)).sound("east") + +Shorter variant: + + + +polyrythms & polymeters + +-- This can make for flexible time signatures: + +d1 $ sound "[bd bd sn:5] [bd sn:3]" + +-- You can put subsequences inside subsequences: +d1 $ sound "[[bd bd] bd sn:5] [bd sn:3]" + +-- Keep going.. +d1 $ sound "[[bd [bd bd bd bd]] bd sn:5] [bd sn:3]" + +-- \* Polymetric / polyrhythmic sequences + +-- Play two subsequences at once by separating with a comma: + +d1 $ sound "[voodoo voodoo:3, arpy arpy:4 arpy:2]" + +-- compare how [,] and {,} work: + +d1 $ sound "[voodoo voodoo:3, arpy arpy:4 arpy:2]" + +d1 $ sound "{voodoo voodoo:3, arpy arpy:4 arpy:2}" + +d1 $ sound "[drum bd hh bd, can can:2 can:3 can:4 can:2]" + +d1 $ sound "{drum bd hh bd, can can:2 can:3 can:4 can:2}" + +d1 $ sound "[bd sn, can:2 can:3 can:1, arpy arpy:1 arpy:2 arpy:3 arpy:5]" + +d1 $ sound "{bd sn, can:2 can:3 can:1, arpy arpy:1 arpy:2 arpy:3 arpy:5}" + +**Play X per cycle with \{ \}** + + + + + +Try different numbers after `%` + +`{ ... }%1` is the same as `< ... >` + + + +## Bracket Recap + +- `[]` squeezes contents to 1 cycle +- `<>` plays one item per cycle +- `{}%x` plays x items per cycle + +/2")) +.sound("gm_synth_bass_1")`} +/> + +vertical + + +< 4 4 4 3> +<[2,7] [2,6] [1,6] [1,6]> +< 4 4 4 3> +>*2\`) +.scale("/4") +.sound("piano")`} +/> + +horizontal + +*2\`) +.scale("/4") +.sound("piano")`} +/> diff --git a/website/src/pages/de/workshop/mini-notation.mdx b/website/src/pages/de/workshop/mini-notation.mdx new file mode 100644 index 000000000..86bef096d --- /dev/null +++ b/website/src/pages/de/workshop/mini-notation.mdx @@ -0,0 +1,69 @@ +--- +title: First Sounds +layout: ../../../layouts/MainLayout.astro +--- + +import { MiniRepl } from '../../docs/MiniRepl'; + +# Mini Notation + +Mini Notation is everything between the quotes. It the short rhythm language of Tidal. + +## Cycles + +**The longer the sequence, the faster it runs** + + + +**Play less sounds per cycle with \{curly braces\}** + + + +**Use \`backticks\` for multiple lines** + + + +**Play one sounds per cycle with \** + +")`} punchcard /> + +This is the same as `{...}%1` + +## Operators + +**Multiplication: Speed things up** + + + +**Division: Slow things down** + + + +`bd` will play only every second time + +## Combining it all + +**Speed up Sub-Sequences** + + + +**Slow down Sequences** + + + +**Parallel Sub-Sequences** + + + +**Sample Numbers on groups** + + diff --git a/website/src/pages/de/workshop/pattern-effects.mdx b/website/src/pages/de/workshop/pattern-effects.mdx new file mode 100644 index 000000000..4be0a77a3 --- /dev/null +++ b/website/src/pages/de/workshop/pattern-effects.mdx @@ -0,0 +1,194 @@ +--- +title: Pattern Effects +layout: ../../../layouts/MainLayout.astro +--- + +import { MiniRepl } from '@src/docs/MiniRepl'; +import Box from '@components/Box.astro'; +import QA from '@components/QA'; + +# Pattern Effects + +Up until now, most of the functions we've seen are what other music programs are typically capable of: sequencing sounds, playing notes, controlling effects. + +In this chapter, we are going to look at functions that are more unique to tidal. + +**reverse patterns with rev** + + + +**play pattern left and modify it right with jux** + + + +This is the same as: + + + +Let's visualize what happens here: + + + + + +Try commenting out one of the two by adding `//` before a line + + + +**multiple tempos** + + + +This is like doing + + + + + +Try commenting out one or more by adding `//` before a line + + + +**add** + +>")) +.color(">").adsr("[.1 0]:.2:[1 0]") +.sound("gm_acoustic_bass").room(.5)`} + punchcard +/> + + + +If you add a number to a note, the note will be treated as if it was a number + + + +We can add as often as we like: + +>").add("0,7")) +.color(">").adsr("[.1 0]:.2:[1 0]") +.sound("gm_acoustic_bass").room(.5)`} + punchcard +/> + +**add with scale** + + [~ <4 1>]>*2".add("<0 [0,2,4]>/4")) +.scale("C5:minor").release(.5) +.sound("gm_xylophone").room(.5)`} + punchcard +/> + +**time to stack** + + [~ <4 1>]>*2".add("<0 [0,2,4]>/4")) + .scale("C5:minor") + .sound("gm_xylophone") + .room(.4).delay(.125), + note("c2 [eb3,g3]".add("<0 <1 -1>>")) + .adsr("[.1 0]:.2:[1 0]") + .sound("gm_acoustic_bass") + .room(.5), + n("0 1 [2 3] 2").sound("jazz").jux(rev).slow(2) +)`} +/> + +**ply** + + + +this is like writing: + + + + + +Try patterning the `ply` function, for example using `"<1 2 1 3>"` + + + +**off** + +] <2 3> [~ 1]>" + .off(1/8, x=>x.add(4)) + //.off(1/4, x=>x.add(7)) +).scale("/4") +.s("triangle").room(.5).ds(".1:0").delay(.5)`} + punchcard +/> + + + +In the notation `x=>x.`, the `x` is the shifted pattern, which where modifying. + + + +The above is like writing: + +] <2 3> [~ 1]>*2").color("cyan"), + n("<0 [4 <3 2>] <2 3> [~ 1]>*2".add(7).late(1/8)).color("magenta") +).scale("/2") +.s("triangle").adsr(".01:.1:0").room(.5)`} + punchcard +/> + +off is also useful for sounds: + +x.speed(1.5).gain(.25))`} +/> + +| name | description | example | +| ---- | ------------------------------ | ---------------------------------------------------------------------------------------------- | +| rev | reverse | | +| jux | split left/right, modify right | | +| add | add numbers / notes | ")).scale("C:minor")`} /> | +| ply | speed up each event n times | ")`} /> | +| off | copy, shift time & modify | x.speed(2))`} /> | diff --git a/website/src/pages/de/workshop/recap.mdx b/website/src/pages/de/workshop/recap.mdx new file mode 100644 index 000000000..85f3e015d --- /dev/null +++ b/website/src/pages/de/workshop/recap.mdx @@ -0,0 +1,98 @@ +--- +title: Recap +layout: ../../../layouts/MainLayout.astro +--- + +import { MiniRepl } from '../../../docs/MiniRepl'; + +# Workshop Recap + +This page is just a listing of all functions covered in the workshop! + +## Mini Notation + +| Concept | Syntax | Example | +| ----------------- | -------- | -------------------------------------------------------------------------------- | +| Sequence | space | | +| Sample Number | :x | | +| Rests | ~ | | +| Sub-Sequences | \[\] | | +| Sub-Sub-Sequences | \[\[\]\] | | +| Speed up | \* | | +| Parallel | , | | +| Slow down | \/ | | +| Alternate | \<\> | ")`} /> | +| Elongate | @ | | +| Replicate | ! | | + +## Sounds + +| Name | Description | Example | +| ----- | --------------------------------- | ---------------------------------------------------------------------------------- | +| sound | plays the sound of the given name | | +| bank | selects the sound bank | | +| n | select sample number | | + +## Notes + +| Name | Description | Example | +| --------- | ----------------------------------- | -------------------------------------------------------------------------------------------- | +| note | set pitch as number or letter | | +| n + scale | set note in scale | | +| stack | play patterns in parallel (read on) | | + +## Audio Effects + +| name | example | +| ----- | -------------------------------------------------------------------------------------------------- | +| lpf | ")`} /> | +| vowel | ")`} /> | +| gain | | +| delay | | +| room | | +| pan | | +| speed | ")`} /> | +| range | | + +## Pattern Effects + +| name | description | example | +| ---- | ----------------------------------- | ---------------------------------------------------------------------------------------------- | +| cpm | sets the tempo in cycles per minute | | +| fast | speed up | | +| slow | slow down | | +| rev | reverse | | +| jux | split left/right, modify right | | +| add | add numbers / notes | ")).scale("C:minor")`} /> | +| ply | speed up each event n times | ")`} /> | +| off | copy, shift time & modify | x.speed(2))`} /> | + +## Samples + +``` +casio control crow techno house jazz +metal east jvbass juno insect space wind +bd sd sn cp hh +piano +``` + +## Synths + +``` +gm_electric_guitar_muted gm_acoustic_bass +gm_voice_oohs gm_blown_bottle sawtooth square triangle +gm_xylophone gm_synth_bass_1 gm_synth_strings_1 +``` + +## Banks + +``` +RolandTR909 +``` + +## Scales + +``` +major minor dorian mixolydian +minor:pentatonic major:pentatonic +``` From 3db1c7a52122cc0eb180c764ea87447b5b88a3bc Mon Sep 17 00:00:00 2001 From: Roipoussiere Date: Mon, 5 Jun 2023 19:04:56 +0200 Subject: [PATCH 22/63] 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 6d86e384a..ecc1d41ac 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 23/63] 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 9a564609b..0e60ab264 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 24/63] 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 b1ceae114..c973f2eb0 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 000000000..f6b8f628d --- /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 ecc1d41ac..febc52aab 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 25/63] 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 bf26fff15..f4dba71bb 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 678d4ccc1..809e04ab2 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 000000000..663e82c0e --- /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 26/63] 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 809e04ab2..8551a8a29 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 663e82c0e..47a19c1e4 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 f6b8f628d..9fadc30d3 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 febc52aab..4bf6bb4ae 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 27/63] 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 9fadc30d3..b4448c2a1 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 28/63] 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 000000000..21646d8fc --- /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 29/63] 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 8551a8a29..5e2b37b46 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 47a19c1e4..2f3be348c 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 a88759ba9462d20e1d7ef8248858e512a09718ef Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Tue, 6 Jun 2023 22:43:51 +0200 Subject: [PATCH 30/63] translate notes chapter --- website/src/pages/de/workshop/first-notes.mdx | 104 +++++++++--------- 1 file changed, 53 insertions(+), 51 deletions(-) diff --git a/website/src/pages/de/workshop/first-notes.mdx b/website/src/pages/de/workshop/first-notes.mdx index c33fbb1a8..7cf16205e 100644 --- a/website/src/pages/de/workshop/first-notes.mdx +++ b/website/src/pages/de/workshop/first-notes.mdx @@ -151,7 +151,7 @@ Wir schauen uns später noch mehr Möglichkeiten an wie man patterns kombiniert. ## Längere Sequenzen -**Divide sequences with `/` to slow them down** +**Sequenzen verlangsamen mit `/`** {/* [c2 bb1 f2 eb2] */} @@ -159,27 +159,27 @@ Wir schauen uns später noch mehr Möglichkeiten an wie man patterns kombiniert. -The `/4` plays the sequence in brackets over 4 cycles (=4s). +Das `/4` spielt die Sequenz 4 mal so langsam, also insgesamt 4 cycles = 4s. -So each of the 4 notes is 1s long. +Jede Note ist nun also 1s lang. -Try adding more notes inside the brackets and notice how it gets faster. +Schreib noch mehr Töne in die Klammern und achte darauf dass es schneller wird. -Because it is so common to just play one thing per cycle, you can.. +Wenn eine Sequenz unabhängig von ihrem Inhalt immer gleich schnell bleiben soll, gibt es noch eine andere Art Klammern: -**Play one per cycle with \< \>** +**Eins pro Cycle per \< \>** ").sound("gm_acoustic_bass")`} punchcard /> -Try adding more notes inside the brackets and notice how it does **not** get faster. +Füg noch mehr Töne hinzu und achte darauf wie das tempo gleich bleibt! -**Play one sequence per cycle** +**Eine Sequenz pro Cycle** {/* <[c2 c3]*4 [bb1 bb2]*4 [f2 f3]*4 [eb2 eb3]*4>/2 */} @@ -191,7 +191,7 @@ Try adding more notes inside the brackets and notice how it does **not** get fas punchcard /> -**Alternate between multiple things** +**Alternativen** -This is also useful for unpitched sounds: +Das ist auch praktisch für atonale Sounds: -## Scales +## Skalen -Finding the right notes can be difficult.. Scales are here to help: +Es kann mühsam sein die richtigen Noten zu finden wenn man alle zur Verfügung hat. +Mit Skalen ist das einfacher: -Try out different numbers. Any number should sound good! +Probier verschiedene Zahlen aus. Jede klingt gut! -Try out different scales: +Probier verschiedene Skalen: - C:major - A2:minor @@ -238,9 +239,9 @@ Try out different scales: -**automate scales** +**Automatisierte Skalen** -Just like anything, we can automate the scale with a pattern: +Wie alle Funktionen können auch Skalen mit einem Pattern automatisiert werden: -If you have no idea what these scale mean, don't worry. -These are just labels for different sets of notes that go well together. +Wenn du keine Ahnung hast was die Skalen bedeuten, keine Sorge. +Es sind einfach nur Namen für verschiedene Gruppen von Tönen die gut zusammenpassen. -Take your time and you'll find scales you like! +Nimm dir Zeit um herauszufinden welche Skalen du magst. -## Repeat & Elongate +## Wiederholen und Verlängern -**Elongate with @** +**Verlängern mit @** -Not using `@` is like using `@1`. In the above example, c is 3 units long and eb is 1 unit long. +Ein Element ohne `@` ist gleichbedeutend mit `@1`. Im Beispiel ist `c` drei Einheiten lang, `eb` nur eine. -Try changing that number! +Spiel mit der Länge! -**Elongate within sub-sequences** +**Unter-Sequenzen verlängern** -This groove is called a `shuffle`. -Each beat has two notes, where the first is twice as long as the second. -This is also sometimes called triplet swing. You'll often find it in blues and jazz. +Dieser Groove wird auch `shuffle` genannt. +Jeder Schlag enthält 2 Töne, wobei der erste doppelt so lang wie der zweite ist. +Das nennt man auch manchmal `triolen swing`. Es ist ein typischer Rhythmus im Blues und Jazz. -**Replicate** +**Wiederholen** ]").sound("piano")`} punchcard /> -Try switching between `!`, `*` and `@` +Wechsel zwischen `!`, `*` und `@` hin und her. -What's the difference? +Was ist der Unterschied? -## Recap +## Rückblick -Let's recap what we've learned in this chapter: +Das haben wir in diesem Kapitel gelernt: -| Concept | Syntax | Example | -| --------- | ------ | ------------------------------------------------------------------- | -| Slow down | \/ | | -| Alternate | \<\> | ")`} /> | -| Elongate | @ | | -| Replicate | ! | | +| Concept | Syntax | Example | +| ------------ | ------ | ------------------------------------------------------------------- | +| Verlangsamen | \/ | | +| Alternativen | \<\> | ")`} /> | +| Verlängern | @ | | +| Wiederholen | ! | | -New functions: +Neue Funktionen: -| Name | Description | Example | -| ----- | ----------------------------------- | -------------------------------------------------------------------------------------------- | -| note | set pitch as number or letter | | -| scale | interpret `n` as scale degree | | -| stack | play patterns in parallel (read on) | | +| Name | Description | Example | +| ----- | --------------------------------------- | -------------------------------------------------------------------------------------------- | +| note | Tonhöhe als Buchstabe oder Zahl | | +| scale | Interpretiert `n` als Skalenstufe | | +| stack | Spiele mehrere Patterns parallel (s.u.) | | -## Examples +## Beispiele -**Classy Bassline** +**Bassline** -**Classy Melody** +**Melodie** -**Classy Drums** +**Drums** -**If there just was a way to play all the above at the same time.......** +**Wenn es doch nur einen Weg gäbe das alles gleichzeitig zu spielen.......** -It's called `stack` 😙 +Das geht mit `stack` 😙 @@ -385,4 +386,5 @@ It's called `stack` 😙 )`} /> -This is starting to sound like actual music! We have sounds, we have notes, now the last piece of the puzzle is missing: [effects](/workshop/first-effects) +Das hört sich doch langsam nach echter Musik an! +Wir haben Sounds, wir haben Töne.. noch ein Puzzleteil fehlt: [Effekte](/workshop/first-effects) From 185318a70c75e33ad30e908001af442a16ee74d0 Mon Sep 17 00:00:00 2001 From: Roipoussiere Date: Wed, 7 Jun 2023 11:02:38 +0200 Subject: [PATCH 31/63] 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 21646d8fc..9487ebe23 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 2f3be348c..7038644d5 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 32/63] 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 9487ebe23..cbd0f8a36 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 5e2b37b46..b74002aa7 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 7038644d5..27f46921d 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 b4448c2a1..6a92a8a6e 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 0e60ab264..f2d492e97 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 0c2147d9c55dcc9b0e09825e30ae60780d45e0bc Mon Sep 17 00:00:00 2001 From: Roipoussiere Date: Wed, 7 Jun 2023 15:34:57 +0200 Subject: [PATCH 33/63] repl: add option to display line numbers --- packages/react/src/components/CodeMirror6.jsx | 2 ++ website/src/repl/Footer.jsx | 32 ++++++++++++++----- website/src/repl/Repl.jsx | 3 +- website/src/settings.mjs | 2 ++ website/src/styles/index.css | 4 --- 5 files changed, 30 insertions(+), 13 deletions(-) diff --git a/packages/react/src/components/CodeMirror6.jsx b/packages/react/src/components/CodeMirror6.jsx index d76ac3d3c..ebe3dd112 100644 --- a/packages/react/src/components/CodeMirror6.jsx +++ b/packages/react/src/components/CodeMirror6.jsx @@ -104,6 +104,7 @@ export default function CodeMirror({ onSelectionChange, theme, keybindings, + isLineNumbersDisplayed, fontSize = 18, fontFamily = 'monospace', options, @@ -148,6 +149,7 @@ export default function CodeMirror({ onCreateEditor={handleOnCreateEditor} onUpdate={handleOnUpdate} extensions={extensions} + basicSetup={{ lineNumbers: isLineNumbersDisplayed }} /> ); diff --git a/website/src/repl/Footer.jsx b/website/src/repl/Footer.jsx index 4b3399ad1..c31e167a1 100644 --- a/website/src/repl/Footer.jsx +++ b/website/src/repl/Footer.jsx @@ -273,6 +273,15 @@ function SoundsTab() { ); } +function Checkbox({ label, value, onChange }) { + return ( + + ); +} + function ButtonGroup({ value, onChange, items }) { return (
@@ -355,7 +364,7 @@ const fontFamilyOptions = { }; function SettingsTab({ scheduler }) { - const { theme, keybindings, fontSize, fontFamily } = useSettings(); + const { theme, keybindings, isLineNumbersDisplayed, fontSize, fontFamily } = useSettings(); return (
{/* @@ -397,13 +406,20 @@ function SettingsTab({ scheduler }) { />
- - settingsMap.setKey('keybindings', keybindings)} - items={{ codemirror: 'Codemirror', vim: 'Vim', emacs: 'Emacs' }} - > - +
+ + settingsMap.setKey('keybindings', keybindings)} + items={{ codemirror: 'Codemirror', vim: 'Vim', emacs: 'Emacs' }} + > + + settingsMap.setKey('isLineNumbersDisplayed', cbEvent.target.checked)} + value={isLineNumbersDisplayed} + /> +