Merge pull request #580 from roipoussiere/music_metadata
Music metadata
This commit is contained in:
commit
a3baf07a5c
8 changed files with 498 additions and 62 deletions
|
|
@ -1,6 +1,8 @@
|
|||
---
|
||||
import * as tunes from '../../../src/repl/tunes.mjs';
|
||||
import HeadCommon from '../../components/HeadCommon.astro';
|
||||
|
||||
import { getMetadata } from '../metadata_parser';
|
||||
---
|
||||
|
||||
<head>
|
||||
|
|
@ -12,7 +14,7 @@ import HeadCommon from '../../components/HeadCommon.astro';
|
|||
Object.entries(tunes).map(([name, 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">
|
||||
<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>
|
||||
<img src={`./img/example-${name}.png`} />
|
||||
</a>
|
||||
|
|
|
|||
|
|
@ -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 `@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
|
||||
|
||||
Ok, so what about the content inside the quotes (e.g. `"a3 c#4 e4 a4"`)?
|
||||
|
|
|
|||
94
website/src/pages/learn/metadata.mdx
Normal file
94
website/src/pages/learn/metadata.mdx
Normal 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.
|
||||
*/
|
||||
```
|
||||
34
website/src/pages/metadata_parser.js
Normal file
34
website/src/pages/metadata_parser.js
Normal 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;
|
||||
}
|
||||
|
|
@ -1,9 +1,11 @@
|
|||
import { getMetadata } from '../metadata_parser';
|
||||
|
||||
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]),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue