Merge remote-tracking branch 'origin/main' into workshop-new

This commit is contained in:
Felix Roos 2023-06-09 18:00:16 +02:00
commit 576b88221d
9 changed files with 499 additions and 63 deletions

246
test/metadata.test.mjs Normal file
View File

@ -0,0 +1,246 @@
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 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({
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 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('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:
// const tune = `const str1 = '// @title Awesome song'`;
expect(getMetadata(tune)).toStrictEqual({});
});
});

View File

@ -76,6 +76,7 @@ export const SIDEBAR: Sidebar = {
{ text: 'Patterns', link: 'technical-manual/patterns' }, { text: 'Patterns', link: 'technical-manual/patterns' },
{ text: 'Pattern Alignment', link: 'technical-manual/alignment' }, { text: 'Pattern Alignment', link: 'technical-manual/alignment' },
{ text: 'Strudel vs Tidal', link: 'learn/strudel-vs-tidal' }, { text: 'Strudel vs Tidal', link: 'learn/strudel-vs-tidal' },
{ text: 'Music metadata', link: 'learn/metadata' },
{ text: 'CSound', link: 'learn/csound' }, { text: 'CSound', link: 'learn/csound' },
], ],
'Pattern Functions': [ 'Pattern Functions': [

View File

@ -1,6 +1,8 @@
--- ---
import * as tunes from '../../../src/repl/tunes.mjs'; import * as tunes from '../../../src/repl/tunes.mjs';
import HeadCommon from '../../components/HeadCommon.astro'; import HeadCommon from '../../components/HeadCommon.astro';
import { getMetadata } from '../metadata_parser';
--- ---
<head> <head>
@ -12,7 +14,7 @@ import HeadCommon from '../../components/HeadCommon.astro';
Object.entries(tunes).map(([name, tune]) => ( Object.entries(tunes).map(([name, tune]) => (
<a class="rounded-md bg-slate-900 hover:bg-slate-700 cursor-pointer relative" href={`./#${btoa(tune)}`}> <a class="rounded-md bg-slate-900 hover:bg-slate-700 cursor-pointer relative" href={`./#${btoa(tune)}`}>
<div class="absolute w-full h-full flex justify-center items-center"> <div class="absolute w-full h-full flex justify-center items-center">
<span class="bg-slate-800 p-2 rounded-md text-white">{name}</span> <span class="bg-slate-800 p-2 rounded-md text-white">{getMetadata(tune)['title'] || name}</span>
</div> </div>
<img src={`./img/example-${name}.png`} /> <img src={`./img/example-${name}.png`} />
</a> </a>

View File

@ -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. 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 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 `@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 # Strings
Ok, so what about the content inside the quotes (e.g. `"c a f e"`)? Ok, so what about the content inside the quotes (e.g. `"c a f e"`)?

View File

@ -0,0 +1,94 @@
---
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:
```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
```
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:
- `@title`: music title
- `@by`: music author(s), separated by comma, eventually followed with a link in `<>` (ex: `@by John Doe <https://example.com>`)
- `@license`: music license(s)
- `@details`: some additional information about the music
- `@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 optional 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.
*/
```

View File

@ -24,7 +24,7 @@ Instead of numbers, scientific interval notation can be used as well:
### scale(name) ### scale(name)
Turns numbers into notes in the scale (zero indexed). Also sets scale for other scale operations, like scaleTranpose. Turns numbers into notes in the scale (zero indexed). Also sets scale for other scale operations, like scaleTranspose.
<MiniRepl <MiniRepl
client:idle client:idle

View File

@ -0,0 +1,34 @@
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 = {};
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();
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;
}
}
}
return tags;
}

View File

@ -1,9 +1,11 @@
import { getMetadata } from '../metadata_parser';
export function getMyPatterns() { export function getMyPatterns() {
const my = import.meta.glob('../../../../my-patterns/**', { as: 'raw', eager: true }); const my = import.meta.glob('../../../../my-patterns/**', { as: 'raw', eager: true });
return Object.fromEntries( return Object.fromEntries(
Object.entries(my) // Object.entries(my)
.filter(([name]) => name.endsWith('.txt')) // .filter(([name]) => name.endsWith('.txt'))
.map(([name, raw]) => [name.split('/').slice(-1), raw]), // .map(([name, raw]) => [getMetadata(raw)['title'] || name.split('/').slice(-1), raw]),
); );
} }

View File

@ -121,8 +121,10 @@ stack(
.room(1) .room(1)
//.pianoroll({fold:1})`; //.pianoroll({fold:1})`;
export const caverave = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ export const caverave = `// "Caverave"
// by Felix Roos // @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) const keys = x => x.s('sawtooth').cutoff(1200).gain(.5)
.attack(0).decay(.16).sustain(.3).release(.1); .attack(0).decay(.16).sustain(.3).release(.1);
@ -184,8 +186,10 @@ export const barryHarris = `// adapted from a Barry Harris excercise
.color('#00B8D4') .color('#00B8D4')
`; `;
export const blippyRhodes = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ export const blippyRhodes = `// "Blippy Rhodes"
// by Felix Roos // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
// @by Felix Roos
samples({ samples({
bd: 'samples/tidal/bd/BT0A0D0.wav', bd: 'samples/tidal/bd/BT0A0D0.wav',
sn: 'samples/tidal/sn/ST0T0S3.wav', sn: 'samples/tidal/sn/ST0T0S3.wav',
@ -226,8 +230,10 @@ stack(
).fast(3/2) ).fast(3/2)
//.pianoroll({fold:1})`; //.pianoroll({fold:1})`;
export const wavyKalimba = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ export const wavyKalimba = `// "Wavy kalimba"
// by Felix Roos // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
// @by Felix Roos
samples({ samples({
'kalimba': { c5:'https://freesound.org/data/previews/536/536549_11935698-lq.mp3' } 'kalimba': { c5:'https://freesound.org/data/previews/536/536549_11935698-lq.mp3' }
}) })
@ -256,8 +262,10 @@ stack(
.delay(.2) .delay(.2)
`; `;
export const festivalOfFingers = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ export const festivalOfFingers = `// "Festival of fingers"
// by Felix Roos // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
// @by Felix Roos
const chords = "<Cm7 Fm7 G7 F#7>"; const chords = "<Cm7 Fm7 G7 F#7>";
stack( stack(
chords.voicings('lefthand').struct("x(3,8,-1)").velocity(.5).off(1/7,x=>x.transpose(12).velocity(.2)), 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()`; .note().piano()`;
// iter, echo, echoWith // iter, echo, echoWith
export const undergroundPlumber = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ export const undergroundPlumber = `// "Underground plumber"
// by Felix Roos, inspired by Friendship - Let's not talk about it (1979) :) // @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', 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/') }, 'https://loophole-letters.vercel.app/samples/tidal/')
@ -297,8 +307,10 @@ stack(
.fast(2/3) .fast(2/3)
.pianoroll({})`; .pianoroll({})`;
export const bridgeIsOver = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ export const bridgeIsOver = `// "Bridge is over"
// by Felix Roos, bassline by BDP - The 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'}) samples({mad:'https://freesound.org/data/previews/22/22274_109943-lq.mp3'})
stack( stack(
stack( stack(
@ -318,8 +330,10 @@ stack(
.pianoroll() .pianoroll()
`; `;
export const goodTimes = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ export const goodTimes = `// "Good times"
// by Felix Roos // @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); const scale = cat('C3 dorian','Bb2 major').slow(4);
stack( stack(
"2*4".add(12).scale(scale) "2*4".add(12).scale(scale)
@ -361,8 +375,10 @@ stack(
.pianoroll({maxMidi:100,minMidi:20})`; .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/ export const echoPiano = `// "Echo piano"
// by Felix Roos // @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>" "<0 2 [4 6](3,4,2) 3*2>"
.scale('D minor') .scale('D minor')
.color('salmon') .color('salmon')
@ -408,8 +424,10 @@ stack(
.legato(.5) .legato(.5)
).fast(2).note()`; ).fast(2).note()`;
export const randomBells = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ export const randomBells = `// "Random bells"
// by Felix Roos // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
// @by Felix Roos
samples({ samples({
bell: { c6: 'https://freesound.org/data/previews/411/411089_5121236-lq.mp3' }, bell: { c6: 'https://freesound.org/data/previews/411/411089_5121236-lq.mp3' },
bass: { d2: 'https://freesound.org/data/previews/608/608286_13074022-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'}) .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/ export const waa2 = `// "Waa2"
// by Felix Roos // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
// @by Felix Roos
n( n(
"a4 [a3 c3] a3 c3" "a4 [a3 c3] a3 c3"
.sub("<7 12 5 12>".slow(2)) .sub("<7 12 5 12>".slow(2))
@ -447,8 +467,10 @@ n(
.room(.5) .room(.5)
`; `;
export const hyperpop = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ export const hyperpop = `// "Hyperpop"
// by Felix Roos // @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 lfo = cosine.slow(15);
const lfo2 = sine.slow(16); const lfo2 = sine.slow(16);
const filter1 = x=>x.cutoff(lfo2.range(300,3000)); const filter1 = x=>x.cutoff(lfo2.range(300,3000));
@ -498,8 +520,10 @@ stack(
).slow(2) ).slow(2)
// strudel disable-highlighting`; // strudel disable-highlighting`;
export const festivalOfFingers3 = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ export const festivalOfFingers3 = `// "Festival of fingers 3"
// by Felix Roos // @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]" "[-7*3],0,2,6,[8 7]"
.echoWith(4,1/4, (x,n)=>x .echoWith(4,1/4, (x,n)=>x
.add(n*7) .add(n*7)
@ -516,8 +540,10 @@ export const festivalOfFingers3 = `// licensed with CC BY-NC-SA 4.0 https://crea
.note().piano() .note().piano()
//.pianoroll({maxMidi:160})`; //.pianoroll({maxMidi:160})`;
export const meltingsubmarine = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ export const meltingsubmarine = `// "Melting submarine"
// by Felix Roos // @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/') await samples('github:tidalcycles/Dirt-Samples/master/')
stack( stack(
s("bd:5,[~ <sd:1!3 sd:1(3,4,3)>],hh27(3,4,1)") // drums s("bd:5,[~ <sd:1!3 sd:1(3,4,3)>],hh27(3,4,1)") // drums
@ -554,8 +580,10 @@ stack(
) )
.slow(3/2)`; .slow(3/2)`;
export const outroMusic = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ export const outroMusic = `// "Outro music"
// by Felix Roos // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
// @by Felix Roos
samples({ samples({
bd: ['bd/BT0AADA.wav','bd/BT0AAD0.wav','bd/BT0A0DA.wav','bd/BT0A0D3.wav','bd/BT0A0D0.wav','bd/BT0A0A7.wav'], 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'], sd: ['sd/rytm-01-classic.wav','sd/rytm-00-hard.wav'],
@ -583,8 +611,10 @@ samples({
//.pianoroll({autorange:1,vertical:1,fold:0}) //.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/ export const bassFuge = `// "Bass fuge"
// by Felix Roos // @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'] }, 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/') 'github:cleary/samples-flbass/main/')
samples({ samples({
@ -609,8 +639,10 @@ x=>x.add(7).color('steelblue')
.stack(s("bd:1*2,~ sd:0,[~ hh:0]*2")) .stack(s("bd:1*2,~ sd:0,[~ hh:0]*2"))
.pianoroll({vertical:1})`; .pianoroll({vertical:1})`;
export const chop = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ export const chop = `// "Chop"
// by Felix Roos // @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' }) samples({ p: 'https://cdn.freesound.org/previews/648/648433_11943129-lq.mp3' })
s("p") s("p")
@ -622,8 +654,10 @@ s("p")
.sustain(.6) .sustain(.6)
`; `;
export const delay = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ export const delay = `// "Delay"
// by Felix Roos // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
// @by Felix Roos
stack( stack(
s("bd <sd cp>") s("bd <sd cp>")
.delay("<0 .5>") .delay("<0 .5>")
@ -631,8 +665,10 @@ stack(
.delayfeedback(".6 | .8") .delayfeedback(".6 | .8")
).sometimes(x=>x.speed("-1"))`; ).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/ export const orbit = `// "Orbit"
// by Felix Roos // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
// @by Felix Roos
stack( stack(
s("bd <sd cp>") s("bd <sd cp>")
.delay(.5) .delay(.5)
@ -645,8 +681,10 @@ stack(
.orbit(2) .orbit(2)
).sometimes(x=>x.speed("-1"))`; ).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/ export const belldub = `// "Belldub"
// by Felix Roos // @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'}}) 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 // "Hand Bells, B, Single.wav" by InspectorJ (www.jshaw.co.uk) of Freesound.org
stack( stack(
@ -678,8 +716,10 @@ stack(
.mask("<1 0>/8") .mask("<1 0>/8")
).slow(5)`; ).slow(5)`;
export const dinofunk = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ export const dinofunk = `// "Dinofunk"
// by Felix Roos // @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', samples({bass:'https://cdn.freesound.org/previews/614/614637_2434927-hq.mp3',
dino:{b4:'https://cdn.freesound.org/previews/316/316403_5123851-hq.mp3'}}) dino:{b4:'https://cdn.freesound.org/previews/316/316403_5123851-hq.mp3'}})
setVoicingRange('lefthand', ['c3','a4']) setVoicingRange('lefthand', ['c3','a4'])
@ -699,8 +739,10 @@ note("Abm7".voicings('lefthand').struct("x(3,8,1)".slow(2))),
note("<b4 eb4>").s('dino').delay(.8).slow(8).room(.5) note("<b4 eb4>").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/ export const sampleDemo = `// "Sample demo"
// by Felix Roos // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
// @by Felix Roos
stack( stack(
// percussion // percussion
s("[woodblock:1 woodblock:2*2] snare_rim:0,gong/8,brakedrum:1(3,8),~@3 cowbell:3") 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) .release(.1).room(.5)
)`; )`;
export const holyflute = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ export const holyflute = `// "Holy flute"
// by Felix Roos // @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" "c3 eb3(3,8) c4/2 g3*2"
.superimpose( .superimpose(
x=>x.slow(2).add(12), 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}) .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/ export const flatrave = `// "Flatrave"
// by Felix Roos // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
// @by Felix Roos
stack( stack(
s("bd*2,~ [cp,sd]").bank('RolandTR909'), s("bd*2,~ [cp,sd]").bank('RolandTR909'),
@ -751,8 +797,10 @@ stack(
.decay(.05).sustain(0).delay(.2).degradeBy(.5).mask("<0 1>/16") .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/ export const amensister = `// "Amensister"
// by Felix Roos // @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') await samples('github:tidalcycles/Dirt-Samples/master')
stack( stack(
@ -785,8 +833,10 @@ stack(
n("0 1").s("east").delay(.5).degradeBy(.8).speed(rand.range(.5,1.5)) n("0 1").s("east").delay(.5).degradeBy(.8).speed(rand.range(.5,1.5))
).reset("<x@7 x(5,8,-1)>")`; ).reset("<x@7 x(5,8,-1)>")`;
export const juxUndTollerei = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ export const juxUndTollerei = `// "Jux und tollerei"
// by Felix Roos // @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() note("c3 eb3 g3 bb3").palindrome()
.s('sawtooth') .s('sawtooth')
.jux(x=>x.rev().color('green').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) .delay(.5).delaytime(.1).delayfeedback(.4)
.pianoroll()`; .pianoroll()`;
export const csoundDemo = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ export const csoundDemo = `// "CSound demo"
// by Felix Roos // @license with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
// @by Felix Roos
await loadCsound\` await loadCsound\`
instr CoolSynth instr CoolSynth
iduration = p3 iduration = p3
@ -832,10 +884,10 @@ endin\`
//.pianoroll() //.pianoroll()
.csound('CoolSynth')`; .csound('CoolSynth')`;
export const loungeSponge = ` export const loungeSponge = `// "Lounge sponge"
// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
// by Felix Roos // @by Felix Roos, livecode.orc by Steven Yi
// livecode.orc by Steven Yi
await loadOrc('github:kunstmusik/csound-live-code/master/livecode.orc') await loadOrc('github:kunstmusik/csound-live-code/master/livecode.orc')
stack( stack(
@ -852,8 +904,10 @@ stack(
s("bd*2,[~ hh]*2,~ cp").bank('RolandTR909') 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/ export const arpoon = `// "Arpoon"
// "Arpoon" by Felix Roos // @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') await samples('github:tidalcycles/Dirt-Samples/master')
"<<Am7 C^7> C7 F^7 [Fm7 E7b9]>".voicings('lefthand') "<<Am7 C^7> C7 F^7 [Fm7 E7b9]>".voicings('lefthand')