mirror of
https://codeberg.org/uzu/strudel
synced 2026-07-13 22:35:15 -04:00
Compare commits
37 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 42d7781d44 | |||
| 0416e4d212 | |||
| b9247db001 | |||
| 325152c782 | |||
| c136d12194 | |||
| 7557952155 | |||
| b0b41e6718 | |||
| 22e3067b8c | |||
| e9ab01ab06 | |||
| 058e554b0f | |||
| 6d9327aebc | |||
| c23d7850c7 | |||
| 2854f0cf34 | |||
| a89e87ca2e | |||
| 5419ca431b | |||
| e8b674badd | |||
| 8af40a9c61 | |||
| f54aa3d823 | |||
| 5e91287e3e | |||
| 4e3d4a7060 | |||
| 3d80da9816 | |||
| bf63b716af | |||
| 38d389b838 | |||
| ebdca32c80 | |||
| c06a3710c1 | |||
| a468ddb85b | |||
| 6da98a5f31 | |||
| a746bf5461 | |||
| 700f0be0ba | |||
| cb59d632a8 | |||
| 9fd86458e6 | |||
| 3cd49065c8 | |||
| a7726e2334 | |||
| 26793aec91 | |||
| 7c4ed7e05f | |||
| 93cfe9802c | |||
| 0106a129c9 |
@@ -10,4 +10,5 @@ paper
|
||||
pnpm-lock.yaml
|
||||
pnpm-workspace.yaml
|
||||
**/dev-dist
|
||||
superdough-wasm
|
||||
website/.astro
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<script type="module">
|
||||
import { initStrudel } from 'https://cdn.skypack.dev/@strudel/web@0.8.2';
|
||||
initStrudel({
|
||||
prebake: () => samples('github:tidalcycles/Dirt-Samples/master'),
|
||||
prebake: () => samples('github:tidalcycles/dirt-samples'),
|
||||
});
|
||||
const click = (id, action) => document.getElementById(id).addEventListener('click', action);
|
||||
click('a', () => evaluate(`s('bd,jvbass(3,8)').jux(rev)`));
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
});
|
||||
|
||||
function getTune() {
|
||||
return `await samples('github:tidalcycles/Dirt-Samples/master')
|
||||
return `samples('github:tidalcycles/dirt-samples')
|
||||
|
||||
stack(
|
||||
// amen
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<script src="https://unpkg.com/@strudel/repl@0.9.4"></script>
|
||||
<script src="https://unpkg.com/@strudel/repl@1.0.2"></script>
|
||||
<strudel-editor>
|
||||
<!--
|
||||
// @date 23-08-15
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<script src="https://unpkg.com/@strudel/web@1.0.3"></script>
|
||||
<button id="play">PLAY</button>
|
||||
<script>
|
||||
initStrudel({
|
||||
prebake: () => samples('github:tidalcycles/dirt-samples'),
|
||||
});
|
||||
document.getElementById('play').addEventListener('click', () => s('bd sd').play());
|
||||
</script>
|
||||
@@ -1,6 +1,6 @@
|
||||
export const bumpStreet = `// froos - "22 bump street", licensed with CC BY-NC-SA 4.0
|
||||
await samples('github:felixroos/samples/main')
|
||||
await samples('https://strudel.cc/tidal-drum-machines.json', 'github:ritchse/tidal-drum-machines/main/machines/')
|
||||
samples('github:felixroos/samples')
|
||||
samples('https://strudel.cc/tidal-drum-machines.json', 'github:ritchse/tidal-drum-machines/main/machines/')
|
||||
|
||||
"<[0,<6 7 9>,13,<17 20 22 26>]!2>/2"
|
||||
// make it 22 edo
|
||||
@@ -33,8 +33,8 @@ await samples('https://strudel.cc/tidal-drum-machines.json', 'github:ritchse/tid
|
||||
|
||||
export const trafficFlam = `// froos - "traffic flam", licensed with CC BY-NC-SA 4.0
|
||||
|
||||
await samples('github:felixroos/samples/main')
|
||||
await samples('https://strudel.cc/tidal-drum-machines.json', 'github:ritchse/tidal-drum-machines/main/machines/')
|
||||
samples('github:felixroos/samples')
|
||||
samples('https://strudel.cc/tidal-drum-machines.json', 'github:ritchse/tidal-drum-machines/main/machines/')
|
||||
|
||||
addVoicings('hip', {
|
||||
m11: ['2M 3m 4P 7m'],
|
||||
@@ -69,8 +69,8 @@ export const funk42 = `// froos - how to funk in 42 lines of code
|
||||
// adapted from "how to funk in two minutes" by marc rebillet https://www.youtube.com/watch?v=3vBwRfQbXkg
|
||||
// thanks to peach for the transcription: https://www.youtube.com/watch?v=8eiPXvIgda4
|
||||
|
||||
await samples('github:felixroos/samples/main')
|
||||
await samples('https://strudel.cc/tidal-drum-machines.json', 'github:ritchse/tidal-drum-machines/main/machines/')
|
||||
samples('github:felixroos/samples')
|
||||
samples('https://strudel.cc/tidal-drum-machines.json', 'github:ritchse/tidal-drum-machines/main/machines/')
|
||||
|
||||
setcps(.5)
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<script type="module">
|
||||
import { initStrudel } from '@strudel/web';
|
||||
initStrudel({
|
||||
prebake: () => samples('github:tidalcycles/Dirt-Samples/master'),
|
||||
prebake: () => samples('github:tidalcycles/dirt-samples'),
|
||||
});
|
||||
|
||||
const click = (id, action) => document.getElementById(id).addEventListener('click', action);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export default `await samples('github:tidalcycles/Dirt-Samples/master')
|
||||
export default `samples('github:tidalcycles/dirt-samples')
|
||||
|
||||
stack(
|
||||
// amen
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
const init = Promise.all([
|
||||
initAudioOnFirstClick(),
|
||||
samples('github:tidalcycles/Dirt-Samples/master'),
|
||||
samples('github:tidalcycles/dirt-samples'),
|
||||
registerSynthSounds(),
|
||||
]);
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { closeBrackets } from '@codemirror/autocomplete';
|
||||
// import { search, highlightSelectionMatches } from '@codemirror/search';
|
||||
import { history } from '@codemirror/commands';
|
||||
import { javascript } from '@codemirror/lang-javascript';
|
||||
import { defaultHighlightStyle, syntaxHighlighting } from '@codemirror/language';
|
||||
import { defaultHighlightStyle, syntaxHighlighting, bracketMatching } from '@codemirror/language';
|
||||
import { Compartment, EditorState, Prec } from '@codemirror/state';
|
||||
import {
|
||||
EditorView,
|
||||
@@ -24,6 +24,7 @@ import { persistentAtom } from '@nanostores/persistent';
|
||||
|
||||
const extensions = {
|
||||
isLineWrappingEnabled: (on) => (on ? EditorView.lineWrapping : []),
|
||||
isBracketMatchingEnabled: (on) => (on ? bracketMatching({ brackets: '()[]{}<>' }) : []),
|
||||
isLineNumbersDisplayed: (on) => (on ? lineNumbers() : []),
|
||||
theme,
|
||||
isAutoCompletionEnabled,
|
||||
@@ -37,6 +38,7 @@ const compartments = Object.fromEntries(Object.keys(extensions).map((key) => [ke
|
||||
|
||||
export const defaultSettings = {
|
||||
keybindings: 'codemirror',
|
||||
isBracketMatchingEnabled: false,
|
||||
isLineNumbersDisplayed: true,
|
||||
isActiveLineHighlighted: false,
|
||||
isAutoCompletionEnabled: false,
|
||||
@@ -290,6 +292,9 @@ export class StrudelMirror {
|
||||
setLineWrappingEnabled(enabled) {
|
||||
this.reconfigureExtension('isLineWrappingEnabled', enabled);
|
||||
}
|
||||
setBracketMatchingEnabled(enabled) {
|
||||
this.reconfigureExtension('isBracketMatchingEnabled', enabled);
|
||||
}
|
||||
setLineNumbersDisplayed(enabled) {
|
||||
this.reconfigureExtension('isLineNumbersDisplayed', enabled);
|
||||
}
|
||||
|
||||
@@ -292,7 +292,7 @@ const generic_params = [
|
||||
* @name begin
|
||||
* @param {number | Pattern} amount between 0 and 1, where 1 is the length of the sample
|
||||
* @example
|
||||
* samples({ rave: 'rave/AREUREADY.wav' }, 'github:tidalcycles/Dirt-Samples/master/')
|
||||
* samples({ rave: 'rave/AREUREADY.wav' }, 'github:tidalcycles/dirt-samples')
|
||||
* s("rave").begin("<0 .25 .5 .75>").fast(2)
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -2322,10 +2322,10 @@ const _loopAt = function (factor, pat, cps = 0.5) {
|
||||
* @memberof Pattern
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* await samples('github:tidalcycles/Dirt-Samples/master')
|
||||
* samples('github:tidalcycles/dirt-samples')
|
||||
* s("breaks165").slice(8, "0 1 <2 2*2> 3 [4 0] 5 6 7".every(3, rev)).slow(0.75)
|
||||
* @example
|
||||
* await samples('github:tidalcycles/Dirt-Samples/master')
|
||||
* samples('github:tidalcycles/dirt-samples')
|
||||
* s("breaks125").fit().slice([0,.25,.5,.75], "0 1 1 <2 3>")
|
||||
*/
|
||||
|
||||
@@ -2351,7 +2351,7 @@ export const slice = register(
|
||||
* Works the same as slice, but changes the playback speed of each slice to match the duration of its step.
|
||||
* @name splice
|
||||
* @example
|
||||
* await samples('github:tidalcycles/Dirt-Samples/master')
|
||||
* samples('github:tidalcycles/dirt-samples')
|
||||
* s("breaks165")
|
||||
* .splice(8, "0 1 [2 3 0]@2 3 0@2 7")
|
||||
*/
|
||||
|
||||
@@ -6,11 +6,11 @@ const OFF_MESSAGE = 0x80;
|
||||
const CC_MESSAGE = 0xb0;
|
||||
|
||||
Pattern.prototype.midi = function (output) {
|
||||
return this.onTrigger((time, hap, currentTime) => {
|
||||
return this.onTrigger((time, hap, currentTime, cps) => {
|
||||
const { note, nrpnn, nrpv, ccn, ccv } = hap.value;
|
||||
const offset = (time - currentTime) * 1000;
|
||||
const velocity = Math.floor((hap.context?.velocity ?? 0.9) * 100); // TODO: refactor velocity
|
||||
const duration = Math.floor(hap.duration.valueOf() * 1000 - 10);
|
||||
const duration = Math.floor((hap.duration.valueOf() / cps) * 1000 - 10);
|
||||
const roundedOffset = Math.round(offset);
|
||||
const midichan = (hap.value.midichan ?? 1) - 1;
|
||||
const requestedport = output ?? 'IAC';
|
||||
|
||||
@@ -129,7 +129,7 @@ Pattern.prototype.midi = function (output) {
|
||||
const velocity = hap.context?.velocity ?? 0.9; // TODO: refactor velocity
|
||||
|
||||
// note off messages will often a few ms arrive late, try to prevent glitching by subtracting from the duration length
|
||||
const duration = Math.floor(hap.duration.valueOf() * 1000 - 10);
|
||||
const duration = Math.floor((hap.duration.valueOf() / cps) * 1000 - 10);
|
||||
if (note != null) {
|
||||
const midiNumber = typeof note === 'number' ? note : noteToMidi(note);
|
||||
const midiNote = new Note(midiNumber, { attack: velocity, duration });
|
||||
@@ -167,10 +167,16 @@ let listeners = {};
|
||||
const refs = {};
|
||||
|
||||
export async function midin(input) {
|
||||
if (isPattern(input)) {
|
||||
throw new Error(
|
||||
`.midi does not accept Pattern input. Make sure to pass device name with single quotes. Example: .midi('${
|
||||
WebMidi.outputs?.[0]?.name || 'IAC Driver Bus 1'
|
||||
}')`,
|
||||
);
|
||||
}
|
||||
const initial = await enableWebMidi(); // only returns on first init
|
||||
const device = getDevice(input, WebMidi.inputs);
|
||||
|
||||
if (initial) {
|
||||
if (initial || WebMidi.enabled) {
|
||||
const otherInputs = WebMidi.inputs.filter((o) => o.name !== device.name);
|
||||
logger(
|
||||
`Midi enabled! Using "${device.name}". ${
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
{
|
||||
"name": "@strudel/repl",
|
||||
"version": "1.0.1",
|
||||
"version": "1.0.2",
|
||||
"description": "Strudel REPL as a Web Component",
|
||||
"main": "index.mjs",
|
||||
"module": "index.mjs",
|
||||
"publishConfig": {
|
||||
"main": "dist/index.mjs"
|
||||
"main": "dist/index.js",
|
||||
"module": "dist/index.mjs"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "vite build",
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
rustsaw/target
|
||||
@@ -0,0 +1,57 @@
|
||||
# superdough-wasm
|
||||
|
||||
This is just a very early experiment to find out how to run wasm in an AudioWorklet.
|
||||
WASM can be compiled from several languages, which are tested here..
|
||||
|
||||
## zig
|
||||
|
||||
<https://dev.to/sleibrock/webassembly-with-zig-part-1-4onm>
|
||||
|
||||
```sh
|
||||
# (re)compile dsp.zig
|
||||
brew install zig # prequisite
|
||||
cd zigsaw
|
||||
zig build-lib zigsaw.zig -target wasm32-freestanding -dynamic -rdynamic -O ReleaseSmall # build
|
||||
npx http-server .. -o # run
|
||||
```
|
||||
|
||||
wasm file size: 690B
|
||||
|
||||
## rust
|
||||
|
||||
<https://developer.mozilla.org/en-US/docs/WebAssembly/Rust_to_Wasm>
|
||||
|
||||
```sh
|
||||
# https://www.rust-lang.org/tools/install
|
||||
cargo install wasm-pack # prequisite
|
||||
cd rustsaw
|
||||
wasm-pack build --target bundler # build
|
||||
npx http-server .. -o # run
|
||||
```
|
||||
|
||||
wasm file size: 653B
|
||||
|
||||
## c
|
||||
|
||||
<https://emscripten.org/docs/getting_started/Tutorial.html>
|
||||
|
||||
```sh
|
||||
# brew install emscripten
|
||||
cd csaw
|
||||
emcc -O2 csaw.c -o csaw # build
|
||||
npx http-server .. -o
|
||||
```
|
||||
|
||||
wasm file size: 680B
|
||||
|
||||
## assemblyscript
|
||||
|
||||
<https://www.assemblyscript.org/getting-started.html#setting-up-a-new-project>
|
||||
|
||||
```sh
|
||||
cd ascsaw
|
||||
# npm i
|
||||
npm run asbuild # build
|
||||
```
|
||||
|
||||
wasm file size: 122B !
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"targets": {
|
||||
"debug": {
|
||||
"outFile": "build/debug.wasm",
|
||||
"textFile": "build/debug.wat",
|
||||
"sourceMap": true,
|
||||
"debug": true
|
||||
},
|
||||
"release": {
|
||||
"outFile": "build/release.wasm",
|
||||
"textFile": "build/release.wat",
|
||||
"sourceMap": true,
|
||||
"optimizeLevel": 3,
|
||||
"shrinkLevel": 2,
|
||||
"converge": false,
|
||||
"noAssert": false
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"bindings": "esm"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export function saw(t: f64, f: f64): f64 {
|
||||
return (((f * t * 1.0) % 1.0) - 0.5) * 2.0;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"extends": "assemblyscript/std/assembly.json",
|
||||
"include": [
|
||||
"./**/*.ts"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"name": "ascsaw",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "ascsaw",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"assemblyscript": "^0.27.22"
|
||||
}
|
||||
},
|
||||
"node_modules/assemblyscript": {
|
||||
"version": "0.27.22",
|
||||
"resolved": "https://registry.npmjs.org/assemblyscript/-/assemblyscript-0.27.22.tgz",
|
||||
"integrity": "sha512-6ClobsR4Hxn6K0daYp/+n9qWTqVbpdVeSGSVDqRvUEz66vvFb8atS6nLm+fnQ54JXuXmzLQy0uWYYgB8G59btQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"binaryen": "116.0.0-nightly.20231102",
|
||||
"long": "^5.2.1"
|
||||
},
|
||||
"bin": {
|
||||
"asc": "bin/asc.js",
|
||||
"asinit": "bin/asinit.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16",
|
||||
"npm": ">=7"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/assemblyscript"
|
||||
}
|
||||
},
|
||||
"node_modules/binaryen": {
|
||||
"version": "116.0.0-nightly.20231102",
|
||||
"resolved": "https://registry.npmjs.org/binaryen/-/binaryen-116.0.0-nightly.20231102.tgz",
|
||||
"integrity": "sha512-aPU9tlKdw/gcXx6u4PxtDgOtGjg/ZKnYdk23ctYb70GxZgPhWnGWmnBt01aV5dt5yFFo2V4rbB7SzpSFhViFQA==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"wasm-opt": "bin/wasm-opt",
|
||||
"wasm2js": "bin/wasm2js"
|
||||
}
|
||||
},
|
||||
"node_modules/long": {
|
||||
"version": "5.2.3",
|
||||
"resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz",
|
||||
"integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "ascsaw",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "node tests",
|
||||
"asbuild:debug": "asc assembly/index.ts --target debug",
|
||||
"asbuild:release": "asc assembly/index.ts --target release",
|
||||
"asbuild": "npm run asbuild:debug && npm run asbuild:release",
|
||||
"start": "npx serve ."
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"assemblyscript": "^0.27.22"
|
||||
},
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./build/release.js",
|
||||
"types": "./build/release.d.ts"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
csaw
|
||||
@@ -0,0 +1,7 @@
|
||||
#include <math.h>
|
||||
#include <emscripten.h>
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
double saw(double t, double f) {
|
||||
return fmod((f * t * 1.0), 1.0) - 0.5 * 2.0;
|
||||
}
|
||||
Executable
BIN
Binary file not shown.
@@ -0,0 +1,11 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>WASM AudioWorklet Demo</title>
|
||||
</head>
|
||||
<body>
|
||||
<button id="play">play</button>
|
||||
<input type="range" min="55" max="880" id="freq" />
|
||||
</body>
|
||||
<script src="./main.js"></script>
|
||||
</html>
|
||||
@@ -0,0 +1,24 @@
|
||||
let ac;
|
||||
document.getElementById('play').addEventListener('click', async () => {
|
||||
ac = ac || new AudioContext();
|
||||
await ac.resume();
|
||||
await ac.audioWorklet.addModule('./worklet.js');
|
||||
const node = new AudioWorkletNode(ac, 'saw-processor');
|
||||
|
||||
//let res = await fetch('./zigsaw/zigsaw.wasm');
|
||||
// let res = await fetch('./csaw/csaw.wasm');
|
||||
let res = await fetch('./ascsaw/build/release.wasm');
|
||||
//let res = await fetch('./rustsaw/pkg/rustsaw_bg.wasm');
|
||||
const buffer = await res.arrayBuffer();
|
||||
node.port.onmessage = (e) => {
|
||||
if (e.data === 'OK') {
|
||||
console.log('worklet ready');
|
||||
}
|
||||
};
|
||||
node.port.postMessage({ webassembly: buffer });
|
||||
node.connect(ac.destination);
|
||||
|
||||
document.getElementById('freq').addEventListener('input', async (e) => {
|
||||
node.port.postMessage({ frequency: e.target.value });
|
||||
});
|
||||
});
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.76"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustsaw"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f"
|
||||
@@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "rustsaw"
|
||||
version = "0.1.0"
|
||||
authors = ["Your Name <you@example.com>"]
|
||||
description = "A sample project with wasm-pack"
|
||||
license = "MIT/Apache-2.0"
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
wasm-bindgen = "0.2"
|
||||
@@ -0,0 +1,6 @@
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn saw(t: f64, f: f64) -> f64 {
|
||||
return (((f * t * 1.0) % 1.0) - 0.5) * 2.0;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
class SawProcessor extends AudioWorkletProcessor {
|
||||
constructor() {
|
||||
super();
|
||||
this.t = 0; // samples passed
|
||||
this.f = 110;
|
||||
this.port.onmessage = (e) => {
|
||||
const key = Object.keys(e.data)[0];
|
||||
const value = e.data[key];
|
||||
switch (key) {
|
||||
case 'webassembly':
|
||||
WebAssembly.instantiate(value, this.importObject).then((result) => {
|
||||
this.api = result.instance.exports;
|
||||
this.port.postMessage('OK');
|
||||
});
|
||||
break;
|
||||
case 'frequency':
|
||||
this.f = value;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
process(inputs, outputs, parameters) {
|
||||
if (this.api) {
|
||||
const output = outputs[0];
|
||||
for (let i = 0; i < output[0].length; i++) {
|
||||
let t = this.t;
|
||||
let out = 0;
|
||||
out = this.api.saw(t / 44100, this.f);
|
||||
output.forEach((channel) => {
|
||||
channel[i] = out;
|
||||
});
|
||||
this.t++;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
registerProcessor('saw-processor', SawProcessor);
|
||||
Executable
BIN
Binary file not shown.
@@ -0,0 +1,5 @@
|
||||
const std = @import("std");
|
||||
|
||||
export fn saw(t: f64, f: f64) f64 {
|
||||
return ((@mod(f * t, 1.0)) - 0.5) * 2.0;
|
||||
}
|
||||
@@ -19,7 +19,7 @@ import { superdough, samples, initAudioOnFirstClick, registerSynthSounds } from
|
||||
|
||||
const init = Promise.all([
|
||||
initAudioOnFirstClick(),
|
||||
samples('github:tidalcycles/Dirt-Samples/master'),
|
||||
samples('github:tidalcycles/dirt-samples'),
|
||||
registerSynthSounds(),
|
||||
]);
|
||||
|
||||
@@ -148,7 +148,7 @@ The json file is expected to have the same format as described above.
|
||||
Because it is common to use github for samples, there is a short way to load a sample map from github:
|
||||
|
||||
```js
|
||||
samples('github:tidalcycles/Dirt-Samples/master')
|
||||
samples('github:tidalcycles/dirt-samples')
|
||||
```
|
||||
|
||||
The format is `github:<user>/<repo>/<branch>`.
|
||||
|
||||
@@ -99,6 +99,27 @@ export const getLoadedBuffer = (url) => {
|
||||
return bufferCache[url];
|
||||
};
|
||||
|
||||
function resolveSpecialPaths(base) {
|
||||
if (base.startsWith('bubo:')) {
|
||||
const [_, repo] = base.split(':');
|
||||
base = `github:Bubobubobubobubo/dough-${repo}`;
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
function githubPath(base, subpath = '') {
|
||||
if (!base.startsWith('github:')) {
|
||||
throw new Error('expected "github:" at the start of pseudoUrl');
|
||||
}
|
||||
let [_, path] = base.split('github:');
|
||||
path = path.endsWith('/') ? path.slice(0, -1) : path;
|
||||
if (path.split('/').length === 2) {
|
||||
// assume main as default branch if none set
|
||||
path += '/main';
|
||||
}
|
||||
return `https://raw.githubusercontent.com/${path}/${subpath}`;
|
||||
}
|
||||
|
||||
export const processSampleMap = (sampleMap, fn, baseUrl = sampleMap._base || '') => {
|
||||
return Object.entries(sampleMap).forEach(([key, value]) => {
|
||||
if (typeof value === 'string') {
|
||||
@@ -108,15 +129,19 @@ export const processSampleMap = (sampleMap, fn, baseUrl = sampleMap._base || '')
|
||||
throw new Error('wrong sample map format for ' + key);
|
||||
}
|
||||
baseUrl = value._base || baseUrl;
|
||||
const replaceUrl = (v) => (baseUrl + v).replace('github:', 'https://raw.githubusercontent.com/');
|
||||
baseUrl = resolveSpecialPaths(baseUrl);
|
||||
if (baseUrl.startsWith('github:')) {
|
||||
baseUrl = githubPath(baseUrl, '');
|
||||
}
|
||||
const fullUrl = (v) => baseUrl + v;
|
||||
if (Array.isArray(value)) {
|
||||
//return [key, value.map(replaceUrl)];
|
||||
value = value.map(replaceUrl);
|
||||
value = value.map(fullUrl);
|
||||
} else {
|
||||
// must be object
|
||||
value = Object.fromEntries(
|
||||
Object.entries(value).map(([note, samples]) => {
|
||||
return [note, (typeof samples === 'string' ? [samples] : samples).map(replaceUrl)];
|
||||
return [note, (typeof samples === 'string' ? [samples] : samples).map(fullUrl)];
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -142,7 +167,7 @@ function getSamplesPrefixHandler(url) {
|
||||
/**
|
||||
* Loads a collection of samples to use with `s`
|
||||
* @example
|
||||
* samples('github:tidalcycles/Dirt-Samples/master');
|
||||
* samples('github:tidalcycles/dirt-samples');
|
||||
* s("[bd ~]*2, [~ hh]*2, ~ sd")
|
||||
* @example
|
||||
* samples({
|
||||
@@ -165,18 +190,9 @@ export const samples = async (sampleMap, baseUrl = sampleMap._base || '', option
|
||||
if (handler) {
|
||||
return handler(sampleMap);
|
||||
}
|
||||
if (sampleMap.startsWith('bubo:')) {
|
||||
const [_, repo] = sampleMap.split(':');
|
||||
sampleMap = `github:Bubobubobubobubo/dough-${repo}`;
|
||||
}
|
||||
sampleMap = resolveSpecialPaths(sampleMap);
|
||||
if (sampleMap.startsWith('github:')) {
|
||||
let [_, path] = sampleMap.split('github:');
|
||||
path = path.endsWith('/') ? path.slice(0, -1) : path;
|
||||
if (path.split('/').length === 2) {
|
||||
// assume main as default branch if none set
|
||||
path += '/main';
|
||||
}
|
||||
sampleMap = `https://raw.githubusercontent.com/${path}/strudel.json`;
|
||||
sampleMap = githubPath(sampleMap, 'strudel.json');
|
||||
}
|
||||
if (sampleMap.startsWith('shabda:')) {
|
||||
let [_, path] = sampleMap.split('shabda:');
|
||||
|
||||
@@ -253,6 +253,11 @@ function effectSend(input, effect, wet) {
|
||||
return send;
|
||||
}
|
||||
|
||||
export function resetGlobalEffects() {
|
||||
delays = {};
|
||||
reverbs = {};
|
||||
}
|
||||
|
||||
export const superdough = async (value, deadline, hapDuration) => {
|
||||
const ac = getAudioContext();
|
||||
if (typeof value !== 'object') {
|
||||
|
||||
@@ -23,6 +23,9 @@ describe('transpiler', () => {
|
||||
it('supports top level await', () => {
|
||||
expect(transpiler("await samples('xxx');", simple).output).toEqual("await samples('xxx');");
|
||||
});
|
||||
it('adds await to bare samples call', () => {
|
||||
expect(transpiler("samples('xxx');", simple).output).toEqual("await samples('xxx');");
|
||||
});
|
||||
/* it('parses dynamic imports', () => {
|
||||
expect(
|
||||
transpiler("const { default: foo } = await import('https://bar.com/foo.js');", {
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import escodegen from 'escodegen';
|
||||
import { parse } from 'acorn';
|
||||
import { walk } from 'estree-walker';
|
||||
import { isNoteWithOctave } from '@strudel/core';
|
||||
import { getLeafLocations } from '@strudel/mini';
|
||||
import { parse } from 'acorn';
|
||||
import escodegen from 'escodegen';
|
||||
import { walk } from 'estree-walker';
|
||||
|
||||
export function transpiler(input, options = {}) {
|
||||
const { wrapAsync = false, addReturn = true, emitMiniLocations = true, emitWidgets = true } = options;
|
||||
@@ -47,6 +46,9 @@ export function transpiler(input, options = {}) {
|
||||
});
|
||||
return this.replace(widgetWithLocation(node));
|
||||
}
|
||||
if (isBareSamplesCall(node, parent)) {
|
||||
return this.replace(withAwait(node));
|
||||
}
|
||||
},
|
||||
leave(node, parent, prop, index) {},
|
||||
});
|
||||
@@ -119,3 +121,14 @@ function widgetWithLocation(node) {
|
||||
node.callee.name = 'sliderWithID';
|
||||
return node;
|
||||
}
|
||||
|
||||
function isBareSamplesCall(node, parent) {
|
||||
return node.type === 'CallExpression' && node.callee.name === 'samples' && parent.type !== 'AwaitExpression';
|
||||
}
|
||||
|
||||
function withAwait(node) {
|
||||
return {
|
||||
type: 'AwaitExpression',
|
||||
argument: node,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ By default, no external samples are loaded, but you can add them like this:
|
||||
|
||||
```js
|
||||
initStrudel({
|
||||
prebake: () => samples('github:tidalcycles/Dirt-Samples/master'),
|
||||
prebake: () => samples('github:tidalcycles/dirt-samples'),
|
||||
});
|
||||
|
||||
document.getElementById('play').addEventListener('click',
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
{
|
||||
"name": "@strudel/web",
|
||||
"version": "1.0.1",
|
||||
"version": "1.0.3",
|
||||
"description": "Easy to setup, opiniated bundle of Strudel for the browser.",
|
||||
"main": "web.mjs",
|
||||
"module": "web.mjs",
|
||||
"publishConfig": {
|
||||
"main": "dist/index.mjs"
|
||||
"main": "dist/index.js",
|
||||
"module": "dist/index.mjs"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "vite build",
|
||||
@@ -36,7 +37,8 @@
|
||||
"@strudel/mini": "workspace:*",
|
||||
"@strudel/tonal": "workspace:*",
|
||||
"@strudel/transpiler": "workspace:*",
|
||||
"@strudel/webaudio": "workspace:*"
|
||||
"@strudel/webaudio": "workspace:*",
|
||||
"@rollup/plugin-replace": "^5.0.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"vite": "^5.0.10"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { defineConfig } from 'vite';
|
||||
import { dependencies } from './package.json';
|
||||
import { resolve } from 'path';
|
||||
import replace from '@rollup/plugin-replace';
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
@@ -8,11 +9,18 @@ export default defineConfig({
|
||||
build: {
|
||||
lib: {
|
||||
entry: resolve(__dirname, 'web.mjs'),
|
||||
formats: ['es'],
|
||||
fileName: (ext) => ({ es: 'index.mjs' })[ext],
|
||||
name: 'strudel',
|
||||
formats: ['es', 'iife'],
|
||||
fileName: (ext) => ({ es: 'index.mjs', iife: 'index.js' })[ext],
|
||||
},
|
||||
rollupOptions: {
|
||||
external: [...Object.keys(dependencies)],
|
||||
// external: [...Object.keys(dependencies)],
|
||||
plugins: [
|
||||
replace({
|
||||
'process.env.NODE_ENV': JSON.stringify('production'),
|
||||
preventAssignment: true,
|
||||
}),
|
||||
],
|
||||
},
|
||||
target: 'esnext',
|
||||
},
|
||||
|
||||
Generated
+3
-3
@@ -430,6 +430,9 @@ importers:
|
||||
|
||||
packages/web:
|
||||
dependencies:
|
||||
'@rollup/plugin-replace':
|
||||
specifier: ^5.0.5
|
||||
version: 5.0.5
|
||||
'@strudel/core':
|
||||
specifier: workspace:*
|
||||
version: link:../core
|
||||
@@ -3691,7 +3694,6 @@ packages:
|
||||
dependencies:
|
||||
'@rollup/pluginutils': 5.1.0
|
||||
magic-string: 0.30.5
|
||||
dev: true
|
||||
|
||||
/@rollup/pluginutils@3.1.0(rollup@2.79.1):
|
||||
resolution: {integrity: sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==}
|
||||
@@ -3717,7 +3719,6 @@ packages:
|
||||
'@types/estree': 1.0.0
|
||||
estree-walker: 2.0.2
|
||||
picomatch: 2.3.1
|
||||
dev: true
|
||||
|
||||
/@rollup/rollup-android-arm-eabi@4.9.2:
|
||||
resolution: {integrity: sha512-RKzxFxBHq9ysZ83fn8Iduv3A283K7zPPYuhL/z9CQuyFrjwpErJx0h4aeb/bnJ+q29GRLgJpY66ceQ/Wcsn3wA==}
|
||||
@@ -7118,7 +7119,6 @@ packages:
|
||||
|
||||
/estree-walker@2.0.2:
|
||||
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
|
||||
dev: true
|
||||
|
||||
/estree-walker@3.0.3:
|
||||
resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
|
||||
|
||||
@@ -29,7 +29,7 @@ edit: the desktop app performance on linux is currently not that great.. the web
|
||||
The desktop App has the same features as the webapp, with the additional ability to load samples from disk. It is currently not documented yet, but you can do something like
|
||||
|
||||
```js
|
||||
await samples('~/music/xxx');
|
||||
samples('~/music/xxx');
|
||||
|
||||
s('my_sound');
|
||||
```
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export const examples = [
|
||||
`// "coastline" @by eddyflux
|
||||
await samples('github:eddyflux/crate')
|
||||
samples('github:eddyflux/crate')
|
||||
setcps(.75)
|
||||
let chords = chord("<Bbm9 Fm9>/4").dict('ireal')
|
||||
stack(
|
||||
@@ -30,7 +30,7 @@ stack(
|
||||
.late("[0 .01]*4").late("[0 .01]*2").size(4)`,
|
||||
`// "broken cut 1" @by froos
|
||||
|
||||
await samples('github:tidalcycles/Dirt-Samples/master')
|
||||
samples('github:tidalcycles/dirt-samples')
|
||||
samples({
|
||||
'slap': 'https://cdn.freesound.org/previews/495/495416_10350281-lq.mp3',
|
||||
'whirl': 'https://cdn.freesound.org/previews/495/495313_10350281-lq.mp3',
|
||||
|
||||
@@ -51,7 +51,7 @@ Alternatively, you can get a taste of what Strudel can do by clicking play on th
|
||||
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'],
|
||||
hh: ['hh27/000_hh27closedhh.wav','hh/000_hh3closedhh.wav'],
|
||||
}, 'github:tidalcycles/Dirt-Samples/master/');
|
||||
}, 'github:tidalcycles/dirt-samples');
|
||||
stack(
|
||||
s("bd,[~ <sd!3 sd(3,4,2)>],hh*8") // drums
|
||||
.speed(perlin.range(.7,.9)) // random sample speed variation
|
||||
|
||||
@@ -143,7 +143,7 @@ Because GitHub is a popular place for uploading open source samples, it has its
|
||||
bd: 'bd/BT0AADA.wav',
|
||||
sd: 'sd/rytm-01-classic.wav',
|
||||
hh: 'hh27/000_hh27closedhh.wav',
|
||||
}, 'github:tidalcycles/Dirt-Samples/master/');
|
||||
}, 'github:tidalcycles/dirt-samples');
|
||||
s("bd sd bd sd,hh*16")`}
|
||||
/>
|
||||
|
||||
@@ -174,7 +174,7 @@ It is also possible, to declare multiple files for one sound, using the array no
|
||||
bd: ['bd/BT0AADA.wav','bd/BT0AAD0.wav'],
|
||||
sd: ['sd/rytm-01-classic.wav','sd/rytm-00-hard.wav'],
|
||||
hh: ['hh27/000_hh27closedhh.wav','hh/000_hh3closedhh.wav'],
|
||||
}, 'github:tidalcycles/Dirt-Samples/master/');
|
||||
}, 'github:tidalcycles/dirt-samples');
|
||||
s("bd:0 bd:1,~ <sd:0 sd:1> ~ sd:0,[hh:0 hh:1]*4")`}
|
||||
/>
|
||||
|
||||
@@ -187,7 +187,7 @@ The sample number can also be set using `n`:
|
||||
bd: ['bd/BT0AADA.wav','bd/BT0AAD0.wav'],
|
||||
sd: ['sd/rytm-01-classic.wav','sd/rytm-00-hard.wav'],
|
||||
hh: ['hh27/000_hh27closedhh.wav','hh/000_hh3closedhh.wav'],
|
||||
}, 'github:tidalcycles/Dirt-Samples/master/');
|
||||
}, 'github:tidalcycles/dirt-samples');
|
||||
s("bd bd,~ sd ~ sd,hh*8").n("<0 1>")`}
|
||||
/>
|
||||
|
||||
@@ -231,7 +231,7 @@ For pitched sounds, you can use `note`, just like with synths:
|
||||
client:idle
|
||||
tune={`samples({
|
||||
'gtr': 'gtr/0001_cleanC.wav',
|
||||
}, 'github:tidalcycles/Dirt-Samples/master/');
|
||||
}, 'github:tidalcycles/dirt-samples');
|
||||
note("g3 [bb3 c4] <g4 f4 eb4 f3>@2").s('gtr').gain(.5)`}
|
||||
/>
|
||||
|
||||
@@ -242,7 +242,7 @@ If we want them to behave more like a synth, we can add `clip(1)`:
|
||||
client:idle
|
||||
tune={`samples({
|
||||
'gtr': 'gtr/0001_cleanC.wav',
|
||||
}, 'github:tidalcycles/Dirt-Samples/master/');
|
||||
}, 'github:tidalcycles/dirt-samples');
|
||||
note("g3 [bb3 c4] <g4 f4 eb4 f3>@2").s('gtr').clip(1)
|
||||
.gain(.5)`}
|
||||
/>
|
||||
@@ -256,7 +256,7 @@ If we have 2 samples with different base pitches, we can make them in tune by sp
|
||||
tune={`samples({
|
||||
'gtr': 'gtr/0001_cleanC.wav',
|
||||
'moog': { 'g3': 'moog/005_Mighty%20Moog%20G3.wav' },
|
||||
}, 'github:tidalcycles/Dirt-Samples/master/');
|
||||
}, 'github:tidalcycles/dirt-samples');
|
||||
note("g3 [bb3 c4] <g4 f4 eb4 f3>@2").s("gtr,moog").clip(1)
|
||||
.gain(.5)`}
|
||||
/>
|
||||
@@ -272,7 +272,7 @@ We can also declare different samples for different regions of the keyboard:
|
||||
'g2': 'moog/004_Mighty%20Moog%20G2.wav',
|
||||
'g3': 'moog/005_Mighty%20Moog%20G3.wav',
|
||||
'g4': 'moog/006_Mighty%20Moog%20G4.wav',
|
||||
}}, 'github:tidalcycles/Dirt-Samples/master/');
|
||||
}}, 'github:tidalcycles/dirt-samples');
|
||||
note("g2!2 <bb2 c3>!2, <c4@3 [<eb4 bb3> g4 f4]>")
|
||||
.s('moog').clip(1)
|
||||
.gain(.5).cpm(60)`}
|
||||
@@ -287,7 +287,7 @@ With it, you can enter any sample name(s) to query from [freesound.org](https://
|
||||
|
||||
<MiniRepl
|
||||
client:idle
|
||||
tune={`await samples('shabda:bass:4,hihat:4,rimshot:2')
|
||||
tune={`samples('shabda:bass:4,hihat:4,rimshot:2')
|
||||
stack(
|
||||
n("0 1 2 3 0 1 2 3").s('bass'),
|
||||
n("0 1*2 2 3*2").s('hihat'),
|
||||
@@ -300,8 +300,8 @@ Note that the language code and the gender parameters are optional and default t
|
||||
|
||||
<MiniRepl
|
||||
client:idle
|
||||
tune={`await samples('shabda/speech:the_drum,forever')
|
||||
await samples('shabda/speech/fr-FR/m:magnifique')
|
||||
tune={`samples('shabda/speech:the_drum,forever')
|
||||
samples('shabda/speech/fr-FR/m:magnifique')
|
||||
stack(
|
||||
s("the_drum*2").chop(16).speed(rand.range(0.85,1.1)),
|
||||
s("forever magnifique").slow(4).late(0.125)
|
||||
|
||||
@@ -60,7 +60,7 @@ A sample can be looped and chopped like this:
|
||||
|
||||
<MiniRepl
|
||||
client:visible
|
||||
tune={`await samples('github:yaxu/clean-breaks/main')
|
||||
tune={`samples('github:yaxu/clean-breaks')
|
||||
s("amen/4").fit().chop(32)`}
|
||||
punchcard
|
||||
/>
|
||||
@@ -71,7 +71,7 @@ Let's add randmized doubling + reversing:
|
||||
|
||||
<MiniRepl
|
||||
client:visible
|
||||
tune={`await samples('github:yaxu/clean-breaks/main')
|
||||
tune={`samples('github:yaxu/clean-breaks')
|
||||
s("amen/4").fit().chop(16).cut(1)
|
||||
.sometimesBy(.5, ply("2"))
|
||||
.sometimesBy(.25, mul(speed("-1")))`}
|
||||
@@ -82,7 +82,7 @@ If we want to specify the order of samples, we can replace `chop` with `slice`:
|
||||
|
||||
<MiniRepl
|
||||
client:visible
|
||||
tune={`await samples('github:yaxu/clean-breaks/main')
|
||||
tune={`samples('github:yaxu/clean-breaks')
|
||||
s("amen/4").fit()
|
||||
.slice(8, "<0 1 2 3 4*2 5 6 [6 7]>*2")
|
||||
.cut(1).rarely(ply("2"))`}
|
||||
@@ -93,7 +93,7 @@ If we use `splice` instead of `slice`, the speed adjusts to the duration of the
|
||||
|
||||
<MiniRepl
|
||||
client:visible
|
||||
tune={`await samples('github:yaxu/clean-breaks/main')
|
||||
tune={`samples('github:yaxu/clean-breaks')
|
||||
s("amen")
|
||||
.splice(8, "<0 1 2 3 4*2 5 6 [6 7]>*2")
|
||||
.cut(1).rarely(ply("2"))`}
|
||||
@@ -213,7 +213,7 @@ Using `run` with `n`, we can rush through a sample bank:
|
||||
|
||||
<MiniRepl
|
||||
client:visible
|
||||
tune={`await samples('bubo:fox')
|
||||
tune={`samples('bubo:fox')
|
||||
n(run(8)).s("ftabla")`}
|
||||
punchcard
|
||||
/>
|
||||
@@ -224,7 +224,7 @@ In this case, I hear the beginning at the third sample, which can be accounted f
|
||||
|
||||
<MiniRepl
|
||||
client:visible
|
||||
tune={`await samples('bubo:fox')
|
||||
tune={`samples('bubo:fox')
|
||||
n(run(8)).s("ftabla").early(2/8)`}
|
||||
/>
|
||||
|
||||
@@ -232,7 +232,7 @@ Let's add some randomness:
|
||||
|
||||
<MiniRepl
|
||||
client:visible
|
||||
tune={`await samples('bubo:fox')
|
||||
tune={`samples('bubo:fox')
|
||||
n(run(8)).s("ftabla").early(2/8)
|
||||
.sometimes(mul(speed("1.5")))`}
|
||||
/>
|
||||
@@ -299,7 +299,7 @@ To simplify loading wavetables, any sample that starts with `wt_` will be looped
|
||||
|
||||
<MiniRepl
|
||||
client:visible
|
||||
tune={`await samples('github:bubobubobubobubo/dough-waveforms/main')
|
||||
tune={`samples('github:bubobubobubobubo/dough-waveforms')
|
||||
note("c eb g bb").s("wt_dbass").clip(2)`}
|
||||
/>
|
||||
|
||||
@@ -307,7 +307,7 @@ Running through different wavetables can also give interesting variations:
|
||||
|
||||
<MiniRepl
|
||||
client:visible
|
||||
tune={`await samples('github:bubobubobubobubo/dough-waveforms/main')
|
||||
tune={`samples('github:bubobubobubobubo/dough-waveforms')
|
||||
note("c2*8").s("wt_dbass").n(run(8))`}
|
||||
/>
|
||||
|
||||
@@ -315,7 +315,7 @@ note("c2*8").s("wt_dbass").n(run(8))`}
|
||||
|
||||
<MiniRepl
|
||||
client:visible
|
||||
tune={`await samples('github:bubobubobubobubo/dough-waveforms/main')
|
||||
tune={`samples('github:bubobubobubobubo/dough-waveforms')
|
||||
note("c2*8").s("wt_dbass").n(run(8))
|
||||
.lpf(perlin.range(200,2000).slow(8))
|
||||
.lpenv(-3).lpa(.1).room(.5)`}
|
||||
|
||||
@@ -8,7 +8,7 @@ import { MiniRepl } from '../../docs/MiniRepl';
|
||||
Note:
|
||||
|
||||
- this has been (partly) translated from https://tidalcycles.org/docs/patternlib/howtos/buildrhythms
|
||||
- this only sounds good with `samples('github:tidalcycles/Dirt-Samples/master')` in prebake
|
||||
- this only sounds good with `samples('github:tidalcycles/dirt-samples')` in prebake
|
||||
|
||||
# Build Rhythms
|
||||
|
||||
|
||||
@@ -64,3 +64,8 @@
|
||||
#code .cm-focused {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
#code .cm-matchingBracket {
|
||||
text-decoration: underline 0.18rem;
|
||||
text-underline-offset: 0.22rem;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,13 @@ This program is free software: you can redistribute it and/or modify it under th
|
||||
import { code2hash, getDrawContext, logger, silence } from '@strudel/core';
|
||||
import cx from '@src/cx.mjs';
|
||||
import { transpiler } from '@strudel/transpiler';
|
||||
import { getAudioContext, initAudioOnFirstClick, webaudioOutput } from '@strudel/webaudio';
|
||||
import {
|
||||
getAudioContext,
|
||||
initAudioOnFirstClick,
|
||||
webaudioOutput,
|
||||
resetGlobalEffects,
|
||||
resetLoadedSounds,
|
||||
} from '@strudel/webaudio';
|
||||
import { defaultAudioDeviceName } from '../settings.mjs';
|
||||
import { getAudioDevices, setAudioDevice } from './util.mjs';
|
||||
import { StrudelMirror, defaultSettings } from '@strudel/codemirror';
|
||||
@@ -157,6 +163,7 @@ export function Repl({ embedded = false }) {
|
||||
};
|
||||
|
||||
const resetEditor = async () => {
|
||||
resetGlobalEffects();
|
||||
clearCanvas();
|
||||
resetLoadedSounds();
|
||||
editorRef.current.repl.setCps(0.5);
|
||||
@@ -183,6 +190,7 @@ export function Repl({ embedded = false }) {
|
||||
setViewingPatternData(patternData);
|
||||
clearCanvas();
|
||||
resetLoadedSounds();
|
||||
resetGlobalEffects();
|
||||
await prebake(); // declare default samples
|
||||
editorRef.current.setCode(code);
|
||||
editorRef.current.repl.evaluate(code);
|
||||
|
||||
@@ -77,6 +77,7 @@ export function SettingsTab({ started }) {
|
||||
const {
|
||||
theme,
|
||||
keybindings,
|
||||
isBracketMatchingEnabled,
|
||||
isLineNumbersDisplayed,
|
||||
isPatternHighlightingEnabled,
|
||||
isActiveLineHighlighted,
|
||||
@@ -137,6 +138,11 @@ export function SettingsTab({ started }) {
|
||||
></ButtonGroup>
|
||||
</FormItem>
|
||||
<FormItem label="Code Settings">
|
||||
<Checkbox
|
||||
label="Enable bracket matching"
|
||||
onChange={(cbEvent) => settingsMap.setKey('isBracketMatchingEnabled', cbEvent.target.checked)}
|
||||
value={isBracketMatchingEnabled}
|
||||
/>
|
||||
<Checkbox
|
||||
label="Display line numbers"
|
||||
onChange={(cbEvent) => settingsMap.setKey('isLineNumbersDisplayed', cbEvent.target.checked)}
|
||||
|
||||
@@ -115,13 +115,12 @@ export async function prebake() {
|
||||
'numbers/8.wav',
|
||||
],
|
||||
},
|
||||
'github:tidalcycles/Dirt-Samples/master/',
|
||||
'github:tidalcycles/dirt-samples',
|
||||
{
|
||||
prebake: true,
|
||||
},
|
||||
),
|
||||
]);
|
||||
// await samples('github:tidalcycles/Dirt-Samples/master');
|
||||
}
|
||||
|
||||
const maxPan = noteToMidi('C8');
|
||||
|
||||
@@ -527,7 +527,7 @@ export const meltingsubmarine = `// "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/')
|
||||
samples('github:tidalcycles/dirt-samples')
|
||||
stack(
|
||||
s("bd:5,[~ <sd:1!3 sd:1(3,4,3)>],hh27(3,4,1)") // drums
|
||||
.speed(perlin.range(.7,.9)) // random sample speed variation
|
||||
@@ -574,7 +574,7 @@ samples({
|
||||
sd: ['sd/rytm-01-classic.wav','sd/rytm-00-hard.wav'],
|
||||
hh: ['hh27/000_hh27closedhh.wav','hh/000_hh3closedhh.wav'],
|
||||
perc: ['perc/002_perc2.wav'],
|
||||
}, 'github:tidalcycles/Dirt-Samples/master/');
|
||||
}, 'github:tidalcycles/dirt-samples');
|
||||
|
||||
chord("<C^7 Am7 Dm7 G7>*2").dict('lefthand').anchor("G4").voicing()
|
||||
.stack(n("0@6 [<1 2> <2 0> 1]@2").scale('C5 major'))
|
||||
@@ -608,7 +608,7 @@ 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'],
|
||||
hh: ['hh27/000_hh27closedhh.wav','hh/000_hh3closedhh.wav'],
|
||||
}, 'github:tidalcycles/Dirt-Samples/master/');
|
||||
}, 'github:tidalcycles/dirt-samples');
|
||||
|
||||
note("<8(3,8) <7 7*2> [4 5@3] 8>".sub(1) // sub 1 -> 1-indexed
|
||||
.layer(
|
||||
@@ -797,7 +797,7 @@ export const amensister = `// "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')
|
||||
samples('github:tidalcycles/dirt-samples')
|
||||
|
||||
stack(
|
||||
// amen
|
||||
@@ -906,7 +906,7 @@ export const arpoon = `// "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')
|
||||
samples('github:tidalcycles/dirt-samples')
|
||||
|
||||
n("[0,3] 2 [1,3] 2".fast(3).lastOf(4, fast(2))).clip(2)
|
||||
.offset("<<1 2> 2 1 1>")
|
||||
|
||||
@@ -7,6 +7,7 @@ export const defaultAudioDeviceName = 'System Standard';
|
||||
export const defaultSettings = {
|
||||
activeFooter: 'intro',
|
||||
keybindings: 'codemirror',
|
||||
isBracketMatchingEnabled: true,
|
||||
isLineNumbersDisplayed: true,
|
||||
isActiveLineHighlighted: true,
|
||||
isAutoCompletionEnabled: false,
|
||||
@@ -40,6 +41,7 @@ export function useSettings() {
|
||||
return {
|
||||
...state,
|
||||
isZen: [true, 'true'].includes(state.isZen) ? true : false,
|
||||
isBracketMatchingEnabled: [true, 'true'].includes(state.isBracketMatchingEnabled) ? true : false,
|
||||
isLineNumbersDisplayed: [true, 'true'].includes(state.isLineNumbersDisplayed) ? true : false,
|
||||
isActiveLineHighlighted: [true, 'true'].includes(state.isActiveLineHighlighted) ? true : false,
|
||||
isAutoCompletionEnabled: [true, 'true'].includes(state.isAutoCompletionEnabled) ? true : false,
|
||||
|
||||
Reference in New Issue
Block a user