mirror of
https://github.com/eliasstepanik/strudel.git
synced 2026-01-11 21:58:37 +00:00
Merge branch 'main' into obj-support-scale
This commit is contained in:
commit
7f9ef59bea
16
.github/workflows/test.yml
vendored
16
.github/workflows/test.yml
vendored
@ -10,10 +10,12 @@ jobs:
|
||||
node-version: [18]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: 'npm'
|
||||
- run: npm install
|
||||
- run: npm test
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: "npm"
|
||||
- run: npm install
|
||||
- run: npm run format-check
|
||||
- run: npm run lint
|
||||
- run: npm test
|
||||
|
||||
@ -5,3 +5,4 @@
|
||||
**/out
|
||||
**/dist
|
||||
packages/mini/krill-parser.js
|
||||
packages/xen/tunejs.js
|
||||
|
||||
29
.prettierrc
29
.prettierrc
@ -1,16 +1,15 @@
|
||||
{
|
||||
"printWidth": 120,
|
||||
"useTabs": false,
|
||||
"tabWidth": 2,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"jsxSingleQuote": false,
|
||||
"trailingComma": "all",
|
||||
"bracketSpacing": true,
|
||||
"jsxBracketSameLine": false,
|
||||
"arrowParens": "always",
|
||||
"proseWrap": "preserve",
|
||||
"htmlWhitespaceSensitivity": "css",
|
||||
"endOfLine": "lf"
|
||||
}
|
||||
|
||||
"printWidth": 120,
|
||||
"useTabs": false,
|
||||
"tabWidth": 2,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"jsxSingleQuote": false,
|
||||
"trailingComma": "all",
|
||||
"bracketSpacing": true,
|
||||
"jsxBracketSameLine": false,
|
||||
"arrowParens": "always",
|
||||
"proseWrap": "preserve",
|
||||
"htmlWhitespaceSensitivity": "css",
|
||||
"endOfLine": "lf"
|
||||
}
|
||||
|
||||
110
package-lock.json
generated
110
package-lock.json
generated
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@strudel.cycles/monorepo",
|
||||
"version": "0.0.4",
|
||||
"version": "0.5.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@strudel.cycles/monorepo",
|
||||
"version": "0.0.4",
|
||||
"version": "0.5.0",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
@ -12044,7 +12044,7 @@
|
||||
},
|
||||
"packages/core": {
|
||||
"name": "@strudel.cycles/core",
|
||||
"version": "0.4.1",
|
||||
"version": "0.5.0",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"bjork": "^0.0.1",
|
||||
@ -12053,7 +12053,7 @@
|
||||
},
|
||||
"packages/csound": {
|
||||
"name": "@strudel.cycles/csound",
|
||||
"version": "0.3.0",
|
||||
"version": "0.5.1",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@csound/browser": "^6.18.3"
|
||||
@ -12066,10 +12066,10 @@
|
||||
},
|
||||
"packages/eval": {
|
||||
"name": "@strudel.cycles/eval",
|
||||
"version": "0.4.1",
|
||||
"version": "0.5.0",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "^0.4.1",
|
||||
"@strudel.cycles/core": "^0.5.0",
|
||||
"estraverse": "^5.3.0",
|
||||
"shift-ast": "^7.0.0",
|
||||
"shift-codegen": "^8.1.0",
|
||||
@ -12080,10 +12080,10 @@
|
||||
},
|
||||
"packages/midi": {
|
||||
"name": "@strudel.cycles/midi",
|
||||
"version": "0.4.1",
|
||||
"version": "0.5.0",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/tone": "^0.4.1",
|
||||
"@strudel.cycles/tone": "^0.5.0",
|
||||
"tone": "^14.7.77",
|
||||
"webmidi": "^3.0.21"
|
||||
}
|
||||
@ -12103,12 +12103,12 @@
|
||||
},
|
||||
"packages/mini": {
|
||||
"name": "@strudel.cycles/mini",
|
||||
"version": "0.4.1",
|
||||
"version": "0.5.0",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "^0.4.1",
|
||||
"@strudel.cycles/eval": "^0.4.1",
|
||||
"@strudel.cycles/tone": "^0.4.1"
|
||||
"@strudel.cycles/core": "^0.5.0",
|
||||
"@strudel.cycles/eval": "^0.5.0",
|
||||
"@strudel.cycles/tone": "^0.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"peggy": "^2.0.1"
|
||||
@ -12116,7 +12116,7 @@
|
||||
},
|
||||
"packages/osc": {
|
||||
"name": "@strudel.cycles/osc",
|
||||
"version": "0.3.1",
|
||||
"version": "0.4.0",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"osc-js": "^2.4.0"
|
||||
@ -12127,14 +12127,14 @@
|
||||
},
|
||||
"packages/react": {
|
||||
"name": "@strudel.cycles/react",
|
||||
"version": "0.4.2",
|
||||
"version": "0.5.0",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@codemirror/lang-javascript": "^6.1.1",
|
||||
"@strudel.cycles/core": "^0.4.1",
|
||||
"@strudel.cycles/tone": "^0.4.1",
|
||||
"@strudel.cycles/transpiler": "^0.4.1",
|
||||
"@strudel.cycles/webaudio": "^0.4.2",
|
||||
"@strudel.cycles/core": "^0.5.0",
|
||||
"@strudel.cycles/tone": "^0.5.0",
|
||||
"@strudel.cycles/transpiler": "^0.5.0",
|
||||
"@strudel.cycles/webaudio": "^0.5.0",
|
||||
"@uiw/codemirror-themes": "^4.12.4",
|
||||
"@uiw/react-codemirror": "^4.12.4",
|
||||
"react-hook-inview": "^4.5.0"
|
||||
@ -12201,16 +12201,16 @@
|
||||
},
|
||||
"packages/serial": {
|
||||
"name": "@strudel.cycles/serial",
|
||||
"version": "0.2.0",
|
||||
"version": "0.3.0",
|
||||
"license": "AGPL-3.0-or-later"
|
||||
},
|
||||
"packages/soundfonts": {
|
||||
"name": "@strudel.cycles/soundfonts",
|
||||
"version": "0.4.2",
|
||||
"version": "0.5.0",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "^0.4.1",
|
||||
"@strudel.cycles/webaudio": "^0.4.2",
|
||||
"@strudel.cycles/core": "^0.5.0",
|
||||
"@strudel.cycles/webaudio": "^0.5.0",
|
||||
"sfumato": "^0.1.2",
|
||||
"soundfont2": "^0.4.0"
|
||||
},
|
||||
@ -12237,10 +12237,10 @@
|
||||
},
|
||||
"packages/tonal": {
|
||||
"name": "@strudel.cycles/tonal",
|
||||
"version": "0.4.1",
|
||||
"version": "0.5.0",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "^0.4.1",
|
||||
"@strudel.cycles/core": "^0.5.0",
|
||||
"@tonaljs/tonal": "^4.7.2",
|
||||
"chord-voicings": "^0.0.1",
|
||||
"webmidi": "^3.0.21"
|
||||
@ -12261,19 +12261,19 @@
|
||||
},
|
||||
"packages/tone": {
|
||||
"name": "@strudel.cycles/tone",
|
||||
"version": "0.4.1",
|
||||
"version": "0.5.0",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "^0.4.1",
|
||||
"@strudel.cycles/core": "^0.5.0",
|
||||
"tone": "^14.7.77"
|
||||
}
|
||||
},
|
||||
"packages/transpiler": {
|
||||
"name": "@strudel.cycles/transpiler",
|
||||
"version": "0.4.1",
|
||||
"version": "0.5.0",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "^0.4.1",
|
||||
"@strudel.cycles/core": "^0.5.0",
|
||||
"acorn": "^8.8.1",
|
||||
"escodegen": "^2.0.0",
|
||||
"estree-walker": "^3.0.1"
|
||||
@ -12291,27 +12291,27 @@
|
||||
},
|
||||
"packages/webaudio": {
|
||||
"name": "@strudel.cycles/webaudio",
|
||||
"version": "0.4.2",
|
||||
"version": "0.5.0",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "^0.4.1"
|
||||
"@strudel.cycles/core": "^0.5.0"
|
||||
}
|
||||
},
|
||||
"packages/webdirt": {
|
||||
"name": "@strudel.cycles/webdirt",
|
||||
"version": "0.4.1",
|
||||
"version": "0.5.0",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "^0.4.1",
|
||||
"@strudel.cycles/core": "^0.5.0",
|
||||
"WebDirt": "github:dktr0/WebDirt"
|
||||
}
|
||||
},
|
||||
"packages/xen": {
|
||||
"name": "@strudel.cycles/xen",
|
||||
"version": "0.4.1",
|
||||
"version": "0.5.0",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "^0.4.1"
|
||||
"@strudel.cycles/core": "^0.5.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -13740,7 +13740,7 @@
|
||||
"@strudel.cycles/eval": {
|
||||
"version": "file:packages/eval",
|
||||
"requires": {
|
||||
"@strudel.cycles/core": "^0.4.1",
|
||||
"@strudel.cycles/core": "^0.5.0",
|
||||
"estraverse": "^5.3.0",
|
||||
"shift-ast": "^7.0.0",
|
||||
"shift-codegen": "^8.1.0",
|
||||
@ -13752,7 +13752,7 @@
|
||||
"@strudel.cycles/midi": {
|
||||
"version": "file:packages/midi",
|
||||
"requires": {
|
||||
"@strudel.cycles/tone": "^0.4.1",
|
||||
"@strudel.cycles/tone": "^0.5.0",
|
||||
"tone": "^14.7.77",
|
||||
"webmidi": "^3.0.21"
|
||||
},
|
||||
@ -13769,9 +13769,9 @@
|
||||
"@strudel.cycles/mini": {
|
||||
"version": "file:packages/mini",
|
||||
"requires": {
|
||||
"@strudel.cycles/core": "^0.4.1",
|
||||
"@strudel.cycles/eval": "^0.4.1",
|
||||
"@strudel.cycles/tone": "^0.4.1",
|
||||
"@strudel.cycles/core": "^0.5.0",
|
||||
"@strudel.cycles/eval": "^0.5.0",
|
||||
"@strudel.cycles/tone": "^0.5.0",
|
||||
"peggy": "^2.0.1"
|
||||
}
|
||||
},
|
||||
@ -13786,10 +13786,10 @@
|
||||
"version": "file:packages/react",
|
||||
"requires": {
|
||||
"@codemirror/lang-javascript": "^6.1.1",
|
||||
"@strudel.cycles/core": "^0.4.1",
|
||||
"@strudel.cycles/tone": "^0.4.1",
|
||||
"@strudel.cycles/transpiler": "^0.4.1",
|
||||
"@strudel.cycles/webaudio": "^0.4.2",
|
||||
"@strudel.cycles/core": "^0.5.0",
|
||||
"@strudel.cycles/tone": "^0.5.0",
|
||||
"@strudel.cycles/transpiler": "^0.5.0",
|
||||
"@strudel.cycles/webaudio": "^0.5.0",
|
||||
"@types/react": "^17.0.2",
|
||||
"@types/react-dom": "^17.0.2",
|
||||
"@uiw/codemirror-themes": "^4.12.4",
|
||||
@ -13838,8 +13838,8 @@
|
||||
"@strudel.cycles/soundfonts": {
|
||||
"version": "file:packages/soundfonts",
|
||||
"requires": {
|
||||
"@strudel.cycles/core": "^0.4.1",
|
||||
"@strudel.cycles/webaudio": "^0.4.2",
|
||||
"@strudel.cycles/core": "^0.5.0",
|
||||
"@strudel.cycles/webaudio": "^0.5.0",
|
||||
"node-fetch": "^3.2.6",
|
||||
"sfumato": "^0.1.2",
|
||||
"soundfont2": "^0.4.0"
|
||||
@ -13859,7 +13859,7 @@
|
||||
"@strudel.cycles/tonal": {
|
||||
"version": "file:packages/tonal",
|
||||
"requires": {
|
||||
"@strudel.cycles/core": "^0.4.1",
|
||||
"@strudel.cycles/core": "^0.5.0",
|
||||
"@tonaljs/tonal": "^4.7.2",
|
||||
"chord-voicings": "^0.0.1",
|
||||
"webmidi": "^3.0.21"
|
||||
@ -13877,14 +13877,14 @@
|
||||
"@strudel.cycles/tone": {
|
||||
"version": "file:packages/tone",
|
||||
"requires": {
|
||||
"@strudel.cycles/core": "^0.4.1",
|
||||
"@strudel.cycles/core": "^0.5.0",
|
||||
"tone": "^14.7.77"
|
||||
}
|
||||
},
|
||||
"@strudel.cycles/transpiler": {
|
||||
"version": "file:packages/transpiler",
|
||||
"requires": {
|
||||
"@strudel.cycles/core": "^0.4.1",
|
||||
"@strudel.cycles/core": "^0.5.0",
|
||||
"acorn": "^8.8.1",
|
||||
"escodegen": "^2.0.0",
|
||||
"estree-walker": "^3.0.1"
|
||||
@ -13898,20 +13898,20 @@
|
||||
"@strudel.cycles/webaudio": {
|
||||
"version": "file:packages/webaudio",
|
||||
"requires": {
|
||||
"@strudel.cycles/core": "^0.4.1"
|
||||
"@strudel.cycles/core": "^0.5.0"
|
||||
}
|
||||
},
|
||||
"@strudel.cycles/webdirt": {
|
||||
"version": "file:packages/webdirt",
|
||||
"requires": {
|
||||
"@strudel.cycles/core": "^0.4.1",
|
||||
"@strudel.cycles/core": "^0.5.0",
|
||||
"WebDirt": "github:dktr0/WebDirt"
|
||||
}
|
||||
},
|
||||
"@strudel.cycles/xen": {
|
||||
"version": "file:packages/xen",
|
||||
"requires": {
|
||||
"@strudel.cycles/core": "^0.4.1"
|
||||
"@strudel.cycles/core": "^0.5.0"
|
||||
}
|
||||
},
|
||||
"@tonaljs/abc-notation": {
|
||||
@ -18656,10 +18656,10 @@
|
||||
"version": "file:packages/react",
|
||||
"requires": {
|
||||
"@codemirror/lang-javascript": "^6.1.1",
|
||||
"@strudel.cycles/core": "^0.4.1",
|
||||
"@strudel.cycles/tone": "^0.4.1",
|
||||
"@strudel.cycles/transpiler": "^0.4.1",
|
||||
"@strudel.cycles/webaudio": "^0.4.2",
|
||||
"@strudel.cycles/core": "^0.5.0",
|
||||
"@strudel.cycles/tone": "^0.5.0",
|
||||
"@strudel.cycles/transpiler": "^0.5.0",
|
||||
"@strudel.cycles/webaudio": "^0.5.0",
|
||||
"@types/react": "^17.0.2",
|
||||
"@types/react-dom": "^17.0.2",
|
||||
"@uiw/codemirror-themes": "^4.12.4",
|
||||
|
||||
10
package.json
10
package.json
@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "@strudel.cycles/monorepo",
|
||||
"version": "0.0.4",
|
||||
"version": "0.5.0",
|
||||
"private": true,
|
||||
"description": "Port of tidalcycles to javascript",
|
||||
"scripts": {
|
||||
"pretest": "cd tutorial && npm run jsdoc-json",
|
||||
"test": "npm run lint && vitest run --version",
|
||||
"test": "vitest run --version",
|
||||
"test-ui": "vitest --ui",
|
||||
"test-coverage": "vitest --coverage",
|
||||
"bootstrap": "lerna bootstrap",
|
||||
@ -18,8 +18,10 @@
|
||||
"deploy": "NODE_DEBUG=gh-pages gh-pages -d out",
|
||||
"jsdoc": "jsdoc packages/ -c jsdoc.config.json",
|
||||
"jsdoc-json": "jsdoc packages/ --template ./node_modules/jsdoc-json --destination doc.json -c jsdoc.config.json",
|
||||
"lint": "npx eslint . --ext mjs,js --quiet",
|
||||
"codeformat": "prettier --write ."
|
||||
"lint": "eslint . --ext mjs,js --quiet",
|
||||
"codeformat": "prettier --write .",
|
||||
"format-check": "prettier --check .",
|
||||
"check": "npm run format-check && npm run lint && npm run test"
|
||||
},
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
|
||||
@ -16,7 +16,7 @@ evalScope(
|
||||
import('@strudel.cycles/tonal'),
|
||||
);
|
||||
|
||||
setStringParser(mini)
|
||||
setStringParser(mini);
|
||||
|
||||
const { evaluate } = repl({
|
||||
defaultOutput: webaudioOutput,
|
||||
|
||||
@ -80,16 +80,16 @@ export class Hap {
|
||||
}
|
||||
|
||||
show(compact = false) {
|
||||
const value = typeof this.value === 'object'
|
||||
? compact
|
||||
const value =
|
||||
typeof this.value === 'object'
|
||||
? compact
|
||||
? JSON.stringify(this.value).slice(1, -1).replaceAll('"', '').replaceAll(',', ' ')
|
||||
: JSON.stringify(this.value)
|
||||
: this.value
|
||||
: this.value;
|
||||
var spans = '';
|
||||
if (this.whole == undefined) {
|
||||
spans = '~' + this.part.show;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
var is_whole = this.whole.begin.equals(this.part.begin) && this.whole.end.equals(this.part.end);
|
||||
if (!this.whole.begin.equals(this.part.begin)) {
|
||||
spans = this.whole.begin.show() + ' ⇜ ';
|
||||
@ -102,12 +102,10 @@ export class Hap {
|
||||
spans += ')';
|
||||
}
|
||||
if (!this.whole.end.equals(this.part.end)) {
|
||||
spans += ' ⇝ ' + this.whole.end.show()
|
||||
spans += ' ⇝ ' + this.whole.end.show();
|
||||
}
|
||||
}
|
||||
return (
|
||||
'[ ' + spans + ' | ' + value + ' ]'
|
||||
);
|
||||
return '[ ' + spans + ' | ' + value + ' ]';
|
||||
}
|
||||
|
||||
showWhole(compact = false) {
|
||||
|
||||
2
packages/core/package-lock.json
generated
2
packages/core/package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/core",
|
||||
"version": "0.4.1",
|
||||
"version": "0.5.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/core",
|
||||
"version": "0.4.1",
|
||||
"version": "0.5.0",
|
||||
"description": "Port of Tidal Cycles to JavaScript",
|
||||
"main": "index.mjs",
|
||||
"type": "module",
|
||||
|
||||
@ -534,6 +534,57 @@ export class Pattern {
|
||||
return this.filterHaps((hap) => hap.whole);
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines adjacent haps with the same value and whole. Only
|
||||
* intended for use in tests.
|
||||
*/
|
||||
defragmentHaps() {
|
||||
// remove continuous haps
|
||||
const pat = this.discreteOnly();
|
||||
|
||||
return pat.withHaps((haps) => {
|
||||
const result = [];
|
||||
for (var i = 0; i < haps.length; ++i) {
|
||||
var searching = true;
|
||||
var a = haps[i];
|
||||
while (searching) {
|
||||
const a_value = JSON.stringify(haps[i].value);
|
||||
var found = false;
|
||||
|
||||
for (var j = i + 1; j < haps.length; j++) {
|
||||
const b = haps[j];
|
||||
|
||||
if (a.whole.equals(b.whole)) {
|
||||
if (a.part.begin.eq(b.part.end)) {
|
||||
if (a_value === JSON.stringify(b.value)) {
|
||||
// eat the matching hap into 'a'
|
||||
a = new Hap(a.whole, new TimeSpan(b.part.begin, a.part.end), a.value);
|
||||
haps.splice(j, 1);
|
||||
// restart the search
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
} else if (b.part.begin.eq(a.part.end)) {
|
||||
if (a_value == JSON.stringify(b.value)) {
|
||||
// eat the matching hap into 'a'
|
||||
a = new Hap(a.whole, new TimeSpan(a.part.begin, b.part.end), a.value);
|
||||
haps.splice(j, 1);
|
||||
// restart the search
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
searching = found;
|
||||
}
|
||||
result.push(a);
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries the pattern for the first cycle, returning Haps. Mainly of use when
|
||||
* debugging a pattern.
|
||||
|
||||
@ -915,4 +915,20 @@ describe('Pattern', () => {
|
||||
expect(sequence(1, 2).add.squeeze(4, 5).firstCycle()).toStrictEqual(sequence(5, 6, 6, 7).firstCycle());
|
||||
});
|
||||
});
|
||||
describe('defragmentHaps', () => {
|
||||
it('Can merge two touching haps with same whole and value', () => {
|
||||
expect(stack(pure('a').mask(1, 0), pure('a').mask(0, 1)).defragmentHaps().firstCycle().length).toStrictEqual(1);
|
||||
});
|
||||
it('Doesnt merge two overlapping haps', () => {
|
||||
expect(stack(pure('a').mask(1, 1, 0), pure('a').mask(0, 1)).defragmentHaps().firstCycle().length).toStrictEqual(
|
||||
2,
|
||||
);
|
||||
});
|
||||
it('Doesnt merge two touching haps with different values', () => {
|
||||
expect(stack(pure('a').mask(1, 0), pure('b').mask(0, 1)).defragmentHaps().firstCycle().length).toStrictEqual(2);
|
||||
});
|
||||
it('Doesnt merge two touching haps with different wholes', () => {
|
||||
expect(stack(sequence('a', silence), pure('a').mask(0, 1)).defragmentHaps().firstCycle().length).toStrictEqual(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -10,6 +10,7 @@ import {
|
||||
tokenizeNote,
|
||||
toMidi,
|
||||
fromMidi,
|
||||
freqToMidi,
|
||||
_mod,
|
||||
compose,
|
||||
getFrequency,
|
||||
@ -94,6 +95,12 @@ describe('fromMidi', () => {
|
||||
expect(fromMidi(57)).toEqual(220);
|
||||
});
|
||||
});
|
||||
describe('freqToMidi', () => {
|
||||
it('should turn frequency into midi', () => {
|
||||
expect(freqToMidi(440)).toEqual(69);
|
||||
expect(freqToMidi(220)).toEqual(57);
|
||||
});
|
||||
});
|
||||
describe('getFrequency', () => {
|
||||
const happify = (val, context = {}) => pure(val).firstCycle()[0].setContext(context);
|
||||
it('should turn note into frequency', () => {
|
||||
|
||||
@ -20,9 +20,9 @@ export class TimeSpan {
|
||||
|
||||
// Support zero-width timespans
|
||||
if (begin.equals(end)) {
|
||||
return([new TimeSpan(begin, end)]);
|
||||
return [new TimeSpan(begin, end)];
|
||||
}
|
||||
|
||||
|
||||
while (end.gt(begin)) {
|
||||
// If begin and end are in the same cycle, we're done.
|
||||
if (begin.sam().equals(end_sam)) {
|
||||
|
||||
@ -31,6 +31,30 @@ export const fromMidi = (n) => {
|
||||
return Math.pow(2, (n - 69) / 12) * 440;
|
||||
};
|
||||
|
||||
export const freqToMidi = (freq) => {
|
||||
return (12 * Math.log(freq / 440)) / Math.LN2 + 69;
|
||||
};
|
||||
|
||||
export const valueToMidi = (value, fallbackValue) => {
|
||||
if (typeof value !== 'object') {
|
||||
throw new Error('valueToMidi: expected object value');
|
||||
}
|
||||
let { freq, note } = value;
|
||||
if (typeof freq === 'number') {
|
||||
return freqToMidi(freq);
|
||||
}
|
||||
if (typeof note === 'string') {
|
||||
return toMidi(note);
|
||||
}
|
||||
if (typeof note === 'number') {
|
||||
return note;
|
||||
}
|
||||
if (!fallbackValue) {
|
||||
throw new Error('valueToMidi: expected freq or note to be set');
|
||||
}
|
||||
return fallbackValue;
|
||||
};
|
||||
|
||||
/**
|
||||
* @deprecated does not appear to be referenced or invoked anywhere in the codebase
|
||||
*/
|
||||
|
||||
@ -79,21 +79,25 @@ function eventLogger(type, args) {
|
||||
|
||||
async function load() {
|
||||
if (window.__csound__) {
|
||||
// allows using some other csound instance
|
||||
// Allows using some other csound instance.
|
||||
// In that case, the external Csound is responsible
|
||||
// for compiling an orchestra and starting to perform.
|
||||
logger('[load] Using external Csound', 'warning');
|
||||
_csound = window.__csound__;
|
||||
return _csound;
|
||||
} else {
|
||||
const { Csound } = await import('@csound/browser');
|
||||
_csound = await Csound({ audioContext: getAudioContext() });
|
||||
_csound.removeAllListeners('message');
|
||||
['message'].forEach((k) => _csound.on(k, (...args) => eventLogger(k, args)));
|
||||
await _csound.setOption('-m0d'); // see -m flag https://csound.com/docs/manual/CommandFlags.html
|
||||
await _csound.setOption('--sample-accurate');
|
||||
await _csound.compileCsdText(csd);
|
||||
// await _csound.compileOrc(livecodeOrc);
|
||||
await _csound.compileOrc(presetsOrc);
|
||||
await _csound.start();
|
||||
return _csound;
|
||||
}
|
||||
_csound.removeAllListeners('message');
|
||||
['message'].forEach((k) => _csound.on(k, (...args) => eventLogger(k, args)));
|
||||
await _csound.setOption('-m0d'); // see -m flag https://csound.com/docs/manual/CommandFlags.html
|
||||
await _csound.setOption('--sample-accurate');
|
||||
await _csound.compileCsdText(csd);
|
||||
// await _csound.compileOrc(livecodeOrc);
|
||||
await _csound.compileOrc(presetsOrc);
|
||||
await _csound.start();
|
||||
return _csound;
|
||||
}
|
||||
|
||||
async function init() {
|
||||
@ -118,3 +122,49 @@ export async function loadOrc(url) {
|
||||
}
|
||||
await orcCache[url];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends notes to Csound for rendering with MIDI semantics. The hap value is
|
||||
* translated to these Csound pfields:
|
||||
*
|
||||
* p1 -- Csound instrument either as a number (1-based, can be a fraction),
|
||||
* or as a string name.
|
||||
* p2 -- time in beats (usually seconds) from start of performance.
|
||||
* p3 -- duration in beats (usually seconds).
|
||||
* p4 -- MIDI key number (as a real number, not an integer but in [0, 127].
|
||||
* p5 -- MIDI velocity (as a real number, not an integer but in [0, 127].
|
||||
* p6 -- Strudel controls, as a string.
|
||||
*/
|
||||
export const csoundm = register('csoundm', (instrument, pat) => {
|
||||
let p1 = instrument;
|
||||
if (typeof instrument === 'string') {
|
||||
p1 = `"{instrument}"`;
|
||||
}
|
||||
init(); // not async to support csound inside other patterns + to be able to call pattern methods after it
|
||||
return pat.onTrigger((tidal_time, hap) => {
|
||||
if (!_csound) {
|
||||
logger('[csound] not loaded yet', 'warning');
|
||||
return;
|
||||
}
|
||||
if (typeof hap.value !== 'object') {
|
||||
throw new Error('csound only support objects as hap values');
|
||||
}
|
||||
// Time in seconds counting from now.
|
||||
const p2 = tidal_time - getAudioContext().currentTime;
|
||||
const p3 = hap.duration.valueOf() + 0;
|
||||
const frequency = getFrequency(hap);
|
||||
// Translate frequency to MIDI key number _without_ rounding.
|
||||
const C4 = 261.62558;
|
||||
let octave = Math.log(frequency / C4) / Math.log(2.0) + 8.0;
|
||||
const p4 = octave * 12.0 - 36.0;
|
||||
// We prefer floating point precision, but over the MIDI range [0, 127].
|
||||
const p5 = 127 * (hap.context?.velocity ?? 0.9);
|
||||
// The Strudel controls as a string.
|
||||
const p6 = Object.entries({ ...hap.value, frequency })
|
||||
.flat()
|
||||
.join('/');
|
||||
const i_statement = `i ${p1} ${p2} ${p3} ${p4} ${p5} "${p6}"`;
|
||||
console.log('[csoundm]:', i_statement);
|
||||
_csound.inputMessage(i_statement);
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/csound",
|
||||
"version": "0.3.0",
|
||||
"version": "0.5.1",
|
||||
"description": "csound bindings for strudel",
|
||||
"main": "csound.mjs",
|
||||
"scripts": {
|
||||
|
||||
2
packages/eval/package-lock.json
generated
2
packages/eval/package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/eval",
|
||||
"version": "0.4.1",
|
||||
"version": "0.5.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/eval",
|
||||
"version": "0.4.1",
|
||||
"version": "0.5.0",
|
||||
"description": "Code evaluator for strudel",
|
||||
"main": "index.mjs",
|
||||
"type": "module",
|
||||
@ -28,7 +28,7 @@
|
||||
},
|
||||
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "^0.4.1",
|
||||
"@strudel.cycles/core": "^0.5.0",
|
||||
"estraverse": "^5.3.0",
|
||||
"shift-ast": "^7.0.0",
|
||||
"shift-codegen": "^8.1.0",
|
||||
|
||||
2
packages/midi/package-lock.json
generated
2
packages/midi/package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/midi",
|
||||
"version": "0.4.1",
|
||||
"version": "0.5.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/midi",
|
||||
"version": "0.4.1",
|
||||
"version": "0.5.0",
|
||||
"description": "Midi API for strudel",
|
||||
"main": "index.mjs",
|
||||
"repository": {
|
||||
@ -21,7 +21,7 @@
|
||||
},
|
||||
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/tone": "^0.4.1",
|
||||
"@strudel.cycles/tone": "^0.5.0",
|
||||
"tone": "^14.7.77",
|
||||
"webmidi": "^3.0.21"
|
||||
}
|
||||
|
||||
@ -6,7 +6,6 @@ This program is free software: you can redistribute it and/or modify it under th
|
||||
|
||||
import * as krill from './krill-parser.js';
|
||||
import * as strudel from '@strudel.cycles/core';
|
||||
// import { addMiniLocations } from '@strudel.cycles/eval/shapeshifter.mjs';
|
||||
|
||||
const { pure, Fraction, stack, slowcat, sequence, timeCat, silence, reify } = strudel;
|
||||
|
||||
@ -104,11 +103,11 @@ function resolveReplications(ast) {
|
||||
});
|
||||
}
|
||||
|
||||
export function patternifyAST(ast) {
|
||||
export function patternifyAST(ast, code) {
|
||||
switch (ast.type_) {
|
||||
case 'pattern': {
|
||||
resolveReplications(ast);
|
||||
const children = ast.source_.map(patternifyAST).map(applyOptions(ast));
|
||||
const children = ast.source_.map((child) => patternifyAST(child, code)).map(applyOptions(ast));
|
||||
const alignment = ast.arguments_.alignment;
|
||||
if (alignment === 'v') {
|
||||
return stack(...children);
|
||||
@ -137,9 +136,6 @@ export function patternifyAST(ast) {
|
||||
return silence;
|
||||
}
|
||||
if (typeof ast.source_ !== 'object') {
|
||||
/* if (!addMiniLocations) {
|
||||
return ast.source_;
|
||||
} */
|
||||
if (!ast.location_) {
|
||||
console.warn('no location for', ast);
|
||||
return ast.source_;
|
||||
@ -148,12 +144,20 @@ export function patternifyAST(ast) {
|
||||
const value = !isNaN(Number(ast.source_)) ? Number(ast.source_) : ast.source_;
|
||||
// the following line expects the shapeshifter append .withMiniLocation
|
||||
// because location_ is only relative to the mini string, but we need it relative to whole code
|
||||
return pure(value).withLocation([start.line, start.column, start.offset], [end.line, end.column, end.offset]);
|
||||
// make sure whitespaces are not part of the highlight:
|
||||
const actual = code?.split('').slice(start.offset, end.offset).join('');
|
||||
const [offsetStart = 0, offsetEnd = 0] = actual
|
||||
? actual.split(ast.source_).map((p) => p.split('').filter((c) => c === ' ').length)
|
||||
: [];
|
||||
return pure(value).withLocation(
|
||||
[start.line, start.column + offsetStart, start.offset + offsetStart],
|
||||
[start.line, end.column - offsetEnd, end.offset - offsetEnd],
|
||||
);
|
||||
}
|
||||
return patternifyAST(ast.source_);
|
||||
return patternifyAST(ast.source_, code);
|
||||
}
|
||||
case 'stretch':
|
||||
return patternifyAST(ast.source_).slow(ast.arguments_.amount);
|
||||
return patternifyAST(ast.source_, code).slow(ast.arguments_.amount);
|
||||
/* case 'scale':
|
||||
let [tonic, scale] = Scale.tokenize(ast.arguments_.scale);
|
||||
const intervals = Scale.get(scale).intervals;
|
||||
@ -185,8 +189,9 @@ export function patternifyAST(ast) {
|
||||
// mini notation only (wraps in "")
|
||||
export const mini = (...strings) => {
|
||||
const pats = strings.map((str) => {
|
||||
const ast = krill.parse(`"${str}"`);
|
||||
return patternifyAST(ast);
|
||||
const code = `"${str}"`;
|
||||
const ast = krill.parse(code);
|
||||
return patternifyAST(ast, code);
|
||||
});
|
||||
return sequence(...pats);
|
||||
};
|
||||
@ -195,7 +200,7 @@ export const mini = (...strings) => {
|
||||
export const h = (string) => {
|
||||
const ast = krill.parse(string);
|
||||
// console.log('ast', ast);
|
||||
return patternifyAST(ast);
|
||||
return patternifyAST(ast, string);
|
||||
};
|
||||
|
||||
export function minify(thing) {
|
||||
|
||||
2
packages/mini/package-lock.json
generated
2
packages/mini/package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/mini",
|
||||
"version": "0.4.1",
|
||||
"version": "0.5.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/mini",
|
||||
"version": "0.4.1",
|
||||
"version": "0.5.0",
|
||||
"description": "Mini notation for strudel",
|
||||
"main": "index.mjs",
|
||||
"type": "module",
|
||||
@ -26,9 +26,9 @@
|
||||
},
|
||||
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "^0.4.1",
|
||||
"@strudel.cycles/eval": "^0.4.1",
|
||||
"@strudel.cycles/tone": "^0.4.1"
|
||||
"@strudel.cycles/core": "^0.5.0",
|
||||
"@strudel.cycles/eval": "^0.5.0",
|
||||
"@strudel.cycles/tone": "^0.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"peggy": "^2.0.1"
|
||||
|
||||
2
packages/osc/package-lock.json
generated
2
packages/osc/package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/osc",
|
||||
"version": "0.3.1",
|
||||
"version": "0.4.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/osc",
|
||||
"version": "0.3.1",
|
||||
"version": "0.4.0",
|
||||
"description": "OSC messaging for strudel",
|
||||
"main": "osc.mjs",
|
||||
"scripts": {
|
||||
|
||||
@ -25,8 +25,6 @@ const config = {
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
const osc = new OSC({ plugin: new OSC.BridgePlugin(config) });
|
||||
|
||||
osc.open(); // start a WebSocket server on port 8080
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()]
|
||||
})
|
||||
plugins: [react()],
|
||||
});
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<!-- <link rel="icon" type="image/svg+xml" href="/src/favicon.svg" /> -->
|
||||
<!-- <link rel="icon" type="image/svg+xml" href="/src/favicon.svg" /> -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Strudel React Components</title>
|
||||
</head>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/react",
|
||||
"version": "0.4.2",
|
||||
"version": "0.5.0",
|
||||
"description": "React components for strudel",
|
||||
"main": "dist/index.cjs.js",
|
||||
"module": "dist/index.es.js",
|
||||
@ -39,10 +39,10 @@
|
||||
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
||||
"dependencies": {
|
||||
"@codemirror/lang-javascript": "^6.1.1",
|
||||
"@strudel.cycles/core": "^0.4.1",
|
||||
"@strudel.cycles/tone": "^0.4.1",
|
||||
"@strudel.cycles/transpiler": "^0.4.1",
|
||||
"@strudel.cycles/webaudio": "^0.4.2",
|
||||
"@strudel.cycles/core": "^0.5.0",
|
||||
"@strudel.cycles/tone": "^0.5.0",
|
||||
"@strudel.cycles/transpiler": "^0.5.0",
|
||||
"@strudel.cycles/webaudio": "^0.5.0",
|
||||
"@uiw/codemirror-themes": "^4.12.4",
|
||||
"@uiw/react-codemirror": "^4.12.4",
|
||||
"react-hook-inview": "^4.5.0"
|
||||
|
||||
@ -9,4 +9,4 @@ module.exports = {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,5 +1,10 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import App from './App'
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import App from './App';
|
||||
|
||||
ReactDOM.render(<React.StrictMode><App /></React.StrictMode>,document.getElementById('root'))
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>,
|
||||
document.getElementById('root'),
|
||||
);
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/serial",
|
||||
"version": "0.2.0",
|
||||
"version": "0.3.0",
|
||||
"description": "Webserial API for strudel",
|
||||
"main": "serial.mjs",
|
||||
"repository": {
|
||||
|
||||
@ -23,17 +23,16 @@ export async function getWriter(br = 38400) {
|
||||
const encoder = new TextEncoder();
|
||||
const writer = port.writable.getWriter();
|
||||
writeMessage = function (message, chk) {
|
||||
const encoded = encoder.encode(message)
|
||||
const encoded = encoder.encode(message);
|
||||
if (!chk) {
|
||||
writer.write(encoded);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
const bytes = new Uint8Array(4);
|
||||
bytes[0] = 124; // | symbol
|
||||
bytes[1] = (chk >> 8) & 0xFF;
|
||||
bytes[2] = chk & 0xFF;
|
||||
bytes[1] = (chk >> 8) & 0xff;
|
||||
bytes[2] = chk & 0xff;
|
||||
bytes[3] = 59; // semicolon
|
||||
const withchk = new Uint8Array(encoded.length+4)
|
||||
const withchk = new Uint8Array(encoded.length + 4);
|
||||
withchk.set(encoded);
|
||||
withchk.set(bytes, encoded.length);
|
||||
writer.write(withchk);
|
||||
@ -48,12 +47,12 @@ const latency = 0.1;
|
||||
|
||||
// crc16 (CCITT-FALSE) https://gist.github.com/tijnkooijmans/10981093
|
||||
function crc16(data) {
|
||||
const length = data.length
|
||||
const length = data.length;
|
||||
if (length == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
var crc = 0xFFFF;
|
||||
var crc = 0xffff;
|
||||
for (var i = 0; i < length; ++i) {
|
||||
crc ^= data.charCodeAt(i) << 8;
|
||||
for (var j = 0; j < 8; ++j) {
|
||||
@ -64,7 +63,7 @@ function crc16(data) {
|
||||
return crc & 0xffff;
|
||||
}
|
||||
|
||||
Pattern.prototype.serial = function (br=38400,sendcrc=false,singlecharids=false) {
|
||||
Pattern.prototype.serial = function (br = 38400, sendcrc = false, singlecharids = false) {
|
||||
return this.withHap((hap) => {
|
||||
if (!writeMessage) {
|
||||
getWriter(br);
|
||||
@ -96,7 +95,7 @@ Pattern.prototype.serial = function (br=38400,sendcrc=false,singlecharids=false)
|
||||
}
|
||||
message += ')';
|
||||
if (sendcrc) {
|
||||
chk = crc16(message)
|
||||
chk = crc16(message);
|
||||
}
|
||||
} else {
|
||||
for (const [key, val] of Object.entries(hap.value)) {
|
||||
@ -108,7 +107,9 @@ Pattern.prototype.serial = function (br=38400,sendcrc=false,singlecharids=false)
|
||||
}
|
||||
const offset = (time - currentTime + latency) * 1000;
|
||||
|
||||
window.setTimeout(function () {writeMessage(message, chk)}, offset);
|
||||
window.setTimeout(function () {
|
||||
writeMessage(message, chk);
|
||||
}, offset);
|
||||
};
|
||||
return hap.setContext({ ...hap.context, onTrigger, dominantTrigger: true });
|
||||
});
|
||||
|
||||
@ -42,7 +42,7 @@ export const instruments = [
|
||||
'0031_FluidR3_GM_sf2_file',
|
||||
'0031_GeneralUserGS_sf2_file',
|
||||
'0031_SoundBlasterOld_sf2', // pianos until here
|
||||
'0040_Aspirin_sf2_file',
|
||||
'0040_Aspirin_sf2_file',
|
||||
'0040_Chaos_sf2_file',
|
||||
'0040_FluidR3_GM_sf2_file', // rhodes
|
||||
'0040_GeneralUserGS_sf2_file', // staccato rhodes
|
||||
@ -59,7 +59,7 @@ export const instruments = [
|
||||
'0046_GeneralUserGS_sf2_file', // ?
|
||||
'0050_Aspirin_sf2_file', // glass organ
|
||||
'0050_Chaos_sf2_file', // short glass organ
|
||||
'0050_FluidR3_GM_sf2_file',// long glass organ !
|
||||
'0050_FluidR3_GM_sf2_file', // long glass organ !
|
||||
'0050_GeneralUserGS_sf2_file', // short glass organ
|
||||
'0050_JCLive_sf2_file', // glass organ
|
||||
'0050_SBLive_sf2', // ?
|
||||
@ -1635,7 +1635,7 @@ export const drums = [
|
||||
'81_4_Chaos_sf2_file',
|
||||
];
|
||||
|
||||
// see https://www.midi.org/specifications-old/item/gm-level-1-sound-set
|
||||
// see https://www.midi.org/specifications-old/item/gm-level-1-sound-set
|
||||
export const instrumentNames = [];
|
||||
instrumentNames[0] = 'Acoustic Grand Piano: Piano';
|
||||
instrumentNames[1] = 'Bright Acoustic Piano: Piano';
|
||||
|
||||
2
packages/soundfonts/package-lock.json
generated
2
packages/soundfonts/package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/soundfonts",
|
||||
"version": "0.4.2",
|
||||
"version": "0.5.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/soundfonts",
|
||||
"version": "0.4.2",
|
||||
"version": "0.5.0",
|
||||
"description": "Soundsfont support for strudel",
|
||||
"main": "index.mjs",
|
||||
"type": "module",
|
||||
@ -22,8 +22,8 @@
|
||||
},
|
||||
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "^0.4.1",
|
||||
"@strudel.cycles/webaudio": "^0.4.2",
|
||||
"@strudel.cycles/core": "^0.5.0",
|
||||
"@strudel.cycles/webaudio": "^0.5.0",
|
||||
"sfumato": "^0.1.2",
|
||||
"soundfont2": "^0.4.0"
|
||||
},
|
||||
|
||||
2
packages/tonal/package-lock.json
generated
2
packages/tonal/package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/tonal",
|
||||
"version": "0.4.1",
|
||||
"version": "0.5.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/tonal",
|
||||
"version": "0.4.1",
|
||||
"version": "0.5.0",
|
||||
"description": "Tonal functions for strudel",
|
||||
"main": "index.mjs",
|
||||
"type": "module",
|
||||
@ -25,7 +25,7 @@
|
||||
},
|
||||
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "^0.4.1",
|
||||
"@strudel.cycles/core": "^0.5.0",
|
||||
"@tonaljs/tonal": "^4.7.2",
|
||||
"chord-voicings": "^0.0.1",
|
||||
"webmidi": "^3.0.21"
|
||||
|
||||
2
packages/tone/package-lock.json
generated
2
packages/tone/package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/tone",
|
||||
"version": "0.4.1",
|
||||
"version": "0.5.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/tone",
|
||||
"version": "0.4.1",
|
||||
"version": "0.5.0",
|
||||
"description": "Tone.js API for strudel",
|
||||
"main": "index.mjs",
|
||||
"type": "module",
|
||||
@ -22,7 +22,7 @@
|
||||
},
|
||||
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "^0.4.1",
|
||||
"@strudel.cycles/core": "^0.5.0",
|
||||
"tone": "^14.7.77"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/transpiler",
|
||||
"version": "0.4.1",
|
||||
"version": "0.5.0",
|
||||
"description": "Transpiler for strudel user code. Converts syntactically correct but semantically meaningless JS into evaluatable strudel code.",
|
||||
"main": "index.mjs",
|
||||
"scripts": {
|
||||
@ -24,7 +24,7 @@
|
||||
},
|
||||
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "^0.4.1",
|
||||
"@strudel.cycles/core": "^0.5.0",
|
||||
"acorn": "^8.8.1",
|
||||
"escodegen": "^2.0.0",
|
||||
"estree-walker": "^3.0.1"
|
||||
|
||||
2
packages/webaudio/package-lock.json
generated
2
packages/webaudio/package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/webaudio",
|
||||
"version": "0.4.2",
|
||||
"version": "0.5.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/webaudio",
|
||||
"version": "0.4.2",
|
||||
"version": "0.5.0",
|
||||
"description": "Web Audio helpers for Strudel",
|
||||
"main": "index.mjs",
|
||||
"type": "module",
|
||||
@ -30,6 +30,6 @@
|
||||
},
|
||||
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "^0.4.1"
|
||||
"@strudel.cycles/core": "^0.5.0"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { logger, toMidi } from '@strudel.cycles/core';
|
||||
import { logger, toMidi, valueToMidi } from '@strudel.cycles/core';
|
||||
import { getAudioContext } from './index.mjs';
|
||||
|
||||
const bufferCache = {}; // string: Promise<ArrayBuffer>
|
||||
@ -20,9 +20,12 @@ function humanFileSize(bytes, si) {
|
||||
return bytes.toFixed(1) + ' ' + units[u];
|
||||
}
|
||||
|
||||
export const getSampleBufferSource = async (s, n, note, speed) => {
|
||||
export const getSampleBufferSource = async (s, n, note, speed, freq) => {
|
||||
let transpose = 0;
|
||||
let midi = typeof note === 'string' ? toMidi(note) : note || 36;
|
||||
if (freq !== undefined && note !== undefined) {
|
||||
logger('[sampler] hap has note and freq. ignoring note', 'warning');
|
||||
}
|
||||
let midi = valueToMidi({ freq, note }, 36);
|
||||
transpose = midi - 36; // C3 is middle C
|
||||
|
||||
const ac = getAudioContext();
|
||||
|
||||
@ -288,10 +288,10 @@ export const webaudioOutput = async (hap, deadline, hapDuration) => {
|
||||
|
||||
if (soundfont) {
|
||||
// is soundfont
|
||||
bufferSource = await globalThis.getFontBufferSource(soundfont, note || n, ac);
|
||||
bufferSource = await globalThis.getFontBufferSource(soundfont, note || n, ac, freq);
|
||||
} else {
|
||||
// is sample from loaded samples(..)
|
||||
bufferSource = await getSampleBufferSource(s, n, note, speed);
|
||||
bufferSource = await getSampleBufferSource(s, n, note, speed, freq);
|
||||
}
|
||||
// asny stuff above took too long?
|
||||
if (ac.currentTime > t) {
|
||||
|
||||
2
packages/webdirt/package-lock.json
generated
2
packages/webdirt/package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/webdirt",
|
||||
"version": "0.4.1",
|
||||
"version": "0.5.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/webdirt",
|
||||
"version": "0.4.1",
|
||||
"version": "0.5.0",
|
||||
"description": "WebDirt integration for Strudel",
|
||||
"main": "index.mjs",
|
||||
"type": "module",
|
||||
@ -22,7 +22,7 @@
|
||||
},
|
||||
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "^0.4.1",
|
||||
"@strudel.cycles/core": "^0.5.0",
|
||||
"WebDirt": "github:dktr0/WebDirt"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/xen",
|
||||
"version": "0.4.1",
|
||||
"version": "0.5.0",
|
||||
"description": "Xenharmonic API for strudel",
|
||||
"main": "index.mjs",
|
||||
"scripts": {
|
||||
@ -24,6 +24,6 @@
|
||||
},
|
||||
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "^0.4.1"
|
||||
"@strudel.cycles/core": "^0.5.0"
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,4 +9,4 @@ module.exports = {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,13 +1,11 @@
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
sans-serif;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans',
|
||||
'Droid Sans', 'Helvetica Neue', sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||
monospace;
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { Pattern, toMidi } from '@strudel.cycles/core';
|
||||
import { Pattern, toMidi, valueToMidi } from '@strudel.cycles/core';
|
||||
import { samples } from '@strudel.cycles/webaudio';
|
||||
|
||||
export async function prebake({ isMock = false, baseDir = '.' } = {}) {
|
||||
@ -25,9 +25,9 @@ Pattern.prototype.piano = function () {
|
||||
.s('piano')
|
||||
.release(0.1)
|
||||
.fmap((value) => {
|
||||
const midi = typeof value.note === 'string' ? toMidi(value.note) : value.note;
|
||||
const midi = valueToMidi(value);
|
||||
// pan by pitch
|
||||
const pan = panwidth(Math.min(midi / maxPan, 1), 0.5);
|
||||
const pan = panwidth(Math.min(Math.round(midi) / maxPan, 1), 0.5);
|
||||
return { ...value, pan: (value.pan || 1) * pan };
|
||||
});
|
||||
};
|
||||
|
||||
@ -9,4 +9,4 @@ module.exports = {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user