1use libc;
23use core::{mem, ptr, fmt, cmp};
24use crate::error::{Error, Result};
25use crate::{pcm, PollDescriptors, Direction};
26use crate::pcm::Frames;
27use core::marker::PhantomData;
28
29use super::ffi::*;
30
31#[cfg(feature = "std")]
32type RawFd = std::os::unix::io::RawFd;
33
34#[cfg(not(feature = "std"))]
35type RawFd = core::ffi::c_int;
36
37#[derive(Debug)]
41pub struct SyncPtrStatus(snd_pcm_mmap_status);
42
43impl SyncPtrStatus {
44 pub unsafe fn sync_ptr(fd: RawFd, hwsync: bool, appl_ptr: Option<pcm::Frames>, avail_min: Option<pcm::Frames>) -> Result<Self> {
50 let mut data = snd_pcm_sync_ptr {
51 flags: (if hwsync { SNDRV_PCM_SYNC_PTR_HWSYNC } else { 0 }) +
52 (if appl_ptr.is_some() { SNDRV_PCM_SYNC_PTR_APPL } else { 0 }) +
53 (if avail_min.is_some() { SNDRV_PCM_SYNC_PTR_AVAIL_MIN } else { 0 }),
54 c: snd_pcm_mmap_control_r {
55 control: snd_pcm_mmap_control {
56 appl_ptr: appl_ptr.unwrap_or(0) as snd_pcm_uframes_t,
57 avail_min: avail_min.unwrap_or(0) as snd_pcm_uframes_t,
58 }
59 },
60 s: mem::zeroed()
61 };
62
63 sndrv_pcm_ioctl_sync_ptr(fd, &mut data)?;
64
65 let i = data.s.status.state;
66 if (i >= (pcm::State::Open as snd_pcm_state_t)) && (i <= (pcm::State::Disconnected as snd_pcm_state_t)) {
67 Ok(SyncPtrStatus(data.s.status))
68 } else {
69 Err(Error::unsupported("SNDRV_PCM_IOCTL_SYNC_PTR returned broken state"))
70 }
71 }
72
73 pub fn hw_ptr(&self) -> pcm::Frames { self.0.hw_ptr as pcm::Frames }
74 pub fn state(&self) -> pcm::State { unsafe { mem::transmute(self.0.state as u8) } }
75 pub fn htstamp(&self) -> libc::timespec { self.0.tstamp }
76}
77
78
79
80#[derive(Debug)]
96pub struct Status(DriverMemory<snd_pcm_mmap_status>);
97
98fn pcm_to_fd(p: &pcm::PCM) -> Result<RawFd> {
99 let mut fds: [libc::pollfd; 1] = unsafe { mem::zeroed() };
100 let c = PollDescriptors::fill(p, &mut fds)?;
101 if c != 1 {
102 return Err(Error::unsupported("snd_pcm_poll_descriptors returned wrong number of fds"))
103 }
104 Ok(fds[0].fd)
105}
106
107impl Status {
108 pub fn new(p: &pcm::PCM) -> Result<Self> { Status::from_fd(pcm_to_fd(p)?) }
109
110 pub fn from_fd(fd: RawFd) -> Result<Self> {
111 DriverMemory::new(fd, 1, SNDRV_PCM_MMAP_OFFSET_STATUS as libc::off_t, false).map(Status)
112 }
113
114 pub fn state(&self) -> pcm::State {
116 unsafe {
117 let i = ptr::read_volatile(&(*self.0.ptr).state);
118 assert!((i >= (pcm::State::Open as snd_pcm_state_t)) && (i <= (pcm::State::Disconnected as snd_pcm_state_t)));
119 mem::transmute(i as u8)
120 }
121 }
122
123 pub fn hw_ptr(&self) -> pcm::Frames {
131 unsafe {
132 ptr::read_volatile(&(*self.0.ptr).hw_ptr) as pcm::Frames
133 }
134 }
135
136 pub fn htstamp(&self) -> libc::timespec {
142 unsafe {
143 ptr::read_volatile(&(*self.0.ptr).tstamp)
144 }
145 }
146
147 pub fn audio_htstamp(&self) -> libc::timespec {
153 unsafe {
154 ptr::read_volatile(&(*self.0.ptr).audio_tstamp)
155 }
156 }
157}
158
159#[derive(Debug)]
164pub struct Control(DriverMemory<snd_pcm_mmap_control>);
165
166impl Control {
167 pub fn new(p: &pcm::PCM) -> Result<Self> { Self::from_fd(pcm_to_fd(p)?) }
168
169 pub fn from_fd(fd: RawFd) -> Result<Self> {
170 DriverMemory::new(fd, 1, SNDRV_PCM_MMAP_OFFSET_CONTROL as libc::off_t, true).map(Control)
171 }
172
173 pub fn appl_ptr(&self) -> pcm::Frames {
177 unsafe {
178 ptr::read_volatile(&(*self.0.ptr).appl_ptr) as pcm::Frames
179 }
180 }
181
182 pub fn set_appl_ptr(&self, value: pcm::Frames) {
188 unsafe {
189 ptr::write_volatile(&mut (*self.0.ptr).appl_ptr, value as snd_pcm_uframes_t)
190 }
191 }
192
193 pub fn avail_min(&self) -> pcm::Frames {
195 unsafe {
196 ptr::read_volatile(&(*self.0.ptr).avail_min) as pcm::Frames
197 }
198 }
199
200 pub fn set_avail_min(&self, value: pcm::Frames) {
202 unsafe {
203 ptr::write_volatile(&mut (*self.0.ptr).avail_min, value as snd_pcm_uframes_t)
204 }
205 }
206}
207
208struct DriverMemory<S> {
209 ptr: *mut S,
210 size: libc::size_t,
211}
212
213impl<S> fmt::Debug for DriverMemory<S> {
214 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "DriverMemory({:?})", self.ptr) }
215}
216
217impl<S> DriverMemory<S> {
218 fn new(fd: RawFd, count: usize, offs: libc::off_t, writable: bool) -> Result<Self> {
219 let mut total = count * mem::size_of::<S>();
220 let ps = pagesize();
221 assert!(total > 0);
222 if total % ps != 0 { total += ps - total % ps };
223 let flags = if writable { libc::PROT_WRITE | libc::PROT_READ } else { libc::PROT_READ };
224 let p = unsafe { libc::mmap(ptr::null_mut(), total, flags, libc::MAP_FILE | libc::MAP_SHARED, fd, offs) };
225 if p.is_null() || p == libc::MAP_FAILED {
226 Err(Error::last("mmap (of driver memory)"))
227 } else {
228 Ok(DriverMemory { ptr: p as *mut S, size: total })
229 }
230 }
231}
232
233unsafe impl<S> Send for DriverMemory<S> {}
234unsafe impl<S> Sync for DriverMemory<S> {}
235
236impl<S> Drop for DriverMemory<S> {
237 fn drop(&mut self) {
238 unsafe {{ libc::munmap(self.ptr as *mut libc::c_void, self.size); } }
239 }
240}
241
242#[derive(Debug)]
243struct SampleData<S> {
244 mem: DriverMemory<S>,
245 frames: pcm::Frames,
246 channels: u32,
247}
248
249impl<S> SampleData<S> {
250 pub fn new(p: &pcm::PCM) -> Result<Self> {
251 let params = p.hw_params_current()?;
252 let bufsize = params.get_buffer_size()?;
253 let channels = params.get_channels()?;
254 if params.get_access()? != pcm::Access::MMapInterleaved {
255 return Err(Error::unsupported("Not MMAP interleaved data"))
256 }
257
258 let fd = pcm_to_fd(p)?;
259 let info = unsafe {
260 let mut info: snd_pcm_channel_info = mem::zeroed();
261 sndrv_pcm_ioctl_channel_info(fd, &mut info)?;
262 info
263 };
264 if (info.step != channels * mem::size_of::<S>() as u32 * 8) || (info.first != 0) {
266 return Err(Error::unsupported("MMAP data size mismatch"))
267 }
268 Ok(SampleData {
269 mem: DriverMemory::new(fd, (bufsize as usize) * (channels as usize), info.offset as libc::off_t, true)?,
270 frames: bufsize,
271 channels,
272 })
273 }
274}
275
276
277pub trait MmapDir: fmt::Debug {
279 const DIR: Direction;
280 fn avail(hwptr: Frames, applptr: Frames, buffersize: Frames, boundary: Frames) -> Frames;
281}
282
283#[derive(Copy, Clone, Debug)]
285pub struct Playback;
286
287impl MmapDir for Playback {
288 const DIR: Direction = Direction::Playback;
289 #[inline]
290 fn avail(hwptr: Frames, applptr: Frames, buffersize: Frames, boundary: Frames) -> Frames {
291 let r = hwptr.wrapping_add(buffersize).wrapping_sub(applptr);
292 let r = if r < 0 { r.wrapping_add(boundary) } else { r };
293 if r as usize >= boundary as usize { r.wrapping_sub(boundary) } else { r }
294 }
295}
296
297#[derive(Copy, Clone, Debug)]
299pub struct Capture;
300
301impl MmapDir for Capture {
302 const DIR: Direction = Direction::Capture;
303 #[inline]
304 fn avail(hwptr: Frames, applptr: Frames, _buffersize: Frames, boundary: Frames) -> Frames {
305 let r = hwptr.wrapping_sub(applptr);
306 if r < 0 { r.wrapping_add(boundary) } else { r }
307 }
308}
309
310pub type MmapPlayback<S> = MmapIO<S, Playback>;
311
312pub type MmapCapture<S> = MmapIO<S, Capture>;
313
314#[derive(Debug)]
315pub struct MmapIO<S, D> {
317 data: SampleData<S>,
318 c: Control,
319 ss: Status,
320 bound: Frames,
321 dir: PhantomData<*const D>,
322}
323
324#[derive(Debug, Clone, Copy)]
325pub struct RawSamples<S> {
327 pub ptr: *mut S,
328 pub frames: Frames,
329 pub channels: u32,
330}
331
332impl<S> RawSamples<S> {
333 #[inline]
334 pub fn samples(&self) -> isize { self.frames as isize * (self.channels as isize) }
336
337 pub unsafe fn write_samples<I: Iterator<Item=S>>(&self, i: &mut I) -> (bool, isize) {
342 let mut z = 0;
343 let max_samples = self.samples();
344 while z < max_samples {
345 let b = if let Some(b) = i.next() { b } else { return (true, z) };
346 ptr::write_volatile(self.ptr.offset(z), b);
347 z += 1;
348 };
349 (false, z)
350 }
351
352}
353
354impl<S, D: MmapDir> MmapIO<S, D> {
355 fn new(p: &pcm::PCM) -> Result<Self> {
356 if p.info()?.get_stream() != D::DIR {
357 return Err(Error::unsupported("Wrong direction"));
358 }
359 let boundary = p.sw_params_current()?.get_boundary()?;
360 Ok(MmapIO {
361 data: SampleData::new(p)?,
362 c: Control::new(p)?,
363 ss: Status::new(p)?,
364 bound: boundary,
365 dir: PhantomData,
366 })
367 }
368}
369
370pub (crate) fn new_mmap<S, D: MmapDir>(p: &pcm::PCM) -> Result<MmapIO<S, D>> { MmapIO::new(p) }
371
372impl<S, D: MmapDir> MmapIO<S, D> {
373 pub fn status(&self) -> &Status { &self.ss }
375
376 #[inline]
380 pub fn appl_ptr(&self) -> Frames { self.c.appl_ptr() }
381
382 #[inline]
386 pub fn hw_ptr(&self) -> Frames { self.ss.hw_ptr() }
387
388 #[inline]
390 pub fn boundary(&self) -> Frames { self.bound }
391
392 #[inline]
394 pub fn buffer_size(&self) -> Frames { self.data.frames }
395
396 #[inline]
398 pub fn channels(&self) -> u32 { self.data.channels }
399
400 pub fn commit(&self, v: Frames) {
404 let mut z = self.appl_ptr() + v;
405 if z + v >= self.boundary() { z -= self.boundary() };
406 self.c.set_appl_ptr(z)
407 }
408
409 pub fn avail(&self) -> Frames { D::avail(self.hw_ptr(), self.appl_ptr(), self.buffer_size(), self.boundary()) }
413
414 pub fn data_ptr(&self) -> (RawSamples<S>, Option<RawSamples<S>>) {
423 let (hwptr, applptr) = (self.hw_ptr(), self.appl_ptr());
424 let c = self.channels();
425 let bufsize = self.buffer_size();
426
427 let offs = applptr % bufsize;
430 let mut a = D::avail(hwptr, applptr, bufsize, self.boundary());
431 a = cmp::min(a, bufsize);
432 let b = bufsize - offs;
433 let more_data = if b < a {
434 let z = a - b;
435 a = b;
436 Some( RawSamples { ptr: self.data.mem.ptr, frames: z, channels: c })
437 } else { None };
438
439 let p = unsafe { self.data.mem.ptr.offset(offs as isize * self.data.channels as isize) };
440 (RawSamples { ptr: p, frames: a, channels: c }, more_data)
441 }
442}
443
444impl<S> MmapPlayback<S> {
445 pub fn write<I: Iterator<Item=S>>(&mut self, i: &mut I) -> Frames {
447 let (data, more_data) = self.data_ptr();
448 let (iter_end, samples) = unsafe { data.write_samples(i) };
449 let mut z = samples / data.channels as isize;
450 if !iter_end {
451 if let Some(data2) = more_data {
452 let (_, samples2) = unsafe { data2.write_samples(i) };
453 z += samples2 / data2.channels as isize;
454 }
455 }
456 let z = z as Frames;
457 self.commit(z);
458 z
459 }
460}
461
462impl<S> MmapCapture<S> {
463 pub fn iter(&mut self) -> CaptureIter<'_, S> {
468 let (data, more_data) = self.data_ptr();
469 CaptureIter {
470 m: self,
471 samples: data,
472 p_offs: 0,
473 read_samples: 0,
474 next_p: more_data,
475 }
476 }
477}
478
479#[derive(Debug)]
481pub struct CaptureIter<'a, S: 'static> {
482 m: &'a MmapCapture<S>,
483 samples: RawSamples<S>,
484 p_offs: isize,
485 read_samples: isize,
486 next_p: Option<RawSamples<S>>,
487}
488
489impl<'a, S: 'static + Copy> CaptureIter<'a, S> {
490 fn handle_max(&mut self) {
491 self.p_offs = 0;
492 if let Some(p2) = self.next_p.take() {
493 self.samples = p2;
494 } else {
495 self.m.commit((self.read_samples / self.samples.channels as isize) as Frames);
496 self.read_samples = 0;
497 self.samples.frames = 0; }
499 }
500}
501
502impl<'a, S: 'static + Copy> Iterator for CaptureIter<'a, S> {
503 type Item = S;
504
505 #[inline]
506 fn next(&mut self) -> Option<Self::Item> {
507 if self.p_offs >= self.samples.samples() {
508 self.handle_max();
509 if self.samples.frames <= 0 { return None; }
510 }
511 let s = unsafe { ptr::read_volatile(self.samples.ptr.offset(self.p_offs)) };
512 self.p_offs += 1;
513 self.read_samples += 1;
514 Some(s)
515 }
516}
517
518impl<'a, S: 'static> Drop for CaptureIter<'a, S> {
519 fn drop(&mut self) {
520 self.m.commit((self.read_samples / self.m.data.channels as isize) as Frames);
521 }
522}
523
524
525#[test]
526#[ignore] fn record_from_plughw_rw() {
528 extern crate std;
529 use crate::pcm::*;
530 use crate::{ValueOr, Direction};
531 use ::alloc::ffi::CString;
532 let pcm = PCM::open(&*CString::new("plughw:1").unwrap(), Direction::Capture, false).unwrap();
533 let ss = self::Status::new(&pcm).unwrap();
534 let c = self::Control::new(&pcm).unwrap();
535 let hwp = HwParams::any(&pcm).unwrap();
536 hwp.set_channels(2).unwrap();
537 hwp.set_rate(44100, ValueOr::Nearest).unwrap();
538 hwp.set_format(Format::s16()).unwrap();
539 hwp.set_access(Access::RWInterleaved).unwrap();
540 pcm.hw_params(&hwp).unwrap();
541
542 {
543 let swp = pcm.sw_params_current().unwrap();
544 swp.set_tstamp_mode(true).unwrap();
545 pcm.sw_params(&swp).unwrap();
546 }
547 assert_eq!(ss.state(), State::Prepared);
548 pcm.start().unwrap();
549 assert_eq!(c.appl_ptr(), 0);
550 std::println!("{:?}, {:?}", ss, c);
551 let mut buf = [0i16; 512*2];
552 assert_eq!(pcm.io_i16().unwrap().readi(&mut buf).unwrap(), 512);
553 assert_eq!(c.appl_ptr(), 512);
554
555 assert_eq!(ss.state(), State::Running);
556 assert!(ss.hw_ptr() >= 512);
557 let t2 = ss.htstamp();
558 assert!(t2.tv_sec > 0 || t2.tv_nsec > 0);
559}
560
561
562#[test]
563#[ignore] fn record_from_plughw_mmap() {
565 extern crate std;
566 use crate::pcm::*;
567 use crate::{ValueOr, Direction};
568 use ::alloc::ffi::CString;
569 use ::alloc::vec::Vec;
570 use std::{thread, time, println};
571
572 let pcm = PCM::open(&*CString::new("plughw:1").unwrap(), Direction::Capture, false).unwrap();
573 let hwp = HwParams::any(&pcm).unwrap();
574 hwp.set_channels(2).unwrap();
575 hwp.set_rate(44100, ValueOr::Nearest).unwrap();
576 hwp.set_format(Format::s16()).unwrap();
577 hwp.set_access(Access::MMapInterleaved).unwrap();
578 pcm.hw_params(&hwp).unwrap();
579
580 let ss = unsafe { SyncPtrStatus::sync_ptr(pcm_to_fd(&pcm).unwrap(), false, None, None).unwrap() };
581 assert_eq!(ss.state(), State::Prepared);
582
583 let mut m = pcm.direct_mmap_capture::<i16>().unwrap();
584
585 assert_eq!(m.status().state(), State::Prepared);
586 assert_eq!(m.appl_ptr(), 0);
587 assert_eq!(m.hw_ptr(), 0);
588
589
590 println!("{:?}", m);
591
592 let now = time::Instant::now();
593 pcm.start().unwrap();
594 while m.avail() < 256 { thread::sleep(time::Duration::from_millis(1)) };
595 assert!(now.elapsed() >= time::Duration::from_millis(256 * 1000 / 44100));
596 let (ptr1, md) = m.data_ptr();
597 assert_eq!(ptr1.channels, 2);
598 assert!(ptr1.frames >= 256);
599 assert!(md.is_none());
600 println!("Has {:?} frames at {:?} in {:?}", m.avail(), ptr1.ptr, now.elapsed());
601 let samples: Vec<i16> = m.iter().collect();
602 assert!(samples.len() >= ptr1.frames as usize * 2);
603 println!("Collected {} samples", samples.len());
604 let (ptr2, _md) = m.data_ptr();
605 assert!(unsafe { ptr1.ptr.offset(256 * 2) } <= ptr2.ptr);
606}
607
608#[test]
609#[ignore]
610fn playback_to_plughw_mmap() {
611 extern crate std;
612 use crate::pcm::*;
613 use crate::{ValueOr, Direction};
614 use ::alloc::ffi::CString;
615
616 let pcm = PCM::open(&*CString::new("plughw:1").unwrap(), Direction::Playback, false).unwrap();
617 let hwp = HwParams::any(&pcm).unwrap();
618 hwp.set_channels(2).unwrap();
619 hwp.set_rate(44100, ValueOr::Nearest).unwrap();
620 hwp.set_format(Format::s16()).unwrap();
621 hwp.set_access(Access::MMapInterleaved).unwrap();
622 pcm.hw_params(&hwp).unwrap();
623 let mut m = pcm.direct_mmap_playback::<i16>().unwrap();
624
625 assert_eq!(m.status().state(), State::Prepared);
626 assert_eq!(m.appl_ptr(), 0);
627 assert_eq!(m.hw_ptr(), 0);
628
629 std::println!("{:?}", m);
630 let mut i = (0..(m.buffer_size() * 2)).map(|i|
631 (((i / 2) as f32 * 2.0 * std::f32::consts::PI / 128.0).sin() * 8192.0) as i16);
632 m.write(&mut i);
633 assert_eq!(m.appl_ptr(), m.buffer_size());
634
635 pcm.start().unwrap();
636 pcm.drain().unwrap();
637 assert_eq!(m.appl_ptr(), m.buffer_size());
638 assert!(m.hw_ptr() >= m.buffer_size());
639}