├── .gitignore ├── .travis.yml ├── Cargo.toml ├── README.md ├── examples ├── duplex_blocking.rs ├── duplex_non_blocking.rs └── sine.rs └── src ├── error.rs ├── lib.rs ├── settings.rs ├── stream ├── duplex.rs ├── input.rs ├── mod.rs └── output.rs └── utils.rs /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .DS_Store 3 | *~ 4 | *# 5 | *.o 6 | *.so 7 | *.swp 8 | *.dylib 9 | *.dSYM 10 | *.dll 11 | *.rlib 12 | *.dummy 13 | *.exe 14 | *-test 15 | /bin/main 16 | /bin/test-internal 17 | /bin/test-external 18 | /doc/ 19 | /target/ 20 | /build/ 21 | /.rust/ 22 | rusti.sh 23 | watch.sh 24 | /examples/** 25 | !/examples/*.rs 26 | !/examples/assets/ 27 | Cargo.lock 28 | 29 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | rust: 3 | - nightly 4 | - stable 5 | notifications: 6 | email: 7 | - mitchell.nordine@gmail.com 8 | os: 9 | - linux 10 | - osx 11 | env: 12 | - LD_LIBRARY_PATH=/usr/local/lib 13 | install: 14 | - curl -O http://www.portaudio.com/archives/pa_stable_v19_20140130.tgz 15 | - tar xfz pa_stable_v19_20140130.tgz 16 | - (cd portaudio/ && ./configure && make && sudo make install) 17 | before_script: 18 | - rustc --version 19 | - cargo --version 20 | script: 21 | - cargo build --verbose 22 | - cargo test --verbose 23 | - cargo doc --verbose 24 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | name = "sound_stream" 4 | version = "0.6.0" 5 | authors = ["mitchmindtree "] 6 | description = "Provides a simple interface to the default audio input and output device streams on a user's system." 7 | readme = "README.md" 8 | keywords = ["audio", "dsp", "io", "music", "stream"] 9 | license = "MIT" 10 | repository = "https://github.com/RustAudio/sound_stream.git" 11 | homepage = "https://github.com/RustAudio/sound_stream" 12 | 13 | 14 | [dependencies] 15 | num = { version = "0.1.27", default-features = false } 16 | portaudio = "0.5.1" 17 | sample = "0.2.0" 18 | time = "0.1.32" 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DEPRECATED 2 | 3 | [rust-portaudio](https://github.com/jeremyletang/rust-portaudio)'s API has improved enough to the point where **sound_stream** is no longer necessary. 4 | 5 | # SoundStream [![Build Status](https://travis-ci.org/RustAudio/sound_stream.svg?branch=master)](https://travis-ci.org/RustAudio/sound_stream) 6 | 7 | A simple-as-possible, *fast* audio I/O stream wrapping PortAudio for Rust! It looks like this: 8 | 9 | ```Rust 10 | // The callback we'll use to pass to the Stream. It will write the input directly to the output. 11 | let f = Box::new(move |input: &[f32], _: Settings, 12 | output: &mut[f32], _: Settings, 13 | dt: f64, _: CallbackFlags| { 14 | for (output_sample, input_sample) in o.iter_mut().zip(i.iter()) { 15 | *output_sample = *input_sample; 16 | } 17 | CallbackResult::Continue 18 | }); 19 | 20 | // Run our callback on the default, duplex, non-blocking stream. 21 | let stream = SoundStream::new() 22 | .duplex(StreamParams::new(), StreamParams::new()) 23 | .run_callback(f) 24 | .unwrap(); 25 | ``` 26 | 27 | 28 | Usage 29 | ----- 30 | 31 | Add sound_stream to your Cargo.toml dependencies like so: 32 | 33 | ``` 34 | [dependencies] 35 | sound_stream = "*" 36 | ``` 37 | 38 | For more details, see the [examples](https://github.com/RustAudio/sound_stream/tree/master/examples). 39 | 40 | PortAudio 41 | --------- 42 | 43 | SoundStream uses [PortAudio](http://www.portaudio.com) as a cross-platform audio backend. The [rust-portaudio](https://github.com/jeremyletang/rust-portaudio) dependency will first try to find an already installed version on your system before trying to download it and build PortAudio itself. 44 | 45 | License 46 | ------- 47 | 48 | MIT - Same license as [PortAudio](http://www.portaudio.com/license.html). 49 | 50 | -------------------------------------------------------------------------------- /examples/duplex_blocking.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! A simple-as-possible example showing how to construct and use a blocking duplex stream. 3 | //! 4 | //! In this example we just copy the input buffer straight to the output (beware of feedback). 5 | //! 6 | //! NOTE: It is recommended to use the non-blocking stream instead when possible as blocking 7 | //! streams are currently unstable and trickier to synchronise. 8 | //! 9 | 10 | extern crate sound_stream; 11 | 12 | use sound_stream::{SoundStream, StreamParams}; 13 | use sound_stream::duplex::Event; 14 | 15 | fn main() { 16 | 17 | // Construct the default duplex stream that produces 512 frames per buffer. 18 | let mut stream = SoundStream::new() 19 | .frames_per_buffer(128) 20 | .duplex::(StreamParams::new(), StreamParams::new()) 21 | .run().unwrap(); 22 | 23 | // We'll use this to count down from 3 seconds before breaking from the loop. 24 | let mut count = 3.0; 25 | 26 | // We'll use this to copy the input buffer straight to the output buffer. 27 | let mut intermediate = Vec::new(); 28 | 29 | for event in stream.by_ref() { 30 | match event { 31 | Event::In(input, _) => { ::std::mem::replace(&mut intermediate, input); } 32 | Event::Out(output, settings) => { 33 | for (output_sample, sample) in output.iter_mut().zip(intermediate.iter()) { 34 | *output_sample = *sample; 35 | } 36 | count -= settings.frames as f32 / settings.sample_hz as f32; 37 | if count <= 0.0 { break } 38 | } 39 | } 40 | } 41 | 42 | } 43 | 44 | -------------------------------------------------------------------------------- /examples/duplex_non_blocking.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! A simple-as-possible example showing how to construct and use a non-blocking duplex stream. 3 | //! 4 | //! In this example we just copy the input buffer straight to the output (beware of feedback). 5 | //! 6 | 7 | extern crate sound_stream; 8 | 9 | use sound_stream::{CallbackFlags, CallbackResult, SoundStream, Settings, StreamParams}; 10 | 11 | fn main() { 12 | 13 | // We'll use this to count down from 3 seconds before breaking from the loop. 14 | let mut count = 3.0; 15 | 16 | // The callback we'll use to pass to the Stream. It will write the input directly to the output. 17 | let f = Box::new(move |i: &[f32], _: Settings, o: &mut[f32], _: Settings, dt: f64, _: CallbackFlags| { 18 | for (output_sample, input_sample) in o.iter_mut().zip(i.iter()) { 19 | *output_sample = *input_sample; 20 | } 21 | count -= dt; 22 | if count >= 0.0 { CallbackResult::Continue } else { CallbackResult::Complete } 23 | }); 24 | 25 | // Construct the default duplex stream that produces 512 frames per buffer. 26 | let stream = SoundStream::new() 27 | .frames_per_buffer(512) 28 | .duplex(StreamParams::new(), StreamParams::new()) 29 | .run_callback(f) 30 | .unwrap(); 31 | 32 | while let Ok(true) = stream.is_active() {} 33 | 34 | } 35 | 36 | -------------------------------------------------------------------------------- /examples/sine.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! Generate a 440hz sine wave with sound_stream's non-blocking output stream. 3 | //! 4 | 5 | extern crate sound_stream; 6 | 7 | use sound_stream::{CallbackFlags, CallbackResult, SoundStream, Settings, StreamParams}; 8 | 9 | /// Produce a sine wave given some phase. 10 | fn sine_wave(phase: f64) -> f32 { 11 | ((phase * ::std::f64::consts::PI * 2.0).sin() * 0.5) as f32 12 | } 13 | 14 | fn main() { 15 | 16 | // We'll use this to count down from 3 seconds before breaking from the loop. 17 | let mut count = 3.0; 18 | 19 | // We'll use this as the phase for our oscillator. 20 | let mut phase = 0.0; 21 | 22 | // The callback we'll use to pass to the Stream. It will write a 440hz sine wave to the output. 23 | let callback = Box::new(move |output: &mut[f32], settings: Settings, dt: f64, _: CallbackFlags| { 24 | for frame in output.chunks_mut(settings.channels as usize) { 25 | let amp = sine_wave(phase); 26 | for channel in frame { 27 | *channel = amp; 28 | } 29 | phase += 440.0 / settings.sample_hz as f64; 30 | } 31 | count -= dt; 32 | if count >= 0.0 { CallbackResult::Continue } else { CallbackResult::Complete } 33 | }); 34 | 35 | // Construct the default, non-blocking output stream and run our callback. 36 | let stream = SoundStream::new().output(StreamParams::new()).run_callback(callback).unwrap(); 37 | 38 | while let Ok(true) = stream.is_active() {} 39 | 40 | } 41 | 42 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The sound_stream Error type. 3 | //! 4 | 5 | use portaudio::pa::error::Error as PortAudioError; 6 | 7 | /// A type for representing errors in sound_stream. 8 | #[derive(Debug, Copy, Clone)] 9 | pub enum Error { 10 | /// Errors returned by rust-portaudio. 11 | PortAudio(PortAudioError), 12 | } 13 | 14 | impl ::std::fmt::Display for Error { 15 | fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> { 16 | write!(f, "{:?}", self) 17 | } 18 | } 19 | 20 | impl ::std::error::Error for Error { 21 | fn description(&self) -> &str { 22 | use self::Error::*; 23 | match *self { 24 | PortAudio(ref err) => err.description(), 25 | } 26 | } 27 | } 28 | 29 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | extern crate num; 3 | extern crate portaudio as portaudio_lib; 4 | extern crate sample; 5 | extern crate time; 6 | 7 | 8 | pub use portaudio_lib as portaudio; 9 | 10 | pub use error::Error; 11 | pub use portaudio::pa::Sample as PaSample; 12 | pub use portaudio::pa::Stream as PaStream; 13 | pub use sample::{Amplitude, Sample, Wave}; 14 | pub use settings::{Settings, SampleHz, Frames, Channels}; 15 | pub use stream::Idx as DeviceIdx; 16 | pub use stream::{ 17 | input, 18 | output, 19 | duplex, 20 | CallbackFlags, 21 | CallbackResult, 22 | DeltaTimeSeconds, 23 | Latency, 24 | SoundStream, 25 | StreamFlags, 26 | StreamParams, 27 | }; 28 | 29 | mod error; 30 | mod settings; 31 | mod stream; 32 | mod utils; 33 | 34 | -------------------------------------------------------------------------------- /src/settings.rs: -------------------------------------------------------------------------------- 1 | 2 | pub type SampleHz = u32; 3 | pub type Frames = u16; 4 | pub type Channels = u16; 5 | 6 | /// Settings required for SoundStream. 7 | #[derive(Debug, Copy, Clone, PartialEq)] 8 | pub struct Settings { 9 | /// The number of samples per second. 10 | pub sample_hz: SampleHz, 11 | /// How many samples per channel requested at a time in the buffer. 12 | /// The more frames, the less likely to make glitches, 13 | /// but this gives slower response. 14 | pub frames: Frames, 15 | /// Number of channels, for example 2 for stereo sound (left + right speaker). 16 | pub channels: Channels, 17 | } 18 | 19 | impl Settings { 20 | 21 | /// Custom constructor for the Settings. 22 | pub fn new(sample_hz: SampleHz, frames: Frames, channels: Channels) -> Settings { 23 | Settings { 24 | sample_hz: sample_hz, 25 | frames: frames, 26 | channels: channels 27 | } 28 | } 29 | 30 | /// Default, standard constructor for Settings. 31 | pub fn cd_quality() -> Settings { 32 | Settings { 33 | sample_hz: 44100, 34 | frames: 256, 35 | channels: 2 36 | } 37 | } 38 | 39 | /// Return the length of a SoundBuffer that would use Settings. 40 | pub fn buffer_size(&self) -> usize { 41 | self.frames as usize * self.channels as usize 42 | } 43 | 44 | } 45 | 46 | impl ::std::default::Default for Settings { 47 | fn default() -> Settings { Settings::cd_quality() } 48 | } 49 | 50 | -------------------------------------------------------------------------------- /src/stream/duplex.rs: -------------------------------------------------------------------------------- 1 | 2 | use error::Error; 3 | use portaudio::pa; 4 | use portaudio::pa::Sample as PaSample; 5 | use sample::{Sample, Wave}; 6 | use settings::{Channels, Settings, Frames, SampleHz}; 7 | use std::collections::VecDeque; 8 | use std::marker::PhantomData; 9 | use utils::take_front; 10 | 11 | use super::{ 12 | BufferFrequency, 13 | CallbackFlags, 14 | CallbackResult, 15 | DeltaTimeSeconds, 16 | MINIMUM_BUFFER_RESERVATION, 17 | SoundStream, 18 | StreamFlags, 19 | StreamParams, 20 | wait_for_stream, 21 | }; 22 | 23 | 24 | /// A builder context for a duplex sound stream. 25 | pub struct Builder { 26 | pub stream_params: SoundStream, 27 | pub input_params: StreamParams, 28 | pub output_params: StreamParams, 29 | } 30 | 31 | 32 | /// An iterator of blocking stream events. 33 | pub struct BlockingStream<'a, I=Wave, O=Wave> 34 | where 35 | I: Sample + PaSample, 36 | O: Sample + PaSample, 37 | { 38 | /// Buffer the samples from the input until its length is equal to the buffer_length. 39 | input_buffer: VecDeque, 40 | /// Store samples in this until there is enough to write to the output stream. 41 | output_buffer: VecDeque, 42 | /// A buffer for retrieving samples from the user for writing. 43 | user_buffer: Vec, 44 | /// Number of input channels. 45 | in_channels: Channels, 46 | /// Number of output channels. 47 | out_channels: Channels, 48 | /// Stream sample rate. 49 | sample_hz: SampleHz, 50 | /// Frames per buffer. 51 | frames: Frames, 52 | /// The last event that has occured. 53 | last_event: Option, 54 | /// The port audio stream. 55 | stream: pa::Stream, 56 | is_closed: bool, 57 | marker: PhantomData<&'a ()>, 58 | } 59 | 60 | 61 | /// Stream callback function type. 62 | pub type Callback = 63 | Box CallbackResult>; 64 | 65 | /// A handle to the non-blocking duplex stream. 66 | pub struct NonBlockingStream 67 | where 68 | I: Sample + PaSample, 69 | O: Sample + PaSample, 70 | { 71 | /// The port audio stream. 72 | stream: pa::Stream, 73 | /// Whether or not the stream is currently closed. 74 | is_closed: bool, 75 | } 76 | 77 | /// An event to be returned by the BlockingStream. 78 | #[derive(Debug)] 79 | pub enum Event<'a, I=Wave, O=Wave> where O: 'a { 80 | /// Audio awaits on the stream's input buffer. 81 | In(Vec, Settings), 82 | /// The stream's output buffer is ready to be written to. 83 | Out(&'a mut [O], Settings), 84 | } 85 | 86 | /// Represents the current state of the BlockingStream. 87 | #[derive(Clone, Copy)] 88 | pub enum LastEvent { 89 | In, 90 | Out, 91 | Update, 92 | } 93 | 94 | /// The params to be unwrapped after the building is complete. 95 | type PaParams = (StreamFlags, pa::StreamParameters, pa::StreamParameters, f64, u32); 96 | 97 | impl Builder 98 | where 99 | I: Sample + PaSample, 100 | O: Sample + PaSample, 101 | { 102 | 103 | /// Retrieve the flags, stream parameters, sample rate and frames per buffer. 104 | fn unwrap_params(self) -> Result { 105 | let Builder { stream_params, input_params, output_params } = self; 106 | let SoundStream { maybe_buffer_frequency, maybe_sample_hz, maybe_flags } = stream_params; 107 | 108 | // Retrieve any stream flags. 109 | let flags = maybe_flags.unwrap_or_else(|| StreamFlags::empty()); 110 | 111 | // Construct the PortAudio input params from the sound stream ones. 112 | let input_params = { 113 | let idx = input_params.idx.unwrap_or_else(|| pa::device::get_default_input()); 114 | let info = match pa::device::get_info(idx) { 115 | Ok(info) => info, 116 | Err(err) => return Err(Error::PortAudio(err)), 117 | }; 118 | let channels = input_params.channel_count 119 | .map(|n| ::std::cmp::min(n, info.max_input_channels)) 120 | .unwrap_or_else(|| ::std::cmp::min(2, info.max_input_channels)); 121 | let sample_format = input_params.sample_format(); 122 | let suggested_latency = input_params.suggested_latency 123 | .unwrap_or_else(|| info.default_low_input_latency); 124 | pa::StreamParameters { 125 | device: idx, 126 | channel_count: channels, 127 | sample_format: sample_format, 128 | suggested_latency: suggested_latency, 129 | } 130 | }; 131 | 132 | // Construct the PortAudio output params from the sound stream ones. 133 | let output_params = { 134 | let idx = output_params.idx.unwrap_or_else(|| pa::device::get_default_output()); 135 | let info = match pa::device::get_info(idx) { 136 | Ok(info) => info, 137 | Err(err) => return Err(Error::PortAudio(err)), 138 | }; 139 | let channels = output_params.channel_count 140 | .map(|n| ::std::cmp::min(n, info.max_output_channels)) 141 | .unwrap_or_else(|| ::std::cmp::min(2, info.max_output_channels)); 142 | let sample_format = output_params.sample_format(); 143 | let suggested_latency = output_params.suggested_latency 144 | .unwrap_or_else(|| info.default_low_output_latency); 145 | pa::StreamParameters { 146 | device: idx, 147 | channel_count: channels, 148 | sample_format: sample_format, 149 | suggested_latency: suggested_latency, 150 | } 151 | }; 152 | 153 | // Determine the sample rate. 154 | let sample_hz = match maybe_sample_hz { 155 | Some(sample_hz) => sample_hz, 156 | None => match pa::device::get_info(input_params.device) { 157 | Ok(info) => info.default_sample_rate, 158 | Err(err) => return Err(Error::PortAudio(err)), 159 | }, 160 | }; 161 | 162 | // Determine the closest number of frames per buffer to the requested rate. 163 | let frames = match maybe_buffer_frequency { 164 | Some(BufferFrequency::Frames(frames)) => frames as u32, 165 | Some(BufferFrequency::Hz(hz)) => (sample_hz as f32 / hz).round() as u32, 166 | None => 0, 167 | }; 168 | 169 | Ok((flags, input_params, output_params, sample_hz, frames)) 170 | } 171 | 172 | /// Launch a non-blocking duplex stream with the given callback! 173 | #[inline] 174 | pub fn run_callback(self, mut callback: Callback) 175 | -> Result, Error> 176 | where I: 'static, 177 | O: 'static, 178 | { 179 | 180 | // Initialize PortAudio. 181 | try!(pa::initialize().map_err(|err| Error::PortAudio(err))); 182 | 183 | let (flags, input_params, output_params, sample_hz, frames) = try!(self.unwrap_params()); 184 | let in_channels = input_params.channel_count; 185 | let out_channels = output_params.channel_count; 186 | 187 | // Here we construct our PortAudio stream. 188 | let mut stream = pa::Stream::new(); 189 | 190 | // Remember the last time the callback was called so we can create the delta time. 191 | let mut maybe_last_time = None; 192 | 193 | // Construct a wrapper function around our callback. 194 | let f = Box::new(move |input: &[I], 195 | output: &mut[O], 196 | frames: u32, 197 | time_info: &pa::StreamCallbackTimeInfo, 198 | flags: pa::StreamCallbackFlags| -> pa::StreamCallbackResult { 199 | let in_settings = Settings { 200 | sample_hz: sample_hz as u32, 201 | frames: frames as u16, 202 | channels: in_channels as u16, 203 | }; 204 | let out_settings = Settings { channels: out_channels as u16, ..in_settings }; 205 | let dt = time_info.current_time - maybe_last_time.unwrap_or(time_info.current_time); 206 | maybe_last_time = Some(time_info.current_time); 207 | match callback(input, in_settings, output, out_settings, dt, flags) { 208 | CallbackResult::Continue => pa::StreamCallbackResult::Continue, 209 | CallbackResult::Complete => pa::StreamCallbackResult::Complete, 210 | CallbackResult::Abort => pa::StreamCallbackResult::Abort, 211 | } 212 | }); 213 | 214 | // Here we open the stream. 215 | try!(stream.open(Some(&input_params), Some(&output_params), sample_hz, frames, flags, Some(f)) 216 | .map_err(|err| Error::PortAudio(err))); 217 | 218 | // And now let's kick it off! 219 | try!(stream.start().map_err(|err| Error::PortAudio(err))); 220 | 221 | Ok(NonBlockingStream { stream: stream, is_closed: false }) 222 | } 223 | 224 | /// Launch a blocking duplex stream! 225 | #[inline] 226 | pub fn run<'a>(self) -> Result, Error> 227 | where I: 'static, 228 | O: 'static, 229 | { 230 | 231 | // Initialize PortAudio. 232 | try!(pa::initialize().map_err(|err| Error::PortAudio(err))); 233 | 234 | let (flags, input_params, output_params, sample_hz, frames) = try!(self.unwrap_params()); 235 | 236 | // Here we construct our PortAudio stream. 237 | let mut stream = pa::Stream::new(); 238 | 239 | // Here we open the stream. 240 | try!(stream.open(Some(&input_params), Some(&output_params), sample_hz, frames, flags, None) 241 | .map_err(|err| Error::PortAudio(err))); 242 | 243 | // And now let's kick it off! 244 | try!(stream.start().map_err(|err| Error::PortAudio(err))); 245 | 246 | let in_channels = input_params.channel_count; 247 | let double_input_buffer_len = (frames as usize * in_channels as usize) * 2; 248 | let input_buffer_len = ::std::cmp::max(double_input_buffer_len, MINIMUM_BUFFER_RESERVATION); 249 | 250 | let out_channels = output_params.channel_count; 251 | let double_output_buffer_len = (frames as usize * out_channels as usize) * 2; 252 | let output_buffer_len = ::std::cmp::max(double_output_buffer_len, MINIMUM_BUFFER_RESERVATION); 253 | 254 | Ok(BlockingStream { 255 | stream: stream, 256 | input_buffer: VecDeque::with_capacity(input_buffer_len), 257 | output_buffer: VecDeque::with_capacity(output_buffer_len), 258 | user_buffer: Vec::with_capacity(frames as usize * out_channels as usize), 259 | frames: frames as u16, 260 | in_channels: in_channels as u16, 261 | out_channels: out_channels as u16, 262 | sample_hz: sample_hz as u32, 263 | last_event: None, 264 | is_closed: false, 265 | marker: PhantomData, 266 | }) 267 | } 268 | 269 | } 270 | 271 | impl NonBlockingStream 272 | where 273 | I: Sample + PaSample, 274 | O: Sample + PaSample, 275 | { 276 | 277 | /// Close the stream and terminate PortAudio. 278 | pub fn close(&mut self) -> Result<(), Error> { 279 | self.is_closed = true; 280 | try!(self.stream.close().map_err(|err| Error::PortAudio(err))); 281 | try!(pa::terminate().map_err(|err| Error::PortAudio(err))); 282 | Ok(()) 283 | } 284 | 285 | /// Check whether or not the stream is currently active. 286 | pub fn is_active(&self) -> Result { 287 | self.stream.is_active().map_err(|err| Error::PortAudio(err)) 288 | } 289 | 290 | } 291 | 292 | impl Drop for NonBlockingStream 293 | where 294 | I: Sample + PaSample, 295 | O: Sample + PaSample, 296 | { 297 | fn drop(&mut self) { 298 | if !self.is_closed { 299 | if let Err(err) = self.close() { 300 | println!("An error occurred while closing NonBlockingStream: {}", err); 301 | } 302 | } 303 | } 304 | } 305 | 306 | impl<'a, I, O> BlockingStream<'a, I, O> 307 | where 308 | I: Sample + PaSample, 309 | O: Sample + PaSample, 310 | { 311 | /// Close the stream and terminate PortAudio. 312 | pub fn close(&mut self) -> Result<(), Error> { 313 | self.is_closed = true; 314 | try!(self.stream.close().map_err(|err| Error::PortAudio(err))); 315 | try!(pa::terminate().map_err(|err| Error::PortAudio(err))); 316 | Ok(()) 317 | } 318 | } 319 | 320 | impl<'a, I, O> Drop for BlockingStream<'a, I, O> 321 | where 322 | I: Sample + PaSample, 323 | O: Sample + PaSample, 324 | { 325 | fn drop(&mut self) { 326 | if !self.is_closed { 327 | if let Err(err) = self.close() { 328 | println!("An error occurred while closing BlockingStream: {}", err); 329 | } 330 | } 331 | } 332 | } 333 | 334 | impl<'a, I, O> Iterator for BlockingStream<'a, I, O> 335 | where 336 | I: Sample + PaSample + 'a, 337 | O: Sample + PaSample + 'a, 338 | { 339 | type Item = Event<'a, I, O>; 340 | 341 | fn next(&mut self) -> Option> { 342 | 343 | let BlockingStream { 344 | ref mut stream, 345 | ref mut input_buffer, 346 | ref mut output_buffer, 347 | ref mut user_buffer, 348 | ref mut last_event, 349 | ref frames, 350 | ref in_channels, 351 | ref out_channels, 352 | ref sample_hz, 353 | .. 354 | } = *self; 355 | 356 | let input_settings = Settings { channels: *in_channels, frames: *frames, sample_hz: *sample_hz }; 357 | let target_input_buffer_size = input_settings.buffer_size(); 358 | 359 | let output_settings = Settings { channels: *out_channels, frames: *frames, sample_hz: *sample_hz }; 360 | let target_output_buffer_size = output_settings.buffer_size(); 361 | 362 | // If the user_buffer was written to last event, take it's contents and append them to the 363 | // output_buffer for writing. 364 | if let Some(LastEvent::Out) = *last_event { 365 | // If some frames were written last event, add them to our output_buffer. 366 | if user_buffer.len() > 0 { 367 | output_buffer.extend(user_buffer.iter().map(|&sample| sample)); 368 | user_buffer.clear(); 369 | } 370 | // Considering the last event was an output event, let us check first for an input event. 371 | if input_buffer.len() >= target_input_buffer_size { 372 | let event_buffer = take_front(input_buffer, input_settings.buffer_size()); 373 | *last_event = Some(LastEvent::In); 374 | return Some(Event::In(event_buffer, input_settings)); 375 | } 376 | } 377 | 378 | // Loop until we can satisfy an event condition. 379 | loop { 380 | use std::error::Error as StdError; 381 | 382 | // How many frames are available on the input stream? 383 | let available_in_frames = match wait_for_stream(|| stream.get_stream_read_available()) { 384 | Ok(frames) => frames, 385 | Err(err) => { 386 | println!("An error occurred while requesting the number of available \ 387 | frames for reading from the input stream: {}. BlockingStream will \ 388 | now exit the event loop.", StdError::description(&err)); 389 | return None; 390 | }, 391 | }; 392 | 393 | // If there are frames available, let's take them and add them to our input_buffer. 394 | if available_in_frames > 0 { 395 | match stream.read(available_in_frames) { 396 | Ok(input_samples) => input_buffer.extend(input_samples.into_iter()), 397 | Err(err) => { 398 | println!("An error occurred while reading from the input stream: {}. \ 399 | BlockingStream will now exit the event loop.", 400 | StdError::description(&err)); 401 | return None; 402 | }, 403 | } 404 | } 405 | 406 | // How many frames are available for writing on the output stream? 407 | let available_out_frames = match wait_for_stream(|| stream.get_stream_write_available()) { 408 | Ok(frames) => frames, 409 | Err(err) => { 410 | println!("An error occurred while requesting the number of available \ 411 | frames for writing from the output stream: {}. BlockingStream will \ 412 | now exit the event loop.", StdError::description(&err)); 413 | return None; 414 | }, 415 | }; 416 | 417 | // How many frames do we have in our output_buffer so far? 418 | let output_buffer_frames = (output_buffer.len() / *out_channels as usize) as u32; 419 | 420 | // If there are frames available for writing and we have some to write, then write! 421 | if available_out_frames > 0 && output_buffer_frames > 0 { 422 | // If we have more than enough frames for writing, take them from the start of the buffer. 423 | let (write_buffer, write_frames) = if output_buffer_frames >= available_out_frames { 424 | let out_samples = (available_out_frames * *out_channels as u32) as usize; 425 | let write_buffer = take_front(output_buffer, out_samples); 426 | (write_buffer, available_out_frames) 427 | } 428 | // Otherwise if we have less, just take what we can for now. 429 | else { 430 | let len = output_buffer.len(); 431 | let write_buffer = take_front(output_buffer, len); 432 | (write_buffer, output_buffer_frames) 433 | }; 434 | if let Err(err) = stream.write(write_buffer, write_frames) { 435 | println!("An error occurred while writing to the output stream: {}. \ 436 | BlockingStream will now exit the event loop.", 437 | StdError::description(&err)); 438 | return None 439 | } 440 | } 441 | 442 | // If we need more frames, return a buffer for writing. 443 | if output_buffer.len() <= output_buffer.capacity() - target_output_buffer_size { 444 | use std::iter::repeat; 445 | // Start the slice just after the already filled samples. 446 | let start = user_buffer.len(); 447 | // Extend the update buffer by the necessary number of frames. 448 | user_buffer.extend(repeat(O::zero()).take(output_settings.buffer_size())); 449 | // Here we obtain a mutable reference to the slice with the correct lifetime so 450 | // that we can return it via our `Event::Out`. Note: This means that a twisted, 451 | // evil person could do horrific things with this iterator by calling `.next()` 452 | // multiple times and storing aliasing mutable references to our output buffer, 453 | // HOWEVER - this is extremely unlikely to occur in practise as the api is designed 454 | // in a way that the reference is intended to die at the end of each loop before 455 | // `.next()` even gets called again. 456 | let slice = unsafe { ::std::mem::transmute(&mut user_buffer[start..]) }; 457 | *last_event = Some(LastEvent::Out); 458 | return Some(Event::Out(slice, output_settings)); 459 | } 460 | // Otherwise, if we've read enough frames for an In event, return one. 461 | else if input_buffer.len() >= target_input_buffer_size { 462 | let event_buffer = take_front(input_buffer, input_settings.buffer_size()); 463 | *last_event = Some(LastEvent::In); 464 | return Some(Event::In(event_buffer, input_settings)); 465 | } 466 | 467 | // If no events occured on this loop, set the last_event to None. 468 | *last_event = None; 469 | 470 | } 471 | 472 | } 473 | 474 | } 475 | 476 | -------------------------------------------------------------------------------- /src/stream/input.rs: -------------------------------------------------------------------------------- 1 | 2 | use error::Error; 3 | use portaudio::pa; 4 | use portaudio::pa::Sample as PaSample; 5 | use sample::{Sample, Wave}; 6 | use settings::{Channels, Settings, Frames, SampleHz}; 7 | use std::collections::VecDeque; 8 | 9 | use super::{ 10 | BufferFrequency, 11 | CallbackFlags, 12 | CallbackResult, 13 | DeltaTimeSeconds, 14 | MINIMUM_BUFFER_RESERVATION, 15 | PaParams, 16 | SoundStream, 17 | StreamFlags, 18 | StreamParams, 19 | wait_for_stream, 20 | }; 21 | 22 | 23 | /// A builder context for an Input sound stream. 24 | pub struct Builder { 25 | pub stream_params: SoundStream, 26 | pub input_params: StreamParams, 27 | } 28 | 29 | /// An iterator of blocking input stream events. 30 | pub struct BlockingStream where I: Sample + PaSample { 31 | /// Buffer the samples from the input until its length is equal to the buffer_length. 32 | buffer: VecDeque, 33 | /// Number of input channels. 34 | channels: Channels, 35 | /// Stream sample rate. 36 | sample_hz: SampleHz, 37 | /// Frames per buffer. 38 | frames: Frames, 39 | /// The port audio stream. 40 | stream: pa::Stream, 41 | is_closed: bool, 42 | } 43 | 44 | /// Stream callback function type. 45 | pub type Callback = 46 | Box CallbackResult>; 47 | 48 | /// A handle to the non-blocking input stream. 49 | pub struct NonBlockingStream where I: Sample + PaSample { 50 | /// The port audio stream. 51 | stream: pa::Stream, 52 | /// Is the stream currently closed. 53 | is_closed: bool, 54 | } 55 | 56 | /// An event returned by the Blocking Stream. 57 | #[derive(Clone, Debug)] 58 | pub struct Event(pub Vec, pub Settings); 59 | 60 | impl Builder where I: Sample + PaSample { 61 | 62 | /// Retrieve the flags, input stream parameters, sample rate and frames per buffer. 63 | fn unwrap_params(self) -> Result { 64 | let Builder { stream_params, input_params } = self; 65 | let SoundStream { maybe_buffer_frequency, maybe_sample_hz, maybe_flags } = stream_params; 66 | 67 | // Retrieve any stream flags. 68 | let flags = maybe_flags.unwrap_or_else(|| StreamFlags::empty()); 69 | 70 | // Construct the PortAudio input params from the sound stream ones. 71 | let input_params = { 72 | let idx = input_params.idx.unwrap_or_else(|| pa::device::get_default_input()); 73 | let info = match pa::device::get_info(idx) { 74 | Ok(info) => info, 75 | Err(err) => return Err(Error::PortAudio(err)), 76 | }; 77 | let channels = input_params.channel_count 78 | .map(|n| ::std::cmp::min(n, info.max_input_channels)) 79 | .unwrap_or_else(|| ::std::cmp::min(2, info.max_input_channels)); 80 | let sample_format = input_params.sample_format(); 81 | let suggested_latency = input_params.suggested_latency 82 | .unwrap_or_else(|| info.default_low_input_latency); 83 | pa::StreamParameters { 84 | device: idx, 85 | channel_count: channels, 86 | sample_format: sample_format, 87 | suggested_latency: suggested_latency, 88 | } 89 | }; 90 | 91 | // Determine the sample rate. 92 | let sample_hz = match maybe_sample_hz { 93 | Some(sample_hz) => sample_hz, 94 | None => match pa::device::get_info(input_params.device) { 95 | Ok(info) => info.default_sample_rate, 96 | Err(err) => return Err(Error::PortAudio(err)), 97 | }, 98 | }; 99 | 100 | // Determine the closest number of frames per buffer to the requested rate. 101 | let frames = match maybe_buffer_frequency { 102 | Some(BufferFrequency::Frames(frames)) => frames as u32, 103 | Some(BufferFrequency::Hz(hz)) => (sample_hz as f32 / hz).round() as u32, 104 | None => 0, 105 | }; 106 | 107 | Ok((flags, input_params, sample_hz, frames)) 108 | } 109 | 110 | /// Launch a non-blocking input stream with the given callback! 111 | #[inline] 112 | pub fn run_callback(self, mut callback: Callback) -> Result, Error> 113 | where I: 'static, 114 | { 115 | 116 | // Initialize PortAudio. 117 | try!(pa::initialize().map_err(|err| Error::PortAudio(err))); 118 | 119 | let (flags, input_params, sample_hz, frames) = try!(self.unwrap_params()); 120 | let channels = input_params.channel_count; 121 | 122 | // Here we construct our PortAudio stream. 123 | let mut stream = pa::Stream::new(); 124 | 125 | // Remember the last time the callback was called so we can create the delta time. 126 | let mut maybe_last_time = None; 127 | 128 | // Construct a wrapper function around our callback. 129 | let f = Box::new(move |input: &[I], 130 | _output: &mut[I], 131 | frames: u32, 132 | time_info: &pa::StreamCallbackTimeInfo, 133 | flags: pa::StreamCallbackFlags| -> pa::StreamCallbackResult 134 | { 135 | let settings = Settings { 136 | sample_hz: sample_hz as u32, 137 | frames: frames as u16, 138 | channels: channels as u16, 139 | }; 140 | let dt = time_info.current_time - maybe_last_time.unwrap_or(time_info.current_time); 141 | maybe_last_time = Some(time_info.current_time); 142 | match callback(input, settings, dt, flags) { 143 | CallbackResult::Continue => pa::StreamCallbackResult::Continue, 144 | CallbackResult::Complete => pa::StreamCallbackResult::Complete, 145 | CallbackResult::Abort => pa::StreamCallbackResult::Abort, 146 | } 147 | }); 148 | 149 | // Here we open the stream. 150 | try!(stream.open(Some(&input_params), None, sample_hz, frames, flags, Some(f)) 151 | .map_err(|err| Error::PortAudio(err))); 152 | 153 | // And now let's kick it off! 154 | try!(stream.start().map_err(|err| Error::PortAudio(err))); 155 | 156 | Ok(NonBlockingStream { stream: stream, is_closed: false }) 157 | } 158 | 159 | /// Launch a blocking input stream! 160 | #[inline] 161 | pub fn run(self) -> Result, Error> 162 | where I: 'static, 163 | { 164 | 165 | // Initialize PortAudio. 166 | try!(pa::initialize().map_err(|err| Error::PortAudio(err))); 167 | 168 | let (flags, input_params, sample_hz, frames) = try!(self.unwrap_params()); 169 | 170 | // Here we construct our PortAudio stream. 171 | let mut stream = pa::Stream::new(); 172 | 173 | // Here we open the stream. 174 | try!(stream.open(Some(&input_params), None, sample_hz, frames, flags, None) 175 | .map_err(|err| Error::PortAudio(err))); 176 | 177 | // And now let's kick it off! 178 | try!(stream.start().map_err(|err| Error::PortAudio(err))); 179 | 180 | let channels = input_params.channel_count; 181 | let double_buffer_len = (frames as usize * channels as usize) * 2; 182 | let buffer_len = ::std::cmp::max(double_buffer_len, MINIMUM_BUFFER_RESERVATION); 183 | 184 | Ok(BlockingStream { 185 | buffer: VecDeque::with_capacity(buffer_len), 186 | stream: stream, 187 | channels: channels as u16, 188 | frames: frames as u16, 189 | sample_hz: sample_hz as u32, 190 | is_closed: false, 191 | }) 192 | } 193 | 194 | } 195 | 196 | impl NonBlockingStream where I: Sample + PaSample { 197 | 198 | /// Close the stream and terminate PortAudio. 199 | pub fn close(&mut self) -> Result<(), Error> { 200 | self.is_closed = true; 201 | try!(self.stream.close().map_err(|err| Error::PortAudio(err))); 202 | try!(pa::terminate().map_err(|err| Error::PortAudio(err))); 203 | Ok(()) 204 | } 205 | 206 | /// Check whether or not the stream is currently active. 207 | pub fn is_active(&self) -> Result { 208 | self.stream.is_active().map_err(|err| Error::PortAudio(err)) 209 | } 210 | 211 | } 212 | 213 | impl Drop for NonBlockingStream where I: Sample + PaSample { 214 | fn drop(&mut self) { 215 | if !self.is_closed { 216 | if let Err(err) = self.close() { 217 | println!("An error occurred while closing NonBlockingStream: {}", err); 218 | } 219 | } 220 | } 221 | } 222 | 223 | impl BlockingStream where I: Sample + PaSample { 224 | /// Close the stream and terminate PortAudio. 225 | pub fn close(&mut self) -> Result<(), Error> { 226 | self.is_closed = true; 227 | try!(self.stream.close().map_err(|err| Error::PortAudio(err))); 228 | try!(pa::terminate().map_err(|err| Error::PortAudio(err))); 229 | Ok(()) 230 | } 231 | } 232 | 233 | impl Drop for BlockingStream where I: Sample + PaSample { 234 | fn drop(&mut self) { 235 | if !self.is_closed { 236 | if let Err(err) = self.close() { 237 | println!("An error occurred while closing BlockingStream: {}", err); 238 | } 239 | } 240 | } 241 | } 242 | 243 | impl Iterator for BlockingStream where I: Sample + PaSample { 244 | type Item = Event; 245 | 246 | fn next(&mut self) -> Option> { 247 | 248 | let BlockingStream { 249 | ref mut buffer, 250 | ref mut stream, 251 | ref channels, 252 | ref frames, 253 | ref sample_hz, 254 | .. 255 | } = *self; 256 | 257 | let settings = Settings { channels: *channels, frames: *frames, sample_hz: *sample_hz }; 258 | let buffer_size = settings.buffer_size(); 259 | 260 | loop { 261 | use std::error::Error as StdError; 262 | use utils::take_front; 263 | 264 | // If we have the requested number of frames, return them in an Event. 265 | if buffer.len() >= buffer_size { 266 | let event_buffer = take_front(buffer, buffer_size); 267 | return Some(Event(event_buffer, settings)); 268 | } 269 | 270 | // How many frames are available on the input stream? 271 | let available_frames = match wait_for_stream(|| stream.get_stream_read_available()) { 272 | Ok(frames) => frames, 273 | Err(err) => { 274 | println!("An error occurred while requesting the number of available \ 275 | frames for reading from the input stream: {}. BlockingStream will \ 276 | now exit the event loop.", StdError::description(&err)); 277 | return None; 278 | }, 279 | }; 280 | 281 | // If there are frames available and we have room in the buffer, take them. 282 | if available_frames > 0 && buffer.capacity() >= buffer.len() + available_frames as usize { 283 | match stream.read(available_frames) { 284 | Ok(input_samples) => buffer.extend(input_samples.into_iter()), 285 | Err(err) => { 286 | println!("An error occurred while reading from the input stream: {}. \ 287 | BlockingStream will now exit the event loop.", 288 | StdError::description(&err)); 289 | return None; 290 | }, 291 | } 292 | } 293 | 294 | } 295 | 296 | } 297 | 298 | } 299 | -------------------------------------------------------------------------------- /src/stream/mod.rs: -------------------------------------------------------------------------------- 1 | 2 | use error::Error; 3 | use portaudio::pa; 4 | use portaudio::pa::Sample as PaSample; 5 | use sample::Format as SampleFormat; 6 | use sample::Sample; 7 | use settings::Frames; 8 | use std::marker::PhantomData; 9 | 10 | pub mod duplex; 11 | pub mod input; 12 | pub mod output; 13 | 14 | /// The size of the VecDeque reservation with headroom for overflowing samples. 15 | pub const MINIMUM_BUFFER_RESERVATION: usize = 2048; 16 | 17 | /// A builder context for a SoundStream. 18 | #[derive(Clone, PartialEq)] 19 | pub struct SoundStream { 20 | maybe_buffer_frequency: Option, 21 | maybe_sample_hz: Option, 22 | maybe_flags: Option, 23 | } 24 | 25 | /// Bit flags to be passed to the stream. 26 | pub type StreamFlags = pa::StreamFlags; 27 | 28 | /// Bit flags fed to the callback to indicate non-blocking stream behaviour. 29 | pub type CallbackFlags = pa::StreamCallbackFlags; 30 | 31 | /// Represents the update frequency. 32 | #[derive(Copy, Clone, PartialEq)] 33 | enum BufferFrequency { 34 | Hz(f32), 35 | Frames(Frames), 36 | } 37 | 38 | /// Difference in time between Update events. 39 | pub type DeltaTimeSeconds = f64; 40 | 41 | /// To be returned by the callback that is run by the non-blocking streams. 42 | #[derive(Copy, Clone, Debug, PartialEq)] 43 | pub enum CallbackResult { 44 | /// Successfully finish and close the stream. 45 | Complete, 46 | /// Continue the stream. 47 | Continue, 48 | /// Abort the stream. 49 | Abort, 50 | } 51 | 52 | /// The params unwrapped by the input and output stream builders. 53 | pub type PaParams = (StreamFlags, pa::StreamParameters, f64, u32); 54 | 55 | /// The index of the device. 56 | pub type Idx = pa::DeviceIndex; 57 | 58 | /// A suggested amount of latency. 59 | pub type Latency = pa::Time; 60 | 61 | /// A type for building stream parameters. 62 | #[derive(Copy, Clone, PartialEq)] 63 | pub struct StreamParams { 64 | pub idx: Option, 65 | pub channel_count: Option, 66 | pub suggested_latency: Option, 67 | pub phantom_sample: PhantomData, 68 | } 69 | 70 | impl SoundStream { 71 | 72 | /// Constructs the builder for a new SoundStream. 73 | #[inline] 74 | pub fn new() -> SoundStream { 75 | SoundStream { 76 | maybe_buffer_frequency: None, 77 | maybe_sample_hz: None, 78 | maybe_flags: None, 79 | } 80 | } 81 | 82 | /// Desired stream sample rate (samples per second). For a duplex stream, it is the sample rate 83 | /// for both the input and output streams. 84 | #[inline] 85 | pub fn sample_hz(self, sample_hz: f64) -> SoundStream { 86 | SoundStream { maybe_sample_hz: Some(sample_hz), ..self } 87 | } 88 | 89 | /// Flags indicating stream behaviour. 90 | #[inline] 91 | pub fn flags(self, flags: StreamFlags) -> SoundStream { 92 | SoundStream { maybe_flags: Some(flags), ..self } 93 | } 94 | 95 | /// Used to calculate the number of frames per buffer. 96 | #[inline] 97 | pub fn buffer_hz(self, hz: f32) -> SoundStream { 98 | assert!(hz > 0.0, "`update_hz` must be greater than 0.0, but you gave {:?}", hz); 99 | SoundStream { maybe_buffer_frequency: Some(BufferFrequency::Hz(hz)), ..self } 100 | } 101 | 102 | /// The number of frames per buffer of audio. 103 | #[inline] 104 | pub fn frames_per_buffer(self, frames: Frames) -> SoundStream { 105 | SoundStream { maybe_buffer_frequency: Some(BufferFrequency::Frames(frames)), ..self } 106 | } 107 | 108 | /// Custom input device. 109 | #[inline] 110 | pub fn input(self, params: StreamParams) -> input::Builder 111 | where 112 | I: Sample + PaSample 113 | { 114 | input::Builder { stream_params: self, input_params: params } 115 | } 116 | 117 | /// Custom output device. 118 | #[inline] 119 | pub fn output(self, params: StreamParams) -> output::Builder 120 | where 121 | O: Sample + PaSample 122 | { 123 | output::Builder { stream_params: self, output_params: params } 124 | } 125 | 126 | /// Duplex stream with given custom input and output devices. 127 | #[inline] 128 | pub fn duplex(self, 129 | input_params: StreamParams, 130 | output_params: StreamParams) -> duplex::Builder 131 | where 132 | I: Sample + PaSample, 133 | O: Sample + PaSample, 134 | { 135 | duplex::Builder { 136 | stream_params: self, 137 | input_params: input_params, 138 | output_params: output_params, 139 | } 140 | } 141 | 142 | } 143 | 144 | impl StreamParams { 145 | 146 | /// Construct a default StreamParams. 147 | pub fn new() -> StreamParams { 148 | StreamParams { 149 | idx: None, 150 | channel_count: None, 151 | suggested_latency: None, 152 | phantom_sample: PhantomData, 153 | } 154 | } 155 | 156 | /// Specify the index of the device to be used for the Stream. 157 | #[inline] 158 | pub fn device_idx(self, idx: Idx) -> StreamParams { 159 | StreamParams { idx: Some(idx), ..self } 160 | } 161 | 162 | /// Request a number of channels for the Stream. 163 | #[inline] 164 | pub fn channels(self, channels: i32) -> StreamParams { 165 | StreamParams { channel_count: Some(channels), ..self } 166 | } 167 | 168 | /// Return the sample format for the Stream. 169 | #[inline] 170 | pub fn sample_format(&self) -> pa::SampleFormat where S: pa::Sample { 171 | S::sample_format_for::() 172 | } 173 | 174 | /// Suggest a latency to use for the stream. 175 | #[inline] 176 | pub fn suggest_latency(self, latency: Latency) -> StreamParams { 177 | StreamParams { suggested_latency: Some(latency), ..self } 178 | } 179 | 180 | } 181 | 182 | /// Wait for the given stream to become ready for reading/writing. 183 | fn wait_for_stream(f: F) -> Result 184 | where 185 | F: Fn() -> Result, 186 | { 187 | loop { 188 | match f() { 189 | Ok(available) => match available { 190 | pa::StreamAvailable::Frames(frames) => return Ok(frames as u32), 191 | pa::StreamAvailable::InputOverflowed => println!("Input stream has overflowed"), 192 | pa::StreamAvailable::OutputUnderflowed => println!("Output stream has underflowed"), 193 | }, 194 | Err(err) => return Err(Error::PortAudio(err)), 195 | } 196 | } 197 | } 198 | 199 | -------------------------------------------------------------------------------- /src/stream/output.rs: -------------------------------------------------------------------------------- 1 | 2 | use error::Error; 3 | use portaudio::pa; 4 | use portaudio::pa::Sample as PaSample; 5 | use sample::{Sample, Wave}; 6 | use settings::{Channels, Settings, Frames, SampleHz}; 7 | use std::collections::VecDeque; 8 | use std::marker::PhantomData; 9 | 10 | use super::{ 11 | BufferFrequency, 12 | CallbackFlags, 13 | CallbackResult, 14 | DeltaTimeSeconds, 15 | MINIMUM_BUFFER_RESERVATION, 16 | PaParams, 17 | SoundStream, 18 | StreamFlags, 19 | StreamParams, 20 | wait_for_stream, 21 | }; 22 | 23 | 24 | /// A builder context for an Output sound stream. 25 | pub struct Builder { 26 | pub stream_params: SoundStream, 27 | pub output_params: StreamParams, 28 | } 29 | 30 | /// An iterator of blocking output stream events. 31 | pub struct BlockingStream<'a, O=Wave> where O: Sample + PaSample { 32 | /// Buffer the samples from the output until its length is equal to the buffer_length. 33 | buffer: VecDeque, 34 | /// Buffer passed to the user for writing. 35 | user_buffer: Vec, 36 | /// Number of channels. 37 | channels: Channels, 38 | /// Stream sample rate. 39 | sample_hz: SampleHz, 40 | /// Frames per buffer. 41 | frames: Frames, 42 | /// The port audio stream. 43 | stream: pa::Stream, 44 | is_closed: bool, 45 | marker: PhantomData<&'a ()>, 46 | } 47 | 48 | /// Stream callback function type. 49 | pub type Callback = Box CallbackResult>; 50 | 51 | /// A handle to the non-blocking output stream. 52 | pub struct NonBlockingStream where O: Sample + PaSample { 53 | /// The port audio stream. 54 | stream: pa::Stream, 55 | /// Is the stream currently closed. 56 | is_closed: bool, 57 | } 58 | 59 | /// An event returned by the Blocking Stream. 60 | #[derive(Debug)] 61 | pub struct Event<'a, O: 'a>(pub &'a mut [O], pub Settings); 62 | 63 | impl Builder where O: Sample + PaSample { 64 | 65 | /// Retrieve the flags, output stream parameters, sample rate and frames per buffer. 66 | fn unwrap_params(self) -> Result { 67 | let Builder { stream_params, output_params } = self; 68 | let SoundStream { maybe_buffer_frequency, maybe_sample_hz, maybe_flags } = stream_params; 69 | 70 | // Retrieve any stream flags. 71 | let flags = maybe_flags.unwrap_or_else(|| StreamFlags::empty()); 72 | 73 | // Construct the PortAudio output params from the sound stream ones. 74 | let output_params = { 75 | let idx = output_params.idx.unwrap_or_else(|| pa::device::get_default_output()); 76 | let info = match pa::device::get_info(idx) { 77 | Ok(info) => info, 78 | Err(err) => return Err(Error::PortAudio(err)), 79 | }; 80 | let channels = output_params.channel_count 81 | .map(|n| ::std::cmp::min(n, info.max_output_channels)) 82 | .unwrap_or_else(|| ::std::cmp::min(2, info.max_output_channels)); 83 | let sample_format = output_params.sample_format(); 84 | let suggested_latency = output_params.suggested_latency 85 | .unwrap_or_else(|| info.default_low_output_latency); 86 | pa::StreamParameters { 87 | device: idx, 88 | channel_count: channels, 89 | sample_format: sample_format, 90 | suggested_latency: suggested_latency, 91 | } 92 | }; 93 | 94 | // Determine the sample rate. 95 | let sample_hz = match maybe_sample_hz { 96 | Some(sample_hz) => sample_hz, 97 | None => match pa::device::get_info(output_params.device) { 98 | Ok(info) => info.default_sample_rate, 99 | Err(err) => return Err(Error::PortAudio(err)), 100 | }, 101 | }; 102 | 103 | // Determine the closest number of frames per buffer to the requested rate. 104 | let frames = match maybe_buffer_frequency { 105 | Some(BufferFrequency::Frames(frames)) => frames as u32, 106 | Some(BufferFrequency::Hz(hz)) => (sample_hz as f32 / hz).round() as u32, 107 | None => 0, 108 | }; 109 | 110 | Ok((flags, output_params, sample_hz, frames)) 111 | } 112 | 113 | /// Launch a non-blocking output stream with the given callback! 114 | #[inline] 115 | pub fn run_callback(self, mut callback: Callback) -> Result, Error> 116 | where O: 'static, 117 | { 118 | 119 | // Initialize PortAudio. 120 | try!(pa::initialize().map_err(|err| Error::PortAudio(err))); 121 | 122 | let (flags, output_params, sample_hz, frames) = try!(self.unwrap_params()); 123 | let channels = output_params.channel_count; 124 | 125 | // Here we construct our PortAudio stream. 126 | let mut stream = pa::Stream::new(); 127 | 128 | // Remember the last time the callback was called so we can create the delta time. 129 | let mut maybe_last_time = None; 130 | 131 | // Construct a wrapper function around our callback. 132 | let f = Box::new(move |_input: &[O], 133 | output: &mut[O], 134 | frames: u32, 135 | time_info: &pa::StreamCallbackTimeInfo, 136 | flags: pa::StreamCallbackFlags| -> pa::StreamCallbackResult { 137 | let settings = Settings { 138 | sample_hz: sample_hz as u32, 139 | frames: frames as u16, 140 | channels: channels as u16, 141 | }; 142 | let dt = time_info.current_time - maybe_last_time.unwrap_or(time_info.current_time); 143 | maybe_last_time = Some(time_info.current_time); 144 | match callback(output, settings, dt, flags) { 145 | CallbackResult::Continue => pa::StreamCallbackResult::Continue, 146 | CallbackResult::Complete => pa::StreamCallbackResult::Complete, 147 | CallbackResult::Abort => pa::StreamCallbackResult::Abort, 148 | } 149 | }); 150 | 151 | // Here we open the stream. 152 | try!(stream.open(None, Some(&output_params), sample_hz, frames, flags, Some(f)) 153 | .map_err(|err| Error::PortAudio(err))); 154 | 155 | // And now let's kick it off! 156 | try!(stream.start().map_err(|err| Error::PortAudio(err))); 157 | 158 | Ok(NonBlockingStream { stream: stream, is_closed: false }) 159 | } 160 | 161 | /// Launch a blocking output stream! 162 | #[inline] 163 | pub fn run<'a>(self) -> Result, Error> 164 | where O: 'static, 165 | { 166 | 167 | // Initialize PortAudio. 168 | try!(pa::initialize().map_err(|err| Error::PortAudio(err))); 169 | 170 | let (flags, output_params, sample_hz, frames) = try!(self.unwrap_params()); 171 | 172 | // Here we construct our PortAudio stream. 173 | let mut stream = pa::Stream::new(); 174 | 175 | // Here we open the stream. 176 | try!(stream.open(None, Some(&output_params), sample_hz, frames, flags, None) 177 | .map_err(|err| Error::PortAudio(err))); 178 | 179 | // And now let's kick it off! 180 | try!(stream.start().map_err(|err| Error::PortAudio(err))); 181 | 182 | let channels = output_params.channel_count; 183 | let double_buffer_len = (frames as usize * channels as usize) * 2; 184 | let buffer_len = ::std::cmp::max(double_buffer_len, MINIMUM_BUFFER_RESERVATION); 185 | 186 | Ok(BlockingStream { 187 | buffer: VecDeque::with_capacity(buffer_len), 188 | user_buffer: Vec::with_capacity(frames as usize * channels as usize), 189 | stream: stream, 190 | channels: channels as u16, 191 | frames: frames as u16, 192 | sample_hz: sample_hz as u32, 193 | is_closed: false, 194 | marker: PhantomData, 195 | }) 196 | } 197 | 198 | } 199 | 200 | impl NonBlockingStream where O: Sample + PaSample { 201 | 202 | /// Close the stream and terminate PortAudio. 203 | pub fn close(&mut self) -> Result<(), Error> { 204 | self.is_closed = true; 205 | try!(self.stream.close().map_err(|err| Error::PortAudio(err))); 206 | try!(pa::terminate().map_err(|err| Error::PortAudio(err))); 207 | Ok(()) 208 | } 209 | 210 | /// Check whether or not the stream is currently active. 211 | pub fn is_active(&self) -> Result { 212 | self.stream.is_active().map_err(|err| Error::PortAudio(err)) 213 | } 214 | 215 | } 216 | 217 | impl Drop for NonBlockingStream where O: Sample + PaSample { 218 | fn drop(&mut self) { 219 | if !self.is_closed { 220 | if let Err(err) = self.close() { 221 | println!("An error occurred while closing NonBlockingStream: {}", err); 222 | } 223 | } 224 | } 225 | } 226 | 227 | impl<'a, O> BlockingStream<'a, O> where O: Sample + PaSample { 228 | /// Close the stream and terminate PortAudio. 229 | pub fn close(&mut self) -> Result<(), Error> { 230 | self.is_closed = true; 231 | try!(self.stream.close().map_err(|err| Error::PortAudio(err))); 232 | try!(pa::terminate().map_err(|err| Error::PortAudio(err))); 233 | Ok(()) 234 | } 235 | } 236 | 237 | impl<'a, O> Drop for BlockingStream<'a, O> where O: Sample + PaSample { 238 | fn drop(&mut self) { 239 | if !self.is_closed { 240 | if let Err(err) = self.close() { 241 | println!("An error occurred while closing BlockingStream: {}", err); 242 | } 243 | } 244 | } 245 | } 246 | 247 | impl<'a, O> Iterator for BlockingStream<'a, O> 248 | where O: Sample + PaSample + 'a 249 | { 250 | type Item = Event<'a, O>; 251 | 252 | fn next(&mut self) -> Option> { 253 | use std::error::Error as StdError; 254 | use utils::take_front; 255 | 256 | let BlockingStream { 257 | ref mut buffer, 258 | ref mut user_buffer, 259 | ref mut stream, 260 | ref channels, 261 | ref frames, 262 | ref sample_hz, 263 | .. 264 | } = *self; 265 | 266 | let settings = Settings { channels: *channels, frames: *frames, sample_hz: *sample_hz }; 267 | let buffer_size = settings.buffer_size(); 268 | 269 | if user_buffer.len() > 0 { 270 | buffer.extend(user_buffer.iter().map(|&sample| sample)); 271 | user_buffer.clear(); 272 | } 273 | 274 | loop { 275 | 276 | // How many frames are available for writing on the output stream? 277 | let available_frames = match wait_for_stream(|| stream.get_stream_write_available()) { 278 | Ok(frames) => frames, 279 | Err(err) => { 280 | println!("An error occurred while requesting the number of available \ 281 | frames for writing from the output stream: {}. BlockingStream will \ 282 | now exit the event loop.", StdError::description(&err)); 283 | return None; 284 | }, 285 | }; 286 | 287 | // How many frames do we have in our output_buffer so far? 288 | let output_buffer_frames = (buffer.len() / *channels as usize) as u32; 289 | 290 | // If there are frames available for writing and we have some to write, then write! 291 | if available_frames > 0 && output_buffer_frames > 0 { 292 | // If we have more than enough frames for writing, take them from the start of the buffer. 293 | let (write_buffer, write_frames) = if output_buffer_frames >= available_frames { 294 | let out_samples = (available_frames * *channels as u32) as usize; 295 | let write_buffer = take_front(buffer, out_samples); 296 | (write_buffer, available_frames) 297 | } 298 | // Otherwise if we have less, just take what we can for now. 299 | else { 300 | let len = buffer.len(); 301 | let write_buffer = take_front(buffer, len); 302 | (write_buffer, output_buffer_frames) 303 | }; 304 | if let Err(err) = stream.write(write_buffer, write_frames) { 305 | println!("An error occurred while writing to the output stream: {}. \ 306 | BlockingStream will now exit the event loop.", 307 | StdError::description(&err)); 308 | return None 309 | } 310 | } 311 | 312 | // If we need more frames, return a buffer for writing. 313 | if buffer.len() <= buffer.capacity() - buffer_size { 314 | use std::iter::repeat; 315 | // Start the slice just after the already filled samples. 316 | let start = user_buffer.len(); 317 | // Extend the update buffer by the necessary number of frames. 318 | user_buffer.extend(repeat(O::zero()).take(buffer_size)); 319 | // Here we obtain a mutable reference to the slice with the correct lifetime so 320 | // that we can return it via our `Event::Out`. Note: This means that a twisted, 321 | // evil person could do horrific things with this iterator by calling `.next()` 322 | // multiple times and storing aliasing mutable references to our output buffer, 323 | // HOWEVER - this is extremely unlikely to occur in practise as the api is designed 324 | // in a way that the reference is intended to die at the end of each loop before 325 | // `.next()` even gets called again. 326 | let slice = unsafe { ::std::mem::transmute(&mut user_buffer[start..]) }; 327 | return Some(Event(slice, settings)); 328 | } 329 | 330 | } 331 | 332 | } 333 | 334 | } 335 | 336 | 337 | -------------------------------------------------------------------------------- /src/utils.rs: -------------------------------------------------------------------------------- 1 | 2 | use std::collections::VecDeque; 3 | 4 | /// Take the given number of elements from the front of the VecDeque. 5 | /// 6 | /// Fails if the num_elems given is higher than the length of the VecDeque. 7 | pub fn take_front(vec: &mut VecDeque, num_elems: usize) -> Vec { 8 | (0..num_elems).map(|_| vec.pop_front().unwrap()).collect() 9 | } 10 | 11 | --------------------------------------------------------------------------------