Skip to main content

soundio/
instream.rs

1extern crate libsoundio_sys as raw;
2
3use super::error::*;
4use super::format::*;
5use super::sample::*;
6use super::util::*;
7
8use std::marker::PhantomData;
9use std::os::raw::{c_double, c_int};
10use std::ptr;
11use std::slice;
12
13/// This is called when an instream has been read. The `InStreamUserData` struct is obtained
14/// from the stream.userdata, then the user-supplied callback is called with an `InStreamReader`
15/// object.
16pub extern "C" fn instream_read_callback(
17    stream: *mut raw::SoundIoInStream,
18    frame_count_min: c_int,
19    frame_count_max: c_int,
20) {
21    // Use stream.userdata to get a reference to the InStreamUserData object.
22    let raw_userdata_pointer = unsafe { (*stream).userdata as *mut InStreamUserData };
23    let userdata = unsafe { &mut (*raw_userdata_pointer) };
24
25    let mut stream_reader = InStreamReader {
26        instream: userdata.instream,
27        frame_count_min: frame_count_min as _,
28        frame_count_max: frame_count_max as _,
29        read_started: false,
30        channel_areas: Vec::new(),
31        frame_count: 0,
32        phantom: PhantomData,
33    };
34
35    (userdata.read_callback)(&mut stream_reader);
36}
37
38pub extern "C" fn instream_overflow_callback(stream: *mut raw::SoundIoInStream) {
39    // Use stream.userdata to get a reference to the InStreamUserData object.
40    let raw_userdata_pointer = unsafe { (*stream).userdata as *mut InStreamUserData };
41    let userdata = unsafe { &mut (*raw_userdata_pointer) };
42
43    if let Some(ref mut cb) = userdata.overflow_callback {
44        cb();
45    } else {
46        println!("Overflow!");
47    }
48}
49
50pub extern "C" fn instream_error_callback(stream: *mut raw::SoundIoInStream, err: c_int) {
51    // Use stream.userdata to get a reference to the InStreamUserData object.
52    let raw_userdata_pointer = unsafe { (*stream).userdata as *mut InStreamUserData };
53    let userdata = unsafe { &mut (*raw_userdata_pointer) };
54
55    if let Some(ref mut cb) = userdata.error_callback {
56        cb(err.into());
57    } else {
58        println!("Error: {}", Error::from(err));
59    }
60}
61
62/// InStream represents an input stream for recording.
63///
64/// It is obtained from `Device` using `Device::open_instream()` and
65/// can be started and paused.
66pub struct InStream<'a> {
67    pub userdata: Box<InStreamUserData<'a>>,
68
69    // This is just here to say that InStream cannot outlive the Device it was created from.
70    pub phantom: PhantomData<&'a ()>,
71}
72
73/// The callbacks required for an instream are stored in this object. We also store a pointer
74/// to the raw instream so that it can be passed to `InStreamReader` in the write callback.
75pub struct InStreamUserData<'a> {
76    pub instream: *mut raw::SoundIoInStream,
77
78    pub read_callback: Box<dyn FnMut(&mut InStreamReader) + 'a>,
79    pub overflow_callback: Option<Box<dyn FnMut() + 'a>>,
80    pub error_callback: Option<Box<dyn FnMut(Error) + 'a>>,
81}
82
83impl<'a> Drop for InStreamUserData<'a> {
84    fn drop(&mut self) {
85        unsafe {
86            raw::soundio_instream_destroy(self.instream);
87        }
88    }
89}
90
91impl<'a> InStream<'a> {
92    /// Starts the stream, returning `Ok(())` if it started successfully. Once
93    /// started the read callback will be periodically called according to the
94    /// requested latency.
95    ///
96    /// `start()` should only ever be called once on an `InStream`.
97    /// Do not use `start()` to resume a stream after pausing it. Instead call `pause(false)`.
98    ///
99    /// # Errors
100    ///
101    /// * `Error::BackendDisconnected`
102    /// * `Error::Streaming`
103    /// * `Error::OpeningDevice`
104    /// * `Error::SystemResources`
105    ///
106    pub fn start(&mut self) -> Result<()> {
107        match unsafe { raw::soundio_instream_start(self.userdata.instream) } {
108            0 => Ok(()),
109            x => Err(x.into()),
110        }
111    }
112
113    // TODO: Can pause() be called from the read callback?
114
115    /// If the underlying backend and device support pausing, this pauses the
116    /// stream. The `write_callback()` may be called a few more times if
117    /// the buffer is not full.
118    ///
119    /// Pausing might put the hardware into a low power state which is ideal if your
120    /// software is silent for some time.
121    ///
122    /// This should not be called before `start()`. Pausing when already paused or
123    /// unpausing when already unpaused has no effect and returns `Ok(())`.
124    ///
125    /// # Errors
126    ///
127    /// * `Error::BackendDisconnected`
128    /// * `Error::Streaming`
129    /// * `Error::IncompatibleDevice` - device does not support pausing/unpausing
130    ///
131    pub fn pause(&mut self, pause: bool) -> Result<()> {
132        match unsafe { raw::soundio_instream_pause(self.userdata.instream, pause as i8) } {
133            0 => Ok(()),
134            e => Err(e.into()),
135        }
136    }
137
138    /// Returns the stream format.
139    pub fn format(&self) -> Format {
140        unsafe { (*self.userdata.instream).format.into() }
141    }
142
143    /// Sample rate is the number of frames per second.
144    pub fn sample_rate(&self) -> i32 {
145        unsafe { (*self.userdata.instream).sample_rate as _ }
146    }
147
148    /// Ignoring hardware latency, this is the number of seconds it takes for a
149    /// captured sample to become available for reading.
150    /// After you call `Device::open_instream()`, this value is replaced with the
151    /// actual software latency, as near to this value as possible.
152    ///
153    /// A higher value means less CPU usage. Defaults to a large value,
154    /// potentially upwards of 2 seconds.
155    ///
156    /// If the device has unknown software latency min and max values, you may
157    /// still set this (in `Device::open_instream()`), but you might not
158    /// get the value you requested.
159    ///
160    /// For PulseAudio, if you set this value to non-default, it sets
161    /// `PA_STREAM_ADJUST_LATENCY` and is the value used for `fragsize`.
162    /// For JACK, this value is always equal to
163    /// `Device::software_latency().current`.
164    pub fn software_latency(&self) -> f64 {
165        unsafe { (*self.userdata.instream).software_latency as _ }
166    }
167
168    /// The name of the stream, which defaults to "SoundIoInStream".
169    ///
170    /// PulseAudio uses this for the stream name.
171    /// JACK uses this for the client name of the client that connects when you
172    /// open the stream.
173    /// WASAPI uses this for the session display name.
174    /// Must not contain a colon (":").
175    ///
176    /// TODO: Currently there is no way to set this.
177    pub fn name(&self) -> String {
178        unsafe { utf8_to_string((*self.userdata.instream).name) }
179    }
180
181    /// The number of bytes per frame, equal to the number of bytes
182    /// per sample, multiplied by the number of channels.
183    pub fn bytes_per_frame(&self) -> i32 {
184        unsafe { (*self.userdata.instream).bytes_per_frame as _ }
185    }
186
187    /// The number of bytes in a sample, e.g. 3 for `i24`.
188    pub fn bytes_per_sample(&self) -> i32 {
189        unsafe { (*self.userdata.instream).bytes_per_sample as _ }
190    }
191}
192
193/// `InStreamReader` is passed to the read callback and can be used to read from the stream.
194///
195/// You start by calling `begin_read()` and then you can read the samples. When the `InStreamReader`
196/// is dropped the samples are dropped. An error at that point is written to the console and ignored.
197///
198pub struct InStreamReader<'a> {
199    instream: *mut raw::SoundIoInStream,
200    frame_count_min: usize,
201    frame_count_max: usize,
202
203    read_started: bool,
204
205    // The memory area to write to - one for each channel. Populated after begin_read()
206    channel_areas: Vec<raw::SoundIoChannelArea>,
207    // The actual frame count. Populated after begin_read()
208    frame_count: usize,
209
210    // This cannot outlive the scope that it is spawned from (in the write callback).
211    phantom: PhantomData<&'a ()>,
212}
213
214impl<'a> InStreamReader<'a> {
215    /// Start a read. You can only call this once per callback otherwise it panics.
216    ///
217    /// frame_count is the number of frames you want to read. It must be between
218    /// frame_count_min and frame_count_max inclusive, or `begin_read()` will panic.
219    ///
220    /// It returns the number of frames you can actually read. The returned value
221    /// will always be less than or equal to the provided value.
222    ///
223    /// # Errors
224    ///
225    /// * `Error::Invalid`
226    ///   * `frame_count` < `frame_count_min` or `frame_count` > `frame_count_max`
227    /// * `Error::Streaming`
228    /// * `Error::IncompatibleDevice` - in rare cases it might just now
229    ///   be discovered that the device uses non-byte-aligned access, in which
230    ///   case this error code is returned.
231    ///
232    pub fn begin_read(&mut self, frame_count: usize) -> Result<usize> {
233        assert!(
234            frame_count >= self.frame_count_min && frame_count <= self.frame_count_max,
235            "frame_count out of range"
236        );
237
238        let mut areas: *mut raw::SoundIoChannelArea = ptr::null_mut();
239        let mut actual_frame_count: c_int = frame_count as _;
240
241        match unsafe {
242            raw::soundio_instream_begin_read(
243                self.instream,
244                &mut areas as *mut _,
245                &mut actual_frame_count as *mut _,
246            )
247        } {
248            0 => {
249                self.read_started = true;
250                self.frame_count = actual_frame_count as _;
251                // Return now if there's no frames to actually read.
252                if actual_frame_count <= 0 {
253                    return Ok(0);
254                }
255                let cc = self.channel_count();
256                self.channel_areas = vec![
257                    raw::SoundIoChannelArea {
258                        ptr: ptr::null_mut(),
259                        step: 0
260                    };
261                    cc
262                ];
263                unsafe {
264                    self.channel_areas.copy_from_slice(slice::from_raw_parts::<
265                        raw::SoundIoChannelArea,
266                    >(areas, cc));
267                }
268                Ok(actual_frame_count as _)
269            }
270            e => Err(e.into()),
271        }
272    }
273
274    /// Commits the write that you began with `begin_read()`.
275    ///
276    /// Errors are currently are just printed to the console and ignored.
277    ///
278    /// # Errors
279    ///
280    /// * `Error::Streaming`
281    /// * `Error::Underflow` - an underflow caused this call to fail. You might
282    ///   also get an `underflow_callback()`, and you might not get
283    ///   this error code when an underflow occurs. Unlike `Error::Streaming`,
284    ///   the outstream is still in a valid state and streaming can continue.
285    pub fn end_read(&mut self) {
286        if self.read_started {
287            unsafe {
288                match raw::soundio_instream_end_read(self.instream) {
289                    0 => {
290                        self.read_started = false;
291                    }
292                    x => println!("Error ending instream: {}", Error::from(x)),
293                }
294            }
295        }
296    }
297
298    /// Get the minimum frame count that you can call `begin_read()` with.
299    /// Retreive this value before calling `begin_read()` to ensure you read the correct number
300    /// of frames.
301    pub fn frame_count_min(&self) -> usize {
302        self.frame_count_min
303    }
304
305    /// Get the maximum frame count that you can call `begin_read()` with.
306    /// Retreive this value before calling `begin_read()` to ensure you read the correct number
307    /// of frames.
308    pub fn frame_count_max(&self) -> usize {
309        self.frame_count_max
310    }
311
312    /// Get the actual frame count that you did call `begin_read()` with. Panics if you haven't called
313    /// `begin_read()` yet.
314    pub fn frame_count(&self) -> usize {
315        assert!(self.read_started);
316        self.frame_count
317    }
318
319    /// Get latency in seconds due to software only, not including hardware.
320    pub fn software_latency(&self) -> f64 {
321        unsafe { (*self.instream).software_latency as _ }
322    }
323
324    /// Return the number of channels in this stream. Guaranteed to be at least 1.
325    pub fn channel_count(&self) -> usize {
326        unsafe { (*self.instream).layout.channel_count as _ }
327    }
328
329    /// Get the sample rate in Hertz.
330    pub fn sample_rate(&self) -> i32 {
331        unsafe { (*self.instream).sample_rate as _ }
332    }
333
334    /// Obtain the number of seconds that the next frame of sound being
335    /// captured will take to arrive in the buffer, plus the amount of time that is
336    /// represented in the buffer. This includes both software and hardware latency.
337    ///
338    /// # Errors
339    ///
340    /// * `Error::Streaming`
341    ///
342    pub fn get_latency(&mut self) -> Result<f64> {
343        let mut x: c_double = 0.0;
344        match unsafe { raw::soundio_instream_get_latency(self.instream, &mut x as *mut c_double) } {
345            0 => Ok(x),
346            e => Err(e.into()),
347        }
348    }
349
350    /// Get the value of a sample. This panics if the `channel` or `frame` are
351    /// out of range or if you haven't called `begin_read()` yet.
352    ///
353    /// If you request a different type from the actual one it will be converted.
354    ///
355    /// # Examples
356    ///
357    /// ```
358    /// fn read_callback(stream: &mut soundio::InStreamReader) {
359    ///     let frame_count_max = stream.frame_count_max();
360    ///     stream.begin_read(frame_count_max).unwrap();
361    ///     for c in 0..stream.channel_count() {
362    ///         for f in 0..stream.frame_count() {
363    ///             do_something_with(stream.sample::<i16>(c, f));
364    ///         }
365    ///     }
366    /// }
367    /// # fn do_something_with(_: i16) { }
368    /// ```
369    pub fn sample<T: Sample>(&self, channel: usize, frame: usize) -> T {
370        assert!(self.read_started);
371
372        assert!(channel < self.channel_count(), "Channel out of range");
373        assert!(frame < self.frame_count(), "Frame out of range");
374
375        unsafe {
376            let ptr = self.channel_areas[channel]
377                .ptr
378                .add(frame * self.channel_areas[channel].step as usize)
379                as *mut u8;
380
381            match (*self.instream).format {
382                raw::SoundIoFormat::SoundIoFormatS8 => T::from_i8(i8::from_raw_le(ptr)),
383                raw::SoundIoFormat::SoundIoFormatU8 => T::from_u8(u8::from_raw_le(ptr)),
384                raw::SoundIoFormat::SoundIoFormatS16LE => T::from_i16(i16::from_raw_le(ptr)),
385                raw::SoundIoFormat::SoundIoFormatS16BE => T::from_i16(i16::from_raw_be(ptr)),
386                raw::SoundIoFormat::SoundIoFormatU16LE => T::from_u16(u16::from_raw_le(ptr)),
387                raw::SoundIoFormat::SoundIoFormatU16BE => T::from_u16(u16::from_raw_be(ptr)),
388                raw::SoundIoFormat::SoundIoFormatS24LE => T::from_i24(i24::from_raw_le(ptr)),
389                raw::SoundIoFormat::SoundIoFormatS24BE => T::from_i24(i24::from_raw_be(ptr)),
390                raw::SoundIoFormat::SoundIoFormatU24LE => T::from_u24(u24::from_raw_le(ptr)),
391                raw::SoundIoFormat::SoundIoFormatU24BE => T::from_u24(u24::from_raw_be(ptr)),
392                raw::SoundIoFormat::SoundIoFormatS32LE => T::from_i32(i32::from_raw_le(ptr)),
393                raw::SoundIoFormat::SoundIoFormatS32BE => T::from_i32(i32::from_raw_be(ptr)),
394                raw::SoundIoFormat::SoundIoFormatU32LE => T::from_u32(u32::from_raw_le(ptr)),
395                raw::SoundIoFormat::SoundIoFormatU32BE => T::from_u32(u32::from_raw_be(ptr)),
396                raw::SoundIoFormat::SoundIoFormatFloat32LE => T::from_f32(f32::from_raw_le(ptr)),
397                raw::SoundIoFormat::SoundIoFormatFloat32BE => T::from_f32(f32::from_raw_be(ptr)),
398                raw::SoundIoFormat::SoundIoFormatFloat64LE => T::from_f64(f64::from_raw_le(ptr)),
399                raw::SoundIoFormat::SoundIoFormatFloat64BE => T::from_f64(f64::from_raw_be(ptr)),
400                _ => panic!("Unknown format"),
401            }
402        }
403    }
404
405    // TODO: To acheive speed *and* safety I can use iterators. That will be in a future API.
406}
407
408impl<'a> Drop for InStreamReader<'a> {
409    /// This will drop all of the frames from when you called `begin_read()`.
410    ///
411    /// Errors are currently are just printed to the console and ignored.
412    ///
413    /// # Errors
414    ///
415    /// * `Error::Streaming`
416    fn drop(&mut self) {
417        if self.read_started {
418            unsafe {
419                match raw::soundio_instream_end_read(self.instream) {
420                    0 => {}
421                    x => println!("Error reading instream: {}", Error::from(x)),
422                }
423            }
424        }
425    }
426}