Merge remote-tracking branch 'origin/HEAD' into feedbackdelay

This commit is contained in:
Felix Roos 2022-09-22 19:22:08 +02:00
commit 5c99b72e2f
10 changed files with 11442 additions and 11080 deletions

46
.github/workflows/deploy.yml vendored Normal file
View File

@ -0,0 +1,46 @@
name: Build and Deploy
on: [workflow_dispatch]
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write
deployments: write
# Allow one concurrent deployment
concurrency:
group: "pages"
cancel-in-progress: true
jobs:
build:
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v3
with:
node-version: 16
cache: "npm"
- name: Install Dependencies
run: npm ci && cd repl && npm ci && cd ../tutorial && npm ci
- name: Build
run: npm run build
- name: Setup Pages
uses: actions/configure-pages@v2
- name: Upload artifact
uses: actions/upload-pages-artifact@v1
with:
# Upload entire repository
path: "./out"
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v1

View File

@ -2,5 +2,8 @@
"cSpell.words": [ "cSpell.words": [
"subspan", "subspan",
"vals" "vals"
] ],
"yaml.schemas": {
"https://json.schemastore.org/github-workflow.json": "file:///home/felix/projects/strudel/.github/workflows/deploy.yml"
}
} }

267
package-lock.json generated
View File

