1 | //! Audio playback and capture |
2 | //! |
3 | //! # Example |
4 | //! Playback a sine wave through the "default" device. |
5 | //! |
6 | //! ``` |
7 | //! use alsa::{Direction, ValueOr}; |
8 | //! use alsa::pcm::{PCM, HwParams, Format, Access, State}; |
9 | //! |
10 | //! // Open default playback device |
11 | //! let pcm = PCM::new("default" , Direction::Playback, false).unwrap(); |
12 | //! |
13 | //! // Set hardware parameters: 44100 Hz / Mono / 16 bit |
14 | //! let hwp = HwParams::any(&pcm).unwrap(); |
15 | //! hwp.set_channels(1).unwrap(); |
16 | //! hwp.set_rate(44100, ValueOr::Nearest).unwrap(); |
17 | //! hwp.set_format(Format::s16()).unwrap(); |
18 | //! hwp.set_access(Access::RWInterleaved).unwrap(); |
19 | //! pcm.hw_params(&hwp).unwrap(); |
20 | //! let io = pcm.io_i16().unwrap(); |
21 | //! |
22 | //! // Make sure we don't start the stream too early |
23 | //! let hwp = pcm.hw_params_current().unwrap(); |
24 | //! let swp = pcm.sw_params_current().unwrap(); |
25 | //! swp.set_start_threshold(hwp.get_buffer_size().unwrap()).unwrap(); |
26 | //! pcm.sw_params(&swp).unwrap(); |
27 | //! |
28 | //! // Make a sine wave |
29 | //! let mut buf = [0i16; 1024]; |
30 | //! for (i, a) in buf.iter_mut().enumerate() { |
31 | //! *a = ((i as f32 * 2.0 * ::std::f32::consts::PI / 128.0).sin() * 8192.0) as i16 |
32 | //! } |
33 | //! |
34 | //! // Play it back for 2 seconds. |
35 | //! for _ in 0..2*44100/1024 { |
36 | //! assert_eq!(io.writei(&buf[..]).unwrap(), 1024); |
37 | //! } |
38 | //! |
39 | //! // In case the buffer was larger than 2 seconds, start the stream manually. |
40 | //! if pcm.state() != State::Running { pcm.start().unwrap() }; |
41 | //! // Wait for the stream to finish playback. |
42 | //! pcm.drain().unwrap(); |
43 | //! ``` |
44 | |
45 | |
46 | use libc::{c_int, c_uint, c_void, ssize_t, c_short, timespec, pollfd}; |
47 | use crate::alsa; |
48 | use std::convert::Infallible; |
49 | use std::marker::PhantomData; |
50 | use std::mem::size_of; |
51 | use std::ffi::{CStr, CString}; |
52 | use std::str::FromStr; |
53 | use std::{io, fmt, ptr, cell}; |
54 | use super::error::*; |
55 | use super::{Direction, Output, poll, ValueOr, chmap}; |
56 | |
57 | pub use super::chmap::{Chmap, ChmapPosition, ChmapType, ChmapsQuery}; |
58 | |
59 | /// [snd_pcm_sframes_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m.html) |
60 | pub type Frames = alsa::snd_pcm_sframes_t; |
61 | |
62 | /// [snd_pcm_info_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m.html) wrapper - PCM generic info container |
63 | pub struct Info(pub(crate) *mut alsa::snd_pcm_info_t); |
64 | |
65 | impl Info { |
66 | pub fn new() -> Result<Info> { |
67 | let mut p = ptr::null_mut(); |
68 | acheck!(snd_pcm_info_malloc(&mut p)).map(|_| Info(p)) |
69 | } |
70 | |
71 | pub fn get_card(&self) -> i32 { |
72 | unsafe { alsa::snd_pcm_info_get_card(self.0) } |
73 | } |
74 | |
75 | pub fn get_device(&self) -> u32 { |
76 | unsafe { alsa::snd_pcm_info_get_device(self.0) } |
77 | } |
78 | |
79 | pub fn get_subdevice(&self) -> u32 { |
80 | unsafe { alsa::snd_pcm_info_get_subdevice(self.0) } |
81 | } |
82 | |
83 | pub fn get_id(&self) -> Result<&str> { |
84 | let c = unsafe { alsa::snd_pcm_info_get_id(self.0) }; |
85 | from_const("snd_pcm_info_get_id" , c) |
86 | } |
87 | |
88 | pub fn get_name(&self) -> Result<&str> { |
89 | let c = unsafe { alsa::snd_pcm_info_get_name(self.0) }; |
90 | from_const("snd_pcm_info_get_name" , c) |
91 | } |
92 | |
93 | pub fn get_subdevice_name(&self) -> Result<&str> { |
94 | let c = unsafe { alsa::snd_pcm_info_get_subdevice_name(self.0) }; |
95 | from_const("snd_pcm_info_get_subdevice_name" , c) |
96 | } |
97 | |
98 | pub fn get_stream(&self) -> Direction { |
99 | match unsafe { alsa::snd_pcm_info_get_stream(self.0) } { |
100 | alsa::SND_PCM_STREAM_CAPTURE => Direction::Capture, |
101 | alsa::SND_PCM_STREAM_PLAYBACK => Direction::Playback, |
102 | n @ _ => panic!("snd_pcm_info_get_stream invalid direction ' {}'" , n), |
103 | } |
104 | } |
105 | |
106 | pub fn get_subdevices_count(&self) -> u32 { |
107 | unsafe { alsa::snd_pcm_info_get_subdevices_count(self.0) } |
108 | } |
109 | |
110 | pub fn get_subdevices_avail(&self) -> u32 { |
111 | unsafe { alsa::snd_pcm_info_get_subdevices_avail(self.0) } |
112 | } |
113 | |
114 | pub(crate) fn set_device(&mut self, device: u32) { |
115 | unsafe { alsa::snd_pcm_info_set_device(self.0, device) } |
116 | } |
117 | |
118 | pub(crate) fn set_stream(&mut self, direction: Direction) { |
119 | let stream = match direction { |
120 | Direction::Capture => alsa::SND_PCM_STREAM_CAPTURE, |
121 | Direction::Playback => alsa::SND_PCM_STREAM_PLAYBACK, |
122 | }; |
123 | unsafe { alsa::snd_pcm_info_set_stream(self.0, stream) } |
124 | } |
125 | |
126 | pub(crate) fn set_subdevice(&mut self, subdevice: u32) { |
127 | unsafe { alsa::snd_pcm_info_set_subdevice(self.0, subdevice) } |
128 | } |
129 | } |
130 | |
131 | impl Drop for Info { |
132 | fn drop(&mut self) { unsafe { alsa::snd_pcm_info_free(self.0) }; } |
133 | } |
134 | |
135 | /// [snd_pcm_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m.html) wrapper - start here for audio playback and recording |
136 | pub struct PCM(*mut alsa::snd_pcm_t, cell::Cell<bool>); |
137 | |
138 | unsafe impl Send for PCM {} |
139 | |
140 | impl PCM { |
141 | fn check_has_io(&self) { |
142 | if self.1.get() { panic!("No hw_params call or additional IO objects allowed" ) } |
143 | } |
144 | |
145 | /// Wrapper around open that takes a &str instead of a &CStr |
146 | pub fn new(name: &str, dir: Direction, nonblock: bool) -> Result<PCM> { |
147 | Self::open(&CString::new(name).unwrap(), dir, nonblock) |
148 | } |
149 | |
150 | // Does not offer async mode (it's not very Rustic anyway) |
151 | pub fn open(name: &CStr, dir: Direction, nonblock: bool) -> Result<PCM> { |
152 | let mut r = ptr::null_mut(); |
153 | let stream = match dir { |
154 | Direction::Capture => alsa::SND_PCM_STREAM_CAPTURE, |
155 | Direction::Playback => alsa::SND_PCM_STREAM_PLAYBACK |
156 | }; |
157 | let flags = if nonblock { alsa::SND_PCM_NONBLOCK } else { 0 }; |
158 | acheck!(snd_pcm_open(&mut r, name.as_ptr(), stream, flags)).map(|_| PCM(r, cell::Cell::new(false))) |
159 | } |
160 | |
161 | pub fn start(&self) -> Result<()> { acheck!(snd_pcm_start(self.0)).map(|_| ()) } |
162 | pub fn drop(&self) -> Result<()> { acheck!(snd_pcm_drop(self.0)).map(|_| ()) } |
163 | pub fn pause(&self, pause: bool) -> Result<()> { |
164 | acheck!(snd_pcm_pause(self.0, if pause { 1 } else { 0 })).map(|_| ()) } |
165 | pub fn resume(&self) -> Result<()> { acheck!(snd_pcm_resume(self.0)).map(|_| ()) } |
166 | pub fn drain(&self) -> Result<()> { acheck!(snd_pcm_drain(self.0)).map(|_| ()) } |
167 | pub fn prepare(&self) -> Result<()> { acheck!(snd_pcm_prepare(self.0)).map(|_| ()) } |
168 | pub fn reset(&self) -> Result<()> { acheck!(snd_pcm_reset(self.0)).map(|_| ()) } |
169 | pub fn recover(&self, err: c_int, silent: bool) -> Result<()> { |
170 | acheck!(snd_pcm_recover(self.0, err, if silent { 1 } else { 0 })).map(|_| ()) } |
171 | |
172 | /// Wrapper around snd_pcm_recover. |
173 | /// |
174 | /// Returns Ok if the error was successfully recovered from, or the original |
175 | /// error if the error was unhandled. |
176 | pub fn try_recover(&self, err: Error, silent: bool) -> Result<()> { |
177 | self.recover(err.errno() as c_int, silent) |
178 | } |
179 | |
180 | pub fn wait(&self, timeout_ms: Option<u32>) -> Result<bool> { |
181 | acheck!(snd_pcm_wait(self.0, timeout_ms.map(|x| x as c_int).unwrap_or(-1))).map(|i| i == 1) } |
182 | |
183 | pub fn state(&self) -> State { |
184 | let rawstate = self.state_raw(); |
185 | if let Ok(state) = State::from_c_int(rawstate, "snd_pcm_state" ) { |
186 | state |
187 | } |
188 | else { |
189 | panic!("snd_pcm_state returned an invalid value of {}" , rawstate); |
190 | } |
191 | } |
192 | |
193 | /// Only used internally, and for debugging the alsa library. Please use the "state" function instead. |
194 | pub fn state_raw(&self) -> c_int { unsafe { alsa::snd_pcm_state(self.0) as c_int } } |
195 | |
196 | pub fn bytes_to_frames(&self, i: isize) -> Frames { unsafe { alsa::snd_pcm_bytes_to_frames(self.0, i as ssize_t) }} |
197 | pub fn frames_to_bytes(&self, i: Frames) -> isize { unsafe { alsa::snd_pcm_frames_to_bytes(self.0, i) as isize }} |
198 | |
199 | pub fn avail_update(&self) -> Result<Frames> { acheck!(snd_pcm_avail_update(self.0)) } |
200 | pub fn avail(&self) -> Result<Frames> { acheck!(snd_pcm_avail(self.0)) } |
201 | |
202 | pub fn avail_delay(&self) -> Result<(Frames, Frames)> { |
203 | let (mut a, mut d) = (0, 0); |
204 | acheck!(snd_pcm_avail_delay(self.0, &mut a, &mut d)).map(|_| (a, d)) |
205 | } |
206 | pub fn delay(&self) -> Result<Frames> { |
207 | let mut d = 0; |
208 | acheck!(snd_pcm_delay(self.0, &mut d)).map(|_| d) |
209 | } |
210 | |
211 | pub fn status(&self) -> Result<Status> { |
212 | StatusBuilder::new().build(self) |
213 | } |
214 | |
215 | fn verify_format(&self, f: Format) -> Result<()> { |
216 | let ff = self.hw_params_current().and_then(|h| h.get_format())?; |
217 | if ff == f { Ok(()) } |
218 | else { |
219 | // let s = format!("Invalid sample format ({:?}, expected {:?})", ff, f); |
220 | Err(Error::unsupported("io_xx" )) |
221 | } |
222 | } |
223 | |
224 | pub fn io_i8(&self) -> Result<IO<i8>> { self.io_checked() } |
225 | pub fn io_u8(&self) -> Result<IO<u8>> { self.io_checked() } |
226 | pub fn io_i16(&self) -> Result<IO<i16>> { self.io_checked() } |
227 | pub fn io_u16(&self) -> Result<IO<u16>> { self.io_checked() } |
228 | pub fn io_i32(&self) -> Result<IO<i32>> { self.io_checked() } |
229 | pub fn io_u32(&self) -> Result<IO<u32>> { self.io_checked() } |
230 | pub fn io_f32(&self) -> Result<IO<f32>> { self.io_checked() } |
231 | pub fn io_f64(&self) -> Result<IO<f64>> { self.io_checked() } |
232 | |
233 | pub fn io_checked<S: IoFormat>(&self) -> Result<IO<S>> { |
234 | self.verify_format(S::FORMAT).map(|_| IO::new(self)) |
235 | } |
236 | |
237 | /// Creates IO without checking [`S`] is valid type. |
238 | /// |
239 | /// SAFETY: Caller must guarantee [`S`] is valid type for this PCM stream |
240 | /// and that no other IO objects exist at the same time for the same stream |
241 | /// (or in some other way guarantee mmap safety) |
242 | pub unsafe fn io_unchecked<S: IoFormat>(&self) -> IO<S> { |
243 | IO::new_unchecked(self) |
244 | } |
245 | |
246 | #[deprecated (note = "renamed to io_bytes" )] |
247 | pub fn io(&self) -> IO<u8> { IO::new(self) } |
248 | pub fn io_bytes(&self) -> IO<u8> { IO::new(self) } |
249 | |
250 | /// Read buffers by talking to the kernel directly, bypassing alsa-lib. |
251 | pub fn direct_mmap_capture<S>(&self) -> Result<crate::direct::pcm::MmapCapture<S>> { |
252 | self.check_has_io(); |
253 | crate::direct::pcm::new_mmap(self) |
254 | } |
255 | |
256 | /// Write buffers by talking to the kernel directly, bypassing alsa-lib. |
257 | pub fn direct_mmap_playback<S>(&self) -> Result<crate::direct::pcm::MmapPlayback<S>> { |
258 | self.check_has_io(); |
259 | crate::direct::pcm::new_mmap(self) |
260 | } |
261 | |
262 | /// Sets hw parameters. Note: No IO object can exist for this PCM |
263 | /// when hw parameters are set. |
264 | pub fn hw_params(&self, h: &HwParams) -> Result<()> { |
265 | self.check_has_io(); |
266 | acheck!(snd_pcm_hw_params(self.0, h.0)).map(|_| ()) |
267 | } |
268 | |
269 | /// Retreive current PCM hardware configuration. |
270 | pub fn hw_params_current(&self) -> Result<HwParams> { |
271 | HwParams::new(self).and_then(|h| |
272 | acheck!(snd_pcm_hw_params_current(self.0, h.0)).map(|_| h)) |
273 | } |
274 | |
275 | pub fn sw_params(&self, h: &SwParams) -> Result<()> { |
276 | acheck!(snd_pcm_sw_params(self.0, h.0)).map(|_| ()) |
277 | } |
278 | |
279 | pub fn sw_params_current(&self) -> Result<SwParams> { |
280 | SwParams::new(self).and_then(|h| |
281 | acheck!(snd_pcm_sw_params_current(self.0, h.0)).map(|_| h)) |
282 | } |
283 | |
284 | /// Wraps `snd_pcm_get_params`, returns `(buffer_size, period_size)`. |
285 | pub fn get_params(&self) -> Result<(u64, u64)> { |
286 | let mut buffer_size = 0; |
287 | let mut period_size = 0; |
288 | acheck!(snd_pcm_get_params(self.0, &mut buffer_size, &mut period_size)) |
289 | .map(|_| (buffer_size as u64, period_size as u64)) |
290 | |
291 | } |
292 | |
293 | pub fn info(&self) -> Result<Info> { |
294 | Info::new().and_then(|info| |
295 | acheck!(snd_pcm_info(self.0, info.0)).map(|_| info )) |
296 | } |
297 | |
298 | pub fn dump(&self, o: &mut Output) -> Result<()> { |
299 | acheck!(snd_pcm_dump(self.0, super::io::output_handle(o))).map(|_| ()) |
300 | } |
301 | |
302 | pub fn dump_hw_setup(&self, o: &mut Output) -> Result<()> { |
303 | acheck!(snd_pcm_dump_hw_setup(self.0, super::io::output_handle(o))).map(|_| ()) |
304 | } |
305 | |
306 | pub fn dump_sw_setup(&self, o: &mut Output) -> Result<()> { |
307 | acheck!(snd_pcm_dump_sw_setup(self.0, super::io::output_handle(o))).map(|_| ()) |
308 | } |
309 | |
310 | pub fn query_chmaps(&self) -> ChmapsQuery { |
311 | chmap::chmaps_query_new(unsafe { alsa::snd_pcm_query_chmaps(self.0) }) |
312 | } |
313 | |
314 | pub fn set_chmap(&self, c: &Chmap) -> Result<()> { |
315 | acheck!(snd_pcm_set_chmap(self.0, chmap::chmap_handle(c))).map(|_| ()) |
316 | } |
317 | |
318 | pub fn get_chmap(&self) -> Result<Chmap> { |
319 | let p = unsafe { alsa::snd_pcm_get_chmap(self.0) }; |
320 | if p.is_null() { Err(Error::unsupported("snd_pcm_get_chmap" )) } |
321 | else { Ok(chmap::chmap_new(p)) } |
322 | } |
323 | |
324 | pub fn link(&self, other: &PCM) -> Result<()> { |
325 | acheck!(snd_pcm_link(self.0, other.0)).map(|_| ()) |
326 | } |
327 | |
328 | pub fn unlink(&self) -> Result<()> { |
329 | acheck!(snd_pcm_unlink(self.0)).map(|_| ()) |
330 | } |
331 | } |
332 | |
333 | impl Drop for PCM { |
334 | fn drop(&mut self) { unsafe { alsa::snd_pcm_close(self.0) }; } |
335 | } |
336 | |
337 | |
338 | impl poll::Descriptors for PCM { |
339 | fn count(&self) -> usize { |
340 | unsafe { alsa::snd_pcm_poll_descriptors_count(self.0) as usize } |
341 | } |
342 | fn fill(&self, p: &mut [pollfd]) -> Result<usize> { |
343 | let z: i32 = unsafe { alsa::snd_pcm_poll_descriptors(self.0, pfds:p.as_mut_ptr(), space:p.len() as c_uint) }; |
344 | from_code("snd_pcm_poll_descriptors" , z).map(|_| z as usize) |
345 | } |
346 | fn revents(&self, p: &[pollfd]) -> Result<poll::Flags> { |
347 | let mut r: u16 = 0; |
348 | let z: i32 = unsafe { alsa::snd_pcm_poll_descriptors_revents(self.0, pfds:p.as_ptr() as *mut pollfd, nfds:p.len() as c_uint, &mut r) }; |
349 | from_code("snd_pcm_poll_descriptors_revents" , z).map(|_| poll::Flags::from_bits_truncate(bits:r as c_short)) |
350 | } |
351 | } |
352 | |
353 | /// Sample format dependent struct for reading from and writing data to a `PCM`. |
354 | /// Also implements `std::io::Read` and `std::io::Write`. |
355 | /// |
356 | /// Note: Only one IO object is allowed in scope at a time (for mmap safety). |
357 | pub struct IO<'a, S: Copy>(&'a PCM, PhantomData<S>); |
358 | |
359 | impl<'a, S: Copy> Drop for IO<'a, S> { |
360 | fn drop(&mut self) { (self.0).1.set(val:false) } |
361 | } |
362 | |
363 | impl<'a, S: Copy> IO<'a, S> { |
364 | |
365 | fn new(a: &'a PCM) -> IO<'a, S> { |
366 | a.check_has_io(); |
367 | a.1.set(true); |
368 | IO(a, PhantomData) |
369 | } |
370 | |
371 | unsafe fn new_unchecked(a: &'a PCM) -> IO<'a, S> { |
372 | a.1.set(true); |
373 | IO(a, PhantomData) |
374 | } |
375 | |
376 | fn to_frames(&self, b: usize) -> alsa::snd_pcm_uframes_t { |
377 | // TODO: Do we need to check for overflow here? |
378 | self.0.bytes_to_frames((b * size_of::<S>()) as isize) as alsa::snd_pcm_uframes_t |
379 | } |
380 | |
381 | fn from_frames(&self, b: alsa::snd_pcm_uframes_t) -> usize { |
382 | // TODO: Do we need to check for overflow here? |
383 | (self.0.frames_to_bytes(b as Frames) as usize) / size_of::<S>() |
384 | } |
385 | |
386 | /// On success, returns number of *frames* written. |
387 | /// (Multiply with number of channels to get number of items in buf successfully written.) |
388 | pub fn writei(&self, buf: &[S]) -> Result<usize> { |
389 | acheck!(snd_pcm_writei((self.0).0, buf.as_ptr() as *const c_void, self.to_frames(buf.len()))).map(|r| r as usize) |
390 | } |
391 | |
392 | /// On success, returns number of *frames* read. |
393 | /// (Multiply with number of channels to get number of items in buf successfully read.) |
394 | pub fn readi(&self, buf: &mut [S]) -> Result<usize> { |
395 | acheck!(snd_pcm_readi((self.0).0, buf.as_mut_ptr() as *mut c_void, self.to_frames(buf.len()))).map(|r| r as usize) |
396 | } |
397 | |
398 | /// Wrapper around snd_pcm_mmap_begin and snd_pcm_mmap_commit. |
399 | /// |
400 | /// You can read/write into the sound card's buffer during the call to the closure. |
401 | /// According to alsa-lib docs, you should call avail_update before calling this function. |
402 | /// |
403 | /// All calculations are in *frames*, i e, the closure should return number of frames processed. |
404 | /// Also, there might not be as many frames to read/write as requested, and there can even be |
405 | /// an empty buffer supplied to the closure. |
406 | /// |
407 | /// Note: This function works only with interleaved access mode. |
408 | pub fn mmap<F: FnOnce(&mut [S]) -> usize>(&self, frames: usize, func: F) -> Result<usize> { |
409 | let mut f = frames as alsa::snd_pcm_uframes_t; |
410 | let mut offs: alsa::snd_pcm_uframes_t = 0; |
411 | let mut areas = ptr::null(); |
412 | acheck!(snd_pcm_mmap_begin((self.0).0, &mut areas, &mut offs, &mut f))?; |
413 | |
414 | let (first, step) = unsafe { ((*areas).first, (*areas).step) }; |
415 | if first != 0 || step as isize != self.0.frames_to_bytes(1) * 8 { |
416 | unsafe { alsa::snd_pcm_mmap_commit((self.0).0, offs, 0) }; |
417 | // let s = format!("Can only mmap a single interleaved buffer (first = {:?}, step = {:?})", first, step); |
418 | return Err(Error::unsupported("snd_pcm_mmap_begin" )); |
419 | } |
420 | |
421 | let buf = unsafe { |
422 | let p = ((*areas).addr as *mut S).add(self.from_frames(offs)); |
423 | ::std::slice::from_raw_parts_mut(p, self.from_frames(f)) |
424 | }; |
425 | let fres = func(buf); |
426 | debug_assert!(fres <= f as usize); |
427 | acheck!(snd_pcm_mmap_commit((self.0).0, offs, fres as alsa::snd_pcm_uframes_t)).map(|r| r as usize) |
428 | } |
429 | } |
430 | |
431 | impl<'a, S: Copy> io::Read for IO<'a, S> { |
432 | fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { |
433 | let size: u64 = self.0.bytes_to_frames(buf.len() as isize) as alsa::snd_pcm_uframes_t; // TODO: Do we need to check for overflow here? |
434 | let r: i64 = unsafe { alsa::snd_pcm_readi((self.0).0, buffer:buf.as_mut_ptr() as *mut c_void, size) }; |
435 | if r < 0 { Err(io::Error::from_raw_os_error(code:r as i32)) } |
436 | else { Ok(self.0.frames_to_bytes(r) as usize) } |
437 | } |
438 | } |
439 | |
440 | impl<'a, S: Copy> io::Write for IO<'a, S> { |
441 | fn write(&mut self, buf: &[u8]) -> io::Result<usize> { |
442 | let size: u64 = self.0.bytes_to_frames(buf.len() as isize) as alsa::snd_pcm_uframes_t; // TODO: Do we need to check for overflow here? |
443 | let r: i64 = unsafe { alsa::snd_pcm_writei((self.0).0, buffer:buf.as_ptr() as *const c_void, size) }; |
444 | if r < 0 { Err(io::Error::from_raw_os_error(code:r as i32)) } |
445 | else { Ok(self.0.frames_to_bytes(r) as usize) } |
446 | } |
447 | fn flush(&mut self) -> io::Result<()> { Ok(()) } |
448 | } |
449 | |
450 | |
451 | alsa_enum!( |
452 | /// [SND_PCM_STATE_xxx](http://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m.html) constants |
453 | State, ALL_STATES[9], |
454 | |
455 | Open = SND_PCM_STATE_OPEN, |
456 | Setup = SND_PCM_STATE_SETUP, |
457 | Prepared = SND_PCM_STATE_PREPARED, |
458 | Running = SND_PCM_STATE_RUNNING, |
459 | XRun = SND_PCM_STATE_XRUN, |
460 | Draining = SND_PCM_STATE_DRAINING, |
461 | Paused = SND_PCM_STATE_PAUSED, |
462 | Suspended = SND_PCM_STATE_SUSPENDED, |
463 | Disconnected = SND_PCM_STATE_DISCONNECTED, |
464 | ); |
465 | |
466 | alsa_enum!( |
467 | #[non_exhaustive ] |
468 | /// [SND_PCM_FORMAT_xxx](http://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m.html) constants |
469 | Format, ALL_FORMATS[48], |
470 | |
471 | Unknown = SND_PCM_FORMAT_UNKNOWN, |
472 | S8 = SND_PCM_FORMAT_S8, |
473 | U8 = SND_PCM_FORMAT_U8, |
474 | S16LE = SND_PCM_FORMAT_S16_LE, |
475 | S16BE = SND_PCM_FORMAT_S16_BE, |
476 | U16LE = SND_PCM_FORMAT_U16_LE, |
477 | U16BE = SND_PCM_FORMAT_U16_BE, |
478 | S24LE = SND_PCM_FORMAT_S24_LE, |
479 | S24BE = SND_PCM_FORMAT_S24_BE, |
480 | U24LE = SND_PCM_FORMAT_U24_LE, |
481 | U24BE = SND_PCM_FORMAT_U24_BE, |
482 | S32LE = SND_PCM_FORMAT_S32_LE, |
483 | S32BE = SND_PCM_FORMAT_S32_BE, |
484 | U32LE = SND_PCM_FORMAT_U32_LE, |
485 | U32BE = SND_PCM_FORMAT_U32_BE, |
486 | FloatLE = SND_PCM_FORMAT_FLOAT_LE, |
487 | FloatBE = SND_PCM_FORMAT_FLOAT_BE, |
488 | Float64LE = SND_PCM_FORMAT_FLOAT64_LE, |
489 | Float64BE = SND_PCM_FORMAT_FLOAT64_BE, |
490 | IEC958SubframeLE = SND_PCM_FORMAT_IEC958_SUBFRAME_LE, |
491 | IEC958SubframeBE = SND_PCM_FORMAT_IEC958_SUBFRAME_BE, |
492 | MuLaw = SND_PCM_FORMAT_MU_LAW, |
493 | ALaw = SND_PCM_FORMAT_A_LAW, |
494 | ImaAdPCM = SND_PCM_FORMAT_IMA_ADPCM, |
495 | MPEG = SND_PCM_FORMAT_MPEG, |
496 | GSM = SND_PCM_FORMAT_GSM, |
497 | Special = SND_PCM_FORMAT_SPECIAL, |
498 | S243LE = SND_PCM_FORMAT_S24_3LE, |
499 | S243BE = SND_PCM_FORMAT_S24_3BE, |
500 | U243LE = SND_PCM_FORMAT_U24_3LE, |
501 | U243BE = SND_PCM_FORMAT_U24_3BE, |
502 | S203LE = SND_PCM_FORMAT_S20_3LE, |
503 | S203BE = SND_PCM_FORMAT_S20_3BE, |
504 | U203LE = SND_PCM_FORMAT_U20_3LE, |
505 | U203BE = SND_PCM_FORMAT_U20_3BE, |
506 | S183LE = SND_PCM_FORMAT_S18_3LE, |
507 | S183BE = SND_PCM_FORMAT_S18_3BE, |
508 | U183LE = SND_PCM_FORMAT_U18_3LE, |
509 | U183BE = SND_PCM_FORMAT_U18_3BE, |
510 | G72324 = SND_PCM_FORMAT_G723_24, |
511 | G723241B = SND_PCM_FORMAT_G723_24_1B, |
512 | G72340 = SND_PCM_FORMAT_G723_40, |
513 | G723401B = SND_PCM_FORMAT_G723_40_1B, |
514 | DSDU8 = SND_PCM_FORMAT_DSD_U8, |
515 | DSDU16LE = SND_PCM_FORMAT_DSD_U16_LE, |
516 | DSDU32LE = SND_PCM_FORMAT_DSD_U32_LE, |
517 | DSDU16BE = SND_PCM_FORMAT_DSD_U16_BE, |
518 | DSDU32BE = SND_PCM_FORMAT_DSD_U32_BE, |
519 | ); |
520 | |
521 | impl fmt::Display for Format { |
522 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
523 | use Format::*; |
524 | match *self { |
525 | S8 => write!(f, "S8" ), |
526 | U8 => write!(f, "U8" ), |
527 | S16LE => write!(f, "S16_LE" ), |
528 | S16BE => write!(f, "S16_BE" ), |
529 | U16LE => write!(f, "U16_LE" ), |
530 | U16BE => write!(f, "U16_BE" ), |
531 | S24LE => write!(f, "S24_LE" ), |
532 | S24BE => write!(f, "S24_BE" ), |
533 | U24LE => write!(f, "U24_LE" ), |
534 | U24BE => write!(f, "U24_BE" ), |
535 | S32LE => write!(f, "S32_LE" ), |
536 | S32BE => write!(f, "S32_BE" ), |
537 | U32LE => write!(f, "U32_LE" ), |
538 | U32BE => write!(f, "U32_BE" ), |
539 | FloatLE => write!(f, "FLOAT_LE" ), |
540 | FloatBE => write!(f, "FLOAT_BE" ), |
541 | Float64LE => write!(f, "FLOAT64_LE" ), |
542 | Float64BE => write!(f, "FLOAT64_BE" ), |
543 | IEC958SubframeLE => write!(f, "IEC958_SUBFRAME_LE" ), |
544 | IEC958SubframeBE => write!(f, "IEC958_SUBFRAME_BE" ), |
545 | MuLaw => write!(f, "MU_LAW" ), |
546 | ALaw => write!(f, "A_LAW" ), |
547 | ImaAdPCM => write!(f, "IMA_ADPCM" ), |
548 | MPEG => write!(f, "MPEG" ), |
549 | GSM => write!(f, "GSM" ), |
550 | Special => write!(f, "SPECIAL" ), |
551 | S243LE => write!(f, "S24_3LE" ), |
552 | S243BE => write!(f, "S24_3BE" ), |
553 | U243LE => write!(f, "U24_3LE" ), |
554 | U243BE => write!(f, "U24_3BE" ), |
555 | S203LE => write!(f, "S20_3LE" ), |
556 | S203BE => write!(f, "S20_3BE" ), |
557 | U203LE => write!(f, "U20_3LE" ), |
558 | U203BE => write!(f, "U20_3BE" ), |
559 | S183LE => write!(f, "S18_3LE" ), |
560 | S183BE => write!(f, "S18_3BE" ), |
561 | U183LE => write!(f, "U18_3LE" ), |
562 | U183BE => write!(f, "U18_3BE" ), |
563 | G72324 => write!(f, "G723_24" ), |
564 | G723241B => write!(f, "G723_24_1B" ), |
565 | G72340 => write!(f, "G723_40" ), |
566 | G723401B => write!(f, "G723_40_1B" ), |
567 | DSDU8 => write!(f, "DSD_U8" ), |
568 | DSDU16LE => write!(f, "DSD_U16_LE" ), |
569 | DSDU32LE => write!(f, "DSD_U32_LE" ), |
570 | DSDU16BE => write!(f, "DSD_U16_BE" ), |
571 | DSDU32BE => write!(f, "DSD_U32_BE" ), |
572 | _ => write!(f, "UNKNOWN" ), |
573 | } |
574 | } |
575 | } |
576 | |
577 | impl FromStr for Format { |
578 | type Err = Infallible; |
579 | |
580 | fn from_str(s: &str) -> std::result::Result<Self, Self::Err> { |
581 | use Format::*; |
582 | Ok(match s.to_ascii_uppercase().as_str() { |
583 | "S8" => S8, |
584 | "U8" => U8, |
585 | "S16_LE" => S16LE, |
586 | "S16_BE" => S16BE, |
587 | "U16_LE" => U16LE, |
588 | "U16_BE" => U16BE, |
589 | "S24_LE" => S24LE, |
590 | "S24_BE" => S24BE, |
591 | "U24_LE" => U24LE, |
592 | "U24_BE" => U24BE, |
593 | "S32_LE" => S32LE, |
594 | "S32_BE" => S32BE, |
595 | "U32_LE" => U32LE, |
596 | "U32_BE" => U32BE, |
597 | "FLOAT_LE" => FloatLE, |
598 | "FLOAT_BE" => FloatBE, |
599 | "FLOAT64_LE" => Float64LE, |
600 | "FLOAT64_BE" => Float64BE, |
601 | "IEC958_SUBFRAME_LE" => IEC958SubframeLE, |
602 | "IEC958_SUBFRAME_BE" => IEC958SubframeBE, |
603 | "MU_LAW" => MuLaw, |
604 | "A_LAW" => ALaw, |
605 | "IMA_ADPCM" => ImaAdPCM, |
606 | "MPEG" => MPEG, |
607 | "GSM" => GSM, |
608 | "SPECIAL" => Special, |
609 | "S24_3LE" => S243LE, |
610 | "S24_3BE" => S243BE, |
611 | "U24_3LE" => U243LE, |
612 | "U24_3BE" => U243BE, |
613 | "S20_3LE" => S203LE, |
614 | "S20_3BE" => S203BE, |
615 | "U20_3LE" => U203LE, |
616 | "U20_3BE" => U203BE, |
617 | "S18_3LE" => S183LE, |
618 | "S18_3BE" => S183BE, |
619 | "U18_3LE" => U183LE, |
620 | "U18_3BE" => U183BE, |
621 | "G723_24" => G72324, |
622 | "G723_24_1B" => G723241B, |
623 | "G723_40" => G72340, |
624 | "G723_40_1B" => G723401B, |
625 | "DSD_U8" => DSDU8, |
626 | "DSD_U16_LE" => DSDU16LE, |
627 | "DSD_U32_LE" => DSDU32LE, |
628 | "DSD_U16_BE" => DSDU16BE, |
629 | "DSD_U32_BE" => DSDU32BE, |
630 | _ => Unknown, |
631 | }) |
632 | } |
633 | } |
634 | |
635 | impl Format { |
636 | pub const fn s16() -> Format { <i16 as IoFormat>::FORMAT } |
637 | pub const fn u16() -> Format { <u16 as IoFormat>::FORMAT } |
638 | pub const fn s32() -> Format { <i32 as IoFormat>::FORMAT } |
639 | pub const fn u32() -> Format { <u32 as IoFormat>::FORMAT } |
640 | pub const fn float() -> Format { <f32 as IoFormat>::FORMAT } |
641 | pub const fn float64() -> Format { <f64 as IoFormat>::FORMAT } |
642 | |
643 | #[cfg (target_endian = "little" )] pub const fn s24() -> Format { Format::S24LE } |
644 | #[cfg (target_endian = "big" )] pub const fn s24() -> Format { Format::S24BE } |
645 | |
646 | #[cfg (target_endian = "little" )] pub const fn s24_3() -> Format { Format::S243LE } |
647 | #[cfg (target_endian = "big" )] pub const fn s24_3() -> Format { Format::S243BE } |
648 | |
649 | #[cfg (target_endian = "little" )] pub const fn u24() -> Format { Format::U24LE } |
650 | #[cfg (target_endian = "big" )] pub const fn u24() -> Format { Format::U24BE } |
651 | |
652 | #[cfg (target_endian = "little" )] pub const fn u24_3() -> Format { Format::U243LE } |
653 | #[cfg (target_endian = "big" )] pub const fn u24_3() -> Format { Format::U243BE } |
654 | |
655 | #[cfg (target_endian = "little" )] pub const fn s20_3() -> Format { Format::S203LE } |
656 | #[cfg (target_endian = "big" )] pub const fn s20_3() -> Format { Format::S203BE } |
657 | |
658 | #[cfg (target_endian = "little" )] pub const fn u20_3() -> Format { Format::U203LE } |
659 | #[cfg (target_endian = "big" )] pub const fn u20_3() -> Format { Format::U203BE } |
660 | |
661 | #[cfg (target_endian = "little" )] pub const fn s18_3() -> Format { Format::S183LE } |
662 | #[cfg (target_endian = "big" )] pub const fn s18_3() -> Format { Format::S183BE } |
663 | |
664 | #[cfg (target_endian = "little" )] pub const fn u18_3() -> Format { Format::U183LE } |
665 | #[cfg (target_endian = "big" )] pub const fn u18_3() -> Format { Format::U183BE } |
666 | |
667 | #[cfg (target_endian = "little" )] pub const fn dsd_u16() -> Format { Format::DSDU16LE } |
668 | #[cfg (target_endian = "big" )] pub const fn dsd_u16() -> Format { Format::DSDU16BE } |
669 | |
670 | #[cfg (target_endian = "little" )] pub const fn dsd_u32() -> Format { Format::DSDU32LE } |
671 | #[cfg (target_endian = "big" )] pub const fn dsd_u32() -> Format { Format::DSDU32BE } |
672 | |
673 | #[cfg (target_endian = "little" )] pub const fn iec958_subframe() -> Format { Format::IEC958SubframeLE } |
674 | #[cfg (target_endian = "big" )] pub const fn iec958_subframe() -> Format { Format::IEC958SubframeBE } |
675 | |
676 | pub fn physical_width(&self) -> Result<i32> { |
677 | acheck!(snd_pcm_format_physical_width(self.to_c_int())) |
678 | } |
679 | |
680 | pub fn width(&self) -> Result<i32> { |
681 | acheck!(snd_pcm_format_width(self.to_c_int())) |
682 | } |
683 | |
684 | pub fn silence_16(&self) -> u16 { |
685 | unsafe { alsa::snd_pcm_format_silence_16(self.to_c_int()) } |
686 | } |
687 | |
688 | pub fn little_endian(&self) -> Result<bool> { |
689 | acheck!(snd_pcm_format_little_endian(self.to_c_int())).map(|v| v != 0) |
690 | } |
691 | } |
692 | |
693 | |
694 | pub trait IoFormat: Copy { |
695 | const FORMAT: Format; |
696 | } |
697 | |
698 | impl IoFormat for i8 { const FORMAT: Format = Format::S8; } |
699 | impl IoFormat for u8 { const FORMAT: Format = Format::U8; } |
700 | |
701 | impl IoFormat for i16 { |
702 | #[cfg (target_endian = "little" )] |
703 | const FORMAT: Format = Format::S16LE; |
704 | #[cfg (target_endian = "big" )] |
705 | const FORMAT: Format = Format::S16BE; |
706 | } |
707 | impl IoFormat for u16 { |
708 | #[cfg (target_endian = "little" )] |
709 | const FORMAT: Format = Format::U16LE; |
710 | #[cfg (target_endian = "big" )] |
711 | const FORMAT: Format = Format::U16BE; |
712 | } |
713 | impl IoFormat for i32 { |
714 | #[cfg (target_endian = "little" )] |
715 | const FORMAT: Format = Format::S32LE; |
716 | #[cfg (target_endian = "big" )] |
717 | const FORMAT: Format = Format::S32BE; |
718 | } |
719 | impl IoFormat for u32 { |
720 | #[cfg (target_endian = "little" )] |
721 | const FORMAT: Format = Format::U32LE; |
722 | #[cfg (target_endian = "big" )] |
723 | const FORMAT: Format = Format::U32BE; |
724 | } |
725 | impl IoFormat for f32 { |
726 | #[cfg (target_endian = "little" )] |
727 | const FORMAT: Format = Format::FloatLE; |
728 | #[cfg (target_endian = "big" )] |
729 | const FORMAT: Format = Format::FloatBE; |
730 | } |
731 | impl IoFormat for f64 { |
732 | #[cfg (target_endian = "little" )] |
733 | const FORMAT: Format = Format::Float64LE; |
734 | #[cfg (target_endian = "big" )] |
735 | const FORMAT: Format = Format::Float64BE; |
736 | } |
737 | |
738 | |
739 | alsa_enum!( |
740 | /// [SND_PCM_ACCESS_xxx](http://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m.html) constants |
741 | Access, ALL_ACCESSES[5], |
742 | |
743 | MMapInterleaved = SND_PCM_ACCESS_MMAP_INTERLEAVED, |
744 | MMapNonInterleaved = SND_PCM_ACCESS_MMAP_NONINTERLEAVED, |
745 | MMapComplex = SND_PCM_ACCESS_MMAP_COMPLEX, |
746 | RWInterleaved = SND_PCM_ACCESS_RW_INTERLEAVED, |
747 | RWNonInterleaved = SND_PCM_ACCESS_RW_NONINTERLEAVED, |
748 | ); |
749 | |
750 | alsa_enum!( |
751 | /// [SND_PCM_TSTAMP_TYPE_xxx](http://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m.html) constants |
752 | TstampType, ALL_TSTAMP_TYPES[3], |
753 | |
754 | Gettimeofday = SND_PCM_TSTAMP_TYPE_GETTIMEOFDAY, |
755 | Monotonic = SND_PCM_TSTAMP_TYPE_MONOTONIC, |
756 | MonotonicRaw = SND_PCM_TSTAMP_TYPE_MONOTONIC_RAW, |
757 | ); |
758 | |
759 | /// [snd_pcm_hw_params_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m___h_w___params.html) wrapper |
760 | pub struct HwParams<'a>(*mut alsa::snd_pcm_hw_params_t, &'a PCM); |
761 | |
762 | impl<'a> Drop for HwParams<'a> { |
763 | fn drop(&mut self) { unsafe { alsa::snd_pcm_hw_params_free(self.0) }; } |
764 | } |
765 | |
766 | impl<'a> HwParams<'a> { |
767 | fn new(a: &'a PCM) -> Result<HwParams<'a>> { |
768 | let mut p = ptr::null_mut(); |
769 | acheck!(snd_pcm_hw_params_malloc(&mut p)).map(|_| HwParams(p, a)) |
770 | } |
771 | |
772 | pub fn any(a: &'a PCM) -> Result<HwParams<'a>> { HwParams::new(a).and_then(|p| |
773 | acheck!(snd_pcm_hw_params_any(a.0, p.0)).map(|_| p) |
774 | )} |
775 | |
776 | pub fn get_rate_resample(&self) -> Result<bool> { |
777 | let mut v = 0; |
778 | acheck!(snd_pcm_hw_params_get_rate_resample((self.1).0, self.0, &mut v)).map(|_| v != 0) |
779 | } |
780 | |
781 | pub fn set_rate_resample(&self, resample: bool) -> Result<()> { |
782 | acheck!(snd_pcm_hw_params_set_rate_resample((self.1).0, self.0, if resample {1} else {0})).map(|_| ()) |
783 | } |
784 | |
785 | pub fn set_channels_near(&self, v: u32) -> Result<u32> { |
786 | let mut r = v as c_uint; |
787 | acheck!(snd_pcm_hw_params_set_channels_near((self.1).0, self.0, &mut r)).map(|_| r) |
788 | } |
789 | |
790 | pub fn set_channels(&self, v: u32) -> Result<()> { |
791 | acheck!(snd_pcm_hw_params_set_channels((self.1).0, self.0, v as c_uint)).map(|_| ()) |
792 | } |
793 | |
794 | pub fn get_channels(&self) -> Result<u32> { |
795 | let mut v = 0; |
796 | acheck!(snd_pcm_hw_params_get_channels(self.0, &mut v)).map(|_| v as u32) |
797 | } |
798 | |
799 | pub fn get_channels_max(&self) -> Result<u32> { |
800 | let mut v = 0; |
801 | acheck!(snd_pcm_hw_params_get_channels_max(self.0, &mut v)).map(|_| v as u32) |
802 | } |
803 | |
804 | pub fn get_channels_min(&self) -> Result<u32> { |
805 | let mut v = 0; |
806 | acheck!(snd_pcm_hw_params_get_channels_min(self.0, &mut v)).map(|_| v as u32) |
807 | } |
808 | |
809 | pub fn test_channels(&self, v: u32) -> Result<()> { |
810 | acheck!(snd_pcm_hw_params_test_channels((self.1).0, self.0, v as c_uint)).map(|_| ()) |
811 | } |
812 | |
813 | pub fn set_rate_near(&self, v: u32, dir: ValueOr) -> Result<u32> { |
814 | let mut d = dir as c_int; |
815 | let mut r = v as c_uint; |
816 | acheck!(snd_pcm_hw_params_set_rate_near((self.1).0, self.0, &mut r, &mut d)).map(|_| r) |
817 | } |
818 | |
819 | pub fn set_rate(&self, v: u32, dir: ValueOr) -> Result<()> { |
820 | acheck!(snd_pcm_hw_params_set_rate((self.1).0, self.0, v as c_uint, dir as c_int)).map(|_| ()) |
821 | } |
822 | |
823 | pub fn get_rate(&self) -> Result<u32> { |
824 | let (mut v, mut d) = (0,0); |
825 | acheck!(snd_pcm_hw_params_get_rate(self.0, &mut v, &mut d)).map(|_| v as u32) |
826 | } |
827 | |
828 | pub fn get_rate_max(&self) -> Result<u32> { |
829 | let mut v = 0; |
830 | // Note on the null ptr: if this ptr is not null, then the value behind it is replaced with |
831 | // -1 if the suprenum is not in the set (i.e. it's an open range), 0 otherwise. This could |
832 | // be returned along with the value, but it's safe to pass a null ptr in, in which case the |
833 | // pointer is not dereferenced. |
834 | acheck!(snd_pcm_hw_params_get_rate_max(self.0, &mut v, ptr::null_mut())).map(|_| v as u32) |
835 | } |
836 | |
837 | pub fn get_rate_min(&self) -> Result<u32> { |
838 | let mut v = 0; |
839 | // Note on the null ptr: see get_rate_max but read +1 and infinum instead of -1 and |
840 | // suprenum. |
841 | acheck!(snd_pcm_hw_params_get_rate_min(self.0, &mut v, ptr::null_mut())).map(|_| v as u32) |
842 | } |
843 | |
844 | pub fn test_rate(&self, rate: u32) -> Result<()> { |
845 | acheck!(snd_pcm_hw_params_test_rate((self.1).0, self.0, rate as c_uint, 0)).map(|_| ()) |
846 | } |
847 | |
848 | pub fn set_format(&self, v: Format) -> Result<()> { |
849 | acheck!(snd_pcm_hw_params_set_format((self.1).0, self.0, v as c_int)).map(|_| ()) |
850 | } |
851 | |
852 | pub fn get_format(&self) -> Result<Format> { |
853 | let mut v = 0; |
854 | acheck!(snd_pcm_hw_params_get_format(self.0, &mut v)) |
855 | .and_then(|_| Format::from_c_int(v, "snd_pcm_hw_params_get_format" )) |
856 | } |
857 | |
858 | pub fn test_format(&self, v: Format) -> Result<()> { |
859 | acheck!(snd_pcm_hw_params_test_format((self.1).0, self.0, v as c_int)).map(|_| ()) |
860 | } |
861 | |
862 | pub fn set_access(&self, v: Access) -> Result<()> { |
863 | acheck!(snd_pcm_hw_params_set_access((self.1).0, self.0, v as c_uint)).map(|_| ()) |
864 | } |
865 | |
866 | pub fn get_access(&self) -> Result<Access> { |
867 | let mut v = 0; |
868 | acheck!(snd_pcm_hw_params_get_access(self.0, &mut v)) |
869 | .and_then(|_| Access::from_c_int(v as c_int, "snd_pcm_hw_params_get_access" )) |
870 | } |
871 | |
872 | pub fn set_period_size_near(&self, v: Frames, dir: ValueOr) -> Result<Frames> { |
873 | let mut d = dir as c_int; |
874 | let mut r = v as alsa::snd_pcm_uframes_t; |
875 | acheck!(snd_pcm_hw_params_set_period_size_near((self.1).0, self.0, &mut r, &mut d)).map(|_| r as Frames) |
876 | } |
877 | |
878 | pub fn set_period_size(&self, v: Frames, dir: ValueOr) -> Result<()> { |
879 | acheck!(snd_pcm_hw_params_set_period_size((self.1).0, self.0, v as alsa::snd_pcm_uframes_t, dir as c_int)).map(|_| ()) |
880 | } |
881 | |
882 | pub fn set_period_time_near(&self, v: u32, dir: ValueOr) -> Result<u32> { |
883 | let mut d = dir as c_int; |
884 | let mut r = v as c_uint; |
885 | acheck!(snd_pcm_hw_params_set_period_time_near((self.1).0, self.0, &mut r, &mut d)).map(|_| r as u32) |
886 | } |
887 | |
888 | pub fn get_period_size(&self) -> Result<Frames> { |
889 | let (mut v, mut d) = (0,0); |
890 | acheck!(snd_pcm_hw_params_get_period_size(self.0, &mut v, &mut d)).map(|_| v as Frames) |
891 | } |
892 | |
893 | pub fn get_period_size_min(&self) -> Result<Frames> { |
894 | let (mut v, mut d) = (0,0); |
895 | acheck!(snd_pcm_hw_params_get_period_size_min(self.0, &mut v, &mut d)).map(|_| v as Frames) |
896 | } |
897 | |
898 | pub fn get_period_size_max(&self) -> Result<Frames> { |
899 | let (mut v, mut d) = (0,0); |
900 | acheck!(snd_pcm_hw_params_get_period_size_max(self.0, &mut v, &mut d)).map(|_| v as Frames) |
901 | } |
902 | |
903 | pub fn set_periods(&self, v: u32, dir: ValueOr) -> Result<()> { |
904 | acheck!(snd_pcm_hw_params_set_periods((self.1).0, self.0, v as c_uint, dir as c_int)).map(|_| ()) |
905 | } |
906 | |
907 | pub fn get_periods(&self) -> Result<u32> { |
908 | let (mut v, mut d) = (0,0); |
909 | acheck!(snd_pcm_hw_params_get_periods(self.0, &mut v, &mut d)).map(|_| v as u32) |
910 | } |
911 | |
912 | pub fn set_buffer_size_near(&self, v: Frames) -> Result<Frames> { |
913 | let mut r = v as alsa::snd_pcm_uframes_t; |
914 | acheck!(snd_pcm_hw_params_set_buffer_size_near((self.1).0, self.0, &mut r)).map(|_| r as Frames) |
915 | } |
916 | |
917 | pub fn set_buffer_size_max(&self, v: Frames) -> Result<Frames> { |
918 | let mut r = v as alsa::snd_pcm_uframes_t; |
919 | acheck!(snd_pcm_hw_params_set_buffer_size_max((self.1).0, self.0, &mut r)).map(|_| r as Frames) |
920 | } |
921 | |
922 | pub fn set_buffer_size_min(&self, v: Frames) -> Result<Frames> { |
923 | let mut r = v as alsa::snd_pcm_uframes_t; |
924 | acheck!(snd_pcm_hw_params_set_buffer_size_min((self.1).0, self.0, &mut r)).map(|_| r as Frames) |
925 | } |
926 | |
927 | pub fn set_buffer_size(&self, v: Frames) -> Result<()> { |
928 | acheck!(snd_pcm_hw_params_set_buffer_size((self.1).0, self.0, v as alsa::snd_pcm_uframes_t)).map(|_| ()) |
929 | } |
930 | |
931 | pub fn set_buffer_time_near(&self, v: u32, dir: ValueOr) -> Result<u32> { |
932 | let mut d = dir as c_int; |
933 | let mut r = v as c_uint; |
934 | acheck!(snd_pcm_hw_params_set_buffer_time_near((self.1).0, self.0, &mut r, &mut d)).map(|_| r as u32) |
935 | } |
936 | |
937 | pub fn get_buffer_size(&self) -> Result<Frames> { |
938 | let mut v = 0; |
939 | acheck!(snd_pcm_hw_params_get_buffer_size(self.0, &mut v)).map(|_| v as Frames) |
940 | } |
941 | |
942 | pub fn get_buffer_size_min(&self) -> Result<Frames> { |
943 | let mut v = 0; |
944 | acheck!(snd_pcm_hw_params_get_buffer_size_min(self.0, &mut v)).map(|_| v as Frames) |
945 | } |
946 | |
947 | pub fn get_buffer_size_max(&self) -> Result<Frames> { |
948 | let mut v = 0; |
949 | acheck!(snd_pcm_hw_params_get_buffer_size_max(self.0, &mut v)).map(|_| v as Frames) |
950 | } |
951 | |
952 | pub fn get_buffer_time_min(&self) -> Result<u32> { |
953 | let (mut v, mut d) = (0,0); |
954 | acheck!(snd_pcm_hw_params_get_buffer_time_min(self.0, &mut v, &mut d)).map(|_| v as u32) |
955 | } |
956 | |
957 | pub fn get_buffer_time_max(&self) -> Result<u32> { |
958 | let (mut v, mut d) = (0,0); |
959 | acheck!(snd_pcm_hw_params_get_buffer_time_max(self.0, &mut v, &mut d)).map(|_| v as u32) |
960 | } |
961 | |
962 | /// Returns true if the alsa stream can be paused, false if not. |
963 | /// |
964 | /// This function should only be called when the configuration space contains a single |
965 | /// configuration. Call `PCM::hw_params` to choose a single configuration from the |
966 | /// configuration space. |
967 | pub fn can_pause(&self) -> bool { |
968 | unsafe { alsa::snd_pcm_hw_params_can_pause(self.0) != 0 } |
969 | } |
970 | |
971 | /// Returns true if the alsa stream can be resumed, false if not. |
972 | /// |
973 | /// This function should only be called when the configuration space contains a single |
974 | /// configuration. Call `PCM::hw_params` to choose a single configuration from the |
975 | /// configuration space. |
976 | pub fn can_resume(&self) -> bool { |
977 | unsafe { alsa::snd_pcm_hw_params_can_resume(self.0) != 0 } |
978 | } |
979 | |
980 | /// Returns true if the alsa stream supports the provided `AudioTstampType`, false if not. |
981 | /// |
982 | /// This function should only be called when the configuration space contains a single |
983 | /// configuration. Call `PCM::hw_params` to choose a single configuration from the |
984 | /// configuration space. |
985 | pub fn supports_audio_ts_type(&self, type_: AudioTstampType) -> bool { |
986 | unsafe { alsa::snd_pcm_hw_params_supports_audio_ts_type(self.0, type_ as libc::c_int) != 0 } |
987 | } |
988 | |
989 | pub fn dump(&self, o: &mut Output) -> Result<()> { |
990 | acheck!(snd_pcm_hw_params_dump(self.0, super::io::output_handle(o))).map(|_| ()) |
991 | } |
992 | |
993 | pub fn copy_from(&mut self, other: &HwParams<'a>) { |
994 | self.1 = other.1; |
995 | unsafe { alsa::snd_pcm_hw_params_copy(self.0, other.0) }; |
996 | } |
997 | } |
998 | |
999 | impl<'a> Clone for HwParams<'a> { |
1000 | fn clone(&self) -> HwParams<'a> { |
1001 | let mut r: HwParams<'_> = HwParams::new(self.1).unwrap(); |
1002 | r.copy_from(self); |
1003 | r |
1004 | } |
1005 | } |
1006 | |
1007 | impl<'a> fmt::Debug for HwParams<'a> { |
1008 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
1009 | f&mut DebugStruct<'_, '_>.debug_struct("HwParams" ) |
1010 | .field("channels" , &self.get_channels()) |
1011 | .field("rate" , &format!(" {:?} Hz" , self.get_rate())) |
1012 | .field("format" , &self.get_format()) |
1013 | .field("access" , &self.get_access()) |
1014 | .field("period_size" , &format!(" {:?} frames" , self.get_period_size())) |
1015 | .field(name:"buffer_size" , &format!(" {:?} frames" , self.get_buffer_size())) |
1016 | .finish() |
1017 | } |
1018 | } |
1019 | |
1020 | /// [snd_pcm_sw_params_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m___s_w___params.html) wrapper |
1021 | pub struct SwParams<'a>(*mut alsa::snd_pcm_sw_params_t, &'a PCM); |
1022 | |
1023 | impl<'a> Drop for SwParams<'a> { |
1024 | fn drop(&mut self) { unsafe { alsa::snd_pcm_sw_params_free(self.0) }; } |
1025 | } |
1026 | |
1027 | impl<'a> SwParams<'a> { |
1028 | |
1029 | fn new(a: &'a PCM) -> Result<SwParams<'a>> { |
1030 | let mut p = ptr::null_mut(); |
1031 | acheck!(snd_pcm_sw_params_malloc(&mut p)).map(|_| SwParams(p, a)) |
1032 | } |
1033 | |
1034 | pub fn set_avail_min(&self, v: Frames) -> Result<()> { |
1035 | acheck!(snd_pcm_sw_params_set_avail_min((self.1).0, self.0, v as alsa::snd_pcm_uframes_t)).map(|_| ()) |
1036 | } |
1037 | |
1038 | pub fn get_avail_min(&self) -> Result<Frames> { |
1039 | let mut v = 0; |
1040 | acheck!(snd_pcm_sw_params_get_avail_min(self.0, &mut v)).map(|_| v as Frames) |
1041 | } |
1042 | |
1043 | pub fn get_boundary(&self) -> Result<Frames> { |
1044 | let mut v = 0; |
1045 | acheck!(snd_pcm_sw_params_get_boundary(self.0, &mut v)).map(|_| v as Frames) |
1046 | } |
1047 | |
1048 | pub fn set_start_threshold(&self, v: Frames) -> Result<()> { |
1049 | acheck!(snd_pcm_sw_params_set_start_threshold((self.1).0, self.0, v as alsa::snd_pcm_uframes_t)).map(|_| ()) |
1050 | } |
1051 | |
1052 | pub fn get_start_threshold(&self) -> Result<Frames> { |
1053 | let mut v = 0; |
1054 | acheck!(snd_pcm_sw_params_get_start_threshold(self.0, &mut v)).map(|_| v as Frames) |
1055 | } |
1056 | |
1057 | pub fn set_stop_threshold(&self, v: Frames) -> Result<()> { |
1058 | acheck!(snd_pcm_sw_params_set_stop_threshold((self.1).0, self.0, v as alsa::snd_pcm_uframes_t)).map(|_| ()) |
1059 | } |
1060 | |
1061 | pub fn get_stop_threshold(&self) -> Result<Frames> { |
1062 | let mut v = 0; |
1063 | acheck!(snd_pcm_sw_params_get_stop_threshold(self.0, &mut v)).map(|_| v as Frames) |
1064 | } |
1065 | |
1066 | pub fn set_tstamp_mode(&self, v: bool) -> Result<()> { |
1067 | let z = if v { alsa::SND_PCM_TSTAMP_ENABLE } else { alsa::SND_PCM_TSTAMP_NONE }; |
1068 | acheck!(snd_pcm_sw_params_set_tstamp_mode((self.1).0, self.0, z)).map(|_| ()) |
1069 | } |
1070 | |
1071 | pub fn get_tstamp_mode(&self) -> Result<bool> { |
1072 | let mut v = 0; |
1073 | acheck!(snd_pcm_sw_params_get_tstamp_mode(self.0, &mut v)).map(|_| v != 0) |
1074 | } |
1075 | |
1076 | pub fn set_tstamp_type(&self, v: TstampType) -> Result<()> { |
1077 | acheck!(snd_pcm_sw_params_set_tstamp_type((self.1).0, self.0, v as u32)).map(|_| ()) |
1078 | } |
1079 | |
1080 | pub fn get_tstamp_type(&self) -> Result<TstampType> { |
1081 | let mut v = 0; |
1082 | acheck!(snd_pcm_sw_params_get_tstamp_type(self.0, &mut v))?; |
1083 | TstampType::from_c_int(v as c_int, "snd_pcm_sw_params_get_tstamp_type" ) |
1084 | } |
1085 | |
1086 | pub fn dump(&self, o: &mut Output) -> Result<()> { |
1087 | acheck!(snd_pcm_sw_params_dump(self.0, super::io::output_handle(o))).map(|_| ()) |
1088 | } |
1089 | } |
1090 | |
1091 | impl<'a> fmt::Debug for SwParams<'a> { |
1092 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
1093 | write!(f, |
1094 | "SwParams(avail_min: {:?} frames, start_threshold: {:?} frames, stop_threshold: {:?} frames)" , |
1095 | self.get_avail_min(), self.get_start_threshold(), self.get_stop_threshold()) |
1096 | } |
1097 | } |
1098 | |
1099 | const STATUS_SIZE: usize = 152; |
1100 | |
1101 | /// [snd_pcm_status_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m___status.html) wrapper |
1102 | pub struct Status([u8; STATUS_SIZE]); |
1103 | |
1104 | impl Status { |
1105 | fn new() -> Status { |
1106 | assert!(unsafe { alsa::snd_pcm_status_sizeof() } as usize <= STATUS_SIZE); |
1107 | Status([0; STATUS_SIZE]) |
1108 | } |
1109 | |
1110 | fn ptr(&self) -> *mut alsa::snd_pcm_status_t { self.0.as_ptr() as *const _ as *mut alsa::snd_pcm_status_t } |
1111 | |
1112 | pub fn get_htstamp(&self) -> timespec { |
1113 | let mut h = timespec {tv_sec: 0, tv_nsec: 0}; |
1114 | unsafe { alsa::snd_pcm_status_get_htstamp(self.ptr(), &mut h) }; |
1115 | h |
1116 | } |
1117 | |
1118 | pub fn get_trigger_htstamp(&self) -> timespec { |
1119 | let mut h = timespec {tv_sec: 0, tv_nsec: 0}; |
1120 | unsafe { alsa::snd_pcm_status_get_trigger_htstamp(self.ptr(), &mut h) }; |
1121 | h |
1122 | } |
1123 | |
1124 | pub fn get_audio_htstamp(&self) -> timespec { |
1125 | let mut h = timespec {tv_sec: 0, tv_nsec: 0}; |
1126 | unsafe { alsa::snd_pcm_status_get_audio_htstamp(self.ptr(), &mut h) }; |
1127 | h |
1128 | } |
1129 | |
1130 | pub fn get_state(&self) -> State { State::from_c_int( |
1131 | unsafe { alsa::snd_pcm_status_get_state(self.ptr()) } as c_int, "snd_pcm_status_get_state" ).unwrap() } |
1132 | |
1133 | pub fn get_avail(&self) -> Frames { unsafe { alsa::snd_pcm_status_get_avail(self.ptr()) as Frames }} |
1134 | pub fn get_delay(&self) -> Frames { unsafe { alsa::snd_pcm_status_get_delay(self.ptr()) }} |
1135 | pub fn get_avail_max(&self) -> Frames { unsafe { alsa::snd_pcm_status_get_avail_max(self.ptr()) as Frames }} |
1136 | pub fn get_overrange(&self) -> Frames { unsafe { alsa::snd_pcm_status_get_overrange(self.ptr()) as Frames }} |
1137 | |
1138 | pub fn dump(&self, o: &mut Output) -> Result<()> { |
1139 | acheck!(snd_pcm_status_dump(self.ptr(), super::io::output_handle(o))).map(|_| ()) |
1140 | } |
1141 | } |
1142 | |
1143 | /// Builder for [`Status`]. |
1144 | /// |
1145 | /// Allows setting the audio timestamp configuration before retrieving the |
1146 | /// status from the stream. |
1147 | pub struct StatusBuilder(Status); |
1148 | |
1149 | impl StatusBuilder { |
1150 | pub fn new() -> Self { |
1151 | StatusBuilder(Status::new()) |
1152 | } |
1153 | |
1154 | pub fn audio_htstamp_config( |
1155 | self, |
1156 | type_requested: AudioTstampType, |
1157 | report_delay: bool, |
1158 | ) -> Self { |
1159 | let mut cfg: alsa::snd_pcm_audio_tstamp_config_t = unsafe { std::mem::zeroed() }; |
1160 | cfg.set_type_requested(val:type_requested as _); |
1161 | cfg.set_report_delay(val:report_delay as _); |
1162 | unsafe { alsa::snd_pcm_status_set_audio_htstamp_config(self.0.ptr(), &mut cfg) }; |
1163 | self |
1164 | } |
1165 | |
1166 | pub fn build(self, pcm: &PCM) -> Result<Status> { |
1167 | acheck!(snd_pcm_status(pcm.0, self.0.ptr())).map(|_| self.0) |
1168 | } |
1169 | } |
1170 | |
1171 | alsa_enum!( |
1172 | #[non_exhaustive ] |
1173 | /// [SND_PCM_AUDIO_TSTAMP_TYPE_xxx](http://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m.html) constants |
1174 | AudioTstampType, ALL_AUDIO_TSTAMP_TYPES[6], |
1175 | |
1176 | Compat = SND_PCM_AUDIO_TSTAMP_TYPE_COMPAT, |
1177 | Default = SND_PCM_AUDIO_TSTAMP_TYPE_DEFAULT, |
1178 | Link = SND_PCM_AUDIO_TSTAMP_TYPE_LINK, |
1179 | LinkAbsolute = SND_PCM_AUDIO_TSTAMP_TYPE_LINK_ABSOLUTE, |
1180 | LinkEstimated = SND_PCM_AUDIO_TSTAMP_TYPE_LINK_ESTIMATED, |
1181 | LinkSynchronized = SND_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED, |
1182 | ); |
1183 | |
1184 | #[test ] |
1185 | fn info_from_default() { |
1186 | use std::ffi::CString; |
1187 | let pcm: PCM = PCM::open(&*CString::new("default" ).unwrap(), dir:Direction::Capture, nonblock:false).unwrap(); |
1188 | let info: Info = pcm.info().unwrap(); |
1189 | println!("PCM Info:" ); |
1190 | println!(" \tCard: {}" , info.get_card()); |
1191 | println!(" \tDevice: {}" , info.get_device()); |
1192 | println!(" \tSubdevice: {}" , info.get_subdevice()); |
1193 | println!(" \tId: {}" , info.get_id().unwrap()); |
1194 | println!(" \tName: {}" , info.get_name().unwrap()); |
1195 | println!(" \tSubdevice Name: {}" , info.get_subdevice_name().unwrap()); |
1196 | } |
1197 | |
1198 | #[test ] |
1199 | fn drop() { |
1200 | use std::ffi::CString; |
1201 | let pcm: PCM = PCM::open(&*CString::new("default" ).unwrap(), dir:Direction::Capture, nonblock:false).unwrap(); |
1202 | // Verify that this does not cause a naming conflict (issue #14) |
1203 | let _ = pcm.drop(); |
1204 | } |
1205 | |
1206 | #[test ] |
1207 | fn record_from_default() { |
1208 | use std::ffi::CString; |
1209 | let pcm: PCM = PCM::open(&*CString::new("default" ).unwrap(), dir:Direction::Capture, nonblock:false).unwrap(); |
1210 | let hwp: HwParams<'_> = HwParams::any(&pcm).unwrap(); |
1211 | hwp.set_channels(2).unwrap(); |
1212 | hwp.set_rate(v:44100, dir:ValueOr::Nearest).unwrap(); |
1213 | hwp.set_format(Format::s16()).unwrap(); |
1214 | hwp.set_access(Access::RWInterleaved).unwrap(); |
1215 | pcm.hw_params(&hwp).unwrap(); |
1216 | pcm.start().unwrap(); |
1217 | let mut buf: [i16; 1024] = [0i16; 1024]; |
1218 | assert_eq!(pcm.io_i16().unwrap().readi(&mut buf).unwrap(), 1024/2); |
1219 | } |
1220 | |
1221 | #[test ] |
1222 | fn playback_to_default() { |
1223 | use std::ffi::CString; |
1224 | let pcm = PCM::open(&*CString::new("default" ).unwrap(), Direction::Playback, false).unwrap(); |
1225 | let hwp = HwParams::any(&pcm).unwrap(); |
1226 | hwp.set_channels(1).unwrap(); |
1227 | hwp.set_rate(44100, ValueOr::Nearest).unwrap(); |
1228 | hwp.set_format(Format::s16()).unwrap(); |
1229 | hwp.set_access(Access::RWInterleaved).unwrap(); |
1230 | pcm.hw_params(&hwp).unwrap(); |
1231 | |
1232 | let hwp = pcm.hw_params_current().unwrap(); |
1233 | let swp = pcm.sw_params_current().unwrap(); |
1234 | swp.set_start_threshold(hwp.get_buffer_size().unwrap()).unwrap(); |
1235 | pcm.sw_params(&swp).unwrap(); |
1236 | |
1237 | println!("PCM status: {:?}, {:?}" , pcm.state(), pcm.hw_params_current().unwrap()); |
1238 | let mut outp = Output::buffer_open().unwrap(); |
1239 | pcm.dump(&mut outp).unwrap(); |
1240 | println!("== PCM dump == \n{}" , outp); |
1241 | |
1242 | let mut buf = [0i16; 1024]; |
1243 | for (i, a) in buf.iter_mut().enumerate() { |
1244 | *a = ((i as f32 * 2.0 * ::std::f32::consts::PI / 128.0).sin() * 8192.0) as i16 |
1245 | } |
1246 | let io = pcm.io_i16().unwrap(); |
1247 | for _ in 0..2*44100/1024 { // 2 seconds of playback |
1248 | println!("PCM state: {:?}" , pcm.state()); |
1249 | assert_eq!(io.writei(&buf[..]).unwrap(), 1024); |
1250 | } |
1251 | if pcm.state() != State::Running { pcm.start().unwrap() }; |
1252 | |
1253 | let mut outp2 = Output::buffer_open().unwrap(); |
1254 | pcm.status().unwrap().dump(&mut outp2).unwrap(); |
1255 | println!("== PCM status dump == \n{}" , outp2); |
1256 | |
1257 | pcm.drain().unwrap(); |
1258 | } |
1259 | |
1260 | #[test ] |
1261 | fn print_sizeof() { |
1262 | let s: usize = unsafe { alsa::snd_pcm_status_sizeof() } as usize; |
1263 | println!("Status size: {}" , s); |
1264 | |
1265 | assert!(s <= STATUS_SIZE); |
1266 | } |
1267 | |
1268 | #[test ] |
1269 | fn format_display_from_str() { |
1270 | for format: Format in ALL_FORMATS { |
1271 | assert_eq!(format, format.to_string().parse().unwrap()); |
1272 | } |
1273 | } |
1274 | |