├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── README.md ├── patchbay.xml └── src ├── effects ├── autowah.rs ├── delay.rs ├── mod.rs ├── overdrive.rs ├── pedals.rs ├── tremelo.rs └── tuner.rs ├── main.rs ├── notifications.rs └── parser.rs /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /target/ 3 | **/*.rs.bk 4 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "bitflags" 3 | version = "1.0.1" 4 | source = "registry+https://github.com/rust-lang/crates.io-index" 5 | 6 | [[package]] 7 | name = "fuchsia-zircon" 8 | version = "0.3.3" 9 | source = "registry+https://github.com/rust-lang/crates.io-index" 10 | dependencies = [ 11 | "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 12 | "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 13 | ] 14 | 15 | [[package]] 16 | name = "fuchsia-zircon-sys" 17 | version = "0.3.3" 18 | source = "registry+https://github.com/rust-lang/crates.io-index" 19 | 20 | [[package]] 21 | name = "jack" 22 | version = "0.6.0" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | dependencies = [ 25 | "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 26 | "jack-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 27 | "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 28 | "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", 29 | ] 30 | 31 | [[package]] 32 | name = "jack-sys" 33 | version = "0.2.0" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | dependencies = [ 36 | "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 37 | "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", 38 | "libloading 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", 39 | ] 40 | 41 | [[package]] 42 | name = "kernel32-sys" 43 | version = "0.2.2" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | dependencies = [ 46 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 47 | "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 48 | ] 49 | 50 | [[package]] 51 | name = "lazy_static" 52 | version = "1.0.0" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | 55 | [[package]] 56 | name = "libc" 57 | version = "0.2.36" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | 60 | [[package]] 61 | name = "libloading" 62 | version = "0.4.3" 63 | source = "registry+https://github.com/rust-lang/crates.io-index" 64 | dependencies = [ 65 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 66 | "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 67 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 68 | ] 69 | 70 | [[package]] 71 | name = "num" 72 | version = "0.1.41" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | dependencies = [ 75 | "num-bigint 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", 76 | "num-complex 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", 77 | "num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", 78 | "num-iter 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)", 79 | "num-rational 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", 80 | "num-traits 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", 81 | ] 82 | 83 | [[package]] 84 | name = "num-bigint" 85 | version = "0.1.41" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | dependencies = [ 88 | "num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", 89 | "num-traits 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", 90 | "rand 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)", 91 | "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", 92 | ] 93 | 94 | [[package]] 95 | name = "num-complex" 96 | version = "0.1.41" 97 | source = "registry+https://github.com/rust-lang/crates.io-index" 98 | dependencies = [ 99 | "num-traits 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", 100 | "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", 101 | ] 102 | 103 | [[package]] 104 | name = "num-integer" 105 | version = "0.1.35" 106 | source = "registry+https://github.com/rust-lang/crates.io-index" 107 | dependencies = [ 108 | "num-traits 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", 109 | ] 110 | 111 | [[package]] 112 | name = "num-iter" 113 | version = "0.1.34" 114 | source = "registry+https://github.com/rust-lang/crates.io-index" 115 | dependencies = [ 116 | "num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", 117 | "num-traits 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", 118 | ] 119 | 120 | [[package]] 121 | name = "num-rational" 122 | version = "0.1.41" 123 | source = "registry+https://github.com/rust-lang/crates.io-index" 124 | dependencies = [ 125 | "num-bigint 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", 126 | "num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", 127 | "num-traits 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", 128 | "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", 129 | ] 130 | 131 | [[package]] 132 | name = "num-traits" 133 | version = "0.1.42" 134 | source = "registry+https://github.com/rust-lang/crates.io-index" 135 | 136 | [[package]] 137 | name = "pitch_calc" 138 | version = "0.11.1" 139 | source = "registry+https://github.com/rust-lang/crates.io-index" 140 | dependencies = [ 141 | "num 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", 142 | "rand 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)", 143 | ] 144 | 145 | [[package]] 146 | name = "rand" 147 | version = "0.3.20" 148 | source = "registry+https://github.com/rust-lang/crates.io-index" 149 | dependencies = [ 150 | "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 151 | "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", 152 | ] 153 | 154 | [[package]] 155 | name = "rasta" 156 | version = "0.1.0" 157 | dependencies = [ 158 | "jack 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", 159 | "pitch_calc 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)", 160 | "rustfft 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 161 | ] 162 | 163 | [[package]] 164 | name = "rustc-serialize" 165 | version = "0.3.24" 166 | source = "registry+https://github.com/rust-lang/crates.io-index" 167 | 168 | [[package]] 169 | name = "rustfft" 170 | version = "2.0.0" 171 | source = "registry+https://github.com/rust-lang/crates.io-index" 172 | dependencies = [ 173 | "num-complex 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", 174 | "num-traits 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", 175 | ] 176 | 177 | [[package]] 178 | name = "winapi" 179 | version = "0.2.8" 180 | source = "registry+https://github.com/rust-lang/crates.io-index" 181 | 182 | [[package]] 183 | name = "winapi-build" 184 | version = "0.1.1" 185 | source = "registry+https://github.com/rust-lang/crates.io-index" 186 | 187 | [metadata] 188 | "checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf" 189 | "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" 190 | "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" 191 | "checksum jack 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "064b66c901bbbfd8f991e8c5923376f52cf832115aaf77ae97173c4bbc33274e" 192 | "checksum jack-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0d4ca501477fd3cd93a36df581046e5d6338ed826cf7e9b8d302603521e6cc3" 193 | "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" 194 | "checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d" 195 | "checksum libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "1e5d97d6708edaa407429faa671b942dc0f2727222fb6b6539bf1db936e4b121" 196 | "checksum libloading 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "fd38073de8f7965d0c17d30546d4bb6da311ab428d1c7a3fc71dff7f9d4979b9" 197 | "checksum num 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "cc4083e14b542ea3eb9b5f33ff48bd373a92d78687e74f4cc0a30caeb754f0ca" 198 | "checksum num-bigint 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "bdc1494b5912f088f260b775799468d9b9209ac60885d8186a547a0476289e23" 199 | "checksum num-complex 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "58de7b4bf7cf5dbecb635a5797d489864eadd03b107930cbccf9e0fd7428b47c" 200 | "checksum num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "d1452e8b06e448a07f0e6ebb0bb1d92b8890eea63288c0b627331d53514d0fba" 201 | "checksum num-iter 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)" = "7485fcc84f85b4ecd0ea527b14189281cf27d60e583ae65ebc9c088b13dffe01" 202 | "checksum num-rational 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "0b950f75e042fdd710460084d19c8efdcd72d65183ead8ecd04b90483f5a55d2" 203 | "checksum num-traits 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "9936036cc70fe4a8b2d338ab665900323290efb03983c86cbe235ae800ad8017" 204 | "checksum pitch_calc 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "62a4fe4bc5e28312afef25e60cb10dc006dd376db75174d3f260a9cc343224bf" 205 | "checksum rand 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)" = "512870020642bb8c221bf68baa1b2573da814f6ccfe5c9699b1c303047abe9b1" 206 | "checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" 207 | "checksum rustfft 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7dc03728f9f3b5d4cacdaecee2ad9d3da56f8089b7f6b4edfac435afc90bcbec" 208 | "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" 209 | "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" 210 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rasta" 3 | version = "0.1.0" 4 | authors = ["ricky han "] 5 | 6 | [dependencies] 7 | jack = "0.6" 8 | rustfft = "2.0.0" 9 | pitch_calc = "0.11.1" 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rasta 2 | 3 | Guitar/bass effect unit. 4 | 5 | ## Example audio 6 | 7 | [[mp3]](http://rickyhan.com/out.mp3) 8 | 9 | ## Getting started 10 | 11 | 1. Run a JACK server and set [appropriate sample rate](https://askubuntu.com/questions/539406/how-to-avoid-xrun-callback-skips) to achieve low enough latency. 12 | 2. On Ubuntu install `libjack-dev` or equivalent for other system 13 | 3. Load supplied patchbay definition 14 | 5. `cargo run --release` 15 | 6. Type: `c in out` to chain from input to output 16 | 17 | ## How to use 18 | 19 | | Command | Explanation | 20 | |--------------------------|---------------------------------------------------| 21 | | c in delay out | connect input to delay pedal then to out | 22 | | c in aw out | autowah | 23 | | a delay2 delay | add a delay effect named delay2 | 24 | | s delay2 delay 0.14 | Set pedal "delay" parameter delay to 0.14 seconds | 25 | | s delay2 feedback 0.8 | Set feedback to 0.8 | 26 | | c in aw delay delay2 out | daisy chain together | 27 | | p | print current graph definition | 28 | | b aw | bypass autowah pedal | 29 | | b | bypass all effects | 30 | 31 | ## License 32 | 33 | MIT -------------------------------------------------------------------------------- /patchbay.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | rasta_out_l 6 | rasta_out_r 7 | 8 | 9 | capture_2 10 | 11 | 12 | 13 | 14 | playback_1 15 | playback_2 16 | 17 | 18 | guitar_in 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/effects/autowah.rs: -------------------------------------------------------------------------------- 1 | use effects::{CtrlMsg, Effect}; 2 | use std::default::Default; 3 | use std::f32::consts::PI as pi; 4 | 5 | const sinConst3: f32 = -1. / 6.; 6 | const sinConst5: f32 = 1. / 120.; 7 | const tanConst3: f32 = 1. / 3.; 8 | const tanConst5: f32 = 1. / 3.; 9 | 10 | fn sin(x: f32) -> f32 { 11 | x * (1. + sinConst3 * x * x) 12 | } 13 | 14 | fn precisionSin(x: f32) -> f32 { 15 | let x2 = x * x; 16 | let x4 = x2 * x2; 17 | x * (1. + sinConst3*x2 + sinConst5*x4) 18 | } 19 | fn tan(x: f32) -> f32 { 20 | x * (1. + tanConst3*x*x) 21 | } 22 | 23 | fn precisionTan(x: f32) -> f32 { 24 | let x2 = x * x; 25 | let x4 = x2 * x2; 26 | x * (1. + tanConst3*x2 + tanConst5*x4) 27 | } 28 | 29 | #[derive(Default)] 30 | pub struct AutoWah { 31 | bypassing: bool, 32 | frame_size: u32, 33 | 34 | // Level Detector parameters 35 | alphaA: f32, 36 | alphaR: f32, 37 | betaA: f32, 38 | betaR: f32, 39 | bufferL: (f32, f32), 40 | 41 | // Lowpass filter parameters 42 | bufferLP: f32, 43 | gainLP: f32, 44 | 45 | // State Variable Filter parameters 46 | minFreq: f32, 47 | freqBandwidth: f32, 48 | q: f32, 49 | sample_rate: f32, 50 | centerFreq: f32, 51 | yHighpass: f32, 52 | yBandpass: f32, 53 | yLowpass: f32, 54 | filter: FilterType, 55 | 56 | // Mixer parameters 57 | alphaMix: f32, 58 | betaMix: f32, 59 | } 60 | 61 | impl Effect for AutoWah { 62 | fn new(sample_rate: usize, frame_size: u32) -> Self { 63 | let mut aw = AutoWah { 64 | bypassing: false, 65 | sample_rate: sample_rate as f32, 66 | frame_size, 67 | ..Default::default() 68 | }; 69 | 70 | aw.set_attack(0.04); 71 | aw.set_release(0.002); 72 | aw.set_min_maxFreq(20., 3000.); 73 | aw.set_quality_factor(1. / 5.); 74 | aw.set_mixing(0.8); 75 | 76 | aw 77 | } 78 | 79 | fn name(&self) -> &str { 80 | "autowah" 81 | } 82 | 83 | fn process_samples(&mut self, input: &[f32], output_l: &mut [f32], output_r: &mut [f32]) { 84 | for i in 0..self.frame_size as usize { 85 | let x = input[i] * 1.; 86 | let mut y = self.run_effect(x) * 2.; 87 | 88 | //TODO: saturation 89 | if y > 1. {y = 1.;} 90 | else if y < -1. {y = -1.;} 91 | 92 | output_l[i] = y; 93 | output_r[i] = y; 94 | } 95 | } 96 | 97 | fn bypass(&mut self) { 98 | self.bypassing = !self.bypassing; 99 | } 100 | 101 | fn is_bypassing(&self) -> bool { 102 | self.bypassing 103 | } 104 | 105 | fn ctrl(&mut self, msg: CtrlMsg) { 106 | match msg { 107 | _ => (), 108 | } 109 | } 110 | } 111 | 112 | impl AutoWah { 113 | pub fn run_effect(&mut self, x: f32) -> f32 { 114 | let xL = x.abs(); 115 | 116 | let yL = self.level_detector(xL); 117 | 118 | //fc = yL * (maxFreq - minFreq) + minFreq; 119 | self.centerFreq = yL * self.freqBandwidth + self.minFreq; 120 | 121 | //float xF = x; 122 | let xF = self.low_pass_filter(x); 123 | let yF = self.state_variable_filter(xF); 124 | 125 | let y = self.mixer(x, yF); 126 | 127 | return y; 128 | } 129 | 130 | pub fn set_filter_type(&mut self, typ: FilterType) { 131 | self.filter = typ; 132 | } 133 | pub fn set_attack(&mut self, tauA: f32) { 134 | self.alphaA = (-1. / tauA / self.sample_rate ).exp(); 135 | self.betaA = 1. - self.alphaA; 136 | } 137 | pub fn set_release(&mut self, tauR: f32) { 138 | self.alphaR = (-1. / tauR / self.sample_rate ).exp(); 139 | self.betaR = 1. - self.alphaA; 140 | } 141 | pub fn set_min_maxFreq(&mut self, minFreq: f32, maxFreq: f32) { 142 | self.freqBandwidth = pi * (2. * maxFreq - minFreq) / self.sample_rate; 143 | self.minFreq = pi * minFreq / self.sample_rate; 144 | } 145 | pub fn set_sample_rate(&mut self, sample_rate: f32) { 146 | self.sample_rate = sample_rate; 147 | } 148 | pub fn set_quality_factor(&mut self, Q: f32) { 149 | self.q = Q; 150 | self.gainLP = (0.5 * Q).sqrt(); 151 | } 152 | pub fn set_mixing(&mut self, alphaMix: f32) { 153 | self.alphaMix = alphaMix; 154 | self.betaMix = 1. - alphaMix; 155 | } 156 | fn level_detector(&mut self, x: f32) -> f32 { 157 | let y1 = self.alphaR * self.bufferL.1 + self.betaR * x; 158 | if x > y1 { self.bufferL.1 = x; } 159 | else { self.bufferL.1 = y1;} 160 | 161 | self.bufferL.0 = self.alphaA * self.bufferL.0 + self.betaA * self.bufferL.1; 162 | 163 | return self.bufferL.0; 164 | } 165 | fn low_pass_filter(&mut self, x: f32) -> f32 { 166 | let K = tan(self.centerFreq); 167 | let b0 = K / (K + 1.); 168 | let a1 = 2.0 * (b0 - 0.5); 169 | 170 | let xh = x - a1 * self.bufferLP; 171 | let y = b0 * (xh + self.bufferLP); 172 | self.bufferLP = xh; 173 | 174 | return self.gainLP * y; 175 | } 176 | fn state_variable_filter(&mut self, x: f32) -> f32{ 177 | let f = 2.0 * sin(self.centerFreq); 178 | self.yHighpass = x - self.yLowpass - self.q * self.yBandpass; 179 | self.yBandpass += f * self.yHighpass; 180 | self.yLowpass += f * self.yBandpass; 181 | 182 | use self::FilterType::*; 183 | match self.filter { 184 | Lowpass => self.yLowpass, 185 | Bandpass => self.yBandpass, 186 | Highpass => self.yHighpass, 187 | } 188 | } 189 | fn mixer(&self, x: f32, y: f32) -> f32 { 190 | self.alphaMix * y + self.betaMix * x 191 | } 192 | } 193 | 194 | enum FilterType { 195 | Lowpass, 196 | Bandpass, 197 | Highpass 198 | } 199 | 200 | impl Default for FilterType { 201 | fn default() -> Self { 202 | FilterType::Highpass 203 | } 204 | } -------------------------------------------------------------------------------- /src/effects/delay.rs: -------------------------------------------------------------------------------- 1 | use effects::{Effect, CtrlMsg}; 2 | 3 | pub struct Delay { 4 | pub bypassing: bool, 5 | delay_buffer: Vec, 6 | delay_buffer_size: usize, 7 | feedback: f32, 8 | i_idx: usize, 9 | o_idx: usize, 10 | sample_rate: usize, 11 | delay_time: usize, 12 | frame_size: u32, 13 | } 14 | 15 | impl Delay { 16 | 17 | /// t is in seconds 18 | pub fn set_delay(&mut self, t: f32) { 19 | let delay_time = (t * self.sample_rate as f32) as usize; 20 | assert!(delay_time < self.delay_buffer_size); 21 | self.delay_time = delay_time; 22 | } 23 | 24 | pub fn set_feedback(&mut self, f: f32) { 25 | assert!(f < 1.); // multiplying by > 1 would be too loud 26 | self.feedback = f; 27 | } 28 | 29 | 30 | } 31 | 32 | impl Effect for Delay { 33 | 34 | fn new(sample_rate: usize, frame_size: u32) -> Self { 35 | let dbs = sample_rate; 36 | Delay { 37 | bypassing: false, 38 | delay_buffer_size: dbs, 39 | delay_buffer: vec![0.; dbs], 40 | feedback: 0.3, 41 | i_idx: 0, 42 | o_idx: 0, 43 | delay_time: 8820, 44 | sample_rate, 45 | frame_size 46 | } 47 | } 48 | 49 | fn name(&self) -> &str { 50 | "delay" 51 | } 52 | 53 | fn process_samples(&mut self, input: &[f32], output_l: &mut [f32], output_r: &mut [f32]) { 54 | 55 | if self.bypassing { 56 | output_l.clone_from_slice(input); 57 | output_r.clone_from_slice(input); 58 | return; 59 | } 60 | 61 | for bufptr in 0..self.frame_size as usize { 62 | if self.i_idx >= self.delay_buffer_size { 63 | self.i_idx = 0; 64 | } 65 | 66 | self.o_idx = if self.i_idx >= self.delay_time { 67 | self.i_idx - self.delay_time 68 | } else { 69 | self.delay_buffer_size as usize + self.i_idx - self.delay_time 70 | }; 71 | 72 | let y = input[bufptr] + (self.delay_buffer[self.o_idx] * self.feedback); 73 | self.delay_buffer[self.i_idx] = y; 74 | let out = (y + 0.5).cos(); 75 | 76 | output_r[bufptr] = out; 77 | output_l[bufptr] = out; 78 | 79 | self.i_idx += 1; 80 | } 81 | 82 | // output_l.clone_from_slice(&slice); 83 | // output_r.clone_from_slice(&slice); 84 | } 85 | 86 | fn bypass(&mut self) { 87 | self.bypassing = !self.bypassing; 88 | } 89 | 90 | fn is_bypassing(&self) -> bool { 91 | self.bypassing 92 | } 93 | 94 | fn ctrl(&mut self, msg: CtrlMsg) { 95 | use self::CtrlMsg::*; 96 | match msg { 97 | Bypass => self.bypass(), 98 | Set(_pedal_name, conf_name, val) => { 99 | if &conf_name == "feedback" { 100 | self.set_feedback(val); 101 | } else if &conf_name == "delay" { 102 | self.set_delay(val); 103 | } 104 | }, 105 | _ => (), 106 | } 107 | } 108 | 109 | } -------------------------------------------------------------------------------- /src/effects/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod overdrive; 2 | pub mod delay; 3 | pub mod autowah; 4 | pub mod tuner; 5 | pub mod tremelo; 6 | pub mod pedals; 7 | pub use self::pedals::Pedals; 8 | 9 | pub trait Effect: Send { 10 | fn new(sample_rate: usize, frame_size: u32) -> Self 11 | where Self: Sized; 12 | 13 | fn name(&self) -> &str; 14 | 15 | fn process_samples(&mut self, input: &[f32], output_l: &mut [f32], output_r: &mut [f32]) { 16 | output_l.clone_from_slice(input); 17 | output_r.clone_from_slice(input); 18 | } 19 | 20 | fn bypass(&mut self); 21 | 22 | fn is_bypassing(&self) -> bool; 23 | 24 | fn ctrl(&mut self, msg: CtrlMsg); 25 | 26 | } 27 | 28 | type PedalName = String; 29 | type ConfName = String; 30 | type Val = f32; 31 | 32 | pub enum CtrlMsg { 33 | Bypass, 34 | BypassPedal(String), 35 | Tuner, 36 | Connect(String, String), 37 | Chain(Vec), 38 | Disconnect(String), 39 | Connections, 40 | Add(String, String), 41 | Set(PedalName, ConfName, Val), 42 | } 43 | -------------------------------------------------------------------------------- /src/effects/overdrive.rs: -------------------------------------------------------------------------------- 1 | use effects::{Effect, CtrlMsg}; 2 | 3 | pub struct Overdrive { 4 | pub bypassing: bool, 5 | } 6 | 7 | /// Audio at a low input level is driven by higher input 8 | /// levels in a non-linear curve characteristic 9 | /// 10 | /// For overdrive, Symmetrical soft clipping of input values has to 11 | /// be performed. 12 | impl Effect for Overdrive { 13 | 14 | fn new(_sample_rate: usize, _frame_size: u32) -> Self { 15 | Overdrive { 16 | bypassing: false 17 | } 18 | } 19 | 20 | fn name(&self) -> &str { 21 | "overdrive" 22 | } 23 | 24 | fn process_samples(&mut self, input: &[f32], output_l: &mut [f32], output_r: &mut [f32]) { 25 | 26 | if self.bypassing { 27 | output_l.clone_from_slice(input); 28 | output_r.clone_from_slice(input); 29 | return; 30 | } 31 | 32 | let slice = input.iter().map(|&x| { 33 | let x = x.abs(); 34 | if 0. < x && x < 0.333 { 35 | 2. * x 36 | } else if 0.333 < x && x < 0.666 { 37 | let t = 2. - 3. * x; 38 | (3. - t * t) / 3. 39 | } else { 40 | x 41 | } 42 | }).collect::>(); 43 | 44 | output_l.clone_from_slice(&slice); 45 | output_r.clone_from_slice(&slice); 46 | } 47 | 48 | fn bypass(&mut self) { 49 | self.bypassing = !self.bypassing; 50 | } 51 | 52 | fn is_bypassing(&self) -> bool { 53 | self.bypassing 54 | } 55 | 56 | fn ctrl(&mut self, msg: CtrlMsg) { 57 | use self::CtrlMsg::*; 58 | match msg { 59 | Bypass => self.bypass(), 60 | _ => (), 61 | } 62 | 63 | } 64 | 65 | } -------------------------------------------------------------------------------- /src/effects/pedals.rs: -------------------------------------------------------------------------------- 1 | use effects::*; 2 | use std::collections::HashMap; 3 | 4 | pub struct Pedals { 5 | sample_rate: usize, 6 | frame_size: u32, 7 | pub pedals: HashMap>, 8 | pub bypassing: bool, 9 | /// in -> eff1 -> eff2 -> out 10 | chain: HashMap, 11 | } 12 | 13 | impl Effect for Pedals { 14 | 15 | fn new(sample_rate: usize, frame_size: u32) -> Self { 16 | Pedals { 17 | sample_rate, 18 | frame_size, 19 | pedals: HashMap::new(), 20 | bypassing: false, 21 | chain: HashMap::new(), 22 | } 23 | } 24 | 25 | fn name(&self) -> &str { 26 | "effects" 27 | } 28 | 29 | fn process_samples(&mut self, input: &[f32], output_l: &mut [f32], output_r: &mut [f32]) { 30 | 31 | if self.bypassing { 32 | output_l.clone_from_slice(input); 33 | output_r.clone_from_slice(input); 34 | return; 35 | } 36 | 37 | let mut next_node = { 38 | let temp = self.chain.get("in"); 39 | if temp.is_none() { 40 | return; 41 | } else { 42 | temp.unwrap() 43 | } 44 | }; 45 | 46 | let mut temp_buf = input.to_owned(); 47 | 48 | while *next_node != "out" { 49 | let eff = self.pedals.get_mut(next_node); 50 | let eff = if eff.is_none() { 51 | break 52 | } else { 53 | eff.unwrap() 54 | }; 55 | // if eff.is_bypassing() { continue; } 56 | 57 | eff.process_samples(&temp_buf, output_l, output_r); 58 | temp_buf = output_l.to_owned(); 59 | 60 | let next = self.chain.get(next_node); 61 | 62 | next_node = if next.is_none() { 63 | break 64 | } else { 65 | next.unwrap() 66 | }; 67 | } 68 | 69 | output_l.clone_from_slice(&temp_buf); 70 | output_r.clone_from_slice(&temp_buf); 71 | 72 | } 73 | 74 | fn bypass(&mut self) { 75 | self.bypassing = !self.bypassing; 76 | println!("Bypassing: {}", self.bypassing); 77 | } 78 | 79 | fn is_bypassing(&self) -> bool { 80 | self.bypassing 81 | } 82 | 83 | fn ctrl(&mut self, msg: CtrlMsg) { 84 | use self::CtrlMsg::*; 85 | match msg { 86 | Bypass => self.bypass(), 87 | BypassPedal(name) => { 88 | let mut pedal = self.pedals.get_mut(&name).unwrap(); 89 | (*pedal).ctrl(Bypass); 90 | } 91 | Tuner => { 92 | let mut tuner = self.pedals.get_mut("tuner").unwrap(); 93 | (*tuner).ctrl(msg); 94 | }, 95 | Connect(from, to) => { 96 | self.connect(&from, &to) 97 | }, 98 | Disconnect(from) => { 99 | self.disconnect(&from) 100 | }, 101 | Connections => self.print_conn(), 102 | Add(name, eff_type) => { 103 | let eff : Box = match eff_type.as_str() { 104 | "delay" => box delay::Delay::new(self.sample_rate, self.frame_size), 105 | "overdrive" => box overdrive::Overdrive::new(self.sample_rate, self.frame_size), 106 | "tuner" => box tuner::Tuner::new(self.sample_rate, self.frame_size), 107 | "autowah" => box autowah::AutoWah::new(self.sample_rate, self.frame_size), 108 | &_ => unimplemented!() 109 | }; 110 | self.add(&name, eff); 111 | }, 112 | Set(name, conf, val) => { 113 | let mut pedal = self.pedals.get_mut(&name).unwrap(); 114 | (*pedal).ctrl(Set(name, conf, val)); 115 | }, 116 | Chain(v) => { 117 | for i in v.into_iter() { 118 | self.ctrl(i); 119 | } 120 | } 121 | } 122 | } 123 | 124 | } 125 | 126 | impl Pedals { 127 | 128 | pub fn add(&mut self, name: &str, eff: Box) { 129 | self.pedals.insert(name.to_owned(), eff); 130 | } 131 | 132 | pub fn connect(&mut self, from: &str, to: &str) { 133 | self.chain.insert(from.to_owned(), to.to_owned()); 134 | } 135 | 136 | pub fn disconnect(&mut self, from: &str) { 137 | self.chain.remove(from); 138 | } 139 | 140 | pub fn print_conn(&self) { 141 | 142 | print!("Chain: "); 143 | let mut node = "in"; 144 | while node != "out" && self.chain.contains_key(node) { 145 | print!("{} -> ", node); 146 | node = self.chain.get(node).unwrap(); 147 | } 148 | println!("out"); 149 | 150 | println!("Graph: {:?}", self.chain); 151 | 152 | println!("Pedals: {:?}", self.pedals.keys().collect::>()) 153 | } 154 | 155 | } -------------------------------------------------------------------------------- /src/effects/tremelo.rs: -------------------------------------------------------------------------------- 1 | use effects::{Effect, CtrlMsg}; 2 | 3 | pub struct Tremelo { 4 | pub bypassing: bool, 5 | pub depth: f32, 6 | pub control: i16, 7 | pub modulo: i16, 8 | pub counter_limit: i16, 9 | pub offset: f32, 10 | pub alpha_mix: f32, 11 | pub beta_mix: f32, 12 | } 13 | 14 | impl Effect for Tremelo { 15 | 16 | fn new(_sample_rate: usize, _frame_size: u32) -> Self { 17 | let depth = 1.; 18 | Tremelo { 19 | bypassing: false, 20 | counter_limit: 50, 21 | depth, 22 | control: 1, 23 | modulo: 0, 24 | offset: 1. - depth, 25 | alpha_mix: 0.8, 26 | beta_mix: 0.2, 27 | } 28 | } 29 | 30 | fn name(&self) -> &str { 31 | "tremelo" 32 | } 33 | 34 | fn process_samples(&mut self, input: &[f32], output_l: &mut [f32], output_r: &mut [f32]) { 35 | if self.bypassing { 36 | output_l.clone_from_slice(input); 37 | output_r.clone_from_slice(input); 38 | return; 39 | } 40 | 41 | let m = self.modulo as f32 * self.depth / self.counter_limit as f32; 42 | for (i, x) in input.iter().enumerate() { 43 | let y_ = (m + self.offset) * x; 44 | let y = self.mixer(*x, y_); 45 | output_r[i] = y; 46 | output_l[i] = y; 47 | } 48 | 49 | self.sweep(); 50 | } 51 | 52 | 53 | 54 | 55 | fn bypass(&mut self) { 56 | self.bypassing = !self.bypassing; 57 | } 58 | 59 | fn is_bypassing(&self) -> bool { 60 | self.bypassing 61 | } 62 | 63 | fn ctrl(&mut self, msg: CtrlMsg) { 64 | use self::CtrlMsg::*; 65 | match msg { 66 | Bypass => self.bypass(), 67 | Set(_pedal_name, conf_name, val) => { 68 | if &conf_name == "limit" { 69 | self.set_limit(val as i16); 70 | } 71 | if &conf_name == "mix" { 72 | self.set_mixing(val); 73 | } 74 | }, 75 | _ => (), 76 | } 77 | 78 | } 79 | 80 | } 81 | 82 | impl Tremelo { 83 | fn sweep(&mut self) { 84 | self.modulo += self.control; 85 | if self.modulo > self.counter_limit { 86 | self.control = -1; 87 | } else if self.modulo == 0 { 88 | self.control = 1; 89 | } 90 | } 91 | fn set_limit(&mut self, val: i16) { 92 | self.counter_limit = val; 93 | } 94 | fn mixer(&self, x: f32, y: f32) -> f32 { 95 | self.alpha_mix * y + self.beta_mix * x 96 | } 97 | pub fn set_mixing(&mut self, alpha_mix: f32) { 98 | self.alpha_mix = alpha_mix; 99 | self.beta_mix = 1. - alpha_mix; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/effects/tuner.rs: -------------------------------------------------------------------------------- 1 | static TUNER_BUFFER_SIZE : usize = 10240; 2 | 3 | use std::time::Instant; 4 | use std::thread; 5 | 6 | extern crate rustfft; 7 | use effects::{CtrlMsg, Effect}; 8 | use self::rustfft::FFTplanner; 9 | use self::rustfft::num_complex::Complex; 10 | use self::rustfft::num_traits::Zero; 11 | 12 | pub struct Tuner { 13 | tuner_buffer: Vec, 14 | i_idx: usize, 15 | bypassing: bool, 16 | sample_rate: usize, 17 | frame_size: u32, 18 | } 19 | 20 | pub fn calculate_spectrum(samples: &[f32]) -> Vec { 21 | let now = Instant::now(); 22 | 23 | let mut input: Vec> = samples.iter() 24 | .map(|&x| Complex::new(x, 0.0)) 25 | .collect(); 26 | 27 | let mut output: Vec> = vec![Complex::zero(); input.len()]; 28 | 29 | let mut planner = FFTplanner::new(false); 30 | let fft = planner.plan_fft(input.len()); 31 | fft.process(&mut input, &mut output); 32 | 33 | println!("{:?}", now.elapsed()); 34 | 35 | output.iter() 36 | .map(|&c| c.norm_sqr()) 37 | .collect() 38 | } 39 | 40 | pub fn tune(input: &[f32], sample_rate: usize) -> Option { 41 | 42 | let input_len = input.len(); 43 | let freqs = calculate_spectrum(input); 44 | 45 | let buckets: Vec<_> = 46 | (0 .. 1 + input_len / 2) // has Hermitian symmetry to f=0 47 | .filter_map(|i| { 48 | let norm = freqs[i]; 49 | let noise_threshold = 1.0; 50 | if norm > noise_threshold { 51 | let f = i as f32 / input_len as f32 * sample_rate as f32; 52 | Some((f, norm)) 53 | } else { 54 | None 55 | } 56 | }) 57 | .collect(); 58 | 59 | if buckets.is_empty() { 60 | return None 61 | } 62 | 63 | let &(max_f, _max_val) = 64 | buckets.iter() 65 | .max_by(|&&(_f1, ref val1), &&(_f2, ref val2)| val1.partial_cmp(val2).unwrap()) 66 | .unwrap(); 67 | 68 | 69 | println!("Max index is {}", max_f); 70 | // println!("Max value is {}", max_val); 71 | 72 | Some(max_f) 73 | } 74 | 75 | impl Effect for Tuner { 76 | 77 | fn new(sample_rate: usize, frame_size: u32) -> Self { 78 | Self { 79 | tuner_buffer: vec![0.; TUNER_BUFFER_SIZE], 80 | i_idx: 0, 81 | bypassing: true, 82 | sample_rate, 83 | frame_size 84 | } 85 | } 86 | 87 | fn name(&self) -> &str { 88 | "tuner" 89 | } 90 | 91 | fn process_samples(&mut self, input: &[f32], output_l: &mut [f32], output_r: &mut [f32]) { 92 | 93 | for bufptr in 0..self.frame_size as usize { 94 | if self.i_idx >= TUNER_BUFFER_SIZE { 95 | self.i_idx = 0; 96 | } 97 | self.tuner_buffer[self.i_idx] = input[bufptr]; 98 | self.i_idx += 1; 99 | 100 | output_l[bufptr] = input[bufptr]; 101 | output_r[bufptr] = input[bufptr]; 102 | } 103 | 104 | } 105 | 106 | fn bypass(&mut self) { 107 | () 108 | } 109 | 110 | fn is_bypassing(&self) -> bool { 111 | self.bypassing 112 | } 113 | 114 | fn ctrl(&mut self, msg: CtrlMsg) { 115 | use self::CtrlMsg::*; 116 | match msg { 117 | Bypass => self.bypass(), 118 | Tuner => { 119 | let input = self.tuner_buffer.to_owned(); 120 | let sample_rate = self.sample_rate; 121 | thread::spawn(move || { 122 | tune(&input, sample_rate); 123 | }); 124 | }, 125 | _ => (), 126 | } 127 | } 128 | 129 | } 130 | 131 | #[cfg(test)] 132 | mod tests { 133 | use std::f32::consts::PI; 134 | use super::{calculate_spectrum, Tuner}; 135 | use effects::Effect; 136 | 137 | #[test] 138 | fn test_fft() { 139 | 140 | let length = 1024; 141 | let freq = 2.0; 142 | 143 | let sin_vec: Vec = (0..length).map(|i| (((i as f32 * freq * 420.0 * PI / length as f32 ).sin() * 0.5))).collect(); 144 | // println!("sin_vec = {:?}", sin_vec); 145 | let spectrum = calculate_spectrum(sin_vec.as_slice()); 146 | let argmax = { 147 | let mut argmax = spectrum.iter() 148 | .enumerate() 149 | .max_by( 150 | |&(_i0, x0), &(_i1, x1)| 151 | x0.partial_cmp(x1).unwrap() 152 | ) 153 | .unwrap() 154 | .0; 155 | 156 | if argmax > length / 2 { 157 | argmax = length - argmax; 158 | } 159 | argmax 160 | }; 161 | 162 | println!("argmax_spectrum = {}", argmax); 163 | println!("spectrum = {:?}", spectrum); 164 | } 165 | 166 | #[test] 167 | fn test_tune() { 168 | // okay... this has some "numerical stability" issues surrounding 169 | // discret fourier transformation when sampling rate >> length of sample 170 | // there will only be 128 bins if one sample 171 | // contains 128 data points 172 | // this means I will have to use a circular buffer 173 | // to extend the length of the input to fft 174 | 175 | let sampling_rate = 41100.; 176 | let length = 1280; 177 | let freq = 420.0; 178 | 179 | let sin_vec: Vec = (0..length) 180 | .map(|i| { 181 | let t = (i as f32) / sampling_rate; 182 | ((2. * PI) * freq * t).sin() 183 | }).collect(); 184 | 185 | let tuner = Tuner::new(); 186 | tuner.process_samples(&sin_vec, &mut vec![], &mut vec![]); 187 | let note = tuner.tune(); 188 | 189 | println!("NOTE : {:?}", note); 190 | 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | //! Takes 2 audio inputs and outputs them to 2 audio outputs. 2 | //! All JACK notifications are also printed out. 3 | 4 | #![feature(box_syntax)] 5 | 6 | extern crate jack; 7 | 8 | mod notifications; 9 | mod effects; 10 | mod parser; 11 | 12 | use parser::parse_input; 13 | use effects::{Effect}; 14 | use std::io::{self, Write}; 15 | use jack::{Control, Client, ProcessScope}; 16 | use std::sync::mpsc::channel; 17 | 18 | fn main() { 19 | // Create client 20 | let (client, _status) = 21 | jack::Client::new("rasta", jack::ClientOptions::NO_START_SERVER).unwrap(); 22 | 23 | let sample_rate = client.sample_rate(); 24 | let frame_size = client.buffer_size(); 25 | 26 | // Create ports 27 | let in_b = client 28 | .register_port("guitar_in", jack::AudioIn::default()) 29 | .unwrap(); 30 | let mut out_a = client 31 | .register_port("rasta_out_l", jack::AudioOut::default()) 32 | .unwrap(); 33 | let mut out_b = client 34 | .register_port("rasta_out_r", jack::AudioOut::default()) 35 | .unwrap(); 36 | 37 | let mut pedals = effects::Pedals::new(sample_rate, frame_size); 38 | pedals.add("overdrive", box effects::overdrive::Overdrive::new(sample_rate, frame_size)); 39 | pedals.add("delay", box effects::delay::Delay::new(sample_rate, frame_size)); 40 | pedals.add("tuner", box effects::tuner::Tuner::new(sample_rate, frame_size)); 41 | pedals.add("aw", box effects::autowah::AutoWah::new(sample_rate, frame_size)); 42 | pedals.add("trem", box effects::tremelo::Tremelo::new(sample_rate, frame_size)); 43 | 44 | let (tx, rx) = channel(); 45 | 46 | let process_callback = move |_: &Client, ps: &ProcessScope| -> Control { 47 | 48 | let mut out_a_p = out_a.as_mut_slice(ps); 49 | let mut out_b_p = out_b.as_mut_slice(ps); 50 | let in_b_p = in_b.as_slice(ps); 51 | 52 | if let Ok(msg) = rx.try_recv() { 53 | pedals.ctrl(msg); 54 | } 55 | pedals.process_samples(in_b_p, &mut out_a_p, &mut out_b_p); 56 | Control::Continue 57 | }; 58 | 59 | let process = jack::ClosureProcessHandler::new(process_callback); 60 | let active_client = client.activate_async((), process).unwrap(); 61 | 62 | // Wait for user input to quit 63 | let mut user_input = String::new(); 64 | print!(">>> "); 65 | io::stdout().flush().ok().expect("Could not flush stdout"); 66 | while let Ok(_) = io::stdin().read_line(&mut user_input) { 67 | let msg = parse_input(&user_input[0..user_input.len()-1]); 68 | tx.send(msg).unwrap(); 69 | user_input.clear(); 70 | print!(">>> "); 71 | io::stdout().flush().ok().expect("Could not flush stdout"); 72 | } 73 | 74 | active_client.deactivate().unwrap(); 75 | } 76 | -------------------------------------------------------------------------------- /src/notifications.rs: -------------------------------------------------------------------------------- 1 | use jack; 2 | 3 | pub struct Notifications; 4 | 5 | impl jack::NotificationHandler for Notifications { 6 | fn thread_init(&self, _: &jack::Client) { 7 | println!("JACK: thread init"); 8 | } 9 | 10 | fn shutdown(&mut self, status: jack::ClientStatus, reason: &str) { 11 | println!( 12 | "JACK: shutdown with status {:?} because \"{}\"", 13 | status, reason 14 | ); 15 | } 16 | 17 | fn freewheel(&mut self, _: &jack::Client, is_enabled: bool) { 18 | println!( 19 | "JACK: freewheel mode is {}", 20 | if is_enabled { "on" } else { "off" } 21 | ); 22 | } 23 | 24 | fn buffer_size(&mut self, _: &jack::Client, sz: jack::Frames) -> jack::Control { 25 | println!("JACK: buffer size changed to {}", sz); 26 | jack::Control::Continue 27 | } 28 | 29 | fn sample_rate(&mut self, _: &jack::Client, srate: jack::Frames) -> jack::Control { 30 | println!("JACK: sample rate changed to {}", srate); 31 | jack::Control::Continue 32 | } 33 | 34 | fn client_registration(&mut self, _: &jack::Client, name: &str, is_reg: bool) { 35 | println!( 36 | "JACK: {} client with name \"{}\"", 37 | if is_reg { "registered" } else { "unregistered" }, 38 | name 39 | ); 40 | } 41 | 42 | fn port_registration(&mut self, _: &jack::Client, port_id: jack::PortId, is_reg: bool) { 43 | println!( 44 | "JACK: {} port with id {}", 45 | if is_reg { "registered" } else { "unregistered" }, 46 | port_id 47 | ); 48 | } 49 | 50 | fn port_rename( 51 | &mut self, 52 | _: &jack::Client, 53 | port_id: jack::PortId, 54 | old_name: &str, 55 | new_name: &str, 56 | ) -> jack::Control { 57 | println!( 58 | "JACK: port with id {} renamed from {} to {}", 59 | port_id, old_name, new_name 60 | ); 61 | jack::Control::Continue 62 | } 63 | 64 | fn ports_connected( 65 | &mut self, 66 | _: &jack::Client, 67 | port_id_a: jack::PortId, 68 | port_id_b: jack::PortId, 69 | are_connected: bool, 70 | ) { 71 | println!( 72 | "JACK: ports with id {} and {} are {}", 73 | port_id_a, 74 | port_id_b, 75 | if are_connected { 76 | "connected" 77 | } else { 78 | "disconnected" 79 | } 80 | ); 81 | } 82 | 83 | fn graph_reorder(&mut self, _: &jack::Client) -> jack::Control { 84 | println!("JACK: graph reordered"); 85 | jack::Control::Continue 86 | } 87 | 88 | fn xrun(&mut self, _: &jack::Client) -> jack::Control { 89 | println!("JACK: xrun occurred"); 90 | jack::Control::Continue 91 | } 92 | 93 | fn latency(&mut self, _: &jack::Client, mode: jack::LatencyType) { 94 | println!( 95 | "JACK: {} latency has changed", 96 | match mode { 97 | jack::LatencyType::Capture => "capture", 98 | jack::LatencyType::Playback => "playback", 99 | } 100 | ); 101 | } 102 | } -------------------------------------------------------------------------------- /src/parser.rs: -------------------------------------------------------------------------------- 1 | use effects::CtrlMsg; 2 | 3 | pub fn parse_input(cmd: &str) -> CtrlMsg { 4 | use self::CtrlMsg::*; 5 | 6 | if cmd == "t" { 7 | Tuner 8 | } else 9 | 10 | if cmd == "b" { 11 | Bypass 12 | } else 13 | 14 | if cmd.starts_with("b") { 15 | let tokens = cmd[2..] 16 | .split(" ") 17 | .collect::>(); 18 | let mut chain = vec![]; 19 | for token in tokens.into_iter() { 20 | chain.push(BypassPedal(token.to_owned())); 21 | } 22 | Chain(chain) 23 | } else 24 | 25 | if cmd == "p" { 26 | Connections 27 | } else 28 | 29 | if cmd.starts_with("d") { 30 | let tokens = cmd[2..] 31 | .split(" ") 32 | .collect::>(); 33 | let mut chain = vec![]; 34 | for a in tokens.into_iter() { 35 | chain.push(Disconnect(a.to_owned())); 36 | } 37 | Chain(chain) 38 | } else 39 | 40 | if cmd.starts_with("s") { 41 | let tokens = cmd[2..] 42 | .split(" ") 43 | .collect::>(); 44 | let pedal_name = tokens[0].to_owned(); 45 | let conf_name = tokens[1].to_owned(); 46 | let val = tokens[2].parse::().unwrap(); 47 | Set(pedal_name, conf_name, val) 48 | 49 | } else 50 | 51 | if cmd.starts_with("c") { 52 | // allow daisy chaining: 53 | // c in delay overdrive out 54 | let tokens = cmd[2..] 55 | .split(" ") 56 | .collect::>(); 57 | 58 | let mut chain = vec![]; 59 | for (a, b) in tokens.iter().zip(tokens[1..].into_iter()) { 60 | let inp = a.to_owned().to_owned(); 61 | let outp = b.to_owned().to_owned(); 62 | 63 | chain.push(Connect(inp, outp)) 64 | } 65 | 66 | Chain(chain) 67 | } else 68 | 69 | if cmd.starts_with("a") { 70 | let tokens = cmd.split(" ").collect::>(); 71 | let name = tokens[1].to_owned(); 72 | let eff_type = tokens[2].to_owned(); 73 | 74 | Add(name, eff_type) 75 | 76 | } else { 77 | Bypass 78 | } 79 | } --------------------------------------------------------------------------------