├── gain ├── gain.wasm ├── .cargo │ └── config ├── Cargo.toml ├── dsp │ └── gain.dsp ├── gain.json ├── ui │ └── gain.html ├── src │ └── lib.rs └── js │ └── jquery.knob.min.js ├── assets ├── 4track_v1.ai ├── faust_rust_prefix.rs └── faust_rust_postfix.rs ├── .gitignore ├── images ├── reflect_screen_shot.png └── vl1-emulation-screenshot.png ├── vl1 ├── .cargo │ └── config ├── Cargo.toml ├── vl1.json ├── ui │ ├── scripts │ │ ├── vector.js │ │ ├── aa.js │ │ ├── client.js │ │ ├── lib │ │ │ └── build.js │ │ ├── buttons.js │ │ └── renderer.js │ └── debug.html ├── dsp │ ├── VL1.dsp │ └── ADSR.dsp └── vl1_gui_description.json ├── default ├── .cargo │ └── config ├── Cargo.toml ├── default.json ├── ui │ └── default.html └── src │ └── lib.rs ├── nuke ├── ui │ ├── scripts │ │ ├── .local │ │ │ └── 0.80.1227-dev │ │ │ │ └── config.tic │ │ ├── vector.js │ │ ├── aa.js │ │ ├── lib │ │ │ └── build.js │ │ ├── client.js │ │ └── adsr.js │ └── debug.html ├── .cargo │ └── config ├── Cargo.toml ├── nuke.json ├── nuke_gui_description.json └── dsp │ └── nuke.dsp ├── reflect ├── .cargo │ └── config ├── Cargo.toml ├── README.md ├── reflect.json ├── ui │ ├── scripts │ │ ├── vector.js │ │ ├── aa.js │ │ ├── client.js │ │ └── lib │ │ │ └── build.js │ └── debug.html ├── reflect_gui_description.json └── dsp │ └── reflect.dsp ├── four_track ├── .cargo │ └── config ├── Cargo.toml ├── four_track.json ├── four_track_gui_description.json └── src │ ├── playhead.rs │ └── lib.rs ├── scripts ├── map_2_float.py ├── create_faust2audioanywhere ├── gen_wasm.sh ├── package.sh └── faust2audioanywhere ├── root ├── modules.json ├── aa_logo.svg ├── js │ └── support.js ├── muses_text_logo.svg └── index.html ├── muses ├── muses.json ├── ui │ ├── scripts │ │ ├── vector.js │ │ ├── lib │ │ │ └── build.js │ │ ├── client.js │ │ ├── aa.js │ │ └── adsr.js │ └── debug.html └── muses_gui_description.json └── README.md /gain/gain.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bgaster/aa_examples/HEAD/gain/gain.wasm -------------------------------------------------------------------------------- /assets/4track_v1.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bgaster/aa_examples/HEAD/assets/4track_v1.ai -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | Cargo.lock 3 | /target 4 | target 5 | playground 6 | pkg/ 7 | **/pkg 8 | -------------------------------------------------------------------------------- /images/reflect_screen_shot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bgaster/aa_examples/HEAD/images/reflect_screen_shot.png -------------------------------------------------------------------------------- /images/vl1-emulation-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bgaster/aa_examples/HEAD/images/vl1-emulation-screenshot.png -------------------------------------------------------------------------------- /vl1/.cargo/config: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "wasm32-unknown-unknown" 3 | rustflags = ["-C", "llvm-args=-ffast-math", "-C", "opt-level=3"] -------------------------------------------------------------------------------- /default/.cargo/config: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "wasm32-unknown-unknown" 3 | rustflags = ["-C", "llvm-args=-ffast-math", "-C", "opt-level=3"] -------------------------------------------------------------------------------- /nuke/ui/scripts/.local/0.80.1227-dev/config.tic: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bgaster/aa_examples/HEAD/nuke/ui/scripts/.local/0.80.1227-dev/config.tic -------------------------------------------------------------------------------- /gain/.cargo/config: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "wasm32-unknown-unknown" 3 | rustflags = ["-C", "llvm-args=-ffast-math", "-C", "target-feature=+simd128", "-C", "opt-level=3", "-C", "target-feature=+simd128"] -------------------------------------------------------------------------------- /nuke/.cargo/config: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "wasm32-unknown-unknown" 3 | rustflags = ["-C", "llvm-args=-ffast-math", "-C", "target-feature=+simd128", "-C", "opt-level=3", "-C", "target-feature=+simd128"] -------------------------------------------------------------------------------- /reflect/.cargo/config: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "wasm32-unknown-unknown" 3 | rustflags = ["-C", "llvm-args=-ffast-math", "-C", "target-feature=+simd128", "-C", "opt-level=3", "-C", "target-feature=+simd128"] -------------------------------------------------------------------------------- /four_track/.cargo/config: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "wasm32-unknown-unknown" 3 | rustflags = ["-C", "llvm-args=-ffast-math", "-C", "target-feature=+simd128", "-C", "opt-level=3", "-C", "target-feature=+simd128"] -------------------------------------------------------------------------------- /default/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "default" 3 | version = "0.1.0" 4 | authors = ["Benedict Gaster "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | [lib] 9 | crate-type = ["cdylib", "rlib"] 10 | 11 | [dependencies] 12 | -------------------------------------------------------------------------------- /scripts/map_2_float.py: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/python3 2 | 3 | import sys 4 | 5 | def mapFromTo(x, in_min, in_max, out_min, out_max): 6 | v = (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; 7 | return v 8 | 9 | if __name__ == "__main__": 10 | print(mapFromTo(float(sys.argv[1]), float(sys.argv[2]), float(sys.argv[3]), float(sys.argv[4]), float(sys.argv[5]))) -------------------------------------------------------------------------------- /nuke/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nuke" 3 | version = "0.1.0" 4 | authors = ["Benedict Gaster "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | [lib] 9 | crate-type = ["cdylib", "rlib"] 10 | 11 | [dependencies] 12 | 13 | [profile.release] 14 | lto = "fat" 15 | codegen-units = 1 16 | panic = "abort" -------------------------------------------------------------------------------- /vl1/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "vl1" 3 | version = "0.1.0" 4 | authors = ["Benedict Gaster "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [lib] 10 | crate-type = ["cdylib", "rlib"] 11 | 12 | [dependencies] 13 | 14 | [profile.release] 15 | lto = "fat" 16 | codegen-units = 1 17 | panic = "abort" -------------------------------------------------------------------------------- /gain/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gain" 3 | version = "0.1.0" 4 | authors = ["Benedict Gaster "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | [lib] 9 | crate-type = ["cdylib", "rlib"] 10 | 11 | [dependencies] 12 | wasm-bindgen = "0.2.64" 13 | 14 | [profile.release] 15 | lto = "fat" 16 | codegen-units = 1 17 | panic = "abort" -------------------------------------------------------------------------------- /scripts/create_faust2audioanywhere: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cat <"] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [lib] 10 | crate-type = ["cdylib", "rlib"] 11 | 12 | [dependencies] 13 | wee_alloc = "0.4.5" 14 | 15 | [profile.release] 16 | lto = "fat" 17 | codegen-units = 1 18 | panic = "abort" -------------------------------------------------------------------------------- /reflect/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "reflect" 3 | version = "0.1.0" 4 | authors = ["Benedict Gaster "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | [lib] 9 | crate-type = ["cdylib", "rlib"] 10 | 11 | [dependencies] 12 | libm = { version = "0.2.1" } 13 | 14 | [profile.release] 15 | lto = "fat" 16 | codegen-units = 1 17 | panic = "abort" -------------------------------------------------------------------------------- /gain/dsp/gain.dsp: -------------------------------------------------------------------------------- 1 | declare name "volume"; 2 | declare version "1.0"; 3 | declare author "Grame"; 4 | declare license "BSD"; 5 | declare copyright "(c)GRAME 2006"; 6 | 7 | //----------------------------------------------- 8 | // Volume control in dB 9 | //----------------------------------------------- 10 | 11 | import("stdfaust.lib"); 12 | 13 | gain = vslider("gain_control", 0, -70, +4, 0.1) : ba.db2linear : si.smoo; 14 | 15 | process = *(gain); -------------------------------------------------------------------------------- /scripts/gen_wasm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ $# -eq 0 ]; then 4 | echo "No arguments provided" 5 | exit 1 6 | fi 7 | 8 | cargo build --release --target wasm32-unknown-unknown 9 | 10 | if [ $? -eq 0 ] 11 | then 12 | echo Optimizing WASM output into pkg/$1.wasm 13 | 14 | if [ ! -d "./pkg" ] 15 | then 16 | mkdir ./pkg 17 | fi 18 | 19 | wasm-opt -O4 --enable-simd target/wasm32-unknown-unknown/release/$1.wasm -o pkg/$1.wasm 20 | else 21 | echo Compile failed 22 | fi -------------------------------------------------------------------------------- /reflect/README.md: -------------------------------------------------------------------------------- 1 | ## Reflect 2 | 3 | 4 | 5 | A reverb effect. Originally based on a similar reverb from the amazing open source digital hardware synth, groovebox and FX processor [OTTO](https://github.com/OTTO-project/OTTO). 6 | 7 | Like Nuke the UI is based on the [Muses](https://muses-dmi.github.io/) design and 8 | the two are combined into the [Muses synth](https://github.com/bgaster/aa_examples/muses). 9 | 10 | # Building 11 | 12 | # Controls 13 | 14 | -------------------------------------------------------------------------------- /root/modules.json: -------------------------------------------------------------------------------- 1 | { 2 | "default": "/gain/gain.json", 3 | "modules": 4 | [ 5 | { "name": "Gain", "json_url": "/gain/gain.json"}, 6 | { "name": "Nuke", "json_url": "/nuke/nuke.json"}, 7 | { "name": "VL-1", "json_url": "/vl1/vl1.json"}, 8 | { "name": "Reflect", "json_url": "/reflect/reflect.json"}, 9 | { "name": "Default", "json_url": "/default/default.json"}, 10 | { "name": "Muses", "json_url": "/muses/muses.json"}, 11 | { "name": "4 Track", "json_url": "/four_track/four_track.json"} 12 | ] 13 | } -------------------------------------------------------------------------------- /gain/gain.json: -------------------------------------------------------------------------------- 1 | { 2 | "wasm_url": ["/gain/gain.wasm"], 3 | "gui": { 4 | "url": "/gain/gain.html", 5 | "name": "Gain WASM", 6 | "params": [[-50.0]], 7 | "width": 300, 8 | "height": 300 9 | }, 10 | "info": { 11 | "name": "GAIN", 12 | "vendor": "cuberoo_", 13 | "presets": 1, 14 | "parameters": 0, 15 | "inputs": 1, 16 | "outputs": 1, 17 | "midi_inputs": 0, 18 | "midi_outputs": 0, 19 | "id": 9614, 20 | "version": 1, 21 | "category": "Synth", 22 | "initial_delay": 0, 23 | "preset_chunks": false, 24 | "f64_precision": false, 25 | "silent_when_stopped": false 26 | } 27 | } -------------------------------------------------------------------------------- /default/default.json: -------------------------------------------------------------------------------- 1 | { 2 | "wasm_url": ["/default/default.wasm"], 3 | "gui": { 4 | "url": "/default/default.html", 5 | "name": "Default WASM", 6 | "params": [], 7 | "width": 300, 8 | "height": 300 9 | }, 10 | "info": { 11 | "name": "DEFAULT", 12 | "vendor": "cuberoo_", 13 | "presets": 1, 14 | "parameters": 0, 15 | "inputs": 0, 16 | "outputs": 1, 17 | "midi_inputs": 0, 18 | "midi_outputs": 0, 19 | "id": 9615, 20 | "version": 1, 21 | "category": "Synth", 22 | "initial_delay": 0, 23 | "preset_chunks": false, 24 | "f64_precision": false, 25 | "silent_when_stopped": false 26 | } 27 | } -------------------------------------------------------------------------------- /vl1/vl1.json: -------------------------------------------------------------------------------- 1 | { 2 | "wasm_url": ["/vl1/vl1.wasm"], 3 | "gui": { 4 | "url": "/vl1/vl1.html", 5 | "name": "VL-1", 6 | "params": [], 7 | "width": 1380, 8 | "height": 400 9 | }, 10 | "gui_description": "/vl1/vl1_gui_description.json", 11 | "info": { 12 | "name": "VL1", 13 | "vendor": "cuberoo_", 14 | "presets": 1, 15 | "parameters": 0, 16 | "inputs": 0, 17 | "outputs": 1, 18 | "midi_inputs": 1, 19 | "midi_outputs": 0, 20 | "id": 9614, 21 | "version": 1, 22 | "category": "Synth", 23 | "initial_delay": 0, 24 | "preset_chunks": false, 25 | "f64_precision": false, 26 | "silent_when_stopped": false 27 | } 28 | } -------------------------------------------------------------------------------- /nuke/nuke.json: -------------------------------------------------------------------------------- 1 | { 2 | "wasm_url": ["/nuke/nuke.wasm"], 3 | "gui": { 4 | "url": "/nuke/nuke.html", 5 | "name": "Nuke", 6 | "params": [[0.5, 3.0, 1.0, 0.5]], 7 | "width": 640, 8 | "height": 320 9 | }, 10 | "gui_description": "/nuke/nuke_gui_description.json", 11 | "info": { 12 | "name": "Nuke", 13 | "vendor": "cuberoo_", 14 | "presets": 1, 15 | "parameters": 0, 16 | "inputs": 0, 17 | "outputs": 1, 18 | "midi_inputs": 1, 19 | "midi_outputs": 0, 20 | "id": 9614, 21 | "version": 1, 22 | "category": "Synth", 23 | "initial_delay": 0, 24 | "preset_chunks": false, 25 | "f64_precision": false, 26 | "silent_when_stopped": false 27 | } 28 | } -------------------------------------------------------------------------------- /muses/muses.json: -------------------------------------------------------------------------------- 1 | { 2 | "wasm_url": ["/nuke/nuke.wasm", "/reflect/reflect.wasm"], 3 | "gui": { 4 | "url": "/muses/muses.html", 5 | "name": "Muses", 6 | "params": [], 7 | "width": 740, 8 | "height": 640 9 | }, 10 | "gui_description": "/muses/muses_gui_description.json", 11 | "info": { 12 | "name": "Muses", 13 | "vendor": "cuberoo_", 14 | "presets": 1, 15 | "parameters": 0, 16 | "inputs": 0, 17 | "outputs": 2, 18 | "midi_inputs": 1, 19 | "midi_outputs": 0, 20 | "id": 9614, 21 | "version": 1, 22 | "category": "Synth", 23 | "initial_delay": 0, 24 | "preset_chunks": false, 25 | "f64_precision": false, 26 | "silent_when_stopped": false 27 | } 28 | } -------------------------------------------------------------------------------- /reflect/reflect.json: -------------------------------------------------------------------------------- 1 | { 2 | "wasm_url": ["/reflect/reflect.wasm"], 3 | "gui": { 4 | "url": "/reflect/reflect.html", 5 | "name": "Reflect WASM", 6 | "params": [[1.0, 0.0, 0.5, 0.0]], 7 | "width": 380, 8 | "height": 320 9 | }, 10 | "gui_description": "/reflect/reflect_gui_description.json", 11 | "info": { 12 | "name": "REFLECT", 13 | "vendor": "cuberoo_", 14 | "presets": 1, 15 | "parameters": 0, 16 | "inputs": 1, 17 | "outputs": 2, 18 | "midi_inputs": 0, 19 | "midi_outputs": 0, 20 | "id": 9614, 21 | "version": 1, 22 | "category": "Effect", 23 | "initial_delay": 0, 24 | "preset_chunks": false, 25 | "f64_precision": false, 26 | "silent_when_stopped": false 27 | } 28 | } -------------------------------------------------------------------------------- /four_track/four_track.json: -------------------------------------------------------------------------------- 1 | { 2 | "wasm_url": ["/four_track/four_track.wasm"], 3 | "gui": { 4 | "url": "/four_track/four_track.html", 5 | "name": "4 Track", 6 | "params": [[0.5, 3.0, 1.0, 0.5]], 7 | "width": 640, 8 | "height": 320 9 | }, 10 | "gui_description": "/four_track/four_track_gui_description.json", 11 | "info": { 12 | "name": "4 Track", 13 | "vendor": "cuberoo_", 14 | "presets": 1, 15 | "parameters": 0, 16 | "inputs": 2, 17 | "outputs": 2, 18 | "midi_inputs": 1, 19 | "midi_outputs": 0, 20 | "id": 9614, 21 | "version": 1, 22 | "category": "Effect", 23 | "initial_delay": 0, 24 | "preset_chunks": false, 25 | "f64_precision": false, 26 | "silent_when_stopped": false 27 | } 28 | } -------------------------------------------------------------------------------- /root/aa_logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 9 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /muses/ui/scripts/vector.js: -------------------------------------------------------------------------------- 1 | //const { DH_UNABLE_TO_CHECK_GENERATOR } = require("constants"); 2 | 3 | function Vector(x, y) { 4 | var that = this; 5 | 6 | // Set vector value 7 | that.x = x; 8 | that.y = y; 9 | 10 | // Plus two vectors 11 | that.plus = function (vector){ 12 | return new Vector(that.x + vector.x, that.y + vector.y); 13 | } 14 | 15 | // Minus two vectors 16 | that.minus = function (vector){ 17 | return new Vector(that.x - vector.x, that.y - vector.y); 18 | } 19 | 20 | that.mul = function(vector) { 21 | return new Vector(that.x * vector.x, that.y * vector.y); 22 | } 23 | 24 | that.mul_scalar = function(scalar) { 25 | return new Vector(that.x * scalar, that.y * scalar); 26 | } 27 | 28 | // Overriding property 'length' 29 | Object.defineProperty(that, 'length', { 30 | get: function (){ 31 | return Math.sqrt(Math.pow(that.y, 2) + Math.pow(that.x, 2)); 32 | } 33 | }); 34 | }; -------------------------------------------------------------------------------- /nuke/ui/scripts/vector.js: -------------------------------------------------------------------------------- 1 | //const { DH_UNABLE_TO_CHECK_GENERATOR } = require("constants"); 2 | 3 | function Vector(x, y) { 4 | var that = this; 5 | 6 | // Set vector value 7 | that.x = x; 8 | that.y = y; 9 | 10 | // Plus two vectors 11 | that.plus = function (vector){ 12 | return new Vector(that.x + vector.x, that.y + vector.y); 13 | } 14 | 15 | // Minus two vectors 16 | that.minus = function (vector){ 17 | return new Vector(that.x - vector.x, that.y - vector.y); 18 | } 19 | 20 | that.mul = function(vector) { 21 | return new Vector(that.x * vector.x, that.y * vector.y); 22 | } 23 | 24 | that.mul_scalar = function(scalar) { 25 | return new Vector(that.x * scalar, that.y * scalar); 26 | } 27 | 28 | // Overriding property 'length' 29 | Object.defineProperty(that, 'length', { 30 | get: function (){ 31 | return Math.sqrt(Math.pow(that.y, 2) + Math.pow(that.x, 2)); 32 | } 33 | }); 34 | }; -------------------------------------------------------------------------------- /vl1/ui/scripts/vector.js: -------------------------------------------------------------------------------- 1 | //const { DH_UNABLE_TO_CHECK_GENERATOR } = require("constants"); 2 | 3 | function Vector(x, y) { 4 | var that = this; 5 | 6 | // Set vector value 7 | that.x = x; 8 | that.y = y; 9 | 10 | // Plus two vectors 11 | that.plus = function (vector){ 12 | return new Vector(that.x + vector.x, that.y + vector.y); 13 | } 14 | 15 | // Minus two vectors 16 | that.minus = function (vector){ 17 | return new Vector(that.x - vector.x, that.y - vector.y); 18 | } 19 | 20 | that.mul = function(vector) { 21 | return new Vector(that.x * vector.x, that.y * vector.y); 22 | } 23 | 24 | that.mul_scalar = function(scalar) { 25 | return new Vector(that.x * scalar, that.y * scalar); 26 | } 27 | 28 | // Overriding property 'length' 29 | Object.defineProperty(that, 'length', { 30 | get: function (){ 31 | return Math.sqrt(Math.pow(that.y, 2) + Math.pow(that.x, 2)); 32 | } 33 | }); 34 | }; -------------------------------------------------------------------------------- /reflect/ui/scripts/vector.js: -------------------------------------------------------------------------------- 1 | //const { DH_UNABLE_TO_CHECK_GENERATOR } = require("constants"); 2 | 3 | function Vector(x, y) { 4 | var that = this; 5 | 6 | // Set vector value 7 | that.x = x; 8 | that.y = y; 9 | 10 | // Plus two vectors 11 | that.plus = function (vector){ 12 | return new Vector(that.x + vector.x, that.y + vector.y); 13 | } 14 | 15 | // Minus two vectors 16 | that.minus = function (vector){ 17 | return new Vector(that.x - vector.x, that.y - vector.y); 18 | } 19 | 20 | that.mul = function(vector) { 21 | return new Vector(that.x * vector.x, that.y * vector.y); 22 | } 23 | 24 | that.mul_scalar = function(scalar) { 25 | return new Vector(that.x * scalar, that.y * scalar); 26 | } 27 | 28 | // Overriding property 'length' 29 | Object.defineProperty(that, 'length', { 30 | get: function (){ 31 | return Math.sqrt(Math.pow(that.y, 2) + Math.pow(that.x, 2)); 32 | } 33 | }); 34 | }; -------------------------------------------------------------------------------- /reflect/ui/debug.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | ui 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 20 | 21 | 22 | 32 | 33 | -------------------------------------------------------------------------------- /vl1/ui/debug.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | ui 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 20 | 21 | 22 | 32 | 33 | -------------------------------------------------------------------------------- /vl1/ui/scripts/aa.js: -------------------------------------------------------------------------------- 1 | function OnParamChange(node, param, value) { 2 | } 3 | 4 | function controlChange(ctrlTag, value) { 5 | } 6 | 7 | function sendAttack(value) { 8 | parent.sendMsg(1, 0, 0, map(value, 0, 9, 0.001, 0.5)) 9 | } 10 | 11 | function sendDecay(value) { 12 | parent.sendMsg(1, 0, 1, map(value, 0.0, 9.0, 0.001, 2.0)) 13 | } 14 | 15 | function sendPitchMod(value) { 16 | parent.sendMsg(1, 0, 2, value) 17 | } 18 | 19 | function sendRelease(value) { 20 | parent.sendMsg(1, 0, 3, map(value, 0.0, 9.0, 0.001, 2.0)) 21 | } 22 | 23 | function sendSustain(value) { 24 | parent.sendMsg(1, 0, 4, map(value, 0, 9, 0.1, 1.0)) 25 | } 26 | 27 | function sendTremelo(value) { 28 | parent.sendMsg(1, 0, 5, value) 29 | } 30 | 31 | function sendVibrato(value) { 32 | parent.sendMsg(1, 0, 6, value) 33 | } 34 | 35 | function sendWaveform(value) { 36 | parent.sendMsg(1, 0, 7, value) 37 | } 38 | 39 | function map(x, in_min, in_max, out_min, out_max) { 40 | return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min 41 | } -------------------------------------------------------------------------------- /nuke/ui/debug.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | ui 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 22 | 23 | 33 | 34 | -------------------------------------------------------------------------------- /muses/ui/debug.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | ui 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 22 | 23 | 24 | 34 | 35 | -------------------------------------------------------------------------------- /root/js/support.js: -------------------------------------------------------------------------------- 1 | var console_msg = 0; 2 | var send_param = 1; 3 | var get_param = 2; 4 | var change_module = 3; 5 | 6 | function getParam(paramIdx) { 7 | var message = { 8 | "msg": "getParam", 9 | "index": paramIdx, 10 | }; 11 | external.invoke(JSON.stringify(message)); 12 | } 13 | 14 | function sendParam(paramIdx, value) { 15 | var message = { 16 | "msg": "sendParam", 17 | "index": paramIdx, 18 | "value": value 19 | }; 20 | external.invoke(JSON.stringify(message)); 21 | } 22 | 23 | function sendMsg(msgType, node, paramIdx, value) { 24 | if (value == undefined) { 25 | var message = { 26 | "msg": msgType, 27 | "node": node, 28 | "index": paramIdx, 29 | }; 30 | } 31 | else { 32 | var message = { 33 | "msg": msgType, 34 | "node": node, 35 | "index": paramIdx, 36 | "value": value 37 | }; 38 | } 39 | external.invoke(JSON.stringify(message)); 40 | } 41 | 42 | function sendConsole(value) { 43 | var message = { 44 | "msg": "console", 45 | "node": 0, 46 | "index": 0, 47 | "value": value 48 | }; 49 | external.invoke(JSON.stringify(message)); 50 | } -------------------------------------------------------------------------------- /reflect/ui/scripts/aa.js: -------------------------------------------------------------------------------- 1 | const paramShape = 0 2 | const paramSpread = 1 3 | const paramLength = 2 4 | const paramShimmer = 3 5 | 6 | // Need to add midi learn 7 | const controlShape = 1 8 | const controlSpread = 2 9 | const controlLength = 3 10 | const controlShimmer = 4 11 | const controlDryWet = 5 12 | 13 | function OnParamChange(node, param, value) { 14 | if (param == paramShape) { 15 | client.renderer.updateShape(value) 16 | } 17 | else if (param == paramSpread) { 18 | client.renderer.updateSpread(value) 19 | } 20 | else if (param == paramRelation) { 21 | client.renderer.updateLength(value) 22 | } 23 | else if (param == paramShimmer) { 24 | client.renderer.updateShimmer(value) 25 | } 26 | } 27 | 28 | function controlChange(ctrlTag, value) { 29 | if (ctrlTag == controlShape && client) { 30 | parent.sendMsg(1, 0, 3, client.renderer.controlShape(value)) 31 | } 32 | else if (ctrlTag == controlSpread && client) { 33 | parent.sendMsg(1, 0, 5, client.renderer.controlSpread(value)) 34 | } 35 | else if (ctrlTag == controlLength && client) { 36 | parent.sendMsg(1, 0, 1, client.renderer.controlLength(value)) 37 | } 38 | else if (ctrlTag == controlShimmer && client) { 39 | parent.sendMsg(1, 0, 4, client.renderer.controlShimmer(value)) 40 | } 41 | else if (ctrlTag == controlDryWet && client) { 42 | parent.sendMsg(1, 0, 0, client.renderer.controlDryWet(value)) 43 | } 44 | } -------------------------------------------------------------------------------- /root/muses_text_logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 13 | 14 | 15 | 16 | 17 | 18 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /scripts/package.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Package up AA example release 4 | # Really we need to move to a more robust was of generating a package, but 5 | # for now we assume that each of the examples to be added are already built, 6 | # including the corresponding files: 7 | # 8 | # module/module.json 9 | # module/target/wasm32-unknown-unknown/release/module.wasm 10 | # module/ui/index.html 11 | # module/any extras required for example 12 | 13 | rm -rf ./pkg 14 | mkdir -p ./pkg 15 | cp -r ./root/* ./pkg 16 | 17 | # each audio graph 18 | mkdir ./pkg/nuke 19 | cp -r nuke/nuke.json ./pkg/nuke 20 | cp -r nuke/target/wasm32-unknown-unknown/release/nuke.wasm ./pkg/nuke 21 | cp -r nuke/ui/index.html ./pkg/nuke/nuke.html 22 | 23 | mkdir ./pkg/gain 24 | mkdir ./pkg/gain/js 25 | cp -r gain/js/* ./pkg/gain/js 26 | cp -r gain/gain.json ./pkg/gain 27 | cp -r gain/target/wasm32-unknown-unknown/release/gain.wasm ./pkg/gain 28 | cp -r gain/ui/gain.html ./pkg/gain/gain.html 29 | 30 | mkdir ./pkg/reflect 31 | cp -r reflect/reflect.json ./pkg/reflect 32 | cp -r reflect/target/wasm32-unknown-unknown/release/reflect.wasm ./pkg/reflect 33 | cp -r reflect/ui/index.html ./pkg/reflect/reflect.html 34 | 35 | mkdir ./pkg/vl1 36 | cp -r vl1/vl1.json ./pkg/vl1 37 | cp -r vl1/target/wasm32-unknown-unknown/release/vl1.wasm ./pkg/vl1 38 | cp -r vl1/ui/index.html ./pkg/vl1/vl1.html 39 | 40 | mkdir ./pkg/default 41 | cp -r default/default.json ./pkg/default 42 | cp -r default/target/wasm32-unknown-unknown/release/default.wasm ./pkg/default 43 | cp -r default/ui/default.html ./pkg/default/default.html 44 | 45 | mkdir ./pkg/muses/ 46 | cp -r muses/muses.json ./pkg/muses 47 | # NOTE: muses requires tht nuke.wasm and reflect.wasm are installed in their respective directories 48 | cp -r muses/ui/index.html ./pkg/muses/muses.html -------------------------------------------------------------------------------- /vl1/dsp/VL1.dsp: -------------------------------------------------------------------------------- 1 | declare aavoices "1"; 2 | 3 | import("stdfaust.lib"); 4 | 5 | waveforms = piano, fantasy, violin, flute, 6 | guitar1 : ba.selectn(5, wave) 7 | with { 8 | wave = nentry("/Waveform", 0, 0, 4, 1); 9 | freq = hslider("freq", 440, 50, 2000, 0.01); 10 | gain = hslider("gain", 0.5, 0, 1, 0.01); 11 | g = button("gate"); 12 | 13 | piano = os.pulsetrain(freq, 0.7) * envelope : filter 14 | with{ 15 | envelope = en.adsr(0.005, 0.178, 0.306, 0.178, g) * gain; 16 | filter = fi.resonlp(3000,0.5,gain); 17 | }; 18 | 19 | fantasy = os.pulsetrain(2*freq + vibrato, 0.5) * envelope : filter 20 | with { 21 | envelope = en.adsr(0.005, 0.05, 0.830, 1.303, g) * gain; 22 | filter = fi.resonlp(3000,0.5,gain); 23 | vibrato = os.lf_triangle(5.7)*10; 24 | }; 25 | 26 | violin = ((os.pulsetrain(freq + vibrato, 0.5) * envelope) + (os.pulsetrain(freq + vibrato, 0.4) * envelope) + (os.pulsetrain(freq + vibrato, 0.3) * envelope) + (os.pulsetrain(freq + vibrato, 0.2) * envelope) + (os.pulsetrain(freq + vibrato, 0.1) * envelope))/5 : filter 27 | with { 28 | envelope = en.adsr(0.05, 0.05, 0.830, 0.25, g) * gain; 29 | filter = fi.resonlp(3500,5,gain); 30 | vibrato = os.lf_triangle(5.7)*5; 31 | }; 32 | 33 | flute = os.pulsetrain(freq + vibrato, 0.5) * envelope : filter 34 | with { 35 | envelope = en.adsr(0.05, 0.05, 0.830, 0.25, g) * gain; 36 | filter = fi.resonlp(3000,0.5,gain); 37 | vibrato = os.lf_triangle(5.7)*3; 38 | }; 39 | 40 | guitar1 = (os.pulsetrain(0.5*freq, 0.1) * envelope + os.pulsetrain(0.5*freq, 0.2) * envelope)/2 : filter 41 | with { 42 | envelope = en.adsr(0.03, 0.05, 0.830, 0.25, g) * gain; 43 | filter = fi.resonlp(3500,2,gain); 44 | }; 45 | }; 46 | 47 | process = vgroup("voices", par(n, 1, vgroup("aavoice%n", waveforms))) :> _ ; 48 | 49 | //process = waveforms : _ ; 50 | -------------------------------------------------------------------------------- /nuke/ui/scripts/aa.js: -------------------------------------------------------------------------------- 1 | const paramFilter = 0 2 | const paramWave = 1 3 | const paramRelation = 2 4 | const paramSub = 3 5 | 6 | // Need to add midi learn 7 | const controlFilter = 1 8 | const controlWave = 2 9 | const controlRelation = 3 10 | const controlSub = 4 11 | 12 | const controlAttack = 5 13 | const controlDecay = 6 14 | const controlSubstain = 7 15 | const controlRelease = 8 16 | 17 | function OnParamChange(node, param, value) { 18 | if (param == paramFilter) { 19 | client.renderer.updateFilter(value) 20 | } 21 | else if (param == paramWave) { 22 | client.renderer.updateWave(value) 23 | } 24 | else if (param == paramRelation) { 25 | client.renderer.updateRelation(value) 26 | } 27 | else if (param == paramSub) { 28 | client.renderer.updateSub(value) 29 | } 30 | } 31 | 32 | function controlChange(ctrlTag, value) { 33 | if (ctrlTag == controlFilter && client) { 34 | parent.sendMsg(1, 0, 0, client.renderer.controlFilter(value)) 35 | } 36 | else if (ctrlTag == controlWave && client) { 37 | parent.sendMsg(1, 0, 3, client.renderer.controlWave(value)) 38 | } 39 | else if (ctrlTag == controlRelation && client) { 40 | parent.sendMsg(1, 0, 1, client.renderer.controlRelation(value)) 41 | } 42 | else if (ctrlTag == controlSub && client) { 43 | parent.sendMsg(1, 0, 2, client.renderer.controlSub(value)) 44 | } 45 | else if (ctrlTag == controlAttack && client) { 46 | parent.sendMsg(1, 0, 4, client.adsr.controlAttack(value)) 47 | } 48 | else if (ctrlTag == controlDecay && client) { 49 | parent.sendMsg(1, 0, 5, client.adsr.controlDecay(value)) 50 | } 51 | else if (ctrlTag == controlSubstain && client) { 52 | parent.sendMsg(1, 0, 7, client.adsr.controlSubstain(value)) 53 | } 54 | else if (ctrlTag == controlRelease && client) { 55 | parent.sendMsg(1, 0, 6, client.adsr.controlRelease(value)) 56 | } 57 | } -------------------------------------------------------------------------------- /reflect/reflect_gui_description.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Reflect", 3 | "width": 180, 4 | "height": 143, 5 | "widgets": { 6 | "sliders": [ 7 | { 8 | "type": "float_slider", 9 | "name": "length", 10 | "x": 5, "y": 18, 11 | "w": 80, "h": 15, 12 | "init": 0.5, 13 | "min": 0.0, 14 | "max": 1.0, 15 | "digits": 2, 16 | "node": 0, 17 | "index": 1 18 | }, 19 | { 20 | "type": "float_slider", 21 | "name": "shape", 22 | "anchored": "anchored_below", 23 | "w": 80, "h": 15, 24 | "init": 1.0, 25 | "min": 0.0, 26 | "max": 2.0, 27 | "node": 0, 28 | "index": 3 29 | }, 30 | { 31 | "type": "float_slider", 32 | "name": "shimmer", 33 | "anchored": "anchored_below", 34 | "w": 100, "h": 15, 35 | "init": 0.0, 36 | "min": 0.0, 37 | "max": 1.2, 38 | "node": 0, 39 | "index": 4 40 | }, 41 | { 42 | "type": "float_slider", 43 | "name": "spread", 44 | "anchored": "anchored_below", 45 | "w": 80, "h": 15, 46 | "init": 0.0, 47 | "min": 0.0, 48 | "max": 1.0, 49 | "node": 0, 50 | "index": 5 51 | }, 52 | { 53 | "type": "float_slider", 54 | "name": "dry/wet", 55 | "anchored": "anchored_below", 56 | "w": 80, "h": 15, 57 | "init": 0.0, 58 | "min": 0.0, 59 | "max": 1.0, 60 | "node": 0, 61 | "index": 0 62 | } 63 | ], 64 | "logo": { "x": 115, "y": 70, "scale_x": 0.25, "scale_y": 0.25 } 65 | } 66 | } -------------------------------------------------------------------------------- /default/ui/default.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Default 6 | 7 | 8 | 9 | 10 | 54 | 55 | 62 | 63 | 64 |
65 |

Default

66 |
67 | 68 | -------------------------------------------------------------------------------- /nuke/nuke_gui_description.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Nuke", 3 | "width": 180, 4 | "height": 143, 5 | "widgets": { 6 | "sliders": [ 7 | { 8 | "type": "float_slider", 9 | "name": "wv", 10 | "x": 5, "y": 18, 11 | "w": 80, "h": 15, 12 | "init": 0.5, 13 | "min": 0.08, 14 | "max": 4.0, 15 | "digits": 2, 16 | "node": 0, 17 | "index": 3 18 | }, 19 | { 20 | "type": "float_slider", 21 | "name": "sub", 22 | "anchored": "anchored_below", 23 | "w": 80, "h": 15, 24 | "init": 1.0, 25 | "min": 0.0, 26 | "max": 1.0, 27 | "node": 0, 28 | "index": 2 29 | }, 30 | { 31 | "type": "float_slider", 32 | "name": "rel", 33 | "anchored": "anchored_below", 34 | "w": 80, "h": 15, 35 | "init": 3.0, 36 | "min": 0.0, 37 | "max": 3.01, 38 | "node": 0, 39 | "index": 1 40 | }, 41 | { 42 | "type": "float_slider", 43 | "name": "filter", 44 | "anchored": "anchored_below", 45 | "w": 80, "h": 15, 46 | "init": 0.5, 47 | "min": 0.0, 48 | "max": 1.0, 49 | "node": 0, 50 | "index": 0 51 | } 52 | ], 53 | "adsrs": [ 54 | { 55 | "type": "adsr", 56 | "name": "env", 57 | "x": 95, "y": 18, 58 | "w": 80, "h": 36, 59 | "node": 0, 60 | 61 | "indexA": 4, 62 | "initA": 2.0001, 63 | "minA": 0.0, 64 | "maxA": 4.0, 65 | 66 | "indexD": 5, 67 | "initD": 1.0, 68 | "minD": 0.0, 69 | "maxD": 4.0, 70 | 71 | "indexS": 7, 72 | "initS": 0.75, 73 | "minS": 0.0, 74 | "maxS": 1.0, 75 | 76 | "indexR": 6, 77 | "initR": 2.0, 78 | "minR": 0.0, 79 | "maxR": 4.0 80 | } 81 | ], 82 | "logo": { "x": 115, "y": 70, "scale_x": 0.25, "scale_y": 0.25 } 83 | } 84 | } -------------------------------------------------------------------------------- /reflect/ui/scripts/client.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | function Client () { 4 | this.install = function (host) { 5 | console.info('Client', 'Installing..') 6 | 7 | this.renderer = new Renderer(this) 8 | 9 | host.appendChild(this.renderer.el); 10 | 11 | // host.appendChild(this.renderer.el) 12 | 13 | document.addEventListener('keypress', (e) => { this.handleKeyPress(e) }, false ); 14 | // document.addEventListener('mousedown', (e) => { this.cursor.down(e) }, false) 15 | // document.addEventListener('mousemove', (e) => { this.cursor.move(e) }, false) 16 | // document.addEventListener('contextmenu', (e) => { this.cursor.alt(e) }, false) 17 | // document.addEventListener('mouseup', (e) => { this.cursor.up(e) }, false) 18 | // document.addEventListener('copy', (e) => { this.copy(e) }, false) 19 | // document.addEventListener('cut', (e) => { this.cut(e) }, false) 20 | // document.addEventListener('paste', (e) => { this.paste(e) }, false) 21 | // window.addEventListener('resize', (e) => { this.onResize() }, false) 22 | // window.addEventListener('dragover', (e) => { e.stopPropagation(); e.preventDefault(); e.dataTransfer.dropEffect = 'copy' }) 23 | // window.addEventListener('drop', this.onDrop) 24 | } 25 | 26 | this.start = () => { 27 | console.log('Client', 'Starting..') 28 | 29 | this.renderer.start() 30 | 31 | setInterval(() => { this.renderer.update() }, 33) // redraw at 30hz 32 | setTimeout(() => { document.body.className += ' ready' }, 250) 33 | } 34 | 35 | this.update = () => { 36 | console.log('Client', 'Update...') 37 | this.renderer.update() 38 | } 39 | 40 | this.clear = () => { 41 | } 42 | 43 | this.reset = () => { 44 | } 45 | 46 | this.whenOpen = (file, data) => { 47 | } 48 | 49 | this.getPadding = () => { 50 | return { x: 60, y: 90 } 51 | } 52 | 53 | this.getWindowSize = () => { 54 | return { width: window.innerWidth, height: window.innerHeight } 55 | } 56 | 57 | this.getProjectSize = () => { 58 | return this.tool.settings.size 59 | } 60 | 61 | this.getPaddedSize = () => { 62 | const rect = this.getWindowSize() 63 | const pad = this.getPadding() 64 | return { width: step(rect.width - pad.x, 15), height: step(rect.height - pad.y, 15) } 65 | } 66 | 67 | this.handleKeyPress = (e) => { 68 | } 69 | 70 | function sizeOffset (a, b) { return { width: a.width - b.width, height: a.height - b.height } } 71 | function step (v, s) { return Math.round(v / s) * s } 72 | } 73 | -------------------------------------------------------------------------------- /assets/faust_rust_prefix.rs: -------------------------------------------------------------------------------- 1 | #![feature(wasm_target_feature)] 2 | 3 | // ALL OF THE FOLLOWING WARNINGS NEED TO BE ADDRESSED IN THE FAUST COMPILER 4 | #![allow(non_snake_case)] 5 | #![allow(unused_mut)] 6 | #![allow(unused_parens)] 7 | #![allow(non_camel_case_types)] 8 | #![allow(dead_code)] 9 | #![allow(unused_variables)] 10 | // REMOVE SOMETIME SOON :-) 11 | 12 | const MAX_PARAM_SIZE: usize = 1024; 13 | #[no_mangle] 14 | pub static mut PARAM_NAME: [u8;MAX_PARAM_SIZE] = [65;MAX_PARAM_SIZE]; 15 | 16 | const MAX_BUFFER_SIZE: usize = 1024; 17 | 18 | // Everything following should not be visible to the outside world 19 | 20 | #[derive(Clone)] 21 | struct ParamRange { 22 | init: f32, 23 | min: f32, 24 | max: f32, 25 | step: f32, 26 | } 27 | 28 | impl ParamRange { 29 | pub fn new(init: f32, min: f32, max: f32, step: f32) -> Self { 30 | Self { 31 | init, 32 | min, 33 | max, 34 | step, 35 | } 36 | } 37 | } 38 | 39 | #[derive(Clone)] 40 | struct Param { 41 | index: i32, 42 | range: ParamRange, 43 | } 44 | 45 | impl Param { 46 | pub fn new(name: String, index: i32, range: ParamRange) -> Self { 47 | Self { 48 | index, 49 | range 50 | } 51 | } 52 | } 53 | 54 | // Notes and voices 55 | 56 | type Note = i32; 57 | type Pitch = f32; 58 | 59 | /// convert midi note to its corresponding frequency 60 | #[inline] 61 | fn to_freq(n: i32) -> Pitch { 62 | 2.0f32.powf( (n - 69) as Pitch / 12.0 ) * 440.0 63 | } 64 | 65 | /// convert midi note to an index within voices range, can then be used as 66 | /// sample index, for example. 67 | #[inline] 68 | fn to_index(n: i32, voices: u32) -> u32 { 69 | (12.0*(to_freq(n) / 130.81).log2()).round().abs() as u32 % voices 70 | } 71 | 72 | #[inline] 73 | fn freq_to_index(freq: f32, voices: u32) -> u32 { 74 | (12.0*(freq / 130.81).log2()).round().abs() as u32 % voices 75 | } 76 | 77 | /// convert midi note to its corresponding frequency, with explicit base tuning 78 | #[inline] 79 | fn to_freq_tuning(n:i32, tuning: Pitch) -> Pitch { 80 | 2.0f32.powf( (n - 69) as f32 / 12.0 ) * tuning 81 | } 82 | 83 | #[derive(Copy, Clone)] 84 | struct VoiceInfo { 85 | pub active: bool, 86 | pub note: Note, 87 | pub channel: i32, 88 | pub voice_age: i64, 89 | } 90 | 91 | impl VoiceInfo { 92 | pub fn new() -> VoiceInfo { 93 | VoiceInfo { 94 | active: false, 95 | note: 0, 96 | channel: 0, 97 | voice_age: 0, 98 | } 99 | } 100 | } -------------------------------------------------------------------------------- /four_track/four_track_gui_description.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "4 Track", 3 | "width": 440, 4 | "height": 230, 5 | "widgets": { 6 | "sliders": [ 7 | ], 8 | "adsrs": [ 9 | ], 10 | "global_midi": [ 11 | { "midi_off": { "name": "stop", "node": 0, "index": 10 } } 12 | ], 13 | "4tracks": { "x": 20, "y": 10, "width": 320, "time_in_secs": 60 }, 14 | "checkboxes": [ 15 | { 16 | "type": "checkbox", 17 | "label": "rec", 18 | "x": 50, "y": 190, 19 | "init": false, 20 | "node": 0, 21 | "index": 1, 22 | "midi_toggle": { "name": "record", "node": 0, "index": 1 }, 23 | "midi_off": { "name": "stop" } 24 | }, 25 | { 26 | "type": "checkbox", 27 | "label": "play", 28 | "anchored": "anchored_right", 29 | "init": false, 30 | "node": 0, 31 | "index": 6, 32 | "midi_on": { "name": "play", "node": 0, "index": 6 }, 33 | "midi_off": { "name": "stop" } 34 | }, 35 | { 36 | "type": "checkbox", 37 | "label": "M1", 38 | "x": 160, "y": 190, 39 | "init": false, 40 | "node": 0, 41 | "index": 2 42 | }, 43 | { 44 | "type": "checkbox", 45 | "label": "M2", 46 | "anchored": "anchored_right", 47 | "init": false, 48 | "node": 0, 49 | "index": 3 50 | }, 51 | { 52 | "type": "checkbox", 53 | "label": "M3", 54 | "anchored": "anchored_right", 55 | "init": false, 56 | "node": 0, 57 | "index": 4 58 | }, 59 | { 60 | "type": "checkbox", 61 | "label": "M4", 62 | "anchored": "anchored_right", 63 | "init": false, 64 | "node": 0, 65 | "index": 5 66 | } 67 | ], 68 | "radio_buttons": [ 69 | { 70 | "type": "radiobutton", 71 | "name": "Track", 72 | "direction": "direction_vertical", 73 | "x" : 370, 74 | "y" : 30, 75 | "buttons": [ 76 | { "label": "T1", "node": 0, "index": 0, "value": 0.0 }, 77 | { "label": "T2", "node": 0, "index": 0, "value": 1.0 }, 78 | { "label": "T3", "node": 0, "index": 0, "value": 2.0 }, 79 | { "label": "T4", "node": 0, "index": 0, "value": 3.0 } 80 | ] 81 | } 82 | ], 83 | "logo": { "x": 360, "y": 150, "scale_x": 0.25, "scale_y": 0.25 } 84 | } 85 | } -------------------------------------------------------------------------------- /vl1/ui/scripts/client.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | function Client () { 4 | this.install = function (host) { 5 | console.info('Client', 'Installing..') 6 | 7 | this.renderer = new Renderer(this) 8 | 9 | // add canvas to the DOM 10 | host.appendChild(this.renderer.el); 11 | 12 | document.addEventListener('keypress', (e) => { handleKeyPress(e) }, false ); 13 | document.addEventListener('mousedown', (e) => mouseDown(e, this), false) 14 | document.addEventListener('mousemove', (e) => { }, false) 15 | document.addEventListener('mouseup', (e) => mouseUp(e, this), false) 16 | 17 | 18 | //window.addEventListener('resize', (e) => { this.onResize() }, false) 19 | window.addEventListener('dragover', (e) => { }) 20 | } 21 | 22 | this.start = () => { 23 | console.log('Client', 'Starting..') 24 | 25 | this.renderer.start() 26 | 27 | setInterval(() => { this.update() }, 33) // redraw at 30hz 28 | setTimeout(() => { document.body.className += ' ready' }, 250) 29 | } 30 | 31 | this.update = () => { 32 | this.renderer.update() 33 | } 34 | 35 | this.clear = () => { 36 | } 37 | 38 | this.reset = () => { 39 | } 40 | 41 | this.getPadding = () => { 42 | return { x: 0, y: 0 } 43 | } 44 | 45 | this.getWindowSize = () => { 46 | return { width: window.innerWidth, height: window.innerHeight } 47 | } 48 | 49 | this.getProjectSize = () => { 50 | return this.tool.settings.size 51 | } 52 | 53 | this.getPaddedSize = () => { 54 | const rect = this.getWindowSize() 55 | const pad = this.getPadding() 56 | return { width: step(rect.width - pad.x, 15), height: step(rect.height - pad.y, 15) } 57 | } 58 | 59 | this.handleKeyPress = (e) => { 60 | if (e.key === "q") { 61 | alert("hello"); 62 | } 63 | else if (e.key === "a") { 64 | 65 | } 66 | else if (e.key === "w") { 67 | 68 | } 69 | else if (e.key === "s") { 70 | 71 | } 72 | else if (e.key === "e") { 73 | 74 | } 75 | else if (e.key === "d") { 76 | 77 | } 78 | else if (e.key === "r") { 79 | 80 | } else if (e.key === "f") { 81 | 82 | } 83 | } 84 | var hold = false 85 | function mouseDown(e, client) { 86 | hold = true 87 | client.renderer.hold = hold 88 | buttonPresses(e, client); 89 | } 90 | 91 | function mouseUp(e, client){ 92 | hold = false 93 | client.renderer.hold = hold 94 | buttonUnpresses(client); 95 | } 96 | 97 | 98 | 99 | 100 | function sizeOffset (a, b) { return { width: a.width - b.width, height: a.height - b.height } } 101 | function step (v, s) { return Math.round(v / s) * s } 102 | } 103 | -------------------------------------------------------------------------------- /assets/faust_rust_postfix.rs: -------------------------------------------------------------------------------- 1 | #[no_mangle] 2 | pub fn get_input(index: u32) -> u32 { 3 | unsafe { ENGINE.get_input(index) } 4 | } 5 | 6 | #[no_mangle] 7 | pub fn get_output(index: u32) -> u32 { 8 | unsafe { ENGINE.get_output(index) } 9 | } 10 | 11 | #[no_mangle] 12 | pub fn set_input(index: u32, offset: u32) { 13 | unsafe { ENGINE.set_input(index, offset); }; 14 | } 15 | 16 | #[no_mangle] 17 | pub fn set_output(index: u32, offset: u32) { 18 | unsafe { ENGINE.set_output(index, offset); }; 19 | } 20 | 21 | #[no_mangle] 22 | pub fn handle_note_on(mn: i32, vel: f32) { 23 | unsafe { ENGINE.handle_note_on(mn, vel); } 24 | } 25 | 26 | #[no_mangle] 27 | pub fn handle_note_off(mn: i32, vel: f32) { 28 | unsafe { ENGINE.handle_note_off(mn, vel); } 29 | } 30 | 31 | #[no_mangle] 32 | pub fn get_voices() -> i32 { 33 | unsafe { ENGINE.get_voices() } 34 | } 35 | 36 | #[no_mangle] 37 | pub fn get_param_index(length: i32) -> i32 { 38 | if length < MAX_PARAM_SIZE as i32 { 39 | let mut param = String::new(); 40 | for i in 0..length as usize { 41 | let c = unsafe { PARAM_NAME[i] } as char; 42 | param.push(c); 43 | } 44 | return unsafe { ENGINE.get_param_info(¶m).index }; 45 | } 46 | else { 47 | return -1; 48 | } 49 | } 50 | 51 | #[no_mangle] 52 | pub fn get_gain_index() -> i32 { 53 | unsafe { ENGINE.get_param_info("gain").index } 54 | } 55 | 56 | #[no_mangle] 57 | pub fn get_gate_index() -> i32 { 58 | unsafe { ENGINE.get_param_info("gate").index } 59 | } 60 | 61 | #[no_mangle] 62 | pub fn get_freq_index() -> i32 { 63 | unsafe { ENGINE.get_param_info("freq").index } 64 | } 65 | 66 | 67 | #[no_mangle] 68 | pub fn get_sample_rate() -> f64 { 69 | unsafe { ENGINE.get_sample_rate() as f64 } 70 | } 71 | 72 | // number of input channels (currently max 2) 73 | #[no_mangle] 74 | pub fn get_num_input_channels() -> u32 { 75 | unsafe { ENGINE.get_num_inputs() as u32 } 76 | } 77 | 78 | // number of output channels (currently max 2) 79 | #[no_mangle] 80 | pub fn get_num_output_channels() -> u32 { 81 | unsafe { ENGINE.get_num_outputs() as u32 } 82 | } 83 | 84 | #[no_mangle] 85 | pub fn init(sample_rate: f64) -> () { 86 | unsafe { ENGINE.init(sample_rate as i32); } 87 | } 88 | 89 | #[no_mangle] 90 | pub fn set_param_float(index: u32, v: f32) { 91 | unsafe { ENGINE.set_param(index, v); } 92 | } 93 | 94 | #[no_mangle] 95 | pub fn set_param_int(index: u32, v: i32) { 96 | unsafe { ENGINE.set_param(index, v as f32); } 97 | } 98 | 99 | #[no_mangle] 100 | pub fn get_param_float(index: u32) -> f32 { 101 | unsafe { ENGINE.get_param(index) } 102 | } 103 | 104 | #[no_mangle] 105 | pub fn get_param_int(index: u32) -> i32 { 106 | unsafe { ENGINE.get_param(index) as i32 } 107 | } 108 | 109 | #[no_mangle] 110 | pub fn compute(frames: u32) -> () { 111 | unsafe { ENGINE.compute_external(frames as i32); } 112 | } -------------------------------------------------------------------------------- /vl1/ui/scripts/lib/build.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const fs = require('fs') 4 | const libs = fs.readdirSync('./scripts/lib').filter((file) => { return file.indexOf('.js') > 0 && file !== 'build.js' }) 5 | const scripts = fs.readdirSync('./scripts').filter((file) => { return file.indexOf('.js') > 0 }) 6 | const styles = fs.readdirSync('./links').filter((file) => { return file.indexOf('.css') > 0 }) 7 | const id = process.cwd().split('/').slice(-1)[0] 8 | 9 | function cleanup (txt) { 10 | const lines = txt.split('\n') 11 | let output = '' 12 | for (const line of lines) { 13 | if (line.trim() === '') { continue } 14 | if (line.trim().substr(0, 2) === '//') { continue } 15 | if (line.indexOf('/*') > -1 && line.indexOf('*/') > -1) { continue } 16 | output += line + '\n' 17 | } 18 | return output 19 | } 20 | 21 | // Create release 22 | 23 | fs.writeFileSync('index.html', cleanup(` 24 | 25 | 26 | 27 | 28 | 29 | 30 | ${id} 31 | 32 | 34 | 35 | 36 | 48 | 51 | 52 | `)) 53 | 54 | // Create debug 55 | 56 | fs.writeFileSync('debug.html', ` 57 | 58 | 59 | 60 | 61 | 62 | 63 | ${id} 64 | ${styles.reduce((acc, item) => { return `${acc}\n` }, '')} 65 | ${libs.reduce((acc, item) => { return `${acc}\n` }, '')} 66 | ${scripts.reduce((acc, item) => { return `${acc}\n` }, '')} 67 | 68 | 70 | 71 | 72 | 82 | 83 | `) 84 | 85 | console.log(`Built ${id}`) 86 | -------------------------------------------------------------------------------- /reflect/dsp/reflect.dsp: -------------------------------------------------------------------------------- 1 | /* 2 | Black Hole Reverb for the OTTO -- By Jonatan Midtgaard 3 | Adapted by with permission from the "OWLGAZER" modules by Xavier Godart (Empirical Noises). 4 | 5 | To-Do: 6 | - Perhaps adjust allpass values to be closer to freeverb? 7 | - Add stereo spread. Are some delay lines for left/right?? Modulation for this? 8 | - Look at shimmer. More modulation? 9 | - Being able to change number of AP filters. Few makes more of a metallic clang. 10 | 11 | */ 12 | 13 | import("stdfaust.lib"); 14 | 15 | blackhole(decay,shape,pitchmix,spread) = 16 | _ <: 17 | ( 18 | (si.bus(N*2) :> networkline)~(feedbackline) 19 | ) : si.bus(N) <: (par(i, N, *(1-spread)), (ro.cross(N) : par(i,N,*(1+spread)))) 20 | :> ef.gate_stereo(gate_amount*64-60,0.001,0.01,0.04) 21 | with { 22 | N = 4; //Number of comb filters 23 | earlyAPNb = 4; //Number of allpass filters. 24 | hicut = 10000; 25 | MAXDELAY = 8192; 26 | gate_amount = (shape-1) : max(0); 27 | duck_amount = (1-shape) : max(0); 28 | 29 | 30 | //delays = (1356, 1422, 1557, 1617, 1933, 2401, 3125, 6561, 14641); //latereflections values (comb) 31 | delays = (1422, 1617, 2401, 6561); 32 | delayval(i) = ba.take(i+1,delays) : *(time_lfo_late); 33 | time_lfo_late = os.osc(lfo_rate)*(lfo_strength_late) : +(1); 34 | lfo_strength_late = 0.001; 35 | lfo_rate = 0.25; 36 | 37 | pitchshifter(delay, pitch, amount) = _ <: de.delay(MAXDELAY, delay)*(1-amount),(ef.transpose(delay,delay,pitch)*amount) :> _; 38 | 39 | earlyreflections(i) = seq(j, earlyAPNb, fi.allpass_fcomb(2048, delayval(j+1), -allpassfb)) 40 | with{ 41 | allpassfb = 0.6; 42 | //delays = (243, 343, 441, 625, 727, 1331, 2403, 3119); //earlyreflections values (allpass) 43 | delays = (243, 441, 727, 2403, 3119); 44 | time_lfo = os.osc(lfo_rate)*(lfo_strength) : +(1); 45 | lfo_strength = 0.001; 46 | delayval(x) = ba.take(x+1, delays) : *(time_lfo); 47 | }; 48 | 49 | latereflections(i) = _ <: 50 | de.fdelay(MAXDELAY, delayval(i))*(i!=3), 51 | pitchshifter(MAXDELAY + delayval(i),12,pitchmix)*(i==3) :> 52 | _; 53 | 54 | networkline = par(i,N, 55 | _ : 56 | earlyreflections(i) : 57 | latereflections(i) : 58 | _/sqrt(N) 59 | ) : _,_,fi.highpass(2, 100),_; 60 | 61 | feedbackline = ro.hadamard(N) : par(i,N,(*(decay) : fi.lowpass(2, hicut) )); 62 | }; 63 | 64 | blackhole_master = blackhole(decay,shape,pitchmix,spread) 65 | with { 66 | decay = hslider("/Length", 0.5, 0.0, 1.50, 0.01) :si.smoo ; 67 | shape = hslider("/Shape[scale:log]", 1, 0, 2, 0.01) :si.smoo; 68 | pitchmix = hslider("/Shimmer", 0.0, 0, 1.2, 0.01); 69 | spread = hslider("/Spread", 0, 0, 1, 0.01) : si.smoo; 70 | }; 71 | 72 | dry_wet = hslider("/Drywet", 0.0, 0.0, 1.0, 0.01); 73 | output_gain = hslider("/Outputgain", 1.0, 0.0, 1.0, 0.01); 74 | 75 | original = _ : *(1.0 - dry_wet) <: (_,_); 76 | 77 | effect = blackhole_master : *(dry_wet) , *(dry_wet); 78 | 79 | dry_wet_signal = _ <: original, effect :> *(output_gain), *(output_gain); 80 | 81 | //process = _ : blackhole_master : _ , _ ; 82 | 83 | process = _ : dry_wet_signal; 84 | -------------------------------------------------------------------------------- /muses/ui/scripts/lib/build.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const fs = require('fs') 4 | const libs = fs.readdirSync('./scripts/lib').filter((file) => { return file.indexOf('.js') > 0 && file !== 'build.js' }) 5 | const scripts = fs.readdirSync('./scripts').filter((file) => { return file.indexOf('.js') > 0 }) 6 | const styles = fs.readdirSync('./links').filter((file) => { return file.indexOf('.css') > 0 }) 7 | const id = process.cwd().split('/').slice(-1)[0] 8 | 9 | function cleanup (txt) { 10 | const lines = txt.split('\n') 11 | let output = '' 12 | for (const line of lines) { 13 | if (line.trim() === '') { continue } 14 | if (line.trim().substr(0, 2) === '//') { continue } 15 | if (line.indexOf('/*') > -1 && line.indexOf('*/') > -1) { continue } 16 | output += line + '\n' 17 | } 18 | return output 19 | } 20 | 21 | // Create release 22 | 23 | fs.writeFileSync('index.html', cleanup(` 24 | 25 | 26 | 27 | 28 | 29 | 30 | ${id} 31 | 32 | 34 | 35 | 36 | 48 | 51 | 52 | `)) 53 | 54 | // Create debug 55 | 56 | fs.writeFileSync('debug.html', ` 57 | 58 | 59 | 60 | 61 | 62 | 63 | ${id} 64 | 65 | ${styles.reduce((acc, item) => { return `${acc}\n` }, '')} 66 | ${libs.reduce((acc, item) => { return `${acc}\n` }, '')} 67 | ${scripts.reduce((acc, item) => { return `${acc}\n` }, '')} 68 | 69 | 71 | 72 | 73 | 83 | 84 | `) 85 | 86 | console.log(`Built ${id}`) 87 | -------------------------------------------------------------------------------- /nuke/ui/scripts/lib/build.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const fs = require('fs') 4 | const libs = fs.readdirSync('./scripts/lib').filter((file) => { return file.indexOf('.js') > 0 && file !== 'build.js' }) 5 | const scripts = fs.readdirSync('./scripts').filter((file) => { return file.indexOf('.js') > 0 }) 6 | const styles = fs.readdirSync('./links').filter((file) => { return file.indexOf('.css') > 0 }) 7 | const id = process.cwd().split('/').slice(-1)[0] 8 | 9 | function cleanup (txt) { 10 | const lines = txt.split('\n') 11 | let output = '' 12 | for (const line of lines) { 13 | if (line.trim() === '') { continue } 14 | if (line.trim().substr(0, 2) === '//') { continue } 15 | if (line.indexOf('/*') > -1 && line.indexOf('*/') > -1) { continue } 16 | output += line + '\n' 17 | } 18 | return output 19 | } 20 | 21 | // Create release 22 | 23 | fs.writeFileSync('index.html', cleanup(` 24 | 25 | 26 | 27 | 28 | 29 | 30 | ${id} 31 | 32 | 34 | 35 | 36 | 48 | 51 | 52 | `)) 53 | 54 | // Create debug 55 | 56 | fs.writeFileSync('debug.html', ` 57 | 58 | 59 | 60 | 61 | 62 | 63 | ${id} 64 | 65 | ${styles.reduce((acc, item) => { return `${acc}\n` }, '')} 66 | ${libs.reduce((acc, item) => { return `${acc}\n` }, '')} 67 | ${scripts.reduce((acc, item) => { return `${acc}\n` }, '')} 68 | 69 | 71 | 72 | 73 | 83 | 84 | `) 85 | 86 | console.log(`Built ${id}`) 87 | -------------------------------------------------------------------------------- /reflect/ui/scripts/lib/build.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const fs = require('fs') 4 | const libs = fs.readdirSync('./scripts/lib').filter((file) => { return file.indexOf('.js') > 0 && file !== 'build.js' }) 5 | const scripts = fs.readdirSync('./scripts').filter((file) => { return file.indexOf('.js') > 0 }) 6 | const styles = fs.readdirSync('./links').filter((file) => { return file.indexOf('.css') > 0 }) 7 | const id = process.cwd().split('/').slice(-1)[0] 8 | 9 | function cleanup (txt) { 10 | const lines = txt.split('\n') 11 | let output = '' 12 | for (const line of lines) { 13 | if (line.trim() === '') { continue } 14 | if (line.trim().substr(0, 2) === '//') { continue } 15 | if (line.indexOf('/*') > -1 && line.indexOf('*/') > -1) { continue } 16 | output += line + '\n' 17 | } 18 | return output 19 | } 20 | 21 | // Create release 22 | 23 | fs.writeFileSync('index.html', cleanup(` 24 | 25 | 26 | 27 | 28 | 29 | 30 | ${id} 31 | 32 | 34 | 35 | 36 | 48 | 51 | 52 | `)) 53 | 54 | // Create debug 55 | 56 | fs.writeFileSync('debug.html', ` 57 | 58 | 59 | 60 | 61 | 62 | 63 | ${id} 64 | 65 | ${styles.reduce((acc, item) => { return `${acc}\n` }, '')} 66 | ${libs.reduce((acc, item) => { return `${acc}\n` }, '')} 67 | ${scripts.reduce((acc, item) => { return `${acc}\n` }, '')} 68 | 69 | 71 | 72 | 73 | 83 | 84 | `) 85 | 86 | console.log(`Built ${id}`) 87 | -------------------------------------------------------------------------------- /vl1/dsp/ADSR.dsp: -------------------------------------------------------------------------------- 1 | declare aavoices "1"; 2 | 3 | import("stdfaust.lib"); 4 | 5 | gain = hslider("gain", 0.5, 0, 1, 0.01): si.smoo; 6 | 7 | waveforms = piano, fantasy, violin, flute, 8 | guitar1, guitar2, horn, electro1, 9 | electro2, electro3 : ba.selectn(10, wave) 10 | with { 11 | wave = nentry("/Waveform", 0, 0, 9, 1); 12 | freq = hslider("freq", 440, 50, 2000, 0.01); 13 | 14 | piano = os.pulsetrain(freq + vibrato, 0.7) * envelope * tremelo : filter 15 | with{ 16 | filter = fi.resonlp(3000,0.5,gain); 17 | }; 18 | 19 | fantasy = os.pulsetrain(2*freq + vibrato, 0.5) * envelope * tremelo : filter 20 | with { 21 | filter = fi.resonlp(3000,0.5,gain); 22 | }; 23 | 24 | violin = ((os.pulsetrain(freq + vibrato, 0.5) * envelope * tremelo) + (os.pulsetrain(freq + vibrato, 0.4) * envelope * tremelo) + (os.pulsetrain(freq + vibrato, 0.3) * envelope * tremelo) + (os.pulsetrain(freq + vibrato, 0.2) * envelope * tremelo) + (os.pulsetrain(freq + vibrato, 0.1) * envelope * tremelo))/5 : filter 25 | with { 26 | filter = fi.resonlp(3500,5,gain); 27 | }; 28 | 29 | flute = os.pulsetrain(freq + vibrato, 0.5) * envelope * tremelo : filter 30 | with { 31 | filter = fi.resonlp(3000,0.5,gain); 32 | }; 33 | 34 | guitar1 = (os.pulsetrain(0.5*freq + vibrato, 0.1) * envelope * tremelo + os.pulsetrain(0.5*freq + vibrato, 0.2) * envelope * tremelo)/2 : filter 35 | with { 36 | filter = fi.resonlp(3500,1,gain); 37 | }; 38 | 39 | guitar2 = (os.pulsetrain(0.5*freq + vibrato, 0.5) * envelope * tremelo + os.pulsetrain(0.5*freq + vibrato, 0.25) * envelope * tremelo + os.pulsetrain(0.5*freq + vibrato, 0.125) * envelope * tremelo) /3 : filter 40 | with { 41 | filter = fi.resonlp(3000,2,gain); 42 | }; 43 | 44 | horn = os.pulsetrain(0.5*freq + vibrato, 0.125) * envelope * tremelo : filter 45 | with{ 46 | filter = fi.resonlp(3000,0.5,gain); 47 | }; 48 | 49 | electro1 = os.pulsetrain(freq + vibrato + (pitchMod*220), 0.7) * envelope * tremelo: filter 50 | with{ 51 | filter = fi.resonlp(3000,0.5,gain); 52 | }; 53 | 54 | electro2 = os.pulsetrain(2*freq + vibrato + (pitchMod*440), 0.5) * envelope * tremelo : filter 55 | with{ 56 | filter = fi.resonlp(3000,0.5,gain); 57 | }; 58 | 59 | electro3 = ((os.pulsetrain(freq + vibrato + (pitchMod*220), 0.5)* envelope * tremelo) + (os.pulsetrain(freq + vibrato + (pitchMod*220), 0.4) * envelope * tremelo) + (os.pulsetrain(freq + vibrato + (pitchMod*220), 0.3) * envelope * tremelo) + (os.pulsetrain(freq + vibrato + (pitchMod*220), 0.2) * envelope * tremelo) + (os.pulsetrain(freq + vibrato + (pitchMod*220), 0.1) * envelope * tremelo))/5 : filter 60 | with { 61 | filter = fi.resonlp(3500,10,gain); 62 | }; 63 | }; 64 | 65 | vibrato = os.lf_triangle(5.7)*vdepth 66 | with { 67 | vdepth = nentry("/Vibrato", 0, 0, 10, 1); 68 | }; 69 | 70 | tremelo = os.lf_triangle(tremFreq)*5 71 | with { 72 | tremFreq = nentry("/Tremelo", 0, 0, 10, 1); 73 | }; 74 | 75 | pitchMod = os.lf_squarewavepos(modFreq)*-1 76 | with { 77 | modFreq = nentry("/PitchMod", 4.5,2.7, 10, 0.365); 78 | }; 79 | 80 | envelope = en.adsr(a,d,s,r,g) * gain 81 | with { 82 | a = nentry("/Attack", 0.005, 0.001, 0.5,0.05); 83 | d = nentry("/Decay", 0.178, 0.001, 2, 0.2); 84 | s = nentry("/Sustain", 0.306, 0.1, 1, 0.1); 85 | r = nentry("/Release", 0.178, 0.001, 2, 0.2); 86 | g = button("gate") : si.smoo; 87 | }; 88 | 89 | process = vgroup("voices", par(n, 1, vgroup("aavoice%n", waveforms))) : _; 90 | -------------------------------------------------------------------------------- /default/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(wasm_target_feature)] 2 | 3 | const MAX_BUFFER_SIZE: usize = 1024; 4 | 5 | #[no_mangle] 6 | pub static mut IN_BUFFER: [f32;MAX_BUFFER_SIZE] = [0.;MAX_BUFFER_SIZE]; 7 | 8 | #[no_mangle] 9 | pub static mut OUT_BUFFER: [f32;MAX_BUFFER_SIZE] = [0.;MAX_BUFFER_SIZE]; 10 | 11 | static mut INPUTS: [* const f32;1] = [0 as * const f32; 1]; 12 | static mut OUTPUTS: [* mut f32;1] = [0 as * mut f32; 1]; 13 | 14 | static mut SAMPLE_RATE: f64 = 0.; 15 | 16 | #[no_mangle] 17 | pub fn get_input(index: u32) -> u32 { 18 | unsafe { INPUTS[index as usize] as u32 } 19 | } 20 | 21 | #[no_mangle] 22 | pub fn get_output(index: u32) -> u32 { 23 | unsafe { OUTPUTS[index as usize] as u32 } 24 | } 25 | 26 | #[no_mangle] 27 | pub fn set_input(index: u32, offset: u32) { 28 | unsafe { INPUTS[index as usize] = offset as * const f32; }; 29 | } 30 | 31 | #[no_mangle] 32 | pub fn set_output(index: u32, offset: u32) { 33 | unsafe { OUTPUTS[index as usize] = offset as * mut f32; }; 34 | } 35 | 36 | #[no_mangle] 37 | pub fn handle_note_on(_mn: i32, _vel: f32) { 38 | } 39 | 40 | #[no_mangle] 41 | pub fn handle_note_off(_mn: i32, _vel: f32) { 42 | } 43 | 44 | #[no_mangle] 45 | pub fn get_voices() -> i32 { 46 | 0 47 | } 48 | 49 | #[no_mangle] 50 | pub fn get_param_index(_length: i32) -> i32 { 51 | -1 52 | } 53 | 54 | #[no_mangle] 55 | pub fn get_sample_rate() -> f64 { 56 | unsafe { SAMPLE_RATE } 57 | } 58 | 59 | #[inline] 60 | fn set_sample_rate(sample_rate: f64) { 61 | unsafe { SAMPLE_RATE = sample_rate; } 62 | } 63 | 64 | #[no_mangle] 65 | pub fn get_num_input_channels() -> u32 { 66 | 0 67 | } 68 | 69 | // number of output channels (currently max 2) 70 | #[no_mangle] 71 | pub fn get_num_output_channels() -> u32 { 72 | 1 73 | } 74 | 75 | #[no_mangle] 76 | pub fn init(sample_rate: f64) -> () { 77 | set_sample_rate(sample_rate); 78 | unsafe { 79 | INPUTS[0] = IN_BUFFER.as_ptr(); 80 | OUTPUTS[0] = OUT_BUFFER.as_mut_ptr(); 81 | }; 82 | } 83 | 84 | #[no_mangle] 85 | pub fn set_param_float(_index: u32, _v: f32) { 86 | // avoid bounds checking (NOTE: WASM will do this for us) 87 | unsafe { core::arch::wasm32::unreachable(); } 88 | } 89 | 90 | #[no_mangle] 91 | pub fn set_param_int(_index: u32, _v: i32) { 92 | unsafe { core::arch::wasm32::unreachable(); } 93 | } 94 | 95 | /// set bool parameter 96 | /// panics if param not defined 97 | #[no_mangle] 98 | pub fn set_param_bool(_index: u32, _v: u8) { 99 | unsafe { core::arch::wasm32::unreachable(); } 100 | } 101 | 102 | // float parameter at index 103 | // panics if param not defined 104 | #[no_mangle] 105 | pub fn get_param_float(_index: u32) -> f32 { 106 | unsafe { core::arch::wasm32::unreachable(); } 107 | } 108 | 109 | // int parameter at index 110 | // panics if param not defined 111 | #[no_mangle] 112 | pub fn get_param_int(_index: u32) -> i32 { 113 | unsafe { core::arch::wasm32::unreachable(); } 114 | } 115 | 116 | // bool parameter at index 117 | // panics if param not defined 118 | #[no_mangle] 119 | pub fn get_param_bool(_index: u32) -> bool { 120 | unsafe { core::arch::wasm32::unreachable(); } 121 | } 122 | 123 | #[no_mangle] 124 | pub fn compute(frames: u32) -> () { 125 | let outputs = unsafe { ::std::slice::from_raw_parts_mut(OUT_BUFFER.as_mut_ptr(), frames as usize) }; 126 | // write 0 to output buffer 127 | for o in outputs[..frames as usize].iter_mut() { 128 | *o = 0.; 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /nuke/dsp/nuke.dsp: -------------------------------------------------------------------------------- 1 | // An analog style synth with ringmod and hardsync. 2 | // Based on an earlier Faust synth from OTTO 3 | 4 | declare aavoices "2"; 5 | 6 | import("stdfaust.lib"); 7 | 8 | process = vgroup("voices", par(n, 2, vgroup("aavoice%n", voice))) :> _ ; 9 | 10 | voice = hgroup("midi", ( multi_osc(osc1_freq) , multi_osc(osc2_freq) ) <: mixer :> VCF : *(envelope) ) 11 | with { 12 | midigate = button ("gate"): si.smoo; 13 | midifreq = hslider("freq", 440, 20, 1000, 1); 14 | midigain = hslider("gain", 1, 0, 1, 1/127): si.smoo; 15 | 16 | // Filter 17 | flt = hslider("/Filter",0.5, 0, 1, 0.01) : si.smoo; 18 | fc = flt*flt*10000 + 100; 19 | Q = 1.25; 20 | VCF = fi.resonlp(fc,Q,0.5) : fi.resonlp(fc,Q,0.5); 21 | 22 | 23 | //Oscillators-------------------------- 24 | wave = hslider("/Wave", 1, 0.08, 4, 0.01) : si.smoo; 25 | //Waveform modifiers 26 | hardsync = wave : max(3) : -(3) : *(midifreq) : *(0.1); //Amount of hardsync of the saw-wave 27 | duty = wave*(0.5) : min(0.5); //Duty cycle in the pulse wave 28 | 29 | sq_amount = 2-(wave) : max(0) : min(1); 30 | saw_amount = wave-(2) : max(0) : min(1); 31 | tri_amount = 1-(sq_amount) : -(saw_amount); 32 | 33 | multi_osc(frequency) = ( os.pulsetrain(frequency, duty)*(sq_amount) , os.triangle(frequency)*(tri_amount)*(1.5) , saw2_own(frequency+(hardsync),clock)*(saw_amount) ) :> _ 34 | with{ 35 | clock = ba.pulse(ba.sec2samp(1/(frequency))); //Master osc. for hardsync 36 | phase(slave_freq,master_freq) = 2 * (os.hs_phasor(1<<16,slave_freq,master_freq)/(1<<16) ) - 1.0; //Zero-mean sawtooth 37 | saw2_own(slave_freq,master_freq) = phase(slave_freq,master_freq) <: * <: -(mem) : *(0.25'*ma.SR/slave_freq); //Hard-syncable anti-alialised saw 38 | }; 39 | //----------------------------------------------- 40 | 41 | //Oscillator relations 42 | relation = hslider("/Relation", 2, 0, 3.001, 0.001) : *(2) : /(2) : si.smoo ; 43 | detune_amount = (1, (relation-2.1 :*(0.02) : +(1.0)), (1-relation : *(0.02) : +(1.0) ) ) : (max, _) : max ; 44 | detune = ba.if( (relation == 0) + (relation >= 3) , 1, detune_amount); 45 | 46 | mixer = (_,_,_,_) <: (_,_,_,!,!,!,_,_) : ((+ : *(relation >= 2)), *(1-(octave)) , (* : *(octave))) : (_, (+ : *(relation<2)), third_osc_send) ; 47 | octave = 2-relation : max(0) : min(1); 48 | detune_osc2 = 1.0/(detune); 49 | 50 | fifth = ba.if( (relation >= 3) , 1.49830, 1 ); //A fifth is seven semitones above 51 | osc1_freq = midifreq*detune; 52 | osc2_freq = midifreq*(detune_osc2) : *(fifth); 53 | 54 | //Fourth knob 55 | Sub = hslider("/Sub", 0, 0, 1, 0.001) : si.smoo; 56 | tonic_osc_send = multi_osc(midifreq)*(1-Sub); 57 | sub_osc_send = multi_osc(midifreq/(2))*(Sub); 58 | third_osc_send = tonic_osc_send+sub_osc_send; 59 | 60 | 61 | //ADSR envelope---------------------------- 62 | envelope = adsre(a,d,s,r,midigate)*(midigain); 63 | a = hslider("/v:envelope/Attack", 0.001, 0.001, 4, 0.001); 64 | d = hslider("/v:envelope/Decay", 0.0, 0.0, 4, 0.001); 65 | s = hslider("/v:envelope/Sustain", 1.0, 0.0, 1.0, 0.01); 66 | r = hslider("/v:envelope/Release", 0.0, 0.0, 4.0, 0.01); 67 | 68 | adsre(attT60,decT60,susLvl,relT60,gate) = envel 69 | with { 70 | ugate = gate>0; 71 | samps = ugate : +~(*(ugate)); // ramp time in samples 72 | attSamps = int(attT60 * ma.SR); 73 | target = select2(ugate, 0.0, 74 | select2(samps { this.handleKeyPress(e) }, false ); 29 | // document.addEventListener('mousedown', (e) => { this.cursor.down(e) }, false) 30 | // document.addEventListener('mousemove', (e) => { this.cursor.move(e) }, false) 31 | // document.addEventListener('contextmenu', (e) => { this.cursor.alt(e) }, false) 32 | // document.addEventListener('mouseup', (e) => { this.cursor.up(e) }, false) 33 | // document.addEventListener('copy', (e) => { this.copy(e) }, false) 34 | // document.addEventListener('cut', (e) => { this.cut(e) }, false) 35 | // document.addEventListener('paste', (e) => { this.paste(e) }, false) 36 | // window.addEventListener('resize', (e) => { this.onResize() }, false) 37 | // window.addEventListener('dragover', (e) => { e.stopPropagation(); e.preventDefault(); e.dataTransfer.dropEffect = 'copy' }) 38 | // window.addEventListener('drop', this.onDrop) 39 | } 40 | 41 | this.start = () => { 42 | console.log('Client', 'Starting..') 43 | 44 | this.adsr.start() 45 | this.renderer.start() 46 | 47 | setInterval(() => { this.renderer.update() }, 33) // redraw at 30hz 48 | setTimeout(() => { document.body.className += ' ready' }, 250) 49 | } 50 | 51 | this.update = () => { 52 | console.log('Client', 'Update...') 53 | this.renderer.update() 54 | } 55 | 56 | this.clear = () => { 57 | } 58 | 59 | this.reset = () => { 60 | } 61 | 62 | this.whenOpen = (file, data) => { 63 | } 64 | 65 | this.getPadding = () => { 66 | return { x: 60, y: 90 } 67 | } 68 | 69 | this.getWindowSize = () => { 70 | return { width: window.innerWidth, height: window.innerHeight } 71 | } 72 | 73 | this.getProjectSize = () => { 74 | return this.tool.settings.size 75 | } 76 | 77 | this.getPaddedSize = () => { 78 | const rect = this.getWindowSize() 79 | const pad = this.getPadding() 80 | return { width: step(rect.width - pad.x, 15), height: step(rect.height - pad.y, 15) } 81 | } 82 | 83 | this.handleKeyPress = (e) => { 84 | if (e.key === "q") { 85 | this.renderer.incParamWave(1) 86 | } 87 | else if (e.key === "a") { 88 | this.renderer.incParamWave(-1) 89 | } 90 | else if (e.key === "w") { 91 | this.renderer.incParamSub(1) 92 | } 93 | else if (e.key === "s") { 94 | this.renderer.incParamSub(-1) 95 | } 96 | else if (e.key === "e") { 97 | this.renderer.incParamRelation(1) 98 | } 99 | else if (e.key === "d") { 100 | this.renderer.incParamRelation(-1) 101 | } 102 | else if (e.key === "r") { 103 | this.renderer.incParamFilter(1) 104 | } 105 | else if (e.key === "f") { 106 | this.renderer.incParamFilter(-1) 107 | } 108 | } 109 | 110 | function sizeOffset (a, b) { return { width: a.width - b.width, height: a.height - b.height } } 111 | function step (v, s) { return Math.round(v / s) * s } 112 | } 113 | -------------------------------------------------------------------------------- /muses/ui/scripts/client.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /* global Acels */ 4 | /* global Theme */ 5 | /* global Source */ 6 | /* global History */ 7 | 8 | /* global Manager */ 9 | /* global Renderer */ 10 | /* global Tool */ 11 | /* global Interface */ 12 | /* global Picker */ 13 | /* global Cursor */ 14 | 15 | /* global FileReader */ 16 | 17 | function Client () { 18 | this.install = function (host) { 19 | console.info('Client', 'Installing..') 20 | 21 | this.adsr = new ADSR(this) 22 | this.renderer = new Renderer(this) 23 | this.reflectRenderer = new ReflectRenderer(this) 24 | 25 | host.appendChild(this.renderer.el); 26 | host.appendChild(this.reflectRenderer.el); 27 | 28 | document.addEventListener('keypress', (e) => { this.handleKeyPress(e) }, false ); 29 | // document.addEventListener('mousedown', (e) => { this.cursor.down(e) }, false) 30 | // document.addEventListener('mousemove', (e) => { this.cursor.move(e) }, false) 31 | // document.addEventListener('contextmenu', (e) => { this.cursor.alt(e) }, false) 32 | // document.addEventListener('mouseup', (e) => { this.cursor.up(e) }, false) 33 | // document.addEventListener('copy', (e) => { this.copy(e) }, false) 34 | // document.addEventListener('cut', (e) => { this.cut(e) }, false) 35 | // document.addEventListener('paste', (e) => { this.paste(e) }, false) 36 | // window.addEventListener('resize', (e) => { this.onResize() }, false) 37 | // window.addEventListener('dragover', (e) => { e.stopPropagation(); e.preventDefault(); e.dataTransfer.dropEffect = 'copy' }) 38 | // window.addEventListener('drop', this.onDrop) 39 | } 40 | 41 | this.start = () => { 42 | console.log('Client', 'Starting..') 43 | 44 | this.adsr.start() 45 | this.renderer.start() 46 | 47 | setInterval(() => { 48 | this.renderer.update() 49 | this.reflectRenderer.update() 50 | }, 33) // redraw at 30hz 51 | setTimeout(() => { document.body.className += ' ready' }, 250) 52 | } 53 | 54 | this.update = () => { 55 | console.log('Client', 'Update...') 56 | this.renderer.update() 57 | } 58 | 59 | this.clear = () => { 60 | } 61 | 62 | this.reset = () => { 63 | } 64 | 65 | this.whenOpen = (file, data) => { 66 | } 67 | 68 | this.getPadding = () => { 69 | return { x: 60, y: 90 } 70 | } 71 | 72 | this.getWindowSize = () => { 73 | return { width: window.innerWidth, height: window.innerHeight } 74 | } 75 | 76 | this.getProjectSize = () => { 77 | return this.tool.settings.size 78 | } 79 | 80 | this.getPaddedSize = () => { 81 | const rect = this.getWindowSize() 82 | const pad = this.getPadding() 83 | return { width: step(rect.width - pad.x, 15), height: step(rect.height - pad.y, 15) } 84 | } 85 | 86 | this.handleKeyPress = (e) => { 87 | if (e.key === "q") { 88 | this.renderer.incParamWave(1) 89 | } 90 | else if (e.key === "a") { 91 | this.renderer.incParamWave(-1) 92 | } 93 | else if (e.key === "w") { 94 | this.renderer.incParamSub(1) 95 | } 96 | else if (e.key === "s") { 97 | this.renderer.incParamSub(-1) 98 | } 99 | else if (e.key === "e") { 100 | this.renderer.incParamRelation(1) 101 | } 102 | else if (e.key === "d") { 103 | this.renderer.incParamRelation(-1) 104 | } 105 | else if (e.key === "r") { 106 | this.renderer.incParamFilter(1) 107 | } 108 | else if (e.key === "f") { 109 | this.renderer.incParamFilter(-1) 110 | } 111 | } 112 | 113 | function sizeOffset (a, b) { return { width: a.width - b.width, height: a.height - b.height } } 114 | function step (v, s) { return Math.round(v / s) * s } 115 | } 116 | -------------------------------------------------------------------------------- /muses/muses_gui_description.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Muses", 3 | "width": 320, 4 | "height": 143, 5 | "widgets": { 6 | "sliders": [ 7 | { 8 | "type": "float_slider", 9 | "name": "wv", 10 | "x": 5, "y": 18, 11 | "w": 80, "h": 15, 12 | "init": 0.5, 13 | "min": 0.08, 14 | "max": 4.0, 15 | "digits": 2, 16 | "node": 0, 17 | "index": 3 18 | }, 19 | { 20 | "type": "float_slider", 21 | "name": "sub", 22 | "anchored": "anchored_below", 23 | "w": 80, "h": 15, 24 | "init": 1.0, 25 | "min": 0.0, 26 | "max": 1.0, 27 | "node": 0, 28 | "index": 2 29 | }, 30 | { 31 | "type": "float_slider", 32 | "name": "rel", 33 | "anchored": "anchored_below", 34 | "w": 80, "h": 15, 35 | "init": 3.0, 36 | "min": 0.0, 37 | "max": 3.01, 38 | "node": 0, 39 | "index": 1 40 | }, 41 | { 42 | "type": "float_slider", 43 | "name": "filter", 44 | "anchored": "anchored_below", 45 | "w": 80, "h": 15, 46 | "init": 0.5, 47 | "min": 0.0, 48 | "max": 1.0, 49 | "node": 0, 50 | "index": 0 51 | }, 52 | { 53 | "type": "float_slider", 54 | "name": "length", 55 | "x": 190, "y": 18, 56 | "w": 80, "h": 15, 57 | "init": 0.5, 58 | "min": 0.0, 59 | "max": 1.0, 60 | "digits": 2, 61 | "node": 1, 62 | "index": 1 63 | }, 64 | { 65 | "type": "float_slider", 66 | "name": "shape", 67 | "anchored": "anchored_below", 68 | "w": 80, "h": 15, 69 | "init": 1.0, 70 | "min": 0.0, 71 | "max": 2.0, 72 | "node": 1, 73 | "index": 3 74 | }, 75 | { 76 | "type": "float_slider", 77 | "name": "shimmer", 78 | "anchored": "anchored_below", 79 | "w": 100, "h": 15, 80 | "init": 0.0, 81 | "min": 0.0, 82 | "max": 1.2, 83 | "node": 1, 84 | "index": 4 85 | }, 86 | { 87 | "type": "float_slider", 88 | "name": "spread", 89 | "anchored": "anchored_below", 90 | "w": 80, "h": 15, 91 | "init": 0.0, 92 | "min": 0.0, 93 | "max": 1.0, 94 | "node": 1, 95 | "index": 5 96 | }, 97 | { 98 | "type": "float_slider", 99 | "name": "dry/wet", 100 | "anchored": "anchored_below", 101 | "w": 80, "h": 15, 102 | "init": 0.0, 103 | "min": 0.0, 104 | "max": 1.0, 105 | "node": 1, 106 | "index": 0 107 | } 108 | ], 109 | "adsrs": [ 110 | { 111 | "type": "adsr", 112 | "name": "env", 113 | "x": 95, "y": 18, 114 | "w": 80, "h": 36, 115 | "node": 0, 116 | 117 | "indexA": 4, 118 | "initA": 2.0001, 119 | "minA": 0.0, 120 | "maxA": 4.0, 121 | 122 | "indexD": 5, 123 | "initD": 1.0, 124 | "minD": 0.0, 125 | "maxD": 4.0, 126 | 127 | "indexS": 7, 128 | "initS": 0.75, 129 | "minS": 0.0, 130 | "maxS": 1.0, 131 | 132 | "indexR": 6, 133 | "initR": 2.0, 134 | "minR": 0.0, 135 | "maxR": 4.0 136 | } 137 | ], 138 | "logo": { "x": 115, "y": 70, "scale_x": 0.25, "scale_y": 0.25 } 139 | } 140 | } -------------------------------------------------------------------------------- /muses/ui/scripts/aa.js: -------------------------------------------------------------------------------- 1 | // This currently only works with the Push 2's endless encoders. We should really 2 | // add MIDI learn and the like, but that is for another rainy day 3 | 4 | // TODO: Midi learn 5 | // TODO: move to class so that vars encapuslated 6 | 7 | const paramFilter = 0 8 | const paramWave = 1 9 | const paramRelation = 2 10 | const paramSub = 3 11 | 12 | // Need to add midi learn 13 | const controlFilter = 15 14 | const controlWave = 71 15 | const controlRelation = 72 16 | const controlSub = 73 17 | 18 | const controlAttack = 74 19 | const controlDecay = 75 20 | const controlSubstain = 76 21 | const controlRelease = 77 22 | 23 | const nukeNode = 0 24 | const reflectNode = 1 25 | 26 | const paramShape = 0 27 | const paramSpread = 1 28 | const paramLength = 2 29 | const paramShimmer = 3 30 | 31 | // Need to add midi learn 32 | const controlShape = 15 33 | const controlSpread = 71 34 | const controlLength = 72 35 | const controlShimmer = 73 36 | const controlDryWet = 74 37 | 38 | var controlNuke = true 39 | 40 | function OnParamChange(node, param, value) { 41 | if (node == nukeNode) { 42 | if (param == paramFilter) { 43 | client.renderer.updateFilter(value) 44 | } 45 | else if (param == paramWave) { 46 | client.renderer.updateWave(value) 47 | } 48 | else if (param == paramRelation) { 49 | client.renderer.updateRelation(value) 50 | } 51 | else if (param == paramSub) { 52 | client.renderer.updateSub(value) 53 | } 54 | } 55 | else if (node == reflectNode) { 56 | if (param == paramShape) { 57 | client.reflectRenderer.updateShape(value) 58 | } 59 | else if (param == paramSpread) { 60 | client.reflectRenderer.updateSpread(value) 61 | } 62 | else if (param == paramRelation) { 63 | client.reflectRenderer.updateLength(value) 64 | } 65 | else if (param == paramShimmer) { 66 | client.reflectRenderer.updateShimmer(value) 67 | } 68 | } 69 | } 70 | 71 | function controlChange(ctrlTag, value) { 72 | //console.log(ctrlTag + " " + value) 73 | 74 | if (ctrlTag == 20 && value == 127) { 75 | controlNuke = !controlNuke 76 | return 77 | } 78 | 79 | // Endless controller on Push 2 is 1 for +1 (turn clockwise) and 127 for -1 (turn anti-clockwise) 80 | if (value == 127) { 81 | value = -1 82 | } 83 | 84 | if (controlNuke) { 85 | if (ctrlTag == controlFilter && client) { 86 | parent.sendMsg(1, 0, 0, client.renderer.incFilter(value)) 87 | } 88 | else if (ctrlTag == controlWave && client) { 89 | parent.sendMsg(1, 0, 3, client.renderer.incWave(value)) 90 | } 91 | else if (ctrlTag == controlRelation && client) { 92 | parent.sendMsg(1, 0, 1, client.renderer.incRelation(value)) 93 | } 94 | else if (ctrlTag == controlSub && client) { 95 | parent.sendMsg(1, 0, 2, client.renderer.incSub(value)) 96 | } 97 | else if (ctrlTag == controlAttack && client) { 98 | parent.sendMsg(1, 0, 4, client.adsr.incAttack(value)) 99 | } 100 | else if (ctrlTag == controlDecay && client) { 101 | parent.sendMsg(1, 0, 5, client.adsr.incDecay(value)) 102 | } 103 | else if (ctrlTag == controlSubstain && client) { 104 | parent.sendMsg(1, 0, 7, client.adsr.incSubstain(value)) 105 | } 106 | else if (ctrlTag == controlRelease && client) { 107 | parent.sendMsg(1, 0, 6, client.adsr.incRelease(value)) 108 | } 109 | } 110 | else { 111 | // reflect midl 112 | 113 | if (ctrlTag == controlShape && client) { 114 | parent.sendMsg(1, 1, 3, client.reflectRenderer.incShape(value)) 115 | } 116 | else if (ctrlTag == controlSpread && client) { 117 | parent.sendMsg(1, 1, 5, client.reflectRenderer.incSpread(value)) 118 | } 119 | else if (ctrlTag == controlLength && client) { 120 | parent.sendMsg(1, 1, 1, client.reflectRenderer.incLength(value)) 121 | } 122 | else if (ctrlTag == controlShimmer && client) { 123 | parent.sendMsg(1, 1, 4, client.reflectRenderer.incShimmer(value)) 124 | } 125 | else if (ctrlTag == controlDryWet && client) { 126 | parent.sendMsg(1, 1, 0, client.reflectRenderer.incDryWet(value)) 127 | } 128 | } 129 | 130 | } -------------------------------------------------------------------------------- /four_track/src/playhead.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, Clone)] 2 | enum Direction { 3 | Forward, 4 | Backward 5 | } 6 | 7 | #[derive(Debug, Clone)] 8 | pub struct Playhead { 9 | // invariant: len(left_channel) == len(right_channel) 10 | /// samples for left channel 11 | left_channel: Vec, 12 | /// samples for right channel 13 | right_channel: Vec, 14 | 15 | /// current position within audio 16 | head_pos: usize, 17 | /// position of loop IN marker 18 | loop_in: usize, 19 | /// position of loop OUT marker 20 | loop_out: usize, 21 | /// is looping enabled 22 | looping: bool, 23 | } 24 | 25 | impl Playhead { 26 | pub const ONE_MINUTE_SECONDS: usize = 60; 27 | //pub const ONE_MINUTE_MILLIS: usize = 60000; 28 | 29 | /// create a Playhead instance, channels are zero length at this point 30 | pub fn new(sample_rate: usize, length_ms: usize) -> Self { 31 | let size = sample_rate * length_ms; 32 | Self { 33 | //left_channel: Vec::new(), 34 | left_channel: vec![0.;size], 35 | //right_channel: Vec::new(), 36 | right_channel: vec![0.;size], 37 | head_pos: 0, 38 | loop_in: 0, 39 | loop_out: size-1, 40 | looping: false, 41 | } 42 | } 43 | 44 | /// overdub a sample to the current playhead pos, advance playhead 45 | /// returns the overdub sample 46 | pub fn overdub(&mut self, left: f32, right: f32) -> (f32, f32) { 47 | if self.head_pos != self.num_samples() { 48 | // first overdub left and right values at current head 49 | self.left_channel[self.head_pos] += left; 50 | self.right_channel[self.head_pos] += right; 51 | 52 | self.read_unchecked() 53 | } 54 | else { 55 | (0.,0.) 56 | } 57 | } 58 | 59 | #[inline] 60 | fn move_head_unchecked(&mut self) { 61 | self.head_pos += 1; 62 | // handle loop bounds 63 | if self.looping && self.head_pos >= self.loop_out { 64 | self.head_pos = self.loop_in; 65 | } 66 | } 67 | 68 | #[inline] 69 | fn read_unchecked(&mut self) -> (f32, f32) { 70 | let samples = (self.left_channel[self.head_pos], self.right_channel[self.head_pos]); 71 | self.move_head_unchecked(); 72 | samples 73 | } 74 | 75 | /// read sample at current playhead pos, advance playhead 76 | /// returns the read sample 77 | pub fn read(&mut self) -> (f32, f32) { 78 | if self.head_pos != self.num_samples() { 79 | self.read_unchecked() 80 | } 81 | else { // at end of buffer 82 | (0., 0.) 83 | } 84 | } 85 | 86 | /// return true if head within loop in and out markers 87 | #[inline] 88 | fn within_loop_markers(&self) -> bool { 89 | self.head_pos >= self.loop_in && self.head_pos <= self.loop_out 90 | } 91 | 92 | /// reset head to start of buffer 93 | #[inline] 94 | pub fn rewind(&mut self) { 95 | self.head_pos = 0; 96 | } 97 | 98 | /// reset head to start of loop section or start of buffer 99 | pub fn gate(&mut self) { 100 | if self.looping && self.within_loop_markers() { 101 | self.head_pos = self.loop_in; 102 | } 103 | else { 104 | self.rewind(); 105 | } 106 | } 107 | 108 | /// set loop in position 109 | pub fn loop_in(&mut self, offset: usize) { 110 | if offset <= self.num_samples() { 111 | self.loop_in = offset; 112 | } 113 | } 114 | 115 | /// set loop out position 116 | pub fn loop_out(&mut self, offset: usize) { 117 | if offset <= self.num_samples() { 118 | self.loop_out = offset; 119 | } 120 | } 121 | 122 | /// set looping mode 123 | pub fn looping(&mut self, value: bool) { 124 | self.looping = value; 125 | } 126 | 127 | /// return the number of samples in buffer 128 | pub fn num_samples(&self) -> usize { 129 | self.left_channel.len() 130 | } 131 | 132 | /// move forward or backwards along tape 133 | /// head is only moved if within range 134 | /// loop markers do not count 135 | pub fn seek(&mut self, offset: usize, dir: Direction) { 136 | match dir { 137 | Direction::Forward if self.head_pos + offset < self.num_samples() => { 138 | self.head_pos += offset; 139 | }, 140 | Direction::Backward if self.head_pos>=offset => { 141 | self.head_pos -= offset; 142 | }, 143 | _ => { } 144 | }; 145 | } 146 | } -------------------------------------------------------------------------------- /gain/ui/gain.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 54 | 55 | 56 | 57 | 58 | 122 | 123 | 124 | 125 |
126 | Gain 127 |
128 |
129 |
130 | 131 |
132 |
133 |
134 | No canvas 135 |
136 | 139 |
140 |
141 | 142 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # About 2 | 3 | This repo is part of a larger project called Audio Anywhere(AA). Audio Anywhere a 4 | framework for working with audio plugins that are compiled once and run anywhere. 5 | At the heart of Audio Anywhere is an audio engine whose Digital Signal Processing (DSP) components are written in Faust and deployed with WebAssembly. 6 | 7 | Details about the project can be found on the [project's homepage](https://muses-dmi.github.io/projects/). 8 | 9 | ## Introduction 10 | 11 | 12 | 13 | This repo contains a number of AA module examples. 14 | 15 | * [Default](https://github.com/bgaster/aa_examples/tree/master/default) is a do nothing module that can be loaded, well to do nothing 16 | * [Nuke](https://github.com/bgaster/aa_examples/tree/master/nuke) is an analogue synth based on one of the Muses instruments 17 | * [VL1](https://github.com/bgaster/aa_examples/tree/master/vl1) is a fairly close emulation of the [Casio VL-1](https://en.wikipedia.org/wiki/Casio_VL-1). (Audio Anywhere standalone version pictured above.) 18 | * [Gain](https://github.com/bgaster/aa_examples/tree/master/gain) is a simple gain controller 19 | * [Reflect](https://github.com/bgaster/aa_examples/tree/master/reflect) is a spring reverb emulation 20 | * [Muses Synth](https://github.com/bgaster/aa_examples/tree/master/muses) is the Muses Synth, which consists of the Nuke synth followed by Reflect reverb. This example demostrates Audio Anywhere audio graphs, where each DSP is a seperate WASM, but there is single UI. 21 | * More in development 22 | 23 | ## Building 24 | 25 | The example audio components are mostly implmented in [Rust](https://www.rust-lang.org/) and compiled to [WebAssembly](https://webassembly.org/). (Although it is planed to implemented at 26 | least one using C/C++.) 27 | 28 | They currenlty make use of features only on nightly. 29 | 30 | To install Rust go you need simply to install [Rustup](https://rustup.rs/) and 31 | if you already have Rust installed, then you can update with the command rustup update. 32 | 33 | Now you need to install nightly: 34 | 35 | ```bash 36 | rustup toolchain install nightly 37 | ``` 38 | Don't forget to update if you already have nightly installed. Now install the WASM target: 39 | 40 | ```bash 41 | rustup target add wasm32-unknown-unknown --toolchain nightly 42 | ``` 43 | 44 | Each of the examples have a Cargo config that automatically compiles to WASM. (Later sections detail how to create your own example.) 45 | 46 | Most interfaces are written in HTML5, with pure Javascript. To build some of the GUIs into a single HTML file [Node.js](https://nodejs.org/en/) is required. 47 | 48 | All that said there is no requirement that each module is implemented in the same fashion, and as such to build a particular module see the corresponding readme in its directory. 49 | 50 | # Module Bundles 51 | 52 | An AA module must consist of the following: 53 | 54 | * **module.json** is a description of the module, which is used to load it. 55 | * **module.wasm** is an AA WASM API conforming module that implements the audio part of a module. 56 | * **module.html** (optional) UI implemented in HTML5. 57 | 58 | # Deploying a set of Audio Anywhere Modules 59 | 60 | It is possible to deploy AA modules in a number of different forms, but currently the 61 | [standalone application](https://github.com/bgaster/aa_standalone) and VST 2.x plugin take basically the same approach. They assume that the modules are served via a websever, which 62 | for now requires that it is a single server, but this is not necessary. The root of the web-server contains the files in the directory **root** (it additionally contains some resources files): 63 | 64 | * **index.html** code for the single page app of the interface; 65 | * **modules.json** description of example modules that can be loaded; 66 | * **js** Javascript files loaded by **index.html** and also by AA modules; 67 | * **css** CSS loaded by **index.html**. 68 | 69 | When a AA hosting application runs its interface is populated with **index.html**, 70 | and the set of modules is added to its module menu, which can be selected and loaded 71 | by the user. Additionally, a default module is loaded, as specified in **modules.json**. 72 | 73 | Each module is assumed to have its own directory under the server root and contains the 74 | module bundle, as defined above. 75 | 76 | A release can be built of existing AA modules and the AA hosting pages with the command: 77 | 78 | ```bash 79 | ./scripts/package.sh 80 | ``` 81 | 82 | This will create a directory, **pkg**, containing all the root files, plus bundles for each module in corresponding directories. 83 | 84 | # Adding a Module 85 | 86 | TODO 87 | 88 | Once an example is implemented the following steps must be completed to ensure that is added correctly to a release: 89 | 90 | * Add the module to **modules.json** so that a hosting application knows about the module; 91 | * Add comamnds to copy the module bundle to **package.sh** so it is added when building a release. 92 | 93 | # License 94 | 95 | © 2020 [Benedict R. Gaster (cuberoo_)](https://bgaster.github.io/) 96 | 97 | Licensed under either of 98 | 99 | * Apache License, Version 2.0 100 | ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 101 | * MIT license 102 | ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 103 | 104 | at your option. 105 | 106 | ## Contribution 107 | 108 | Unless you explicitly state otherwise, any contribution intentionally submitted 109 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 110 | dual licensed as above, without any additional terms or conditions. 111 | -------------------------------------------------------------------------------- /scripts/faust2audioanywhere: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | FAUST=/Users/br-gaster/dev/wasm/faust_bgaster/build/bin/faust 3 | if [ $# -eq 0 ]; then 4 | echo "No arguments provided" 5 | exit 1 6 | fi 7 | cat < Self { 37 | Self { 38 | init, 39 | min, 40 | max, 41 | step, 42 | } 43 | } 44 | } 45 | 46 | #[derive(Clone)] 47 | struct Param { 48 | index: i32, 49 | range: ParamRange, 50 | } 51 | 52 | impl Param { 53 | pub fn new(name: String, index: i32, range: ParamRange) -> Self { 54 | Self { 55 | index, 56 | range 57 | } 58 | } 59 | } 60 | 61 | // Notes and voices 62 | 63 | type Note = i32; 64 | type Pitch = f32; 65 | 66 | /// convert midi note to its corresponding frequency 67 | #[inline] 68 | fn to_freq(n: i32) -> Pitch { 69 | 2.0f32.powf( (n - 69) as Pitch / 12.0 ) * 440.0 70 | } 71 | 72 | /// convert midi note to an index within voices range, can then be used as 73 | /// sample index, for example. 74 | #[inline] 75 | fn to_index(n: i32, voices: u32) -> u32 { 76 | (12.0*(to_freq(n) / 130.81).log2()).round().abs() as u32 % voices 77 | } 78 | 79 | #[inline] 80 | fn freq_to_index(freq: f32, voices: u32) -> u32 { 81 | (12.0*(freq / 130.81).log2()).round().abs() as u32 % voices 82 | } 83 | 84 | /// convert midi note to its corresponding frequency, with explicit base tuning 85 | #[inline] 86 | fn to_freq_tuning(n:i32, tuning: Pitch) -> Pitch { 87 | 2.0f32.powf( (n - 69) as f32 / 12.0 ) * tuning 88 | } 89 | 90 | #[derive(Copy, Clone)] 91 | struct VoiceInfo { 92 | pub active: bool, 93 | pub note: Note, 94 | pub channel: i32, 95 | pub voice_age: i64, 96 | } 97 | 98 | impl VoiceInfo { 99 | pub fn new() -> VoiceInfo { 100 | VoiceInfo { 101 | active: false, 102 | note: 0, 103 | channel: 0, 104 | voice_age: 0, 105 | } 106 | } 107 | } 108 | EOF 109 | $FAUST -lang rust $1 110 | cat < u32 { 113 | unsafe { ENGINE.get_input(index) } 114 | } 115 | 116 | #[no_mangle] 117 | pub fn get_output(index: u32) -> u32 { 118 | unsafe { ENGINE.get_output(index) } 119 | } 120 | 121 | #[no_mangle] 122 | pub fn set_input(index: u32, offset: u32) { 123 | unsafe { ENGINE.set_input(index, offset); }; 124 | } 125 | 126 | #[no_mangle] 127 | pub fn set_output(index: u32, offset: u32) { 128 | unsafe { ENGINE.set_output(index, offset); }; 129 | } 130 | 131 | #[no_mangle] 132 | pub fn handle_note_on(mn: i32, vel: f32) { 133 | unsafe { ENGINE.handle_note_on(mn, vel); } 134 | } 135 | 136 | #[no_mangle] 137 | pub fn handle_note_off(mn: i32, vel: f32) { 138 | unsafe { ENGINE.handle_note_off(mn, vel); } 139 | } 140 | 141 | #[no_mangle] 142 | pub fn get_voices() -> i32 { 143 | unsafe { ENGINE.get_voices() } 144 | } 145 | 146 | #[no_mangle] 147 | pub fn get_param_index(length: i32) -> i32 { 148 | if length < MAX_PARAM_SIZE as i32 { 149 | let mut param = String::new(); 150 | for i in 0..length as usize { 151 | let c = unsafe { PARAM_NAME[i] } as char; 152 | param.push(c); 153 | } 154 | return unsafe { ENGINE.get_param_info(¶m).index }; 155 | } 156 | else { 157 | return -1; 158 | } 159 | } 160 | 161 | #[no_mangle] 162 | pub fn get_gain_index() -> i32 { 163 | unsafe { ENGINE.get_param_info("gain").index } 164 | } 165 | 166 | #[no_mangle] 167 | pub fn get_gate_index() -> i32 { 168 | unsafe { ENGINE.get_param_info("gate").index } 169 | } 170 | 171 | #[no_mangle] 172 | pub fn get_freq_index() -> i32 { 173 | unsafe { ENGINE.get_param_info("freq").index } 174 | } 175 | 176 | 177 | #[no_mangle] 178 | pub fn get_sample_rate() -> f64 { 179 | unsafe { ENGINE.get_sample_rate() as f64 } 180 | } 181 | 182 | // number of input channels (currently max 2) 183 | #[no_mangle] 184 | pub fn get_num_input_channels() -> u32 { 185 | unsafe { ENGINE.get_num_inputs() as u32 } 186 | } 187 | 188 | // number of output channels (currently max 2) 189 | #[no_mangle] 190 | pub fn get_num_output_channels() -> u32 { 191 | unsafe { ENGINE.get_num_outputs() as u32 } 192 | } 193 | 194 | #[no_mangle] 195 | pub fn init(sample_rate: f64) -> () { 196 | unsafe { ENGINE.init(sample_rate as i32); } 197 | } 198 | 199 | #[no_mangle] 200 | pub fn set_param_float(index: u32, v: f32) { 201 | unsafe { ENGINE.set_param(index, v); } 202 | } 203 | 204 | #[no_mangle] 205 | pub fn set_param_int(index: u32, v: i32) { 206 | unsafe { ENGINE.set_param(index, v as f32); } 207 | } 208 | 209 | #[no_mangle] 210 | pub fn get_param_float(index: u32) -> f32 { 211 | unsafe { ENGINE.get_param(index) } 212 | } 213 | 214 | #[no_mangle] 215 | pub fn get_param_int(index: u32) -> i32 { 216 | unsafe { ENGINE.get_param(index) as i32 } 217 | } 218 | 219 | #[no_mangle] 220 | pub fn compute(frames: u32) -> () { 221 | unsafe { ENGINE.compute_external(frames as i32); } 222 | } 223 | EOF 224 | -------------------------------------------------------------------------------- /vl1/vl1_gui_description.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "VL-1", 3 | "width": 180, 4 | "height": 138, 5 | "widgets": { 6 | "sliders": [ 7 | { 8 | "type": "int_slider", 9 | "name": "wv", 10 | "x": 10, "y": 8, 11 | "w": 80, "h": 15, 12 | "init": 0, 13 | "min": 0, 14 | "max": 7, 15 | "node": 0, 16 | "index": 7 17 | }, 18 | { 19 | "type": "int_slider", 20 | "name": "attack", 21 | "anchored": "anchored_below", 22 | "w": 80, "h": 15, 23 | "init": 0, 24 | "min": 0, 25 | "max": 9, 26 | "node": 0, 27 | "index": 0, 28 | "map_to_float": { 29 | "min": 0.001, 30 | "max": 0.5 31 | } 32 | }, 33 | { 34 | "type": "int_slider", 35 | "name": "decay", 36 | "anchored": "anchored_below", 37 | "w": 80, "h": 15, 38 | "init": 0, 39 | "min": 0, 40 | "max": 9, 41 | "node": 0, 42 | "index": 1, 43 | "map_to_float": { 44 | "min": 0.001, 45 | "max": 2.0 46 | } 47 | }, 48 | { 49 | "type": "int_slider", 50 | "name": "sustain", 51 | "anchored": "anchored_below", 52 | "w": 80, "h": 15, 53 | "init": 0, 54 | "min": 0, 55 | "max": 9, 56 | "node": 0, 57 | "index": 4, 58 | "map_to_float": { 59 | "min": 0.1, 60 | "max": 1.0 61 | } 62 | }, 63 | { 64 | "type": "int_slider", 65 | "name": "release", 66 | "anchored": "anchored_below", 67 | "w": 80, "h": 15, 68 | "init": 0, 69 | "min": 0, 70 | "max": 9, 71 | "node": 0, 72 | "index": 3, 73 | "map_to_float": { 74 | "min": 0.001, 75 | "max": 2.0 76 | } 77 | }, 78 | { 79 | "type": "int_slider", 80 | "name": "tremelo", 81 | "anchored": "anchored_below", 82 | "w": 80, "h": 15, 83 | "init": 0, 84 | "min": 0, 85 | "max": 10, 86 | "node": 0, 87 | "index": 5 88 | }, 89 | { 90 | "type": "int_slider", 91 | "name": "vibrato", 92 | "anchored": "anchored_below", 93 | "w": 80, "h": 15, 94 | "init": 0, 95 | "min": 0, 96 | "max": 10, 97 | "node": 0, 98 | "index": 6 99 | } 100 | ], 101 | "dropdowns": [ 102 | { 103 | "type": "dropdown", 104 | "name": "presets", 105 | "x" : 100, 106 | "y" : 20, 107 | "params": [ 108 | { 109 | "name" : "Piano", 110 | "values": [ 111 | { "node": 0, "index": 7, "value": 0.0, "slider": { "name": "wv", "value": 0 } }, 112 | { "node": 0, "index": 0, "value": 0.001, "slider": { "name": "attack", "value": 0 } }, 113 | { "node": 0, "index": 1, "value": 0.223, "slider": { "name": "decay", "value": 1 } }, 114 | { "node": 0, "index": 4, "value": 0.3, "slider": { "name": "sustain", "value": 2 } }, 115 | { "node": 0, "index": 3, "value": 0.223, "slider": { "name": "release", "value": 1 } }, 116 | { "node": 0, "index": 6, "value": 0.0, "slider": { "name": "vibrato", "value": 0 } }, 117 | { "node": 0, "index": 5, "value": 0.0, "slider": { "name": "tremelo", "value": 0 } } 118 | ] 119 | }, 120 | { 121 | "name" : "Fantasy", 122 | "values": [ 123 | { "node": 0, "index": 7, "value": 1.0, "slider": { "name": "wv", "value": 1 } }, 124 | { "node": 0, "index": 0, "value": 0.001, "slider": { "name": "attack", "value": 0 } }, 125 | { "node": 0, "index": 1, "value": 0.001, "slider": { "name": "decay", "value": 0 } }, 126 | { "node": 0, "index": 4, "value": 0.8, "slider": { "name": "sustain", "value": 7 } }, 127 | { "node": 0, "index": 3, "value": 1.112, "slider": { "name": "release", "value": 5 } }, 128 | { "node": 0, "index": 6, "value": 9.0, "slider": { "name": "vibrato", "value": 9 } }, 129 | { "node": 0, "index": 5, "value": 0.0, "slider": { "name": "tremelo", "value": 0 } } 130 | ] 131 | }, 132 | { 133 | "name" : "Violin", 134 | "values": [ 135 | { "node": 0, "index": 7, "value": 2.0, "slider": { "name": "wv", "value": 2 } }, 136 | { "node": 0, "index": 0, "value": 0.001, "slider": { "name": "attack", "value": 1 } }, 137 | { "node": 0, "index": 1, "value": 0.001, "slider": { "name": "decay", "value": 0 } }, 138 | { "node": 0, "index": 4, "value": 0.8, "slider": { "name": "sustain", "value": 7 } }, 139 | { "node": 0, "index": 3, "value": 1.112, "slider": { "name": "release", "value": 1 } }, 140 | { "node": 0, "index": 6, "value": 9.0, "slider": { "name": "vibrato", "value": 5 } }, 141 | { "node": 0, "index": 5, "value": 0.0, "slider": { "name": "tremelo", "value": 0 } } 142 | ] 143 | }, 144 | { 145 | "name" : "Flute", 146 | "values": [ 147 | { "node": 0, "index": 7, "value": 3.0, "slider": { "name": "wv", "value": 3 } }, 148 | { "node": 0, "index": 0, "value": 0.0564, "slider": { "name": "attack", "value": 1 } }, 149 | { "node": 0, "index": 1, "value": 0.001, "slider": { "name": "decay", "value": 0 } }, 150 | { "node": 0, "index": 4, "value": 0.7999, "slider": { "name": "sustain", "value": 7 } }, 151 | { "node": 0, "index": 3, "value": 0.223, "slider": { "name": "release", "value": 1 } }, 152 | { "node": 0, "index": 6, "value": 3.0, "slider": { "name": "vibrato", "value": 3 } }, 153 | { "node": 0, "index": 5, "value": 0.0, "slider": { "name": "tremelo", "value": 0 } } 154 | ] 155 | }, 156 | { 157 | "name" : "Guitar", 158 | "values": [ 159 | { "node": 0, "index": 7, "value": 4.0, "slider": { "name": "wv", "value": 4 } }, 160 | { "node": 0, "index": 0, "value": 0.2228, "slider": { "name": "attack", "value": 4 } }, 161 | { "node": 0, "index": 1, "value": 0.2231, "slider": { "name": "decay", "value": 1 } }, 162 | { "node": 0, "index": 4, "value": 0.1, "slider": { "name": "sustain", "value": 0 } }, 163 | { "node": 0, "index": 3, "value": 1.5558, "slider": { "name": "release", "value": 7 } }, 164 | { "node": 0, "index": 6, "value": 0.0, "slider": { "name": "vibrato", "value": 0 } }, 165 | { "node": 0, "index": 5, "value": 0.0, "slider": { "name": "tremelo", "value": 0 } } 166 | ] 167 | } 168 | ] 169 | } 170 | ] 171 | } 172 | } -------------------------------------------------------------------------------- /nuke/ui/scripts/adsr.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | */ 4 | 'use strict' 5 | 6 | function ADSR (client) { 7 | var attack = 2.001 8 | var attackMin = 0.001 9 | var attackMax = 4.0 10 | var attackStep = 0.01 11 | 12 | var decay = 1.0 13 | var decayMin = 0.0 14 | var decayMax = 4.0 15 | var decayStep = 0.01 16 | 17 | var substain = 0.75 18 | var substainMin = 0.0 19 | var substainMax = 1.0 20 | var substainStep = 0.01 21 | 22 | var release = 2.0 23 | var releaseMin = 0.0 24 | var releaseMax = 4.0 25 | var releaseStep = 0.01 26 | 27 | const red = "#DD4A22" 28 | const yellow = "#F3B83C" 29 | const blue = "#6060A6" 30 | const rose = "#FFBDB0" 31 | const rootbeer = "#1f0c07" 32 | 33 | this.controlAttack = (v) => { 34 | let mv = map(v, 0, 127, attackMin, attackMax) 35 | attack = mv 36 | return mv 37 | } 38 | 39 | this.controlDecay = (v) => { 40 | let mv = map(v, 0, 127, decayMin, decayMax) 41 | decay = mv 42 | return mv 43 | } 44 | 45 | this.controlSubstain = (v) => { 46 | let mv = map(v, 0, 127, substainMin, substainMax) 47 | substain = mv 48 | return mv 49 | } 50 | 51 | this.controlRelease = (v) => { 52 | let mv = map(v, 0, 127, releaseMin, releaseMax) 53 | release = mv 54 | return mv 55 | } 56 | 57 | this.start = function () { 58 | console.log("ADSR") 59 | } 60 | 61 | this.draw = function(context, transpose) { 62 | drawEnvelope(context, transpose) 63 | } 64 | 65 | drawEnvelope = function(context, transpose) { 66 | let bX = 30.0 67 | let bY = 60.0 68 | let bWidth = 260.0 69 | let bHeight = 110.0 70 | 71 | let spacing = 10.0 72 | let maxWidth = (bWidth - 3.0 * spacing) / 3.0 73 | 74 | let aw = maxWidth * normalize(attack, attackMin, attackMax) 75 | let dw = maxWidth * normalize(decay, decayMin, decayMax) 76 | let sh = bHeight * normalize(substain, substainMin, substainMax) 77 | let rw = maxWidth * normalize(release, releaseMin, releaseMax) 78 | let arcSize = 0.9 79 | 80 | let moveTo = (p) => { 81 | let tp = translate(transpose, p) 82 | context.moveTo(tp.x, tp.y) 83 | } 84 | let lineTo = (p) => { 85 | let tp = translate(transpose, p) 86 | context.lineTo(tp.x, tp.y) 87 | } 88 | 89 | let bezierCurveTo = (p1, p2, p3) => { 90 | let tp1 = translate(transpose, p1) 91 | let tp2 = translate(transpose, p2) 92 | let tp3 = translate(transpose, p3) 93 | context.bezierCurveTo(tp1.x, tp1.y, tp2.x, tp2.y, tp3.x, tp3.y) 94 | } 95 | 96 | let quadraticCurveTo = (p1, p2) => { 97 | let tp1 = translate(transpose, p1) 98 | let tp2 = translate(transpose, p2) 99 | context.quadraticCurveTo(tp1.x, tp1.y, tp2.x, tp2.y) 100 | } 101 | 102 | // line 103 | context.beginPath() 104 | moveTo(point(bX, bY + bHeight + spacing)) 105 | lineTo(point(bX + bWidth, bY + bHeight + spacing)) 106 | context.strokeStyle = "#d3d3d3" 107 | context.stroke() 108 | 109 | // attack 110 | context.beginPath() 111 | moveTo(point(bX, bY + bHeight)) 112 | quadraticCurveTo(point(bX + aw * arcSize, bY + bHeight * arcSize), point(bX + aw, bY)) 113 | lineTo(point(bX + aw, bY + bHeight)) 114 | lineTo(point(bX, bY + bHeight)) 115 | context.strokeStyle = rose 116 | context.stroke() 117 | 118 | context.beginPath() 119 | moveTo(point(bX, bY + bHeight)); 120 | quadraticCurveTo(point(bX + aw * arcSize, bY + bHeight * arcSize), point(bX + aw, bY)); 121 | lineTo(point(bX + aw, bY + bHeight)) 122 | context.fillStyle = rose 123 | context.fill() 124 | 125 | // decay 126 | context.beginPath() 127 | moveTo(point(bX + aw + spacing, bY + bHeight)) 128 | lineTo(point(bX + aw + spacing, bY)) 129 | quadraticCurveTo(point( 130 | bX + aw + spacing + dw * (1.0 - arcSize), bY + (bHeight - sh) * arcSize), 131 | point(bX + aw + spacing + dw, bY + bHeight - sh)) 132 | lineTo(point(bX + aw + spacing + dw, bY + bHeight)) 133 | context.closePath() 134 | context.strokeStyle = blue 135 | context.stroke() 136 | 137 | context.beginPath() 138 | moveTo(point(bX + aw + spacing, bY + bHeight)) 139 | lineTo(point(bX + aw + spacing, bY)); 140 | quadraticCurveTo(point( 141 | bX + aw + spacing + dw * (1.0 - arcSize), bY + (bHeight - sh) * arcSize), 142 | point(bX + aw + spacing + dw, bY + bHeight - sh)) 143 | lineTo(point(bX + aw + spacing + dw, bY + bHeight)) 144 | lineTo(point(bX + aw + spacing, bY + bHeight)) 145 | context.fillStyle = blue 146 | context.fill() 147 | 148 | // substain 149 | context.beginPath() 150 | moveTo(point(bX + aw + spacing + dw + spacing, bY + bHeight - sh)) 151 | lineTo(point(bX + bWidth - spacing - rw, bY + bHeight - sh)) 152 | lineTo(point(bX + bWidth - spacing - rw, bY + bHeight)) 153 | lineTo(point(bX + aw + spacing + dw + spacing, bY + bHeight)) 154 | context.closePath() 155 | context.strokeStyle = yellow 156 | context.stroke() 157 | 158 | context.beginPath() 159 | moveTo(point(bX + aw + spacing + dw + spacing, bY + bHeight - sh)) 160 | lineTo(point(bX + bWidth - spacing - rw, bY + bHeight - sh)) 161 | lineTo(point(bX + bWidth - spacing - rw, bY + bHeight)) 162 | lineTo(point(bX + aw + spacing + dw + spacing, bY + bHeight)) 163 | context.fillStyle = yellow 164 | context.fill() 165 | 166 | // release 167 | context.beginPath() 168 | moveTo(point(bX + bWidth - rw, bY + bHeight)) 169 | lineTo(point(bX + bWidth - rw, bY + bHeight - sh)) 170 | quadraticCurveTo( 171 | point(bX + bWidth - rw * arcSize, bY + bHeight - sh * (1.0 - arcSize)), 172 | point(bX + bWidth, bY + bHeight)) 173 | context.closePath() 174 | context.strokeStyle = red 175 | context.stroke() 176 | 177 | context.beginPath() 178 | moveTo(point(bX + bWidth - rw, bY + bHeight)) 179 | lineTo(point(bX + bWidth - rw, bY + bHeight - sh)) 180 | quadraticCurveTo( 181 | point(bX + bWidth - rw * arcSize, bY + bHeight - sh * (1.0 - arcSize)), 182 | point(bX + bWidth, bY + bHeight)) 183 | context.fillStyle = red 184 | context.fill() 185 | 186 | } 187 | 188 | // TODO: move to utils.js 189 | function clamp (v, min, max) { return v < min ? min : v > max ? max : v } 190 | function normalize(n, min, max) { return (n - min) / (max - min) } 191 | function vector(x,y) { return new Vector(x,y) } 192 | function point(x,y) { return new Vector(x,y) } // hmm... 193 | function floor(x) { return Math.floor(x) } 194 | function transform_scale(v, s) { return v.mul(s) } 195 | function translate(v,p) { return v.plus(p) } 196 | function rotate(p,a,orgin) { 197 | let s = Math.sin(a) 198 | let c = Math.cos(a) 199 | 200 | // translate to orgin 201 | p = p.minus(orgin) 202 | 203 | // rotate around orgin 204 | p = point(p.x * c - p.y * s, p.x * s + p.y * c) 205 | 206 | // translate back to orignal position 207 | return p.plus(orgin) 208 | } 209 | function radians(d) { return d * (Math.PI/180) } 210 | function map(x, in_min, in_max, out_min, out_max) { 211 | return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min 212 | } 213 | } -------------------------------------------------------------------------------- /muses/ui/scripts/adsr.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | */ 4 | 'use strict' 5 | 6 | function ADSR (client) { 7 | var attack = 2.001 8 | var attackMin = 0.001 9 | var attackMax = 4.0 10 | var attackStep = 0.01 11 | 12 | var decay = 1.0 13 | var decayMin = 0.0 14 | var decayMax = 4.0 15 | var decayStep = 0.01 16 | 17 | var substain = 0.75 18 | var substainMin = 0.0 19 | var substainMax = 1.0 20 | var substainStep = 0.01 21 | 22 | var release = 2.0 23 | var releaseMin = 0.0 24 | var releaseMax = 4.0 25 | var releaseStep = 0.01 26 | 27 | const red = "#DD4A22" 28 | const yellow = "#F3B83C" 29 | const blue = "#6060A6" 30 | const rose = "#FFBDB0" 31 | const rootbeer = "#1f0c07" 32 | 33 | this.controlAttack = (v) => { 34 | let mv = map(v, 0, 127, attackMin, attackMax) 35 | attack = mv 36 | return mv 37 | } 38 | 39 | this.controlDecay = (v) => { 40 | let mv = map(v, 0, 127, decayMin, decayMax) 41 | decay = mv 42 | return mv 43 | } 44 | 45 | this.controlSubstain = (v) => { 46 | let mv = map(v, 0, 127, substainMin, substainMax) 47 | substain = mv 48 | return mv 49 | } 50 | 51 | this.controlRelease = (v) => { 52 | let mv = map(v, 0, 127, releaseMin, releaseMax) 53 | release = mv 54 | return mv 55 | } 56 | 57 | this.incAttack = (v) => { 58 | attack = clamp(attack + 0.01 * v, attackMin, attackMax) 59 | return attack 60 | } 61 | 62 | this.incDecay = (v) => { 63 | decay = clamp(decay + 0.01 * v, decayMin, decayMax) 64 | return decay 65 | } 66 | 67 | this.incSubstain = (v) => { 68 | substain = clamp(substain + 0.01 * v, substainMin, substainMax) 69 | return substain 70 | } 71 | 72 | this.incRelease = (v) => { 73 | release = clamp(release + 0.01 * v, releaseMin, releaseMax) 74 | return release 75 | } 76 | 77 | this.start = function () { 78 | console.log("ADSR") 79 | } 80 | 81 | this.draw = function(context, transpose) { 82 | drawEnvelope(context, transpose) 83 | } 84 | 85 | drawEnvelope = function(context, transpose) { 86 | let bX = 30.0 87 | let bY = 60.0 88 | let bWidth = 260.0 89 | let bHeight = 110.0 90 | 91 | let spacing = 10.0 92 | let maxWidth = (bWidth - 3.0 * spacing) / 3.0 93 | 94 | let aw = maxWidth * normalize(attack, attackMin, attackMax) 95 | let dw = maxWidth * normalize(decay, decayMin, decayMax) 96 | let sh = bHeight * normalize(substain, substainMin, substainMax) 97 | let rw = maxWidth * normalize(release, releaseMin, releaseMax) 98 | let arcSize = 0.9 99 | 100 | let moveTo = (p) => { 101 | let tp = translate(transpose, p) 102 | context.moveTo(tp.x, tp.y) 103 | } 104 | let lineTo = (p) => { 105 | let tp = translate(transpose, p) 106 | context.lineTo(tp.x, tp.y) 107 | } 108 | 109 | let bezierCurveTo = (p1, p2, p3) => { 110 | let tp1 = translate(transpose, p1) 111 | let tp2 = translate(transpose, p2) 112 | let tp3 = translate(transpose, p3) 113 | context.bezierCurveTo(tp1.x, tp1.y, tp2.x, tp2.y, tp3.x, tp3.y) 114 | } 115 | 116 | let quadraticCurveTo = (p1, p2) => { 117 | let tp1 = translate(transpose, p1) 118 | let tp2 = translate(transpose, p2) 119 | context.quadraticCurveTo(tp1.x, tp1.y, tp2.x, tp2.y) 120 | } 121 | 122 | // line 123 | context.beginPath() 124 | moveTo(point(bX, bY + bHeight + spacing)) 125 | lineTo(point(bX + bWidth, bY + bHeight + spacing)) 126 | context.strokeStyle = "#d3d3d3" 127 | context.stroke() 128 | 129 | // attack 130 | context.beginPath() 131 | moveTo(point(bX, bY + bHeight)) 132 | quadraticCurveTo(point(bX + aw * arcSize, bY + bHeight * arcSize), point(bX + aw, bY)) 133 | lineTo(point(bX + aw, bY + bHeight)) 134 | lineTo(point(bX, bY + bHeight)) 135 | context.strokeStyle = rose 136 | context.stroke() 137 | 138 | context.beginPath() 139 | moveTo(point(bX, bY + bHeight)); 140 | quadraticCurveTo(point(bX + aw * arcSize, bY + bHeight * arcSize), point(bX + aw, bY)); 141 | lineTo(point(bX + aw, bY + bHeight)) 142 | context.fillStyle = rose 143 | context.fill() 144 | 145 | // decay 146 | context.beginPath() 147 | moveTo(point(bX + aw + spacing, bY + bHeight)) 148 | lineTo(point(bX + aw + spacing, bY)) 149 | quadraticCurveTo(point( 150 | bX + aw + spacing + dw * (1.0 - arcSize), bY + (bHeight - sh) * arcSize), 151 | point(bX + aw + spacing + dw, bY + bHeight - sh)) 152 | lineTo(point(bX + aw + spacing + dw, bY + bHeight)) 153 | context.closePath() 154 | context.strokeStyle = blue 155 | context.stroke() 156 | 157 | context.beginPath() 158 | moveTo(point(bX + aw + spacing, bY + bHeight)) 159 | lineTo(point(bX + aw + spacing, bY)); 160 | quadraticCurveTo(point( 161 | bX + aw + spacing + dw * (1.0 - arcSize), bY + (bHeight - sh) * arcSize), 162 | point(bX + aw + spacing + dw, bY + bHeight - sh)) 163 | lineTo(point(bX + aw + spacing + dw, bY + bHeight)) 164 | lineTo(point(bX + aw + spacing, bY + bHeight)) 165 | context.fillStyle = blue 166 | context.fill() 167 | 168 | // substain 169 | context.beginPath() 170 | moveTo(point(bX + aw + spacing + dw + spacing, bY + bHeight - sh)) 171 | lineTo(point(bX + bWidth - spacing - rw, bY + bHeight - sh)) 172 | lineTo(point(bX + bWidth - spacing - rw, bY + bHeight)) 173 | lineTo(point(bX + aw + spacing + dw + spacing, bY + bHeight)) 174 | context.closePath() 175 | context.strokeStyle = yellow 176 | context.stroke() 177 | 178 | context.beginPath() 179 | moveTo(point(bX + aw + spacing + dw + spacing, bY + bHeight - sh)) 180 | lineTo(point(bX + bWidth - spacing - rw, bY + bHeight - sh)) 181 | lineTo(point(bX + bWidth - spacing - rw, bY + bHeight)) 182 | lineTo(point(bX + aw + spacing + dw + spacing, bY + bHeight)) 183 | context.fillStyle = yellow 184 | context.fill() 185 | 186 | // release 187 | context.beginPath() 188 | moveTo(point(bX + bWidth - rw, bY + bHeight)) 189 | lineTo(point(bX + bWidth - rw, bY + bHeight - sh)) 190 | quadraticCurveTo( 191 | point(bX + bWidth - rw * arcSize, bY + bHeight - sh * (1.0 - arcSize)), 192 | point(bX + bWidth, bY + bHeight)) 193 | context.closePath() 194 | context.strokeStyle = red 195 | context.stroke() 196 | 197 | context.beginPath() 198 | moveTo(point(bX + bWidth - rw, bY + bHeight)) 199 | lineTo(point(bX + bWidth - rw, bY + bHeight - sh)) 200 | quadraticCurveTo( 201 | point(bX + bWidth - rw * arcSize, bY + bHeight - sh * (1.0 - arcSize)), 202 | point(bX + bWidth, bY + bHeight)) 203 | context.fillStyle = red 204 | context.fill() 205 | 206 | } 207 | 208 | // TODO: move to utils.js 209 | function clamp (v, min, max) { return v < min ? min : v > max ? max : v } 210 | function normalize(n, min, max) { return (n - min) / (max - min) } 211 | function vector(x,y) { return new Vector(x,y) } 212 | function point(x,y) { return new Vector(x,y) } // hmm... 213 | function floor(x) { return Math.floor(x) } 214 | function transform_scale(v, s) { return v.mul(s) } 215 | function translate(v,p) { return v.plus(p) } 216 | function rotate(p,a,orgin) { 217 | let s = Math.sin(a) 218 | let c = Math.cos(a) 219 | 220 | // translate to orgin 221 | p = p.minus(orgin) 222 | 223 | // rotate around orgin 224 | p = point(p.x * c - p.y * s, p.x * s + p.y * c) 225 | 226 | // translate back to orignal position 227 | return p.plus(orgin) 228 | } 229 | function radians(d) { return d * (Math.PI/180) } 230 | function map(x, in_min, in_max, out_min, out_max) { 231 | return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min 232 | } 233 | } -------------------------------------------------------------------------------- /gain/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(wasm_target_feature)] 2 | 3 | // ALL OF THE FOLLOWING WARNINGS NEED TO BE ADDRESSED IN THE FAUST COMPILER 4 | #![allow(non_snake_case)] 5 | #![allow(unused_mut)] 6 | #![allow(unused_parens)] 7 | #![allow(non_camel_case_types)] 8 | #![allow(dead_code)] 9 | #![allow(unused_variables)] 10 | // REMOVE SOMETIME SOON :-) 11 | 12 | const MAX_PARAM_SIZE: usize = 1024; 13 | #[no_mangle] 14 | pub static mut PARAM_NAME: [u8;MAX_PARAM_SIZE] = [65;MAX_PARAM_SIZE]; 15 | 16 | const MAX_BUFFER_SIZE: usize = 1024; 17 | 18 | // Everything following should not be visible to the outside world 19 | 20 | #[derive(Clone)] 21 | struct ParamRange { 22 | init: f32, 23 | min: f32, 24 | max: f32, 25 | step: f32, 26 | } 27 | 28 | impl ParamRange { 29 | pub fn new(init: f32, min: f32, max: f32, step: f32) -> Self { 30 | Self { 31 | init, 32 | min, 33 | max, 34 | step, 35 | } 36 | } 37 | } 38 | 39 | #[derive(Clone)] 40 | struct Param { 41 | index: i32, 42 | range: ParamRange, 43 | } 44 | 45 | impl Param { 46 | pub fn new(name: String, index: i32, range: ParamRange) -> Self { 47 | Self { 48 | index, 49 | range 50 | } 51 | } 52 | } 53 | 54 | // Notes and voices 55 | 56 | type Note = i32; 57 | type Pitch = f32; 58 | 59 | /// convert midi note to its corresponding frequency 60 | #[inline] 61 | fn to_freq(n: i32) -> Pitch { 62 | 2.0f32.powf( (n - 69) as Pitch / 12.0 ) * 440.0 63 | } 64 | 65 | /// convert midi note to an index within voices range, can then be used as 66 | /// sample index, for example. 67 | #[inline] 68 | fn to_index(n: i32, voices: u32) -> u32 { 69 | (12.0*(to_freq(n) / 130.81).log2()).round().abs() as u32 % voices 70 | } 71 | 72 | #[inline] 73 | fn freq_to_index(freq: f32, voices: u32) -> u32 { 74 | (12.0*(freq / 130.81).log2()).round().abs() as u32 % voices 75 | } 76 | 77 | /// convert midi note to its corresponding frequency, with explicit base tuning 78 | #[inline] 79 | fn to_freq_tuning(n:i32, tuning: Pitch) -> Pitch { 80 | 2.0f32.powf( (n - 69) as f32 / 12.0 ) * tuning 81 | } 82 | 83 | #[derive(Copy, Clone)] 84 | struct VoiceInfo { 85 | pub active: bool, 86 | pub note: Note, 87 | pub channel: i32, 88 | pub voice_age: i64, 89 | } 90 | 91 | impl VoiceInfo { 92 | pub fn new() -> VoiceInfo { 93 | VoiceInfo { 94 | active: false, 95 | note: 0, 96 | channel: 0, 97 | voice_age: 0, 98 | } 99 | } 100 | } 101 | 102 | #[no_mangle] 103 | static mut IN_BUFFER0: [f32;MAX_BUFFER_SIZE] = [0.;MAX_BUFFER_SIZE]; 104 | #[no_mangle] 105 | static mut OUT_BUFFER0: [f32;MAX_BUFFER_SIZE] = [0.;MAX_BUFFER_SIZE]; 106 | static mut INPUTS: [* const f32;1] = [0 as * const f32; 1]; 107 | static mut OUTPUTS: [* mut f32;1] = [0 as * mut f32; 1]; 108 | static mut ENGINE : mydsp = mydsp { 109 | fVslider0: 0.0, 110 | fRec0: [0.0;2], 111 | fSampleRate: 0, 112 | }; 113 | 114 | type T = f32; 115 | 116 | struct mydsp { 117 | fVslider0: f32, 118 | fRec0: [f32;2], 119 | fSampleRate: i32, 120 | 121 | } 122 | 123 | impl mydsp { 124 | 125 | fn new() -> mydsp { 126 | mydsp { 127 | fVslider0: 0.0, 128 | fRec0: [0.0;2], 129 | fSampleRate: 0, 130 | } 131 | } 132 | pub fn get_voices(&self) -> i32 { 133 | 0 134 | } 135 | 136 | pub fn get_input(&self, index: u32) -> u32 { 137 | unsafe { INPUTS[index as usize] as u32 } 138 | } 139 | 140 | pub fn get_output(&self, index: u32) -> u32 { 141 | unsafe { OUTPUTS[index as usize] as u32 } 142 | } 143 | 144 | pub fn set_input(&self, index: u32, offset: u32) { 145 | unsafe { INPUTS[index as usize] = offset as * const f32; }; 146 | } 147 | 148 | pub fn set_output(&self, index: u32, offset: u32) { 149 | unsafe { OUTPUTS[index as usize] = offset as * mut f32; }; 150 | } 151 | 152 | pub fn get_sample_rate(&self) -> i32 { 153 | return self.fSampleRate; 154 | } 155 | pub fn get_num_inputs(&self) -> i32 { 156 | return 1; 157 | } 158 | pub fn get_num_outputs(&self) -> i32 { 159 | return 1; 160 | } 161 | pub fn get_input_rate(&self, channel: i32) -> i32 { 162 | let mut rate: i32; 163 | match (channel) { 164 | 0 => { 165 | rate = 1; 166 | }, 167 | _ => { 168 | rate = -1; 169 | }, 170 | } 171 | return rate; 172 | } 173 | pub fn get_output_rate(&self, channel: i32) -> i32 { 174 | let mut rate: i32; 175 | match (channel) { 176 | 0 => { 177 | rate = 1; 178 | }, 179 | _ => { 180 | rate = -1; 181 | }, 182 | } 183 | return rate; 184 | } 185 | 186 | fn class_init(sample_rate: i32) { 187 | } 188 | fn instance_reset_params(&mut self) { 189 | self.fVslider0 = 0.0; 190 | } 191 | fn instance_clear(&mut self) { 192 | for l0 in 0..2 { 193 | self.fRec0[l0 as usize] = 0.0; 194 | } 195 | } 196 | fn instance_constants(&mut self, sample_rate: i32) { 197 | self.fSampleRate = sample_rate; 198 | } 199 | fn instance_init(&mut self, sample_rate: i32) { 200 | self.instance_constants(sample_rate); 201 | self.instance_reset_params(); 202 | self.instance_clear(); 203 | } 204 | pub fn init(&mut self, sample_rate: i32) { 205 | mydsp::class_init(sample_rate); 206 | self.instance_init(sample_rate); 207 | self.init_voices(); 208 | self.init_buffers(); 209 | } 210 | pub fn get_param_info(&mut self, name: &str) -> Param { 211 | match name { 212 | "gain_control" => Param { index: 0, range: ParamRange::new(0.0, -70.0, 4.0, 0.10000000000000001) }, 213 | 214 | _ => Param { index: -1, range: ParamRange::new(0.0, 0.0, 0.0, 0.0)} 215 | } 216 | } 217 | fn init_voices(&mut self) { 218 | } 219 | pub fn handle_note_on(&mut self, _mn: Note, _vel: f32) { 220 | }pub fn handle_note_off(&mut self, _mn: Note, _vel: f32) { 221 | } 222 | fn init_buffers(&self) { 223 | unsafe { 224 | INPUTS[0] = IN_BUFFER0.as_ptr(); 225 | OUTPUTS[0] = OUT_BUFFER0.as_mut_ptr(); 226 | }; 227 | } 228 | 229 | pub fn get_param(&self, param: u32) -> T { 230 | match param { 231 | 0 => self.fVslider0, 232 | _ => 0., 233 | } 234 | } 235 | 236 | pub fn set_param(&mut self, param: u32, value: T) { 237 | match param { 238 | 0 => { self.fVslider0 = value } 239 | _ => {} 240 | } 241 | } 242 | #[target_feature(enable = "simd128")] 243 | #[inline] 244 | unsafe fn compute(&mut self, count: i32, inputs: &[&[T];1], outputs: &mut [&mut [T];1]) { 245 | let inputs0 = inputs[0][..count as usize].iter(); 246 | let [outputs0] = outputs; 247 | let (outputs0) = { 248 | let outputs0 = outputs0[..count as usize].iter_mut(); 249 | (outputs0) 250 | }; 251 | let mut fSlow0: f32 = (0.00100000005 * f32::powf(10.0, (0.0500000007 * (self.fVslider0 as f32)))); 252 | let zipped_iterators = inputs0.zip(outputs0); 253 | for (input0, output0) in zipped_iterators { 254 | self.fRec0[0] = (fSlow0 + (0.999000013 * self.fRec0[1])); 255 | *output0 = (((*input0 as f32) * self.fRec0[0]) as f32); 256 | self.fRec0[1] = self.fRec0[0]; 257 | } 258 | } 259 | 260 | #[inline] 261 | pub fn compute_external(&mut self, count: i32) { 262 | let (input0, output0) = unsafe { 263 | (::std::slice::from_raw_parts(INPUTS[0], count as usize), 264 | ::std::slice::from_raw_parts_mut(OUTPUTS[0], count as usize)) 265 | }; 266 | unsafe { self.compute(count, &[input0], &mut [output0]); } 267 | } 268 | } 269 | 270 | #[no_mangle] 271 | pub fn get_input(index: u32) -> u32 { 272 | unsafe { ENGINE.get_input(index) } 273 | } 274 | 275 | #[no_mangle] 276 | pub fn get_output(index: u32) -> u32 { 277 | unsafe { ENGINE.get_output(index) } 278 | } 279 | 280 | #[no_mangle] 281 | pub fn set_input(index: u32, offset: u32) { 282 | unsafe { ENGINE.set_input(index, offset); }; 283 | } 284 | 285 | #[no_mangle] 286 | pub fn set_output(index: u32, offset: u32) { 287 | unsafe { ENGINE.set_output(index, offset); }; 288 | } 289 | 290 | #[no_mangle] 291 | pub fn handle_note_on(mn: i32, vel: f32) { 292 | unsafe { ENGINE.handle_note_on(mn, vel); } 293 | } 294 | 295 | #[no_mangle] 296 | pub fn handle_note_off(mn: i32, vel: f32) { 297 | unsafe { ENGINE.handle_note_off(mn, vel); } 298 | } 299 | 300 | #[no_mangle] 301 | pub fn get_voices() -> i32 { 302 | unsafe { ENGINE.get_voices() } 303 | } 304 | 305 | #[no_mangle] 306 | pub fn get_param_index(length: i32) -> i32 { 307 | if length < MAX_PARAM_SIZE as i32 { 308 | let mut param = String::new(); 309 | for i in 0..length as usize { 310 | let c = unsafe { PARAM_NAME[i] } as char; 311 | param.push(c); 312 | } 313 | return unsafe { ENGINE.get_param_info(¶m).index }; 314 | } 315 | else { 316 | return -1; 317 | } 318 | } 319 | 320 | #[no_mangle] 321 | pub fn get_gain_index() -> i32 { 322 | unsafe { ENGINE.get_param_info("gain").index } 323 | } 324 | 325 | #[no_mangle] 326 | pub fn get_gate_index() -> i32 { 327 | unsafe { ENGINE.get_param_info("gate").index } 328 | } 329 | 330 | #[no_mangle] 331 | pub fn get_freq_index() -> i32 { 332 | unsafe { ENGINE.get_param_info("freq").index } 333 | } 334 | 335 | 336 | #[no_mangle] 337 | pub fn get_sample_rate() -> f64 { 338 | unsafe { ENGINE.get_sample_rate() as f64 } 339 | } 340 | 341 | // number of input channels (currently max 2) 342 | #[no_mangle] 343 | pub fn get_num_input_channels() -> u32 { 344 | unsafe { ENGINE.get_num_inputs() as u32 } 345 | } 346 | 347 | // number of output channels (currently max 2) 348 | #[no_mangle] 349 | pub fn get_num_output_channels() -> u32 { 350 | unsafe { ENGINE.get_num_outputs() as u32 } 351 | } 352 | 353 | #[no_mangle] 354 | pub fn init(sample_rate: f64) -> () { 355 | unsafe { ENGINE.init(sample_rate as i32); } 356 | } 357 | 358 | #[no_mangle] 359 | pub fn set_param_float(index: u32, v: f32) { 360 | unsafe { ENGINE.set_param(index, v); } 361 | } 362 | 363 | #[no_mangle] 364 | pub fn set_param_int(index: u32, v: i32) { 365 | unsafe { ENGINE.set_param(index, v as f32); } 366 | } 367 | 368 | #[no_mangle] 369 | pub fn get_param_float(index: u32) -> f32 { 370 | unsafe { ENGINE.get_param(index) } 371 | } 372 | 373 | #[no_mangle] 374 | pub fn get_param_int(index: u32) -> i32 { 375 | unsafe { ENGINE.get_param(index) as i32 } 376 | } 377 | 378 | #[no_mangle] 379 | pub fn compute(frames: u32) -> () { 380 | unsafe { ENGINE.compute_external(frames as i32); } 381 | } -------------------------------------------------------------------------------- /gain/js/jquery.knob.min.js: -------------------------------------------------------------------------------- 1 | (function(e){if(typeof define==="function"&&define.amd){define(["jquery"],e)}else{e(jQuery)}})(function(e){"use strict";var t={},n=Math.max,r=Math.min;t.c={};t.c.d=e(document);t.c.t=function(e){return e.originalEvent.touches.length-1};t.o=function(){var n=this;this.o=null;this.$=null;this.i=null;this.g=null;this.v=null;this.cv=null;this.x=0;this.y=0;this.w=0;this.h=0;this.$c=null;this.c=null;this.t=0;this.isInit=false;this.fgColor=null;this.pColor=null;this.dH=null;this.cH=null;this.eH=null;this.rH=null;this.scale=1;this.relative=false;this.relativeWidth=false;this.relativeHeight=false;this.$div=null;this.run=function(){var t=function(e,t){var r;for(r in t){n.o[r]=t[r]}n._carve().init();n._configure()._draw()};if(this.$.data("kontroled"))return;this.$.data("kontroled",true);this.extend();this.o=e.extend({min:this.$.data("min")!==undefined?this.$.data("min"):0,max:this.$.data("max")!==undefined?this.$.data("max"):100,stopper:true,readOnly:this.$.data("readonly")||this.$.attr("readonly")==="readonly",cursor:this.$.data("cursor")===true&&30||this.$.data("cursor")||0,thickness:this.$.data("thickness")&&Math.max(Math.min(this.$.data("thickness"),1),.01)||.35,lineCap:this.$.data("linecap")||"butt",width:this.$.data("width")||200,height:this.$.data("height")||200,displayInput:this.$.data("displayinput")==null||this.$.data("displayinput"),displayPrevious:this.$.data("displayprevious"),fgColor:this.$.data("fgcolor")||"#87CEEB",inputColor:this.$.data("inputcolor"),font:this.$.data("font")||"Arial",fontWeight:this.$.data("font-weight")||"bold",inline:false,step:this.$.data("step")||1,rotation:this.$.data("rotation"),draw:null,change:null,cancel:null,release:null,format:function(e){return e},parse:function(e){return parseFloat(e)}},this.o);this.o.flip=this.o.rotation==="anticlockwise"||this.o.rotation==="acw";if(!this.o.inputColor){this.o.inputColor=this.o.fgColor}if(this.$.is("fieldset")){this.v={};this.i=this.$.find("input");this.i.each(function(t){var r=e(this);n.i[t]=r;n.v[t]=n.o.parse(r.val());r.bind("change blur",function(){var e={};e[t]=r.val();n.val(n._validate(e))})});this.$.find("legend").remove()}else{this.i=this.$;this.v=this.o.parse(this.$.val());this.v===""&&(this.v=this.o.min);this.$.bind("change blur",function(){n.val(n._validate(n.o.parse(n.$.val())))})}!this.o.displayInput&&this.$.hide();this.$c=e(document.createElement("canvas")).attr({width:this.o.width,height:this.o.height});this.$div=e('
');this.$.wrap(this.$div).before(this.$c);this.$div=this.$.parent();if(typeof G_vmlCanvasManager!=="undefined"){G_vmlCanvasManager.initElement(this.$c[0])}this.c=this.$c[0].getContext?this.$c[0].getContext("2d"):null;if(!this.c){throw{name:"CanvasNotSupportedException",message:"Canvas not supported. Please use excanvas on IE8.0.",toString:function(){return this.name+": "+this.message}}}this.scale=(window.devicePixelRatio||1)/(this.c.webkitBackingStorePixelRatio||this.c.mozBackingStorePixelRatio||this.c.msBackingStorePixelRatio||this.c.oBackingStorePixelRatio||this.c.backingStorePixelRatio||1);this.relativeWidth=this.o.width%1!==0&&this.o.width.indexOf("%");this.relativeHeight=this.o.height%1!==0&&this.o.height.indexOf("%");this.relative=this.relativeWidth||this.relativeHeight;this._carve();if(this.v instanceof Object){this.cv={};this.copy(this.v,this.cv)}else{this.cv=this.v}this.$.bind("configure",t).parent().bind("configure",t);this._listen()._configure()._xy().init();this.isInit=true;this.$.val(this.o.format(this.v));this._draw();return this};this._carve=function(){if(this.relative){var e=this.relativeWidth?this.$div.parent().width()*parseInt(this.o.width)/100:this.$div.parent().width(),t=this.relativeHeight?this.$div.parent().height()*parseInt(this.o.height)/100:this.$div.parent().height();this.w=this.h=Math.min(e,t)}else{this.w=this.o.width;this.h=this.o.height}this.$div.css({width:this.w+"px",height:this.h+"px"});this.$c.attr({width:this.w,height:this.h});if(this.scale!==1){this.$c[0].width=this.$c[0].width*this.scale;this.$c[0].height=this.$c[0].height*this.scale;this.$c.width(this.w);this.$c.height(this.h)}return this};this._draw=function(){var e=true;n.g=n.c;n.clear();n.dH&&(e=n.dH());e!==false&&n.draw()};this._touch=function(e){var r=function(e){var t=n.xy2val(e.originalEvent.touches[n.t].pageX,e.originalEvent.touches[n.t].pageY);if(t==n.cv)return;if(n.cH&&n.cH(t)===false)return;n.change(n._validate(t));n._draw()};this.t=t.c.t(e);r(e);t.c.d.bind("touchmove.k",r).bind("touchend.k",function(){t.c.d.unbind("touchmove.k touchend.k");n.val(n.cv)});return this};this._mouse=function(e){var r=function(e){var t=n.xy2val(e.pageX,e.pageY);if(t==n.cv)return;if(n.cH&&n.cH(t)===false)return;n.change(n._validate(t));n._draw()};r(e);t.c.d.bind("mousemove.k",r).bind("keyup.k",function(e){if(e.keyCode===27){t.c.d.unbind("mouseup.k mousemove.k keyup.k");if(n.eH&&n.eH()===false)return;n.cancel()}}).bind("mouseup.k",function(e){t.c.d.unbind("mousemove.k mouseup.k keyup.k");n.val(n.cv)});return this};this._xy=function(){var e=this.$c.offset();this.x=e.left;this.y=e.top;return this};this._listen=function(){if(!this.o.readOnly){this.$c.bind("mousedown",function(e){e.preventDefault();n._xy()._mouse(e)}).bind("touchstart",function(e){e.preventDefault();n._xy()._touch(e)});this.listen()}else{this.$.attr("readonly","readonly")}if(this.relative){e(window).resize(function(){n._carve().init();n._draw()})}return this};this._configure=function(){if(this.o.draw)this.dH=this.o.draw;if(this.o.change)this.cH=this.o.change;if(this.o.cancel)this.eH=this.o.cancel;if(this.o.release)this.rH=this.o.release;if(this.o.displayPrevious){this.pColor=this.h2rgba(this.o.fgColor,"0.4");this.fgColor=this.h2rgba(this.o.fgColor,"0.6")}else{this.fgColor=this.o.fgColor}return this};this._clear=function(){this.$c[0].width=this.$c[0].width};this._validate=function(e){var t=~~((e<0?-.5:.5)+e/this.o.step)*this.o.step;return Math.round(t*100)/100};this.listen=function(){};this.extend=function(){};this.init=function(){};this.change=function(e){};this.val=function(e){};this.xy2val=function(e,t){};this.draw=function(){};this.clear=function(){this._clear()};this.h2rgba=function(e,t){var n;e=e.substring(1,7);n=[parseInt(e.substring(0,2),16),parseInt(e.substring(2,4),16),parseInt(e.substring(4,6),16)];return"rgba("+n[0]+","+n[1]+","+n[2]+","+t+")"};this.copy=function(e,t){for(var n in e){t[n]=e[n]}}};t.Dial=function(){t.o.call(this);this.startAngle=null;this.xy=null;this.radius=null;this.lineWidth=null;this.cursorExt=null;this.w2=null;this.PI2=2*Math.PI;this.extend=function(){this.o=e.extend({bgColor:this.$.data("bgcolor")||"#EEEEEE",angleOffset:this.$.data("angleoffset")||0,angleArc:this.$.data("anglearc")||360,inline:true},this.o)};this.val=function(e,t){if(null!=e){e=this.o.parse(e);if(t!==false&&e!=this.v&&this.rH&&this.rH(e)===false){return}this.cv=this.o.stopper?n(r(e,this.o.max),this.o.min):e;this.v=this.cv;this.$.val(this.o.format(this.v));this._draw()}else{return this.v}};this.xy2val=function(e,t){var i,s;i=Math.atan2(e-(this.x+this.w2),-(t-this.y-this.w2))-this.angleOffset;if(this.o.flip){i=this.angleArc-i-this.PI2}if(this.angleArc!=this.PI2&&i<0&&i>-.5){i=0}else if(i<0){i+=this.PI2}s=i*(this.o.max-this.o.min)/this.angleArc+this.o.min;this.o.stopper&&(s=n(r(s,this.o.max),this.o.min));return s};this.listen=function(){var t=this,i,s,o=function(e){e.preventDefault();var o=e.originalEvent,u=o.detail||o.wheelDeltaX,a=o.detail||o.wheelDeltaY,f=t._validate(t.o.parse(t.$.val()))+(u>0||a>0?t.o.step:u<0||a<0?-t.o.step:0);f=n(r(f,t.o.max),t.o.min);t.val(f,false);if(t.rH){clearTimeout(i);i=setTimeout(function(){t.rH(f);i=null},100);if(!s){s=setTimeout(function(){if(i)t.rH(f);s=null},200)}}},u,a,f=1,l={37:-t.o.step,38:t.o.step,39:t.o.step,40:-t.o.step};this.$.bind("keydown",function(i){var s=i.keyCode;if(s>=96&&s<=105){s=i.keyCode=s-48}u=parseInt(String.fromCharCode(s));if(isNaN(u)){s!==13&&s!==8&&s!==9&&s!==189&&(s!==190||t.$.val().match(/\./))&&i.preventDefault();if(e.inArray(s,[37,38,39,40])>-1){i.preventDefault();var o=t.o.parse(t.$.val())+l[s]*f;t.o.stopper&&(o=n(r(o,t.o.max),t.o.min));t.change(t._validate(o));t._draw();a=window.setTimeout(function(){f*=2},30)}}}).bind("keyup",function(e){if(isNaN(u)){if(a){window.clearTimeout(a);a=null;f=1;t.val(t.$.val())}}else{t.$.val()>t.o.max&&t.$.val(t.o.max)||t.$.val()this.o.max){this.v=this.o.min}this.$.val(this.v);this.w2=this.w/2;this.cursorExt=this.o.cursor/100;this.xy=this.w2*this.scale;this.lineWidth=this.xy*this.o.thickness;this.lineCap=this.o.lineCap;this.radius=this.xy-this.lineWidth/2;this.o.angleOffset&&(this.o.angleOffset=isNaN(this.o.angleOffset)?0:this.o.angleOffset);this.o.angleArc&&(this.o.angleArc=isNaN(this.o.angleArc)?this.PI2:this.o.angleArc);this.angleOffset=this.o.angleOffset*Math.PI/180;this.angleArc=this.o.angleArc*Math.PI/180;this.startAngle=1.5*Math.PI+this.angleOffset;this.endAngle=1.5*Math.PI+this.angleOffset+this.angleArc;var e=n(String(Math.abs(this.o.max)).length,String(Math.abs(this.o.min)).length,2)+2;this.o.displayInput&&this.i.css({width:(this.w/2+4>>0)+"px",height:(this.w/3>>0)+"px",position:"absolute","vertical-align":"middle","margin-top":(this.w/3>>0)+"px","margin-left":"-"+(this.w*3/4+2>>0)+"px",border:0,background:"none",font:this.o.fontWeight+" "+(this.w/e>>0)+"px "+this.o.font,"text-align":"center",color:this.o.inputColor||this.o.fgColor,padding:"0px","-webkit-appearance":"none"})||this.i.css({width:"0px",visibility:"hidden"})};this.change=function(e){this.cv=e;this.$.val(this.o.format(e))};this.angle=function(e){return(e-this.o.min)*this.angleArc/(this.o.max-this.o.min)};this.arc=function(e){var t,n;e=this.angle(e);if(this.o.flip){t=this.endAngle+1e-5;n=t-e-1e-5}else{t=this.startAngle-1e-5;n=t+e+1e-5}this.o.cursor&&(t=n-this.cursorExt)&&(n=n+this.cursorExt);return{s:t,e:n,d:this.o.flip&&!this.o.cursor}};this.draw=function(){var e=this.g,t=this.arc(this.cv),n,r=1;e.lineWidth=this.lineWidth;e.lineCap=this.lineCap;if(this.o.bgColor!=="none"){e.beginPath();e.strokeStyle=this.o.bgColor;e.arc(this.xy,this.xy,this.radius,this.endAngle-1e-5,this.startAngle+1e-5,true);e.stroke()}if(this.o.displayPrevious){n=this.arc(this.v);e.beginPath();e.strokeStyle=this.pColor;e.arc(this.xy,this.xy,this.radius,n.s,n.e,n.d);e.stroke();r=this.cv==this.v}e.beginPath();e.strokeStyle=r?this.o.fgColor:this.fgColor;e.arc(this.xy,this.xy,this.radius,t.s,t.e,t.d);e.stroke()};this.cancel=function(){this.val(this.v)}};e.fn.dial=e.fn.knob=function(n){return this.each(function(){var r=new t.Dial;r.o=n;r.$=e(this);r.run()}).parent()}}) -------------------------------------------------------------------------------- /vl1/ui/scripts/buttons.js: -------------------------------------------------------------------------------- 1 | var waveform = 0 2 | var attack = 0 3 | var decay = 1 4 | var sustain = 2 5 | var release = 1 6 | var vibrato = 0 7 | var tremelo = 0 8 | var tempo = 0 9 | var octave = 1 10 | 11 | var params = [578,608,668,698,738,768,808,838,878,908,968,998,1048,1078,1128,1158]; 12 | var notes = ['G1', 'A1', 'B1', 'C2', 'D2', 'E2', 'F2', 'G2', 'A2', 'B2', 'C3', 'D3', 'E3', 'F3', 'G3', 'A3', 'B3']; 13 | 14 | var midiG1 = 31 15 | 16 | currentNote = 0 17 | isWhite = true 18 | noteOnRecieved = false 19 | 20 | toMidi = function(note) { 21 | return note + midiG1 + (octave * 12) 22 | } 23 | 24 | buttonPresses = function(e, client) { 25 | for (var x = 0; x < notes.length; x++) { 26 | if (e.clientX >= 333 + (x*57) && e.clientY >= 263 && e.clientX <= 363 + (x*57) && e.clientY <= 323) { 27 | //console.log(notes[x] + " on"); 28 | parent.sendMsg(6, 0, 0, [toMidi(x), 127, 0]) 29 | currentNote = x 30 | isWhite = true 31 | noteOnRecieved = true 32 | client.renderer.animateKeys(x, true) 33 | } 34 | if (e.clientX >= 364 + (x*57) && e.clientY >= 178 && e.clientX <= 389 + (x*57) && e.clientY <= 248) { 35 | if (notes[x].includes("B") == false && notes[x].includes("E") == false) { 36 | //console.log(notes[x]+ "# on") 37 | parent.sendMsg(6, 0, 0, [toMidi(x), 127, 0]) 38 | currentNote = x 39 | isWhite = false 40 | noteOnRecieved = true 41 | client.renderer.animateKeys(x, false) 42 | } 43 | } 44 | } 45 | if (e.clientX >= params[0] && e.clientY >= 108 && e.clientX <= params[0] + 20 && e.clientY <= 149) { 46 | if (waveform > 0) { 47 | waveform = waveform - 1 48 | //console.log("- Waveform") 49 | client.renderer.setParams(0, waveform) 50 | } 51 | noteOnRecieved = false 52 | } 53 | if (e.clientX >= params[1] && e.clientY >= 108 && e.clientX <= params[1] + 20 && e.clientY <= 149) { 54 | if (waveform < 9) { 55 | waveform = waveform + 1 56 | //console.log("+ Waveform") 57 | client.renderer.setParams(0, waveform) 58 | } 59 | noteOnRecieved = false 60 | } 61 | if (e.clientX >= params[2] && e.clientY >= 108 && e.clientX <= params[2] + 20 && e.clientY <= 149) { 62 | if (attack > 0) { 63 | attack = attack - 1 64 | //console.log("- Attack") 65 | client.renderer.setParams(1, attack) 66 | } 67 | noteOnRecieved = false 68 | } 69 | if (e.clientX >= params[3] && e.clientY >= 108 && e.clientX <= params[3] + 20 && e.clientY <= 149) { 70 | if (attack < 9) { 71 | attack = attack + 1 72 | //console.log("+ Attack") 73 | client.renderer.setParams(1, attack) 74 | } 75 | noteOnRecieved = false 76 | } 77 | if (e.clientX >= params[4] && e.clientY >= 108 && e.clientX <= params[4] + 20 && e.clientY <= 149) { 78 | if (decay > 0) { 79 | decay = decay - 1 80 | //console.log("- Decay") 81 | client.renderer.setParams(2, decay) 82 | } 83 | noteOnRecieved = false 84 | } 85 | if (e.clientX >= params[5] && e.clientY >= 108 && e.clientX <= params[5] + 20 && e.clientY <= 149) { 86 | if (decay < 9) { 87 | decay = decay + 1 88 | //console.log("+ Decay") 89 | client.renderer.setParams(2, decay) 90 | } 91 | noteOnRecieved = false 92 | } 93 | if (e.clientX >= params[6] && e.clientY >= 108 && e.clientX <= params[6] + 20 && e.clientY <= 149) { 94 | if (sustain > 0) { 95 | sustain = sustain - 1 96 | //console.log("- Sustain") 97 | client.renderer.setParams(3, sustain) 98 | } 99 | noteOnRecieved = false 100 | } 101 | if (e.clientX >= params[7] && e.clientY >= 108 && e.clientX <= params[7] + 20 && e.clientY <= 149) { 102 | if (sustain < 9) { 103 | sustain = sustain + 1 104 | //console.log("+ Sustain") 105 | client.renderer.setParams(3, sustain) 106 | } 107 | noteOnRecieved = false 108 | } 109 | if (e.clientX >= params[8] && e.clientY >= 108 && e.clientX <= params[8] + 20 && e.clientY <= 149) { 110 | if (release > 0) { 111 | release = release - 1 112 | //("- Release") 113 | client.renderer.setParams(4, release) 114 | } 115 | noteOnRecieved = false 116 | } 117 | if (e.clientX >= params[9] && e.clientY >= 108 && e.clientX <= params[9] + 20 && e.clientY <= 149) { 118 | if (release < 9) { 119 | release = release + 1 120 | //console.log("+ Release") 121 | client.renderer.setParams(4, release) 122 | } 123 | noteOnRecieved = false 124 | } 125 | if (e.clientX >= params[10] && e.clientY >= 108 && e.clientX <= params[10] + 20 && e.clientY <= 149) { 126 | if (vibrato > 0) { 127 | vibrato = vibrato - 1 128 | //console.log("- Vibrato") 129 | client.renderer.setParams(5, vibrato) 130 | } 131 | noteOnRecieved = false 132 | } 133 | if (e.clientX >= params[11] && e.clientY >= 108 && e.clientX <= params[11] + 20 && e.clientY <= 149) { 134 | if (vibrato < 9) { 135 | vibrato = vibrato + 1 136 | //console.log("+ Vibrato") 137 | client.renderer.setParams(5, vibrato) 138 | } 139 | noteOnRecieved = false 140 | } 141 | if (e.clientX >= params[12] && e.clientY >= 108 && e.clientX <= params[12] + 20 && e.clientY <= 149) { 142 | if (tremelo > 0) { 143 | tremelo = tremelo - 1 144 | //console.log("- Tremelo") 145 | client.renderer.setParams(6, tremelo) 146 | } 147 | noteOnRecieved = false 148 | } 149 | if (e.clientX >= params[13] && e.clientY >= 108 && e.clientX <= params[13] + 20 && e.clientY <= 149) { 150 | if (tremelo < 9) { 151 | tremelo = tremelo + 1 152 | //console.log("+ Tremelo") 153 | client.renderer.setParams(6, tremelo) 154 | } 155 | noteOnRecieved = false 156 | } 157 | if (e.clientX >= params[14] && e.clientY >= 108 && e.clientX <= params[14] + 20 && e.clientY <= 149) { 158 | if (tempo > 0) { 159 | tempo = tempo - 1 160 | //console.log("- Tempo") 161 | client.renderer.setParams(7, tempo) 162 | } 163 | noteOnRecieved = false 164 | } 165 | if (e.clientX >= params[15] && e.clientY >= 108 && e.clientX <= params[15] + 20 && e.clientY <= 149) { 166 | if (tempo < 19) { 167 | tempo = tempo + 1 168 | //console.log("+ Tempo") 169 | client.renderer.setParams(7, tempo) 170 | } 171 | noteOnRecieved = false 172 | } 173 | if (e.clientX >= 593 && e.clientY >= 53 && e.clientX <= 633 && e.clientY <= 73) { 174 | //console.log("Piano button pressed") 175 | waveform = 0 176 | client.renderer.setParams(0, waveform) 177 | attack = 0 178 | client.renderer.setParams(1, attack) 179 | decay = 1 180 | client.renderer.setParams(2, decay) 181 | sustain = 2 182 | client.renderer.setParams(3, sustain) 183 | release = 1 184 | client.renderer.setParams(4, release) 185 | vibrato = 0 186 | client.renderer.setParams(5, vibrato) 187 | tremelo = 0 188 | client.renderer.setParams(6, tremelo) 189 | noteOnRecieved = false 190 | } 191 | if (e.clientX >= 663 && e.clientY >= 53 && e.clientX <= 703 && e.clientY <= 73) { 192 | //console.log("Fantasy button pressed") 193 | waveform = 1 194 | client.renderer.setParams(0, waveform) 195 | attack = 0 196 | client.renderer.setParams(1, attack) 197 | decay = 0 198 | client.renderer.setParams(2, decay) 199 | sustain = 7 200 | client.renderer.setParams(3, sustain) 201 | release = 5 202 | client.renderer.setParams(4, release) 203 | vibrato = 9 204 | client.renderer.setParams(5, vibrato) 205 | tremelo = 0 206 | client.renderer.setParams(6, tremelo) 207 | noteOnRecieved = false 208 | } 209 | if (e.clientX >= 733 && e.clientY >= 53 && e.clientX <= 773 && e.clientY <= 73) { 210 | //console.log("Violin button pressed") 211 | waveform = 2 212 | client.renderer.setParams(0, waveform) 213 | attack = 1 214 | client.renderer.setParams(1, attack) 215 | decay = 0 216 | client.renderer.setParams(2, decay) 217 | sustain = 7 218 | client.renderer.setParams(3, sustain) 219 | release = 1 220 | client.renderer.setParams(4, release) 221 | vibrato = 5 222 | client.renderer.setParams(5, vibrato) 223 | tremelo = 0 224 | client.renderer.setParams(6, tremelo) 225 | noteOnRecieved = false 226 | } 227 | if (e.clientX >= 803 && e.clientY >= 53 && e.clientX <= 843 && e.clientY <= 73) { 228 | //console.log("Flute button pressed") 229 | waveform = 3 230 | client.renderer.setParams(0, waveform) 231 | attack = 1 232 | client.renderer.setParams(1, attack) 233 | decay = 0 234 | client.renderer.setParams(2, decay) 235 | sustain = 7 236 | client.renderer.setParams(3, sustain) 237 | release = 1 238 | client.renderer.setParams(4, release) 239 | vibrato = 3 240 | client.renderer.setParams(5, vibrato) 241 | tremelo = 0 242 | client.renderer.setParams(6, tremelo) 243 | noteOnRecieved = false 244 | } 245 | if (e.clientX >= 873 && e.clientY >= 53 && e.clientX <= 913 && e.clientY <= 73) { 246 | //console.log("Guitar button pressed") 247 | client.renderer.setParams(0, waveform) 248 | attack = 4 249 | client.renderer.setParams(1, attack) 250 | decay = 1 251 | client.renderer.setParams(2, decay) 252 | sustain = 0 253 | client.renderer.setParams(3, sustain) 254 | release = 7 255 | client.renderer.setParams(4, release) 256 | vibrato = 0 257 | client.renderer.setParams(5, vibrato) 258 | tremelo = 0 259 | client.renderer.setParams(6, tremelo) 260 | noteOnRecieved = false 261 | } 262 | if (e.clientX >= 1028 && e.clientY >= 53 && e.clientX <= 1028 + 40 && e.clientY <= 53 + 20) { 263 | if (octave > 0) { 264 | octave = octave - 1 265 | console.log("- Octave") 266 | console.log(octave) 267 | client.renderer.setParams(8, octave) 268 | } 269 | noteOnRecieved = false 270 | } 271 | if (e.clientX >= 1088 && e.clientY >= 53 && e.clientX <= 1088 + 40 && e.clientY <= 53 + 20) { 272 | if (octave < 2) { 273 | octave = octave + 1 274 | //console.log("+ Octave") 275 | client.renderer.setParams(8, octave) 276 | } 277 | noteOnRecieved = false 278 | } 279 | } 280 | 281 | buttonUnpresses = function(client){ 282 | if (noteOnRecieved == true) { 283 | if (isWhite == true) { 284 | //console.log(notes[currentNote] + " off"); 285 | client.renderer.animateKeys(currentNote, true) 286 | } 287 | else { 288 | //console.log(notes[currentNote] + "# off"); 289 | client.renderer.animateKeys(currentNote, false) 290 | } 291 | parent.sendMsg(7, 0, 0, [toMidi(currentNote), 127, 0]) 292 | } 293 | noteOnRecieved == false 294 | } 295 | -------------------------------------------------------------------------------- /four_track/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(wasm_target_feature)] 2 | 3 | extern crate wee_alloc; 4 | 5 | // Use `wee_alloc` as the global allocator. 6 | #[global_allocator] 7 | static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; 8 | 9 | extern "C" { 10 | fn print(i: i32); 11 | } 12 | 13 | mod playhead; 14 | use playhead::*; 15 | 16 | const NUM_TRACKS: usize = 4; 17 | const MAX_BUFFER_SIZE: usize = 1024; 18 | 19 | #[no_mangle] 20 | static mut IN_BUFFER0: [f32;MAX_BUFFER_SIZE] = [0.;MAX_BUFFER_SIZE]; 21 | #[no_mangle] 22 | static mut IN_BUFFER1: [f32;MAX_BUFFER_SIZE] = [0.;MAX_BUFFER_SIZE]; 23 | #[no_mangle] 24 | static mut OUT_BUFFER0: [f32;MAX_BUFFER_SIZE] = [0.;MAX_BUFFER_SIZE]; 25 | #[no_mangle] 26 | static mut OUT_BUFFER1: [f32;MAX_BUFFER_SIZE] = [0.;MAX_BUFFER_SIZE]; 27 | static mut INPUTS: [* const f32;2] = [0 as * const f32; 2]; 28 | static mut OUTPUTS: [* mut f32;2] = [0 as * mut f32; 2]; 29 | 30 | static mut ENGINE : FourTrack = FourTrack { 31 | sample_rate: 0, 32 | active_track: 0, 33 | recording: false, 34 | playing: false, 35 | mutes: [1.0;NUM_TRACKS], 36 | playheads: Vec::new(), 37 | param_history: (-1, -1.), 38 | }; 39 | 40 | // Notes and voices 41 | 42 | type Note = i32; 43 | type Pitch = f32; 44 | 45 | struct FourTrack { 46 | sample_rate: i32, 47 | active_track: usize, 48 | recording: bool, 49 | playing: bool, 50 | mutes: [f32;NUM_TRACKS], 51 | playheads: Vec, 52 | 53 | /// record last param set 54 | param_history: (i32, f32), 55 | } 56 | 57 | impl FourTrack { 58 | pub fn get_voices(&self) -> i32 { 59 | 0 60 | } 61 | 62 | pub fn get_input(&self, index: u32) -> u32 { 63 | unsafe { INPUTS[index as usize] as u32 } 64 | } 65 | 66 | pub fn get_output(&self, index: u32) -> u32 { 67 | unsafe { OUTPUTS[index as usize] as u32 } 68 | } 69 | 70 | pub fn set_input(&self, index: u32, offset: u32) { 71 | unsafe { INPUTS[index as usize] = offset as * const f32; }; 72 | } 73 | 74 | pub fn set_output(&self, index: u32, offset: u32) { 75 | unsafe { OUTPUTS[index as usize] = offset as * mut f32; }; 76 | } 77 | 78 | pub fn get_sample_rate(&self) -> i32 { 79 | return self.sample_rate; 80 | } 81 | 82 | pub fn get_num_inputs(&self) -> i32 { 83 | return 2; 84 | } 85 | 86 | pub fn get_num_outputs(&self) -> i32 { 87 | return 2; 88 | } 89 | 90 | pub fn init(&mut self, sample_rate: i32) { 91 | self.sample_rate = sample_rate; 92 | self.playheads = vec![ 93 | Playhead::new(sample_rate as usize, Playhead::ONE_MINUTE_SECONDS), 94 | Playhead::new(sample_rate as usize, Playhead::ONE_MINUTE_SECONDS), 95 | Playhead::new(sample_rate as usize, Playhead::ONE_MINUTE_SECONDS), 96 | Playhead::new(sample_rate as usize, Playhead::ONE_MINUTE_SECONDS)]; 97 | self.init_buffers(); 98 | } 99 | 100 | pub fn handle_note_on(&mut self, _mn: Note, _vel: f32) { 101 | // no required here 102 | // really we want to move some of these functions to being optional 103 | } 104 | 105 | pub fn handle_note_off(&mut self, _mn: Note, _vel: f32) { 106 | } 107 | 108 | fn init_buffers(&self) { 109 | unsafe { 110 | INPUTS[0] = IN_BUFFER0.as_ptr(); 111 | INPUTS[1] = IN_BUFFER1.as_ptr(); 112 | OUTPUTS[0] = OUT_BUFFER0.as_mut_ptr(); 113 | OUTPUTS[1] = OUT_BUFFER1.as_mut_ptr(); 114 | }; 115 | } 116 | 117 | pub fn get_param(&self, param: u32) -> f32 { 118 | match param { 119 | 0 => self.active_track as f32, 120 | 1 => self.recording as i32 as f32, 121 | // mute track 1 122 | 2 => self.mutes[0] as f32, 123 | // mute track 2 124 | 3 => self.mutes[1] as f32, 125 | // mute track 3 126 | 4 => self.mutes[2] as f32, 127 | // mute track 4 128 | 5 => self.mutes[3] as f32, 129 | 6 => self.playing as i32 as f32, 130 | _ => 0., 131 | } 132 | } 133 | 134 | pub fn set_param(&mut self, param: u32, value: f32) { 135 | // unsafe { 136 | // print(self.param_history.0 as i32); 137 | // print(self.param_history.1 as i32); 138 | // } 139 | match param { 140 | 0 => { 141 | let v = value as usize; 142 | if v <= NUM_TRACKS { 143 | self.active_track = v; 144 | } 145 | }, 146 | // set recording 147 | 1 => { 148 | self.recording = value != 0.0; 149 | }, 150 | // mute track 151 | 2 | 3 | 4 | 5 => { 152 | self.mutes[(param-2) as usize] = if value == 0.0 { 1.0 } else { 0.0 }; 153 | }, 154 | // set playing 155 | 6 => { 156 | self.playing = value != 0.; 157 | }, 158 | // set loop in 159 | 7 => { 160 | for p in &mut self.playheads { 161 | p.loop_in(value as usize); 162 | } 163 | }, 164 | // set loop out 165 | 8 => { 166 | for p in &mut self.playheads { 167 | p.loop_out(value as usize); 168 | } 169 | }, 170 | // loop 171 | 9 => { 172 | for p in &mut self.playheads { 173 | p.looping(value != 0.0); 174 | } 175 | }, 176 | // stop 177 | 10 => { 178 | self.playing = false; 179 | self.recording = false; 180 | // if stop is sent twice in a row, then reset playheads 181 | if self.param_history.0 == 10 && self.param_history.1 == 0. { 182 | for p in &mut self.playheads { 183 | p.gate(); 184 | } 185 | } 186 | }, 187 | _ => {} 188 | } 189 | self.param_history = (param as i32, value); 190 | } 191 | 192 | #[target_feature(enable = "simd128")] 193 | unsafe fn compute(&mut self, count: i32, inputs: &[&[f32];2], outputs: &mut [&mut [f32];2]) { 194 | let [inputs0, inputs1] = inputs; //inputs[0][..count as usize].iter(); 195 | let (inputs0, inputs1) = { 196 | let inputs0 = inputs0[..count as usize].iter(); 197 | let inputs1 = inputs1[..count as usize].iter(); 198 | (inputs0, inputs1) 199 | }; 200 | let [outputs0, outputs1] = outputs; 201 | let (outputs0, outputs1) = { 202 | let outputs0 = outputs0[..count as usize].iter_mut(); 203 | let outputs1 = outputs1[..count as usize].iter_mut(); 204 | (outputs0, outputs1) 205 | }; 206 | 207 | // TODO :-) 208 | if self.playing { 209 | let zipped_inputs = inputs0.zip(inputs1).zip(outputs0).zip(outputs1); 210 | for (((input0, input1), output0), output1) in zipped_inputs { 211 | *output0 = 0.; 212 | *output1 = 0.; 213 | let playhead_mute = self.playheads.iter_mut().zip(self.mutes.iter()); 214 | for (i, ( p, mute)) in playhead_mute.enumerate() { 215 | let (sample0, sample1) = 216 | // overdub if recording and the track is active 217 | if self.recording && self.active_track == i { 218 | p.overdub(*input0, *input1) 219 | } 220 | else { 221 | p.read() 222 | }; 223 | *output0 += sample0 * mute; 224 | *output1 += sample1 * mute; 225 | } 226 | } 227 | // if self.recording { 228 | // // recoding input onto active track 229 | // let zipped_inputs = inputs0.zip(inputs1).zip(outputs0).zip(outputs1); 230 | // for (((input0, input1), output0), output1) in zipped_inputs { 231 | // *output0 = 0.; 232 | // *output1 = 0.; 233 | // let playhead_mute = self.playheads.iter_mut().zip(self.mutes.iter()); 234 | // for (p, mute) in playhead_mute { 235 | // let (sample0, sample1) = p.overdub(*input0, *input1); 236 | // *output0 += sample0 * mute; 237 | // *output1 += sample1 * mute; 238 | // } 239 | // } 240 | // } 241 | // else { 242 | // // only playing so add in output from non muted tracks to input 243 | // let zipped_inputs = inputs0.zip(inputs1).zip(outputs0).zip(outputs1); 244 | // for (((input0, input1), output0), output1) in zipped_inputs { 245 | // *output0 = 0.; 246 | // *output1 = 0.; 247 | // let playhead_mute = self.playheads.iter_mut().zip(self.mutes.iter()); 248 | // for (p, mute) in playhead_mute { 249 | // let (sample0, sample1) = p.read(); 250 | // *output0 = (sample0 + *input0) * mute; 251 | // *output1 = (sample1 + *input1) * mute; 252 | // } 253 | // } 254 | 255 | // // let zipped_iterators = outputs0.zip(outputs1); 256 | // // for (output0, output1) in zipped_iterators { 257 | // // *output0 = 0.; 258 | // // *output1 = 0.; 259 | // // for p in &mut self.playheads { 260 | // // let (sample0, sample1) = p.read(); 261 | // // *output0 = sample0; 262 | // // *output1 = sample1; 263 | // // } 264 | // // } 265 | // } 266 | } 267 | else { 268 | // not playing so pass input through 269 | let zipped_inputs = inputs0.zip(inputs1).zip(outputs0).zip(outputs1); 270 | for (((input0, input1), output0), output1) in zipped_inputs { 271 | *output0 = *input0; 272 | *output1 = *input1; 273 | } 274 | } 275 | } 276 | 277 | #[inline] 278 | pub fn compute_external(&mut self, count: i32) { 279 | let (input0, input1, output0, output1) = unsafe { 280 | (::std::slice::from_raw_parts(INPUTS[0], count as usize), 281 | ::std::slice::from_raw_parts(INPUTS[1], count as usize), 282 | ::std::slice::from_raw_parts_mut(OUTPUTS[0], count as usize), 283 | ::std::slice::from_raw_parts_mut(OUTPUTS[1], count as usize)) 284 | }; 285 | unsafe { self.compute(count, &[input0, input1], &mut [output0, output1]); } 286 | } 287 | } 288 | 289 | #[no_mangle] 290 | pub fn get_input(index: u32) -> u32 { 291 | unsafe { ENGINE.get_input(index) } 292 | } 293 | 294 | #[no_mangle] 295 | pub fn get_output(index: u32) -> u32 { 296 | unsafe { ENGINE.get_output(index) } 297 | } 298 | 299 | #[no_mangle] 300 | pub fn set_input(index: u32, offset: u32) { 301 | unsafe { ENGINE.set_input(index, offset); }; 302 | } 303 | 304 | #[no_mangle] 305 | pub fn set_output(index: u32, offset: u32) { 306 | unsafe { ENGINE.set_output(index, offset); }; 307 | } 308 | 309 | #[no_mangle] 310 | pub fn handle_note_on(mn: i32, vel: f32) { 311 | unsafe { ENGINE.handle_note_on(mn, vel); } 312 | } 313 | 314 | #[no_mangle] 315 | pub fn handle_note_off(mn: i32, vel: f32) { 316 | unsafe { ENGINE.handle_note_off(mn, vel); } 317 | } 318 | 319 | #[no_mangle] 320 | pub fn get_voices() -> i32 { 321 | unsafe { ENGINE.get_voices() } 322 | } 323 | 324 | #[no_mangle] 325 | pub fn get_param_index(length: i32) -> i32 { 326 | 0 327 | } 328 | 329 | #[no_mangle] 330 | pub fn get_sample_rate() -> f64 { 331 | unsafe { ENGINE.get_sample_rate() as f64 } 332 | } 333 | 334 | // number of input channels (currently max 2) 335 | #[no_mangle] 336 | pub fn get_num_input_channels() -> u32 { 337 | unsafe { ENGINE.get_num_inputs() as u32 } 338 | } 339 | 340 | // number of output channels (currently max 2) 341 | #[no_mangle] 342 | pub fn get_num_output_channels() -> u32 { 343 | unsafe { ENGINE.get_num_outputs() as u32 } 344 | } 345 | 346 | #[no_mangle] 347 | pub fn init(sample_rate: f64) -> () { 348 | unsafe { ENGINE.init(sample_rate as i32); } 349 | } 350 | 351 | #[no_mangle] 352 | pub fn set_param_float(index: u32, v: f32) { 353 | unsafe { ENGINE.set_param(index, v); } 354 | } 355 | 356 | #[no_mangle] 357 | pub fn set_param_int(index: u32, v: i32) { 358 | unsafe { ENGINE.set_param(index, v as f32); } 359 | } 360 | 361 | #[no_mangle] 362 | pub fn get_param_float(index: u32) -> f32 { 363 | unsafe { ENGINE.get_param(index) } 364 | } 365 | 366 | #[no_mangle] 367 | pub fn get_param_int(index: u32) -> i32 { 368 | unsafe { ENGINE.get_param(index) as i32 } 369 | } 370 | 371 | #[no_mangle] 372 | pub fn compute(frames: u32) -> () { 373 | unsafe { ENGINE.compute_external(frames as i32); } 374 | } -------------------------------------------------------------------------------- /root/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 103 | 104 | 105 | 106 | 107 | 108 | 122 | 123 | 124 | 125 |
126 |
127 |
128 |
129 |
130 | 131 |
132 | 138 | 139 | 145 | 146 | 152 |
153 | 154 | 193 | 194 | 344 | 345 | -------------------------------------------------------------------------------- /vl1/ui/scripts/renderer.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | */ 4 | 'use strict' 5 | 6 | this.hold = false 7 | this.currentKey = 0 8 | 9 | 10 | function Renderer (client) { 11 | this.el = document.createElement('canvas') 12 | this.el.id = 'guide' 13 | // set required size of canvas here 14 | this.el.width = 640 15 | this.el.height = 640 16 | this.el.style.width = '320px' 17 | this.el.style.height = '320px' 18 | this.context = this.el.getContext('2d') 19 | this.showExtras = true 20 | 21 | 22 | this.scale = 1 //window.devicePixelRatio 23 | 24 | // params 25 | this.param1 = 0.05 26 | 27 | // handle incoming control message, set values of params, etc. 28 | this.controlParam1 = (v) => { 29 | let mv = map(v, 0, 127, 0.08, 4.0) 30 | this.param1 = mv 31 | return mv 32 | } 33 | 34 | // constants 35 | const offwhite = "#fffddd" 36 | const red = "#DD4A22" 37 | const yellow = "#F3B83C" 38 | const blue = "#6060A6" 39 | const rose = "#FFBDB0" 40 | const rootbeer = "#1f0c07" 41 | const black = "#000000" 42 | const shadow = "#a2a2a2" 43 | const shadower = "#626262" 44 | const white = "#ffffff" 45 | const display = "#5c5c3d" 46 | const moreoff = "#ffffcc" 47 | const redd = "#ff0000" 48 | const orange = "#ff8600" 49 | const blue2 = "#0020ff" 50 | 51 | // functions 52 | this.start = function () { 53 | this.update() 54 | } 55 | 56 | this.update = function (force = false) { 57 | this.resize() 58 | this.draw() 59 | } 60 | 61 | this.draw = function() { 62 | this.clear() 63 | this.drawKey() 64 | } 65 | 66 | this.clear = function () { 67 | this.context.clearRect(0, 0, this.el.width * this.scale, this.el.height * this.scale); 68 | this.context.rect(0, 0, this.el.width * this.scale, this.el.height * this.scale); 69 | this.context.fillStyle = rootbeer; 70 | this.context.fill(); 71 | } 72 | 73 | this.resize = function () { 74 | const _target = client.getPaddedSize() 75 | const _current = { width: this.el.width / this.scale, height: this.el.height / this.scale } 76 | const offset = sizeOffset(_target, _current) 77 | if (offset.width === 0 && offset.height === 0) { 78 | return 79 | } 80 | console.log('Renderer', `Require resize: ${printSize(_target)}, from ${printSize(_current)}`) 81 | this.el.width = (_target.width) * this.scale 82 | this.el.height = (_target.height) * this.scale 83 | this.el.style.width = (_target.width) + 'px' 84 | this.el.style.height = (_target.height) + 'px' 85 | } 86 | 87 | this.aButton = function(x, y, colour) { 88 | this.context.beginPath(); 89 | this.context.fillStyle = colour; 90 | this.context.fillRect(x, y, 20, 40); 91 | this.context.lineStyle = shadow; 92 | this.context.lineWidth = 2; 93 | this.context.moveTo(x+1, y+40); 94 | this.context.lineTo(x+20, y+40); 95 | this.context.lineTo(x+20, y+1); 96 | this.context.stroke(); 97 | } 98 | 99 | // Animate keys 100 | var isWhite = true 101 | var animatedColour = white 102 | this.animateKeys = function(x, aWhite){ 103 | this.currentKey = x 104 | if (aWhite == true) { 105 | isWhite = true 106 | if (this.hold == true) { 107 | animatedColour = shadow 108 | } 109 | if (this.hold == false) { 110 | animatedColour = white 111 | } 112 | } 113 | if (aWhite == false) { 114 | isWhite = false 115 | if (this.hold == true) { 116 | animatedColour = shadow 117 | } 118 | if (this.hold == false) { 119 | animatedColour = black 120 | } 121 | } 122 | } 123 | 124 | 125 | // Set params 126 | this.waveform = 0 127 | this.attack = 0 128 | this.decay = 1 129 | this.sustain = 2 130 | this.release = 1 131 | this.vibrato = 0 132 | this.tremelo = 0 133 | this.tempo = 0 134 | this.octave = 1 135 | 136 | this.setParams = function(param, value) { 137 | if (param == 0) { 138 | this.waveform = value 139 | sendWaveform(value) 140 | } 141 | if (param == 1) { 142 | this.attack = value 143 | sendAttack(value) 144 | } 145 | if (param == 2) { 146 | this.decay = value 147 | sendDecay(value) 148 | } 149 | if (param == 3) { 150 | this.sustain = value 151 | sendSustain(value) 152 | } 153 | if (param == 4) { 154 | this.release = value 155 | sendRelease(value) 156 | } 157 | if (param == 5) { 158 | this.vibrato = value 159 | sendVibrato(value) 160 | } 161 | if (param == 6) { 162 | this.tremelo = value 163 | sendTremelo(value) 164 | } 165 | if (param == 7) { 166 | this.tempo = value 167 | } 168 | if (param == 8) { 169 | this.octave = octave 170 | } 171 | } 172 | 173 | 174 | // ------------------ 175 | 176 | this.drawKey = function() { 177 | var style = { color: offwhite, thickness: 6.0, strokeLinecap: "round", strokeLinejoin: "round"} 178 | this.setStyle(style) 179 | 180 | // outline 181 | this.context.strokeStyle = offwhite; 182 | this.context.beginPath(); 183 | this.context.moveTo(50, 20); 184 | this.context.lineTo(1270, 20); 185 | this.context.bezierCurveTo(1270, 20, 1300, 30, 1300, 60); 186 | this.context.lineTo(1300, 300); 187 | this.context.bezierCurveTo(1300, 300, 1300, 340, 1280, 340); 188 | this.context.lineTo(60, 340); 189 | this.context.bezierCurveTo(60, 340, 40, 335, 30, 300); 190 | this.context.lineTo(30, 60); 191 | this.context.bezierCurveTo(30, 60, 30, 20, 50, 20); 192 | this.context.closePath(); 193 | this.context.fillStyle = moreoff; 194 | this.context.fill(); 195 | this.context.stroke(); 196 | 197 | // speaker 198 | this.context.beginPath(); 199 | this.context.strokeStyle = shadow; 200 | this.context.lineWidth = 3; 201 | this.context.moveTo(55, 45); 202 | for (var x = 0; x < 28; x++) { 203 | this.context.lineTo(255, (x*10)+45); 204 | this.context. moveTo(55, ((x+1)*10)+45); 205 | } 206 | this.context.stroke(); 207 | this.context.beginPath(); 208 | this.context.strokeStyle = black; 209 | this.context.lineWidth = 3; 210 | this.context.strokeRect(55, 35, 200, 290); 211 | 212 | this.context.beginPath(); 213 | this.context.strokeStyle = black; 214 | this.context.lineWidth =2; 215 | this.context.moveTo(55, 43); 216 | for (var x = 0; x < 28; x++) { 217 | this.context.lineTo(255, (x*10)+43); 218 | this.context. moveTo(55, ((x+1)*10)+43); 219 | } 220 | this.context.stroke(); 221 | 222 | //Screen 223 | this.context.fillStyle = black; 224 | this.context.fillRect(285, 16, 270, 140); 225 | this.context.stroke(); 226 | this.context.fillStyle = display; 227 | this.context.fillRect(310, 60, 220, 60); 228 | this.context.stroke(); 229 | 230 | this.context.beginPath(); 231 | this.context.strokeStyle = black; 232 | this.context.lineWidth = 1; 233 | this.context.moveTo(310, 62); 234 | this.context.lineTo(530,62); 235 | this.context.stroke(); 236 | this.context.beginPath(); 237 | this.context.strokeStyle = white; 238 | this.context.lineWidth = 2; 239 | this.context.moveTo(285, 20); 240 | this.context.lineTo(555,20); 241 | this.context.moveTo(285, 24); 242 | this.context.lineTo(555,24); 243 | this.context.stroke(); 244 | this.context.beginPath(); 245 | this.context.strokeStyle = shadow; 246 | this.context.lineWidth = 4; 247 | this.context.moveTo(286, 30); 248 | this.context.lineTo(554,30); 249 | this.context.stroke(); 250 | this.context.beginPath() 251 | this.context.lineWidth = 2; 252 | this.context.moveTo(286, 150); 253 | this.context.lineTo(554, 150); 254 | this.context.stroke(); 255 | 256 | this.context.font = "bold 14px Arial" 257 | this.context.fillStyle = shadow; 258 | this.context.fillText("CASIO", 370, 50); 259 | this.context.stroke(); 260 | this.context.font = "11px Arial" 261 | this.context.fillStyle = shadow; 262 | this.context.fillText("VL-TONE", 420, 50); 263 | this.context.stroke(); 264 | this.context.font = "8px Arial" 265 | this.context.fillStyle = shadow; 266 | this.context.fillText("ELECTRONIC MUSICAL INSTRUMENT VL-1", 335, 140); 267 | this.context.stroke(); 268 | 269 | 270 | // key bed 271 | this.context.beginPath(); 272 | this.context.strokeStyle = shadow; 273 | this.context.lineWidth =3; 274 | this.context.moveTo(285, 173); 275 | this.context.lineTo(1304, 173); 276 | this.context.moveTo(288, 173); 277 | this.context.lineTo(288, 343); 278 | this.context.stroke(); 279 | this.context.lineWidth = 2; 280 | this.context.moveTo(307, 173); 281 | this.context.lineTo(307, 343); 282 | this.context.moveTo(1285, 173); 283 | this.context.lineTo(1285, 341); 284 | this.context.stroke(); 285 | this.context.beginPath(); 286 | this.context.strokeStyle = black; 287 | this.context.lineWidth =3; 288 | this.context.moveTo(285, 345); 289 | this.context.lineTo(285, 170); 290 | this.context.lineTo(1305, 170); 291 | this.context.stroke(); 292 | 293 | // keys 294 | this.context.beginPath(); 295 | this.context.fillStyle = white; 296 | var notes = ['G1', 'A1', 'B1', 'C2', 'D2', 'E2', 'F2', 'G2', 'A2', 'B2', 'C3', 'D3', 'E3', 'F3', 'G3', 'A3', 'B3']; 297 | for (var x = 0; x < notes.length; x++) { 298 | this.context.fillStyle = white; 299 | this.context.fillRect(325 + (x*57), 255, 30, 60); 300 | if (notes[x].includes("G") || notes[x].includes("A") || notes[x].includes("C") || notes[x].includes("D") || notes[x].includes("F")) { 301 | this.context.fillStyle = black; 302 | this.context.fillRect(356 + (x*57), 170, 25, 70); 303 | } 304 | if (x == this.currentKey && isWhite == true){ 305 | this.context.fillStyle = animatedColour; 306 | this.context.fillRect(325 + (x*57), 255, 30, 60); 307 | } 308 | if (x == this.currentKey && isWhite == false){ 309 | this.context.fillStyle = animatedColour; 310 | this.context.fillRect(356 + (x*57), 170, 25, 70); 311 | } 312 | } 313 | for (var x = 0; x < notes.length; x++) { 314 | this.context.strokeStyle = shadow; 315 | this.context.lineWidth = 2; 316 | var aPoint = 325 + (x*57); 317 | this.context.moveTo(aPoint, 315); 318 | this.context.lineTo(aPoint + 30, 315); 319 | this.context.lineTo(aPoint + 30, 255); 320 | if (notes[x].includes("G") || notes[x].includes("A") || notes[x].includes("C") || notes[x].includes("D") || notes[x].includes("F")) { 321 | this.context.moveTo((357 + (x*57)), 241); 322 | this.context.lineTo((357 + (x*57)) + 25, 241); 323 | this.context.lineTo((357 + (x*57)) + 25, 172); 324 | } 325 | this.context.stroke(); 326 | } 327 | 328 | // Interface 329 | var buttonXs = [[570,redd],[600,redd],[660,white],[690,white],[730,white],[760,white],[800,white],[830,white],[870,white],[900,white],[960,orange],[990,orange],[1040,blue2],[1070,blue2],[1120,rose],[1150,rose]]; 330 | for (var x = 0; x < buttonXs.length; x++) { 331 | this.context.strokeStyle = shadow 332 | this.context.lineWidth = 2 333 | this.aButton(buttonXs[x][0], 100, buttonXs[x][1]); 334 | this.context.beginPath(); 335 | this.context.strokeStyle = black 336 | this.context.lineWidth = 1 337 | this.context.moveTo(buttonXs[x][0] + 5, 120); 338 | this.context.lineTo(buttonXs[x][0] + 15, 120); 339 | if (x % 2 != 0) { 340 | this.context.moveTo(buttonXs[x][0] + 10, 115); 341 | this.context.lineTo(buttonXs[x][0] + 10, 125); 342 | } 343 | this.context.stroke(); 344 | } 345 | 346 | this.context.beginPath(); 347 | this.context.fillStyle = shadow; 348 | this.context.strokeStyle = shadower 349 | this.context.lineWidth = 2; 350 | for (var x = 0; x <5; x++) { 351 | this.context.fillRect(585 + 70*x, 45, 40, 20); 352 | this.context.moveTo(586+70*x, 45+20); 353 | this.context.lineTo(625+70*x, 45+20); 354 | this.context.lineTo(625+70*x, 45+1); 355 | this.context.stroke(); 356 | } 357 | 358 | this.context.beginPath() 359 | this.context.fillRect(1020, 45, 40, 20); 360 | this.context.moveTo(1020, 45+20); 361 | this.context.lineTo(1060, 45+20); 362 | this.context.lineTo(1060, 45+1); 363 | this.context.fillRect(1080, 45, 40, 20); 364 | this.context.moveTo(1080, 45+20); 365 | this.context.lineTo(1120, 45+20); 366 | this.context.lineTo(1120, 45+1); 367 | this.context.stroke(); 368 | this.context.beginPath(); 369 | this.context.moveTo(1020 + 15, 55); 370 | this.context.lineTo(1020 + 25, 55); 371 | this.context.moveTo(1085 + 15, 50); 372 | this.context.lineTo(1085 + 15, 60); 373 | this.context.moveTo(1080 + 15, 55); 374 | this.context.lineTo(1080 + 25, 55); 375 | 376 | 377 | // Interface text 378 | this.context.font = "11px Arial" 379 | this.context.fillStyle = black; 380 | this.context.fillText("Waveform", buttonXs[0][0], 157); 381 | this.context.fillText("Attack", buttonXs[2][0]+12, 157); 382 | this.context.fillText("Decay", buttonXs[4][0]+11, 157); 383 | this.context.fillText("Sustain", buttonXs[6][0]+9, 157); 384 | this.context.fillText("Release", buttonXs[8][0]+8, 157); 385 | this.context.fillText("Vibrato", buttonXs[10][0]+8, 157); 386 | this.context.fillText("Tremelo", buttonXs[12][0]+7, 157); 387 | this.context.fillText("Tempo", buttonXs[14][0]+11, 157); 388 | this.context.fillText("Volume", 1217, 157) 389 | this.context.fillText("Piano", 593, 80); 390 | this.context.fillText("Fantasy", 656, 80); 391 | this.context.fillText("Violin", 732, 80); 392 | this.context.fillText("Flute", 803, 80); 393 | this.context.fillText("Guitar", 871, 80); 394 | this.context.fillText("Octave", 1052, 80); 395 | this.context.stroke(); 396 | 397 | 398 | 399 | // Screen Text 400 | this.context.font = "40px Arial" 401 | this.context.fillStyle = black; 402 | this.context.fillText(this.waveform, 320, 108) 403 | this.context.fillText(this.attack, 350, 108) 404 | this.context.fillText(this.decay, 380, 108) 405 | this.context.fillText(this.sustain, 410, 108) 406 | this.context.fillText(this.release, 440, 108) 407 | this.context.fillText(this.vibrato, 470, 108) 408 | this.context.fillText(this.tremelo, 500, 108) 409 | this.context.stroke(); 410 | 411 | // Volume slider 412 | this.context.beginPath(); 413 | this.context.lineWidth = 7 414 | this.context.strokeStyle = black 415 | this.context.moveTo(1235, 138); 416 | this.context.lineTo(1235, 44); 417 | this.context.stroke(); 418 | 419 | this.context.beginPath() 420 | this.context.fillStyle = shadow 421 | this.context.fillRect(1225, 54, 20, 30) 422 | this.context.lineWidth = 1 423 | this.context.strokeStyle = black 424 | this.context.moveTo(1225, 54 + 15) 425 | this.context.lineTo(1245, 54 + 15) 426 | this.context.moveTo(1225, 54 + 10) 427 | this.context.lineTo(1245, 54 + 10) 428 | this.context.moveTo(1225, 54 + 20) 429 | this.context.lineTo(1245, 54 + 20) 430 | this.context.stroke() 431 | 432 | 433 | } 434 | 435 | 436 | this.setStyle = function(style) { 437 | this.context.strokeStyle = style.color 438 | this.context.lineWidth = style.thickness * this.scale 439 | this.context.lineCap = style.strokeLinecap 440 | this.context.lineJoin = style.strokeLinejoin 441 | } 442 | 443 | this.drawPath = function (path, style) { 444 | const p = new Path2D(path) 445 | 446 | this.context.strokeStyle = style.color 447 | this.context.lineWidth = style.thickness * this.scale 448 | this.context.lineCap = style.strokeLinecap 449 | this.context.lineJoin = style.strokeLinejoin 450 | 451 | if (style.fill && style.fill !== 'none') { 452 | this.context.fillStyle = style.color 453 | this.context.fill(p) 454 | } 455 | 456 | // Dash 457 | this.context.save() 458 | if (style.strokeLineDash) { 459 | this.context.setLineDash(style.strokeLineDash) 460 | } 461 | else { 462 | this.context.setLineDash([]) 463 | } 464 | this.context.stroke(p) 465 | this.context.restore() 466 | } 467 | 468 | // some helper functions 469 | 470 | function printSize (size) { return `${size.width}x${size.height}` } 471 | function sizeOffset (a, b) { return { width: a.width - b.width, height: a.height - b.height } } 472 | function clamp (v, min, max) { return v < min ? min : v > max ? max : v } 473 | function normalize(n, min, max) { return (n - min) / (max - min) } 474 | function vector(x,y) { return new Vector(x,y) } 475 | function point(x,y) { return new Vector(x,y) } // hmm... 476 | function floor(x) { return Math.floor(x) } 477 | function transform_scale(v, s) { return v.mul(s) } 478 | function translate(v,p) { return v.plus(p) } 479 | function rotate(p,a,orgin) { 480 | let s = Math.sin(a) 481 | let c = Math.cos(a) 482 | 483 | // translate to orgin 484 | p = p.minus(orgin) 485 | 486 | // rotate around orgin 487 | p = point(p.x * c - p.y * s, p.x * s + p.y * c) 488 | 489 | // translate back to orignal position 490 | return p.plus(orgin) 491 | } 492 | function radians(d) { return d * (Math.PI/180) } 493 | function map(x, in_min, in_max, out_min, out_max) { 494 | return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min 495 | } 496 | } 497 | --------------------------------------------------------------------------------