@ -20,6 +20,7 @@
"jsdoc-json": "^2.0.2", "jsdoc-json": "^2.0.2",
"jsdoc-to-markdown": "^7.1.1", "jsdoc-to-markdown": "^7.1.1",
"lerna": "^4.0.0", "lerna": "^4.0.0",
"rollup-plugin-visualizer": "^5.8.1",
"vitest": "^0.21.1" "vitest": "^0.21.1"
} }
}, },
@ -4256,6 +4257,15 @@
"clone": "^1.0.2" "clone": "^1.0.2"
} }
}, },
"node_modules/define-lazy-prop": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz",
"integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==",
"dev": true,
"engines": {
"node": ">=8"
}
},
"node_modules/define-properties": { "node_modules/define-properties": {
"version": "1.1.3", "version": "1.1.3",
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
@ -6506,6 +6516,21 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/is-docker": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
"integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
"dev": true,
"bin": {
"is-docker": "cli.js"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-extglob": { "node_modules/is-extglob": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@ -6722,6 +6747,18 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/is-wsl": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
"integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
"dev": true,
"dependencies": {
"is-docker": "^2.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/isarray": { "node_modules/isarray": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
@ -7998,6 +8035,18 @@
"integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==",
"dev": true "dev": true
}, },
"node_modules/nanoid": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
"dev": true,
"bin": {
"nanoid": "bin/nanoid.cjs"
},
"engines": {
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/negotiator": { "node_modules/negotiator": {
"version": "0.6.3", "version": "0.6.3",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
@ -8575,6 +8624,23 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/open": {
"version": "8.4.0",
"resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz",
"integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==",
"dev": true,
"dependencies": {
"define-lazy-prop": "^2.0.0",
"is-docker": "^2.1.1",
"is-wsl": "^2.2.0"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/optionator": { "node_modules/optionator": {
"version": "0.8.3", "version": "0.8.3",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
@ -9178,18 +9244,6 @@
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
"dev": true "dev": true
}, },
"node_modules/postcss/node_modules/nanoid": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
"dev": true,
"bin": {
"nanoid": "bin/nanoid.cjs"
},
"engines": {
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/prelude-ls": { "node_modules/prelude-ls": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
@ -9941,6 +9995,91 @@
"fsevents": "~2.3.2" "fsevents": "~2.3.2"
} }
}, },
"node_modules/rollup-plugin-visualizer": {
"version": "5.8.1",
"resolved": "https://registry.npmjs.org/rollup-plugin-visualizer/-/rollup-plugin-visualizer-5.8.1.tgz",
"integrity": "sha512-NBT/xN/LWCwDM2/j5vYmjzpEAKHyclo/8Cv8AfTCwgADAG+tLJDy1vzxMw6NO0dSDjmTeRELD9UU3FwknLv0GQ==",
"dev": true,
"dependencies": {
"nanoid": "^3.3.4",
"open": "^8.4.0",
"source-map": "^0.7.3",
"yargs": "^17.5.1"
},
"bin": {
"rollup-plugin-visualizer": "dist/bin/cli.js"
},
"engines": {
"node": ">=14"
},
"peerDependencies": {
"rollup": "^2.0.0"
},
"peerDependenciesMeta": {
"rollup": {
"optional": true
}
}
},
"node_modules/rollup-plugin-visualizer/node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true,
"engines": {
"node": ">=8"
}
},
"node_modules/rollup-plugin-visualizer/node_modules/source-map": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
"integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==",
"dev": true,
"engines": {
"node": ">= 8"
}
},
"node_modules/rollup-plugin-visualizer/node_modules/string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/rollup-plugin-visualizer/node_modules/yargs": {
"version": "17.5.1",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz",
"integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==",
"dev": true,
"dependencies": {
"cliui": "^7.0.2",
"escalade": "^3.1.1",
"get-caller-file": "^2.0.5",
"require-directory": "^2.1.1",
"string-width": "^4.2.3",
"y18n": "^5.0.5",
"yargs-parser": "^21.0.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/rollup-plugin-visualizer/node_modules/yargs-parser": {
"version": "21.1.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
"dev": true,
"engines": {
"node": ">=12"
}
},
"node_modules/run-async": { "node_modules/run-async": {
"version": "2.4.1", "version": "2.4.1",
"resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz",
@ -15950,6 +16089,12 @@
"clone": "^1.0.2" "clone": "^1.0.2"
} }
}, },
"define-lazy-prop": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz",
"integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==",
"dev": true
},
"define-properties": { "define-properties": {
"version": "1.1.3", "version": "1.1.3",
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
@ -17575,6 +17720,12 @@
"has-tostringtag": "^1.0.0" "has-tostringtag": "^1.0.0"
} }
}, },
"is-docker": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
"integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
"dev": true
},
"is-extglob": { "is-extglob": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@ -17725,6 +17876,15 @@
"call-bind": "^1.0.2" "call-bind": "^1.0.2"
} }
}, },
"is-wsl": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
"integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
"dev": true,
"requires": {
"is-docker": "^2.0.0"
}
},
"isarray": { "isarray": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
@ -18733,6 +18893,12 @@
"integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==",
"dev": true "dev": true
}, },
"nanoid": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
"dev": true
},
"negotiator": { "negotiator": {
"version": "0.6.3", "version": "0.6.3",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
@ -19176,6 +19342,17 @@
"mimic-fn": "^2.1.0" "mimic-fn": "^2.1.0"
} }
}, },
"open": {
"version": "8.4.0",
"resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz",
"integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==",
"dev": true,
"requires": {
"define-lazy-prop": "^2.0.0",
"is-docker": "^2.1.1",
"is-wsl": "^2.2.0"
}
},
"optionator": { "optionator": {
"version": "0.8.3", "version": "0.8.3",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
@ -19551,14 +19728,6 @@
"nanoid": "^3.3.4", "nanoid": "^3.3.4",
"picocolors": "^1.0.0", "picocolors": "^1.0.0",
"source-map-js": "^1.0.2" "source-map-js": "^1.0.2"
},
"dependencies": {
"nanoid": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
"dev": true
}
} }
}, },
"postcss-js": { "postcss-js": {
@ -20263,6 +20432,64 @@
"fsevents": "~2.3.2" "fsevents": "~2.3.2"
} }
}, },
"rollup-plugin-visualizer": {
"version": "5.8.1",
"resolved": "https://registry.npmjs.org/rollup-plugin-visualizer/-/rollup-plugin-visualizer-5.8.1.tgz",
"integrity": "sha512-NBT/xN/LWCwDM2/j5vYmjzpEAKHyclo/8Cv8AfTCwgADAG+tLJDy1vzxMw6NO0dSDjmTeRELD9UU3FwknLv0GQ==",
"dev": true,
"requires": {
"nanoid": "^3.3.4",
"open": "^8.4.0",
"source-map": "^0.7.3",
"yargs": "^17.5.1"
},
"dependencies": {
"is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true
},
"source-map": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
"integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==",
"dev": true
},
"string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
"requires": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
}
},
"yargs": {
"version": "17.5.1",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz",
"integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==",
"dev": true,
"requires": {
"cliui": "^7.0.2",
"escalade": "^3.1.1",
"get-caller-file": "^2.0.5",
"require-directory": "^2.1.1",
"string-width": "^4.2.3",
"y18n": "^5.0.5",
"yargs-parser": "^21.0.0"
}
},
"yargs-parser": {
"version": "21.1.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
"dev": true
}
}
},
"run-async": { "run-async": {
"version": "2.4.1", "version": "2.4.1",
"resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz",

View File

@ -46,6 +46,7 @@
"jsdoc-json": "^2.0.2", "jsdoc-json": "^2.0.2",
"jsdoc-to-markdown": "^7.1.1", "jsdoc-to-markdown": "^7.1.1",
"lerna": "^4.0.0", "lerna": "^4.0.0",
"rollup-plugin-visualizer": "^5.8.1",
"vitest": "^0.21.1" "vitest": "^0.21.1"
} }
} }

