├── .gitignore ├── .travis.yml ├── Cargo.toml ├── LICENSE ├── README.md ├── sounds ├── 808.wav ├── HiHat.wav ├── HiHat2.wav ├── Kick.wav ├── Snare.wav └── fxtestinput.wav └── src ├── bin └── demotrack.rs ├── clock.rs ├── consts.rs ├── conversions.rs ├── device.rs ├── effects.rs ├── events.rs ├── files.rs ├── lib.rs ├── sampler.rs └── synth.rs /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /target 3 | **/*.rs.bk 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | rust: 3 | - stable 4 | - beta 5 | - nightly 6 | 7 | matrix: 8 | allow_failures: 9 | - rust: nightly 10 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dawr" 3 | version = "0.0.2" 4 | authors = ["Taylor Hornby "] 5 | license = "MIT" 6 | repository = "https://github.com/defuse/DAWr" 7 | description = "A DAW-like audio playground for Rust coders." 8 | 9 | [dependencies] 10 | portaudio = "0.7" 11 | rand = "0.4" 12 | hound = "3.4" 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Taylor Hornby . 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DAWr 2 | 3 | [![Build Status](https://travis-ci.org/defuse/DAWr.svg?branch=master)](https://travis-ci.org/defuse/DAWr) 4 | [![crates.io](https://img.shields.io/crates/v/dawr.svg)](https://crates.io/crates/dawr) 5 | 6 | DAWr (pronounced "door") is an audio playground for people who like to write 7 | Rust code. It has some features of a simple DAW, including a build-in wavetable 8 | synthesizer, sampler, and basic audio effects. However, it's still missing 9 | a lot: there's no equalizer, the sampler only works in one-shot mode, and more. 10 | I don't plan on adding new features for the time being, but I'd appreciate 11 | suggestions and pull requests! 12 | 13 | The feature set is limited, but it's enough to make a shitty future bass drop 14 | section in Rust! You can find code for this in 15 | [src/bin/demotrack.rs](src/bin/demotrack.rs). Here's what it [sounds like (on 16 | SoundCloud)](https://soundcloud.com/earthrise5/dawr-library-example). 17 | 18 | **WARNING:** This library doesn't have any unit tests! There are probably bugs! 19 | 20 | ## Devices 21 | 22 | In this library, devices are things that emit a signal, which can either be mono 23 | or stereo. Stereo is usually audio, and mono is usually some kind of control 24 | parameter. There are two important traits, `MonoEmitter` and `StereoEmitter` for 25 | devices which output mono or stereo respectively. There is also an `EventSource` 26 | type representing a collection of events that occur at specific moments in time 27 | (e.g. `NoteOn(frequency)` and `NoteOff`). The philosophy is that each device 28 | should do one simple thing well, and then more powerful devices can be created 29 | by chaining simpler ones. 30 | 31 | New devices are constructed by providing references to all of their input 32 | devices and references to all of the event sources they need to listen to. For 33 | example, to build the `MonoSynth` device, it needs to be provided references to 34 | an `Oscillator` (a `MonoEmitter` which tells it what its current phase in the 35 | wave is), another `MonoEmitter` to tell it which wave of the wavetable is 36 | selected at the current moment in time, and another `MonoEmitter` specifying the 37 | amplitude envelope. In order to get notes to play, the amp envelope device will 38 | listen to an `EventSource` and output a non-zero amplitude value 39 | during the notes. We use Rust's `Rc` pointers extensively, so multiple devices 40 | can use the same device as input. 41 | 42 | Here is a list of all the built-in devices: 43 | 44 | ### Mono Sources 45 | 46 | - `ConstSignal`: Outputs a constant-valued mono signal. 47 | - `Envelope`: Listens for `NoteOn` events and outputs 0.0 when there are no notes 48 | playing and 1.0 while a note is playing. 49 | - `Oscillator`: For use with `MonoSynth`, outputs the current phase of the wave 50 | being played. 51 | 52 | ### Instruments 53 | 54 | - `MonoSynth`: A monophonic wavetable synthesizer. You can create polyphony and 55 | unison by using more than one instance along with the `Pan` effect and the 56 | `MonoSynth`'s `Oscillator`'s detune parameter. 57 | 58 | - `Sampler`: Listens to an `EventSource` and plays some audio 59 | whenever it sees a `Play` or `PlayAtSpeed(speed)` event. In the latter case, 60 | the playback is sped up by a factor of `speed` (by skipping over or 61 | duplicating samples). 62 | 63 | ### Effects 64 | 65 | - `Mixer`: Sums the outputs of multiple stereo inputs. 66 | - `Gain`: Multiplies a stereo input signal with a mono input signal. 67 | - `Pan`: Adjusts the balance of a stereo signal according to a mono input signal 68 | in the range [-1, 1]. 69 | - `MonoToStereo`: Duplicates a mono signal to both channels of a stereo signal. 70 | - `StereoToMono`: Sums the left and right channels of a stereo input to create 71 | a mono signal. 72 | - `WaveShaperEffect`: Applies waveshaping (e.g. hard clipping) to a stereo 73 | signal. 74 | 75 | ## Wishlist 76 | 77 | This library is missing some really important stuff, like: 78 | 79 | - High-pass and low-pass filters. 80 | - An equalizer. 81 | - MIDI support. 82 | - A polyphonic synth device that listens to MIDI events, based on `MonoSynth`. 83 | - Unit tests. 84 | -------------------------------------------------------------------------------- /sounds/808.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defuse/DAWr/227d1e225fc766570d85407899df17789ee02e68/sounds/808.wav -------------------------------------------------------------------------------- /sounds/HiHat.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defuse/DAWr/227d1e225fc766570d85407899df17789ee02e68/sounds/HiHat.wav -------------------------------------------------------------------------------- /sounds/HiHat2.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defuse/DAWr/227d1e225fc766570d85407899df17789ee02e68/sounds/HiHat2.wav -------------------------------------------------------------------------------- /sounds/Kick.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defuse/DAWr/227d1e225fc766570d85407899df17789ee02e68/sounds/Kick.wav -------------------------------------------------------------------------------- /sounds/Snare.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defuse/DAWr/227d1e225fc766570d85407899df17789ee02e68/sounds/Snare.wav -------------------------------------------------------------------------------- /sounds/fxtestinput.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defuse/DAWr/227d1e225fc766570d85407899df17789ee02e68/sounds/fxtestinput.wav -------------------------------------------------------------------------------- /src/bin/demotrack.rs: -------------------------------------------------------------------------------- 1 | extern crate dawr; 2 | extern crate portaudio; 3 | 4 | use dawr::clock::*; 5 | use dawr::synth::*; 6 | use dawr::events::*; 7 | use dawr::device::*; 8 | use dawr::effects::*; 9 | use dawr::conversions::*; 10 | use dawr::sampler::*; 11 | use dawr::files; 12 | use dawr::consts; 13 | use std::rc::Rc; 14 | 15 | use portaudio as pa; 16 | 17 | 18 | fn main() { 19 | match run() { 20 | Ok(_) => {}, 21 | e => { 22 | eprintln!("Example failed with the following: {:?}", e); 23 | } 24 | } 25 | } 26 | 27 | // device 28 | 29 | fn run() -> Result<(), pa::Error> { 30 | let c = Clock::new(); 31 | 32 | let mut kickevents = Vec::<(u64, SamplerEvent)>::new(); 33 | let mut snareevents = Vec::<(u64, SamplerEvent)>::new(); 34 | let mut hatevents = Vec::<(u64, SamplerEvent)>::new(); 35 | let mut hat2events = Vec::<(u64, SamplerEvent)>::new(); 36 | let mut bassevents = Vec::<(u64, SamplerEvent)>::new(); 37 | 38 | let mut notes_a = Vec::<(u64, NoteEvent)>::new(); 39 | let mut notes_b = Vec::<(u64, NoteEvent)>::new(); 40 | let mut notes_c = Vec::<(u64, NoteEvent)>::new(); 41 | let mut notes_d = Vec::<(u64, NoteEvent)>::new(); 42 | 43 | let mut m = TimeCalculator::new(160.0); 44 | for bar in 0..16 { 45 | for beat in 0..4 { 46 | if (bar % 2 == 0 && beat == 0) || (bar % 2 == 1 && beat == 1) || (bar % 4 == 1 && beat == 3) { 47 | kickevents.push((m.add_quarters(beat as f64).time(), SamplerEvent::Play)); 48 | let bass_speed = { 49 | if bar % 2 == 0 { 50 | 1.0 51 | } else { 52 | 1.5 53 | } 54 | }; 55 | bassevents.push((m.add_quarters(beat as f64).time(), SamplerEvent::PlayAtSpeed(bass_speed))); 56 | } 57 | if beat == 2 { 58 | snareevents.push((m.add_quarters(beat as f64).time(), SamplerEvent::Play)); 59 | } 60 | } 61 | for eights in 0..8 { 62 | hatevents.push((m.add_eighths(eights as f64).time(), SamplerEvent::Play)); 63 | if bar >= 4 { 64 | hat2events.push((m.add_eighths(eights as f64).add_sixteenths(1.0).time(), SamplerEvent::Play)); 65 | } 66 | } 67 | 68 | const E1_FREQ : f32 = 41.20; 69 | let half_step : f32 = 2.0_f32.powf(1.0/12.0); 70 | 71 | match bar % 4 { 72 | 0 => { 73 | // III7 74 | let shift = half_step.powf(3.0); 75 | for i in 0..4 { 76 | notes_a.push((m.add_eighths(i as f64).time(), NoteEvent::NoteOn(shift*8.0*E1_FREQ))); 77 | notes_a.push((m.add_eighths(i as f64).add_sixteenths(1.0).time(), NoteEvent::NoteOff)); 78 | 79 | notes_b.push((m.add_eighths(i as f64).time(), NoteEvent::NoteOn(shift*8.0*E1_FREQ * half_step.powf(4.0) ))); 80 | notes_b.push((m.add_eighths(i as f64).add_sixteenths(1.0).time(), NoteEvent::NoteOff)); 81 | 82 | notes_c.push((m.add_eighths(i as f64).time(), NoteEvent::NoteOn(shift*8.0*E1_FREQ * half_step.powf(7.0) ))); 83 | notes_c.push((m.add_eighths(i as f64).add_sixteenths(1.0).time(), NoteEvent::NoteOff)); 84 | 85 | notes_d.push((m.add_eighths(i as f64).time(), NoteEvent::NoteOn(shift*8.0*E1_FREQ * half_step.powf(11.0) ))); 86 | notes_d.push((m.add_eighths(i as f64).add_sixteenths(1.0).time(), NoteEvent::NoteOff)); 87 | } 88 | }, 89 | 2 => { 90 | // i7 91 | notes_a.push((m.time(), NoteEvent::NoteOn(8.0*E1_FREQ))); 92 | notes_a.push((m.add_quarters(2.0).time(), NoteEvent::NoteOff)); 93 | 94 | notes_b.push((m.time(), NoteEvent::NoteOn(8.0*E1_FREQ * half_step.powf(3.0) ))); 95 | notes_b.push((m.add_quarters(2.0).time(), NoteEvent::NoteOff)); 96 | 97 | notes_c.push((m.time(), NoteEvent::NoteOn(8.0*E1_FREQ * half_step.powf(7.0) ))); 98 | notes_c.push((m.add_quarters(2.0).time(), NoteEvent::NoteOff)); 99 | 100 | notes_d.push((m.time(), NoteEvent::NoteOn(8.0*E1_FREQ * half_step.powf(10.0)))); 101 | notes_d.push((m.add_quarters(2.0).time(), NoteEvent::NoteOff)); 102 | }, 103 | 1 | 3 => { 104 | let fifth = half_step.powf(7.0); 105 | // v7 106 | notes_a.push((m.add_quarters(1.0).time(), NoteEvent::NoteOn(fifth*8.0*E1_FREQ))); 107 | notes_a.push((m.add_quarters(2.0).time(), NoteEvent::NoteOff)); 108 | 109 | notes_a.push((m.add_quarters(3.0).time(), NoteEvent::NoteOn(fifth*8.0*E1_FREQ))); 110 | notes_a.push((m.add_quarters(4.0).time(), NoteEvent::NoteOff)); 111 | 112 | notes_b.push((m.add_quarters(1.0).time(), NoteEvent::NoteOn(fifth*8.0*E1_FREQ * half_step.powf(3.0) ))); 113 | notes_b.push((m.add_quarters(2.0).time(), NoteEvent::NoteOff)); 114 | 115 | notes_b.push((m.add_quarters(3.0).time(), NoteEvent::NoteOn(fifth*8.0*E1_FREQ * half_step.powf(3.0) ))); 116 | notes_b.push((m.add_quarters(4.0).time(), NoteEvent::NoteOff)); 117 | 118 | notes_c.push((m.add_quarters(1.0).time(), NoteEvent::NoteOn(fifth*8.0*E1_FREQ * half_step.powf(7.0) ))); 119 | notes_c.push((m.add_quarters(2.0).time(), NoteEvent::NoteOff)); 120 | 121 | notes_c.push((m.add_quarters(3.0).time(), NoteEvent::NoteOn(fifth*8.0*E1_FREQ * half_step.powf(7.0) ))); 122 | notes_c.push((m.add_quarters(4.0).time(), NoteEvent::NoteOff)); 123 | 124 | notes_d.push((m.add_quarters(1.0).time(), NoteEvent::NoteOn(fifth*8.0*E1_FREQ * half_step.powf(10.0)))); 125 | notes_d.push((m.add_quarters(2.0).time(), NoteEvent::NoteOff)); 126 | 127 | notes_d.push((m.add_quarters(3.0).time(), NoteEvent::NoteOn(fifth*8.0*E1_FREQ * half_step.powf(10.0)))); 128 | notes_d.push((m.add_quarters(4.0).time(), NoteEvent::NoteOff)); 129 | }, 130 | _ => { 131 | panic!("This will never happen!"); 132 | } 133 | }; 134 | m = m.add_bars(1.0); 135 | } 136 | 137 | let (kick_l, kick_r) = files::load_wav_to_stereo("sounds/Kick.wav"); 138 | let kick = Gain::new( 139 | c.clone(), 140 | Sampler::new(c.clone(), EventSource::new(kickevents, c.clone()), kick_l, kick_r), 141 | ConstSignal::new(c.clone(), decibels(6.0)) 142 | ); 143 | 144 | let (hat_l, hat_r) = files::load_wav_to_stereo("sounds/HiHat.wav"); 145 | let hihat = Sampler::new(c.clone(), EventSource::new(hatevents, c.clone()), hat_l, hat_r); 146 | 147 | let (hat2_l, hat2_r) = files::load_wav_to_stereo("sounds/HiHat2.wav"); 148 | let hihat2 = Sampler::new(c.clone(), EventSource::new(hat2events, c.clone()), hat2_l, hat2_r); 149 | 150 | let (snare_l, snare_r) = files::load_wav_to_stereo("sounds/Snare.wav"); 151 | let snare = Gain::new( 152 | c.clone(), 153 | Sampler::new(c.clone(), EventSource::new(snareevents, c.clone()), snare_l, snare_r), 154 | ConstSignal::new(c.clone(), decibels(6.0)) 155 | ); 156 | 157 | let (bass_l, bass_r) = files::load_wav_to_stereo("sounds/808.wav"); 158 | let bass = Gain::new( 159 | c.clone(), 160 | Sampler::new(c.clone(), EventSource::new(bassevents, c.clone()), bass_l, bass_r), 161 | ConstSignal::new(c.clone(), decibels(6.0)) 162 | ); 163 | 164 | let note_channels = vec![notes_a, notes_b, notes_c, notes_d]; 165 | let mut synths = Vec::>::new(); 166 | 167 | for events in note_channels { 168 | let es = EventSource::new(events, c.clone()); 169 | let voices = 4; 170 | for voice in 0..voices { 171 | let ratio = voice as f32 / (voices - 1) as f32; 172 | let detune = 0.99 + 0.02 * ratio; 173 | let pan = 1.0 - 2.0 * ratio; 174 | let synth = Pan::new( 175 | c.clone(), 176 | MonoToStereo::new( 177 | c.clone(), 178 | MonoSynth::new( 179 | c.clone(), 180 | WaveTable::new(vec![Wave::saw()]), 181 | Oscillator::new(c.clone(), es.clone(), ConstSignal::new(c.clone(), detune)), 182 | // Wavetable position. 183 | ConstSignal::new(c.clone(), 0.0), 184 | Envelope::new(c.clone(), es.clone()) 185 | ) 186 | ), 187 | ConstSignal::new(c.clone(), pan) 188 | ); 189 | synths.push(synth); 190 | } 191 | } 192 | 193 | let synths_mixed = Gain::new( 194 | c.clone(), 195 | Mixer::new(c.clone(), synths), 196 | ConstSignal::new(c.clone(), decibels(-10.0)) 197 | ); 198 | 199 | let mix = Mixer::new(c.clone(), vec![kick, hihat, hihat2, snare, bass, synths_mixed]); 200 | let master = WaveShaperEffect::new( 201 | c.clone(), 202 | Gain::new( 203 | c.clone(), 204 | mix.clone(), 205 | ConstSignal::new(c.clone(), decibels(-5.0)) 206 | ), 207 | HardClipper::new() 208 | ); 209 | 210 | println!("Rendering audio..."); 211 | let (left, right) = dawr::render_audio(c, master, TimeCalculator::new(160.0).add_bars(16.0).time() as usize); 212 | 213 | println!("Saving audio to Output.wav..."); 214 | dawr::files::save_stereo_to_wav(&left, &right, "Output.wav"); 215 | 216 | let pa = try!(pa::PortAudio::new()); 217 | let mut settings = try!(pa.default_output_stream_settings( 218 | 2, // num channels 219 | consts::SAMPLE_RATE as f64, 220 | consts::CHUNK_SIZE as u32 221 | )); 222 | settings.flags = pa::stream_flags::CLIP_OFF; 223 | 224 | let mut clipped = false; 225 | 226 | let mut position = 0; 227 | 228 | let callback = move |pa::OutputStreamCallbackArgs { buffer, frames, .. }| { 229 | assert_eq!(frames, consts::CHUNK_SIZE); 230 | for f in 0..frames { 231 | if position < right.len() && position < left.len() { 232 | let left_sample = left[position]; 233 | let right_sample = right[position]; 234 | position += 1; 235 | 236 | if clipped == false && (left_sample.abs() > 1.0 || right_sample.abs() > 1.0) { 237 | clipped = true; 238 | println!("WARNING: The signal is clipping!"); 239 | } 240 | 241 | buffer[2*f] = left_sample; 242 | buffer[2*f+1] = right_sample; 243 | } else { 244 | buffer[2*f] = 0.0; 245 | buffer[2*f+1] = 0.0; 246 | } 247 | } 248 | pa::Continue 249 | }; 250 | 251 | let mut stream = try!(pa.open_non_blocking_stream(settings, callback)); 252 | try!(stream.start()); 253 | 254 | println!("Playing for 20 seconds."); 255 | pa.sleep(20 * 1_000); 256 | 257 | try!(stream.stop()); 258 | try!(stream.close()); 259 | 260 | println!("Finished!"); 261 | 262 | Ok(()) 263 | } 264 | -------------------------------------------------------------------------------- /src/clock.rs: -------------------------------------------------------------------------------- 1 | use std::cell::*; 2 | use std::rc::Rc; 3 | 4 | use consts; 5 | 6 | pub struct Clock { 7 | time: Cell 8 | } 9 | 10 | impl Clock { 11 | pub fn new() -> Rc { 12 | Rc::new(Self { time: Cell::new(0) }) 13 | } 14 | 15 | pub fn time(&self) -> u64 { 16 | self.time.get() 17 | } 18 | 19 | pub fn increment(&self) { 20 | self.time.set(self.time.get() + (consts::CHUNK_SIZE as u64)); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/consts.rs: -------------------------------------------------------------------------------- 1 | 2 | pub const SAMPLE_RATE : u32 = 44100; 3 | pub const CHUNK_SIZE : usize = 4096; 4 | /* This won't create a bug unless people are working with eons of audio. */ 5 | pub const TIME_INFINITY : u64 = 0xFFFFFFFFFFFFFFFF; 6 | -------------------------------------------------------------------------------- /src/conversions.rs: -------------------------------------------------------------------------------- 1 | use consts; 2 | 3 | // Right now this only supports 4/4 time. 4 | pub struct TimeCalculator { 5 | sample_time: u64, 6 | bpm: f64 7 | } 8 | 9 | impl TimeCalculator { 10 | pub fn new(bpm: f64) -> Self { 11 | Self { sample_time: 0, bpm } 12 | } 13 | 14 | pub fn add_seconds(&self, seconds: f64) -> Self { 15 | Self { 16 | sample_time: self.sample_time + (consts::SAMPLE_RATE as f64 * seconds) as u64, 17 | bpm: self.bpm 18 | } 19 | } 20 | 21 | pub fn add_quarters(&self, quarters: f64) -> Self { 22 | let seconds_per_beat = 60.0 / self.bpm; 23 | self.add_seconds(seconds_per_beat * quarters) 24 | } 25 | 26 | pub fn add_bars(&self, bars: f64) -> Self { 27 | self.add_quarters(bars * 4.0) 28 | } 29 | 30 | pub fn add_eighths(&self, eigths: f64) -> Self { 31 | self.add_quarters(eigths / 2.0) 32 | } 33 | 34 | pub fn add_sixteenths(&self, sixteenths: f64) -> Self { 35 | self.add_quarters(sixteenths / 4.0) 36 | } 37 | 38 | pub fn time(&self) -> u64 { 39 | self.sample_time 40 | } 41 | } 42 | 43 | pub fn decibels(db: f32) -> f32 { 44 | 10.0_f32.powf(db/20.0) 45 | } 46 | -------------------------------------------------------------------------------- /src/device.rs: -------------------------------------------------------------------------------- 1 | use std::cell::*; 2 | use std::rc::Rc; 3 | use clock::Clock; 4 | use consts; 5 | 6 | pub struct MonoStateContainer { 7 | clock: Rc, 8 | output_time: Cell, 9 | output: RefCell>, 10 | state: RefCell 11 | } 12 | 13 | impl MonoStateContainer { 14 | pub fn new(clock: Rc, state: T) -> Self { 15 | Self { 16 | clock: clock, 17 | output_time: Cell::new(consts::TIME_INFINITY), 18 | output: RefCell::new(vec![0.0; consts::CHUNK_SIZE]), 19 | state: RefCell::new(state) 20 | } 21 | } 22 | 23 | pub fn clock_advanced(&self) -> bool { 24 | self.output_time.get() != self.clock.time() 25 | } 26 | 27 | pub fn time(&self) -> u64 { 28 | self.clock.time() 29 | } 30 | 31 | pub fn mark_as_up_to_date(&self) { 32 | self.output_time.set(self.clock.time()); 33 | } 34 | 35 | pub fn borrow_output(&self) -> Ref> { 36 | self.output.borrow() 37 | } 38 | 39 | pub fn borrow_to_modify(&self) -> RefMut> { 40 | self.output.borrow_mut() 41 | } 42 | 43 | pub fn borrow_state_mut(&self) -> RefMut { 44 | self.state.borrow_mut() 45 | } 46 | } 47 | 48 | pub trait MonoEmitter { 49 | // The returned Ref must go out of scope before output() can be called again. 50 | fn output(&self) -> Ref>; 51 | } 52 | 53 | pub struct StereoStateContainer { 54 | clock: Rc, 55 | output_time: Cell, 56 | left: RefCell>, 57 | right: RefCell>, 58 | state: RefCell 59 | } 60 | 61 | impl StereoStateContainer { 62 | pub fn new(clock: Rc, state: T) -> Self { 63 | Self { 64 | clock: clock, 65 | output_time: Cell::new(consts::TIME_INFINITY), 66 | left: RefCell::new(vec![0.0; consts::CHUNK_SIZE]), 67 | right: RefCell::new(vec![0.0; consts::CHUNK_SIZE]), 68 | state: RefCell::new(state) 69 | } 70 | } 71 | 72 | pub fn clock_advanced(&self) -> bool { 73 | self.output_time.get() != self.clock.time() 74 | } 75 | 76 | pub fn time(&self) -> u64 { 77 | self.clock.time() 78 | } 79 | 80 | pub fn mark_as_up_to_date(&self) { 81 | self.output_time.set(self.clock.time()); 82 | } 83 | 84 | pub fn borrow_output(&self) -> (Ref>, Ref>) { 85 | (self.left.borrow(), self.right.borrow()) 86 | } 87 | 88 | pub fn borrow_left_to_modify(&self) -> RefMut> { 89 | self.left.borrow_mut() 90 | } 91 | 92 | pub fn borrow_right_to_modify(&self) -> RefMut> { 93 | self.right.borrow_mut() 94 | } 95 | 96 | pub fn borrow_state_mut(&self) -> RefMut { 97 | self.state.borrow_mut() 98 | } 99 | } 100 | 101 | pub trait StereoEmitter { 102 | fn output(&self) -> (Ref>, Ref>); 103 | } 104 | -------------------------------------------------------------------------------- /src/effects.rs: -------------------------------------------------------------------------------- 1 | 2 | use device::*; 3 | use clock::*; 4 | use std::rc::Rc; 5 | use std::cell::*; 6 | use consts; 7 | use conversions; 8 | 9 | 10 | pub struct Mixer { 11 | inputs: Vec>, 12 | device: StereoStateContainer<()> 13 | } 14 | 15 | impl Mixer { 16 | pub fn new(clock: Rc, inputs: Vec>) -> Rc { 17 | Rc::new(Self { inputs: inputs, device: StereoStateContainer::<()>::new(clock, ()) }) 18 | } 19 | } 20 | 21 | impl StereoEmitter for Mixer { 22 | fn output(&self) -> (Ref>, Ref>) { 23 | if self.device.clock_advanced() { 24 | self.device.mark_as_up_to_date(); 25 | 26 | let mut left = self.device.borrow_left_to_modify(); 27 | let mut right = self.device.borrow_right_to_modify(); 28 | 29 | for i in 0..consts::CHUNK_SIZE { 30 | // TODO: just use the buffer instead of temp variables 31 | let mut ls = 0.0; 32 | for j in 0..self.inputs.len() { 33 | ls += self.inputs[j].output().0[i]; 34 | } 35 | left[i] = ls; 36 | 37 | let mut rs = 0.0; 38 | for j in 0..self.inputs.len() { 39 | rs += self.inputs[j].output().1[i]; 40 | } 41 | right[i] = rs; 42 | } 43 | 44 | } 45 | self.device.borrow_output() 46 | } 47 | } 48 | 49 | pub struct Gain { 50 | device: StereoStateContainer<()>, 51 | input: Rc, 52 | boost: Rc 53 | } 54 | 55 | impl Gain { 56 | pub fn new(clock: Rc, input: Rc, boost: Rc) -> Rc { 57 | Rc::new(Self { device: StereoStateContainer::<()>::new(clock, ()), input, boost }) 58 | } 59 | } 60 | 61 | impl StereoEmitter for Gain { 62 | fn output(&self) -> (Ref>, Ref>) { 63 | if self.device.clock_advanced() { 64 | self.device.mark_as_up_to_date(); 65 | 66 | let mut left = self.device.borrow_left_to_modify(); 67 | let mut right = self.device.borrow_right_to_modify(); 68 | 69 | let boost_buf = self.boost.output(); 70 | let input_left = self.input.output().0; 71 | let input_right = self.input.output().1; 72 | 73 | for i in 0..consts::CHUNK_SIZE { 74 | left[i] = boost_buf[i]*input_left[i]; 75 | right[i] = boost_buf[i]*input_right[i]; 76 | } 77 | } 78 | self.device.borrow_output() 79 | } 80 | } 81 | 82 | pub struct ConstSignal { 83 | device: MonoStateContainer<()>, 84 | value: f32 85 | } 86 | 87 | impl ConstSignal { 88 | pub fn new(clock: Rc, value: f32) -> Rc { 89 | Rc::new(Self { device: MonoStateContainer::<()>::new(clock, ()), value: value }) 90 | } 91 | } 92 | 93 | impl MonoEmitter for ConstSignal { 94 | fn output(&self) -> Ref> { 95 | if self.device.clock_advanced() { 96 | self.device.mark_as_up_to_date(); 97 | let mut chunk = self.device.borrow_to_modify(); 98 | 99 | for i in 0..chunk.len() { 100 | chunk[i] = self.value; 101 | } 102 | } 103 | self.device.borrow_output() 104 | } 105 | } 106 | 107 | pub struct Pan { 108 | device: StereoStateContainer<()>, 109 | input: Rc, 110 | position: Rc, 111 | } 112 | 113 | impl Pan { 114 | pub fn new(clock: Rc, input: Rc, position: Rc) -> Rc { 115 | Rc::new(Self { device: StereoStateContainer::<()>::new(clock, ()), input, position }) 116 | } 117 | } 118 | 119 | impl StereoEmitter for Pan { 120 | fn output(&self) -> (Ref>, Ref>) { 121 | if self.device.clock_advanced() { 122 | self.device.mark_as_up_to_date(); 123 | 124 | let mut left = self.device.borrow_left_to_modify(); 125 | let mut right = self.device.borrow_right_to_modify(); 126 | let input_left = self.input.output().0; 127 | let input_right = self.input.output().1; 128 | 129 | let positions = self.position.output(); 130 | 131 | for i in 0..consts::CHUNK_SIZE { 132 | 133 | let minus3 = conversions::decibels(-3.0); 134 | 135 | let pan_pow2 = positions[i].powf(2.0); 136 | let less = minus3 - pan_pow2 * minus3; 137 | let more = minus3 + pan_pow2 * (1.0 - minus3); 138 | 139 | if positions[i] > 0.0 { 140 | left[i] = input_left[i]*less; 141 | right[i] = input_right[i]*more; 142 | } else { 143 | left[i] = input_left[i]*more; 144 | right[i] = input_right[i]*less; 145 | } 146 | } 147 | } 148 | self.device.borrow_output() 149 | } 150 | } 151 | 152 | pub struct MonoToStereo { 153 | device: StereoStateContainer<()>, 154 | input: Rc 155 | } 156 | 157 | impl MonoToStereo { 158 | pub fn new(clock: Rc, input: Rc) -> Rc { 159 | Rc::new(Self { 160 | device: StereoStateContainer::<()>::new(clock, ()), 161 | input 162 | }) 163 | } 164 | } 165 | 166 | impl StereoEmitter for MonoToStereo { 167 | fn output(&self) -> (Ref>, Ref>) { 168 | if self.device.clock_advanced() { 169 | self.device.mark_as_up_to_date(); 170 | 171 | let output = self.input.output(); 172 | let mut left = self.device.borrow_left_to_modify(); 173 | let mut right = self.device.borrow_right_to_modify(); 174 | 175 | for i in 0..consts::CHUNK_SIZE { 176 | left[i] = output[i]; 177 | right[i] = output[i]; 178 | } 179 | } 180 | self.device.borrow_output() 181 | } 182 | } 183 | 184 | pub struct StereoToMono { 185 | device: MonoStateContainer<()>, 186 | input: Rc 187 | } 188 | 189 | impl StereoToMono { 190 | pub fn new(clock: Rc, input: Rc) -> Rc { 191 | Rc::new(Self { 192 | device: MonoStateContainer::<()>::new(clock, ()), 193 | input 194 | }) 195 | } 196 | } 197 | 198 | impl MonoEmitter for StereoToMono { 199 | fn output(&self) -> Ref> { 200 | if self.device.clock_advanced() { 201 | self.device.mark_as_up_to_date(); 202 | 203 | let mut output = self.device.borrow_to_modify(); 204 | let left = self.input.output().0; 205 | let right = self.input.output().1; 206 | 207 | for i in 0..consts::CHUNK_SIZE { 208 | output[i] = 0.5*(left[i] + right[i]); 209 | } 210 | } 211 | self.device.borrow_output() 212 | } 213 | } 214 | 215 | 216 | pub trait WaveShaper { 217 | fn shape(&self, sample: f32) -> f32; 218 | } 219 | 220 | pub struct HardClipper { 221 | 222 | } 223 | 224 | impl HardClipper { 225 | pub fn new() -> Rc { 226 | Rc::new(Self { }) 227 | } 228 | } 229 | 230 | impl WaveShaper for HardClipper { 231 | fn shape(&self, sample: f32) -> f32 { 232 | if sample > 1.0 { 233 | 1.0 234 | } else if sample < -1.0 { 235 | -1.0 236 | } else { 237 | sample 238 | } 239 | } 240 | } 241 | 242 | pub struct WaveShaperEffect { 243 | device: StereoStateContainer<()>, 244 | input: Rc, 245 | shaper: Rc 246 | } 247 | 248 | impl WaveShaperEffect { 249 | pub fn new(clock: Rc, input: Rc, shaper: Rc) -> Rc { 250 | Rc::new(Self { 251 | device: StereoStateContainer::<()>::new(clock, ()), 252 | input, 253 | shaper 254 | }) 255 | } 256 | } 257 | 258 | impl StereoEmitter for WaveShaperEffect { 259 | fn output(&self) -> (Ref>, Ref>) { 260 | if self.device.clock_advanced() { 261 | self.device.mark_as_up_to_date(); 262 | 263 | let input_left = self.input.output().0; 264 | let input_right = self.input.output().1; 265 | let mut left = self.device.borrow_left_to_modify(); 266 | let mut right = self.device.borrow_right_to_modify(); 267 | 268 | for i in 0..consts::CHUNK_SIZE { 269 | left[i] = self.shaper.shape(input_left[i]); 270 | right[i] = self.shaper.shape(input_right[i]); 271 | } 272 | } 273 | self.device.borrow_output() 274 | } 275 | } 276 | 277 | // 278 | //struct Delay { 279 | // 280 | //} 281 | // 282 | //struct Fader { 283 | // 284 | //} 285 | // struct SoftClipper { 286 | // 287 | // } 288 | -------------------------------------------------------------------------------- /src/events.rs: -------------------------------------------------------------------------------- 1 | use std::cell::*; 2 | use std::rc::Rc; 3 | use clock::Clock; 4 | use consts; 5 | 6 | pub struct EventSource { 7 | clock: Rc, 8 | events: Vec<(u64, T)>, 9 | cursor: Cell, 10 | last_time: Cell 11 | } 12 | 13 | impl EventSource { 14 | pub fn new(events: Vec<(u64, T)>, clock: Rc) -> Rc { 15 | if !Self::is_sorted(&events) { 16 | panic!("Events are not sorted by time!"); 17 | } 18 | Rc::new(Self { 19 | clock: clock, 20 | events: events, 21 | cursor: Cell::new(0), 22 | last_time: Cell::new(consts::TIME_INFINITY) 23 | }) 24 | } 25 | 26 | fn is_sorted(events: &Vec<(u64, T)>) -> bool { 27 | if events.len() == 0 { 28 | return true; 29 | } else { 30 | let mut max = events[0].0; 31 | for i in 1..events.len() { 32 | if events[i].0 < max { 33 | //println!("{} {}", events[i].0, max); 34 | return false; 35 | } else { 36 | max = events[i].0; 37 | } 38 | } 39 | return true; 40 | } 41 | } 42 | 43 | pub fn events_this_chunk<'b>(&'b self) -> &'b[(u64, T)] { 44 | let time = self.clock.time(); 45 | if self.last_time.get() != self.clock.time() { 46 | self.last_time.set(self.clock.time()); 47 | 48 | // Skip past all events before the current time. 49 | while self.cursor.get() < self.events.len() && self.events[self.cursor.get()].0 < time { 50 | self.cursor.set(self.cursor.get() + 1); 51 | } 52 | } 53 | let start = self.cursor.get(); 54 | let mut end = start; 55 | while end < self.events.len() && self.events[end].0 < time + (consts::CHUNK_SIZE as u64) { 56 | end += 1; 57 | } 58 | &self.events[start..end] 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/files.rs: -------------------------------------------------------------------------------- 1 | use hound; 2 | use std::i32; 3 | use std::i16; 4 | 5 | pub fn load_wav_to_stereo(filename: &str) -> (Vec, Vec) { 6 | let mut reader = hound::WavReader::open(filename).unwrap(); 7 | let spec = reader.spec(); 8 | if spec.channels != 2 || spec.sample_rate != 44100 { 9 | panic!("Sorry, this only supports 2-channel 44.1kHz WAV."); 10 | } 11 | 12 | let samples = reader.samples::(); 13 | 14 | let mut left = Vec::::new(); 15 | let mut right = Vec::::new(); 16 | 17 | let mut channel = 0; 18 | 19 | for sample in samples { 20 | if channel % 2 == 0 { 21 | left.push((sample.unwrap() as f32) / 2.0_f32.powf(spec.bits_per_sample as f32)); 22 | } else { 23 | right.push((sample.unwrap() as f32) / 2.0_f32.powf(spec.bits_per_sample as f32)); 24 | } 25 | channel = (channel + 1) % 2; 26 | } 27 | 28 | assert!(left.len() == right.len()); 29 | 30 | (left, right) 31 | } 32 | 33 | pub fn save_stereo_to_wav(left: &Vec, right: &Vec, filename: &str) { 34 | assert!(left.len() == right.len()); 35 | 36 | let spec = hound::WavSpec { 37 | channels: 2, 38 | sample_rate: 44100, 39 | bits_per_sample: 16, 40 | sample_format: hound::SampleFormat::Int, 41 | }; 42 | let mut writer = hound::WavWriter::create(filename, spec).unwrap(); 43 | 44 | for i in 0..left.len() { 45 | let amplitude = i16::MAX as f32; 46 | writer.write_sample((left[i] * amplitude) as i16).unwrap(); 47 | writer.write_sample((right[i] * amplitude) as i16).unwrap(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate rand; 2 | extern crate portaudio; 3 | extern crate hound; 4 | 5 | pub mod clock; 6 | pub mod consts; 7 | pub mod synth; 8 | pub mod events; 9 | pub mod device; 10 | pub mod effects; 11 | pub mod conversions; 12 | pub mod sampler; 13 | pub mod files; 14 | 15 | use clock::*; 16 | use device::*; 17 | use std::rc::Rc; 18 | 19 | pub fn render_audio(clock: Rc, master: Rc, length: usize) -> (Vec, Vec) { 20 | let mut left = Vec::::new(); 21 | let mut right = Vec::::new(); 22 | 23 | while left.len() < length { 24 | assert!(master.output().0.len() == master.output().1.len()); 25 | for i in 0..master.output().0.len() { 26 | left.push(master.output().0[i]); 27 | right.push(master.output().1[i]); 28 | } 29 | clock.increment(); 30 | } 31 | 32 | assert!(left.len() == right.len()); 33 | 34 | left.truncate(length); 35 | right.truncate(length); 36 | 37 | (left, right) 38 | } 39 | -------------------------------------------------------------------------------- /src/sampler.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | use device::*; 3 | use clock::Clock; 4 | use events::EventSource; 5 | use consts; 6 | use std::cell::*; 7 | 8 | pub enum SamplerEvent { 9 | Play, 10 | PlayAtSpeed(f64) 11 | } 12 | 13 | pub struct Sampler { 14 | device: StereoStateContainer, 15 | sampler_events: Rc>, 16 | left_samples: Vec, 17 | right_samples: Vec 18 | } 19 | 20 | struct SamplerState { 21 | playing: bool, 22 | position: f64, 23 | playspeed: f64 24 | } 25 | 26 | impl Sampler { 27 | pub fn new(clock: Rc, sampler_events: Rc>, left_samples: Vec, right_samples: Vec) -> Rc { 28 | assert!(left_samples.len() == right_samples.len()); 29 | Rc::new(Self { 30 | device: StereoStateContainer::new(clock, SamplerState { playing: false, position: 0.0, playspeed: 1.0 }), 31 | left_samples, right_samples, sampler_events 32 | }) 33 | } 34 | } 35 | 36 | impl StereoEmitter for Sampler { 37 | fn output(&self) -> (Ref>, Ref>) { 38 | if self.device.clock_advanced() { 39 | self.device.mark_as_up_to_date(); 40 | 41 | let mut left = self.device.borrow_left_to_modify(); 42 | let mut right = self.device.borrow_right_to_modify(); 43 | let mut state = self.device.borrow_state_mut(); 44 | 45 | let events = self.sampler_events.events_this_chunk(); 46 | let mut cursor = 0; 47 | 48 | for i in 0..consts::CHUNK_SIZE { 49 | 50 | while cursor < events.len() && events[cursor].0 == self.device.time() + i as u64 { 51 | match events[cursor].1 { 52 | SamplerEvent::Play => { 53 | state.playing = true; 54 | state.position = 0.0; 55 | state.playspeed = 1.0; 56 | } 57 | SamplerEvent::PlayAtSpeed(speed) => { 58 | state.playing = true; 59 | state.position = 0.0; 60 | state.playspeed = speed; 61 | } 62 | } 63 | cursor += 1 64 | } 65 | 66 | debug_assert!(self.left_samples.len() == self.right_samples.len()); 67 | if state.position.floor() as usize >= self.left_samples.len() { 68 | state.playing = false; 69 | } 70 | if state.playing { 71 | left[i] = self.left_samples[state.position.floor() as usize]; 72 | right[i] = self.right_samples[state.position.floor() as usize]; 73 | state.position += 1.0 * state.playspeed; 74 | } else { 75 | left[i] = 0.0; 76 | right[i] = 0.0; 77 | } 78 | } 79 | } 80 | self.device.borrow_output() 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/synth.rs: -------------------------------------------------------------------------------- 1 | use rand; 2 | 3 | use std::rc::Rc; 4 | use device::*; 5 | use clock::Clock; 6 | use events::EventSource; 7 | use consts; 8 | use std::cell::*; 9 | 10 | // Provides full-resolution down to 21 Hz for 44.1kHz sample rate. 11 | const WAVE_SAMPLES : usize = 2010; 12 | 13 | pub enum NoteEvent { 14 | NoteOn(f32), 15 | NoteOff 16 | } 17 | 18 | #[derive(Clone)] 19 | pub struct Wave { 20 | samples: [f32; WAVE_SAMPLES] 21 | } 22 | 23 | impl Wave { 24 | pub fn square() -> Self { 25 | let mut samples = [-1.0; WAVE_SAMPLES]; 26 | for i in 0..samples.len()/2 { 27 | samples[i] = 1.0; 28 | } 29 | Self { samples } 30 | } 31 | 32 | pub fn saw() -> Self { 33 | let mut samples = [0.0; WAVE_SAMPLES]; 34 | for i in 0..samples.len() { 35 | samples[i] = 1.0 - 2.0*(i as f32 / (samples.len() - 1) as f32); 36 | } 37 | Self { samples } 38 | } 39 | 40 | pub fn triangle() -> Self { 41 | let mut samples = [0.0; WAVE_SAMPLES]; 42 | debug_assert!(samples.len() % 2 == 0); 43 | for i in 0..samples.len() { 44 | if i < samples.len() / 2 { 45 | samples[i] = -1.0 + 2.0 * (i as f32 / (samples.len() / 2 - 1) as f32); 46 | } else { 47 | samples[i] = samples[samples.len() - i - 1] 48 | } 49 | } 50 | Self { samples } 51 | } 52 | 53 | pub fn zero() -> Self { 54 | Self { samples: [0.0; WAVE_SAMPLES] } 55 | } 56 | } 57 | 58 | #[derive(Clone)] 59 | pub struct WaveTable { 60 | waves: Vec 61 | // TODO: ensure that waves.len() == WAVE_SAMPLES 62 | } 63 | 64 | impl WaveTable { 65 | pub fn new(waves: Vec) -> Self { 66 | assert!(waves.len() >= 1); 67 | Self { waves } 68 | } 69 | } 70 | 71 | pub struct Envelope { 72 | device: MonoStateContainer, 73 | // TODO: attack and release 74 | note_events: Rc> 75 | } 76 | 77 | struct EnvelopeState { 78 | on: bool 79 | } 80 | 81 | impl Envelope { 82 | pub fn new (clock: Rc, note_events: Rc>) -> Rc { 83 | Rc::new(Self { device: MonoStateContainer::::new(clock, EnvelopeState { on: false }), note_events }) 84 | } 85 | } 86 | 87 | impl MonoEmitter for Envelope { 88 | fn output(&self) -> Ref> { 89 | if self.device.clock_advanced() { 90 | self.device.mark_as_up_to_date(); 91 | 92 | let events = self.note_events.events_this_chunk(); 93 | let mut cursor = 0; 94 | 95 | let mut chunk = self.device.borrow_to_modify(); 96 | let mut state = self.device.borrow_state_mut(); 97 | 98 | 99 | for i in 0..chunk.len() { 100 | while cursor < events.len() && events[cursor].0 == self.device.time() + i as u64 { 101 | match events[cursor].1 { 102 | NoteEvent::NoteOff => { 103 | state.on = false; 104 | }, 105 | NoteEvent::NoteOn(_freq) => { 106 | state.on = true; 107 | } 108 | } 109 | cursor += 1 110 | } 111 | if state.on { 112 | chunk[i] = 1.0; 113 | } else { 114 | chunk[i] = 0.0; 115 | } 116 | } 117 | } 118 | self.device.borrow_output() 119 | } 120 | } 121 | 122 | pub struct Oscillator { 123 | device: MonoStateContainer, 124 | note_events: Rc>, 125 | detune_multiplier: Rc, 126 | } 127 | 128 | struct OscillatorState { 129 | position: f32, 130 | frequency: f32 131 | } 132 | 133 | impl Oscillator { 134 | pub fn new(clock: Rc, note_events: Rc>, detune_multiplier: Rc) -> Rc { 135 | Rc::new(Self { 136 | device: MonoStateContainer::::new(clock, OscillatorState { position: 0.0, frequency: 0.0 }), 137 | note_events, 138 | detune_multiplier 139 | }) 140 | } 141 | } 142 | 143 | 144 | impl MonoEmitter for Oscillator { 145 | fn output(&self) -> Ref> { 146 | if self.device.clock_advanced() { 147 | self.device.mark_as_up_to_date(); 148 | 149 | let events = self.note_events.events_this_chunk(); 150 | let mut cursor = 0; 151 | 152 | let mut chunk = self.device.borrow_to_modify(); 153 | let multipliers = self.detune_multiplier.output(); 154 | let mut state = self.device.borrow_state_mut(); 155 | 156 | for i in 0..chunk.len() { 157 | while cursor < events.len() && events[cursor].0 == self.device.time() + i as u64 { 158 | match events[cursor].1 { 159 | NoteEvent::NoteOff => { 160 | // do nothing, osc keeps running 161 | }, 162 | NoteEvent::NoteOn(freq) => { 163 | state.frequency = freq; 164 | state.position = (rand::random::() * (WAVE_SAMPLES as f32)).floor(); 165 | } 166 | } 167 | cursor += 1 168 | } 169 | 170 | chunk[i] = state.position; 171 | // XXX: possible floating point inaccuracy over the long term 172 | let increment = WAVE_SAMPLES as f32 * state.frequency * multipliers[i] / (consts::SAMPLE_RATE as f32); 173 | state.position = (state.position + increment) % (WAVE_SAMPLES as f32); 174 | 175 | } 176 | 177 | } 178 | self.device.borrow_output() 179 | } 180 | } 181 | 182 | pub struct MonoSynth { 183 | device: MonoStateContainer<()>, 184 | wavetable: WaveTable, 185 | // Values in [0, WAVE_SAMPLES) 186 | oscillator: Rc, 187 | // Values in [0, 1], 0 being the first wave, 1 being the last. 188 | wavetable_position: Rc, 189 | // Values in [0, 1]. 190 | envelope: Rc, 191 | } 192 | 193 | impl MonoSynth { 194 | pub fn new(clock: Rc, wavetable: WaveTable, oscillator: Rc, wavetable_position: Rc, envelope: Rc) -> Rc { 195 | Rc::new(Self { 196 | device: MonoStateContainer::<()>::new(clock, ()), 197 | wavetable, 198 | oscillator, 199 | wavetable_position, 200 | envelope 201 | }) 202 | } 203 | } 204 | 205 | impl MonoEmitter for MonoSynth { 206 | fn output(&self) -> Ref> { 207 | if self.device.clock_advanced() { 208 | self.device.mark_as_up_to_date(); 209 | 210 | let mut output = self.device.borrow_to_modify(); 211 | 212 | let wave_position = self.oscillator.output(); 213 | let wavetable_position = self.wavetable_position.output(); 214 | let amplitude = self.envelope.output(); 215 | 216 | for i in 0..consts::CHUNK_SIZE { 217 | // For e.g. 10 waves we want it to work like: 218 | // 0: [0, 0.1) 219 | // 1: [0.1, 0.2), 220 | // ... 221 | // 9: [0.9, 1.0] <-- this square bracket is the reason for the "max" below. 222 | let mut wt_index = (wavetable_position[i].max(0.99999) * self.wavetable.waves.len() as f32).floor() as usize; 223 | output[i] = amplitude[i] * self.wavetable.waves[wt_index].samples[wave_position[i] as usize]; 224 | } 225 | } 226 | self.device.borrow_output() 227 | } 228 | } 229 | 230 | 231 | pub struct WhiteNoise { 232 | device: StereoStateContainer<()> 233 | } 234 | 235 | impl WhiteNoise { 236 | pub fn new(clock: Rc) -> Rc { 237 | Rc::new(Self { 238 | device: StereoStateContainer::new(clock, ()) 239 | }) 240 | } 241 | } 242 | 243 | impl StereoEmitter for WhiteNoise { 244 | fn output(&self) -> (Ref>, Ref>) { 245 | if self.device.clock_advanced() { 246 | self.device.mark_as_up_to_date(); 247 | 248 | let mut left = self.device.borrow_left_to_modify(); 249 | let mut right = self.device.borrow_right_to_modify(); 250 | 251 | for i in 0..consts::CHUNK_SIZE { 252 | left[i] = 1.0 - 2.0 * rand::random::(); 253 | right[i] = 1.0 - 2.0 * rand::random::(); 254 | } 255 | } 256 | self.device.borrow_output() 257 | } 258 | } 259 | --------------------------------------------------------------------------------