├── .github
├── FUNDING.yaml
└── workflows
│ └── ci.yml
├── .gitignore
├── .vscode
└── settings.json
├── LICENSE
├── README.md
├── js
├── LICENSE
├── README.md
├── assets
│ ├── 2json.js
│ ├── 808bd.wav
│ ├── 808ch.wav
│ ├── 808clap.wav
│ ├── 808ht.wav
│ ├── 808lt.wav
│ ├── 808oh.wav
│ ├── 808sd.wav
│ ├── bass3.wav
│ ├── bin.wav
│ ├── blip.wav
│ ├── boip.wav
│ ├── bong.wav
│ ├── bpblart.wav
│ ├── can.wav
│ ├── casiohigh.wav
│ ├── casiolow.wav
│ ├── casionoise.wav
│ ├── cb.wav
│ ├── closedhh.wav
│ ├── clubkick.wav
│ ├── crash.wav
│ ├── echo.wav
│ ├── fat808sub.wav
│ ├── giveit.wav
│ ├── glass.wav
│ ├── guitar.wav
│ ├── hit1.wav
│ ├── hit2.wav
│ ├── hit3.wav
│ ├── industrial.wav
│ ├── junglesine.wav
│ ├── kick1.wav
│ ├── kick2.wav
│ ├── latibro.wav
│ ├── moog.wav
│ ├── openhh.wav
│ ├── pad.wav
│ ├── perc1.wav
│ ├── perc2.wav
│ ├── pluck.wav
│ ├── ride.wav
│ ├── rm.wav
│ ├── sax.wav
│ ├── sid.wav
│ ├── snare1.wav
│ ├── snare2.wav
│ ├── stab.wav
│ ├── talk1.wav
│ ├── talk2.wav
│ ├── tink.wav
│ ├── tok.wav
│ ├── ufo.wav
│ ├── whoosh.wav
│ └── wind.wav
├── index.html
├── main.js
├── neo.css
├── npm
│ ├── README.md
│ ├── detect.js
│ ├── glicol-engine.js
│ ├── glicol_wasm.js
│ ├── glicol_wasm_bg.wasm
│ ├── index.js
│ ├── nodechain.js
│ ├── nosab.js
│ ├── package-lock.json
│ ├── package.json
│ └── ringbuf.js
├── package-lock.json
├── package.json
├── pnpm-lock.yaml
├── src
│ ├── glicol-api.json
│ ├── glicol-engine.js
│ ├── glicol-mode.js
│ ├── glicol.js
│ ├── glicol_wasm.js
│ ├── glicol_wasm_bg.wasm
│ ├── sample-list.json
│ └── utils.js
├── style.css
└── vite.config.js
└── rs
├── Cargo.toml
├── LICENSE
├── README.md
├── main
├── Cargo.toml
├── LICENSE
├── README.md
├── examples
│ ├── expr.rs
│ ├── hello.rs
│ ├── input.rs
│ ├── msg_synth.rs
│ ├── p_synth.rs
│ ├── plot.rs
│ ├── plpf.rs
│ ├── raw.rs
│ └── update.rs
├── src
│ ├── error.rs
│ ├── lib.rs
│ └── util.rs
└── tests
│ └── sin.rs
├── parser
├── Cargo.toml
├── LICENSE
├── examples
│ ├── choose.rs
│ ├── hello.rs
│ ├── lcs.rs
│ ├── pattern.rs
│ ├── psampler.rs
│ ├── seq.rs
│ └── test.rs
├── src
│ ├── glicol.pest
│ ├── lib.rs
│ ├── nodes.rs
│ └── util.rs
└── tests
│ ├── all_nodes.rs
│ └── sin.rs
├── synth
├── Cargo.toml
├── LICENSE
├── README.md
├── benches
│ ├── fm.rs
│ └── next_block.rs
├── examples
│ ├── chain.rs
│ ├── connect.rs
│ ├── freeverb.rs
│ ├── gnode.rs
│ ├── hello.rs
│ ├── plot-am.rs
│ ├── plot-g.rs
│ ├── plot-imp.rs
│ ├── plot.rs
│ ├── plot2.rs
│ ├── plot3.rs
│ ├── plot_psynth.rs
│ └── sin.rs
└── src
│ ├── buffer.rs
│ ├── context.rs
│ ├── graph.rs
│ ├── lib.rs
│ └── node
│ ├── boxed.rs
│ ├── compound
│ ├── bd.rs
│ ├── hh.rs
│ ├── mod.rs
│ ├── sawsynth.rs
│ ├── sn.rs
│ ├── squsynth.rs
│ └── trisynth.rs
│ ├── delay
│ ├── delayms.rs
│ ├── delayn.rs
│ └── mod.rs
│ ├── dynamic
│ ├── eval.rs
│ ├── expr.rs
│ ├── meta.rs
│ └── mod.rs
│ ├── effect
│ ├── balance.rs
│ ├── mod.rs
│ ├── pan.rs
│ ├── plate.rs
│ └── reverb.rs
│ ├── envelope
│ ├── adsr.rs
│ ├── envperc.rs
│ └── mod.rs
│ ├── filter
│ ├── apfmsgain.rs
│ ├── mod.rs
│ ├── onepole.rs
│ ├── rhpf.rs
│ └── rlpf.rs
│ ├── mod.rs
│ ├── operator
│ ├── add.rs
│ ├── mod.rs
│ └── mul.rs
│ ├── oscillator
│ ├── mod.rs
│ ├── saw_osc.rs
│ ├── sin_osc.rs
│ ├── squ_osc.rs
│ └── tri_osc.rs
│ ├── pass.rs
│ ├── sampling
│ ├── mod.rs
│ ├── psampler.rs
│ └── sampler.rs
│ ├── sequencer
│ ├── arrange.rs
│ ├── choose.rs
│ ├── mod.rs
│ ├── seq.rs
│ └── speed.rs
│ ├── signal
│ ├── constsig.rs
│ ├── imp.rs
│ ├── mod.rs
│ ├── noise.rs
│ ├── phasor.rs
│ └── points.rs
│ ├── sum.rs
│ └── synth
│ ├── mod.rs
│ ├── msgsynth.rs
│ └── pattern_synth.rs
└── wasm
├── Cargo.toml
├── LICENSE
├── build.bat
├── build.sh
└── src
└── lib.rs
/.github/FUNDING.yaml:
--------------------------------------------------------------------------------
1 | github: [chaosprint]
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 | branches:
9 | - main
10 |
11 | jobs:
12 | clippy-test:
13 | runs-on: ubuntu-latest
14 | steps:
15 | - uses: actions/checkout@v4
16 | # - name: Update apt
17 | # run: sudo apt update
18 | # - name: Install alsa
19 | # run: sudo apt-get install libasound2-dev
20 | # - name: Install libjack
21 | # run: sudo apt-get install libjack-jackd2-dev libjack-jackd2-0
22 | - name: Install stable
23 | uses: dtolnay/rust-toolchain@stable
24 | with:
25 | components: clippy
26 | - name: Run clippy
27 | working-directory: rs
28 | run: cargo clippy --workspace --exclude glicol-wasm
29 | # --exclude glicol-wasm
30 | rustfmt-check:
31 | runs-on: ubuntu-latest
32 | steps:
33 | - uses: actions/checkout@v4
34 | - name: Install stable
35 | uses: dtolnay/rust-toolchain@stable
36 | with:
37 | components: rustfmt
38 | - name: Run rustfmt
39 | working-directory: rs
40 | run: cargo fmt --all -- --check
41 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "rust-analyzer.linkedProjects": ["rs/Cargo.toml"]
3 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2020-present Qichao Lan (chaopsrint)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/js/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2020-present Qichao Lan (chaopsrint)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/js/README.md:
--------------------------------------------------------------------------------
1 | ## What's this?
2 |
3 | This folder contains the JavaScript bindings for [Glicol](https://glicol.org) language and audio engine.
4 |
5 | So, you can now use Glicol as the audio engien for your own browser-based music app.
6 |
7 | There are two usages: `NPM` or `CDN`.
8 |
9 | > Note that you need to have `cross-origin isolation` enabled on the web server to use Glicol. For vite dev server, you can use my plugin [here](https://github.com/chaosprint/vite-plugin-cross-origin-isolation). For deployment on Netlify or Firebase, check their docs for editing the header files. If you use a customised server, you have to figure it out yourself.
10 |
11 | ## Usage - NPM
12 |
13 | See the [https://glicol.js.org/](https://glicol.js.org/) for detailed introduction.
14 |
15 | ## Usage - CDN
16 |
17 | This mode exposes all the methods such as `run` or `stop` to the `window` Object.
18 |
19 | Just include the following line into your `index.html`:
20 |
21 | ```
22 |
23 | ```
24 |
25 | The `run()` function is bind to the window.
26 |
27 | You can map it to buttons on the page or even do live coding in the browser console.
28 |
29 | Call it for the first time will run the code:
30 | ```run(`hello: sin 440`)```
31 |
32 | Glicol engine knows you are updating the code if you call the func again.
33 |
34 | Call `stop()` function will restart the engine.
35 |
36 | To run the demo in this folder:
37 | ```
38 | npm i
39 | npm run dev
40 | ```
41 |
42 | ## License
43 |
44 | The MIT License (MIT)
45 |
46 | Copyright (c) 2020 - present Qichao Lan (chaosprint)
47 |
--------------------------------------------------------------------------------
/js/assets/2json.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const folder = './';
3 | var info = Object();
4 | var source = "https://github.com/chaosprint/Dirt-Samples"
5 | fs.readdir(folder, (_err, files) => {
6 | // info["selectedFromDirtSamples"] = files.filter(x=>x!=='.DS_Store')
7 | files.forEach(file => {
8 | if (file !== '.DS_Store' && file !== "2json.js") {
9 | info[file.replace(".wav", "")] = source
10 | }
11 | });
12 | let json = JSON.stringify(info)
13 | var outputFilename = '../src/sample-list.json';
14 | fs.writeFile(outputFilename, json, function(err) {
15 | if(err) {
16 | console.log(err);
17 | } else {
18 | console.log("JSON saved to " + outputFilename);
19 | }
20 | });
21 | });
22 |
--------------------------------------------------------------------------------
/js/assets/808bd.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/808bd.wav
--------------------------------------------------------------------------------
/js/assets/808ch.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/808ch.wav
--------------------------------------------------------------------------------
/js/assets/808clap.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/808clap.wav
--------------------------------------------------------------------------------
/js/assets/808ht.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/808ht.wav
--------------------------------------------------------------------------------
/js/assets/808lt.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/808lt.wav
--------------------------------------------------------------------------------
/js/assets/808oh.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/808oh.wav
--------------------------------------------------------------------------------
/js/assets/808sd.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/808sd.wav
--------------------------------------------------------------------------------
/js/assets/bass3.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/bass3.wav
--------------------------------------------------------------------------------
/js/assets/bin.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/bin.wav
--------------------------------------------------------------------------------
/js/assets/blip.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/blip.wav
--------------------------------------------------------------------------------
/js/assets/boip.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/boip.wav
--------------------------------------------------------------------------------
/js/assets/bong.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/bong.wav
--------------------------------------------------------------------------------
/js/assets/bpblart.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/bpblart.wav
--------------------------------------------------------------------------------
/js/assets/can.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/can.wav
--------------------------------------------------------------------------------
/js/assets/casiohigh.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/casiohigh.wav
--------------------------------------------------------------------------------
/js/assets/casiolow.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/casiolow.wav
--------------------------------------------------------------------------------
/js/assets/casionoise.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/casionoise.wav
--------------------------------------------------------------------------------
/js/assets/cb.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/cb.wav
--------------------------------------------------------------------------------
/js/assets/closedhh.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/closedhh.wav
--------------------------------------------------------------------------------
/js/assets/clubkick.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/clubkick.wav
--------------------------------------------------------------------------------
/js/assets/crash.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/crash.wav
--------------------------------------------------------------------------------
/js/assets/echo.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/echo.wav
--------------------------------------------------------------------------------
/js/assets/fat808sub.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/fat808sub.wav
--------------------------------------------------------------------------------
/js/assets/giveit.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/giveit.wav
--------------------------------------------------------------------------------
/js/assets/glass.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/glass.wav
--------------------------------------------------------------------------------
/js/assets/guitar.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/guitar.wav
--------------------------------------------------------------------------------
/js/assets/hit1.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/hit1.wav
--------------------------------------------------------------------------------
/js/assets/hit2.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/hit2.wav
--------------------------------------------------------------------------------
/js/assets/hit3.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/hit3.wav
--------------------------------------------------------------------------------
/js/assets/industrial.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/industrial.wav
--------------------------------------------------------------------------------
/js/assets/junglesine.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/junglesine.wav
--------------------------------------------------------------------------------
/js/assets/kick1.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/kick1.wav
--------------------------------------------------------------------------------
/js/assets/kick2.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/kick2.wav
--------------------------------------------------------------------------------
/js/assets/latibro.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/latibro.wav
--------------------------------------------------------------------------------
/js/assets/moog.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/moog.wav
--------------------------------------------------------------------------------
/js/assets/openhh.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/openhh.wav
--------------------------------------------------------------------------------
/js/assets/pad.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/pad.wav
--------------------------------------------------------------------------------
/js/assets/perc1.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/perc1.wav
--------------------------------------------------------------------------------
/js/assets/perc2.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/perc2.wav
--------------------------------------------------------------------------------
/js/assets/pluck.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/pluck.wav
--------------------------------------------------------------------------------
/js/assets/ride.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/ride.wav
--------------------------------------------------------------------------------
/js/assets/rm.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/rm.wav
--------------------------------------------------------------------------------
/js/assets/sax.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/sax.wav
--------------------------------------------------------------------------------
/js/assets/sid.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/sid.wav
--------------------------------------------------------------------------------
/js/assets/snare1.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/snare1.wav
--------------------------------------------------------------------------------
/js/assets/snare2.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/snare2.wav
--------------------------------------------------------------------------------
/js/assets/stab.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/stab.wav
--------------------------------------------------------------------------------
/js/assets/talk1.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/talk1.wav
--------------------------------------------------------------------------------
/js/assets/talk2.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/talk2.wav
--------------------------------------------------------------------------------
/js/assets/tink.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/tink.wav
--------------------------------------------------------------------------------
/js/assets/tok.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/tok.wav
--------------------------------------------------------------------------------
/js/assets/ufo.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/ufo.wav
--------------------------------------------------------------------------------
/js/assets/whoosh.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/whoosh.wav
--------------------------------------------------------------------------------
/js/assets/wind.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/assets/wind.wav
--------------------------------------------------------------------------------
/js/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | glicol.js minimal demo
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/js/main.js:
--------------------------------------------------------------------------------
1 | import mode from './src/glicol-mode'
2 | import './neo.css'
3 | import './style.css'
4 | let myTextarea = document.getElementById("code");
5 | CodeMirror.defineSimpleMode("simplemode", mode);
6 | window.editor = CodeMirror.fromTextArea(myTextarea, {
7 | lineNumbers: true,
8 | theme: "neo",
9 | extraKeys: {
10 | "Ctrl-Enter": function(cm) {
11 | window.run(cm.getValue())
12 | },
13 | "Ctrl-Shift-Enter": function(cm) {
14 | window.run(cm.getValue())
15 | cm.setValue(window.code)
16 | },
17 | "Cmd-Enter": function(cm) {
18 | window.run(cm.getValue())
19 | },
20 | "Cmd-Shift-Enter": function(cm) {
21 | window.run(cm.getValue())
22 | cm.setValue(window.code)
23 | },
24 | "Alt-D": function(editor) {
25 | let A1 = editor.getCursor().line;
26 | let A2 = editor.getCursor().ch;
27 | let B1 = editor.findWordAt({line: A1, ch: A2}).anchor.ch;
28 | let B2 = editor.findWordAt({line: A1, ch: A2}).head.ch;
29 | window.help(editor.getRange({line: A1,ch: B1}, {line: A1,ch: B2}))
30 | },
31 | "Ctrl-Alt-.": function() {
32 | window.stop()
33 | },
34 | "Cmd-Alt-.": function() {
35 | window.stop()
36 | },
37 | 'Ctrl-/': cm => {cm.execCommand('toggleComment')},
38 | 'Cmd-/': cm => {cm.execCommand('toggleComment')}
39 | }
40 |
41 | });
42 | editor.setValue(window.code)
43 | document.getElementById("run").addEventListener("click", ()=>{
44 | // let currentCode = document.getElementById("code").value;
45 | window.run(window.editor.getValue())
46 | })
47 | document.getElementById("stop").addEventListener("click", ()=>{
48 | window.stop(); // stop function binding to window
49 | })
50 |
--------------------------------------------------------------------------------
/js/neo.css:
--------------------------------------------------------------------------------
1 | /* neo theme for codemirror */
2 |
3 | /* Color scheme */
4 |
5 | .cm-s-neo.CodeMirror {
6 | background-color:#ffffff;
7 | color:#2e383c;
8 | line-height:1.4375;
9 | }
10 | .cm-s-neo .cm-comment { color:#9b9999; }
11 | .cm-s-neo .cm-error { color:#505050; }
12 | .cm-s-neo .cm-keyword, .cm-s-neo .cm-property { color:#7176a0; }
13 | .cm-s-neo .cm-atom,.cm-s-neo .cm-number { color:#75438a; }
14 | .cm-s-neo .cm-node,.cm-s-neo .cm-tag { color:#9c3328; }
15 | .cm-s-neo .cm-string { color:#c24088; }
16 | .cm-s-neo .cm-variable,.cm-s-neo .cm-qualifier { color:#3b7074; }
17 | .cm-s-neo .cm-variable-2,.cm-s-neo .cm-qualifier { color:#166aaf; }
18 |
19 | /* Editor styling */
20 |
21 | .cm-s-neo pre {
22 | padding:0;
23 | }
24 |
25 | .cm-s-neo .CodeMirror-gutters {
26 | border:none;
27 | border-right:10px solid transparent;
28 | background-color:transparent;
29 | }
30 |
31 | .cm-s-neo .CodeMirror-linenumber {
32 | padding:0;
33 | color:#e0e2e5;
34 | }
35 |
36 | .cm-s-neo .CodeMirror-guttermarker { color: #1d75b3; }
37 | .cm-s-neo .CodeMirror-guttermarker-subtle { color: #e0e2e5; }
38 |
39 | .cm-s-neo .CodeMirror-cursor {
40 | width: auto;
41 | border: 0;
42 | background: rgba(155,157,162,0.37);
43 | z-index: 1;
44 | }
--------------------------------------------------------------------------------
/js/npm/README.md:
--------------------------------------------------------------------------------
1 | ## What's this?
2 |
3 | See: https://glicol.js.org
4 |
5 | ## Feedback
6 |
7 | There are many todos for this package. Please let me know your thoughts and suggestions here:
8 |
9 | https://github.com/chaosprint/glicol
10 |
11 | `Issues` or `Discussion` are both fine.
12 |
13 | ## Dev note (not for users)
14 | ```
15 | sudo pnpm link --dir /usr/local/lib/ glicol
16 | ```
--------------------------------------------------------------------------------
/js/npm/detect.js:
--------------------------------------------------------------------------------
1 | export const detectOs = () => {
2 | var userAgent = window.navigator.userAgent,
3 | platform = window.navigator.platform,
4 | macosPlatforms = ['Macintosh', 'MacIntel', 'MacPPC', 'Mac68K'],
5 | windowsPlatforms = ['Win32', 'Win64', 'Windows', 'WinCE'],
6 | iosPlatforms = ['iPhone', 'iPad', 'iPod'],
7 | os = null;
8 | if (macosPlatforms.indexOf(platform) !== -1) {
9 | os = 'Mac OS';
10 | } else if (iosPlatforms.indexOf(platform) !== -1) {
11 | os = 'iOS';
12 | } else if (windowsPlatforms.indexOf(platform) !== -1) {
13 | os = 'Windows';
14 | } else if (/Android/.test(userAgent)) {
15 | os = 'Android';
16 | } else if (!os && /Linux/.test(platform)) {
17 | os = 'Linux';
18 | }
19 | return os;
20 | }
21 |
22 | export const detectBrowser = () => {
23 | const { userAgent } = navigator
24 | // alert(userAgent)
25 | // alert(detectOs());
26 | let name = "";
27 | let version = "0.0";
28 | if (userAgent.includes('Firefox/')) {
29 | // Firefox
30 | name = detectOs() === "Android" ? "Firefox for Android": "Firefox"
31 | version = userAgent.split("Firefox/")[1]
32 | // } else if (userAgent.includes('Edg/')) {
33 | // name = "Edge"
34 | } else if (userAgent.includes('Chrome/')) {
35 | name = detectOs() === "Android" ? "Chrome for Android": "Chrome"
36 | version = userAgent.split("Chrome/")[1].split(" ")[0].split(".")[0]
37 | } else if (userAgent.includes('Safari/') && userAgent.includes('Version/') ) {
38 | name = detectOs() === "iOS" ? "Safari on iOS": "Safari"
39 | version = userAgent.split("Version/")[1].split(" ")[0]
40 | }
41 | return {
42 | name: name,
43 | version: parseFloat(version)
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/js/npm/glicol_wasm_bg.wasm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/npm/glicol_wasm_bg.wasm
--------------------------------------------------------------------------------
/js/npm/nodechain.js:
--------------------------------------------------------------------------------
1 | const isRef = s => String(s).includes('~')
2 |
3 | export function sin(freq) {
4 | if (!isNaN(freq) || isRef(freq)) {
5 | return new NodeChain(`sin ${freq}`)
6 | }
7 | }
8 |
9 | export function saw(freq) {
10 | if (!isNaN(freq) || isRef(freq)) {
11 | return new NodeChain(`saw ${freq}`)
12 | }
13 | }
14 | export function tri(freq) {
15 | if (!isNaN(freq) || isRef(freq)) {
16 | return new NodeChain(`tri ${freq}`)
17 | }
18 | }
19 |
20 | export function squ(freq) {
21 | if (!isNaN(freq) || isRef(freq)) {
22 | return new NodeChain(`squ ${freq}`)
23 | }
24 | }
25 |
26 | export function imp(freq) {
27 | if (!isNaN(freq)) {
28 | return new NodeChain(`imp ${freq}`)
29 | }
30 | }
31 |
32 | export function noise(seed) {
33 | if (!isNaN(seed)) {
34 | return new NodeChain(`noise ${seed}`)
35 | }
36 | }
37 |
38 | export function speed(val) {
39 | if (!isNaN(val)) {
40 | return new NodeChain(`speed ${val}`)
41 | }
42 | }
43 |
44 | export function seq(str) {
45 | // if (!isNaN(seed)) {
46 | return new NodeChain(`seq ${str}`)
47 | // }
48 | }
49 |
50 | export function psynth(str, span) {
51 | if (!isNaN(span)) {
52 | return new NodeChain(`p_synth \`${str} ${span}`)
53 | }
54 | }
55 |
56 | export function psampler(str) {
57 | return new NodeChain(`psampler ${str}`)
58 | }
59 |
60 | export function sig(param) {
61 | return new NodeChain(`constsig ${param}`)
62 | }
63 |
64 | export function mix(str) {
65 | // var result;
66 | // if (typeof str === "Array") {
67 | // result = str.join(" ")
68 | // } else if (typeof str === "String") {
69 | // result = str
70 | // }
71 | return new NodeChain(`mix ${str}`)
72 | }
73 |
74 | export class NodeChain {
75 | constructor(code) {
76 | this.code = code
77 | }
78 |
79 | toString() {
80 | return `${this.code}`;
81 | };
82 |
83 | mul(val) {
84 | if (!isNaN(val) || isRef(val)) {
85 | this.code += ` >> mul ${val}`
86 | }
87 | return this
88 | }
89 | add(val) {
90 | if (!isNaN(val) || isRef(val)) {
91 | this.code += ` >> add ${val}`
92 | }
93 | return this
94 | }
95 |
96 | delayms(val) {
97 | if (!isNaN(val) || isRef(val)) {
98 | this.code += ` >> delayms ${val}`
99 | }
100 | return this
101 | }
102 |
103 | delayn(val) {
104 | if (!isNaN(val)) {
105 | this.code += ` >> delayn ${parseInt(val)}`
106 | }
107 | return this
108 | }
109 |
110 | lpf(cutoff, qvalue) {
111 | // if ( (!isNaN(cutoff) || isRef(cutoff)) && (!isNaN(qvalue))) {
112 | this.code += ` >> lpf ${cutoff} ${qvalue}`
113 | // }
114 | return this
115 | }
116 |
117 | hpf(cutoff, qvalue) {
118 | if ( (!isNaN(cutoff) || isRef(cutoff)) && (!isNaN(qvalue))) {
119 | this.code += ` >> hpf ${cutoff} ${qvalue}`
120 | }
121 | return this
122 | }
123 |
124 | plate(val) {
125 | if (!isNaN(val)) {
126 | this.code += ` >> plate ${val}`
127 | }
128 | return this
129 | }
130 |
131 | bd(val) {
132 | if (!isNaN(val)) {
133 | this.code += ` >> bd ${val}`
134 | }
135 | return this
136 | }
137 |
138 | sn(val) {
139 | if (!isNaN(val)) {
140 | this.code += ` >> sn ${val}`
141 | }
142 | return this
143 | }
144 |
145 | hh(val) {
146 | if (!isNaN(val)) {
147 | this.code += ` >> hh ${val}`
148 | }
149 | return this
150 | }
151 |
152 | sawsynth(att, dec) {
153 | if (!isNaN(att) && !isNaN(dec)) {
154 | this.code += ` >> sawsynth ${att} ${dec}`
155 | }
156 | return this
157 | }
158 |
159 | squsynth(att, dec) {
160 | if (!isNaN(att) && !isNaN(dec)) {
161 | this.code += ` >> squsynth ${att} ${dec}`
162 | }
163 | return this
164 | }
165 |
166 | trisynth(att, dec) {
167 | if (!isNaN(att) && !isNaN(dec)) {
168 | this.code += ` >> trisynth ${att} ${dec}`
169 | }
170 | return this
171 | }
172 |
173 | seq(str) {
174 | // if (!isNaN(str)) {
175 | this.code += ` >> seq ${str}`
176 | // }
177 | return this
178 | }
179 |
180 | adsr(a, d, s, r) {
181 | // if (!isNaN(str)) {
182 | this.code += ` >> adsr ${a} ${d} ${s} ${r}`
183 | // }
184 | return this
185 | }
186 |
187 | sp(sampleName) {
188 | // if (!isNaN(str)) {
189 | this.code += ` >> sp \\${sampleName}`
190 | // }
191 | return this
192 | }
193 |
194 | envperc(attack, decay) {
195 | // if (!isNaN(str)) {
196 | this.code += ` >> envperc ${attack} ${decay}`
197 | // }
198 | return this
199 | }
200 |
201 | }
--------------------------------------------------------------------------------
/js/npm/nosab.js:
--------------------------------------------------------------------------------
1 | export default `To get the best audio performance for browsers, we need SharedArrayBuffer (SAB). However, it may not be supported in current browser (see https://caniuse.com/?search=sharedarraybuffer). Also, SharedArrayBuffer requires 'cross-origin isolation', either on the dev or deployment server (see https://web.dev/coop-coep/).`
--------------------------------------------------------------------------------
/js/npm/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "glicol",
3 | "version": "0.2.11",
4 | "lockfileVersion": 2,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "glicol",
9 | "version": "0.2.11",
10 | "license": "MIT"
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/js/npm/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "glicol",
3 | "version": "0.4.0",
4 | "description": "A light-weight, audio-thread-GC-free, memory-safe, and easy-to-use audio engine for browser-based music apps.",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://github.com/chaosprint/glicol.git"
12 | },
13 | "keywords": [
14 | "audio",
15 | "music",
16 | "live",
17 | "coding",
18 | "synth",
19 | "sound",
20 | "tone",
21 | "glicol"
22 | ],
23 | "author": "chaosprint (Qichao Lan)",
24 | "license": "MIT",
25 | "bugs": {
26 | "url": "https://github.com/chaosprint/glicol/issues"
27 | },
28 | "homepage": "https://glicol.js.org"
29 | }
--------------------------------------------------------------------------------
/js/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "glicol",
3 | "version": "0.0.0",
4 | "description": "Glicol as an independent JavaScript library.",
5 | "scripts": {
6 | "dev": "vite",
7 | "build": "vite build",
8 | "serve": "vite preview"
9 | },
10 | "devDependencies": {
11 | "vite": "^2.3.0"
12 | },
13 | "dependencies": {
14 | "install": "^0.13.0"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/js/src/glicol-engine.js:
--------------------------------------------------------------------------------
1 | import { initSync, set_sr, set_seed, add_sample, set_bpm, set_track_amp, update, process } from "./glicol_wasm.js"
2 |
3 | class GlicolEngine extends AudioWorkletProcessor {
4 | static get parameterDescriptors() {
5 | return []
6 | }
7 | constructor(options) {
8 | super(options)
9 | const { wasmBlob } = options.processorOptions;
10 | initSync(wasmBlob);
11 | this.port.onmessage = async e => {
12 | if (e.data.type === "load") {
13 | set_sr(sampleRate)
14 | set_seed(Math.random() * 4096)
15 | this.port.postMessage({type: 'ready'})
16 | } else if (e.data.type === "loadsample") {
17 | // console.log("data: ", e.data)
18 | let channels = e.data.channels;
19 | let sr = e.data.sr;
20 | let name = e.data.name
21 |
22 | add_sample(name, e.data.sample, channels, sr)
23 |
24 | // recall this to ensure
25 | } else if (e.data.type === "run") {
26 | this.update(e.data.value)
27 | } else if (e.data.type === "bpm") {
28 | set_bpm(e.data.value);
29 | } else if (e.data.type === "amp") {
30 | set_track_amp(e.data.value);
31 | // } else if (e.data.type === "sab") {
32 |
33 | } else {
34 | throw `Unexpected data type ${e.data.type}`;
35 | }
36 | }
37 | }
38 | update(code) {
39 | let result = update(code)
40 | if (result[0] !== 0) {
41 | this.port.postMessage({type: 'e', info: result})
42 | }
43 | }
44 | process(_, outputs, _parameters) {
45 | // if (midiSize) {
46 | // let codeUint8ArrayPtr = this._wasm.exports.alloc_uint8array(size);
47 | // let codeUint8Array = new Uint8Array(this._wasm.exports.memory.buffer, codeUint8ArrayPtr, size);
48 | // codeUint8Array.set(this._codeArray.slice(0, size));
49 |
50 | let outBuf = process(256)
51 |
52 | outputs[0][0].set(outBuf.slice(0, outBuf.length / 2))
53 | outputs[0][1].set(outBuf.slice(outBuf.length / 2, outBuf.length))
54 | return true
55 | }
56 | }
57 |
58 | registerProcessor('glicol-engine', GlicolEngine)
59 |
--------------------------------------------------------------------------------
/js/src/glicol-mode.js:
--------------------------------------------------------------------------------
1 | export default {
2 | // The start state contains the rules that are initially used
3 | start: [
4 | {regex: /(delayms|true|false|let|const|else|switch|do|loop|until|continue|break|fn|this|return|throw|try|catch|import|export|as|global|print|debug|eval|map|for|while|if|sin|exp|in|expr|seq|squsynth|sawsynth|trisynth|bd|hh|sn|speed|choose|mul|add|linrange|apfdecay|delayn|sin|saw|squ|imp|envperc|sampler|noiz|shape|tri|noise|noiz|rlpf|plate|onepole|rhpf|pha|buf|state|freeverb|pan|delay|apfgain|lpf|hpf|comb|mix|monosum|const_sig|sp|spd|amplfo|balance|meta|script|pad|in)(?![a-z])/,
5 | token: "string"},
6 | // The regex matches the token, the token property contains the type
7 | // {regex: /~([a-z]+(_)?)+/, token: "variable-3"},
8 | // You can match multiple tokens at once. Note that the captured
9 | // groups must span the whole string in this case
10 | // {regex: /##([\S\n\t\v ]+?)#/, token: "error"},
11 | {regex: /[-+]?([0-9]{1,}[.][0-9]+)/, token: "variable"},
12 | {regex: /PI/, token: "variable"},
13 | // {regex: /\\(\S)*/, token: "number"},
14 | {regex: /([0-9]{1,3}|(~[a-z](?![a-z0-9\.]))|_)+/, token: "variable"},
15 | // {regex: /(~)([a-z])(?!([a-z]))/, token: "variable"},
16 | {regex: /^~[a-z][0-9a-z\_\.]+/, token: "variable-2"},
17 |
18 |
19 | {regex: /\/\/.*/, token: "comment"},
20 | // {regex: /`[\s\S\n\t]+`/, token: "meta"},
21 | {regex: /\}|\{|\;|\`|\-|\/|:|>>|,|\*|\+|\=|\||\(|\)/, token: "error"},
22 | {regex: /^\\[0-9a-z\_]+/, token: "variable"},
23 | {regex: /^[a-z][0-9a-z\_]*/, token: "keyword"},
24 | // A next property will cause the mode to move to a different state
25 | {regex: /\/\*/, token: "comment", next: "comment"},
26 | // {regex: /[-+\/*=<>!]+/, token: "operator"},
27 | // indent and dedent properties guide autoindentation
28 | {regex: /[\{\[\(]/, indent: true},
29 | {regex: /[\}\]\)]/, dedent: true},
30 | {regex: /##/, token: "error", next: "error"}, //js is error
31 | // {regex: /`/, token: "meta", next: "meta"}, //js is error
32 | // {regex: /(?<=##)[^#]*(?=#)/, token: "meta"}
33 | ],
34 | // The multi-line comment state.
35 | comment: [
36 | {regex: /.*?\*\//, token: "comment", next: "start"},
37 | {regex: /.*/, token: "comment"}
38 | ],
39 | // meta: [
40 | // {regex: /.*?`/, token: "meta", next: "start"},
41 | // {regex: /.*/, token: "meta"}
42 | // ],
43 | error: [
44 | {regex: /.*?#/, token: "error", next: "start"},
45 | {regex: /.*/, token: "error"}
46 | ],
47 | // The meta property contains global information about the mode. It
48 | // can contain properties like lineComment, which are supported by
49 | // all modes, and also directives like dontIndentStates, which are
50 | // specific to simple modes.
51 | meta: {
52 | dontIndentStates: ["comment"],
53 | lineComment: "//"
54 | }
55 | };
--------------------------------------------------------------------------------
/js/src/glicol_wasm_bg.wasm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaosprint/glicol/0317db2e3f157b911bfc28c72ad5db90db963f24/js/src/glicol_wasm_bg.wasm
--------------------------------------------------------------------------------
/js/src/sample-list.json:
--------------------------------------------------------------------------------
1 | {"808bd":"https://github.com/chaosprint/Dirt-Samples","808ch":"https://github.com/chaosprint/Dirt-Samples","808clap":"https://github.com/chaosprint/Dirt-Samples","808ht":"https://github.com/chaosprint/Dirt-Samples","808lt":"https://github.com/chaosprint/Dirt-Samples","808oh":"https://github.com/chaosprint/Dirt-Samples","808sd":"https://github.com/chaosprint/Dirt-Samples","bass3":"https://github.com/chaosprint/Dirt-Samples","bin":"https://github.com/chaosprint/Dirt-Samples","blip":"https://github.com/chaosprint/Dirt-Samples","boip":"https://github.com/chaosprint/Dirt-Samples","bong":"https://github.com/chaosprint/Dirt-Samples","bpblart":"https://github.com/chaosprint/Dirt-Samples","can":"https://github.com/chaosprint/Dirt-Samples","casiohigh":"https://github.com/chaosprint/Dirt-Samples","casiolow":"https://github.com/chaosprint/Dirt-Samples","casionoise":"https://github.com/chaosprint/Dirt-Samples","cb":"https://github.com/chaosprint/Dirt-Samples","closedhh":"https://github.com/chaosprint/Dirt-Samples","clubkick":"https://github.com/chaosprint/Dirt-Samples","crash":"https://github.com/chaosprint/Dirt-Samples","echo":"https://github.com/chaosprint/Dirt-Samples","fat808sub":"https://github.com/chaosprint/Dirt-Samples","giveit":"https://github.com/chaosprint/Dirt-Samples","glass":"https://github.com/chaosprint/Dirt-Samples","guitar":"https://github.com/chaosprint/Dirt-Samples","hit1":"https://github.com/chaosprint/Dirt-Samples","hit2":"https://github.com/chaosprint/Dirt-Samples","hit3":"https://github.com/chaosprint/Dirt-Samples","industrial":"https://github.com/chaosprint/Dirt-Samples","junglesine":"https://github.com/chaosprint/Dirt-Samples","kick1":"https://github.com/chaosprint/Dirt-Samples","kick2":"https://github.com/chaosprint/Dirt-Samples","latibro":"https://github.com/chaosprint/Dirt-Samples","moog":"https://github.com/chaosprint/Dirt-Samples","openhh":"https://github.com/chaosprint/Dirt-Samples","pad":"https://github.com/chaosprint/Dirt-Samples","perc1":"https://github.com/chaosprint/Dirt-Samples","perc2":"https://github.com/chaosprint/Dirt-Samples","pluck":"https://github.com/chaosprint/Dirt-Samples","ride":"https://github.com/chaosprint/Dirt-Samples","rm":"https://github.com/chaosprint/Dirt-Samples","sax":"https://github.com/chaosprint/Dirt-Samples","sid":"https://github.com/chaosprint/Dirt-Samples","snare1":"https://github.com/chaosprint/Dirt-Samples","snare2":"https://github.com/chaosprint/Dirt-Samples","stab":"https://github.com/chaosprint/Dirt-Samples","talk1":"https://github.com/chaosprint/Dirt-Samples","talk2":"https://github.com/chaosprint/Dirt-Samples","tink":"https://github.com/chaosprint/Dirt-Samples","tok":"https://github.com/chaosprint/Dirt-Samples","ufo":"https://github.com/chaosprint/Dirt-Samples","whoosh":"https://github.com/chaosprint/Dirt-Samples","wind":"https://github.com/chaosprint/Dirt-Samples"}
--------------------------------------------------------------------------------
/js/style.css:
--------------------------------------------------------------------------------
1 | html, body, main {
2 | height:100%;
3 | width:100%;
4 | display: flex;
5 | justify-content: center;
6 | align-items: center;
7 | }
8 | * {
9 | font-size:large
10 | }
--------------------------------------------------------------------------------
/js/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "vite";
2 |
3 | export default defineConfig({
4 | plugins: [
5 | {
6 | name: "configure-response-headers",
7 | configureServer: (server) => {
8 | server.middlewares.use((_req, res, next) => {
9 | res.setHeader("Cross-Origin-Embedder-Policy", "require-corp");
10 | res.setHeader("Cross-Origin-Opener-Policy", "same-origin");
11 | next();
12 | });
13 | },
14 | },
15 | ],
16 | });
17 |
--------------------------------------------------------------------------------
/rs/Cargo.toml:
--------------------------------------------------------------------------------
1 | [workspace]
2 | default-members = ["main"]
3 | members = ["main", "parser", "synth", "wasm"]
4 | resolver = "1"
5 |
6 | [workspace.package]
7 | version = "0.14.0-dev"
8 | edition = "2021"
9 | repository = "https://github.com/chaosprint/glicol.git"
10 | authors = ["Qichao Lan "]
11 |
12 | [workspace.dependencies]
13 | petgraph = { version = "0.6", default-features = false, features = ["stable_graph"] }
14 | dasp_slice = { version = "0.11.0", default-features = false, features = [
15 | "std",
16 | ] }
17 |
18 | dasp_ring_buffer = { version = "0.11.0", default-features = false }
19 | # default-features = false can't be used here; we need std
20 | dasp_signal = { version = "0.11.0" }
21 | dasp_interpolate = { version = "0.11.0", features = ["linear", "sinc"] }
22 | hashbrown = "0.14.3"
23 | rhai = { version = "1.12.0", default-features = false, features = [
24 | "sync",
25 | "f32_float",
26 | "only_i32",
27 | ] }
28 | fasteval = "0.2.4"
29 | pest = "2.7.9"
30 | pest_derive = "2.7.9"
31 | yoke = { version = "0.7.3", default-features = false, features = ["derive", "alloc"] }
32 |
33 | [profile.wasm-release]
34 | inherits = "release"
35 | opt-level = 'z' # Optimize for size.
36 | lto = "fat" # Enable Link Time Optimization
37 | codegen-units = 1 # Reduce number of codegen units to increase optimizations.
38 | panic = 'abort' # Abort on panic
39 |
--------------------------------------------------------------------------------
/rs/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2020-present Qichao Lan (chaopsrint)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/rs/README.md:
--------------------------------------------------------------------------------
1 | ## Introduction
2 |
3 | This folder contains files for the engine of Glicol written in Rust.
4 |
5 | The `main` provides an `Engine` that takes the code as input, stores samples, and yield `next_block` of audio constantly.
6 |
7 | The `parser` offers the `get_ast` function for the `Engine` to parse the code.
8 |
9 | The `wasm` crate exports the `Engine` to a WebAssembly file which can be used in web browsers.
10 |
11 | The `synth` provides all the audio support.
12 |
13 | ## Glicol synth as a standalone Rust audio library
14 |
15 | You can write an audio project like this:
16 |
17 | ```
18 | use glicol_synth::{AudioContextBuilder, signal::ConstSig, Message};
19 |
20 | fn main() {
21 | let mut context = AudioContextBuilder::<128>::new()
22 | .sr(44100).channels(1).build();
23 |
24 | let node_a = context.add_mono_node(ConstSig::new(42.));
25 | context.connect(node_a, context.destination);
26 | println!("first block {:?}", context.next_block());
27 |
28 | context.send_msg(node_a, Message::SetToNumber((0, 100.)) );
29 | println!("second block, after msg {:?}", context.next_block());
30 | }
31 | ```
32 |
33 | See [./synth](./synth) folder for more details.
34 |
35 | ## Try it out
36 |
37 | First you should have Rust compiler installed. Make sure you can call `cargo` in your terminal.
38 |
39 | Then:
40 | ```
41 | git clone https://github.com/chaosprint/glicol.git
42 | cd glicol/rs/synth
43 | cargo run --example hello
44 | cargo run --example chain
45 | ```
46 |
47 | You can explore more examples in the `./rs/synth/` folder.
48 |
49 | ## License
50 |
51 | The MIT License (MIT)
52 |
53 | Copyright (c) 2020 - present Qichao Lan (chaosprint)
54 |
--------------------------------------------------------------------------------
/rs/main/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "glicol"
3 | version = { workspace = true }
4 | edition = { workspace = true }
5 | keywords = ["audio", "music", "DSP", "synth", "synthesizer"]
6 |
7 | license-file = "LICENSE"
8 | description = "Glicol language main entry point."
9 | repository = "https://github.com/chaosprint/glicol.git"
10 | authors = ["Qichao Lan "]
11 |
12 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
13 |
14 | [features]
15 | default = []
16 | use-samples = []
17 | use-meta = []
18 | bela = []
19 | wasm-bindgen = ["glicol_synth/wasm-bindgen"]
20 |
21 | [dependencies]
22 | petgraph = { workspace = true }
23 | glicol_parser = { path = "../parser", version = "0.14.0-dev" }
24 | glicol_synth = { path = "../synth", version = "0.14.0-dev", features = [
25 | "use-samples",
26 | "use-meta",
27 | ] }
28 | pest = { workspace = true }
29 | hashbrown = { workspace = true }
30 | yoke = { workspace = true }
31 |
32 | [dev-dependencies]
33 | gnuplot = "0.0.43"
34 | # petgraph = { version = "0.6", features = ["stable_graph"] }
35 | # cpal = "0.15.3"
36 | anyhow = "1.0.63"
37 |
--------------------------------------------------------------------------------
/rs/main/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2020-present Qichao Lan (chaopsrint)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/rs/main/README.md:
--------------------------------------------------------------------------------
1 | ## glicol-rs
2 |
3 | You can use this crate to build audio apps with Glicol syntax.
4 |
5 | ```rust
6 | use glicol::Engine;
7 | fn main() {
8 | let mut engine = Engine::<32>::new();
9 | engine.update_with_code(r#"o: sin 440"#);
10 | println!("next block {:?}", engine.next_block(vec![]));
11 | }
12 | ```
13 |
14 | More examples [here](https://github.com/chaosprint/glicol/tree/main/rs/main/examples).
15 |
16 | Learn Glicol syntax [here](https://glicol.org).
17 |
18 | It compiles to WebAssembly and runs in browsers.
19 |
20 | It can also be used on VST and Bela, but these are all experimental.
21 |
22 | See the GitHub repository for details.
--------------------------------------------------------------------------------
/rs/main/examples/expr.rs:
--------------------------------------------------------------------------------
1 | use glicol::Engine;
2 | // use glicol::{EngineError, get_error_info};
3 |
4 | // use glicol::GlicolNodeInfo;
5 | // use std::collections::HashMap;
6 |
7 | fn main() {
8 | let mut engine = Engine::<32>::new();
9 | engine
10 | .update_with_code(r#"o: eval `x:=x>1*(x-1.0)+x*x<=1;x`"#)
11 | .unwrap(); // y=math::sin(2*PI*x);x+=440.0/sr;y
12 | println!("next block {:?}", engine.next_block(vec![]));
13 | }
14 |
--------------------------------------------------------------------------------
/rs/main/examples/hello.rs:
--------------------------------------------------------------------------------
1 | use glicol::Engine;
2 | fn main() {
3 | let mut engine = Engine::<8>::new();
4 | engine
5 | .update_with_code(r#"o: constsig 42 >> pan 0.9"#)
6 | .unwrap();
7 | println!("next block {:?}", engine.next_block(vec![]));
8 | }
9 |
--------------------------------------------------------------------------------
/rs/main/examples/input.rs:
--------------------------------------------------------------------------------
1 | use glicol::Engine;
2 | // use glicol::{EngineError, get_error_info};
3 |
4 | // use glicol::GlicolNodeInfo;
5 | // use std::collections::HashMap;
6 |
7 | fn main() {
8 | let mut engine = Engine::<8>::new();
9 | engine
10 | .update_with_code(r#"out: ~input >> mul 2.0"#)
11 | .unwrap();
12 | println!(
13 | "next block {:?}",
14 | engine.next_block(vec![&[0.1; 8], &[0.2; 8]])
15 | );
16 | }
17 |
--------------------------------------------------------------------------------
/rs/main/examples/msg_synth.rs:
--------------------------------------------------------------------------------
1 | use glicol::Engine;
2 | fn main() {
3 | let mut engine = Engine::<8>::new();
4 | engine
5 | .update_with_code(r#"o: msgsynth \saw 0.01 0.1"#)
6 | .unwrap();
7 | println!("next block {:?}", engine.next_block(vec![]));
8 | }
9 |
--------------------------------------------------------------------------------
/rs/main/examples/p_synth.rs:
--------------------------------------------------------------------------------
1 | use glicol::Engine;
2 | // use glicol::{EngineError, get_error_info};
3 |
4 | // use glicol::GlicolNodeInfo;
5 | // use std::collections::HashMap;
6 |
7 | fn main() {
8 | let mut engine = Engine::<8>::new();
9 | engine.update_with_code(r#"o: pattern_synth `` 1"#).unwrap();
10 | println!("next block {:?}", engine.next_block(vec![]));
11 | }
12 |
--------------------------------------------------------------------------------
/rs/main/examples/plot.rs:
--------------------------------------------------------------------------------
1 | use glicol::Engine;
2 | use gnuplot::*;
3 |
4 | fn main() {
5 | let mut engine = Engine::<128>::new();
6 | engine
7 | .update_with_code(r#"o: [0.1=>100, 1/4=> 10.0, 1/3=>50]*(1/2).."#)
8 | .unwrap();
9 |
10 | // plot part
11 | let mut x = Vec::::new();
12 | let mut y = Vec::::new();
13 | let mut n = 0;
14 |
15 | for _ in 0..(220500 / 128) {
16 | let buf = engine.next_block(vec![]);
17 | for i in 0..128 {
18 | x.push(n);
19 | n += 1;
20 | y.push(buf[0][i]); // use the buf here
21 | }
22 | }
23 |
24 | let mut fg = Figure::new();
25 | fg.axes2d()
26 | .set_title("Glicol output", &[])
27 | .set_legend(Graph(0.5), Graph(0.9), &[], &[])
28 | .lines(&x, &y, &[Caption("left")]);
29 | fg.show().unwrap();
30 | }
31 |
--------------------------------------------------------------------------------
/rs/main/examples/plpf.rs:
--------------------------------------------------------------------------------
1 | // o: saw 400 >> lpf "100@0.0 200@0.5"(1) 1.0
2 |
3 | use glicol::Engine;
4 | fn main() {
5 | let mut engine = Engine::<8>::new();
6 | engine
7 | .update_with_code(r#"o: saw 400 >> lpf "100@0.0 200@0.5"(1) 1.0"#)
8 | .unwrap();
9 | println!("next block {:?}", engine.next_block(vec![]));
10 | }
11 |
--------------------------------------------------------------------------------
/rs/main/examples/raw.rs:
--------------------------------------------------------------------------------
1 | use glicol::Engine;
2 |
3 | fn main() {
4 | let mut engine = Engine::<128>::new();
5 | // engine.set_code("o: sin 440.0 >> mul 0.5 ");
6 | // engine.update();
7 | // engine.next_block();
8 | // engine.set_code("a: constsig 42 >> mul 0.1");
9 | // engine.update();
10 | engine.next_block(vec![]);
11 | // println!("index_info {:?}", engine.index_info);
12 | // engine.send_msg("o", 0, (0, "440."));
13 | // engine.next_block();
14 | }
15 |
--------------------------------------------------------------------------------
/rs/main/examples/update.rs:
--------------------------------------------------------------------------------
1 | use glicol::Engine;
2 | // use glicol::GlicolNodeInfo;
3 | // use std::collections::HashMap;
4 |
5 | fn main() {
6 | let mut engine = Engine::<16>::new();
7 | engine.livecoding = false;
8 | // engine.update(r#"o: constsig 42 >> mul 0.3"#);
9 | // engine.next_block();
10 | // engine.update(r#"o: constsig 42 >> mul ~mod; ~mod: constsig 0.9"#);
11 | // engine.next_block();
12 | // engine.update(r#"o: constsig 42 >> mul 0.3"#);
13 | // engine.next_block();
14 | // engine.update(r#"o: constsig 42 >> mul ~mod; ~mod: constsig 0.9"#);
15 | // engine.next_block();
16 |
17 | // engine.update(r#"o: constsig 42. >> add 0 >> mul ~mod; ~mod: constsig 0.5"#).unwrap();
18 | // engine.next_block();
19 | // engine.update(r#"o: constsig 42. >> mul ~mod; ~mod: constsig 0.5"#).unwrap();
20 | // engine.next_block();
21 |
22 | // engine.update(r#"o: imp 10 >> mul 0.1"#).unwrap();
23 | // engine.next_block();
24 | // engine.update(r#"o: saw 56"#).unwrap();
25 | // engine.next_block();
26 | // engine.update(r#"o: imp 100 >> mul 0.1"#).unwrap();
27 | // engine.next_block();
28 | // engine.update(r#"o: imp 100 >> mul ~mod
29 | // ~mo: sin 10 >> add 1.0"#).unwrap();
30 | // engine.next_block();
31 |
32 | // engine.update(r#"o: sin 110 >> mul 0.1"#).unwrap();
33 | // println!(" engine.next_block() {:?}", engine.next_block());
34 | // engine.update(r#"o: sin 110 >> add 0.0"#).unwrap();
35 | // println!(" engine.next_block() {:?}", engine.next_block());
36 | // engine.update(r#"o: sin 110 >> mul 0.1"#).unwrap();
37 | // println!(" engine.next_block() {:?}", engine.next_block());
38 |
39 | // engine.update(r#"o: seq ~a; ~a: choose 60"#).unwrap();
40 | // println!(" engine.next_block() {:?}", engine.next_block());
41 | // engine.update(r#"o: seq ~a; ~a: choose 70"#).unwrap();
42 | // println!(" engine.next_block() {:?}", engine.next_block());
43 |
44 | // engine.update(r#"o: sin 10"#).unwrap();
45 | // println!(" engine.next_block() {:?}", engine.next_block());
46 | // engine.update(r#"o: saw 12"#).unwrap();
47 | // println!(" engine.next_block() {:?}", engine.next_block());
48 |
49 | // engine.update_with_code(r#"a: constsig 10 >> lpf 300 0.1"#);
50 | // println!(" engine.next_block() 0 {:?}", engine.next_block().0);
51 | // engine.update_with_code(r#"a: constsig 10 >> lpf ~m 0.1; ~m: constsig 0.5"#);
52 | // println!(" engine.next_block() 1 {:?}", engine.next_block().0);
53 | // engine.add_sample(r#"\test"#, &[0.9, 0.8, 0.7, 0.6, 0.5], 1, 44100);
54 | engine
55 | .update_with_code(
56 | r#"
57 | ~t1: sig 10
58 | ~t2: sig 31
59 | ~t3: sig 42
60 | o: balance ~t1 ~t2"#,
61 | )
62 | .unwrap();
63 | println!(" engine.next_block() 0 {:?}", engine.next_block(vec![]));
64 | engine
65 | .update_with_code(
66 | r#"
67 | ~t1: sig 10
68 | ~t2: sig 31
69 | ~t3: sig 42
70 | o: balance ~t1 ~t3"#,
71 | )
72 | .unwrap();
73 | println!(" engine.next_block() 1 {:?}", engine.next_block(vec![]));
74 | }
75 |
--------------------------------------------------------------------------------
/rs/main/src/error.rs:
--------------------------------------------------------------------------------
1 | use glicol_parser::Rule;
2 | use pest::error::Error;
3 | pub use pest::error::ErrorVariant;
4 |
5 | #[derive(Debug, PartialEq)]
6 | pub enum EngineError {
7 | ParsingError(Box>),
8 | NonExistReference(String),
9 | NonExistSample(String),
10 | }
11 |
12 | impl From>> for EngineError {
13 | fn from(err: Box>) -> EngineError {
14 | EngineError::ParsingError(err)
15 | }
16 | }
17 |
18 | pub fn get_error_info(e: Error) -> (Vec, Vec) {
19 | match e.variant {
20 | ErrorVariant::ParsingError {
21 | positives,
22 | negatives,
23 | } => (positives, negatives),
24 | _ => unimplemented!(),
25 | }
26 | }
27 |
28 | impl std::fmt::Display for EngineError {
29 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30 | match self {
31 | Self::ParsingError(err) => writeln!(f, "Parsing error: {err}"),
32 | EngineError::NonExistSample(v) => writeln!(f, "There is no sample named {v}s"),
33 | EngineError::NonExistReference(v) => writeln!(f, "There is no reference named {v}"),
34 | }
35 | }
36 | }
37 |
38 | impl std::error::Error for EngineError {}
39 |
--------------------------------------------------------------------------------
/rs/main/tests/sin.rs:
--------------------------------------------------------------------------------
1 | use glicol::*;
2 |
3 | #[test]
4 | fn pan() {
5 | let mut engine = Engine::<128>::new();
6 | assert_eq!(engine.update_with_code(r#"o: sin 440 >> pan 0.5"#), Ok(()));
7 | }
8 |
--------------------------------------------------------------------------------
/rs/parser/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "glicol_parser"
3 | version = { workspace = true }
4 | edition = { workspace = true }
5 | keywords = ["audio", "music", "DSP", "synth", "synthesizer"]
6 |
7 | license-file = "LICENSE"
8 | description = "Parser for Glicol language."
9 | repository = "https://github.com/chaosprint/glicol.git"
10 | authors = ["Qichao Lan "]
11 |
12 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
13 |
14 | [dependencies]
15 | pest = { workspace = true }
16 | pest_derive = { workspace = true }
17 | hashbrown = { workspace = true }
18 | fasteval = { workspace = true }
19 | yoke = { workspace = true }
20 |
21 | [dev-dependencies]
22 | trace = "0.1.7"
23 |
--------------------------------------------------------------------------------
/rs/parser/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2020-present Qichao Lan (chaopsrint)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/rs/parser/examples/choose.rs:
--------------------------------------------------------------------------------
1 | // choose is quite unique as it takes unlimited number of notes
2 | use glicol_parser::*;
3 |
4 | fn main() {
5 | println!("{:?}", get_ast("o: choose 60 50 80 70").unwrap());
6 | }
7 |
--------------------------------------------------------------------------------
/rs/parser/examples/hello.rs:
--------------------------------------------------------------------------------
1 | // use pest::Parser;
2 | // use pest::iterators::Pairs;
3 | use glicol_parser::*;
4 |
5 | fn main() {
6 | println!(
7 | "{:?}",
8 | get_ast("a: [0.1 => 0.2, 1/2 - 100_ms => 0.3]").unwrap()
9 | );
10 | // println!("{:?}", get_ast("o: arrange ~t1 1 ~t2 3"));
11 | // println!("{:?}", get_ast("o: envperc 1.0 2.0"));
12 | // get_ast(input);
13 | // let line = GlicolParser::parse(Rule::block, input);
14 | // println!("{:?}", line);
15 | // }
16 | }
17 |
--------------------------------------------------------------------------------
/rs/parser/examples/lcs.rs:
--------------------------------------------------------------------------------
1 | use glicol_parser::*;
2 | use pest::error::ErrorVariant;
3 | use pest::Parser;
4 | // use lcs_diff::*;
5 |
6 | // fn main() {
7 | // let a = [1, 3, 5];
8 | // let b = [3, 4, 5];
9 | // // println!("result {:?}", diff(&a, &b));
10 | // let result = diff(&a, &b);
11 | // for r in result {
12 | // println!("result {:?}", r);
13 | // }
14 | // }
15 |
16 | fn main() {
17 | let mut block = match GlicolParser::parse(
18 | Rule::block,
19 | r#"out: sin 440 >> mul 0.1 >> add 0.1; b: seq 60 _60 >> sp \808;"#,
20 | ) {
21 | Ok(v) => v,
22 | Err(e) => {
23 | // println!("in location: {:?}; line_col: {:?}", e.location, e.line_col);
24 |
25 | match e.variant {
26 | ErrorVariant::ParsingError {
27 | positives,
28 | negatives,
29 | } => {
30 | if !positives.is_empty() {
31 | print!("\n\nexpecting ");
32 | for possible in positives {
33 | print!("{:?} ", possible)
34 | }
35 | print!("\n\n");
36 | }
37 | if !negatives.is_empty() {
38 | print!("\n\nunexpected element: ");
39 | for possible in negatives {
40 | print!("{:?} ", possible)
41 | }
42 | print!("\n\n");
43 | }
44 | panic!();
45 | }
46 | _ => panic!("unknown parsing error"),
47 | }
48 | }
49 | };
50 | let lines = block.next().unwrap(); // this can be a comment though, but we call it a line
51 | let mut ast = std::collections::HashMap::new();
52 | for line in lines.into_inner() {
53 | if line.as_rule() == Rule::line {
54 | println!("each line {:?}", line.as_str());
55 | let mut key = "";
56 | let mut chain_node_names = vec![];
57 | let mut chain_paras = vec![];
58 | for line_component in line.into_inner() {
59 | match line_component.as_rule() {
60 | Rule::reference => {
61 | println!("ref {:?}", line_component.as_str());
62 | key = line_component.as_str();
63 | }
64 | Rule::chain => {
65 | println!("chain {:?}", line_component.as_str());
66 | let chain = line_component;
67 | for node_pair in chain.into_inner() {
68 | let node = node_pair.into_inner().next().unwrap();
69 | match node.as_rule() {
70 | Rule::sin => {
71 | println!("node {:?}", node.as_str()); //"sin 440"
72 | let paras = node.into_inner().next().unwrap();
73 | println!("paras {:?}", paras.as_str()); //"440"
74 | chain_node_names.push("sin");
75 | chain_paras.push(paras.as_str());
76 | }
77 | Rule::mul => {
78 | println!("node {:?}", node.as_str());
79 | let paras = node.into_inner().next().unwrap();
80 | println!("paras {:?}", paras.as_str());
81 | chain_node_names.push("mul");
82 | chain_paras.push(paras.as_str());
83 | }
84 | Rule::add => {
85 | println!("node {:?}", node.as_str());
86 | let paras = node.into_inner().next().unwrap();
87 | println!("paras {:?}", paras.as_str());
88 | chain_node_names.push("add");
89 | chain_paras.push(paras.as_str());
90 | }
91 | Rule::seq => {
92 | println!("node {:?}", node.as_str());
93 | let paras = node.into_inner().next().unwrap();
94 | println!("paras {:?}", paras.as_str());
95 | chain_node_names.push("seq");
96 | chain_paras.push(paras.as_str());
97 | }
98 | Rule::sp => {
99 | println!("node {:?}", node.as_str());
100 | let paras = node.into_inner().next().unwrap();
101 | println!("paras {:?}", paras.as_str());
102 | chain_node_names.push("sp");
103 | chain_paras.push(paras.as_str());
104 | }
105 | _ => {}
106 | }
107 | }
108 | // println!("chain.into_inner(); {:?}", chain.into_inner());
109 | }
110 | _ => {}
111 | }
112 | }
113 | ast.insert(key, (chain_node_names, chain_paras));
114 | };
115 | }
116 | println!("ast {:?}", ast);
117 | }
118 |
--------------------------------------------------------------------------------
/rs/parser/examples/pattern.rs:
--------------------------------------------------------------------------------
1 | // choose is quite unique as it takes unlimited number of notes
2 | use glicol_parser::*;
3 |
4 | fn main() {
5 | println!("{:?}", get_ast(r#"o: sig "60@0.0 72@0.5"(1)"#).unwrap());
6 | }
7 |
--------------------------------------------------------------------------------
/rs/parser/examples/psampler.rs:
--------------------------------------------------------------------------------
1 | // choose is quite unique as it takes unlimited number of notes
2 | use glicol_parser::*;
3 |
4 | fn main() {
5 | println!(
6 | "{:?}",
7 | get_ast(r#"o: psampler "'bd'@0.0 'sd'@0.5""#).unwrap()
8 | );
9 | }
10 |
--------------------------------------------------------------------------------
/rs/parser/examples/seq.rs:
--------------------------------------------------------------------------------
1 | use glicol_parser::*;
2 |
3 | fn main() {
4 | println!("{:?}", get_ast("o: seq 60 _60 __60 ~a >> mul 0.5").unwrap());
5 | }
6 |
--------------------------------------------------------------------------------
/rs/parser/examples/test.rs:
--------------------------------------------------------------------------------
1 | // use pest::Parser;
2 | // use pest::iterators::Pairs;
3 | use glicol_parser::*;
4 |
5 | fn main() {
6 | println!("{:?}", get_ast(r#"o: lpf "400@0.5 600@0.9"(1) 1"#).unwrap());
7 | // get_ast(input);
8 | // let line = GlicolParser::parse(Rule::block, input);
9 | // println!("{:?}", line);
10 | // }
11 | }
12 |
--------------------------------------------------------------------------------
/rs/parser/src/util.rs:
--------------------------------------------------------------------------------
1 | use pest::{
2 | error::{Error, ErrorVariant},
3 | iterators::{Pair, Pairs},
4 | RuleType, Span,
5 | };
6 |
7 | use crate::{
8 | nodes::{NumberOrRef, TimeList, UsizeOrRef},
9 | Rule,
10 | };
11 |
12 | pub trait ToPestErrWithPositives {
13 | fn to_err_with_positives(self, positives: [R; N])
14 | -> Box>;
15 | }
16 |
17 | impl ToPestErrWithPositives for Span<'_> {
18 | fn to_err_with_positives(
19 | self,
20 | positives: [R; N],
21 | ) -> Box> {
22 | Box::new(Error::new_from_span(
23 | ErrorVariant::ParsingError {
24 | positives: positives.to_vec(),
25 | negatives: vec![],
26 | },
27 | self,
28 | ))
29 | }
30 | }
31 |
32 | pub trait RuleRepresentable: std::str::FromStr {
33 | const RULE: Rule;
34 | }
35 |
36 | impl RuleRepresentable for f32 {
37 | const RULE: Rule = Rule::number;
38 | }
39 |
40 | impl RuleRepresentable for usize {
41 | const RULE: Rule = Rule::integer;
42 | }
43 |
44 | impl RuleRepresentable for u32 {
45 | const RULE: Rule = Rule::integer;
46 | }
47 |
48 | pub trait TryToParse {
49 | fn try_to_parse(&self) -> Result>>
50 | where
51 | T: RuleRepresentable;
52 | }
53 |
54 | impl TryToParse for Pair<'_, Rule> {
55 | fn try_to_parse(&self) -> Result>>
56 | where
57 | T: RuleRepresentable,
58 | {
59 | self.as_str()
60 | .parse::()
61 | .map_err(|_| self.as_span().to_err_with_positives([T::RULE]))
62 | }
63 | }
64 |
65 | pub trait GetNextParsed {
66 | fn next_parsed(&mut self, start_span: Span<'_>) -> Result>>
67 | where
68 | T: RuleRepresentable;
69 | }
70 |
71 | impl GetNextParsed for Pairs<'_, Rule> {
72 | fn next_parsed(&mut self, start_span: Span<'_>) -> Result>>
73 | where
74 | T: RuleRepresentable,
75 | {
76 | self.next()
77 | .ok_or_else(|| start_span.to_err_with_positives([T::RULE]))
78 | .and_then(|p| p.try_to_parse())
79 | }
80 | }
81 |
82 | pub trait EndSpan<'ast> {
83 | fn as_end_span(&self) -> Span<'ast>;
84 | }
85 |
86 | impl<'ast, R> EndSpan<'ast> for Pair<'ast, R>
87 | where
88 | R: pest::RuleType,
89 | {
90 | fn as_end_span(&self) -> Span<'ast> {
91 | self.as_span().as_end_span()
92 | }
93 | }
94 |
95 | impl<'ast> EndSpan<'ast> for Span<'ast> {
96 | fn as_end_span(&self) -> Span<'ast> {
97 | // This is safe to unwrap 'cause we know it's valid due to the indexes we pass in
98 | self.get(self.end() - self.start()..).unwrap()
99 | }
100 | }
101 |
102 | pub trait ToInnerOwned {
103 | type Owned;
104 | fn to_inner_owned(&self) -> Self::Owned;
105 | }
106 |
107 | impl ToInnerOwned for NumberOrRef<&T>
108 | where
109 | T: AsRef + ToOwned + ?Sized,
110 | ::Owned: AsRef,
111 | {
112 | type Owned = NumberOrRef<::Owned>;
113 | fn to_inner_owned(&self) -> Self::Owned {
114 | match self {
115 | Self::Ref(s) => NumberOrRef::Ref((*s).to_owned()),
116 | Self::Number(n) => NumberOrRef::Number(*n),
117 | }
118 | }
119 | }
120 |
121 | impl ToInnerOwned for UsizeOrRef<&T>
122 | where
123 | T: AsRef + ToOwned + ?Sized,
124 | ::Owned: AsRef,
125 | {
126 | type Owned = UsizeOrRef<::Owned>;
127 | fn to_inner_owned(&self) -> Self::Owned {
128 | match self {
129 | Self::Ref(s) => UsizeOrRef::Ref((*s).to_owned()),
130 | Self::Usize(u) => UsizeOrRef::Usize(*u),
131 | }
132 | }
133 | }
134 |
135 | impl ToInnerOwned for Vec<(T, U)>
136 | where
137 | T: ToInnerOwned,
138 | U: ToInnerOwned,
139 | {
140 | type Owned = Vec<(::Owned, ::Owned)>;
141 | fn to_inner_owned(&self) -> Self::Owned {
142 | self.iter()
143 | .map(|(t, u)| (t.to_inner_owned(), u.to_inner_owned()))
144 | .collect()
145 | }
146 | }
147 |
148 | impl ToInnerOwned for Vec
149 | where
150 | T: ToInnerOwned,
151 | {
152 | type Owned = Vec<::Owned>;
153 | fn to_inner_owned(&self) -> Self::Owned {
154 | self.iter().map(ToInnerOwned::to_inner_owned).collect()
155 | }
156 | }
157 |
158 | impl ToInnerOwned for f32 {
159 | type Owned = f32;
160 | fn to_inner_owned(&self) -> Self::Owned {
161 | *self
162 | }
163 | }
164 |
165 | impl ToInnerOwned for TimeList {
166 | type Owned = TimeList;
167 | fn to_inner_owned(&self) -> Self::Owned {
168 | self.clone()
169 | }
170 | }
171 |
172 | #[macro_export]
173 | macro_rules! match_or_return_err{
174 | ($pair:ident, $($variant:path => $arm:tt,)+) => {
175 | match $pair.as_rule() {
176 | $($variant => $arm),*
177 | _ => return ::core::result::Result::Err(::std::boxed::Box::new(::pest::error::Error::new_from_span(
178 | ::pest::error::ErrorVariant::ParsingError {
179 | positives: vec![$($variant,)*],
180 | negatives: vec![]
181 | },
182 | $pair.as_span()
183 | )))
184 | }
185 | }
186 | }
187 |
--------------------------------------------------------------------------------
/rs/parser/tests/sin.rs:
--------------------------------------------------------------------------------
1 | use glicol_parser::*;
2 |
3 | #[test]
4 | fn minimal() {
5 | let res = get_ast("o: sin 440");
6 | assert!(res.is_ok());
7 | }
8 |
9 | #[test]
10 | fn comment_within_chain() {
11 | let res = get_ast(
12 | "o: sin 440
13 | // >> mul 0.5
14 | >> mul 0.6",
15 | );
16 | assert!(res.is_ok());
17 |
18 | let res = get_ast(
19 | "o: sin 440
20 | // ooooh this is nonsense
21 | >> add 6.00",
22 | );
23 | assert!(res.is_ok());
24 | }
25 |
--------------------------------------------------------------------------------
/rs/synth/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "glicol_synth"
3 | version = { workspace = true }
4 | edition = { workspace = true }
5 | keywords = [
6 | "audio",
7 | "music",
8 | "DSP",
9 | "synth",
10 | "synthesizer"
11 | ]
12 | readme = "README.md"
13 | license-file = "LICENSE"
14 | description = "A graph-based music DSP library written in Rust"
15 | repository = { workspace = true }
16 | authors = { workspace = true }
17 |
18 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
19 | [features]
20 | default = ["node-boxed", "node-pass", "node-sum"]
21 | use-samples = ["node-sampling"]
22 | use-meta = ["node-dynamic"]
23 | node-boxed = []
24 | node-pass = []
25 | node-sampling = []
26 | node-dynamic = ["rhai"]
27 | node-sum = ["dasp_slice"]
28 | wasm-bindgen = ["rhai/wasm-bindgen"]
29 |
30 | [[bench]]
31 | name = "next_block"
32 | harness = false
33 |
34 | [[bench]]
35 | name = "fm"
36 | harness = false
37 |
38 | [dependencies]
39 | petgraph = { workspace = true }
40 | dasp_slice = { workspace = true, optional = true }
41 | dasp_ring_buffer = { workspace = true }
42 | dasp_signal = { workspace = true }
43 | dasp_interpolate = { workspace = true }
44 | hashbrown = { workspace = true }
45 | rhai = { workspace = true, optional = true }
46 | fasteval = { workspace = true }
47 | glicol_parser = { path = "../parser", version = "0.14.0-dev"}
48 |
49 | [dev-dependencies]
50 | gnuplot = "0.0.43"
51 | criterion = "0.5.1"
52 | # petgraph = { version = "0.6", features = ["stable_graph"] }
53 | # cpal = "0.14.0"
54 | # anyhow = "1.0.63"
55 |
--------------------------------------------------------------------------------
/rs/synth/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2020-present Qichao Lan (chaopsrint)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/rs/synth/README.md:
--------------------------------------------------------------------------------
1 | # glicol_synth: a graph-based audio DSP library written in Rust
2 |
3 | `glicol_synth` is the audio engine of glicol computer music language.
4 | It can be used as a standalone audio library, with quite intuitive APIs:
5 |
6 | ```rust
7 | use glicol_synth::{AudioContextBuilder, signal::ConstSig, Message};
8 |
9 | let mut context = AudioContextBuilder::<16>::new()
10 | .sr(44100).channels(1).build();
11 |
12 | let node_a = context.add_mono_node(ConstSig::new(42.));
13 | context.connect(node_a, context.destination);
14 | println!("first block {:?}", context.next_block());
15 |
16 | context.send_msg(node_a, Message::SetToNumber(0, 100.));
17 | println!("second block, after msg {:?}", context.next_block());
18 | ```
19 |
20 | ## Overview
21 |
22 | `glicol_synth` begins with a fork of the `dasp_graph` crate, written by @mitchmindtree.
23 | many features and contents are added:
24 |
25 | - use const generics for a customisable buffer size
26 | - replace the input from vec to a map, so users can use a node id to select input
27 | - users can send message to each node in real-time for interaction
28 | - add a higher level audiocontext for easier APIs
29 | - many useful audio nodes from oscillators, filters, etc.
30 |
31 | See the [examples on GitHub](https://github.com/chaosprint/glicol/tree/main/rs/synth/examples) for the basic usage.
32 |
--------------------------------------------------------------------------------
/rs/synth/benches/fm.rs:
--------------------------------------------------------------------------------
1 | use criterion::{black_box, criterion_group, criterion_main, Criterion};
2 | use glicol_synth::{
3 | operator::{Add, Mul},
4 | oscillator::SinOsc,
5 | AudioContextBuilder,
6 | };
7 |
8 | fn fm_benchmark(c: &mut Criterion) {
9 | let mut context = AudioContextBuilder::<128>::new()
10 | .sr(44100)
11 | .channels(2)
12 | .build(); //black_box(200.)
13 | let sin1 = context.add_stereo_node(SinOsc::new());
14 | let mul1 = context.add_stereo_node(Mul::new(0.1));
15 | let sin2 = context.add_stereo_node(SinOsc::new().freq(black_box(200.)));
16 | let mul2 = context.add_stereo_node(Mul::new(300.));
17 | let add2 = context.add_stereo_node(Add::new(600.));
18 | context.connect(sin1, mul1);
19 | context.connect(sin2, mul2);
20 | context.connect(mul2, add2);
21 | context.connect(add2, sin1);
22 | context.connect(mul1, context.destination);
23 |
24 | c.bench_function("fm", |b| {
25 | b.iter(|| {
26 | context.next_block();
27 | })
28 | });
29 | }
30 |
31 | criterion_group!(benches, fm_benchmark);
32 | criterion_main!(benches);
33 |
--------------------------------------------------------------------------------
/rs/synth/benches/next_block.rs:
--------------------------------------------------------------------------------
1 | use criterion::{black_box, criterion_group, criterion_main, Criterion};
2 | use glicol_synth::{oscillator::SinOsc, AudioContextBuilder};
3 |
4 | fn next_block_benchmark(c: &mut Criterion) {
5 | let mut context = AudioContextBuilder::<128>::new()
6 | .sr(44100)
7 | .channels(2)
8 | .build();
9 | let node_a = context.add_stereo_node(SinOsc::new().freq(black_box(440.)));
10 | context.connect(node_a, context.destination);
11 |
12 | c.bench_function("next_block", |b| {
13 | b.iter(|| {
14 | context.next_block();
15 | })
16 | });
17 | }
18 |
19 | criterion_group!(benches, next_block_benchmark);
20 | criterion_main!(benches);
21 |
--------------------------------------------------------------------------------
/rs/synth/examples/chain.rs:
--------------------------------------------------------------------------------
1 | use glicol_synth::{operator::Mul, signal::ConstSig, AudioContextBuilder};
2 |
3 | fn main() {
4 | let mut context = AudioContextBuilder::<8>::new()
5 | .sr(44100)
6 | .channels(1)
7 | .build();
8 |
9 | let node_a = context.add_mono_node(ConstSig::new(24.));
10 | let node_b = context.add_mono_node(Mul::new(0.5));
11 | // context.chain(vec![node_a, node_b, context.destination]);
12 |
13 | let node_c = context.add_mono_node(ConstSig::new(0.1));
14 | context.chain(vec![node_c, node_b]);
15 |
16 | context.chain(vec![node_a, node_b, context.destination]);
17 | println!("first block {:?}", context.next_block());
18 | }
19 |
--------------------------------------------------------------------------------
/rs/synth/examples/connect.rs:
--------------------------------------------------------------------------------
1 | use glicol_synth::{operator::Mul, signal::ConstSig, AudioContextBuilder, Message};
2 |
3 | fn main() {
4 | let mut context = AudioContextBuilder::<8>::new()
5 | .sr(44100)
6 | .channels(2)
7 | .build();
8 |
9 | let node_a = context.add_mono_node(ConstSig::new(42.));
10 | let node_b = context.add_stereo_node(Mul::new(0.1));
11 | context.connect(node_a, node_b);
12 | context.connect(node_b, context.destination);
13 |
14 | println!("first block {:?}", context.next_block());
15 | // message
16 | context.send_msg(node_a, Message::SetToNumber(0, 100.));
17 | println!("second block, after msg {:?}", context.next_block());
18 | }
19 |
--------------------------------------------------------------------------------
/rs/synth/examples/freeverb.rs:
--------------------------------------------------------------------------------
1 | use glicol_synth::{
2 | operator::Mul,
3 | // effect::FreeverbNode,
4 | signal::Impulse,
5 | AudioContextBuilder,
6 | };
7 | /// deprecated!
8 | use gnuplot::*;
9 |
10 | fn main() {
11 | let mut context = AudioContextBuilder::<128>::new()
12 | .sr(44100)
13 | .channels(2)
14 | .build();
15 | let node_a = context.add_mono_node(Impulse::new().freq(0.1));
16 | let node_a2 = context.add_stereo_node(Mul::new(0.9));
17 | // let node_b = context.add_stereo_node( FreeverbNode::new() );
18 | // context.connect(node_a, context.destination);
19 | context.chain(vec![node_a, node_a2, context.destination]);
20 |
21 | // plot part
22 | let mut x = Vec::::new();
23 | let mut y = Vec::::new();
24 | let mut y2 = Vec::::new();
25 | let mut n = 0;
26 |
27 | for _ in 0..(44100 / 128) {
28 | let buf = context.next_block();
29 | for i in 0..128 {
30 | x.push(n);
31 | n += 1;
32 | y.push(buf[0][i]); // use the buf here
33 | y2.push(buf[1][i]);
34 | }
35 | }
36 |
37 | let mut fg = Figure::new();
38 | fg.axes2d()
39 | .set_title("Impulse response", &[Font("Courier", 8.)])
40 | .set_legend(Graph(0.8), Graph(0.9), &[], &[Font("Courier", 8.)])
41 | .lines(&x, &y, &[Caption("left"), Color("#e879f9"), LineWidth(2.0)])
42 | .lines(
43 | &x,
44 | &y2,
45 | &[Caption("right"), Color("#38bdf8"), LineWidth(2.0)],
46 | );
47 | fg.show().unwrap();
48 | }
49 |
--------------------------------------------------------------------------------
/rs/synth/examples/gnode.rs:
--------------------------------------------------------------------------------
1 | use glicol_synth::{effect::Plate, signal::ConstSig, AudioContextBuilder};
2 |
3 | fn main() {
4 | let mut context = AudioContextBuilder::<8>::new()
5 | .sr(44100)
6 | .channels(2)
7 | .build();
8 |
9 | let c = context.add_stereo_node(ConstSig::new(1.));
10 | let node_a = context.add_stereo_node(Plate::new(0.5));
11 |
12 | // all the process will happen to the destination node
13 | context.chain(vec![c, node_a, context.destination]);
14 |
15 | // that's all, you can use this graph.next_block() in a callback loop
16 | println!("first block {:?}", context.next_block());
17 |
18 | // message
19 | // context.send_msg(node_a, Message::SetToNumber(0, 1.) );
20 | // println!("second block, after msg {:?}", context.next_block());
21 | }
22 |
--------------------------------------------------------------------------------
/rs/synth/examples/hello.rs:
--------------------------------------------------------------------------------
1 | use glicol_synth::{signal::ConstSig, AudioContextBuilder, Message};
2 |
3 | fn main() {
4 | let mut context = AudioContextBuilder::<128>::new()
5 | .sr(44100)
6 | .channels(1)
7 | .build();
8 |
9 | let node_a = context.add_mono_node(ConstSig::new(42.));
10 |
11 | // all the process will happen to the destination node
12 | context.connect(node_a, context.destination);
13 |
14 | // that's all, you can use this graph.next_block() in a callback loop
15 | println!("first block {:?}", context.next_block());
16 |
17 | // message
18 | context.send_msg(node_a, Message::SetToNumber(0, 100.));
19 | println!("second block, after msg {:?}", context.next_block());
20 | }
21 |
--------------------------------------------------------------------------------
/rs/synth/examples/plot-am.rs:
--------------------------------------------------------------------------------
1 | use glicol_synth::{operator::Add, operator::Mul, oscillator::SinOsc, AudioContextBuilder};
2 | use gnuplot::*;
3 |
4 | fn main() {
5 | let mut context = AudioContextBuilder::<128>::new()
6 | .sr(44100)
7 | .channels(1)
8 | .build();
9 | let node_a = context.add_mono_node(SinOsc::new().freq(10.));
10 | let node_b = context.add_mono_node(Mul::new(0.5));
11 | // context.connect(node_a, context.destination);
12 |
13 | // let node_c = context.add_mono_node( SquOsc::new().freq(10.) );
14 | let node_d = context.add_mono_node(Mul::new(8.0));
15 | let node_e = context.add_mono_node(Add::new(1.0));
16 | let node_f = context.add_mono_node(SinOsc::new().freq(100.));
17 | context.chain(vec![node_a, node_b, context.destination]);
18 | context.chain(vec![node_f, node_d, node_e, node_b]);
19 | // context.connect_with_order(node_e, node_b, 1);
20 |
21 | // plot part
22 | let mut x = Vec::::new();
23 | let mut y = Vec::::new();
24 | let mut n = 0;
25 |
26 | for _ in 0..(44100 / 128) {
27 | let buf = context.next_block();
28 | for i in 0..128 {
29 | x.push(n);
30 | n += 1;
31 | y.push(buf[0][i]); // use the buf here
32 | }
33 | }
34 |
35 | let mut fg = Figure::new();
36 | fg.axes2d()
37 | .set_title("Glicol output", &[])
38 | .set_legend(Graph(0.5), Graph(0.9), &[], &[])
39 | .lines(&x, &y, &[Caption("left")]);
40 | fg.show().unwrap();
41 | }
42 |
--------------------------------------------------------------------------------
/rs/synth/examples/plot-g.rs:
--------------------------------------------------------------------------------
1 | use glicol_synth::{effect::Plate, signal::Impulse, AudioContextBuilder};
2 | use gnuplot::*;
3 |
4 | fn main() {
5 | let mut context = AudioContextBuilder::<128>::new()
6 | .sr(44100)
7 | .channels(1)
8 | .build();
9 |
10 | let c = context.add_mono_node(Impulse::new().freq(10.));
11 | let node_a = context.add_stereo_node(Plate::new(0.5));
12 |
13 | // all the process will happen to the destination node
14 | context.chain(vec![c, node_a, context.destination]);
15 |
16 | // plot part
17 | let mut x = Vec::::new();
18 | let mut y = Vec::::new();
19 | let mut n = 0;
20 |
21 | for _ in 0..(44100 / 128) {
22 | let buf = context.next_block();
23 | for i in 0..128 {
24 | x.push(n);
25 | n += 1;
26 | y.push(buf[0][i]); // use the buf here
27 | }
28 | }
29 |
30 | let mut fg = Figure::new();
31 | fg.axes2d()
32 | .set_title("Glicol output", &[])
33 | .set_legend(Graph(0.5), Graph(0.9), &[], &[])
34 | .lines(&x, &y, &[Caption("left")]);
35 | fg.show().unwrap();
36 | }
37 |
--------------------------------------------------------------------------------
/rs/synth/examples/plot-imp.rs:
--------------------------------------------------------------------------------
1 | use glicol_synth::{effect::Plate, signal::Impulse, AudioContextBuilder};
2 | use gnuplot::*;
3 |
4 | fn main() {
5 | let mut context = AudioContextBuilder::<128>::new()
6 | .sr(44100)
7 | .channels(2)
8 | .build();
9 | let node_a = context.add_mono_node(Impulse::new().freq(0.1));
10 | let node_b = context.add_stereo_node(Plate::new(0.1));
11 | // context.connect(node_a, context.destination);
12 | context.chain(vec![node_a, node_b, context.destination]);
13 |
14 | // plot part
15 | let mut x = Vec::::new();
16 | let mut y = Vec::::new();
17 | let mut y2 = Vec::::new();
18 | let mut n = 0;
19 |
20 | for _ in 0..(44100 / 128) {
21 | let buf = context.next_block();
22 | for i in 0..128 {
23 | x.push(n);
24 | n += 1;
25 | y.push(buf[0][i]); // use the buf here
26 | y2.push(buf[1][i]);
27 | }
28 | }
29 |
30 | let mut fg = Figure::new();
31 | fg.axes2d()
32 | .set_title("Impulse response", &[Font("Courier", 8.)])
33 | .set_legend(Graph(0.8), Graph(0.9), &[], &[Font("Courier", 8.)])
34 | .lines(&x, &y, &[Caption("left"), Color("#e879f9"), LineWidth(2.0)])
35 | .lines(
36 | &x,
37 | &y2,
38 | &[Caption("right"), Color("#38bdf8"), LineWidth(2.0)],
39 | );
40 | fg.show().unwrap();
41 | }
42 |
--------------------------------------------------------------------------------
/rs/synth/examples/plot.rs:
--------------------------------------------------------------------------------
1 | use glicol_synth::{operator::Mul, oscillator::SinOsc, AudioContextBuilder};
2 | use gnuplot::*;
3 |
4 | fn main() {
5 | let mut context = AudioContextBuilder::<128>::new()
6 | .sr(44100)
7 | .channels(1)
8 | .build();
9 | let node_a = context.add_mono_node(SinOsc::new().freq(2.));
10 | let node_b = context.add_mono_node(Mul::new(0.5));
11 | // context.connect(node_a, context.destination);
12 | context.chain(vec![node_a, node_b, context.destination]);
13 |
14 | // plot part
15 | let mut x = Vec::::new();
16 | let mut y = Vec::::new();
17 | let mut n = 0;
18 |
19 | for _ in 0..(88000 / 128) {
20 | let buf = context.next_block();
21 | for i in 0..128 {
22 | x.push(n);
23 | n += 1;
24 | y.push(buf[0][i]); // use the buf here
25 | }
26 | }
27 |
28 | let mut fg = Figure::new();
29 | fg.axes2d()
30 | .set_title("Glicol output", &[])
31 | .set_legend(Graph(0.5), Graph(0.9), &[], &[])
32 | .lines(&x, &y, &[Caption("left")]);
33 | fg.show().unwrap();
34 | }
35 |
--------------------------------------------------------------------------------
/rs/synth/examples/plot2.rs:
--------------------------------------------------------------------------------
1 | use glicol_synth::{
2 | filter::ResonantLowPassFilter, oscillator::SawOsc, signal::ConstSig, AudioContextBuilder,
3 | };
4 | use gnuplot::*;
5 |
6 | fn main() {
7 | let mut context = AudioContextBuilder::<128>::new()
8 | .sr(44100)
9 | .channels(1)
10 | .build();
11 | let node_a = context.add_mono_node(SawOsc::new().freq(30.));
12 | let node_b = context.add_mono_node(ResonantLowPassFilter::new().cutoff(100.0));
13 | let node_c = context.add_mono_node(ConstSig::new(50.0));
14 | context.chain(vec![node_a, node_b, context.destination]);
15 | context.connect(node_c, node_b);
16 |
17 | // plot part
18 | let mut x = Vec::::new();
19 | let mut y = Vec::::new();
20 | let mut n = 0;
21 |
22 | for _ in 0..(44100 / 128) {
23 | let buf = context.next_block();
24 | for i in 0..128 {
25 | x.push(n);
26 | n += 1;
27 | y.push(buf[0][i]); // use the buf here
28 | }
29 | }
30 |
31 | let mut fg = Figure::new();
32 | fg.axes2d()
33 | .set_title("Glicol output", &[])
34 | .set_legend(Graph(0.5), Graph(0.9), &[], &[])
35 | .lines(&x, &y, &[Caption("left")]);
36 | fg.show().unwrap();
37 | }
38 |
--------------------------------------------------------------------------------
/rs/synth/examples/plot3.rs:
--------------------------------------------------------------------------------
1 | use glicol_synth::{
2 | delay::DelayMs,
3 | operator::{Add, Mul},
4 | // sampling::{Sampler},
5 | oscillator::SinOsc,
6 | signal::Impulse,
7 | AudioContextBuilder,
8 | };
9 | use gnuplot::*;
10 |
11 | fn main() {
12 | let mut context = AudioContextBuilder::<128>::new()
13 | .sr(44100)
14 | .channels(1)
15 | .build();
16 | let node_a = context.add_mono_node(Impulse::new().freq(1000.0));
17 | // let node_b = context.add_stereo_node( Sampler::new((&[1.0, 0.0, 0.0], 1)) );
18 | // let node_c = context.add_stereo_node( Mul::new(0.5) );
19 | let node_b = context.add_mono_node(DelayMs::new().delay(0.0, 1));
20 | let (node_d, node_e, node_f) = (
21 | context.add_mono_node(SinOsc::new()),
22 | context.add_mono_node(Mul::new(100.)),
23 | context.add_mono_node(Add::new(200.)),
24 | );
25 | // let node_j = context.add_mono_node(Mul::new(100.));
26 |
27 | context.chain(vec![
28 | node_a,
29 | node_b,
30 | // node_j,
31 | context.destination,
32 | ]);
33 | context.chain(vec![node_d, node_e, node_f, node_b]);
34 |
35 | // plot part
36 | let mut x = Vec::::new();
37 | let mut y = Vec::::new();
38 | let mut n = 0;
39 |
40 | for _ in 0..(512 / 128) {
41 | let buf = context.next_block();
42 | for i in 0..128 {
43 | x.push(n);
44 | n += 1;
45 | y.push(buf[0][i]); // use the buf here
46 | }
47 | }
48 |
49 | let mut fg = Figure::new();
50 | fg.axes2d()
51 | .set_title("Glicol output", &[])
52 | .set_legend(Graph(0.5), Graph(0.9), &[], &[])
53 | .lines(&x, &y, &[Caption("left")]);
54 | fg.show().unwrap();
55 | }
56 |
--------------------------------------------------------------------------------
/rs/synth/examples/plot_psynth.rs:
--------------------------------------------------------------------------------
1 | use glicol_synth::{synth::PatternSynth, AudioContextBuilder};
2 | use gnuplot::*;
3 |
4 | fn main() {
5 | let mut context = AudioContextBuilder::<128>::new()
6 | .sr(44100)
7 | .channels(1)
8 | .build();
9 |
10 | let node_a = context.add_mono_node(PatternSynth::new(vec![(0.0, 60.), (0.5, 72.)]));
11 |
12 | context.chain(vec![node_a, context.destination]);
13 |
14 | // plot part
15 | let mut x = Vec::::new();
16 | let mut y = Vec::::new();
17 | let mut n = 0;
18 |
19 | for _ in 0..(88200 / 128) {
20 | let buf = context.next_block();
21 | for i in 0..128 {
22 | x.push(n);
23 | n += 1;
24 | y.push(buf[0][i]); // use the buf here
25 | }
26 | }
27 |
28 | let mut fg = Figure::new();
29 | fg.axes2d()
30 | .set_title("Glicol output", &[])
31 | .set_legend(Graph(0.5), Graph(0.9), &[], &[])
32 | .lines(&x, &y, &[Caption("left")]);
33 | fg.show().unwrap();
34 | }
35 |
--------------------------------------------------------------------------------
/rs/synth/examples/sin.rs:
--------------------------------------------------------------------------------
1 | use glicol_synth::{operator::Mul, oscillator::SinOsc, AudioContextBuilder, Message};
2 |
3 | fn main() {
4 | let mut context = AudioContextBuilder::<8>::new()
5 | .sr(44100)
6 | .channels(2)
7 | .build();
8 |
9 | let node_a = context.add_mono_node(SinOsc::new().freq(440.0));
10 | let node_b = context.add_stereo_node(Mul::new(0.1));
11 | context.connect(node_a, node_b);
12 | context.connect(node_b, context.destination);
13 |
14 | println!("first block {:?}", context.next_block());
15 | // message
16 | context.send_msg(node_a, Message::SetToNumber(0, 100.));
17 | println!("second block, after msg {:?}", context.next_block());
18 | }
19 |
--------------------------------------------------------------------------------
/rs/synth/src/buffer.rs:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT)
2 |
3 | // Copyright (c) 2016 RustAudio Developers
4 |
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 |
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 |
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | // modified: add const generics
24 |
25 | use core::fmt;
26 | use core::ops::{Deref, DerefMut};
27 |
28 | /// The fixed-size buffer used for processing the graph.
29 | #[derive(Clone)]
30 | pub struct Buffer {
31 | data: [f32; N],
32 | }
33 |
34 | impl Buffer {
35 | pub const LEN: usize = N;
36 | /// A silent **Buffer**.
37 | pub const SILENT: Self = Buffer { data: [0.0; N] };
38 |
39 | /// Short-hand for writing silence to the whole buffer.
40 | pub fn silence(&mut self) {
41 | self.data.copy_from_slice(&Self::SILENT)
42 | }
43 | }
44 |
45 | impl Default for Buffer {
46 | fn default() -> Self {
47 | Self::SILENT
48 | }
49 | }
50 |
51 | impl From<[f32; N]> for Buffer {
52 | fn from(data: [f32; N]) -> Self {
53 | Buffer { data }
54 | }
55 | }
56 |
57 | impl fmt::Debug for Buffer {
58 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
59 | fmt::Debug::fmt(&self.data[..], f)
60 | }
61 | }
62 |
63 | impl PartialEq for Buffer {
64 | fn eq(&self, other: &Self) -> bool {
65 | self[..] == other[..]
66 | }
67 | }
68 |
69 | impl Deref for Buffer {
70 | type Target = [f32];
71 | fn deref(&self) -> &Self::Target {
72 | &self.data[..]
73 | }
74 | }
75 |
76 | impl DerefMut for Buffer {
77 | fn deref_mut(&mut self) -> &mut Self::Target {
78 | &mut self.data[..]
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/rs/synth/src/lib.rs:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT)
2 |
3 | // Copyright (c) 2016 RustAudio Developers
4 |
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 |
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 |
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | #![doc = include_str!("../README.md")]
24 | mod context;
25 |
26 | pub use context::*;
27 |
28 | mod graph;
29 | use glicol_parser::{
30 | nodes::{TimeList, UsizeOrRef},
31 | ToInnerOwned,
32 | };
33 | pub use graph::*;
34 |
35 | mod node;
36 | pub use node::{
37 | compound, delay, effect, envelope, filter, operator, oscillator, sequencer, signal, synth,
38 | };
39 | pub use node::{Input, Node};
40 | // pub use node::*; // TODO: Do not expose every struct here
41 |
42 | mod buffer;
43 | pub use buffer::Buffer;
44 |
45 | #[cfg(feature = "node-sampling")]
46 | pub use node::sampling;
47 |
48 | #[cfg(feature = "node-dynamic")]
49 | pub use node::dynamic;
50 |
51 | #[cfg(feature = "node-boxed")]
52 | pub use node::{BoxedNode, BoxedNodeSend};
53 |
54 | #[cfg(feature = "node-sum")]
55 | pub use node::{Sum, Sum2};
56 |
57 | #[cfg(feature = "node-pass")]
58 | pub use node::Pass;
59 |
60 | use hashbrown::HashMap;
61 |
62 | #[derive(Debug, Clone)]
63 | pub enum Message {
64 | SetToNumber(u8, f32),
65 | SetToNumberList(u8, Vec),
66 | SetToSymbol(u8, String),
67 | SetToSamples(u8, (&'static [f32], usize, usize)),
68 | SetSamplePattern(
69 | Vec<(String, f32)>,
70 | f32,
71 | HashMap,
72 | ),
73 | SetPattern(Vec<(f32, f32)>, f32),
74 | SetToSeq(u8, Vec<(f32, UsizeOrRef)>),
75 | SetRefOrder(HashMap),
76 | SetBPM(f32),
77 | SetSampleRate(usize),
78 | MainInput(petgraph::graph::NodeIndex),
79 | SidechainInput(petgraph::graph::NodeIndex),
80 | Index(usize),
81 | IndexOrder(usize, usize),
82 | ResetOrder,
83 | SetParam(u8, GlicolPara),
84 | SetToBool(u8, bool),
85 | }
86 |
87 | #[derive(Debug, PartialEq, PartialOrd, Clone)]
88 | pub enum GlicolPara
89 | where
90 | S: AsRef,
91 | {
92 | Number(f32),
93 | Bool(bool),
94 | NumberList(Vec),
95 | Reference(S),
96 | SampleSymbol(S), // symbol is for sample only
97 | Symbol(S),
98 | Sequence(Vec<(f32, UsizeOrRef)>),
99 | Pattern(Vec<(Self, f32)>, f32),
100 | Event(Vec<(Self, f32)>),
101 | Points(Vec<(TimeList, f32)>),
102 | Bar(f32),
103 | Second(f32),
104 | Millisecond(f32),
105 | }
106 |
107 | impl ToInnerOwned for GlicolPara<&S>
108 | where
109 | S: AsRef + ToOwned + ?Sized,
110 | ::Owned: AsRef,
111 | {
112 | type Owned = GlicolPara<::Owned>;
113 | fn to_inner_owned(&self) -> Self::Owned {
114 | match self {
115 | Self::Number(num) => GlicolPara::Number(*num),
116 | Self::Bool(bool) => GlicolPara::Bool(*bool),
117 | Self::NumberList(vec) => GlicolPara::NumberList(vec.to_vec()),
118 | Self::Reference(s) => GlicolPara::Reference((*s).to_owned()),
119 | Self::SampleSymbol(s) => GlicolPara::SampleSymbol((*s).to_owned()),
120 | Self::Symbol(s) => GlicolPara::Symbol((*s).to_owned()),
121 | Self::Sequence(seq) => GlicolPara::Sequence(seq.to_inner_owned()),
122 | Self::Pattern(vec, num) => GlicolPara::Pattern(vec.to_inner_owned(), *num),
123 | Self::Event(vec) => GlicolPara::Event(vec.to_inner_owned()),
124 | Self::Points(vec) => GlicolPara::Points(vec.to_inner_owned()),
125 | Self::Bar(num) => GlicolPara::Bar(*num),
126 | Self::Second(num) => GlicolPara::Second(*num),
127 | Self::Millisecond(num) => GlicolPara::Millisecond(*num),
128 | }
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/rs/synth/src/node/boxed.rs:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT)
2 |
3 | // Copyright (c) 2016 RustAudio Developers
4 |
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 |
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 |
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | use crate::{Buffer, Input, Message, Node};
24 | use core::fmt;
25 | use core::ops::{Deref, DerefMut};
26 | use hashbrown::HashMap;
27 |
28 | /// A wrapper around a `Box`.
29 | ///
30 | /// Provides the necessary `Sized` implementation to allow for compatibility with the graph process
31 | /// function.
32 | pub struct BoxedNode(pub Box>);
33 |
34 | /// A wrapper around a `Box`.
35 | ///
36 | /// Provides the necessary `Sized` implementation to allow for compatibility with the graph process
37 | /// function.
38 | ///
39 | /// Useful when the ability to send nodes from one thread to another is required. E.g. this is
40 | /// common when initialising nodes or the audio graph itself on one thread before sending them to
41 | /// the audio thread.
42 | pub struct BoxedNodeSend(pub Box + Send>);
43 |
44 | impl BoxedNode {
45 | /// Create a new `BoxedNode` around the given `node`.
46 | ///
47 | /// This is short-hand for `BoxedNode::from(Box::new(node))`.
48 | pub fn new(node: T) -> Self
49 | where
50 | T: 'static + Node,
51 | {
52 | Self::from(Box::new(node))
53 | }
54 | }
55 |
56 | impl BoxedNodeSend {
57 | /// Create a new `BoxedNode` around the given `node`.
58 | ///
59 | /// This is short-hand for `BoxedNode::from(Box::new(node))`.
60 | pub fn new(node: T) -> Self
61 | where
62 | T: 'static + Node + Send,
63 | {
64 | Self::from(Box::new(node))
65 | }
66 | }
67 |
68 | impl Node for BoxedNode {
69 | fn process(&mut self, inputs: &mut HashMap>, output: &mut [Buffer]) {
70 | self.0.process(inputs, output)
71 | }
72 | fn send_msg(&mut self, info: Message) {
73 | self.0.send_msg(info)
74 | }
75 | }
76 |
77 | impl Node for BoxedNodeSend {
78 | fn process(&mut self, inputs: &mut HashMap>, output: &mut [Buffer]) {
79 | self.0.process(inputs, output)
80 | }
81 | fn send_msg(&mut self, info: Message) {
82 | self.0.send_msg(info)
83 | }
84 | }
85 |
86 | impl From> for BoxedNode
87 | where
88 | T: 'static + Node,
89 | {
90 | fn from(n: Box) -> Self {
91 | BoxedNode(n as Box>)
92 | }
93 | }
94 |
95 | impl From> for BoxedNodeSend
96 | where
97 | T: 'static + Node + Send,
98 | {
99 | fn from(n: Box) -> Self {
100 | BoxedNodeSend(n as Box + Send>)
101 | }
102 | }
103 |
104 | impl From> for Box> {
105 | fn from(value: BoxedNode) -> Self {
106 | value.0
107 | }
108 | }
109 |
110 | impl From> for Box + Send> {
111 | fn from(value: BoxedNodeSend) -> Self {
112 | value.0
113 | }
114 | }
115 |
116 | impl fmt::Debug for BoxedNode {
117 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
118 | f.debug_struct("BoxedNode").finish()
119 | }
120 | }
121 |
122 | impl fmt::Debug for BoxedNodeSend {
123 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
124 | f.debug_struct("BoxedNodeSend").finish()
125 | }
126 | }
127 |
128 | impl Deref for BoxedNode {
129 | type Target = Box>;
130 | fn deref(&self) -> &Self::Target {
131 | &self.0
132 | }
133 | }
134 |
135 | impl Deref for BoxedNodeSend {
136 | type Target = Box + Send>;
137 | fn deref(&self) -> &Self::Target {
138 | &self.0
139 | }
140 | }
141 |
142 | impl DerefMut for BoxedNode {
143 | fn deref_mut(&mut self) -> &mut Self::Target {
144 | &mut self.0
145 | }
146 | }
147 |
148 | impl DerefMut for BoxedNodeSend {
149 | fn deref_mut(&mut self) -> &mut Self::Target {
150 | &mut self.0
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/rs/synth/src/node/compound/bd.rs:
--------------------------------------------------------------------------------
1 | // ~env: imp 1 >> envperc 0.003 0.3
2 |
3 | // ~p: ~ep >> mul 50 >> add 60
4 |
5 | // o: sin ~p >> mul ~env
6 |
7 | // ~ep: imp 1 >> envperc 0.01 0.1;
8 |
9 | use crate::{
10 | envelope::EnvPerc,
11 | operator::{Add, Mul},
12 | oscillator::SinOsc,
13 | AudioContext, Pass,
14 | };
15 | use crate::{Buffer, Input, Message, Node};
16 | use hashbrown::HashMap;
17 |
18 | use petgraph::graph::NodeIndex;
19 |
20 | use super::process_compound;
21 |
22 | pub struct Bd {
23 | input: NodeIndex,
24 | context: AudioContext,
25 | input_order: Vec,
26 | }
27 |
28 | impl Bd {
29 | pub fn new(decay: f32) -> Self {
30 | Self::from(decay)
31 | }
32 | }
33 |
34 | impl From for Bd {
35 | fn from(decay: f32) -> Self {
36 | let mut context = crate::AudioContextBuilder::::new().channels(2).build();
37 | let input = context.add_mono_node(Pass {});
38 | let env_amp = context.add_mono_node(EnvPerc::new().attack(0.003).decay(decay));
39 | context.tags.insert("d", env_amp);
40 | let env_pitch = context.add_mono_node(EnvPerc::new().attack(0.01).decay(0.1));
41 | let mul = context.add_stereo_node(Mul::new(50.));
42 | let add = context.add_stereo_node(Add::new(60.));
43 | let sin = context.add_mono_node(SinOsc::new());
44 | let sin_amp = context.add_mono_node(Mul::new(0.));
45 | context.chain(vec![sin, sin_amp, context.destination]);
46 | context.chain(vec![input, env_amp, sin_amp]);
47 | context.chain(vec![input, env_pitch, mul, add, sin]);
48 |
49 | Self {
50 | context,
51 | input,
52 | input_order: vec![],
53 | }
54 | }
55 | }
56 |
57 | impl Node for Bd {
58 | fn process(&mut self, inputs: &mut HashMap>, output: &mut [Buffer]) {
59 | process_compound(
60 | inputs,
61 | &self.input_order,
62 | self.input,
63 | &mut self.context,
64 | output,
65 | )
66 | }
67 |
68 | fn send_msg(&mut self, info: Message) {
69 | match info {
70 | Message::SetToNumber(0, value) => self.context.graph[self.context.tags["d"]]
71 | .node
72 | .send_msg(Message::SetToNumber(1, value)),
73 | Message::Index(i) => self.input_order.push(i),
74 | Message::IndexOrder(pos, index) => self.input_order.insert(pos, index),
75 | _ => {}
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/rs/synth/src/node/compound/hh.rs:
--------------------------------------------------------------------------------
1 | // output: noiz 42 >> mul ~env >> hpf 15000 1.0 >> mul 0.8;
2 | // ~env: ~trigger >> envperc 0.001 #decay;
3 | // ~trigger: ~input;
4 | use crate::{
5 | envelope::EnvPerc, filter::ResonantHighPassFilter, operator::Mul, signal::Noise, AudioContext,
6 | Pass,
7 | };
8 | use crate::{Buffer, Input, Message, Node};
9 | use hashbrown::HashMap;
10 |
11 | use petgraph::graph::NodeIndex;
12 |
13 | use super::process_compound;
14 |
15 | pub struct Hh {
16 | input: NodeIndex,
17 | context: AudioContext,
18 | input_order: Vec,
19 | }
20 |
21 | impl Hh {
22 | pub fn new(decay: f32) -> Self {
23 | Self::from(decay)
24 | }
25 | }
26 |
27 | impl From for Hh {
28 | fn from(decay: f32) -> Self {
29 | let mut context = crate::AudioContextBuilder::::new().channels(2).build();
30 | let input = context.add_mono_node(Pass {});
31 |
32 | let source = context.add_mono_node(Noise::new(42));
33 | let filter = context.add_mono_node(ResonantHighPassFilter::new().cutoff(15000.));
34 | let amp = context.add_stereo_node(Mul::new(0.));
35 |
36 | context.chain(vec![source, filter, amp, context.destination]);
37 |
38 | let env_amp = context.add_mono_node(EnvPerc::new().attack(0.003).decay(decay));
39 | context.tags.insert("d", env_amp);
40 | context.chain(vec![input, env_amp, amp]);
41 |
42 | Self {
43 | context,
44 | input,
45 | input_order: vec![],
46 | }
47 | }
48 | }
49 |
50 | impl Node for Hh {
51 | fn process(&mut self, inputs: &mut HashMap>, output: &mut [Buffer]) {
52 | process_compound(
53 | inputs,
54 | &self.input_order,
55 | self.input,
56 | &mut self.context,
57 | output,
58 | );
59 | }
60 |
61 | fn send_msg(&mut self, info: Message) {
62 | match info {
63 | Message::SetToNumber(0, value) => self.context.graph[self.context.tags["d"]]
64 | .node
65 | .send_msg(Message::SetToNumber(1, value)),
66 | Message::Index(i) => self.input_order.push(i),
67 | Message::IndexOrder(pos, index) => self.input_order.insert(pos, index),
68 | _ => {}
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/rs/synth/src/node/compound/mod.rs:
--------------------------------------------------------------------------------
1 | mod bd;
2 | pub use bd::*;
3 | mod hh;
4 | pub use hh::*;
5 | mod sn;
6 | pub use sn::*;
7 | mod sawsynth;
8 | pub use sawsynth::*;
9 | mod squsynth;
10 | pub use squsynth::*;
11 | mod trisynth;
12 | pub use trisynth::*;
13 |
14 | use crate::{AudioContext, Buffer, Input};
15 | use hashbrown::HashMap;
16 | use petgraph::graph::NodeIndex;
17 |
18 | fn process_compound(
19 | inputs: &mut HashMap>,
20 | input_order: &[usize],
21 | input: NodeIndex,
22 | context: &mut AudioContext,
23 | output: &mut [Buffer],
24 | ) {
25 | if inputs.len() == 1 {
26 | let main_input = inputs[&input_order[0]].buffers();
27 | context.graph[input].buffers[0] = main_input[0].clone();
28 | // self.context.graph[self.input].buffers[1] = main_input[1].clone();
29 | let cout = context.next_block();
30 |
31 | output[0][..N].copy_from_slice(&cout[0][..N]);
32 | output[1][..N].copy_from_slice(&cout[1][..N]);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/rs/synth/src/node/compound/sawsynth.rs:
--------------------------------------------------------------------------------
1 | // output: saw ~pitch >> mul ~env;
2 | // ~trigger: ~input;
3 | // ~pitch: ~trigger >> mul 261.626;
4 | // ~env: ~trigger >> envperc #attack #decay;
5 | use crate::{
6 | envelope::EnvPerc,
7 | operator::Mul,
8 | // filter::ResonantHighPassFilter,
9 | oscillator::SawOsc,
10 | AudioContext,
11 | Pass,
12 | };
13 | use crate::{Buffer, Input, Message, Node};
14 | use hashbrown::HashMap;
15 |
16 | use petgraph::graph::NodeIndex;
17 |
18 | use super::process_compound;
19 |
20 | pub struct SawSynth {
21 | input: NodeIndex,
22 | context: AudioContext,
23 | input_order: Vec,
24 | }
25 |
26 | impl SawSynth {
27 | pub fn new(attack: f32, decay: f32) -> Self {
28 | let mut context = crate::AudioContextBuilder::::new().channels(2).build();
29 | let input = context.add_mono_node(Pass {});
30 |
31 | let source = context.add_mono_node(SawOsc::new());
32 | let amp = context.add_stereo_node(Mul::new(0.));
33 |
34 | context.chain(vec![source, amp, context.destination]);
35 |
36 | let env_amp = context.add_mono_node(EnvPerc::new().attack(attack).decay(decay));
37 | context.tags.insert("env_amp", env_amp);
38 | context.chain(vec![input, env_amp, amp]);
39 |
40 | let pitch = context.add_stereo_node(Mul::new(261.63));
41 | context.chain(vec![input, pitch, source]);
42 |
43 | Self {
44 | context,
45 | input,
46 | input_order: vec![],
47 | }
48 | }
49 | }
50 |
51 | impl Node for SawSynth {
52 | fn process(&mut self, inputs: &mut HashMap>, output: &mut [Buffer]) {
53 | process_compound(
54 | inputs,
55 | &self.input_order,
56 | self.input,
57 | &mut self.context,
58 | output,
59 | );
60 | }
61 |
62 | fn send_msg(&mut self, info: Message) {
63 | match info {
64 | Message::SetToNumber(pos, value) => match pos {
65 | 0 => self.context.graph[self.context.tags["env_amp"]]
66 | .node
67 | .send_msg(Message::SetToNumber(0, value)),
68 | 1 => self.context.graph[self.context.tags["env_amp"]]
69 | .node
70 | .send_msg(Message::SetToNumber(1, value)),
71 | _ => {}
72 | },
73 | Message::Index(i) => self.input_order.push(i),
74 | Message::IndexOrder(pos, index) => self.input_order.insert(pos, index),
75 | _ => {}
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/rs/synth/src/node/compound/sn.rs:
--------------------------------------------------------------------------------
1 | // output: sin ~snpitch >> add ~no >> mul ~snenv >> hpf 5000 1.0;
2 | // ~no: noiz 42 >> mul 0.3;
3 | // ~snenv: ~sntriggee >> envperc 0.001 #decay;
4 | // ~snpitch: ~sntriggee >> envperc 0.001 0.1 >> mul 60 >> add 60;
5 | // ~sntriggee: ~input;
6 |
7 | use crate::{
8 | // filter::ResonantHighPassFilter,
9 | envelope::EnvPerc,
10 | operator::{Add, Mul},
11 | oscillator::SinOsc,
12 | signal::Noise,
13 | AudioContext,
14 | Pass,
15 | };
16 | use crate::{Buffer, Input, Message, Node};
17 | use hashbrown::HashMap;
18 |
19 | use petgraph::graph::NodeIndex;
20 |
21 | use super::process_compound;
22 |
23 | pub struct Sn {
24 | input: NodeIndex,
25 | context: AudioContext,
26 | input_order: Vec,
27 | }
28 |
29 | impl Sn {
30 | pub fn new(decay: f32) -> Self {
31 | Self::from(decay)
32 | }
33 | }
34 |
35 | impl From for Sn {
36 | fn from(decay: f32) -> Self {
37 | let mut context = crate::AudioContextBuilder::::new().channels(2).build();
38 | let input = context.add_mono_node(Pass {});
39 | let env_amp = context.add_mono_node(EnvPerc::new().attack(0.001).decay(decay));
40 | context.tags.insert("d", env_amp);
41 | let env_pitch = context.add_mono_node(EnvPerc::new().attack(0.001).decay(0.1));
42 | let mul = context.add_stereo_node(Mul::new(55.));
43 | let add = context.add_stereo_node(Add::new(60.));
44 |
45 | let sin = context.add_mono_node(SinOsc::new());
46 | let mix = context.add_stereo_node(Add::new(0.));
47 | let filter = context.add_stereo_node(Add::new(0.));
48 | let amp = context.add_stereo_node(Mul::new(0.));
49 |
50 | let noise = context.add_mono_node(Noise::new(42));
51 | let noise_amp = context.add_stereo_node(Mul::new(0.3));
52 |
53 | context.chain(vec![sin, mix, filter, amp, context.destination]);
54 | context.chain(vec![noise, noise_amp, mix]);
55 | context.chain(vec![input, env_amp, amp]);
56 | context.chain(vec![input, env_pitch, mul, add, sin]);
57 |
58 | Self {
59 | context,
60 | input,
61 | input_order: vec![],
62 | }
63 | }
64 | }
65 |
66 | impl Node for Sn {
67 | fn process(&mut self, inputs: &mut HashMap>, output: &mut [Buffer]) {
68 | process_compound(
69 | inputs,
70 | &self.input_order,
71 | self.input,
72 | &mut self.context,
73 | output,
74 | )
75 | }
76 |
77 | fn send_msg(&mut self, info: Message) {
78 | match info {
79 | Message::SetToNumber(0, value) => self.context.graph[self.context.tags["d"]]
80 | .node
81 | .send_msg(Message::SetToNumber(1, value)),
82 | Message::Index(i) => self.input_order.push(i),
83 | Message::IndexOrder(pos, index) => self.input_order.insert(pos, index),
84 | _ => {}
85 | }
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/rs/synth/src/node/compound/squsynth.rs:
--------------------------------------------------------------------------------
1 | // output: saw ~pitch >> mul ~env;
2 | // ~trigger: ~input;
3 | // ~pitch: ~trigger >> mul 261.626;
4 | // ~env: ~trigger >> envperc #attack #decay;
5 | use crate::{envelope::EnvPerc, operator::Mul, oscillator::SquOsc, AudioContext, Pass};
6 | use crate::{Buffer, Input, Message, Node};
7 | use hashbrown::HashMap;
8 |
9 | use petgraph::graph::NodeIndex;
10 |
11 | use super::process_compound;
12 |
13 | pub struct SquSynth {
14 | input: NodeIndex,
15 | context: AudioContext,
16 | input_order: Vec,
17 | }
18 |
19 | impl SquSynth {
20 | pub fn new(attack: f32, decay: f32) -> Self {
21 | let mut context = crate::AudioContextBuilder::::new().channels(2).build();
22 | let input = context.add_mono_node(Pass {});
23 |
24 | let source = context.add_mono_node(SquOsc::new());
25 | let amp = context.add_stereo_node(Mul::new(0.));
26 |
27 | context.chain(vec![source, amp, context.destination]);
28 |
29 | let env_amp = context.add_mono_node(EnvPerc::new().attack(attack).decay(decay));
30 | context.tags.insert("d", env_amp);
31 | context.chain(vec![input, env_amp, amp]);
32 |
33 | let pitch = context.add_stereo_node(Mul::new(261.63));
34 | context.chain(vec![input, pitch, source]);
35 |
36 | Self {
37 | context,
38 | input,
39 | input_order: vec![],
40 | }
41 | }
42 | }
43 |
44 | impl Node for SquSynth {
45 | fn process(&mut self, inputs: &mut HashMap>, output: &mut [Buffer]) {
46 | process_compound(
47 | inputs,
48 | &self.input_order,
49 | self.input,
50 | &mut self.context,
51 | output,
52 | );
53 | }
54 |
55 | fn send_msg(&mut self, info: Message) {
56 | match info {
57 | Message::SetToNumber(pos @ 0..=1, value) => self.context.graph[self.context.tags["d"]]
58 | .node
59 | .send_msg(Message::SetToNumber(pos, value)),
60 |
61 | Message::Index(i) => self.input_order.push(i),
62 | Message::IndexOrder(pos, index) => self.input_order.insert(pos, index),
63 | _ => {}
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/rs/synth/src/node/compound/trisynth.rs:
--------------------------------------------------------------------------------
1 | // output: saw ~pitch >> mul ~env;
2 | // ~trigger: ~input;
3 | // ~pitch: ~trigger >> mul 261.626;
4 | // ~env: ~trigger >> envperc #attack #decay;
5 | use crate::{envelope::EnvPerc, operator::Mul, oscillator::TriOsc, AudioContext, Pass};
6 | use crate::{Buffer, Input, Message, Node};
7 | use hashbrown::HashMap;
8 |
9 | use petgraph::graph::NodeIndex;
10 |
11 | use super::process_compound;
12 |
13 | pub struct TriSynth {
14 | input: NodeIndex,
15 | context: AudioContext,
16 | input_order: Vec,
17 | }
18 |
19 | impl TriSynth {
20 | pub fn new(attack: f32, decay: f32) -> Self {
21 | let mut context = crate::AudioContextBuilder::::new().channels(2).build();
22 | let input = context.add_mono_node(Pass {});
23 |
24 | let source = context.add_mono_node(TriOsc::new());
25 | let amp = context.add_stereo_node(Mul::new(0.));
26 |
27 | context.chain(vec![source, amp, context.destination]);
28 |
29 | let env_amp = context.add_mono_node(EnvPerc::new().attack(attack).decay(decay));
30 | context.tags.insert("d", env_amp);
31 | context.chain(vec![input, env_amp, amp]);
32 |
33 | let pitch = context.add_stereo_node(Mul::new(261.63));
34 | context.chain(vec![input, pitch, source]);
35 |
36 | Self {
37 | context,
38 | input,
39 | input_order: vec![],
40 | }
41 | }
42 | }
43 |
44 | impl Node for TriSynth {
45 | fn process(&mut self, inputs: &mut HashMap>, output: &mut [Buffer]) {
46 | process_compound(
47 | inputs,
48 | &self.input_order,
49 | self.input,
50 | &mut self.context,
51 | output,
52 | );
53 | }
54 |
55 | fn send_msg(&mut self, info: Message) {
56 | match info {
57 | Message::SetToNumber(pos @ 0..=1, value) => self.context.graph[self.context.tags["d"]]
58 | .node
59 | .send_msg(Message::SetToNumber(pos, value)),
60 |
61 | Message::Index(i) => self.input_order.push(i),
62 | Message::IndexOrder(pos, index) => self.input_order.insert(pos, index),
63 | _ => {}
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/rs/synth/src/node/delay/delayms.rs:
--------------------------------------------------------------------------------
1 | use crate::{Buffer, Input, Message, Node};
2 | use dasp_ring_buffer as ring_buffer;
3 | type Fixed = ring_buffer::Fixed>;
4 | use hashbrown::HashMap;
5 | #[derive(Debug, Clone)]
6 | pub struct DelayMs {
7 | buf: Vec,
8 | sr: usize,
9 | input_order: Vec,
10 | delay_n: usize,
11 | }
12 |
13 | impl Default for DelayMs {
14 | fn default() -> Self {
15 | Self::new()
16 | }
17 | }
18 |
19 | impl DelayMs {
20 | pub fn new() -> Self {
21 | Self {
22 | buf: vec![],
23 | delay_n: 1,
24 | sr: 44100,
25 | input_order: vec![],
26 | }
27 | }
28 |
29 | pub fn delay(self, delay: f32, chan: u8) -> Self {
30 | let delay_n = ((delay / 1000. * self.sr as f32) as usize).max(1);
31 | let buf = vec![Fixed::from(vec![0.0; delay_n]); chan as usize];
32 |
33 | Self {
34 | buf,
35 | delay_n,
36 | ..self
37 | }
38 | }
39 |
40 | pub fn sr(self, sr: usize) -> Self {
41 | Self { sr, ..self }
42 | }
43 | }
44 |
45 | impl Node for DelayMs {
46 | fn process(&mut self, inputs: &mut HashMap>, output: &mut [Buffer]) {
47 | let main_input = inputs.values().next().unwrap();
48 | if !(1..=2).contains(&main_input.buffers().len()) {
49 | return;
50 | }
51 |
52 | match inputs.len() {
53 | 1 => {
54 | // no modulation
55 | match self.delay_n {
56 | // equal to a pass node
57 | 0 => output[0].copy_from_slice(&main_input.buffers()[0]),
58 | _ => {
59 | let iter = self
60 | .buf
61 | .iter_mut()
62 | .zip(output.iter_mut())
63 | .zip(main_input.buffers());
64 |
65 | for ((fixed, out_buf), main_buf) in iter {
66 | for (out, main) in out_buf.iter_mut().zip(main_buf.iter()) {
67 | *out = fixed.push(*main);
68 | }
69 | }
70 | }
71 | }
72 | }
73 | 2 => {
74 | let main_input = &inputs[&self.input_order[0]]; // can panic if there is no id
75 | let ref_input = &inputs[&self.input_order[1]]; // can panic if there is no id
76 |
77 | let mod_buf = &mut ref_input.buffers();
78 | for i in 0..N {
79 | let mut pos = -mod_buf[0][i] / 1000. * self.sr as f32;
80 | while pos < 0. {
81 | pos += self.buf[0].len() as f32;
82 | }
83 | let pos_int = pos.floor() as usize;
84 | let pos_frac = pos.fract();
85 |
86 | let iter = self
87 | .buf
88 | .iter_mut()
89 | .zip(output.iter_mut())
90 | .zip(main_input.buffers());
91 |
92 | for ((fixed, out_buf), main_buf) in iter {
93 | out_buf[i] = fixed.get(pos_int) * pos_frac
94 | + fixed.get(pos_int + 1) * (1. - pos_frac);
95 | fixed.push(main_buf[i]);
96 | }
97 | }
98 |
99 | // output[1][i] = self.buf2.get(pos_int) * pos_frac + self.buf2.get(pos_int+1) * (1.-pos_frac);
100 |
101 | // self.buf2.push(main_input.buffers()[1][i]);
102 | }
103 | _ => (),
104 | }
105 | }
106 |
107 | fn send_msg(&mut self, info: Message) {
108 | match info {
109 | Message::SetToNumber(0, value) => {
110 | let delay_n = (value / 1000. * self.sr as f32) as usize;
111 | self.delay_n = delay_n;
112 |
113 | if delay_n == 0 {
114 | self.buf.clear();
115 | } else {
116 | let chan = self.buf.len();
117 | self.buf = vec![Fixed::from(vec![0.0; delay_n]); chan];
118 | };
119 | }
120 | Message::Index(i) => self.input_order.push(i),
121 | Message::IndexOrder(pos, index) => self.input_order.insert(pos, index),
122 | Message::ResetOrder => {
123 | self.input_order.clear();
124 | }
125 | _ => {}
126 | }
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/rs/synth/src/node/delay/delayn.rs:
--------------------------------------------------------------------------------
1 | use crate::{Buffer, Input, Message, Node};
2 | use dasp_ring_buffer as ring_buffer;
3 | use hashbrown::HashMap;
4 | type Fixed = ring_buffer::Fixed>;
5 |
6 | #[derive(Debug, Clone)]
7 | pub struct DelayN {
8 | buf: Fixed,
9 | delay_n: usize,
10 | input_order: Vec,
11 | }
12 |
13 | impl DelayN {
14 | pub fn new(n: usize) -> Self {
15 | let delay_n = n;
16 | let init_n = match n {
17 | 0 => 1,
18 | _ => n,
19 | };
20 | let buf = ring_buffer::Fixed::from(vec![0.0; init_n]);
21 | Self {
22 | buf,
23 | delay_n,
24 | input_order: Vec::new(),
25 | }
26 | }
27 | }
28 |
29 | impl Node for DelayN {
30 | fn process(&mut self, inputs: &mut HashMap>, output: &mut [Buffer]) {
31 | if inputs.len() == 1 {
32 | let main_input = inputs.values_mut().next().unwrap();
33 | if self.delay_n != 0 {
34 | for i in 0..N {
35 | output[0][i] = self.buf.push(main_input.buffers()[0][i]);
36 | if main_input.buffers().len() == 1 && output.len() == 2 {
37 | output[1][i] = output[0][i];
38 | }
39 | }
40 | } else {
41 | // same as Pass node
42 | let Some(input) = inputs.values().next() else {
43 | return;
44 | };
45 |
46 | match (input.buffers(), &mut *output) {
47 | ([in_buf], [ref mut out_a, ref mut out_b]) => {
48 | out_a.copy_from_slice(in_buf);
49 | out_b.copy_from_slice(in_buf);
50 | }
51 | _ => {
52 | for (out_buf, in_buf) in output.iter_mut().zip(input.buffers()) {
53 | out_buf.copy_from_slice(in_buf);
54 | }
55 | }
56 | }
57 | }
58 | }
59 | }
60 |
61 | fn send_msg(&mut self, info: Message) {
62 | match info {
63 | Message::SetToNumber(0, value) => {
64 | self.delay_n = value as usize;
65 | self.buf = Fixed::from(vec![0.0; self.delay_n]);
66 | // buf2 = Fixed::from(vec![0.0; delay_n]);
67 | // self.buf.set_first(self.delay_n);
68 | // self.buf2.set_first(delay_n);
69 | }
70 | Message::Index(i) => self.input_order.push(i),
71 | Message::IndexOrder(pos, index) => self.input_order.insert(pos, index),
72 | Message::ResetOrder => {
73 | self.input_order.clear();
74 | }
75 | _ => {}
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/rs/synth/src/node/delay/mod.rs:
--------------------------------------------------------------------------------
1 | mod delayn;
2 | pub use delayn::*;
3 | mod delayms;
4 | pub use delayms::*;
5 |
--------------------------------------------------------------------------------
/rs/synth/src/node/dynamic/eval.rs:
--------------------------------------------------------------------------------
1 | use crate::{BoxedNodeSend, Buffer, Input, Message, Node, NodeData};
2 | use hashbrown::HashMap;
3 | // use evalexpr::*;
4 | use fasteval::Compiler;
5 | use fasteval::Evaler; // use this trait so we can call eval().
6 | use std::collections::BTreeMap; // use this trait so we can call compile().
7 | // use fasteval::eval_compiled;
8 |
9 | pub struct Eval {
10 | pub sr: usize,
11 | pub bpm: f32,
12 | phase: usize,
13 | // code: String,
14 | // precompiled: evalexpr::Node,
15 | var: Vec,
16 | map: BTreeMap,
17 | compiled: Vec,
18 | parser: fasteval::Parser,
19 | slab: Vec,
20 | input_order: Vec,
21 | }
22 |
23 | impl Default for Eval {
24 | fn default() -> Self {
25 | Self::new()
26 | }
27 | }
28 |
29 | impl Eval {
30 | pub fn new() -> Self {
31 | let parser = fasteval::Parser::new();
32 | // let mut slab = fasteval::Slab::new();
33 |
34 | let mut map: BTreeMap = BTreeMap::new();
35 | map.insert("x".to_string(), 0.0);
36 | map.insert("y".to_string(), 0.0);
37 | map.insert("z".to_string(), 0.0);
38 | map.insert("sr".to_string(), 44100.);
39 |
40 | // let compiled = parser.parse("0.0", &mut slab.ps).unwrap().from(&slab.ps).compile(&slab.ps, &mut slab.cs);
41 |
42 | Self {
43 | sr: 44100,
44 | bpm: 120.,
45 | phase: 0,
46 | map,
47 | parser,
48 | var: vec![],
49 | slab: vec![],
50 | compiled: vec![],
51 | input_order: Vec::new(),
52 | }
53 | }
54 |
55 | pub fn sr(mut self, sr: usize) -> Self {
56 | self.map.insert("sr".to_string(), sr as f64);
57 | // self.context.set_value("sr".to_owned(), Value::Int(sr as i64)).unwrap();
58 | Self { sr, ..self }
59 | }
60 |
61 | pub fn bpm(self, bpm: f32) -> Self {
62 | Self { bpm, ..self }
63 | }
64 |
65 | pub fn code(mut self, code: &str) -> Self {
66 | self.apply_code(code);
67 | Self { ..self }
68 | }
69 |
70 | fn apply_code(&mut self, code: &str) {
71 | let lines = code.split(';');
72 | for line in lines {
73 | let mut slab = fasteval::Slab::new();
74 | let mut assign = line.split(":=");
75 | if line.contains(":=") {
76 | self.var
77 | .push(assign.next().unwrap().replace([' ', '\t', '\n'], ""));
78 | }
79 | let compiled = self
80 | .parser
81 | .parse(assign.next().unwrap(), &mut slab.ps)
82 | .unwrap()
83 | .from(&slab.ps)
84 | .compile(&slab.ps, &mut slab.cs);
85 | self.slab.push(slab);
86 | self.compiled.push(compiled);
87 | }
88 | }
89 | }
90 |
91 | impl Node for Eval {
92 | fn process(&mut self, inputs: &mut HashMap>, output: &mut [Buffer