├── .gitignore ├── LICENSE ├── README.md ├── core ├── Cargo.toml ├── build.rs └── src │ ├── filter │ ├── iir.rs │ └── mod.rs │ ├── freq.rs │ ├── lib.rs │ ├── oscillator.rs │ ├── panning.rs │ ├── types.rs │ └── wavetable │ ├── mod.rs │ ├── saw.rs │ └── sin.rs ├── studio ├── Cargo.toml └── src │ ├── audio │ ├── buffers.rs │ ├── mod.rs │ ├── processing.rs │ └── types.rs │ ├── control │ └── mod.rs │ ├── engine │ ├── events.rs │ ├── mod.rs │ └── types.rs │ ├── main.rs │ ├── midi │ ├── decoder.rs │ ├── events.rs │ ├── io.rs │ ├── messages.rs │ ├── mod.rs │ └── types.rs │ └── osc │ └── mod.rs ├── synth ├── Cargo.toml ├── open-stage-control.js └── src │ ├── lib.rs │ ├── patch.rs │ ├── synth.rs │ └── voice.rs └── wfgen ├── Cargo.toml └── src ├── lib.rs ├── main.rs ├── saw.rs └── sin.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Christian Pérez-Llamas 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hero-synth 2 | 3 | The end goal of this project is to experiment with different technologies around music creation. 4 | 5 | The initial plan is to create an audio synthesizer that is able to model Additive, Substractive and FM synthesis all in one. The idea is to add some basic units such as oscilators, LFOs, evelopes, filters and effects, and allow the user to route them using a kind of [matrix mixer](http://en.wikipedia.org/wiki/Matrix_mixer). 6 | 7 | I also plan to experiment with MIDI interfaces to control the system. But more of that later on ... 8 | 9 | These are the sub-projects right now: 10 | - core: core components to build synths and audio effects 11 | - synth: the matrix synth implementation 12 | - host: The audio/MIDI system that hosts the synth. 13 | - wfgen: An utility to build wavetable data as code, used by the core wavetable component. 14 | 15 | To run the host: 16 | 17 | ```bash 18 | cd host 19 | cargo run --release 20 | ``` 21 | -------------------------------------------------------------------------------- /core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hero_core" 3 | version = "0.1.0" 4 | authors = ["Christian Perez-Llamas"] 5 | build = "build.rs" 6 | 7 | [build-dependencies.hero_wfgen] 8 | path = "../wfgen" 9 | 10 | [dependencies] 11 | 12 | [lib] 13 | name = "hero_core" 14 | path = "src/lib.rs" 15 | -------------------------------------------------------------------------------- /core/build.rs: -------------------------------------------------------------------------------- 1 | extern crate hero_wfgen; 2 | 3 | use hero_wfgen::{DEFAULT_WAVETABLE_SIZE, gen_lut, sin, saw}; 4 | 5 | const WT_SRC: &'static str = "src/wavetable"; 6 | 7 | fn main() { 8 | let mut sin_gen = sin::SinGen::new(DEFAULT_WAVETABLE_SIZE); 9 | gen_lut(WT_SRC, "sin", &mut sin_gen).unwrap(); 10 | 11 | let mut saw_gen = saw::SawGen::new(DEFAULT_WAVETABLE_SIZE); 12 | gen_lut(WT_SRC, "saw", &mut saw_gen).unwrap(); 13 | } 14 | -------------------------------------------------------------------------------- /core/src/filter/iir.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! Infinite Impulse Response (IIR) filters 3 | //! 4 | //! Based on https://code.google.com/p/amsynth/source/browse/src/VoiceBoard/LowPassFilter.cc 5 | //! 6 | 7 | use std::f64::consts::PI; 8 | use std::fmt::Display; 9 | use std::fmt; 10 | use filter::{Mode, Slope, Filter}; 11 | 12 | const CUTOFF_DELTA: f64 = 0.01; 13 | const CUTOFF_MIN: f64 = 10.0; 14 | 15 | fn limit_cutoff(cutoff: f64, sample_rate: f64) -> f64 { 16 | cutoff.max(CUTOFF_MIN).min((sample_rate - 1.0) / 2.0) 17 | } 18 | 19 | #[derive(Debug)] 20 | pub struct Coeffs { 21 | a0: f64, 22 | a1: f64, 23 | a2: f64, 24 | b1: f64, 25 | b2: f64, 26 | } 27 | 28 | impl Display for Coeffs { 29 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 30 | write!(f, "a0={}, a1={}, a2={}, b1={}, b2={}", 31 | self.a0, self.a1, self.a2, self.b1, self.b2) 32 | } 33 | } 34 | 35 | impl Default for Coeffs { 36 | fn default() -> Self { 37 | Coeffs { 38 | a0: 0.0, 39 | a1: 0.0, 40 | a2: 0.0, 41 | b1: 0.0, 42 | b2: 0.0 43 | } 44 | } 45 | } 46 | 47 | impl Coeffs { 48 | pub fn new(a0: f64, a1: f64, a2: f64, b1: f64, b2: f64) -> Coeffs { 49 | Coeffs { 50 | a0: a0, 51 | a1: a1, 52 | a2: a2, 53 | b1: b1, 54 | b2: b2 55 | } 56 | } 57 | 58 | pub fn zero() -> Coeffs { 59 | Coeffs { 60 | a0: 0.0, 61 | a1: 0.0, 62 | a2: 0.0, 63 | b1: 0.0, 64 | b2: 0.0 65 | } 66 | } 67 | 68 | fn common(sample_rate: f64, cutoff: f64, res: f64) -> (f64, f64, f64) { 69 | let w: f64 = cutoff / sample_rate; // cutoff freq [ 0 <= w <= 0.5 ] 70 | let r: f64 = (0.001f64).max(2.0 * (1.0 - res)); // r is 1/Q (sqrt(2) for a butterworth response) 71 | 72 | let k = (w * PI).tan(); 73 | let k2 = k * k; 74 | let rk = r * k; 75 | let bh = 1.0 + rk + k2; 76 | 77 | (k2, rk, bh) 78 | } 79 | 80 | fn lowpass(sample_rate: f64, cutoff: f64, res: f64) -> Coeffs { 81 | 82 | let (k2, rk, bh) = Self::common(sample_rate, cutoff, res); 83 | 84 | let a0: f64 = k2 / bh; 85 | 86 | Coeffs { 87 | a0: a0, 88 | a1: a0 * 2.0, 89 | a2: a0, 90 | b1: (2.0 * (k2 - 1.0)) / bh, 91 | b2: (1.0 - rk + k2) / bh, 92 | } 93 | } 94 | 95 | fn highpass(sample_rate: f64, cutoff: f64, res: f64) -> Coeffs { 96 | 97 | let (k2, rk, bh) = Self::common(sample_rate, cutoff, res); 98 | 99 | let a0: f64 = 1.0 / bh; 100 | 101 | Coeffs { 102 | a0: a0, 103 | a1: -2.0 / bh, 104 | a2: a0, 105 | b1: (2.0 * (k2 - 1.0)) / bh, 106 | b2: (1.0 - rk + k2) / bh, 107 | } 108 | } 109 | 110 | fn bandpass(sample_rate: f64, cutoff: f64, res: f64) -> Coeffs { 111 | 112 | let (k2, rk, bh) = Self::common(sample_rate, cutoff, res); 113 | 114 | Coeffs { 115 | a0: rk / bh, 116 | a1: 0.0, 117 | a2: -rk / bh, 118 | b1: (2.0 * (k2 - 1.0)) / bh, 119 | b2: (1.0 - rk + k2) / bh, 120 | } 121 | } 122 | 123 | fn bandstop(sample_rate: f64, cutoff: f64, res: f64) -> Coeffs { 124 | 125 | let (k2, rk, bh) = Self::common(sample_rate, cutoff, res); 126 | 127 | let a0: f64 = (1.0 + k2) / bh; 128 | let a1: f64 = (2.0 * (k2 - 1.0)) / bh; 129 | 130 | Coeffs { 131 | a0: a0, 132 | a1: a1, 133 | a2: a0, 134 | b1: a1, 135 | b2: (1.0 - rk + k2) / bh, 136 | } 137 | } 138 | } 139 | 140 | #[derive(Debug)] 141 | pub struct IIR { 142 | mode: Mode, 143 | slope: Slope, 144 | sample_rate: f64, 145 | cutoff: f64, 146 | res: f64, 147 | enabled: bool, 148 | pub coeff: Coeffs, 149 | d1: f64, d2: f64, d3: f64, d4: f64, 150 | invalid_coeffs: bool, 151 | invalid_delays: bool, 152 | } 153 | 154 | impl IIR { 155 | pub fn new(mode: Mode, slope: Slope, sample_rate: f64, cutoff: f64, res: f64) -> IIR { 156 | assert!(sample_rate > 0.0); 157 | assert!(cutoff >= 0.0); 158 | assert!(res >= 0.0); 159 | 160 | let mut f = IIR { 161 | mode: mode, 162 | slope: slope, 163 | sample_rate: sample_rate, 164 | cutoff: limit_cutoff(cutoff, sample_rate), 165 | res: res, 166 | enabled: true, 167 | coeff: Coeffs::default(), 168 | d1: 0.0, d2: 0.0, d3: 0.0, d4: 0.0, 169 | invalid_coeffs: true, 170 | invalid_delays: true, 171 | }; 172 | f.update_coeffs(); 173 | f 174 | } 175 | 176 | pub fn bypass(sample_rate: f64) -> IIR { 177 | IIR::new(Mode::ByPass, Slope::Slope12, sample_rate, 0.0, 0.0) 178 | } 179 | 180 | pub fn lowpass12(sample_rate: f64, cutoff: f64, res: f64) -> IIR { 181 | IIR::new(Mode::LowPass, Slope::Slope12, sample_rate, cutoff, res) 182 | } 183 | 184 | pub fn highpass12(sample_rate: f64, cutoff: f64, res: f64) -> IIR { 185 | IIR::new(Mode::HighPass, Slope::Slope12, sample_rate, cutoff, res) 186 | } 187 | 188 | pub fn bandpass12(sample_rate: f64, cutoff: f64, res: f64) -> IIR { 189 | IIR::new(Mode::BandPass, Slope::Slope12, sample_rate, cutoff, res) 190 | } 191 | 192 | pub fn bandstop12(sample_rate: f64, cutoff: f64, res: f64) -> IIR { 193 | IIR::new(Mode::BandStop, Slope::Slope12, sample_rate, cutoff, res) 194 | } 195 | 196 | pub fn lowpass24(sample_rate: f64, cutoff: f64, res: f64) -> IIR { 197 | IIR::new(Mode::LowPass, Slope::Slope24, sample_rate, cutoff, res) 198 | } 199 | 200 | pub fn highpass24(sample_rate: f64, cutoff: f64, res: f64) -> IIR { 201 | IIR::new(Mode::HighPass, Slope::Slope24, sample_rate, cutoff, res) 202 | } 203 | 204 | pub fn bandpass24(sample_rate: f64, cutoff: f64, res: f64) -> IIR { 205 | IIR::new(Mode::BandPass, Slope::Slope24, sample_rate, cutoff, res) 206 | } 207 | 208 | pub fn bandstop24(sample_rate: f64, cutoff: f64, res: f64) -> IIR { 209 | IIR::new(Mode::BandStop, Slope::Slope24, sample_rate, cutoff, res) 210 | } 211 | 212 | fn update_coeffs(&mut self) { 213 | self.coeff = match self.mode { 214 | Mode::ByPass => Coeffs::zero(), 215 | Mode::LowPass => Coeffs::lowpass(self.sample_rate, self.cutoff, self.res), 216 | Mode::HighPass => Coeffs::highpass(self.sample_rate, self.cutoff, self.res), 217 | Mode::BandPass => Coeffs::bandpass(self.sample_rate, self.cutoff, self.res), 218 | Mode::BandStop => Coeffs::bandstop(self.sample_rate, self.cutoff, self.res), 219 | }; 220 | self.invalid_coeffs = false; 221 | } 222 | 223 | fn reset_delays(&mut self) { 224 | self.d1 = 0.0; self.d2 = 0.0; self.d3 = 0.0; self.d4 = 0.0; 225 | self.invalid_delays = false; 226 | } 227 | } 228 | 229 | impl Filter for IIR { 230 | fn reset(&mut self) { 231 | self.reset_delays(); 232 | } 233 | 234 | fn set_enabled(&mut self, enabled: bool) { 235 | if self.enabled != enabled { 236 | self.enabled = enabled; 237 | self.invalid_delays = true; 238 | } 239 | } 240 | 241 | fn set_mode(&mut self, mode: Mode) { 242 | self.mode = mode; 243 | self.invalid_coeffs = true; 244 | self.invalid_delays = true; 245 | } 246 | 247 | fn set_slope(&mut self, slope: Slope) { 248 | self.slope = slope; 249 | self.invalid_coeffs = true; 250 | self.invalid_delays = true; 251 | } 252 | 253 | fn set_cutoff(&mut self, cutoff: f64) { 254 | if (self.cutoff - cutoff).abs() >= CUTOFF_DELTA { 255 | self.cutoff = limit_cutoff(cutoff, self.sample_rate); 256 | self.invalid_coeffs = true; 257 | } 258 | } 259 | 260 | fn set_resonance(&mut self, res: f64) { 261 | if self.res != res { 262 | self.res = res; 263 | self.invalid_coeffs = true; 264 | } 265 | } 266 | 267 | fn process(&mut self, signal: f64) -> f64 { 268 | if self.enabled { 269 | if self.invalid_coeffs { 270 | if self.invalid_delays { 271 | self.reset_delays(); 272 | } 273 | 274 | self.update_coeffs(); 275 | } 276 | 277 | let Coeffs { a0, a1, a2, b1, b2 } = self.coeff; 278 | 279 | let value = match self.slope { 280 | Slope::Slope12 => { 281 | let out = a0 * signal + self.d1; 282 | self.d1 = self.d2 + a1 * signal - b1 * out; 283 | self.d2 = a2 * signal - b2 * out; 284 | self.d3 = 0.0; 285 | self.d4 = 0.0; 286 | out 287 | }, 288 | 289 | Slope::Slope24 => { 290 | let out = a0 * signal + self.d1; 291 | self.d1 = self.d2 + a1 * signal - b1 * out; 292 | self.d2 = a2 * signal - b2 * out; 293 | let signal = out; 294 | let out = a0 * signal + self.d3; 295 | self.d3 = self.d4 + a1 * signal - b1 * out; 296 | self.d4 = a2 * signal - b2 * out; 297 | out 298 | } 299 | }; 300 | value 301 | } 302 | else { 303 | signal 304 | } 305 | } 306 | } 307 | -------------------------------------------------------------------------------- /core/src/filter/mod.rs: -------------------------------------------------------------------------------- 1 | 2 | #[derive(Debug)] 3 | pub enum Mode { 4 | ByPass = 0, 5 | LowPass, 6 | HighPass, 7 | BandPass, 8 | BandStop 9 | } 10 | 11 | #[derive(Debug)] 12 | pub enum Slope { 13 | Slope12 = 0, 14 | Slope24 15 | } 16 | 17 | pub trait Filter { 18 | fn reset(&mut self); 19 | fn set_enabled(&mut self, enabled: bool); 20 | fn set_mode(&mut self, mode: Mode); 21 | fn set_slope(&mut self, slope: Slope); 22 | fn set_cutoff(&mut self, cutoff: f64); 23 | fn set_resonance(&mut self, res: f64); 24 | fn process(&mut self, signal: f64) -> f64; 25 | } 26 | 27 | pub mod iir; 28 | -------------------------------------------------------------------------------- /core/src/freq.rs: -------------------------------------------------------------------------------- 1 | 2 | pub static KEY_FREQ: [f64; 128] = [ 3 | 8.176, 8.662, 9.177, 9.723, 10.301, 10.913, 11.562, 12.250, 12.978, 13.750, 14.568, 15.434, 4 | 16.352, 17.324, 18.354, 19.445, 20.602, 21.827, 23.125, 24.500, 25.957, 27.500, 29.135, 30.868, 5 | 32.703, 34.648, 36.708, 38.891, 41.203, 43.654, 46.249, 48.999, 51.913, 55.000, 58.270, 61.735, 6 | 65.406, 69.296, 73.416, 77.782, 82.407, 87.307, 92.499, 97.999, 103.826, 110.000, 116.541, 123.471, 7 | 130.813, 138.591, 146.832, 155.563, 164.814, 174.614, 184.997, 195.998, 207.652, 220.000, 233.082, 246.942, 8 | 261.626, 277.183, 293.665, 311.127, 329.628, 349.228, 369.994, 391.995, 415.305, 440.000, 466.164, 493.883, 9 | 523.251, 554.365, 587.330, 622.254, 659.255, 698.456, 739.989, 783.991, 830.609, 880.000, 932.328, 987.767, 10 | 1046.502, 1108.731, 1174.659, 1244.508, 1318.510, 1396.913, 1479.978, 1567.982, 1661.219, 1760.000, 1864.655, 1975.533, 11 | 2093.005, 2217.461, 2349.318, 2489.016, 2637.020, 2793.826, 2959.955, 3135.963, 3322.438, 3520.000, 3729.310, 3951.066, 12 | 4186.009, 4434.922, 4698.636, 4978.032, 5274.041, 5587.652, 5919.911, 6271.927, 6644.875, 7040.000, 7458.620, 7902.133, 13 | 8372.018, 8869.844, 9397.273, 9956.063, 10548.082, 11175.303, 11839.822, 12543.854]; 14 | 15 | pub fn freq_from_key(key: u8) -> f64 { 16 | 440.0f64 * (2_f64).powf((key as f64 - 69.0) / 12.0) 17 | } 18 | 19 | pub fn key_from_freq(freq: f64) -> u8 { 20 | 69 + (12.0 * (freq / 440.0).log2().round()) as u8 21 | } 22 | 23 | pub fn pitch_scale(octaves: f64, semitones: f64, detune: f64) -> f64 { 24 | (2_f64).powf((octaves * 1200.0 + semitones * 100.0 + detune) / 1200.0) 25 | } 26 | -------------------------------------------------------------------------------- /core/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! HeroSynth core library 3 | //! 4 | 5 | pub mod types; 6 | 7 | pub mod freq; 8 | 9 | pub mod wavetable; 10 | 11 | pub mod oscillator; 12 | 13 | pub mod filter; 14 | 15 | pub mod panning; 16 | -------------------------------------------------------------------------------- /core/src/oscillator.rs: -------------------------------------------------------------------------------- 1 | use std::f64::consts::PI; 2 | 3 | use freq; 4 | use wavetable::Wavetable; 5 | 6 | #[derive(Clone, Debug)] 7 | pub struct Oscillator { 8 | is_enabled: bool, 9 | 10 | wavetable: Wavetable, 11 | 12 | is_free_phase: bool, // When true, the phase is not reset to the initial_phase, but continues from where it was 13 | initial_phase: f64, // The initial phase for the wave in radians 14 | 15 | freq_to_table_incr: f64, 16 | table_incr: f64, 17 | table_offset: f64, 18 | 19 | amplitude: f64, // Oscillator signal amplitude 20 | amp_mod: f64, // Amplitude modulation 21 | 22 | base_frequency: f64, // Oscillator base frequency 23 | octaves: f64, // Number of octaves to shift from the base_frequency. Aka Ratio 24 | semitones: f64, // Number of semitones to shift from the base_frequency. Aka Pitch 25 | detune: f64, // Fine shift from the base_frequency 26 | 27 | frequency: f64, // Calculated from base_frequency, octaves, semitones and detune 28 | phase_mod: f64, // Phase modulation calculated from frequency and freq_mod 29 | } 30 | 31 | impl Default for Oscillator { 32 | fn default() -> Self { 33 | Oscillator { 34 | is_enabled: true, 35 | wavetable: Wavetable::default(), 36 | is_free_phase: false, 37 | initial_phase: 0.0, 38 | freq_to_table_incr: 0.0, 39 | table_incr: 0.0, 40 | table_offset: 0.0, 41 | amplitude: 1.0, 42 | amp_mod: 1.0, 43 | base_frequency: 440.0, 44 | octaves: 0.0, 45 | semitones: 0.0, 46 | detune: 0.0, 47 | frequency: 0.0, 48 | phase_mod: 0.0 49 | } 50 | } 51 | } 52 | 53 | impl Oscillator { 54 | pub fn new(sample_rate: f64, wavetable: Wavetable, freq: f64) -> Oscillator { 55 | let wt_size = wavetable.size() as f64; 56 | let mut o = Oscillator { 57 | wavetable: wavetable, 58 | freq_to_table_incr: wt_size / sample_rate, 59 | base_frequency: freq, 60 | ..Default::default() 61 | }; 62 | o.init(); 63 | o 64 | } 65 | 66 | pub fn from_sample_rate(sample_rate: f64) -> Oscillator { 67 | let mut o = Oscillator::default(); 68 | let wt_size = o.wavetable.size() as f64; 69 | o.freq_to_table_incr = wt_size / sample_rate; 70 | o.init(); 71 | o 72 | } 73 | 74 | pub fn from_wavetable(sample_rate: f64, wavetable: Wavetable) -> Oscillator { 75 | let wt_size = wavetable.size() as f64; 76 | let mut o = Oscillator { 77 | wavetable: wavetable, 78 | freq_to_table_incr: wt_size / sample_rate, 79 | ..Default::default() 80 | }; 81 | o.init(); 82 | o 83 | } 84 | 85 | fn init(&mut self) { 86 | self.reset_phase(); 87 | self.update_frequency(); 88 | } 89 | 90 | pub fn reset(&mut self) { 91 | if !self.is_free_phase { 92 | self.reset_phase(); 93 | } 94 | } 95 | 96 | fn reset_phase(&mut self) { 97 | self.table_offset = (self.initial_phase / (2.0 * PI)) * (self.wavetable.size() as f64); 98 | } 99 | 100 | fn update_frequency(&mut self) { 101 | let pitch_scale = freq::pitch_scale(self.octaves, self.semitones, self.detune); 102 | self.frequency = self.base_frequency * pitch_scale; 103 | if self.frequency < 0.0 { 104 | self.frequency = 0.0; 105 | } 106 | self.table_incr = self.frequency * self.freq_to_table_incr; 107 | } 108 | 109 | pub fn set_enabled(&mut self, is_enabled: bool) { 110 | self.is_enabled = is_enabled; 111 | } 112 | 113 | pub fn is_enabled(&self) -> bool { 114 | self.is_enabled 115 | } 116 | 117 | pub fn set_free_phase(&mut self, is_free_phase: bool) { 118 | self.is_free_phase = is_free_phase; 119 | } 120 | 121 | pub fn is_free_phase(&self) -> bool { 122 | self.is_free_phase 123 | } 124 | 125 | pub fn set_initial_phase(&mut self, initial_phase: f64) { 126 | self.initial_phase = initial_phase; 127 | self.update_frequency(); 128 | } 129 | 130 | pub fn get_initial_phase(&self) -> f64 { 131 | self.initial_phase 132 | } 133 | 134 | pub fn set_octaves(&mut self, octaves: f64) { 135 | self.octaves = octaves; 136 | self.update_frequency(); 137 | } 138 | 139 | pub fn get_octaves(&self) -> f64 { 140 | self.octaves 141 | } 142 | 143 | pub fn set_semitones(&mut self, semitones: f64) { 144 | self.semitones = semitones; 145 | self.update_frequency(); 146 | } 147 | 148 | pub fn get_semitones(&self) -> f64 { 149 | self.semitones 150 | } 151 | 152 | pub fn set_detune(&mut self, detune: f64) { 153 | self.detune = detune; 154 | self.update_frequency(); 155 | } 156 | 157 | pub fn get_detune(&self) -> f64 { 158 | self.detune 159 | } 160 | 161 | pub fn set_amplitude(&mut self, value: f64) { 162 | self.amplitude = value; 163 | } 164 | 165 | pub fn set_amplitude_modulation(&mut self, value: f64) { 166 | self.amp_mod = value; 167 | } 168 | 169 | pub fn set_base_frequency(&mut self, freq: f64) { 170 | self.base_frequency = freq; 171 | self.update_frequency(); 172 | } 173 | 174 | pub fn get_base_frequency(&self) -> f64 { 175 | self.base_frequency 176 | } 177 | 178 | pub fn set_freq_modulation(&mut self, value: f64) { 179 | self.phase_mod = value * self.freq_to_table_incr; 180 | } 181 | 182 | pub fn process(&mut self) -> f64 { 183 | let wt_size = self.wavetable.size() as f64; 184 | if self.table_offset < 0.0 { 185 | self.table_offset = wt_size - (self.table_offset.abs() % wt_size); 186 | // self.table_offset += wt_size; 187 | } else if self.table_offset >= wt_size { 188 | self.table_offset = self.table_offset % wt_size; 189 | // self.table_offset -= wt_size; 190 | } 191 | 192 | let mut value = 0.0f64; 193 | if self.is_enabled && self.amplitude > 0.0 { 194 | value = self.amplitude * self.amp_mod * self.wavetable.value(self.table_offset); 195 | } 196 | 197 | self.table_offset += self.table_incr + self.phase_mod; 198 | 199 | value 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /core/src/panning.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! Non linear panning using a wavetable 3 | //! 4 | 5 | use wavetable::{self, Wavetable}; 6 | 7 | #[derive(Debug)] 8 | pub struct Panning { 9 | left: f64, 10 | right: f64, 11 | value: f64, 12 | wavetable: Wavetable 13 | } 14 | 15 | impl Default for Panning { 16 | fn default() -> Self { 17 | Panning { 18 | left: 0.5, 19 | right: 0.5, 20 | value: 0.0, 21 | wavetable: Wavetable::from_stock(wavetable::Stock::Sin) 22 | } 23 | } 24 | } 25 | 26 | impl Panning { 27 | pub fn new(value: f64) -> Panning { 28 | let mut p = Panning::default(); 29 | p.set_value(value); 30 | p 31 | } 32 | 33 | pub fn set_value(&mut self, value: f64) { 34 | if self.value != value { 35 | let wt_size = self.wavetable.size() as f64; 36 | self.left = self.wavetable.value(((1.0 - value) / 8.0) * wt_size); 37 | self.right = self.wavetable.value(((1.0 + value) / 8.0) * wt_size); 38 | self.value = value; 39 | } 40 | } 41 | 42 | pub fn process(&self, signal: f64) -> (f64, f64) { 43 | (signal * self.left, signal * self.right) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /core/src/types.rs: -------------------------------------------------------------------------------- 1 | pub type Tempo = f64; 2 | 3 | pub const DEFAULT_TEMPO: Tempo = 120 as Tempo; 4 | 5 | pub type SampleRate = f64; 6 | 7 | pub static DEFAULT_SAMPLE_RATE: SampleRate = 44100 as SampleRate; 8 | -------------------------------------------------------------------------------- /core/src/wavetable/mod.rs: -------------------------------------------------------------------------------- 1 | mod sin; 2 | mod saw; 3 | 4 | use std::fmt; 5 | 6 | pub enum Stock { 7 | Sin = 0, 8 | Saw, 9 | } 10 | 11 | impl Stock { 12 | pub fn from_name(name: &str) -> Option { 13 | match name { 14 | "sin" => Some(Stock::Sin), 15 | "saw" => Some(Stock::Saw), 16 | _ => None 17 | } 18 | } 19 | } 20 | 21 | pub struct Wavetable { 22 | data: Vec, 23 | } 24 | 25 | impl fmt::Debug for Wavetable { 26 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 27 | write!(f, "Wavetable({})", self.data.len()) 28 | } 29 | } 30 | 31 | impl Clone for Wavetable { 32 | fn clone(&self) -> Self { Wavetable { data: self.data.clone() } } 33 | } 34 | 35 | impl Default for Wavetable { 36 | fn default() -> Self { 37 | Wavetable { data: sin::LUT.to_vec() } 38 | } 39 | } 40 | 41 | impl Wavetable { 42 | pub fn new(data: Vec) -> Wavetable { 43 | Wavetable { 44 | data: data 45 | } 46 | } 47 | 48 | pub fn from_stock(stock: Stock) -> Wavetable { 49 | match stock { 50 | Stock::Sin => Wavetable { data: sin::LUT.to_vec() }, 51 | Stock::Saw => Wavetable { data: saw::LUT.to_vec() }, 52 | } 53 | } 54 | 55 | pub fn size(&self) -> usize { 56 | return self.data.len(); 57 | } 58 | 59 | pub fn value(&self, offset: f64) -> f64 { 60 | let data_len = self.data.len(); 61 | let pos: usize = offset.floor() as usize; 62 | assert!(pos < data_len); 63 | let value = self.data[pos]; 64 | 65 | let next_pos: usize = (pos + 1) % data_len; 66 | let next_value = self.data[next_pos]; 67 | 68 | let diff = next_value - value; 69 | let fraction = offset - (pos as f64); 70 | return value + diff * fraction; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /studio/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hero_studio" 3 | version = "0.1.0" 4 | authors = ["Christian Perez-Llamas"] 5 | 6 | [dependencies] 7 | docopt = "0.7.0" 8 | rand = "0.3.15" 9 | 10 | portaudio = "0.7.0" 11 | portmidi = "^0.2" 12 | rosc = "0.1.5" 13 | tokio-core = "0.1.4" 14 | futures = "0.1.10" 15 | 16 | hero_core = { path = "../core" } 17 | hero_synth = { path = "../synth" } 18 | -------------------------------------------------------------------------------- /studio/src/audio/buffers.rs: -------------------------------------------------------------------------------- 1 | use std::ops::{Index, IndexMut}; 2 | 3 | use audio::processing::{AudioInputBuffer, AudioOutputBuffer}; 4 | 5 | pub struct DeinterlacedOutputBuffer { 6 | buffer: *mut f32 7 | } 8 | 9 | impl DeinterlacedOutputBuffer { 10 | 11 | pub fn from_left_channel(buffer: &mut [f32]) -> DeinterlacedOutputBuffer { 12 | DeinterlacedOutputBuffer { 13 | buffer: buffer as *mut _ as *mut f32 14 | } 15 | } 16 | 17 | pub fn from_right_channel(buffer: &mut [f32]) -> DeinterlacedOutputBuffer { 18 | let buffer = buffer as *mut _ as *mut f32; 19 | DeinterlacedOutputBuffer { 20 | buffer: unsafe { buffer.offset(1) } 21 | } 22 | } 23 | } 24 | 25 | impl Index for DeinterlacedOutputBuffer { 26 | type Output = f32; 27 | 28 | fn index<'b>(&'b self, index: usize) -> &f32 { 29 | unsafe { &*self.buffer.offset((index * 2) as isize) } 30 | } 31 | } 32 | 33 | impl IndexMut for DeinterlacedOutputBuffer { 34 | 35 | fn index_mut<'b>(&'b mut self, index: usize) -> &mut f32 { 36 | unsafe { &mut *self.buffer.offset((index * 2) as isize) } 37 | } 38 | } 39 | 40 | impl AudioInputBuffer for DeinterlacedOutputBuffer {} 41 | impl AudioOutputBuffer for DeinterlacedOutputBuffer {} 42 | 43 | pub struct DeinterlacedOutputBuffers { 44 | pub left: DeinterlacedOutputBuffer, 45 | pub right: DeinterlacedOutputBuffer 46 | } 47 | 48 | impl DeinterlacedOutputBuffers { 49 | pub fn from(buffer: &mut [f32]) -> DeinterlacedOutputBuffers { 50 | DeinterlacedOutputBuffers { 51 | left: DeinterlacedOutputBuffer::from_left_channel(buffer), 52 | right: DeinterlacedOutputBuffer::from_right_channel(buffer) 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /studio/src/audio/mod.rs: -------------------------------------------------------------------------------- 1 | mod buffers; 2 | pub mod processing; 3 | pub mod types; 4 | 5 | pub use self::types::Timestamp; 6 | 7 | use std::sync::{Arc, Mutex}; 8 | 9 | use portaudio; 10 | 11 | use hero_core::types::SampleRate; 12 | 13 | use engine::Engine; 14 | 15 | use self::processing::{ProcessingArgs, Processor}; 16 | use self::buffers::DeinterlacedOutputBuffers; 17 | 18 | type PortAudioStream = portaudio::Stream>; 19 | 20 | pub const SAMPLE_RATE: SampleRate = 44100 as SampleRate; 21 | const INTERLEAVED: bool = true; 22 | const CHANNELS: u32 = 2; 23 | const FRAMES: u32 = 400; 24 | 25 | pub fn audio_start<'a>(pa_ctx: &'a portaudio::PortAudio, engine: Arc>) -> Result { 26 | 27 | let sample_rate = SAMPLE_RATE; 28 | 29 | //let pa_ctx = try!(portaudio::PortAudio::new()); 30 | 31 | let default_output = try!(pa_ctx.default_output_device()); 32 | let output_info = try!(pa_ctx.device_info(default_output)); 33 | 34 | // Construct the output stream parameters. 35 | let latency = output_info.default_low_output_latency; 36 | let output_params = portaudio::StreamParameters::::new( 37 | default_output, CHANNELS as i32, INTERLEAVED, latency); 38 | 39 | // Check that the stream format is supported. 40 | try!(pa_ctx.is_output_format_supported(output_params, sample_rate)); 41 | 42 | // Construct the settings with which we'll open our stream. 43 | let settings = portaudio::OutputStreamSettings::new( 44 | output_params, sample_rate, FRAMES); 45 | 46 | let callback = move |portaudio::OutputStreamCallbackArgs { buffer, frames, time, .. }| { 47 | 48 | let timestamp = (time.buffer_dac * 1000000000.0) as Timestamp; 49 | let mut deinterlaced = DeinterlacedOutputBuffers::from(buffer); 50 | let args = ProcessingArgs::new(timestamp, frames, &mut deinterlaced.left, &mut deinterlaced.right); 51 | 52 | let mut locked_engine = engine.lock().unwrap(); // TODO What if it fails ? 53 | locked_engine.process(args); 54 | 55 | portaudio::Continue 56 | }; 57 | 58 | // Construct a stream with input and output sample types of f32. 59 | let mut stream = try!(pa_ctx.open_non_blocking_stream(settings, callback)); 60 | try!(stream.start()); 61 | 62 | Ok(stream) 63 | } 64 | 65 | pub fn audio_close(stream: &mut PortAudioStream) -> Result<(), portaudio::error::Error> { 66 | println!("Stopping and closing the stream ..."); 67 | try!(stream.stop()); 68 | stream.close() 69 | } 70 | -------------------------------------------------------------------------------- /studio/src/audio/processing.rs: -------------------------------------------------------------------------------- 1 | use std::ops::{Index, IndexMut}; 2 | use std::marker::PhantomData; 3 | 4 | use audio::types::Timestamp; 5 | 6 | pub trait AudioInputBuffer: Index {} 7 | 8 | pub trait AudioOutputBuffer: AudioInputBuffer + IndexMut {} 9 | 10 | pub struct ProcessingArgs<'a, S, O> 11 | where O: AudioOutputBuffer + 'a { 12 | 13 | pub timestamp: Timestamp, 14 | pub num_frames: usize, 15 | // pub audio_in_left: &'a mut I, 16 | // pub audio_in_right: &'a mut I, 17 | pub audio_out_left: &'a mut O, 18 | pub audio_out_right: &'a mut O, 19 | phantom1: PhantomData<&'a O> 20 | } 21 | 22 | impl<'a, S, O> ProcessingArgs<'a, S, O> 23 | where O: AudioOutputBuffer + 'a { 24 | 25 | pub fn new(timestamp: Timestamp, 26 | num_frames: usize, 27 | // audio_in_left: &'a mut I, 28 | // audio_in_right: &'a mut I, 29 | audio_out_left: &'a mut O, 30 | audio_out_right: &'a mut O) -> ProcessingArgs<'a, S, O> { 31 | 32 | ProcessingArgs { 33 | timestamp: timestamp, 34 | num_frames: num_frames, 35 | // audio_in_left: audio_in_left, 36 | // audio_in_right: audio_in_right, 37 | audio_out_left: audio_out_left, 38 | audio_out_right: audio_out_right, 39 | phantom1: PhantomData 40 | } 41 | } 42 | } 43 | 44 | pub trait Processor<'a, S, O> 45 | where O: AudioOutputBuffer { 46 | 47 | fn process(&mut self, args: ProcessingArgs<'a, S, O>); 48 | } 49 | -------------------------------------------------------------------------------- /studio/src/audio/types.rs: -------------------------------------------------------------------------------- 1 | /// Timestamp in nanoseconds 2 | pub type Timestamp = u64; 3 | -------------------------------------------------------------------------------- /studio/src/control/mod.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | use std::sync::atomic::{Ordering, AtomicBool}; 3 | use std::sync::mpsc::{Sender, Receiver}; 4 | use std::thread::{self, JoinHandle}; 5 | 6 | use rosc; 7 | 8 | use midi; 9 | use engine; 10 | use engine::Timestamp; 11 | 12 | pub struct Control { 13 | running: Arc, 14 | midi_join_handler: Option>, 15 | osc_join_handler: Option>, 16 | } 17 | 18 | impl Control { 19 | pub fn new() -> Self { 20 | Control { 21 | running: Arc::new(AtomicBool::new(false)), 22 | midi_join_handler: None, 23 | osc_join_handler: None, 24 | } 25 | } 26 | 27 | pub fn start(&mut self, 28 | midi_input_rx: Receiver, 29 | osc_input_rx: Receiver, 30 | engine_input_tx: Sender, 31 | engine_output_rx: Receiver) { 32 | 33 | let midi_events_tx = engine_input_tx.clone(); 34 | self.midi_join_handler = Some(thread::spawn(move || { 35 | Control::midi_input(midi_input_rx, midi_events_tx) })); 36 | 37 | let osc_events_tx = engine_input_tx.clone(); 38 | self.osc_join_handler = Some(thread::spawn(move || { 39 | Control::osc_input(osc_input_rx, osc_events_tx) })); 40 | } 41 | 42 | pub fn stop(&mut self) { 43 | let running = self.running.swap(false, Ordering::Relaxed); 44 | if running { 45 | self.midi_join_handler.take().unwrap().join().ok(); 46 | } 47 | } 48 | 49 | fn midi_input(midi_input_rx: Receiver, 50 | engine_input_tx: Sender) { 51 | 52 | for midi_port_events in midi_input_rx { 53 | let mut engine_events: Vec = Vec::new(); 54 | for midi_event in midi_port_events.events() { 55 | match midi_event.message() { 56 | midi::Message::NoteOn { key, velocity, .. } => { 57 | let velocity = velocity as f64 / 127.0; 58 | let engine_message = engine::Message::NoteOn {key: key as usize, velocity: velocity }; 59 | let engine_event = engine::Event::new(midi_event.timestamp(), engine_message); 60 | engine_events.push(engine_event); 61 | }, 62 | midi::Message::NoteOff { key, velocity, .. } => { 63 | let velocity = velocity as f64 / 127.0; 64 | let engine_message = engine::Message::NoteOff {key: key as usize, velocity: velocity }; 65 | let engine_event = engine::Event::new(midi_event.timestamp(), engine_message); 66 | engine_events.push(engine_event); 67 | }, 68 | _ => {} 69 | } 70 | } 71 | let device = engine::events::Port::Midi(midi_port_events.port().to_string()); 72 | let engine_src_events = engine::PortEvents::new(device, engine_events); 73 | engine_input_tx.send(engine_src_events).unwrap(); 74 | } 75 | } 76 | 77 | fn osc_input(osc_input_rx: Receiver, 78 | engine_input_tx: Sender) { 79 | 80 | const NOW_TIMESTAMP: Timestamp = 0 as Timestamp; 81 | let default_port: engine::Port = engine::Port::Osc("default".to_string()); 82 | 83 | for osc_packet in osc_input_rx { 84 | let event = engine::Event::new(NOW_TIMESTAMP, engine::Message::Control(osc_packet)); 85 | let src_events = engine::PortEvents::new(default_port.clone(), vec![event]); 86 | engine_input_tx.send(src_events).unwrap(); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /studio/src/engine/events.rs: -------------------------------------------------------------------------------- 1 | use std::collections::btree_map::{self, BTreeMap, Entry}; 2 | 3 | use rosc::OscPacket; 4 | 5 | use engine::types::Timestamp; 6 | 7 | #[derive(Debug, Clone, PartialEq)] 8 | pub enum Message { 9 | NoteOn { key: usize, velocity: f64 }, 10 | NoteOff { key: usize, velocity: f64 }, 11 | Control(OscPacket), 12 | } 13 | 14 | #[derive(Debug, Clone)] 15 | pub struct Event { 16 | timestamp: Timestamp, 17 | message: Message 18 | } 19 | 20 | impl Event { 21 | pub fn new(timestamp: Timestamp, message: Message) -> Event { 22 | Event { 23 | timestamp: timestamp, 24 | message: message 25 | } 26 | } 27 | 28 | #[inline] 29 | pub fn timestamp(&self) -> Timestamp { 30 | self.timestamp 31 | } 32 | 33 | #[inline] 34 | pub fn message(&self) -> &Message { 35 | &self.message 36 | } 37 | } 38 | 39 | #[derive(Debug, Clone)] 40 | pub enum Port { 41 | Midi(String), 42 | Osc(String), 43 | OscAll, 44 | } 45 | 46 | #[derive(Debug, Clone)] 47 | pub struct PortEvents { 48 | port: Port, 49 | events: Vec 50 | } 51 | 52 | impl PortEvents { 53 | pub fn new(port: Port, events: Vec) -> Self { 54 | PortEvents { 55 | port: port, 56 | events: events 57 | } 58 | } 59 | 60 | #[inline] 61 | pub fn port(&self) -> &Port { 62 | &self.port 63 | } 64 | 65 | #[inline] 66 | pub fn events(&self) -> &[Event] { 67 | &self.events 68 | } 69 | } 70 | 71 | pub struct EventsBuffer(BTreeMap>); 72 | 73 | impl EventsBuffer { 74 | pub fn new() -> Self { 75 | EventsBuffer(BTreeMap::new()) 76 | } 77 | 78 | pub fn is_empty(&self) -> bool { 79 | self.0.is_empty() 80 | } 81 | 82 | pub fn len(&self) -> usize { 83 | self.0.len() 84 | } 85 | 86 | pub fn push(&mut self, event: &Event) { 87 | let message = event.message().clone(); 88 | match self.0.entry(event.timestamp()) { 89 | Entry::Occupied(ref mut entry) => entry.get_mut().push(message), 90 | Entry::Vacant(entry) => { entry.insert(vec![message]); } 91 | } 92 | } 93 | 94 | pub fn split(&mut self, until: Timestamp) -> EventsBuffer { 95 | let mut map = self.0.to_owned(); 96 | self.0 = map.split_off(&until); 97 | EventsBuffer(map) 98 | } 99 | 100 | pub fn iter(&self) -> Iter { 101 | Iter(self.0.iter()) 102 | } 103 | 104 | // pub fn pop_until(&mut self, until: Timestamp) -> Vec { 105 | // let events = Vec::new(); 106 | // let iter = self.0.iter(); 107 | // while let Some((timestamp, messages)) = iter.next() { 108 | // events.extend(messages.iter().map(|msg| Event::new(timestamp, msg.clone()))); 109 | // } 110 | // events 111 | // } 112 | } 113 | 114 | pub struct Iter<'a>(btree_map::Iter<'a, Timestamp, Vec>); 115 | 116 | impl<'a> Iterator for Iter<'a> { 117 | type Item = (&'a Timestamp, &'a Vec); 118 | 119 | fn next(&mut self) -> Option<(&'a Timestamp, &'a Vec)> { 120 | self.0.next() 121 | } 122 | } 123 | 124 | #[cfg(test)] 125 | mod tests { 126 | use super::*; 127 | 128 | #[test] 129 | fn events_buffer_len() { 130 | let mut eb = EventsBuffer::new(); 131 | let evt = Event::new(1, Message::NoteOn {key: 0, velocity: 0.1}); 132 | assert_eq!(eb.len(), 0); 133 | assert_eq!(eb.is_empty(), true); 134 | eb.push(&evt); 135 | assert_eq!(eb.len(), 1); 136 | assert_eq!(eb.is_empty(), false); 137 | } 138 | 139 | #[test] 140 | fn events_buffer_push() { 141 | let mut eb = EventsBuffer::new(); 142 | let msg1 = Message::NoteOn {key: 10, velocity: 0.1}; 143 | let msg2 = Message::NoteOn {key: 20, velocity: 0.1}; 144 | let msg3 = Message::NoteOn {key: 30, velocity: 0.1}; 145 | eb.push(&Event::new(1, msg1.clone())); 146 | eb.push(&Event::new(2, msg2.clone())); 147 | eb.push(&Event::new(1, msg3.clone())); 148 | assert_eq!(eb.0.get(&1), Some(&vec![msg1, msg3])); 149 | assert_eq!(eb.0.get(&2), Some(&vec![msg2])); 150 | } 151 | 152 | #[test] 153 | fn events_buffer_split() { 154 | let mut eb = EventsBuffer::new(); 155 | let msg1 = Message::NoteOn {key: 10, velocity: 0.1}; 156 | let msg2 = Message::NoteOn {key: 20, velocity: 0.1}; 157 | let msg3 = Message::NoteOn {key: 30, velocity: 0.1}; 158 | eb.push(&Event::new(1, msg1.clone())); 159 | eb.push(&Event::new(3, msg2.clone())); 160 | eb.push(&Event::new(1, msg3.clone())); 161 | let low = eb.split(2); 162 | assert_eq!(low.0.get(&1), Some(&vec![msg1, msg3])); 163 | assert_eq!(eb.0.get(&3), Some(&vec![msg2])); 164 | } 165 | 166 | #[test] 167 | fn events_buffer_iter() { 168 | let mut eb = EventsBuffer::new(); 169 | let msg1 = Message::NoteOn {key: 10, velocity: 0.1}; 170 | let msg2 = Message::NoteOn {key: 20, velocity: 0.1}; 171 | let msg3 = Message::NoteOn {key: 30, velocity: 0.1}; 172 | eb.push(&Event::new(1, msg1.clone())); 173 | eb.push(&Event::new(3, msg2.clone())); 174 | eb.push(&Event::new(1, msg3.clone())); 175 | let mut it = eb.iter(); 176 | assert_eq!(it.next(), Some((&1, &vec![msg1, msg3]))); 177 | assert_eq!(it.next(), Some((&3, &vec![msg2]))); 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /studio/src/engine/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod events; 2 | pub mod types; 3 | 4 | use std::sync::mpsc::{Sender, Receiver}; 5 | use std::sync::atomic::{Ordering, AtomicBool}; 6 | use std::sync::{Arc, Mutex}; 7 | use std::thread::{self, JoinHandle}; 8 | 9 | use hero_core::types::{SampleRate, Tempo, DEFAULT_TEMPO}; 10 | use hero_synth::synth::Synth as HeroSynth; 11 | 12 | use audio::processing::{AudioOutputBuffer, ProcessingArgs, Processor}; 13 | 14 | use rosc::OscPacket; 15 | 16 | pub use self::types::Timestamp; 17 | pub use self::events::{Message, Event, Port, PortEvents}; 18 | use self::events::EventsBuffer; 19 | 20 | 21 | pub struct Engine { 22 | sample_rate: SampleRate, 23 | tempo: Tempo, 24 | running: Arc, 25 | events_input_join_handler: Option>, 26 | input_events: Arc>, 27 | events_sender: Option>, 28 | hero_synth: HeroSynth, 29 | } 30 | 31 | unsafe impl Send for Engine {} 32 | 33 | impl Engine { 34 | pub fn new(sample_rate: SampleRate) -> Engine { 35 | let hero_synth = HeroSynth::new(sample_rate); 36 | 37 | Engine { 38 | sample_rate: sample_rate, 39 | tempo: DEFAULT_TEMPO, 40 | running: Arc::new(AtomicBool::new(false)), 41 | 42 | events_input_join_handler: None, 43 | input_events: Arc::new(Mutex::new(EventsBuffer::new())), 44 | events_sender: None, 45 | 46 | hero_synth: hero_synth 47 | } 48 | } 49 | 50 | pub fn sample_rate(&self) -> SampleRate { 51 | self.sample_rate 52 | } 53 | 54 | pub fn running(&self) -> bool { 55 | self.running.load(Ordering::Relaxed) 56 | } 57 | 58 | pub fn start(&mut self, events_receiver: Receiver, events_sender: Sender) { 59 | let running = self.running.swap(true, Ordering::Relaxed); 60 | if !running { 61 | let running = self.running.clone(); 62 | let input_events = self.input_events.clone(); 63 | self.events_sender = Some(events_sender); 64 | self.events_input_join_handler = Some(thread::spawn(move || { 65 | Self::events_input_loop(&running, events_receiver, input_events) 66 | })); 67 | } 68 | } 69 | 70 | pub fn stop(&mut self) { 71 | let running = self.running.swap(false, Ordering::Relaxed); 72 | if running { 73 | self.events_input_join_handler.take().unwrap().join().ok(); 74 | } 75 | } 76 | 77 | fn events_input_loop( 78 | running: &AtomicBool, 79 | events_receiver: Receiver, 80 | input_events_mutex: Arc>) { 81 | 82 | while running.load(Ordering::Relaxed) { 83 | for dev_events in events_receiver.iter() { 84 | let mut input_events = input_events_mutex.lock().unwrap(); 85 | for event in dev_events.events() { 86 | input_events.push(event); 87 | println!("{:?}", event); 88 | } 89 | } 90 | } 91 | } 92 | } 93 | 94 | impl<'a, O> Processor<'a, f32, O> for Engine 95 | where O: AudioOutputBuffer { 96 | 97 | fn process(&mut self, args: ProcessingArgs<'a, f32, O>) { 98 | let timestamp = args.timestamp; 99 | let time_delta = 1000000000.0 / self.sample_rate; 100 | let duration = (args.num_frames as f64 * time_delta).ceil() as Timestamp; 101 | let mut process_timestamp = timestamp as f64; 102 | 103 | let mut frame_events = { self.input_events.lock().unwrap().split(timestamp + duration) }; 104 | 105 | for i in 0..args.num_frames { 106 | let next_timestamp = (process_timestamp + time_delta).ceil() as Timestamp; 107 | let proc_events = frame_events.split(next_timestamp); 108 | 109 | for (_timestamp, messages) in proc_events.iter() { 110 | for message in messages.iter() { 111 | match message { 112 | &Message::NoteOn { key, velocity } => self.hero_synth.note_on(key, velocity), 113 | &Message::NoteOff { key, velocity } => self.hero_synth.note_off(key, velocity), 114 | &Message::Control(ref packet) => self.hero_synth.control(packet), 115 | } 116 | } 117 | } 118 | 119 | for sender in self.events_sender.iter() { 120 | let events: Vec = self.hero_synth.output().into_iter().map(|packet| { 121 | Event::new(0 as Timestamp, Message::Control(packet)) 122 | }).collect(); 123 | if !events.is_empty() { 124 | let port_events = PortEvents::new(Port::OscAll, events); 125 | sender.send(port_events).ok(); 126 | } 127 | }; 128 | 129 | let (left, right) = self.hero_synth.process(); 130 | args.audio_out_left[i] = left as f32; 131 | args.audio_out_right[i] = right as f32; 132 | process_timestamp += time_delta; 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /studio/src/engine/types.rs: -------------------------------------------------------------------------------- 1 | /// Timestamp in nanoseconds 2 | pub type Timestamp = u64; 3 | -------------------------------------------------------------------------------- /studio/src/main.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | extern crate portaudio; 4 | extern crate portmidi; 5 | extern crate rosc; 6 | extern crate tokio_core; 7 | extern crate futures; 8 | 9 | extern crate hero_core; 10 | extern crate hero_synth; 11 | 12 | mod audio; 13 | mod midi; 14 | mod osc; 15 | mod engine; 16 | mod control; 17 | 18 | use std::sync::{Arc, Mutex}; 19 | use std::sync::mpsc::{channel, Sender, Receiver}; 20 | 21 | use audio::{SAMPLE_RATE, audio_start, audio_close}; 22 | use midi::Midi; 23 | use osc::Osc; 24 | use engine::Engine; 25 | use control::Control; 26 | 27 | fn main() { 28 | 29 | let (engine_input_tx, engine_input_rx): (Sender, Receiver) = channel(); 30 | let (engine_output_tx, engine_output_rx): (Sender, Receiver) = channel(); 31 | 32 | let mut engine = Engine::new(SAMPLE_RATE); 33 | engine.start(engine_input_rx, engine_output_tx.clone()); 34 | 35 | let engine_mutex = Arc::new(Mutex::new(engine)); 36 | 37 | let (midi_input_tx, midi_input_rx): (Sender, Receiver) = channel(); 38 | 39 | let mut midi = Midi::new(); 40 | midi.start(midi_input_tx); 41 | 42 | let (osc_input_tx, osc_input_rx): (Sender, Receiver) = channel(); 43 | let (osc_output_tx, osc_output_rx): (Sender, Receiver) = channel(); 44 | let mut osc = Osc::new("0.0.0.0:7400"); 45 | osc.start(osc_input_tx, osc_output_rx); 46 | 47 | let mut control = Control::new(); 48 | control.start( 49 | midi_input_rx, osc_input_rx, 50 | engine_input_tx, engine_output_rx); 51 | 52 | let pa_ctx = portaudio::PortAudio::new().unwrap(); 53 | let mut stream = audio_start(&pa_ctx, engine_mutex.clone()).unwrap(); 54 | 55 | // Loop while the non-blocking stream is active. 56 | while let Ok(true) = stream.is_active() { 57 | pa_ctx.sleep(1000); 58 | } 59 | 60 | audio_close(&mut stream).unwrap(); 61 | 62 | control.stop(); 63 | 64 | midi.stop(); 65 | 66 | engine_mutex.lock().unwrap().stop(); 67 | 68 | println!(""); 69 | } 70 | -------------------------------------------------------------------------------- /studio/src/midi/decoder.rs: -------------------------------------------------------------------------------- 1 | use midi::types::{U4, U7}; 2 | use midi::messages::Message; 3 | 4 | pub struct Decoder<'a> { 5 | pos: usize, 6 | start: usize, 7 | sysex_data: Vec, 8 | sysex_decoding: bool, 9 | data: &'a [u8], 10 | } 11 | 12 | impl<'a> Decoder<'a> { 13 | pub fn new(data: &'a [u8]) -> Decoder<'a> { 14 | Decoder { pos: 0, start: 0, sysex_data: Vec::new(), sysex_decoding: false, data: data } 15 | } 16 | 17 | fn unknown(&self, end: usize) -> Message { 18 | Message::Unknown(self.data[self.start .. end].to_vec()) 19 | } 20 | 21 | fn next_data(&mut self) -> Result { 22 | if self.pos < self.data.len() { 23 | let d1 = self.data[self.pos]; 24 | if d1 & 0b10000000 == 0 { 25 | self.pos += 1; 26 | Ok(d1) 27 | } 28 | else { 29 | Err(self.pos) 30 | } 31 | } 32 | else { 33 | Err(self.pos) 34 | } 35 | } 36 | 37 | fn next_data2(&mut self) -> Result<(U7, U7), usize> { 38 | self.next_data().and_then(|d1| self.next_data().and_then(|d2| Ok((d1, d2)))) 39 | } 40 | 41 | fn decode_note(&mut self, channel: U4, is_on: bool) -> Message { 42 | match self.next_data2() { 43 | Ok((key, velocity)) => match is_on { 44 | true => Message::NoteOn { channel: channel, key: key, velocity: velocity }, 45 | false => Message::NoteOff { channel: channel, key: key, velocity: velocity } 46 | }, 47 | Err(end) => self.unknown(end) 48 | } 49 | } 50 | 51 | fn decode_polyphonic_key_pressure(&mut self, channel: U4) -> Message { 52 | match self.next_data2() { 53 | Ok((key, pressure)) => Message::PolyphonicKeyPressure { channel: channel, key: key, value: pressure }, 54 | Err(end) => self.unknown(end) 55 | } 56 | } 57 | 58 | fn decode_control_change(&mut self, channel: U4) -> Message { 59 | match self.next_data2() { 60 | Ok((controller, value)) => match controller { 61 | 120 => match value { 62 | 0 => Message::AllSoundOff { channel: channel }, 63 | _ => self.unknown(self.pos) 64 | }, 65 | 121 => Message::ResetAllControllers { channel: channel }, 66 | 122 => match value { 67 | 0 => Message::LocalControlOff { channel: channel }, 68 | 127 => Message::LocalControlOn { channel: channel }, 69 | _ => self.unknown(self.pos) 70 | }, 71 | 123 => match value { 72 | 0 => Message::AllNotesOff { channel: channel }, 73 | _ => self.unknown(self.pos) 74 | }, 75 | 124 => match value { 76 | 0 => Message::OmniModeOff { channel: channel }, 77 | _ => self.unknown(self.pos) 78 | }, 79 | 125 => match value { 80 | 0 => Message::OmniModeOn { channel: channel }, 81 | _ => self.unknown(self.pos) 82 | }, 83 | 126 => Message::MonoModeOn { channel: channel, num_channels: value }, 84 | 127 => match value { 85 | 0 => Message::PolyModeOn { channel: channel }, 86 | _ => self.unknown(self.pos) 87 | }, 88 | _ => Message::ControlChange { channel: channel, controller: controller, value: value } 89 | }, 90 | Err(end) => self.unknown(end) 91 | } 92 | } 93 | 94 | fn decode_program_change(&mut self, channel: U4) -> Message { 95 | match self.next_data() { 96 | Ok(program) => Message::ProgramChange { channel: channel, value: program }, 97 | Err(end) => self.unknown(end) 98 | } 99 | } 100 | 101 | fn decode_channel_pressure(&mut self, channel: U4) -> Message { 102 | match self.next_data() { 103 | Ok(pressure) => Message::ChannelPressure { channel: channel, value: pressure }, 104 | Err(end) => self.unknown(end) 105 | } 106 | } 107 | 108 | fn decode_pitch_bend(&mut self, channel: U4) -> Message { 109 | match self.next_data2() { 110 | Ok((lsb, msb)) => Message::PitchBend { channel: channel, value: (msb << 7) | lsb }, 111 | Err(end) => self.unknown(end) 112 | } 113 | } 114 | 115 | fn decode_mtc_quarter_frame(&mut self) -> Message { 116 | match self.next_data() { 117 | Ok(data) => Message::MTCQuarterFrame { msg_type: (data >> 4) & 0x07, value: data & 0x0f }, 118 | Err(end) => self.unknown(end) 119 | } 120 | } 121 | 122 | fn decode_song_position_pointer(&mut self) -> Message { 123 | match self.next_data2() { 124 | Ok((lsb, msb)) => Message::SongPositionPointer { beats: (msb << 7) | lsb }, 125 | Err(end) => self.unknown(end) 126 | } 127 | } 128 | 129 | fn decode_song_select(&mut self) -> Message { 130 | match self.next_data() { 131 | Ok(song) => Message::SongSelect { song: song }, 132 | Err(end) => self.unknown(end) 133 | } 134 | } 135 | 136 | fn decode_sysex_start(&mut self) -> Option { 137 | if !self.sysex_decoding { 138 | self.sysex_decoding = true; 139 | self.decode_sysex_data() 140 | } 141 | else { 142 | Some(self.unknown(self.pos)) 143 | } 144 | } 145 | 146 | fn decode_sysex_end(&mut self) -> Option { 147 | if self.sysex_decoding { 148 | self.sysex_decoding = false; 149 | let data = self.sysex_data.to_owned(); 150 | self.sysex_data = Vec::new(); 151 | Some(Message::SysEx { data: data }) 152 | } 153 | else { 154 | Some(self.unknown(self.pos)) 155 | } 156 | } 157 | 158 | fn decode_sysex_data(&mut self) -> Option { 159 | let start = self.pos; 160 | let mut pos = start; 161 | while pos < self.data.len() && (self.data[pos] & 0x80) == 0 { pos += 1 } 162 | self.pos = pos; 163 | let data = &self.data[start .. pos]; 164 | self.sysex_data.extend(data); 165 | if pos < self.data.len() { 166 | let status = self.data[pos]; 167 | self.start = self.pos; 168 | self.pos += 1; 169 | self.decode(status) 170 | } 171 | else { 172 | self.sysex_decoding = false; 173 | let mut data = self.sysex_data.to_owned(); 174 | self.sysex_data = Vec::new(); 175 | data.insert(0, 0b11110000); 176 | Some(Message::Unknown(data)) 177 | } 178 | } 179 | 180 | fn decode(&mut self, status: U7) -> Option { 181 | match (status >> 4) & 0x0f { 182 | 0b1000 => Some(self.decode_note(status & 0x0f, false)), 183 | 0b1001 => Some(self.decode_note(status & 0x0f, true)), 184 | 0b1010 => Some(self.decode_polyphonic_key_pressure(status & 0x0f)), 185 | 0b1011 => Some(self.decode_control_change(status & 0x0f)), 186 | 0b1100 => Some(self.decode_program_change(status & 0x0f)), 187 | 0b1101 => Some(self.decode_channel_pressure(status & 0x0f)), 188 | 0b1110 => Some(self.decode_pitch_bend(status & 0x0f)), 189 | 0b1111 => match status & 0x0f { 190 | 0b0000 => self.decode_sysex_start(), 191 | 0b0001 => Some(self.decode_mtc_quarter_frame()), 192 | 0b0010 => Some(self.decode_song_position_pointer()), 193 | 0b0011 => Some(self.decode_song_select()), 194 | 0b0100 => Some(self.unknown(self.pos)), 195 | 0b0101 => Some(self.unknown(self.pos)), 196 | 0b0110 => Some(Message::TuneRequest), 197 | 0b0111 => self.decode_sysex_end(), 198 | 0b1000 => Some(Message::TimingClock), 199 | 0b1001 => Some(self.unknown(self.pos)), 200 | 0b1010 => Some(Message::Start), 201 | 0b1011 => Some(Message::Continue), 202 | 0b1100 => Some(Message::Stop), 203 | 0b1101 => Some(self.unknown(self.pos)), 204 | 0b1110 => Some(Message::ActiveSensing), 205 | 0b1111 => Some(Message::SystemReset), 206 | _ => None // It should never reach this path but the compiler complains otherwise 207 | }, 208 | _ => Some(Message::Unknown(vec![status])) 209 | } 210 | } 211 | } 212 | 213 | impl<'a> Iterator for Decoder<'a> { 214 | type Item = Message; 215 | 216 | fn next(&mut self) -> Option { 217 | if self.sysex_decoding { 218 | self.decode_sysex_data() 219 | } 220 | else { 221 | if self.pos < self.data.len() { 222 | let status = self.data[self.pos]; 223 | self.start = self.pos; 224 | self.pos += 1; 225 | self.decode(status) 226 | } 227 | else { 228 | None 229 | } 230 | } 231 | } 232 | } 233 | 234 | #[cfg(test)] 235 | mod tests { 236 | use super::*; 237 | use midi::messages::Message; 238 | 239 | #[test] 240 | fn decode_empty_vec() { 241 | let data = &Vec::new(); 242 | let mut dec = Decoder::new(data); 243 | assert_eq!(dec.next(), None); 244 | assert_eq!(dec.next(), None); 245 | } 246 | 247 | #[test] 248 | fn next_data_unknown() { 249 | let data = &vec![0b1000_0000, 64, 0b1000_0001, 0b1000_0010, 12]; 250 | let mut dec = Decoder::new(data); 251 | assert_eq!(dec.next(), Some(Message::Unknown(vec![0b1000_0000, 64]))); 252 | assert_eq!(dec.next(), Some(Message::Unknown(vec![0b1000_0001]))); 253 | assert_eq!(dec.next(), Some(Message::Unknown(vec![0b1000_0010, 12]))); 254 | } 255 | 256 | #[test] 257 | fn decode_notes() { 258 | let data = &vec![0b1000_0101u8, 64, 127, 0b1001_1010, 0, 127]; 259 | let mut dec = Decoder::new(data); 260 | assert_eq!(dec.next(), Some(Message::NoteOff { channel: 0b0101, key: 64, velocity: 127 })); 261 | assert_eq!(dec.next(), Some(Message::NoteOn { channel: 0b1010, key: 0, velocity: 127 })); 262 | assert_eq!(dec.next(), None); 263 | } 264 | 265 | #[test] 266 | fn decode_polyphonic_key_pressure() { 267 | let data = &vec![0b1010_0101u8, 64, 127]; 268 | let mut dec = Decoder::new(data); 269 | assert_eq!(dec.next(), Some(Message::PolyphonicKeyPressure { channel: 0b0101, key: 64, value: 127 })); 270 | assert_eq!(dec.next(), None); 271 | } 272 | 273 | #[test] 274 | fn decode_control_change() { 275 | let data = &vec![0b1011_0101u8, 64, 127]; 276 | let mut dec = Decoder::new(data); 277 | assert_eq!(dec.next(), Some(Message::ControlChange { channel: 0b0101, controller: 64, value: 127 })); 278 | assert_eq!(dec.next(), None); 279 | } 280 | 281 | #[test] 282 | fn decode_program_change() { 283 | let data = &vec![0b1100_0101u8, 0b0_1010101]; 284 | let mut dec = Decoder::new(data); 285 | assert_eq!(dec.next(), Some(Message::ProgramChange { channel: 0b0101, value: 0b0_1010101 })); 286 | assert_eq!(dec.next(), None); 287 | } 288 | 289 | #[test] 290 | fn decode_channel_pressure() { 291 | let data = &vec![0b1101_0101u8, 0b0_1010101]; 292 | let mut dec = Decoder::new(data); 293 | assert_eq!(dec.next(), Some(Message::ChannelPressure { channel: 0b0101, value: 0b0_1010101 })); 294 | assert_eq!(dec.next(), None); 295 | } 296 | 297 | #[test] 298 | fn decode_pitch_bend() { 299 | let data = &vec![0b1110_0101u8, 0b0_1010101, 0b0_0101010]; 300 | let mut dec = Decoder::new(data); 301 | assert_eq!(dec.next(), Some(Message::PitchBend { channel: 0b0101, value: 0b0_1010101 })); 302 | assert_eq!(dec.next(), None); 303 | } 304 | 305 | #[test] 306 | fn decode_mtc_quarter_frame() { 307 | let data = &vec![0b1111_0001u8, 0b0_101_1010]; 308 | let mut dec = Decoder::new(data); 309 | assert_eq!(dec.next(), Some(Message::MTCQuarterFrame { msg_type: 0b101, value: 0b1010 })); 310 | assert_eq!(dec.next(), None); 311 | } 312 | 313 | #[test] 314 | fn decode_song_position_pointer() { 315 | let data = &vec![0b1111_0010u8, 0b0_1010101, 0b0_0101010, 0b1111_0010u8, 0b0_0101010, 0b0_1010101]; 316 | let mut dec = Decoder::new(data); 317 | assert_eq!(dec.next(), Some(Message::SongPositionPointer { beats: 0b01010101010101 })); 318 | assert_eq!(dec.next(), Some(Message::SongPositionPointer { beats: 0b10101010101010 })); 319 | assert_eq!(dec.next(), None); 320 | } 321 | 322 | #[test] 323 | fn decode_song_select() { 324 | let data = &vec![0b1111_0011u8, 0b0_1010101]; 325 | let mut dec = Decoder::new(data); 326 | assert_eq!(dec.next(), Some(Message::SongSelect { song: 0b1010101 })); 327 | assert_eq!(dec.next(), None); 328 | } 329 | 330 | #[test] 331 | fn decode_tune_request() { 332 | let data = &vec![0b1111_0110u8]; 333 | let mut dec = Decoder::new(data); 334 | assert_eq!(dec.next(), Some(Message::TuneRequest)); 335 | assert_eq!(dec.next(), None); 336 | } 337 | 338 | #[test] 339 | fn decode_timing_clock() { 340 | let data = &vec![0b1111_1000u8]; 341 | let mut dec = Decoder::new(data); 342 | assert_eq!(dec.next(), Some(Message::TimingClock)); 343 | assert_eq!(dec.next(), None); 344 | } 345 | 346 | #[test] 347 | fn decode_start_continue_stop() { 348 | let data = &vec![0b1111_1010u8, 0b1111_1011, 0b1111_1100]; 349 | let mut dec = Decoder::new(data); 350 | assert_eq!(dec.next(), Some(Message::Start)); 351 | assert_eq!(dec.next(), Some(Message::Continue)); 352 | assert_eq!(dec.next(), Some(Message::Stop)); 353 | assert_eq!(dec.next(), None); 354 | } 355 | 356 | #[test] 357 | fn decode_active_sensing() { 358 | let data = &vec![0b1111_1110u8]; 359 | let mut dec = Decoder::new(data); 360 | assert_eq!(dec.next(), Some(Message::ActiveSensing)); 361 | assert_eq!(dec.next(), None); 362 | } 363 | 364 | #[test] 365 | fn decode_system_reset() { 366 | let data = &vec![0b1111_1111u8]; 367 | let mut dec = Decoder::new(data); 368 | assert_eq!(dec.next(), Some(Message::SystemReset)); 369 | assert_eq!(dec.next(), None); 370 | } 371 | 372 | #[test] 373 | fn decode_reserved() { 374 | let data = &vec![0b1111_0100u8, 0b1111_0101, 0b1111_1001, 0b1111_1101]; 375 | let mut dec = Decoder::new(data); 376 | assert_eq!(dec.next(), Some(Message::Unknown(vec![0b1111_0100]))); 377 | assert_eq!(dec.next(), Some(Message::Unknown(vec![0b1111_0101]))); 378 | assert_eq!(dec.next(), Some(Message::Unknown(vec![0b1111_1001]))); 379 | assert_eq!(dec.next(), Some(Message::Unknown(vec![0b1111_1101]))); 380 | assert_eq!(dec.next(), None); 381 | } 382 | 383 | #[test] 384 | fn decode_sysex_continuous() { 385 | let data = &vec![0b1111_0000u8, 1, 2, 3, 4, 0b1111_0111]; 386 | let mut dec = Decoder::new(data); 387 | assert_eq!(dec.next(), Some(Message::SysEx { data: vec![1u8, 2, 3, 4] })); 388 | assert_eq!(dec.next(), None); 389 | } 390 | 391 | #[test] 392 | fn decode_sysex_interleaved() { 393 | let data = &vec![0b1111_0000u8, 1, 2, 0b1000_0101u8, 64, 127, 3, 4, 0b1111_0111]; 394 | let mut dec = Decoder::new(data); 395 | assert_eq!(dec.next(), Some(Message::NoteOff { channel: 0b0101, key: 64, velocity: 127 })); 396 | assert_eq!(dec.next(), Some(Message::SysEx { data: vec![1u8, 2, 3, 4] })); 397 | assert_eq!(dec.next(), None); 398 | } 399 | 400 | #[test] 401 | fn decode_sysex_unexpected_end_data() { 402 | let data = &vec![0b1111_0000u8, 1, 2]; 403 | let mut dec = Decoder::new(data); 404 | assert_eq!(dec.next(), Some(Message::Unknown(vec![0b1111_0000u8, 1, 2]))); 405 | assert_eq!(dec.next(), None); 406 | } 407 | 408 | #[test] 409 | fn decode_sysex_unexpected_end_interleaved() { 410 | let data = &vec![0b1111_0000u8, 1, 2, 0b1000_0000u8, 64]; 411 | let mut dec = Decoder::new(data); 412 | assert_eq!(dec.next(), Some(Message::Unknown(vec![0b1000_0000u8, 64]))); 413 | assert_eq!(dec.next(), Some(Message::Unknown(vec![0b1111_0000u8, 1, 2]))); 414 | assert_eq!(dec.next(), None); 415 | } 416 | } 417 | -------------------------------------------------------------------------------- /studio/src/midi/events.rs: -------------------------------------------------------------------------------- 1 | // use std::slice::Iter; 2 | 3 | use midi::messages::Message; 4 | use midi::types::Timestamp; 5 | 6 | #[derive(Clone, Eq, PartialEq)] 7 | pub struct Event { 8 | timestamp: Timestamp, 9 | message: Message 10 | } 11 | 12 | impl Event { 13 | pub fn new(timestamp: Timestamp, message: Message) -> Event { 14 | Event { timestamp: timestamp, message: message } 15 | } 16 | 17 | #[inline] 18 | pub fn timestamp(&self) -> Timestamp { 19 | self.timestamp 20 | } 21 | 22 | #[inline] 23 | pub fn message(&self) -> Message { 24 | self.message.clone() 25 | } 26 | } 27 | 28 | #[derive(Clone)] 29 | pub struct PortEvents { 30 | port: String, 31 | events: Vec 32 | } 33 | 34 | impl PortEvents { 35 | pub fn new(port: &str, events: Vec) -> PortEvents { 36 | PortEvents { 37 | port: port.to_string(), 38 | events: events 39 | } 40 | } 41 | 42 | #[inline] 43 | pub fn port(&self) -> &str { 44 | &self.port 45 | } 46 | 47 | #[inline] 48 | pub fn events(&self) -> &[Event] { 49 | &self.events 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /studio/src/midi/io.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, Mutex}; 2 | use std::sync::atomic::{Ordering, AtomicBool}; 3 | use std::time::{Duration, Instant}; 4 | use std::thread; 5 | use std::sync::mpsc::{channel, Sender, Receiver}; 6 | 7 | use portmidi; 8 | 9 | use midi::decoder::Decoder; 10 | use midi::events::{Event, PortEvents}; 11 | use midi::types::Timestamp; 12 | 13 | const MIDI_BUF_LEN: usize = 1024; 14 | const MIDI_LOOP_DELAY_MILLIS: u64 = 10; 15 | 16 | pub struct Midi { 17 | pm_ctx: portmidi::PortMidi, 18 | running: Arc, 19 | finished: Arc 20 | } 21 | 22 | impl Midi { 23 | pub fn new() -> Midi { 24 | let pm_ctx = portmidi::PortMidi::new().unwrap(); 25 | Midi { 26 | pm_ctx: pm_ctx, 27 | running: Arc::new(AtomicBool::new(false)), 28 | finished: Arc::new(AtomicBool::new(true)) 29 | } 30 | } 31 | 32 | pub fn start(&mut self, sender: Sender/*, receiver: Receiver>*/) { 33 | let running = self.running.swap(true, Ordering::Relaxed); 34 | if !running { 35 | let in_devices: Vec = 36 | self.pm_ctx.devices().unwrap().into_iter().filter(|dev| dev.is_input()).collect(); 37 | 38 | let in_ports: Vec = 39 | in_devices.into_iter().filter_map(|dev| self.pm_ctx.input_port(dev, MIDI_BUF_LEN).ok()).collect(); 40 | 41 | let running = self.running.clone(); 42 | let finished = self.finished.clone(); 43 | 44 | thread::spawn(move || Self::read_loop(&in_ports, &running, &finished, sender)); 45 | } 46 | } 47 | 48 | pub fn stop(&mut self) { 49 | let running = self.running.swap(false, Ordering::Relaxed); 50 | if running { 51 | let wait = Duration::from_millis(250); 52 | while !self.finished.load(Ordering::Relaxed) { 53 | thread::sleep(wait); 54 | } 55 | } 56 | } 57 | 58 | fn read_loop(in_ports: &Vec, 59 | running: &AtomicBool, finished: &AtomicBool, 60 | sender: Sender) { 61 | 62 | finished.store(false, Ordering::Relaxed); 63 | let loop_delay = Duration::from_millis(MIDI_LOOP_DELAY_MILLIS); 64 | let mut dev_events = Vec::::with_capacity(in_ports.len()); 65 | while running.load(Ordering::Relaxed) { 66 | Self::read_events(&in_ports, &mut dev_events); 67 | if !dev_events.is_empty() { 68 | for dev_events in dev_events.iter() { 69 | sender.send(dev_events.clone()).ok(); 70 | } 71 | } 72 | else { 73 | thread::sleep(loop_delay); 74 | } 75 | } 76 | finished.store(true, Ordering::Relaxed); 77 | } 78 | 79 | fn read_events(in_ports: &Vec, dev_events: &mut Vec) { 80 | dev_events.clear(); 81 | for port in in_ports { 82 | if let Ok(Some(raw_events)) = port.read_n(MIDI_BUF_LEN) { 83 | let events = Self::decode_events(raw_events); 84 | if !events.is_empty() { 85 | let device = port.device(); 86 | let dev_name = device.name(); 87 | dev_events.push(PortEvents::new(dev_name, events)); 88 | } 89 | } 90 | } 91 | } 92 | 93 | fn decode_events(raw_events: Vec) -> Vec { 94 | let mut events = Vec::with_capacity(raw_events.len()); 95 | for raw_event in raw_events { 96 | let raw_msg = raw_event.message; 97 | let msg_buf = [raw_msg.status, raw_msg.data1, raw_msg.data2]; 98 | let mut decoder = Decoder::new(&msg_buf); 99 | match decoder.next() { 100 | Some(message) => { 101 | let timestamp = raw_event.timestamp as Timestamp; 102 | let event = Event::new(timestamp, message); 103 | events.push(event); 104 | }, 105 | None => {} 106 | } 107 | } 108 | events.sort_by_key(|event| event.timestamp()); 109 | events 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /studio/src/midi/messages.rs: -------------------------------------------------------------------------------- 1 | use midi::types::{U3, U4, U7, U14}; 2 | 3 | #[derive(Debug, Clone, Eq, PartialEq)] 4 | pub enum Message { 5 | // --- Channel Voice Messages [nnnn = 0-15 (MIDI Channel Number 1-16)] 6 | 7 | /// This message is sent when a note is released (ended). 8 | NoteOff { channel: U4, key: U7, velocity: U7 }, 9 | 10 | /// This message is sent when a note is depressed (start). 11 | NoteOn { channel: U4, key: U7, velocity: U7 }, 12 | 13 | /// This message is most often sent by pressing down on the key after it "bottoms out". 14 | PolyphonicKeyPressure { channel: U4, key: U7, value: U7 }, 15 | 16 | /// This message is sent when a controller value changes. 17 | ControlChange { channel: U4, controller: U7, value: U7 }, 18 | 19 | /// This message sent when the patch number changes. 20 | ProgramChange { channel: U4, value: U7 }, 21 | 22 | /// This message is most often sent by pressing down on the key after it "bottoms out". 23 | /// Use this message to send the single greatest pressure value (of all the current depressed keys). 24 | ChannelPressure { channel: U4, value: U7 }, 25 | 26 | /// Pitch Bend Change. This message is sent to indicate a change in the pitch bender 27 | /// (wheel or lever, typically). The pitch bender is measured by a fourteen bit value. Center 28 | /// (no pitch change) is 2000H. 29 | PitchBend { channel: U4, value: U14 }, 30 | 31 | // --- Channel Mode Messages 32 | 33 | /// When All Sound Off is received all oscillators will turn off, and their 34 | /// volume envelopes are set to zero as soon as possible. 35 | AllSoundOff { channel: U4 }, 36 | 37 | /// When Reset All Controllers is received, all controller values are reset to their default values. 38 | ResetAllControllers { channel: U4 }, 39 | 40 | /// When Local Control is Off, all devices on a given channel will respond only to data 41 | /// received over MIDI. Played data, etc. will be ignored. 42 | LocalControlOff { channel: U4 }, 43 | 44 | /// Local Control On restores the functions of the normal controllers. 45 | LocalControlOn { channel: U4 }, 46 | 47 | /// When an All Notes Off is received, all oscillators will turn off. 48 | AllNotesOff { channel: U4 }, 49 | 50 | /// It also causes all notes off. 51 | OmniModeOff { channel: U4 }, 52 | 53 | /// It also causes all notes off. 54 | OmniModeOn { channel: U4 }, 55 | 56 | /// It also causes all notes off. 57 | MonoModeOn { channel: U4, num_channels: U7 }, 58 | 59 | /// It also causes all notes off. 60 | PolyModeOn { channel: U4 }, 61 | 62 | // --- System Common Messages 63 | 64 | /// This message type allows manufacturers to create their own messages (such 65 | /// as bulk dumps, patch parameters, and other non-spec data) and provides a mechanism for 66 | /// creating additional MIDI Specification messages. 67 | SysEx { data: Vec }, 68 | 69 | /// MIDI Time Code Quarter Frame. 70 | /// The type determines how to interpret the value: 71 | /// 0 Current Frames Low Nibble 72 | /// 1 Current Frames High Nibble 73 | /// 2 Current Seconds Low Nibble 74 | /// 3 Current Seconds High Nibble 75 | /// 4 Current Minutes Low Nibble 76 | /// 5 Current Minutes High Nibble 77 | /// 6 Current Hours Low Nibble 78 | /// 7 Current Hours High Nibble and SMPTE Type 79 | MTCQuarterFrame { msg_type: U3, value: U4 }, 80 | 81 | /// Song Position Pointer. 82 | /// This is an internal 14 bit register that holds the number of MIDI beats 83 | /// (1 beat= six MIDI clocks) since the start of the song. 84 | SongPositionPointer { beats: U14 }, 85 | 86 | /// Song Select 87 | /// The Song Select specifies which sequence or song is to be played. 88 | SongSelect { song: U7 }, 89 | 90 | /// Upon receiving a Tune Request, all analog synthesizers should tune their oscillators. 91 | TuneRequest, 92 | 93 | // --- System Real-Time Messages 94 | 95 | /// Start the current sequence playing. 96 | /// (This message will be followed with Timing Clocks). 97 | Start, 98 | 99 | /// Timing Clock. Sent 24 times per quarter note when synchronization is required. 100 | TimingClock, 101 | 102 | /// Continue at the point the sequence was Stopped 103 | Continue, 104 | 105 | /// Stop the current sequence. 106 | Stop, 107 | 108 | /// This message is intended to be sent repeatedly to tell the receiver that a 109 | /// connection is alive. Use of this message is optional. When initially received, the receiver 110 | /// will expect to receive another Active Sensing message each 300ms (max), and if it does not 111 | /// then it will assume that the connection has been terminated. At termination, the receiver 112 | /// will turn off all voices and return to normal (non- active sensing) operation. 113 | ActiveSensing, 114 | 115 | /// Reset all receivers in the system to power-up status. This should be used sparingly, 116 | /// preferably under manual control. In particular, it should not be sent on power-up. 117 | SystemReset, 118 | 119 | /// Unknown data. Used by the decoder when the data can not be decoded. 120 | Unknown(Vec) 121 | } 122 | -------------------------------------------------------------------------------- /studio/src/midi/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod types; 2 | pub mod messages; 3 | pub mod decoder; 4 | pub mod events; 5 | pub mod io; 6 | 7 | // pub use self::decoder::Decoder; 8 | pub use self::events::{Event, PortEvents}; 9 | pub use self::messages::Message; 10 | pub use self::io::Midi; 11 | -------------------------------------------------------------------------------- /studio/src/midi/types.rs: -------------------------------------------------------------------------------- 1 | 2 | pub type U3 = u8; 3 | pub type U4 = u8; 4 | pub type U7 = u8; 5 | pub type U14 = u8; 6 | 7 | /// Timestamp in nanoseconds 8 | pub type Timestamp = u64; 9 | -------------------------------------------------------------------------------- /studio/src/osc/mod.rs: -------------------------------------------------------------------------------- 1 | use std::thread::{self, JoinHandle}; 2 | use std::sync::mpsc::{Sender, Receiver}; 3 | use std::sync::atomic::{Ordering, AtomicBool}; 4 | use std::sync::{Arc, Mutex}; 5 | use std::str::FromStr; 6 | use std::net::SocketAddr; 7 | use std::io; 8 | 9 | use futures::{self, Future, Stream, Sink}; 10 | use tokio_core::net::{UdpSocket, UdpCodec}; 11 | use tokio_core::reactor::Core; 12 | 13 | use rosc::{self, OscError, OscPacket}; 14 | 15 | pub struct OscCodec; 16 | 17 | impl UdpCodec for OscCodec { 18 | type In = (SocketAddr, OscPacket); 19 | type Out = (SocketAddr, OscPacket); 20 | 21 | fn decode(&mut self, addr: &SocketAddr, buf: &[u8]) -> io::Result { 22 | rosc::decoder::decode(buf) 23 | .map(|packet| (*addr, packet)) 24 | .or_else(|err| match err { 25 | OscError::ReadError(io_err) => Err(io_err), 26 | osc_err => Err(io::Error::new(io::ErrorKind::InvalidInput, format!("{:?}", osc_err))) 27 | }) 28 | } 29 | 30 | fn encode(&mut self, (addr, packet): Self::Out, into: &mut Vec) -> SocketAddr { 31 | rosc::encoder::encode(&packet) 32 | .map(|data| into.extend(data)).ok(); 33 | addr 34 | } 35 | } 36 | 37 | pub struct Osc { 38 | address: SocketAddr, 39 | running: Arc, 40 | input_join_handler: Option>, 41 | output_join_handler: Option>, 42 | } 43 | 44 | impl Osc { 45 | pub fn new(address: &str) -> Self { 46 | let addr = SocketAddr::from_str(address).unwrap(); 47 | Osc { 48 | address: addr, 49 | running: Arc::new(AtomicBool::new(false)), 50 | input_join_handler: None, 51 | output_join_handler: None, 52 | } 53 | } 54 | 55 | pub fn running(&self) -> bool { 56 | self.running.load(Ordering::Relaxed) 57 | } 58 | 59 | pub fn start(&mut self, sender: Sender, receiver: Receiver) { 60 | let running = self.running.swap(true, Ordering::Relaxed); 61 | if !running { 62 | // let socket = Arc::new(Mutex::new(UdpSocket::bind(self.address).unwrap())); 63 | // let socket = UdpSocket::bind(self.address).unwrap(); 64 | // let running = self.running.clone(); 65 | // self.input_join_handler = Some(thread::spawn(move || { 66 | // Self::input_loop(&running, &socket, sender) 67 | // })); 68 | // let running = self.running.clone(); 69 | // // let address = self.address; 70 | // self.output_join_handler = Some(thread::spawn(move || { 71 | // Self::output_loop(&running, &socket, receiver) 72 | // })); 73 | 74 | let address = self.address.clone(); 75 | thread::spawn(move || { 76 | let mut core = Core::new().unwrap(); 77 | let handle = core.handle(); 78 | 79 | let socket = UdpSocket::bind(&address, &handle).unwrap(); 80 | let local_addr = socket.local_addr() 81 | .map(|addr| format!("{}", addr)) 82 | .unwrap_or("unknown".to_string()); 83 | println!("Listening for OSC at {} ...", local_addr); 84 | 85 | let (sink, stream) = socket.framed(OscCodec).split(); 86 | 87 | let osc_input = stream.and_then(|(_addr, packet)| { 88 | // println!("{:?} -> {:?}", _addr, packet); 89 | sender.send(packet).or_else(|_| { 90 | Err(io::Error::new(io::ErrorKind::Other, "OSC sender channel disconnected")) 91 | }) 92 | }); 93 | 94 | // fn pk(p: OscPacket) -> Result<(SocketAddr, OscPacket), io::Error> { 95 | // let addr: SocketAddr = SocketAddr::from_str("255.255.255.255:8080").unwrap(); 96 | // Ok((addr.clone(), p)) 97 | // } 98 | // 99 | // let packets = receiver.into_iter().map(|p: OscPacket| pk(p)); 100 | // let packets_stream = futures::stream::iter(packets).or_else(|_| { 101 | // Err(io::Error::new(io::ErrorKind::Other, "???")) 102 | // }); 103 | // let osc_output = sink.send_all(packets_stream); 104 | // handle.spawn(osc_output.then(|_| Ok(()))); 105 | 106 | // fn process(v: usize) -> Result<(), ()> { 107 | // println!("Computing {}", v); 108 | // use std::thread; 109 | // use std::time::Duration; 110 | // thread::sleep(Duration::from_millis(1000)); 111 | // Ok(()) 112 | // } 113 | // use futures::stream; 114 | // let s2 = stream::repeat(2) 115 | // .map(process) 116 | // // .take(20) 117 | // .for_each(|_| Ok(())); 118 | // handle.spawn(s2); 119 | drop(core.run(osc_input.for_each(|_| Ok(())))); 120 | }); 121 | } 122 | } 123 | 124 | pub fn stop(&mut self) { 125 | let running = self.running.swap(false, Ordering::Relaxed); 126 | if running { 127 | self.input_join_handler.take().unwrap().join().ok(); 128 | } 129 | } 130 | 131 | // fn input_loop( 132 | // running: &AtomicBool, 133 | // socket: &UdpSocket, 134 | // sender: Sender) { 135 | // 136 | // let mut buf = [0u8; rosc::decoder::MTU]; 137 | // 138 | // while running.load(Ordering::Relaxed) { 139 | // match socket.recv_from(&mut buf) { 140 | // Ok((size, addr)) => { 141 | // // println!("Received packet with size {} from: {}", size, addr); 142 | // let packet = rosc::decoder::decode(&buf[..size]).unwrap(); 143 | // sender.send(packet).unwrap(); 144 | // } 145 | // Err(e) => { 146 | // println!("Error receiving from socket: {}", e); 147 | // } 148 | // } 149 | // } 150 | // } 151 | // 152 | // fn output_loop( 153 | // running: &AtomicBool, 154 | // socket: &UdpSocket, 155 | // // address: &str, 156 | // receiver: Receiver) { 157 | // 158 | // while running.load(Ordering::Relaxed) { 159 | // let packet = receiver.recv().unwrap(); 160 | // let buf = rosc::encoder::encode(&packet).unwrap(); 161 | // socket.send(&buf).unwrap(); 162 | // } 163 | // } 164 | 165 | // fn input_loop( 166 | // running: &AtomicBool, 167 | // socket: &UdpSocket, 168 | // sender: Sender) { 169 | // 170 | // let mut buf = [0u8; rosc::decoder::MTU]; 171 | // 172 | // while running.load(Ordering::Relaxed) { 173 | // match socket.recv_from(&mut buf) { 174 | // Ok((size, addr)) => { 175 | // // println!("Received packet with size {} from: {}", size, addr); 176 | // let packet = rosc::decoder::decode(&buf[..size]).unwrap(); 177 | // sender.send(packet).unwrap(); 178 | // } 179 | // Err(e) => { 180 | // println!("Error receiving from socket: {}", e); 181 | // } 182 | // } 183 | // } 184 | // } 185 | // 186 | // fn output_loop( 187 | // running: &AtomicBool, 188 | // socket: &UdpSocket, 189 | // // address: &str, 190 | // receiver: Receiver) { 191 | // 192 | // while running.load(Ordering::Relaxed) { 193 | // let packet = receiver.recv().unwrap(); 194 | // let buf = rosc::encoder::encode(&packet).unwrap(); 195 | // socket.send(&buf).unwrap(); 196 | // } 197 | // } 198 | } 199 | -------------------------------------------------------------------------------- /synth/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hero_synth" 3 | version = "0.1.0" 4 | authors = ["Christian Perez-Llamas"] 5 | 6 | [dependencies] 7 | rosc = "0.1.5" 8 | 9 | hero_core = { path = "../core" } 10 | -------------------------------------------------------------------------------- /synth/open-stage-control.js: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "label": "KEYB", 4 | "widgets": [ 5 | { 6 | "type": "keyboard", 7 | "id": "keyboard", 8 | "label": false, 9 | "left": 0, 10 | "top": 0, 11 | "width": "100%", 12 | "height": "100%", 13 | "color": "auto", 14 | "css": "", 15 | "precision": 1, 16 | "address": "/note", 17 | "preArgs": [], 18 | "target": [], 19 | "keys": 24, 20 | "start": 60, 21 | "traversing": true, 22 | "on": 1, 23 | "off": 0, 24 | "split": false 25 | } 26 | ] 27 | }, 28 | { 29 | "label": "OSC", 30 | "tabs": [ 31 | { 32 | "label": "1", 33 | "widgets": [ 34 | { 35 | "type": "knob", 36 | "id": "amp", 37 | "linkId": "", 38 | "label": "Amplitude", 39 | "unit": "", 40 | "left": 0, 41 | "top": 120, 42 | "width": 90, 43 | "height": 120, 44 | "noPip": false, 45 | "compact": false, 46 | "color": "auto", 47 | "css": "", 48 | "snap": false, 49 | "spring": false, 50 | "range": { 51 | "min": -100, 52 | "max": 100 53 | }, 54 | "origin": 0, 55 | "value": 0.8, 56 | "logScale": false, 57 | "precision": 2, 58 | "address": "/osc/amp", 59 | "preArgs": [ 60 | { 61 | "type": "i", 62 | "value": 1 63 | } 64 | ], 65 | "target": [], 66 | "angle": 270 67 | }, 68 | { 69 | "type": "multifader", 70 | "id": "freq", 71 | "label": "Modulation", 72 | "left": 0, 73 | "top": 240, 74 | "width": "100%", 75 | "height": 200, 76 | "color": "auto", 77 | "css": "", 78 | "address": "/fm", 79 | "preArgs": [ 80 | { 81 | "type": "i", 82 | "value": 1 83 | } 84 | ], 85 | "origin": 0, 86 | "strips": 8, 87 | "start": 1, 88 | "unit": "", 89 | "alignRight": false, 90 | "horizontal": false, 91 | "noPip": false, 92 | "compact": false, 93 | "traversing": true, 94 | "snap": true, 95 | "range": { 96 | "min": -1, 97 | "max": 1 98 | }, 99 | "value": "", 100 | "logScale": false, 101 | "precision": 2, 102 | "meter": false, 103 | "split": false, 104 | "target": [] 105 | }, 106 | { 107 | "type": "knob", 108 | "id": "freq", 109 | "linkId": "", 110 | "label": "Frequency", 111 | "unit": "", 112 | "left": 0, 113 | "top": 0, 114 | "width": 90, 115 | "height": 120, 116 | "noPip": false, 117 | "compact": false, 118 | "color": "auto", 119 | "css": "", 120 | "snap": false, 121 | "spring": false, 122 | "range": { 123 | "min": 0, 124 | "max": 14000 125 | }, 126 | "origin": "auto", 127 | "value": 0, 128 | "logScale": false, 129 | "precision": 2, 130 | "address": "/osc/freq", 131 | "preArgs": [ 132 | { 133 | "type": "i", 134 | "value": 1 135 | } 136 | ], 137 | "target": [], 138 | "angle": 270 139 | }, 140 | { 141 | "type": "toggle", 142 | "top": 160, 143 | "left": 270, 144 | "id": "fixed-freq", 145 | "linkId": "", 146 | "label": "Fixed Freq", 147 | "width": 90, 148 | "height": 40, 149 | "color": "auto", 150 | "css": "", 151 | "on": 1, 152 | "off": 0, 153 | "value": 0, 154 | "precision": 0, 155 | "address": "/osc/fixed-freq", 156 | "preArgs": [ 157 | { 158 | "type": "i", 159 | "value": 1 160 | } 161 | ], 162 | "target": [] 163 | }, 164 | { 165 | "type": "toggle", 166 | "top": 120, 167 | "left": 270, 168 | "id": "enabled", 169 | "linkId": "", 170 | "label": "Enabled", 171 | "width": 90, 172 | "height": 40, 173 | "color": "auto", 174 | "css": "", 175 | "on": 1, 176 | "off": 0, 177 | "value": 0, 178 | "precision": 0, 179 | "address": "/osc/enabled", 180 | "preArgs": [ 181 | { 182 | "type": "i", 183 | "value": 1 184 | } 185 | ], 186 | "target": [] 187 | }, 188 | { 189 | "type": "knob", 190 | "id": "ratio", 191 | "linkId": "", 192 | "label": "Ratio", 193 | "unit": "", 194 | "left": 90, 195 | "top": 0, 196 | "width": 90, 197 | "height": 120, 198 | "noPip": false, 199 | "compact": false, 200 | "color": "auto", 201 | "css": "", 202 | "snap": false, 203 | "spring": false, 204 | "range": { 205 | "min": -8, 206 | "max": 8 207 | }, 208 | "origin": 0, 209 | "value": 0, 210 | "logScale": false, 211 | "precision": 1, 212 | "address": "/osc/octaves", 213 | "preArgs": [ 214 | { 215 | "type": "i", 216 | "value": 1 217 | } 218 | ], 219 | "target": [], 220 | "angle": 270 221 | }, 222 | { 223 | "type": "knob", 224 | "id": "pitch", 225 | "linkId": "", 226 | "label": "Pitch", 227 | "unit": "", 228 | "left": 180, 229 | "top": 0, 230 | "width": 90, 231 | "height": 120, 232 | "noPip": false, 233 | "compact": false, 234 | "color": "auto", 235 | "css": "", 236 | "snap": false, 237 | "spring": false, 238 | "range": { 239 | "min": -12, 240 | "max": 12 241 | }, 242 | "origin": 0, 243 | "value": 0, 244 | "logScale": false, 245 | "precision": 1, 246 | "address": "/osc/semitones", 247 | "preArgs": [ 248 | { 249 | "type": "i", 250 | "value": 1 251 | } 252 | ], 253 | "target": [], 254 | "angle": 270 255 | }, 256 | { 257 | "type": "multitoggle", 258 | "top": 440, 259 | "left": 0, 260 | "id": "fm-enabled", 261 | "width": "100%", 262 | "height": 40, 263 | "color": "auto", 264 | "css": "", 265 | "label": false, 266 | "matrix": [ 267 | 8, 268 | 1 269 | ], 270 | "start": 1, 271 | "traversing": true, 272 | "on": 1, 273 | "off": 0, 274 | "value": "", 275 | "precision": 2, 276 | "address": "/osc/fm/enabled", 277 | "preArgs": [ 278 | { 279 | "type": "i", 280 | "value": 1 281 | } 282 | ], 283 | "split": false, 284 | "target": [] 285 | }, 286 | { 287 | "type": "knob", 288 | "id": "mix", 289 | "linkId": "", 290 | "label": "Mix", 291 | "unit": "", 292 | "left": 90, 293 | "top": 120, 294 | "width": 90, 295 | "height": 120, 296 | "noPip": false, 297 | "compact": false, 298 | "color": "auto", 299 | "css": "", 300 | "snap": false, 301 | "spring": false, 302 | "range": { 303 | "min": 0, 304 | "max": 1 305 | }, 306 | "origin": 0, 307 | "value": 0, 308 | "logScale": false, 309 | "precision": 2, 310 | "address": "/osc/level", 311 | "preArgs": [ 312 | { 313 | "type": "i", 314 | "value": 1 315 | } 316 | ], 317 | "target": [], 318 | "angle": 270 319 | }, 320 | { 321 | "type": "knob", 322 | "id": "pan", 323 | "linkId": "", 324 | "label": "Pan", 325 | "unit": "", 326 | "left": 180, 327 | "top": 120, 328 | "width": 90, 329 | "height": 120, 330 | "noPip": false, 331 | "compact": false, 332 | "color": "auto", 333 | "css": "", 334 | "snap": false, 335 | "spring": false, 336 | "range": { 337 | "min": -1, 338 | "max": 1 339 | }, 340 | "origin": 0, 341 | "value": 0, 342 | "logScale": false, 343 | "precision": 2, 344 | "address": "/osc/pan", 345 | "preArgs": [ 346 | { 347 | "type": "i", 348 | "value": 1 349 | } 350 | ], 351 | "target": [], 352 | "angle": 270 353 | }, 354 | { 355 | "type": "knob", 356 | "id": "phase", 357 | "linkId": "", 358 | "label": "Phase", 359 | "unit": "", 360 | "left": 270, 361 | "top": 0, 362 | "width": 90, 363 | "height": 120, 364 | "noPip": false, 365 | "compact": false, 366 | "color": "auto", 367 | "css": "", 368 | "snap": false, 369 | "spring": false, 370 | "range": { 371 | "min": 0, 372 | "max": { 373 | "2π": 6.2832 374 | } 375 | }, 376 | "origin": 0, 377 | "value": 0, 378 | "logScale": false, 379 | "precision": 2, 380 | "address": "/osc/phase", 381 | "preArgs": [ 382 | { 383 | "type": "i", 384 | "value": 1 385 | } 386 | ], 387 | "target": [], 388 | "angle": 270 389 | }, 390 | { 391 | "type": "toggle", 392 | "top": 200, 393 | "left": 270, 394 | "id": "free-phase", 395 | "linkId": "", 396 | "label": "Free Phase", 397 | "width": 90, 398 | "height": 40, 399 | "color": "auto", 400 | "css": "", 401 | "on": 1, 402 | "off": 0, 403 | "value": 0, 404 | "precision": 0, 405 | "address": "/osc/free-phase", 406 | "preArgs": [ 407 | { 408 | "type": "i", 409 | "value": 1 410 | } 411 | ], 412 | "target": [] 413 | }, 414 | { 415 | "type": "keyboard", 416 | "id": "keyboard", 417 | "label": false, 418 | "left": 360, 419 | "top": 0, 420 | "width": 300, 421 | "height": 200, 422 | "color": "auto", 423 | "css": "", 424 | "precision": 1, 425 | "address": "/note", 426 | "preArgs": [], 427 | "target": [], 428 | "keys": 18, 429 | "start": 60, 430 | "traversing": true, 431 | "on": 1, 432 | "off": 0, 433 | "split": false 434 | }, 435 | { 436 | "type": "push", 437 | "top": 200, 438 | "left": 360, 439 | "id": "Sync", 440 | "linkId": "", 441 | "label": "Sync", 442 | "width": 90, 443 | "height": 40, 444 | "color": "auto", 445 | "css": "", 446 | "on": 1, 447 | "off": 0, 448 | "norelease": false, 449 | "precision": 0, 450 | "address": "/sync", 451 | "preArgs": [], 452 | "target": [] 453 | } 454 | ] 455 | }, 456 | { 457 | "label": "2" 458 | }, 459 | { 460 | "label": "3" 461 | }, 462 | { 463 | "label": "4" 464 | }, 465 | { 466 | "label": "5" 467 | }, 468 | { 469 | "label": "6" 470 | }, 471 | { 472 | "label": "7" 473 | }, 474 | { 475 | "label": "8" 476 | } 477 | ], 478 | "widgets": [] 479 | }, 480 | { 481 | "label": "ENV" 482 | }, 483 | { 484 | "label": "FX" 485 | } 486 | ] -------------------------------------------------------------------------------- /synth/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate rosc; 2 | extern crate hero_core; 3 | 4 | pub mod patch; 5 | pub mod voice; 6 | pub mod synth; 7 | -------------------------------------------------------------------------------- /synth/src/patch.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use hero_core::wavetable::{self, Wavetable}; 4 | use hero_core::oscillator::Oscillator; 5 | use hero_core::types::SampleRate; 6 | 7 | 8 | #[derive(Clone, Debug)] 9 | pub struct OscPatch { 10 | pub is_enabled: bool, 11 | pub amplitude: f64, // Oscillator signal amplitude 12 | pub wavetable: String, 13 | pub is_free_phase: bool, 14 | pub initial_phase: f64, 15 | pub is_fixed_freq: bool, // When it is true the baseFrequency doesn't change with noteOn 16 | pub base_frequency: f64, // Oscillator base frequency 17 | pub octaves: f64, // Number of octaves to shift from the base_frequency 18 | pub semitones: f64, // Number of semitones to shift from the base_frequency 19 | pub detune: f64, // Fine shift from the base_frequency 20 | 21 | pub amp_mod: HashMap, // Send levels for amplitude modulation 22 | pub freq_mod: HashMap, // Send levels for frequency modulation 23 | pub filt_send: HashMap, // Send levels for the filter input 24 | 25 | pub level: f64, // Mix level 26 | pub panning: f64, // Panning [-1, +1] 27 | } 28 | 29 | impl Default for OscPatch { 30 | fn default() -> Self { 31 | OscPatch { 32 | is_enabled: true, 33 | amplitude: 1.0, 34 | wavetable: "sin".to_string(), 35 | is_free_phase: false, 36 | initial_phase: 0.0, 37 | is_fixed_freq: false, 38 | base_frequency: 440.0, 39 | octaves: 0.0, 40 | semitones: 0.0, 41 | detune: 0.0, 42 | 43 | amp_mod: HashMap::new(), 44 | freq_mod: HashMap::new(), 45 | filt_send: HashMap::new(), 46 | 47 | level: 1.0, 48 | panning: 0.0, 49 | } 50 | } 51 | } 52 | 53 | impl OscPatch { 54 | pub fn get_wavetable(&self) -> Wavetable { 55 | let wt_stock = match wavetable::Stock::from_name(&self.wavetable) { 56 | Some(stock) => stock, 57 | None => wavetable::Stock::Sin, 58 | }; 59 | Wavetable::from_stock(wt_stock) 60 | } 61 | 62 | pub fn to_oscillator(&self, sample_rate: SampleRate) -> Oscillator { 63 | let wavetable = self.get_wavetable(); 64 | let mut o = Oscillator::new(sample_rate, wavetable, self.base_frequency); 65 | o.set_enabled(self.is_enabled); 66 | o.set_amplitude(self.amplitude); 67 | o.set_free_phase(self.is_free_phase); 68 | o.set_initial_phase(self.initial_phase); 69 | o.set_octaves(self.octaves); 70 | o.set_semitones(self.semitones); 71 | o.set_detune(self.detune); 72 | o 73 | } 74 | } 75 | 76 | #[derive(Clone, Debug)] 77 | pub struct FilterPatch { 78 | // TODO filter params 79 | pub mode: String, 80 | pub slope: String, 81 | pub freq: f64, 82 | pub res: f64, 83 | 84 | pub amp_mod: HashMap, // Send levels for amplitude modulation 85 | pub freq_mod: HashMap, // Send levels for frequency modulation 86 | pub filt_send: HashMap, // Send levels for the filter input 87 | 88 | pub panning: f64, // Panning [-1, +1] 89 | pub level: f64, // Mix level 90 | } 91 | 92 | #[derive(Clone, Debug)] 93 | pub struct Patch { 94 | pub oscillators: Vec, 95 | pub filters: Vec, 96 | } 97 | 98 | impl Default for Patch { 99 | fn default() -> Self { 100 | const O1: usize = 0; 101 | const O2: usize = 0; 102 | const O3: usize = 0; 103 | const O4: usize = 0; 104 | let mut o1 = OscPatch::default(); 105 | let mut o2 = OscPatch::default(); 106 | let mut o3 = OscPatch::default(); 107 | let mut o4 = OscPatch::default(); 108 | o1.level = 1.0; 109 | // o1.is_fixed_freq = true; 110 | // o1.base_frequency = 1.0 / 4.0; 111 | // o1.detune = 0.75; 112 | // o1.amplitude = 16.0 * o1.base_frequency; 113 | 114 | o2.level = 0.0; 115 | // o2.is_fixed_freq = true; 116 | // o2.base_frequency = 2.0; 117 | // o2.detune = 0.0; 118 | // o2.amplitude = 16.0 * o2.base_frequency; 119 | 120 | o3.level = 0.0; 121 | o3.is_fixed_freq = true; 122 | o3.base_frequency = 4.0; 123 | // o3.octaves = 4.0; 124 | // o3.detune = 0.16; 125 | // o3.amplitude = 16.0 * o2.base_frequency; 126 | 127 | o4.level = -1.0; 128 | // o3.is_fixed_freq = true; 129 | // o3.base_frequency = 2.0; 130 | o4.semitones = 8.0; 131 | // o3.detune = 0.16; 132 | // o3.amplitude = 16.0 * o2.base_frequency; 133 | 134 | o3.freq_mod.insert(O1, 0.70); 135 | 136 | Patch { 137 | oscillators: vec![o1, o2, o3, o4], 138 | filters: Vec::new() 139 | } 140 | } 141 | } 142 | 143 | /* 144 | pub fn load(path: &Path) -> io::Result { 145 | let mut file = try!(File::open(path)); 146 | let mut data = String::new(); 147 | try!(file.read_to_string(&mut data)) 148 | //try!(Json::from_str(&data)) 149 | } 150 | */ 151 | -------------------------------------------------------------------------------- /synth/src/synth.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | use std::cell::RefCell; 3 | use std::collections::HashSet; 4 | use std::sync::mpsc::Sender; 5 | 6 | use rosc::{OscType, OscMessage, OscBundle, OscPacket}; 7 | 8 | use hero_core::types::{SampleRate, DEFAULT_SAMPLE_RATE}; 9 | 10 | use patch::Patch; 11 | use voice::{Voice, MAX_OSCILLATORS}; 12 | 13 | const MAX_KEYS: usize = 128; 14 | 15 | const ADDR_SYNC: &'static str = "/sync"; 16 | const ADDR_NOTE: &'static str = "/note"; 17 | const ADDR_OSC_ENABLED: &'static str = "/osc/enabled"; 18 | const ADDR_OSC_FREE_PHASE: &'static str = "/osc/free-phase"; 19 | const ADDR_OSC_INITIAL_PHASE: &'static str = "/osc/phase"; 20 | const ADDR_OSC_AMP: &'static str = "/osc/amp"; 21 | const ADDR_OSC_FIXED_FREQ: &'static str = "/osc/fixed-freq"; 22 | const ADDR_OSC_FREQ: &'static str = "/osc/freq"; 23 | const ADDR_OSC_OCTAVES: &'static str = "/osc/octaves"; 24 | const ADDR_OSC_SEMITONES: &'static str = "/osc/semitones"; 25 | const ADDR_OSC_DETUNE: &'static str = "/osc/detune"; 26 | const ADDR_OSC_AMP_MOD: &'static str = "/osc/am"; 27 | const ADDR_OSC_FREQ_MOD: &'static str = "/osc/fm"; 28 | const ADDR_OSC_PAN: &'static str = "/osc/pan"; 29 | const ADDR_OSC_LEVEL: &'static str = "/osc/level"; 30 | 31 | pub struct Synth { 32 | sample_rate: SampleRate, 33 | patch: Rc>, 34 | patch_version: usize, 35 | voices: Vec, 36 | active_voices: HashSet, 37 | output_packets: Vec, 38 | } 39 | 40 | impl Default for Synth { 41 | fn default() -> Self { 42 | Synth { 43 | sample_rate: DEFAULT_SAMPLE_RATE, 44 | patch: Rc::new(RefCell::new(Patch::default())), 45 | patch_version: 0, 46 | voices: Vec::new(), 47 | active_voices: HashSet::new(), 48 | output_packets: Vec::new(), 49 | } 50 | } 51 | } 52 | 53 | impl Synth { 54 | pub fn new(sample_rate: SampleRate) -> Synth { 55 | let patch = Rc::new(RefCell::new(Patch::default())); 56 | let mut voices = Vec::::with_capacity(MAX_KEYS); 57 | for _key in 0..MAX_KEYS { 58 | let voice = Voice::new(sample_rate, patch.clone()); 59 | voices.push(voice); 60 | } 61 | 62 | Synth { 63 | sample_rate: sample_rate, 64 | patch: patch, 65 | voices: voices, 66 | 67 | ..Synth::default() 68 | } 69 | } 70 | 71 | pub fn get_sample_rate(&self) -> SampleRate { 72 | self.sample_rate 73 | } 74 | 75 | pub fn note_on(&mut self, key: usize, vel: f64) { 76 | let voice_index = key & 0x7f; 77 | let ref mut voice = self.voices[voice_index]; 78 | voice.reset(); 79 | voice.update_patch(&self.patch.borrow(), self.patch_version); 80 | voice.note_on(key, vel); 81 | self.active_voices.insert(voice_index); 82 | } 83 | 84 | pub fn note_off(&mut self, key: usize, vel: f64) { 85 | let voice_index = key & 0x7f; 86 | let ref mut voice = self.voices[voice_index]; 87 | voice.note_off(key, vel); 88 | self.active_voices.remove(&voice_index); 89 | } 90 | 91 | pub fn control(&mut self, packet: &OscPacket) { 92 | match packet { 93 | &OscPacket::Message(ref msg) => { 94 | match msg.addr.as_ref() { 95 | ADDR_SYNC => self.control_sync(&msg.args), 96 | ADDR_NOTE => self.control_note(&msg.args), 97 | ADDR_OSC_AMP => self.control_osc_amplitude(&msg.args), 98 | ADDR_OSC_FREQ => self.control_osc_frequency(&msg.args), 99 | ADDR_OSC_OCTAVES => self.control_osc_octaves(&msg.args), 100 | ADDR_OSC_SEMITONES => self.control_osc_semitones(&msg.args), 101 | ADDR_OSC_LEVEL => self.control_osc_level(&msg.args), 102 | ADDR_OSC_PAN => self.control_osc_panning(&msg.args), 103 | ADDR_OSC_FREQ_MOD => self.control_osc_fm_mod(&msg.args), 104 | ADDR_OSC_ENABLED => self.control_osc_enabled(&msg.args), 105 | ADDR_OSC_FIXED_FREQ => self.control_osc_fixed_freq(&msg.args), 106 | ADDR_OSC_FREE_PHASE => self.control_osc_free_phase(&msg.args), 107 | _ => {} 108 | } 109 | }, 110 | &OscPacket::Bundle(ref bundle) => { 111 | for bundle_packet in &bundle.content { 112 | self.control(&bundle_packet); 113 | } 114 | } 115 | } 116 | } 117 | 118 | pub fn output(&mut self) -> Vec { 119 | let packets = self.output_packets.to_owned(); 120 | self.output_packets = Vec::new(); 121 | packets 122 | } 123 | 124 | fn control_sync(&mut self, _args: &Option>) { 125 | let mut packets = Vec::with_capacity(8 * (11 + 8 * 2)); 126 | use rosc::OscType::{Int, Float, Time}; 127 | let patch = self.patch.borrow(); 128 | for i in 0..patch.oscillators.len() { 129 | let index = i as i32; 130 | let patch_osc = &patch.oscillators[i]; 131 | packets.push(Self::osc_message(ADDR_OSC_ENABLED, vec![Int(index), Int(patch_osc.is_enabled as i32)])); 132 | packets.push(Self::osc_message(ADDR_OSC_AMP, vec![Int(index), Float(patch_osc.amplitude as f32)])); 133 | packets.push(Self::osc_message(ADDR_OSC_FREE_PHASE, vec![Int(index), Int(patch_osc.is_free_phase as i32)])); 134 | packets.push(Self::osc_message(ADDR_OSC_INITIAL_PHASE, vec![Int(index), Float(patch_osc.initial_phase as f32)])); 135 | packets.push(Self::osc_message(ADDR_OSC_FIXED_FREQ, vec![Int(index), Int(patch_osc.is_fixed_freq as i32)])); 136 | packets.push(Self::osc_message(ADDR_OSC_FREQ, vec![Int(index), Float(patch_osc.base_frequency as f32)])); 137 | packets.push(Self::osc_message(ADDR_OSC_OCTAVES, vec![Int(index), Float(patch_osc.octaves as f32)])); 138 | packets.push(Self::osc_message(ADDR_OSC_SEMITONES, vec![Int(index), Float(patch_osc.semitones as f32)])); 139 | packets.push(Self::osc_message(ADDR_OSC_DETUNE, vec![Int(index), Float(patch_osc.detune as f32)])); 140 | packets.push(Self::osc_message(ADDR_OSC_LEVEL, vec![Int(index), Float(patch_osc.level as f32)])); 141 | packets.push(Self::osc_message(ADDR_OSC_PAN, vec![Int(index), Float(patch_osc.panning as f32)])); 142 | for j in 0..MAX_OSCILLATORS { 143 | let level = match patch_osc.amp_mod.get(&j) { 144 | Some(level) => level.clone() as f32, 145 | None => 0.0f32 146 | }; 147 | packets.push(Self::osc_message(ADDR_OSC_AMP_MOD, vec![Int(j as i32), Int(index), Float(level)])); 148 | 149 | let level = match patch_osc.freq_mod.get(&j) { 150 | Some(level) => level.clone() as f32, 151 | None => 0.0f32 152 | }; 153 | packets.push(Self::osc_message(ADDR_OSC_FREQ_MOD, vec![Int(j as i32), Int(index), Float(level)])); 154 | } 155 | } 156 | let packet = OscPacket::Bundle(OscBundle { 157 | timetag: Time(0, 0), 158 | content: packets.clone() 159 | }); 160 | self.output_packets.push(packet); 161 | } 162 | 163 | fn osc_message(addr: &str, args: Vec) -> OscPacket { 164 | OscPacket::Message(OscMessage { 165 | addr: addr.to_string(), 166 | args: Some(args)}) 167 | } 168 | 169 | fn control_note(&mut self, args: &Option>) { 170 | if let Some((key, velocity)) = args_note(args) { 171 | if velocity > 0.0 { 172 | self.note_on(key, velocity); 173 | } 174 | else { 175 | self.note_off(key, velocity); 176 | } 177 | } 178 | } 179 | 180 | fn control_osc_amplitude(&mut self, args: &Option>) { 181 | if let Some((index, value)) = args_osc_val(args, -100.0, 100.0) { 182 | self.patch.borrow_mut().oscillators[index].amplitude = value; 183 | self.patch_version += 1; 184 | } 185 | } 186 | 187 | fn control_osc_frequency(&mut self, args: &Option>) { 188 | if let Some((index, value)) = args_osc_val(args, 0.0, 22000.0) { 189 | let mut patch = self.patch.borrow_mut(); 190 | if patch.oscillators[index].is_fixed_freq { 191 | patch.oscillators[index].base_frequency = value; 192 | self.patch_version += 1; 193 | } 194 | } 195 | } 196 | 197 | fn control_osc_octaves(&mut self, args: &Option>) { 198 | if let Some((index, value)) = args_osc_val(args, -8.0, 8.0) { 199 | self.patch.borrow_mut().oscillators[index].octaves = value; 200 | self.patch_version += 1; 201 | } 202 | } 203 | 204 | fn control_osc_semitones(&mut self, args: &Option>) { 205 | if let Some((index, value)) = args_osc_val(args, -12.0, 12.0) { 206 | self.patch.borrow_mut().oscillators[index].semitones = value; 207 | self.patch_version += 1; 208 | } 209 | } 210 | 211 | fn control_osc_level(&mut self, args: &Option>) { 212 | if let Some((index, value)) = args_osc_val(args, -1.0, 1.0) { 213 | self.patch.borrow_mut().oscillators[index].level = value; 214 | self.patch_version += 1; 215 | } 216 | } 217 | 218 | fn control_osc_panning(&mut self, args: &Option>) { 219 | if let Some((index, value)) = args_osc_val(args, -1.0, 1.0) { 220 | self.patch.borrow_mut().oscillators[index].panning = value; 221 | self.patch_version += 1; 222 | } 223 | } 224 | 225 | fn control_osc_fm_mod(&mut self, args: &Option>) { 226 | if let Some((src_index, dst_index, value)) = args_osc_mod_val(args) { 227 | self.patch.borrow_mut().oscillators[src_index].freq_mod.insert(dst_index, value); 228 | self.patch_version += 1; 229 | } 230 | } 231 | 232 | fn control_osc_enabled(&mut self, args: &Option>) { 233 | if let Some((index, value)) = args_osc_toggle(args) { 234 | self.patch.borrow_mut().oscillators[index].is_enabled = value; 235 | self.patch_version += 1; 236 | } 237 | } 238 | 239 | fn control_osc_fixed_freq(&mut self, args: &Option>) { 240 | if let Some((index, value)) = args_osc_toggle(args) { 241 | self.patch.borrow_mut().oscillators[index].is_fixed_freq = value; 242 | self.patch_version += 1; 243 | } 244 | } 245 | 246 | fn control_osc_free_phase(&mut self, args: &Option>) { 247 | if let Some((index, value)) = args_osc_toggle(args) { 248 | self.patch.borrow_mut().oscillators[index].is_free_phase = value; 249 | self.patch_version += 1; 250 | } 251 | } 252 | 253 | // fn foreach_active_voice(&mut self, mut f: F) where F: FnMut(&mut Voice) { 254 | // for voice_index in self.active_voices.iter() { 255 | // f(&mut self.voices[*voice_index]) 256 | // } 257 | // } 258 | 259 | pub fn process(&mut self) -> (f64, f64) { 260 | let mut left = 0f64; 261 | let mut right = 0f64; 262 | for voice_index in self.active_voices.iter() { 263 | let ref mut voice = self.voices[*voice_index]; 264 | if voice.patch_version() != self.patch_version { 265 | voice.update_patch(&self.patch.borrow(), self.patch_version); 266 | } 267 | let (voice_left, voice_right) = voice.process(); 268 | left += voice_left; 269 | right += voice_right; 270 | } 271 | 272 | (left, right) 273 | } 274 | } 275 | 276 | fn args_note(args: &Option>) -> Option<(usize, f64)> { 277 | match args { 278 | &Some(ref args) if args.len() == 2 => { 279 | match (&args[0], &args[1]) { 280 | (&OscType::Float(ref key), &OscType::Float(ref value)) => { 281 | let key = key.clone() as usize; 282 | let velocity = value.clone() as f64; 283 | if key < MAX_KEYS && velocity >= 0.0 && velocity <= 1.0 { 284 | Some((key, velocity)) 285 | } 286 | else { None } 287 | }, 288 | _ => None 289 | } 290 | }, 291 | &Some(ref args) if args.len() == 4 => { 292 | match (&args[1], &args[2]) { 293 | (&OscType::Int(ref key), &OscType::Float(ref value)) => { 294 | let key = key.clone() as usize; 295 | let velocity = value.clone() as f64; 296 | if key < MAX_KEYS && velocity >= 0.0 && velocity <= 1.0 { 297 | Some((key, velocity)) 298 | } 299 | else { None } 300 | }, 301 | _ => None 302 | } 303 | }, 304 | _ => None 305 | } 306 | } 307 | 308 | fn args_osc_val(args: &Option>, min: f64, max: f64) -> Option<(usize, f64)> { 309 | match args { 310 | &Some(ref args) if args.len() == 2 => { 311 | match (&args[0], &args[1]) { 312 | (&OscType::Int(ref index), &OscType::Float(ref value)) => { 313 | let index = (index - 1) as usize; 314 | let value = value.clone() as f64; 315 | if index < MAX_OSCILLATORS && value >= min && value <= max { 316 | Some((index, value)) 317 | } 318 | else { None } 319 | }, 320 | _ => None 321 | } 322 | }, 323 | _ => None 324 | } 325 | } 326 | 327 | fn args_osc_toggle(args: &Option>) -> Option<(usize, bool)> { 328 | match args { 329 | &Some(ref args) if args.len() == 2 => { 330 | match (&args[0], &args[1]) { 331 | (&OscType::Int(ref index), &OscType::Int(ref value)) => { 332 | let index = (index - 1) as usize; 333 | let value = value.clone() as usize; 334 | if index < MAX_OSCILLATORS && (value == 1 || value == 0) { 335 | Some((index, value == 1)) 336 | } 337 | else { None } 338 | }, 339 | _ => None 340 | } 341 | }, 342 | _ => None 343 | } 344 | } 345 | 346 | fn args_osc_mod_val(args: &Option>) -> Option<(usize, usize, f64)> { 347 | match args { 348 | &Some(ref args) if args.len() == 3 => { 349 | match (&args[0], &args[1], &args[2]) { 350 | (&OscType::Int(ref dst_index), &OscType::Int(ref src_index), &OscType::Float(ref value)) => { 351 | let src_index = (src_index - 1) as usize; 352 | let dst_index = (dst_index - 1) as usize; 353 | let value = value.clone() as f64; 354 | if src_index < MAX_OSCILLATORS && dst_index < MAX_OSCILLATORS && value >= -1.0 && value <= 1.0 { 355 | Some((src_index, dst_index, value)) 356 | } 357 | else { None } 358 | }, 359 | _ => None 360 | } 361 | }, 362 | _ => None 363 | } 364 | } 365 | -------------------------------------------------------------------------------- /synth/src/voice.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | use std::cell::RefCell; 3 | 4 | use hero_core::types::SampleRate; 5 | use hero_core::freq::KEY_FREQ; 6 | use hero_core::wavetable::{self, Wavetable}; 7 | use hero_core::oscillator::Oscillator; 8 | use hero_core::panning::Panning; 9 | use hero_core::filter::Filter; 10 | use hero_core::filter::iir::IIR; 11 | 12 | use patch::Patch; 13 | 14 | pub const MAX_OSCILLATORS: usize = 8; 15 | pub const MAX_FILTERS: usize = 2; 16 | 17 | /// Modulation index for Frequency Modulation 18 | const MOD_INDEX: f64 = 6.0; 19 | 20 | #[derive(Debug)] 21 | struct VoiceOsc { 22 | oscillator: Oscillator, 23 | panning: Panning, 24 | } 25 | 26 | #[derive(Debug)] 27 | struct VoiceFilter { 28 | iir: IIR, 29 | panning: Panning 30 | } 31 | 32 | #[derive(Debug)] 33 | pub struct Voice { 34 | patch: Rc>, 35 | patch_version: usize, 36 | oscillators: Vec, 37 | filters: Vec, 38 | key: usize, 39 | velocity: f64, 40 | } 41 | 42 | /// This synth has one voice per allowed key, so every voice has a fixed freq. 43 | impl Voice { 44 | pub fn new(sample_rate: SampleRate, patch: Rc>) -> Voice { 45 | let mut oscillators = Vec::::with_capacity(MAX_OSCILLATORS); 46 | for patch_osc in patch.borrow().oscillators.iter().take(MAX_OSCILLATORS) { 47 | let osc = patch_osc.to_oscillator(sample_rate); 48 | let voice_osc = VoiceOsc { 49 | oscillator: osc, 50 | panning: Panning::new(patch_osc.panning), 51 | }; 52 | oscillators.push(voice_osc); 53 | } 54 | while oscillators.len() < MAX_OSCILLATORS { 55 | let wt = Wavetable::from_stock(wavetable::Stock::Sin); 56 | let osc = Oscillator::new(sample_rate, wt, 0.0); 57 | let voice_osc = VoiceOsc { 58 | oscillator: osc, 59 | panning: Panning::new(0.0), 60 | }; 61 | oscillators.push(voice_osc); 62 | } 63 | 64 | let mut filters = Vec::::with_capacity(MAX_FILTERS); 65 | for filt_patch in patch.borrow().filters.iter().take(MAX_FILTERS) { 66 | // TODO filter from patch 67 | let iir = IIR::bypass(sample_rate); 68 | let pan = Panning::new(filt_patch.panning); 69 | let voice_filter = VoiceFilter { 70 | iir: iir, 71 | panning: pan 72 | }; 73 | filters.push(voice_filter); 74 | } 75 | 76 | Voice { 77 | patch: patch, 78 | patch_version: 0, 79 | oscillators: oscillators, 80 | filters: filters, 81 | key: 0, 82 | velocity: 0.0 83 | } 84 | } 85 | 86 | pub fn patch_version(&self) -> usize { 87 | self.patch_version 88 | } 89 | 90 | pub fn update_patch(&mut self, patch: &Patch, patch_version: usize) { 91 | self.patch_version = patch_version; 92 | for index in 0..patch.oscillators.len() { 93 | let patch_osc = &patch.oscillators[index]; 94 | 95 | let voice_osc = &mut self.oscillators[index]; 96 | voice_osc.panning.set_value(patch_osc.panning); 97 | 98 | let osc = &mut voice_osc.oscillator; 99 | osc.set_enabled(patch_osc.is_enabled); 100 | osc.set_amplitude(patch_osc.amplitude); 101 | // TODO wavetable 102 | osc.set_free_phase(patch_osc.is_free_phase); 103 | osc.set_initial_phase(patch_osc.initial_phase); 104 | osc.set_octaves(patch_osc.octaves); 105 | osc.set_semitones(patch_osc.semitones); 106 | osc.set_detune(patch_osc.detune); 107 | } 108 | let remaining_osc = patch.oscillators.len() .. MAX_OSCILLATORS; 109 | for voice_osc in self.oscillators[remaining_osc].iter_mut() { 110 | voice_osc.oscillator.set_enabled(false); 111 | voice_osc.oscillator.set_amplitude(0.0); 112 | } 113 | } 114 | 115 | pub fn reset(&mut self) { 116 | // Oscillators 117 | for voice_osc in self.oscillators.iter_mut() { 118 | voice_osc.oscillator.reset(); 119 | } 120 | 121 | // Filters 122 | for voice_filter in self.filters.iter_mut() { 123 | voice_filter.iir.reset(); 124 | } 125 | } 126 | 127 | pub fn note_on(&mut self, key: usize, vel: f64) { 128 | if self.key != key { 129 | let freq = KEY_FREQ[key & 0x7f]; 130 | let patch = self.patch.borrow(); 131 | for (index, patch_osc) in patch.oscillators.iter().enumerate() { 132 | if !patch_osc.is_fixed_freq { 133 | let mut voice_osc = &mut self.oscillators[index]; 134 | voice_osc.oscillator.set_base_frequency(freq); 135 | } 136 | } 137 | } 138 | self.velocity = vel; 139 | } 140 | 141 | pub fn note_off(&mut self, _key: usize, _vel: f64) { 142 | self.velocity = 0.0; 143 | // TODO envelopes !!! 144 | } 145 | 146 | pub fn process(&mut self) -> (f64, f64) { 147 | let mut osc_signals = [0.0f64; MAX_OSCILLATORS]; 148 | let mut osc_amp_mod = [1.0f64; MAX_OSCILLATORS]; 149 | let mut osc_freq_mod = [0.0f64; MAX_OSCILLATORS]; 150 | 151 | let num_osc = self.oscillators.len(); 152 | 153 | let patch = &self.patch.borrow(); 154 | 155 | // Calculate oscillators' signals and send AM and FM modulation 156 | 157 | for i in 0..patch.oscillators.len() { 158 | let voice_osc = &mut self.oscillators[i]; 159 | let ref mut osc = voice_osc.oscillator; 160 | let sig = osc.process(); 161 | osc_signals[i] = sig; 162 | 163 | if i < patch.oscillators.len() { 164 | let patch_osc = &patch.oscillators[i]; 165 | for (index, level) in patch_osc.amp_mod.iter() { 166 | osc_amp_mod[index.clone()] += sig * level; 167 | } 168 | 169 | let fm = sig * MOD_INDEX * osc.get_base_frequency(); 170 | for (index, level) in patch_osc.freq_mod.iter() { 171 | osc_freq_mod[index.clone()] += fm * level; 172 | } 173 | } 174 | } 175 | 176 | let mut left = 0.0f64; 177 | let mut right = 0.0f64; 178 | 179 | // Update oscillators' modulation, and apply panning and mix their signals 180 | 181 | for i in 0..num_osc { 182 | let voice_osc = &mut self.oscillators[i]; 183 | let ref mut osc = voice_osc.oscillator; 184 | osc.set_amplitude_modulation(osc_amp_mod[i]); 185 | osc.set_freq_modulation(osc_freq_mod[i]); 186 | 187 | if i < patch.oscillators.len() { 188 | let patch_osc = &patch.oscillators[i]; 189 | if patch_osc.level > 0.0 { 190 | let (osc_left, osc_right) = voice_osc.panning.process(osc_signals[i]); 191 | left += osc_left * patch_osc.level; 192 | right += osc_right * patch_osc.level; 193 | } 194 | } 195 | } 196 | 197 | // Normalize output 198 | 199 | let inv_count = 1.0 / num_osc as f64; 200 | let left = left * inv_count * self.velocity; 201 | let right = right * inv_count * self.velocity; 202 | (left, right) 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /wfgen/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hero_wfgen" 3 | version = "0.1.0" 4 | authors = ["Christian Perez-Llamas"] 5 | 6 | [dependencies] 7 | docopt = "0.7.0" 8 | -------------------------------------------------------------------------------- /wfgen/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod sin; 2 | pub mod saw; 3 | 4 | use std::fs::File; 5 | use std::path::PathBuf; 6 | use std::error::Error; 7 | use std::io; 8 | use std::io::prelude::*; 9 | 10 | pub const DEFAULT_WAVETABLE_SIZE: usize = 1 << 14; 11 | 12 | static HEADER: &'static str = "//! 13 | //! Lookup Table for a {} waveform 14 | //! 15 | "; 16 | 17 | static TABLE_START: &'static str = " 18 | pub const LUT: &'static [f64] = &[ 19 | "; 20 | 21 | static TABLE_END: &'static str = "];\n"; 22 | 23 | 24 | pub fn gen_lut(base_path: &str, name: &str, generator: &mut Iterator) -> io::Result<()> { 25 | let mut file_name = String::from(name) + ".rs"; 26 | let path = PathBuf::from(base_path).join(file_name); 27 | 28 | println!("Generating {} ...", path.display()); 29 | 30 | let mut out_file = try!(File::create(&path)); 31 | try!(out_file.write(HEADER.replace("{}", name).as_bytes())); 32 | try!(out_file.write(TABLE_START.as_bytes())); 33 | 34 | for value in generator { 35 | out_file.write(format!(" {:.50}f64,\n", value).as_bytes()).unwrap(); 36 | } 37 | 38 | try!(out_file.write(TABLE_END.as_bytes())); 39 | 40 | Ok(()) 41 | } 42 | -------------------------------------------------------------------------------- /wfgen/src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate docopt; 2 | extern crate wfgen; 3 | 4 | use std::fs::File; 5 | use std::path::Path; 6 | use std::path::PathBuf; 7 | use std::error::Error; 8 | use std::io; 9 | use std::io::prelude::*; 10 | use docopt::Docopt; 11 | 12 | 13 | fn main() { 14 | let args = Docopt::new(USAGE) 15 | .and_then(|dopt| dopt.parse()) 16 | .unwrap_or_else(|e| e.exit()); 17 | 18 | let base_path = args.get_str(""); 19 | 20 | let mut sin_gen = wfgen::sin::SinGen::new(wfgen::DEFAULT_WAVETABLE_SIZE); 21 | wfgen::gen_lut(base_path, "sin", &mut sin_gen).unwrap(); 22 | } 23 | -------------------------------------------------------------------------------- /wfgen/src/saw.rs: -------------------------------------------------------------------------------- 1 | pub struct SawGen { 2 | count: usize, 3 | phase: f64, 4 | step: f64, 5 | } 6 | 7 | impl SawGen { 8 | pub fn new(size: usize) -> SawGen { 9 | SawGen { 10 | count: size, 11 | phase: 0.0, 12 | step: 2.0 / (size as f64), 13 | } 14 | } 15 | } 16 | 17 | impl Iterator for SawGen { 18 | type Item = f64; 19 | 20 | fn next(&mut self) -> Option { 21 | if self.count == 0 { 22 | None 23 | } 24 | else { 25 | let value = -1.0 + self.phase; 26 | self.phase += self.step; 27 | self.count -= 1; 28 | Some(value) 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /wfgen/src/sin.rs: -------------------------------------------------------------------------------- 1 | const PI_2: f64 = 6.28318530717958647692528676655900576f64; 2 | 3 | pub struct SinGen { 4 | count: usize, 5 | phase: f64, 6 | step: f64, 7 | } 8 | 9 | impl SinGen { 10 | pub fn new(size: usize) -> SinGen { 11 | SinGen { 12 | count: size, 13 | phase: 0.0, 14 | step: PI_2 / (size as f64), 15 | } 16 | } 17 | } 18 | 19 | impl Iterator for SinGen { 20 | type Item = f64; 21 | 22 | fn next(&mut self) -> Option { 23 | if self.count == 0 { 24 | None 25 | } 26 | else { 27 | let value = self.phase.sin(); 28 | self.phase += self.step; 29 | self.count -= 1; 30 | Some(value) 31 | } 32 | } 33 | } 34 | --------------------------------------------------------------------------------