1 /*!
2 This module bypasses alsa-lib and directly read and write into memory mapped kernel memory.
3 In case of the sample memory, this is in many cases the DMA buffers that is transferred to the sound card.
4 
5 The reasons for doing this are:
6 
7  * Minimum overhead where it matters most: let alsa-lib do the code heavy setup -
8    then steal its file descriptor and deal with sample streaming from Rust.
9  * RT-safety to the maximum extent possible. Creating/dropping any of these structs causes syscalls,
10    but function calls on these are just read and write from memory. No syscalls, no memory allocations,
11    not even loops (with the exception of `MmapPlayback::write` that loops over samples to write).
12  * Possibility to allow Send + Sync for structs
13  * It's a fun experiment and an interesting deep dive into how alsa-lib does things.
14 
15 Note: Not all sound card drivers support this direct method of communication; although almost all
16 modern/common ones do. It only works with hardware devices though (such as "hw:xxx" device strings),
17 don't expect it to work with, e g, the PulseAudio plugin or so.
18 
19 For an example of how to use this mode, look in the "synth-example" directory.
20 */
21 
22 use libc;
23 use std::{mem, ptr, fmt, cmp};
24 use crate::error::{Error, Result};
25 use std::os::unix::io::RawFd;
26 use crate::{pcm, PollDescriptors, Direction};
27 use crate::pcm::Frames;
28 use std::marker::PhantomData;
29 
30 use super::ffi::*;
31 
32 /// Read PCM status via a simple kernel syscall, bypassing alsa-lib.
33 ///
34 /// If Status is not available on your architecture, this is the second best option.
35 pub struct SyncPtrStatus(snd_pcm_mmap_status);
36 
37 impl SyncPtrStatus {
38     /// Executes sync_ptr syscall.
39     ///
40     /// Unsafe because
41     ///  - setting appl_ptr and avail_min might make alsa-lib confused
42     ///  - no check that the fd is really a PCM
sync_ptr(fd: RawFd, hwsync: bool, appl_ptr: Option<pcm::Frames>, avail_min: Option<pcm::Frames>) -> Result<Self>43     pub unsafe fn sync_ptr(fd: RawFd, hwsync: bool, appl_ptr: Option<pcm::Frames>, avail_min: Option<pcm::Frames>) -> Result<Self> {
44         let mut data = snd_pcm_sync_ptr {
45 			flags: (if hwsync { SNDRV_PCM_SYNC_PTR_HWSYNC } else { 0 }) +
46 				(if appl_ptr.is_some() { SNDRV_PCM_SYNC_PTR_APPL } else { 0 }) +
47 				(if avail_min.is_some() { SNDRV_PCM_SYNC_PTR_AVAIL_MIN } else { 0 }),
48 			c: snd_pcm_mmap_control_r {
49 				control: snd_pcm_mmap_control {
50 					appl_ptr: appl_ptr.unwrap_or(0) as snd_pcm_uframes_t,
51 					avail_min: avail_min.unwrap_or(0) as snd_pcm_uframes_t,
52 				}
53 			},
54 			s: mem::zeroed()
55 		};
56 
57         sndrv_pcm_ioctl_sync_ptr(fd, &mut data).map_err(|_|
58             Error::new("SNDRV_PCM_IOCTL_SYNC_PTR", nix::errno::Errno::last() as i32))?;
59 
60         let i = data.s.status.state;
61         if (i >= (pcm::State::Open as snd_pcm_state_t)) && (i <= (pcm::State::Disconnected as snd_pcm_state_t)) {
62             Ok(SyncPtrStatus(data.s.status))
63         } else {
64             Err(Error::unsupported("SNDRV_PCM_IOCTL_SYNC_PTR returned broken state"))
65         }
66     }
67 
hw_ptr(&self) -> pcm::Frames68     pub fn hw_ptr(&self) -> pcm::Frames { self.0.hw_ptr as pcm::Frames }
state(&self) -> pcm::State69     pub fn state(&self) -> pcm::State { unsafe { mem::transmute(self.0.state as u8) } /* valid range checked in sync_ptr */ }
htstamp(&self) -> libc::timespec70     pub fn htstamp(&self) -> libc::timespec { self.0.tstamp }
71 }
72 
73 
74 
75 /// Read PCM status directly from memory, bypassing alsa-lib.
76 ///
77 /// This means that it's
78 /// 1) less overhead for reading status (no syscall, no allocations, no virtual dispatch, just a read from memory)
79 /// 2) Send + Sync, and
80 /// 3) will only work for "hw" / "plughw" devices (not e g PulseAudio plugins), and not
81 /// all of those are supported, although all common ones are (as of 2017, and a kernel from the same decade).
82 /// Kernel supported archs are: x86, PowerPC, Alpha. Use "SyncPtrStatus" for other archs.
83 ///
84 /// The values are updated every now and then by the kernel. Many functions will force an update to happen,
85 /// e g `PCM::avail()` and `PCM::delay()`.
86 ///
87 /// Note: Even if you close the original PCM device, ALSA will not actually close the device until all
88 /// Status structs are dropped too.
89 ///
90 #[derive(Debug)]
91 pub struct Status(DriverMemory<snd_pcm_mmap_status>);
92 
pcm_to_fd(p: &pcm::PCM) -> Result<RawFd>93 fn pcm_to_fd(p: &pcm::PCM) -> Result<RawFd> {
94     let mut fds: [libc::pollfd; 1] = unsafe { mem::zeroed() };
95     let c = PollDescriptors::fill(p, &mut fds)?;
96     if c != 1 {
97         return Err(Error::unsupported("snd_pcm_poll_descriptors returned wrong number of fds"))
98     }
99     Ok(fds[0].fd)
100 }
101 
102 impl Status {
new(p: &pcm::PCM) -> Result<Self>103     pub fn new(p: &pcm::PCM) -> Result<Self> { Status::from_fd(pcm_to_fd(p)?) }
104 
from_fd(fd: RawFd) -> Result<Self>105     pub fn from_fd(fd: RawFd) -> Result<Self> {
106         DriverMemory::new(fd, 1, SNDRV_PCM_MMAP_OFFSET_STATUS as libc::off_t, false).map(|d| Status(d))
107     }
108 
109     /// Current PCM state.
state(&self) -> pcm::State110     pub fn state(&self) -> pcm::State {
111         unsafe {
112             let i = ptr::read_volatile(&(*self.0.ptr).state);
113             assert!((i >= (pcm::State::Open as snd_pcm_state_t)) && (i <= (pcm::State::Disconnected as snd_pcm_state_t)));
114             mem::transmute(i as u8)
115         }
116     }
117 
118     /// Number of frames hardware has read or written
119     ///
120     /// This number is updated every now and then by the kernel.
121     /// Calling most functions on the PCM will update it, so will usually a period interrupt.
122     /// No guarantees given.
123     ///
124     /// This value wraps at "boundary" (a large value you can read from SwParams).
hw_ptr(&self) -> pcm::Frames125     pub fn hw_ptr(&self) -> pcm::Frames {
126         unsafe {
127             ptr::read_volatile(&(*self.0.ptr).hw_ptr) as pcm::Frames
128         }
129     }
130 
131     /// Timestamp - fast version of alsa-lib's Status::get_htstamp
132     ///
133     /// Note: This just reads the actual value in memory.
134     /// Unfortunately, the timespec is too big to be read atomically on most archs.
135     /// Therefore, this function can potentially give bogus result at times, at least in theory...?
htstamp(&self) -> libc::timespec136     pub fn htstamp(&self) -> libc::timespec {
137         unsafe {
138             ptr::read_volatile(&(*self.0.ptr).tstamp)
139         }
140     }
141 
142     /// Audio timestamp - fast version of alsa-lib's Status::get_audio_htstamp
143     ///
144     /// Note: This just reads the actual value in memory.
145     /// Unfortunately, the timespec is too big to be read atomically on most archs.
146     /// Therefore, this function can potentially give bogus result at times, at least in theory...?
audio_htstamp(&self) -> libc::timespec147     pub fn audio_htstamp(&self) -> libc::timespec {
148         unsafe {
149             ptr::read_volatile(&(*self.0.ptr).audio_tstamp)
150         }
151     }
152 }
153 
154 /// Write PCM appl ptr directly, bypassing alsa-lib.
155 ///
156 /// Provides direct access to appl ptr and avail min, without the overhead of
157 /// alsa-lib or a syscall. Caveats that apply to Status applies to this struct too.
158 #[derive(Debug)]
159 pub struct Control(DriverMemory<snd_pcm_mmap_control>);
160 
161 impl Control {
new(p: &pcm::PCM) -> Result<Self>162     pub fn new(p: &pcm::PCM) -> Result<Self> { Self::from_fd(pcm_to_fd(p)?) }
163 
from_fd(fd: RawFd) -> Result<Self>164     pub fn from_fd(fd: RawFd) -> Result<Self> {
165         DriverMemory::new(fd, 1, SNDRV_PCM_MMAP_OFFSET_CONTROL as libc::off_t, true).map(|d| Control(d))
166     }
167 
168     /// Read number of frames application has read or written
169     ///
170     /// This value wraps at "boundary" (a large value you can read from SwParams).
appl_ptr(&self) -> pcm::Frames171     pub fn appl_ptr(&self) -> pcm::Frames {
172         unsafe {
173             ptr::read_volatile(&(*self.0.ptr).appl_ptr) as pcm::Frames
174         }
175     }
176 
177     /// Set number of frames application has read or written
178     ///
179     /// When the kernel wakes up due to a period interrupt, this value will
180     /// be checked by the kernel. An XRUN will happen in case the application
181     /// has not read or written enough data.
set_appl_ptr(&self, value: pcm::Frames)182     pub fn set_appl_ptr(&self, value: pcm::Frames) {
183         unsafe {
184             ptr::write_volatile(&mut (*self.0.ptr).appl_ptr, value as snd_pcm_uframes_t)
185         }
186     }
187 
188     /// Read minimum number of frames in buffer in order to wakeup process
avail_min(&self) -> pcm::Frames189     pub fn avail_min(&self) -> pcm::Frames {
190         unsafe {
191             ptr::read_volatile(&(*self.0.ptr).avail_min) as pcm::Frames
192         }
193     }
194 
195     /// Write minimum number of frames in buffer in order to wakeup process
set_avail_min(&self, value: pcm::Frames)196     pub fn set_avail_min(&self, value: pcm::Frames) {
197         unsafe {
198             ptr::write_volatile(&mut (*self.0.ptr).avail_min, value as snd_pcm_uframes_t)
199         }
200     }
201 }
202 
203 struct DriverMemory<S> {
204    ptr: *mut S,
205    size: libc::size_t,
206 }
207 
208 impl<S> fmt::Debug for DriverMemory<S> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result209    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "DriverMemory({:?})", self.ptr) }
210 }
211 
212 impl<S> DriverMemory<S> {
new(fd: RawFd, count: usize, offs: libc::off_t, writable: bool) -> Result<Self>213     fn new(fd: RawFd, count: usize, offs: libc::off_t, writable: bool) -> Result<Self> {
214         let mut total = count * mem::size_of::<S>();
215         let ps = pagesize();
216         assert!(total > 0);
217         if total % ps != 0 { total += ps - total % ps };
218         let flags = if writable { libc::PROT_WRITE | libc::PROT_READ } else { libc::PROT_READ };
219         let p = unsafe { libc::mmap(ptr::null_mut(), total, flags, libc::MAP_FILE | libc::MAP_SHARED, fd, offs) };
220         if p == ptr::null_mut() || p == libc::MAP_FAILED {
221             Err(Error::new("mmap (of driver memory)", nix::errno::Errno::last() as i32))
222         } else {
223             Ok(DriverMemory { ptr: p as *mut S, size: total })
224         }
225     }
226 }
227 
228 unsafe impl<S> Send for DriverMemory<S> {}
229 unsafe impl<S> Sync for DriverMemory<S> {}
230 
231 impl<S> Drop for DriverMemory<S> {
drop(&mut self)232     fn drop(&mut self) {
233         unsafe {{ libc::munmap(self.ptr as *mut libc::c_void, self.size); } }
234     }
235 }
236 
237 #[derive(Debug)]
238 struct SampleData<S> {
239     mem: DriverMemory<S>,
240     frames: pcm::Frames,
241     channels: u32,
242 }
243 
244 impl<S> SampleData<S> {
new(p: &pcm::PCM) -> Result<Self>245     pub fn new(p: &pcm::PCM) -> Result<Self> {
246         let params = p.hw_params_current()?;
247         let bufsize = params.get_buffer_size()?;
248         let channels = params.get_channels()?;
249         if params.get_access()? != pcm::Access::MMapInterleaved {
250             return Err(Error::unsupported("Not MMAP interleaved data"))
251         }
252 
253         let fd = pcm_to_fd(p)?;
254         let info = unsafe {
255             let mut info: snd_pcm_channel_info = mem::zeroed();
256             sndrv_pcm_ioctl_channel_info(fd, &mut info).map_err(|_|
257                 Error::new("SNDRV_PCM_IOCTL_CHANNEL_INFO", nix::errno::Errno::last() as i32))?;
258             info
259         };
260         // println!("{:?}", info);
261         if (info.step != channels * mem::size_of::<S>() as u32 * 8) || (info.first != 0) {
262             return Err(Error::unsupported("MMAP data size mismatch"))
263         }
264         Ok(SampleData {
265             mem: DriverMemory::new(fd, (bufsize as usize) * (channels as usize), info.offset as libc::off_t, true)?,
266             frames: bufsize,
267             channels: channels,
268         })
269     }
270 }
271 
272 
273 /// Dummy trait for better generics
274 pub trait MmapDir: fmt::Debug {
275     const DIR: Direction;
avail(hwptr: Frames, applptr: Frames, buffersize: Frames, boundary: Frames) -> Frames276     fn avail(hwptr: Frames, applptr: Frames, buffersize: Frames, boundary: Frames) -> Frames;
277 }
278 
279 /// Dummy struct for better generics
280 #[derive(Copy, Clone, Debug)]
281 pub struct Playback;
282 
283 impl MmapDir for Playback {
284     const DIR: Direction = Direction::Playback;
285     #[inline]
avail(hwptr: Frames, applptr: Frames, buffersize: Frames, boundary: Frames) -> Frames286     fn avail(hwptr: Frames, applptr: Frames, buffersize: Frames, boundary: Frames) -> Frames {
287 	let r = hwptr.wrapping_add(buffersize).wrapping_sub(applptr);
288 	let r = if r < 0 { r.wrapping_add(boundary) } else { r };
289         if r as usize >= boundary as usize { r.wrapping_sub(boundary) } else { r }
290     }
291 }
292 
293 /// Dummy struct for better generics
294 #[derive(Copy, Clone, Debug)]
295 pub struct Capture;
296 
297 impl MmapDir for Capture {
298     const DIR: Direction = Direction::Capture;
299     #[inline]
avail(hwptr: Frames, applptr: Frames, _buffersize: Frames, boundary: Frames) -> Frames300     fn avail(hwptr: Frames, applptr: Frames, _buffersize: Frames, boundary: Frames) -> Frames {
301 	let r = hwptr.wrapping_sub(applptr);
302 	if r < 0 { r.wrapping_add(boundary) } else { r }
303     }
304 }
305 
306 pub type MmapPlayback<S> = MmapIO<S, Playback>;
307 
308 pub type MmapCapture<S> = MmapIO<S, Capture>;
309 
310 #[derive(Debug)]
311 /// Struct containing direct I/O functions shared between playback and capture.
312 pub struct MmapIO<S, D> {
313     data: SampleData<S>,
314     c: Control,
315     ss: Status,
316     bound: Frames,
317     dir: PhantomData<*const D>,
318 }
319 
320 #[derive(Debug, Clone, Copy)]
321 /// A raw pointer to samples, and the amount of samples readable or writable.
322 pub struct RawSamples<S> {
323     pub ptr: *mut S,
324     pub frames: Frames,
325     pub channels: u32,
326 }
327 
328 impl<S> RawSamples<S> {
329     #[inline]
330     /// Returns `frames` * `channels`, i e the amount of samples (of type `S`) that can be read/written.
samples(&self) -> isize331     pub fn samples(&self) -> isize { self.frames as isize * (self.channels as isize) }
332 
333     /// Writes samples from an iterator.
334     ///
335     /// Returns true if iterator was depleted, and the number of samples written.
336     /// This is just raw read/write of memory.
write_samples<I: Iterator<Item=S>>(&self, i: &mut I) -> (bool, isize)337     pub unsafe fn write_samples<I: Iterator<Item=S>>(&self, i: &mut I) -> (bool, isize) {
338         let mut z = 0;
339         let max_samples = self.samples();
340         while z < max_samples {
341             let b = if let Some(b) = i.next() { b } else { return (true, z) };
342             ptr::write_volatile(self.ptr.offset(z), b);
343             z += 1;
344         };
345         (false, z)
346     }
347 
348 }
349 
350 impl<S, D: MmapDir> MmapIO<S, D> {
new(p: &pcm::PCM) -> Result<Self>351     fn new(p: &pcm::PCM) -> Result<Self> {
352         if p.info()?.get_stream() != D::DIR {
353             return Err(Error::unsupported("Wrong direction"));
354         }
355         let boundary = p.sw_params_current()?.get_boundary()?;
356         Ok(MmapIO {
357             data: SampleData::new(p)?,
358             c: Control::new(p)?,
359             ss: Status::new(p)?,
360             bound: boundary,
361             dir: PhantomData,
362         })
363     }
364 }
365 
new_mmap<S, D: MmapDir>(p: &pcm::PCM) -> Result<MmapIO<S, D>>366 pub (crate) fn new_mmap<S, D: MmapDir>(p: &pcm::PCM) -> Result<MmapIO<S, D>> { MmapIO::new(p) }
367 
368 impl<S, D: MmapDir> MmapIO<S, D> {
369     /// Read current status
status(&self) -> &Status370     pub fn status(&self) -> &Status { &self.ss }
371 
372     /// Read current number of frames committed by application
373     ///
374     /// This number wraps at 'boundary'.
375     #[inline]
appl_ptr(&self) -> Frames376     pub fn appl_ptr(&self) -> Frames { self.c.appl_ptr() }
377 
378     /// Read current number of frames read / written by hardware
379     ///
380     /// This number wraps at 'boundary'.
381     #[inline]
hw_ptr(&self) -> Frames382     pub fn hw_ptr(&self) -> Frames { self.ss.hw_ptr() }
383 
384     /// The number at which hw_ptr and appl_ptr wraps.
385     #[inline]
boundary(&self) -> Frames386     pub fn boundary(&self) -> Frames { self.bound }
387 
388     /// Total number of frames in hardware buffer
389     #[inline]
buffer_size(&self) -> Frames390     pub fn buffer_size(&self) -> Frames { self.data.frames }
391 
392     /// Number of channels in stream
393     #[inline]
channels(&self) -> u32394     pub fn channels(&self) -> u32 { self.data.channels }
395 
396     /// Notifies the kernel that frames have now been read / written by the application
397     ///
398     /// This will allow the kernel to write new data into this part of the buffer.
commit(&self, v: Frames)399     pub fn commit(&self, v: Frames) {
400         let mut z = self.appl_ptr() + v;
401         if z + v >= self.boundary() { z -= self.boundary() };
402         self.c.set_appl_ptr(z)
403     }
404 
405     /// Number of frames available to read / write.
406     ///
407     /// In case of an underrun, this value might be bigger than the buffer size.
avail(&self) -> Frames408     pub fn avail(&self) -> Frames { D::avail(self.hw_ptr(), self.appl_ptr(), self.buffer_size(), self.boundary()) }
409 
410     /// Returns raw pointers to data to read / write.
411     ///
412     /// Use this if you want to read/write data yourself (instead of using iterators). If you do,
413     /// using `write_volatile` or `read_volatile` is recommended, since it's DMA memory and can
414     /// change at any time.
415     ///
416     /// Since this is a ring buffer, there might be more data to read/write in the beginning
417     /// of the buffer as well. If so this is returned as the second return value.
data_ptr(&self) -> (RawSamples<S>, Option<RawSamples<S>>)418     pub fn data_ptr(&self) -> (RawSamples<S>, Option<RawSamples<S>>) {
419         let (hwptr, applptr) = (self.hw_ptr(), self.appl_ptr());
420         let c = self.channels();
421         let bufsize = self.buffer_size();
422 
423         // These formulas mostly mimic the behaviour of
424         // snd_pcm_mmap_begin (in alsa-lib/src/pcm/pcm.c).
425         let offs = applptr % bufsize;
426         let mut a = D::avail(hwptr, applptr, bufsize, self.boundary());
427         a = cmp::min(a, bufsize);
428         let b = bufsize - offs;
429         let more_data = if b < a {
430             let z = a - b;
431             a = b;
432             Some( RawSamples { ptr: self.data.mem.ptr, frames: z, channels: c })
433         } else { None };
434 
435         let p = unsafe { self.data.mem.ptr.offset(offs as isize * self.data.channels as isize) };
436         (RawSamples { ptr: p, frames: a, channels: c }, more_data)
437     }
438 }
439 
440 impl<S> MmapPlayback<S> {
441     /// Write samples to the kernel ringbuffer.
write<I: Iterator<Item=S>>(&mut self, i: &mut I) -> Frames442     pub fn write<I: Iterator<Item=S>>(&mut self, i: &mut I) -> Frames {
443         let (data, more_data) = self.data_ptr();
444         let (iter_end, samples) = unsafe { data.write_samples(i) };
445         let mut z = samples / data.channels as isize;
446         if !iter_end {
447             if let Some(data2) = more_data {
448                 let (_, samples2) = unsafe {  data2.write_samples(i) };
449                 z += samples2 / data2.channels as isize;
450             }
451         }
452         let z = z as Frames;
453         self.commit(z);
454         z
455     }
456 }
457 
458 impl<S> MmapCapture<S> {
459     /// Read samples from the kernel ringbuffer.
460     ///
461     /// When the iterator is dropped or depleted, the read samples will be committed, i e,
462     /// the kernel can then write data to the location again. So do this ASAP.
iter<'a>(&'a mut self) -> CaptureIter<'a, S>463     pub fn iter<'a>(&'a mut self) -> CaptureIter<'a, S> {
464         let (data, more_data) = self.data_ptr();
465         CaptureIter {
466             m: self,
467             samples: data,
468             p_offs: 0,
469             read_samples: 0,
470             next_p: more_data,
471         }
472     }
473 }
474 
475 /// Iterator over captured samples
476 pub struct CaptureIter<'a, S: 'static> {
477     m: &'a MmapCapture<S>,
478     samples: RawSamples<S>,
479     p_offs: isize,
480     read_samples: isize,
481     next_p: Option<RawSamples<S>>,
482 }
483 
484 impl<'a, S: 'static + Copy> CaptureIter<'a, S> {
handle_max(&mut self)485     fn handle_max(&mut self) {
486         self.p_offs = 0;
487         if let Some(p2) = self.next_p.take() {
488             self.samples = p2;
489         } else {
490             self.m.commit((self.read_samples / self.samples.channels as isize) as Frames);
491             self.read_samples = 0;
492             self.samples.frames = 0; // Shortcut to "None" in case anyone calls us again
493         }
494     }
495 }
496 
497 impl<'a, S: 'static + Copy> Iterator for CaptureIter<'a, S> {
498     type Item = S;
499 
500     #[inline]
next(&mut self) -> Option<Self::Item>501     fn next(&mut self) -> Option<Self::Item> {
502         if self.p_offs >= self.samples.samples() {
503             self.handle_max();
504             if self.samples.frames <= 0 { return None; }
505         }
506         let s = unsafe { ptr::read_volatile(self.samples.ptr.offset(self.p_offs)) };
507         self.p_offs += 1;
508         self.read_samples += 1;
509         Some(s)
510     }
511 }
512 
513 impl<'a, S: 'static> Drop for CaptureIter<'a, S> {
drop(&mut self)514     fn drop(&mut self) {
515         self.m.commit((self.read_samples / self.m.data.channels as isize) as Frames);
516     }
517 }
518 
519 
520 #[test]
521 #[ignore] // Not everyone has a recording device on plughw:1. So let's ignore this test by default.
record_from_plughw_rw()522 fn record_from_plughw_rw() {
523     use crate::pcm::*;
524     use crate::{ValueOr, Direction};
525     use std::ffi::CString;
526     let pcm = PCM::open(&*CString::new("plughw:1").unwrap(), Direction::Capture, false).unwrap();
527     let ss = self::Status::new(&pcm).unwrap();
528     let c = self::Control::new(&pcm).unwrap();
529     let hwp = HwParams::any(&pcm).unwrap();
530     hwp.set_channels(2).unwrap();
531     hwp.set_rate(44100, ValueOr::Nearest).unwrap();
532     hwp.set_format(Format::s16()).unwrap();
533     hwp.set_access(Access::RWInterleaved).unwrap();
534     pcm.hw_params(&hwp).unwrap();
535 
536     {
537         let swp = pcm.sw_params_current().unwrap();
538         swp.set_tstamp_mode(true).unwrap();
539         pcm.sw_params(&swp).unwrap();
540     }
541     assert_eq!(ss.state(), State::Prepared);
542     pcm.start().unwrap();
543     assert_eq!(c.appl_ptr(), 0);
544     println!("{:?}, {:?}", ss, c);
545     let mut buf = [0i16; 512*2];
546     assert_eq!(pcm.io_i16().unwrap().readi(&mut buf).unwrap(), 512);
547     assert_eq!(c.appl_ptr(), 512);
548 
549     assert_eq!(ss.state(), State::Running);
550     assert!(ss.hw_ptr() >= 512);
551     let t2 = ss.htstamp();
552     assert!(t2.tv_sec > 0 || t2.tv_nsec > 0);
553 }
554 
555 
556 #[test]
557 #[ignore] // Not everyone has a record device on plughw:1. So let's ignore this test by default.
record_from_plughw_mmap()558 fn record_from_plughw_mmap() {
559     use crate::pcm::*;
560     use crate::{ValueOr, Direction};
561     use std::ffi::CString;
562     use std::{thread, time};
563 
564     let pcm = PCM::open(&*CString::new("plughw:1").unwrap(), Direction::Capture, false).unwrap();
565     let hwp = HwParams::any(&pcm).unwrap();
566     hwp.set_channels(2).unwrap();
567     hwp.set_rate(44100, ValueOr::Nearest).unwrap();
568     hwp.set_format(Format::s16()).unwrap();
569     hwp.set_access(Access::MMapInterleaved).unwrap();
570     pcm.hw_params(&hwp).unwrap();
571 
572     let ss = unsafe { SyncPtrStatus::sync_ptr(pcm_to_fd(&pcm).unwrap(), false, None, None).unwrap() };
573     assert_eq!(ss.state(), State::Prepared);
574 
575     let mut m = pcm.direct_mmap_capture::<i16>().unwrap();
576 
577     assert_eq!(m.status().state(), State::Prepared);
578     assert_eq!(m.appl_ptr(), 0);
579     assert_eq!(m.hw_ptr(), 0);
580 
581 
582     println!("{:?}", m);
583 
584     let now = time::Instant::now();
585     pcm.start().unwrap();
586     while m.avail() < 256 { thread::sleep(time::Duration::from_millis(1)) };
587     assert!(now.elapsed() >= time::Duration::from_millis(256 * 1000 / 44100));
588     let (ptr1, md) = m.data_ptr();
589     assert_eq!(ptr1.channels, 2);
590     assert!(ptr1.frames >= 256);
591     assert!(md.is_none());
592     println!("Has {:?} frames at {:?} in {:?}", m.avail(), ptr1.ptr, now.elapsed());
593     let samples: Vec<i16> = m.iter().collect();
594     assert!(samples.len() >= ptr1.frames as usize * 2);
595     println!("Collected {} samples", samples.len());
596     let (ptr2, _md) = m.data_ptr();
597     assert!(unsafe { ptr1.ptr.offset(256 * 2) } <= ptr2.ptr);
598 }
599 
600 #[test]
601 #[ignore]
playback_to_plughw_mmap()602 fn playback_to_plughw_mmap() {
603     use crate::pcm::*;
604     use crate::{ValueOr, Direction};
605     use std::ffi::CString;
606 
607     let pcm = PCM::open(&*CString::new("plughw:1").unwrap(), Direction::Playback, false).unwrap();
608     let hwp = HwParams::any(&pcm).unwrap();
609     hwp.set_channels(2).unwrap();
610     hwp.set_rate(44100, ValueOr::Nearest).unwrap();
611     hwp.set_format(Format::s16()).unwrap();
612     hwp.set_access(Access::MMapInterleaved).unwrap();
613     pcm.hw_params(&hwp).unwrap();
614     let mut m = pcm.direct_mmap_playback::<i16>().unwrap();
615 
616     assert_eq!(m.status().state(), State::Prepared);
617     assert_eq!(m.appl_ptr(), 0);
618     assert_eq!(m.hw_ptr(), 0);
619 
620     println!("{:?}", m);
621     let mut i = (0..(m.buffer_size() * 2)).map(|i|
622         (((i / 2) as f32 * 2.0 * ::std::f32::consts::PI / 128.0).sin() * 8192.0) as i16);
623     m.write(&mut i);
624     assert_eq!(m.appl_ptr(), m.buffer_size());
625 
626     pcm.start().unwrap();
627     pcm.drain().unwrap();
628     assert_eq!(m.appl_ptr(), m.buffer_size());
629     assert!(m.hw_ptr() >= m.buffer_size());
630 }
631