View File

@ -1136,24 +1136,67 @@ export class Pattern {
return this.stutWith(times, time, (pat, i) => pat.velocity(Math.pow(feedback, i))); return this.stutWith(times, time, (pat, i) => pat.velocity(Math.pow(feedback, i)));
} }
// these might change with: https://github.com/tidalcycles/Tidal/issues/902 /**
* Superimpose and offset multiple times, applying the given function each time.
* @name echoWith
* @memberof Pattern
* @returns Pattern
* @param {number} times how many times to repeat
* @param {number} time cycle offset between iterations
* @param {function} func function to apply, given the pattern and the iteration index
* @example
* "<0 [2 4]>"
* .echoWith(4, 1/8, (p,n) => p.add(n*2))
* .scale('C minor').note().legato(.2).out()
*/
_echoWith(times, time, func) { _echoWith(times, time, func) {
return stack(...listRange(0, times - 1).map((i) => func(this.late(Fraction(time).mul(i)), i))); return stack(...listRange(0, times - 1).map((i) => func(this.late(Fraction(time).mul(i)), i)));
} }
/**
* Superimpose and offset multiple times, gradually decreasing the velocity
* @name echo
* @memberof Pattern
* @returns Pattern
* @example
* s("bd sd").echo(3, 1/6, .8).out()
*/
_echo(times, time, feedback) { _echo(times, time, feedback) {
return this._echoWith(times, time, (pat, i) => pat.velocity(Math.pow(feedback, i))); return this._echoWith(times, time, (pat, i) => pat.velocity(Math.pow(feedback, i)));
} }
/**
* Divides a pattern into a given number of subdivisions, plays the subdivisions in order, but increments the starting subdivision each cycle. The pattern wraps to the first subdivision after the last subdivision is played.
* @name iter
* @memberof Pattern
* @returns Pattern
* @example
* note("0 1 2 3".scale('A minor')).iter(4).out()
*/
iter(times, back = false) { iter(times, back = false) {
return slowcat(...listRange(0, times - 1).map((i) => (back ? this.late(i / times) : this.early(i / times)))); return slowcat(...listRange(0, times - 1).map((i) => (back ? this.late(i / times) : this.early(i / times))));
} }
// known as iter' in tidalcycles /**
* Like `iter`, but plays the subdivisions in reverse order. Known as iter' in tidalcycles
* @name iterBack
* @memberof Pattern
* @returns Pattern
* @example
* note("0 1 2 3".scale('A minor')).iterBack(4).out()
*/
iterBack(times) { iterBack(times) {
return this.iter(times, true); return this.iter(times, true);
} }
/**
* Divides a pattern into a given number of parts, then cycles through those parts in turn, applying the given function to each part in turn (one part per cycle).
* @name chunk
* @memberof Pattern
* @returns Pattern
* @example
* "0 1 2 3".chunk(4, x=>x.add(7)).scale('A minor').note().out()
*/
_chunk(n, func, back = false) { _chunk(n, func, back = false) {
const binary = Array(n - 1).fill(false); const binary = Array(n - 1).fill(false);
binary.unshift(true); binary.unshift(true);
@ -1161,6 +1204,14 @@ export class Pattern {
return this.when(binary_pat, func); return this.when(binary_pat, func);
} }
/**
* Like `chunk`, but cycles through the parts in reverse order. Known as chunk' in tidalcycles
* @name chunkBack
* @memberof Pattern
* @returns Pattern
* @example
* "0 1 2 3".chunkBack(4, x=>x.add(7)).scale('A minor').note().out()
*/
_chunkBack(n, func) { _chunkBack(n, func) {
return this._chunk(n, func, true); return this._chunk(n, func, true);
} }

View File

@ -170,6 +170,7 @@ function gainNode(value) {
node.gain.value = value; node.gain.value = value;
return node; return node;
} }
const cutGroups = [];
Pattern.prototype.out = function () { Pattern.prototype.out = function () {
return this.onTrigger(async (t, hap, ct, cps) => { return this.onTrigger(async (t, hap, ct, cps) => {
@ -198,8 +199,8 @@ Pattern.prototype.out = function () {
shape, shape,
pan, pan,
attack = 0.001, attack = 0.001,
decay = 0.05, decay = 0.001,
sustain = 0.5, sustain = 1,
release = 0.001, release = 0.001,
speed = 1, // sample playback speed speed = 1, // sample playback speed
begin = 0, begin = 0,
@ -208,6 +209,10 @@ Pattern.prototype.out = function () {
delay = 0, delay = 0,
delayfeedback = 0, delayfeedback = 0,
delaytime = 0, delaytime = 0,
unit,
nudge = 0, // TODO: is this in seconds?
cut,
loop,
} = hap.value; } = hap.value;
const { velocity = 1 } = hap.context; const { velocity = 1 } = hap.context;
gain *= velocity; // legacy fix for velocity gain *= velocity; // legacy fix for velocity
@ -273,28 +278,33 @@ Pattern.prototype.out = function () {
return; return;
} }
bufferSource.playbackRate.value = Math.abs(speed) * bufferSource.playbackRate.value; bufferSource.playbackRate.value = Math.abs(speed) * bufferSource.playbackRate.value;
// TODO: nudge, unit, cut, loop if (unit === 'c') {
let duration = soundfont || clip ? hapDuration : bufferSource.buffer.duration; // are there other units?
// let duration = bufferSource.buffer.duration; bufferSource.playbackRate.value = bufferSource.playbackRate.value * bufferSource.buffer.duration;
const offset = begin * duration; }
duration = ((end - begin) * duration) / Math.abs(speed); let duration = soundfont || clip ? hapDuration : bufferSource.buffer.duration / bufferSource.playbackRate.value;
if (soundfont || clip) { // "The computation of the offset into the sound is performed using the sound buffer's natural sample rate,
bufferSource.start(t, offset); // duration does not work here for some reason // rather than the current playback rate, so even if the sound is playing at twice its normal speed,
} else { // the midway point through a 10-second audio buffer is still 5."
bufferSource.start(t, offset, duration); const offset = begin * duration * bufferSource.playbackRate.value;
duration = (end - begin) * duration;
if (loop) {
bufferSource.loop = true;
bufferSource.loopStart = offset;
bufferSource.loopEnd = offset + duration;
duration = loop * duration;
}
t += nudge;
bufferSource.start(t, offset);
if (cut !== undefined) {
cutGroups[cut]?.stop(t); // fade out?
cutGroups[cut] = bufferSource;
} }
chain.push(bufferSource); chain.push(bufferSource);
if (soundfont || clip) { bufferSource.stop(t + duration + release);
const releaseLength = 0.1; const adsr = getADSR(attack, decay, sustain, release, 1, t, t + duration);
const env = gainNode(0.6); chain.push(adsr);
env.gain.setValueAtTime(env.gain.value, t + duration);
env.gain.linearRampToValueAtTime(0, t + duration + releaseLength);
// env.gain.linearRampToValueAtTime(0, t + duration + releaseLength);
chain.push(env);
bufferSource.stop(t + duration + releaseLength);
} else {
bufferSource.stop(t + duration);
}
} }
// level down before effects // level down before effects
chain.push(gainNode(gain)); chain.push(gainNode(gain));

View File

@ -214,6 +214,19 @@ function App() {
<>loading...</> <>loading...</>
)} )}
</button> </button>
<button
onClick={() => {
dirty && activateCode();
pushLog('Code updated! Tip: You can also update the code by pressing ctrl+enter.');
}}
className={cx(
'hover:bg-gray-300',
!isEmbedded ? 'p-2' : 'px-2',
!dirty || !activeCode ? 'opacity-50' : '',
)}
>
🔄 update
</button>
{!isEmbedded && ( {!isEmbedded && (
<button <button
className="hover:bg-gray-300 p-2" className="hover:bg-gray-300 p-2"

View File

@ -54,6 +54,7 @@ const panwidth = (pan, width) => pan * width + (1 - width) / 2;
Pattern.prototype.piano = function () { Pattern.prototype.piano = function () {
return this.clip(1) return this.clip(1)
.s('piano') .s('piano')
.release(.1)
.fmap((value) => { .fmap((value) => {
const midi = typeof value.note === 'string' ? toMidi(value.note) : value.note; const midi = typeof value.note === 'string' ? toMidi(value.note) : value.note;
// pan by pitch // pan by pitch

File diff suppressed because it is too large Load Diff

View File

@ -502,6 +502,10 @@ The following functions modify a pattern temporal structure in some way.
{{ 'Pattern.legato' | jsdoc }} {{ 'Pattern.legato' | jsdoc }}
{{ 'Pattern.iter' | jsdoc }}
{{ 'Pattern.iterBack' | jsdoc }}
## Conditional Modifiers ## Conditional Modifiers
{{ 'Pattern.every' | jsdoc }} {{ 'Pattern.every' | jsdoc }}
@ -520,6 +524,10 @@ The following functions modify a pattern temporal structure in some way.
{{ 'Pattern.off' | jsdoc }} {{ 'Pattern.off' | jsdoc }}
{{ 'Pattern.echo' | jsdoc }}
{{ 'Pattern.echoWith' | jsdoc }}
## Concat Modifiers ## Concat Modifiers
{{ 'Pattern.seq' | jsdoc }} {{ 'Pattern.seq' | jsdoc }}
@ -588,13 +596,15 @@ Like layer, but with a single function:
{{ 'Pattern.range' | jsdoc }} {{ 'Pattern.range' | jsdoc }}
{{ 'Pattern.chunk' | jsdoc }}
{{ 'Pattern.chunkBack' | jsdoc }}
## Continuous Signals ## Continuous Signals
Signals are patterns with continuous values, meaning they have theoretically infinite steps. Signals are patterns with continuous values, meaning they have theoretically infinite steps.
They can provide streams of numbers that can be sampled at discrete points in time. They can provide streams of numbers that can be sampled at discrete points in time.
##
{{ 'saw' | jsdoc }} {{ 'saw' | jsdoc }}
{{ 'sine' | jsdoc }} {{ 'sine' | jsdoc }}