mirror of
https://codeberg.org/uzu/strudel
synced 2026-09-23 05:26:37 -04:00
Compare commits
23 Commits
uzu
...
strudel_json
| Author | SHA1 | Date | |
|---|---|---|---|
| d1dd895d94 | |||
| f8124fca80 | |||
| 95fee04bea | |||
| f60894f2ce | |||
| b1aba294ad | |||
| 63ac80eb79 | |||
| 860bb58d61 | |||
| d58b3e490b | |||
| 0003ce174e | |||
| 8e0f23e081 | |||
| 71e60ed8de | |||
| e2a29914c7 | |||
| a9a3d491a2 | |||
| a750764f88 | |||
| 5b397009ab | |||
| e68df9e789 | |||
| 4d3bbd5057 | |||
| 81ca199a96 | |||
| 8d8b8ab99e | |||
| a4f10d5339 | |||
| 33a73c37bc | |||
| 4e6d39666a | |||
| a5b6022506 |
+1
-1
@@ -17,7 +17,7 @@ git remote set-url origin git@codeberg.org:uzu/strudel.git
|
||||
|
||||
To get in touch with the contributors, either
|
||||
|
||||
- [join the Tidal Discord Channel](https://discord.gg/remJ6gQA) and go to the #strudel channel
|
||||
- [join the Tidal Discord Channel](https://discord.com/invite/HGEdXmRkzT) and go to the #strudel channel
|
||||
- Find related discussions on the [tidal club forum](https://club.tidalcycles.org/)
|
||||
|
||||
## Ask a Question
|
||||
|
||||
@@ -32,7 +32,7 @@ This project is organized into many [packages](./packages), which are also avail
|
||||
|
||||
Read more about how to use these in your own project [here](https://strudel.cc/technical-manual/project-start).
|
||||
|
||||
You will need to abide by the terms of the [GNU Affero Public Licence v3](LICENSE.md). As such, Strudel code can only be shared within free/open source projects under the same license -- see the license for details.
|
||||
You will need to abide by the terms of the [GNU Affero Public Licence v3](LICENSE). As such, Strudel code can only be shared within free/open source projects under the same license -- see the license for details.
|
||||
|
||||
Licensing info for the default sound banks can be found over on the [dough-samples](https://github.com/felixroos/dough-samples/blob/main/README.md) repository.
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"private": true,
|
||||
"description": "Port of tidalcycles to javascript",
|
||||
"scripts": {
|
||||
"strudeljson": "node ./website/src/repl/create_strudel_json.mjs",
|
||||
"setup": "pnpm i",
|
||||
"pretest": "npm run jsdoc-json",
|
||||
"prebuild": "npm run jsdoc-json",
|
||||
|
||||
@@ -114,8 +114,9 @@ export function repl({
|
||||
* all(x => x.pianoroll())
|
||||
* ```
|
||||
*/
|
||||
let allTransforms = [];
|
||||
const all = function (transform) {
|
||||
allTransform = transform;
|
||||
allTransforms.push(transform);
|
||||
return silence;
|
||||
};
|
||||
/** Applies a function to each of the running patterns separately. This is intended for future use with upcoming 'stepwise' features. See `all` for a version that applies the function to all the patterns stacked together into a single pattern.
|
||||
@@ -191,6 +192,7 @@ export function repl({
|
||||
await injectPatternMethods();
|
||||
setTime(() => scheduler.now()); // TODO: refactor?
|
||||
await beforeEval?.({ code });
|
||||
allTransforms = []; // reset all transforms
|
||||
shouldHush && hush();
|
||||
|
||||
if (mondo) {
|
||||
@@ -207,8 +209,10 @@ export function repl({
|
||||
} else if (eachTransform) {
|
||||
pattern = eachTransform(pattern);
|
||||
}
|
||||
if (allTransform) {
|
||||
pattern = allTransform(pattern);
|
||||
if (allTransforms.length) {
|
||||
for (let i in allTransforms) {
|
||||
pattern = allTransforms[i](pattern);
|
||||
}
|
||||
}
|
||||
if (!isPattern(pattern)) {
|
||||
const message = `got "${typeof evaluated}" instead of pattern`;
|
||||
|
||||
@@ -37,6 +37,13 @@ describe('tonal', () => {
|
||||
.firstCycleValues.map((h) => h.note),
|
||||
).toEqual(['C3', 'D3', 'E3']);
|
||||
});
|
||||
it('scale without tonic', () => {
|
||||
expect(
|
||||
n(seq(0, 1, 2))
|
||||
.scale('major')
|
||||
.firstCycleValues.map((h) => h.note),
|
||||
).toEqual(['C3', 'D3', 'E3']);
|
||||
});
|
||||
it('scale with mininotation colon', () => {
|
||||
expect(
|
||||
n(seq(0, 1, 2))
|
||||
|
||||
@@ -14,7 +14,7 @@ function scaleStep(step, scale) {
|
||||
scale = scale.replaceAll(':', ' ');
|
||||
step = Math.ceil(step);
|
||||
let { intervals, tonic, empty } = Scale.get(scale);
|
||||
if ((empty && isNote(scale)) || (!empty && !tonic)) {
|
||||
if ((empty && isNote(scale)) || (empty && !tonic)) {
|
||||
throw new Error(`incomplete scale. Make sure to use ":" instead of spaces, example: .scale("C:major")`);
|
||||
} else if (empty) {
|
||||
throw new Error(`invalid scale "${scale}"`);
|
||||
|
||||
@@ -20,7 +20,7 @@ export function WelcomeTab({ context }) {
|
||||
</p>
|
||||
<p>
|
||||
{/* To learn more about what this all means, check out the{' '} */}
|
||||
To get started, check out the
|
||||
To get started, check out the{' '}
|
||||
<a href={`${baseNoTrailing}/workshop/getting-started/`} target="_blank">
|
||||
interactive tutorial
|
||||
</a>
|
||||
@@ -43,7 +43,7 @@ export function WelcomeTab({ context }) {
|
||||
</a>
|
||||
. You can find the source code at{' '}
|
||||
<a href="https://codeberg.org/uzu/strudel" target="_blank">
|
||||
github
|
||||
codeberg
|
||||
</a>
|
||||
. You can also find <a href="https://github.com/felixroos/dough-samples/blob/main/README.md">licensing info</a>{' '}
|
||||
for the default sound banks there. Please consider to{' '}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import { createInterface } from 'node:readline';
|
||||
import * as fs from 'node:fs';
|
||||
|
||||
const validFileTypes = new Set(['.wav', '.mp3', '.aac', '.m4a']);
|
||||
/** For automatically creating the strudel JSON that for samples hosted in a remote repo */
|
||||
async function writeStrudelJson(subpath, hosturl) {
|
||||
const children = await fs.promises.readdir(subpath, { recursive: true });
|
||||
const name = subpath.split('/').slice(-1)[0];
|
||||
const tree = { name, children };
|
||||
let samples = {
|
||||
_base: hosturl,
|
||||
};
|
||||
|
||||
const files = tree.children
|
||||
.filter((path) => {
|
||||
const extension = path.match(/\.[0-9a-z]+$/i)?.[0];
|
||||
return validFileTypes.has(extension);
|
||||
})
|
||||
.sort((a, b) => a.localeCompare(b));
|
||||
|
||||
files.forEach((path) => {
|
||||
const groupName = path.match(/([^/]+)\/[^/]+$/)[1];
|
||||
samples[groupName] = samples[groupName] ?? [];
|
||||
samples[groupName].push(path);
|
||||
});
|
||||
const json = JSON.stringify(samples, null, 2);
|
||||
const filepath = subpath + '/strudel.json';
|
||||
await fs.promises.writeFile(filepath, json);
|
||||
console.log(`wrote strudel.json with ${files.length} samples to ${subpath}`);
|
||||
}
|
||||
|
||||
const rl = createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout,
|
||||
});
|
||||
|
||||
rl.question(`enter sample folder path: `, async (path) => {
|
||||
rl.question(`enter hosted URL: `, async (hosturl) => {
|
||||
const githubregex = /github\.com\/([^/]+)\/([^/]+)/;
|
||||
const githubparams = hosturl.match(githubregex);
|
||||
if (githubparams) {
|
||||
const username = githubparams[1];
|
||||
const repo = githubparams[2];
|
||||
|
||||
hosturl = `https://raw.githubusercontent.com/${username}/${repo}/main/`;
|
||||
console.log('reformatting github URL to: ', hosturl);
|
||||
}
|
||||
|
||||
await writeStrudelJson(path, hosturl);
|
||||
rl.close();
|
||||
});
|
||||
});
|
||||
@@ -33,7 +33,7 @@ async function loadStrudelJson(subpath) {
|
||||
});
|
||||
}
|
||||
|
||||
async function writeStrudelJson(subpath) {
|
||||
export async function writeStrudelJson(subpath) {
|
||||
const children = await readDir(subpath, { dir, recursive: true });
|
||||
const name = subpath.split('/').slice(-1)[0];
|
||||
const tree = { name, children };
|
||||
@@ -41,7 +41,7 @@ async function writeStrudelJson(subpath) {
|
||||
let samples = {};
|
||||
let count = 0;
|
||||
walkFileTree(tree, (entry, parent) => {
|
||||
if (['wav', 'mp3'].includes(entry.name.split('.').slice(-1)[0])) {
|
||||
if (['wav', 'mp3', 'aac', 'm4a'].includes(entry.name.split('.').slice(-1)[0])) {
|
||||
samples[parent.name] = samples[parent.name] || [];
|
||||
count += 1;
|
||||
samples[parent.name].push(entry.subpath.slice(1).concat([entry.name]).join('/'));
|
||||
|
||||
Reference in New Issue
Block a user