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}