1use libc;
23use std::{mem, ptr, fmt, cmp};
24use crate::error::{Error, Result};
25use std::os::unix::io::RawFd;
26use crate::{pcm, PollDescriptors, Direction};
27use crate::pcm::Frames;
28use std::marker::PhantomData;
29
30use super::ffi::*;
31
32pub struct SyncPtrStatus(snd_pcm_mmap_status);
36
37impl SyncPtrStatus {
38 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)?;
58
59 let i = data.s.status.state;
60 if (i >= (pcm::State::Open as snd_pcm_state_t)) && (i <= (pcm::State::Disconnected as snd_pcm_state_t)) {
61 Ok(SyncPtrStatus(data.s.status))
62 } else {
63 Err(Error::unsupported("SNDRV_PCM_IOCTL_SYNC_PTR returned broken state"))
64 }
65 }
66
67 pub fn hw_ptr(&self) -> pcm::Frames { self.0.hw_ptr as pcm::Frames }
68 pub fn state(&self) -> pcm::State { unsafe { mem::transmute(self.0.state as u8) } }
69 pub fn htstamp(&self) -> libc::timespec { self.0.tstamp }
70}
71
72
73
74#[derive(Debug)]
90pub struct Status(DriverMemory<snd_pcm_mmap_status>);
91
92fn pcm_to_fd(p: &pcm::PCM) -> Result<RawFd> {
93 let mut fds: [libc::pollfd; 1] = unsafe { mem::zeroed() };
94 let c = PollDescriptors::fill(p, &mut fds)?;
95 if c != 1 {
96 return Err(Error::unsupported("snd_pcm_poll_descriptors returned wrong number of fds"))
97 }
98 Ok(fds[0].fd)
99}
100
101impl Status {
102 pub fn new(p: &pcm::PCM) -> Result<Self> { Status::from_fd(pcm_to_fd(p)?) }
103
104 pub fn from_fd(fd: RawFd) -> Result<Self> {
105 DriverMemory::new(fd, 1, SNDRV_PCM_MMAP_OFFSET_STATUS as libc::off_t, false).map(Status)
106 }
107
108 pub fn state(&self) -> pcm::State {
110 unsafe {
111 let i = ptr::read_volatile(&(*self.0.ptr).state);
112 assert!((i >= (pcm::State::Open as snd_pcm_state_t)) && (i <= (pcm::State::Disconnected as snd_pcm_state_t)));
113 mem::transmute(i as u8)
114 }
115 }
116
117 pub fn hw_ptr(&self) -> pcm::Frames {
125 unsafe {
126 ptr::read_volatile(&(*self.0.ptr).hw_ptr) as pcm::Frames
127 }
128 }
129
130 pub fn htstamp(&self) -> libc::timespec {
136 unsafe {
137 ptr::read_volatile(&(*self.0.ptr).tstamp)
138 }
139 }
140
141 pub fn audio_htstamp(&self) -> libc::timespec {
147 unsafe {
148 ptr::read_volatile(&(*self.0.ptr).audio_tstamp)
149 }
150 }
151}
152
153#[derive(Debug)]
158pub struct Control(DriverMemory<snd_pcm_mmap_control>);
159
160impl Control {
161 pub fn new(p: &pcm::PCM) -> Result<Self> { Self::from_fd(pcm_to_fd(p)?) }
162
163 pub fn from_fd(fd: RawFd) -> Result<Self> {
164 DriverMemory::new(fd, 1, SNDRV_PCM_MMAP_OFFSET_CONTROL as libc::off_t, true).map(Control)
165 }
166
167 pub fn appl_ptr(&self) -> pcm::Frames {
171 unsafe {
172 ptr::read_volatile(&(*self.0.ptr).appl_ptr) as pcm::Frames
173 }
174 }
175
176 pub fn set_appl_ptr(&self, value: pcm::Frames) {
182 unsafe {
183 ptr::write_volatile(&mut (*self.0.ptr).appl_ptr, value as snd_pcm_uframes_t)
184 }
185 }
186
187 pub fn avail_min(&self) -> pcm::Frames {
189 unsafe {
190 ptr::read_volatile(&(*self.0.ptr).avail_min) as pcm::Frames
191 }
192 }
193
194 pub fn set_avail_min(&self, value: pcm::Frames) {
196 unsafe {
197 ptr::write_volatile(&mut (*self.0.ptr).avail_min, value as snd_pcm_uframes_t)
198 }
199 }
200}
201
202struct DriverMemory<S> {
203 ptr: *mut S,
204 size: libc::size_t,
205}
206
207impl<S> fmt::Debug for DriverMemory<S> {
208 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "DriverMemory({:?})", self.ptr) }
209}
210
211impl<S> DriverMemory<S> {
212 fn new(fd: RawFd, count: usize, offs: libc::off_t, writable: bool) -> Result<Self> {
213 let mut total = count * mem::size_of::<S>();
214 let ps = pagesize();
215 assert!(total > 0);
216 if total % ps != 0 { total += ps - total % ps };
217 let flags = if writable { libc::PROT_WRITE | libc::PROT_READ } else { libc::PROT_READ };
218 let p = unsafe { libc::mmap(ptr::null_mut(), total, flags, libc::MAP_FILE | libc::MAP_SHARED, fd, offs) };
219 if p.is_null() || p == libc::MAP_FAILED {
220 Err(Error::last("mmap (of driver memory)"))
221 } else {
222 Ok(DriverMemory { ptr: p as *mut S, size: total })
223 }
224 }
225}
226
227unsafe impl<S> Send for DriverMemory<S> {}
228unsafe impl<S> Sync for DriverMemory<S> {}
229
230impl<S> Drop for DriverMemory<S> {
231 fn drop(&mut self) {
232 unsafe {{ libc::munmap(self.ptr as *mut libc::c_void, self.size); } }
233 }
234}
235
236#[derive(Debug)]
237struct SampleData<S> {
238 mem: DriverMemory<S>,
239 frames: pcm::Frames,
240 channels: u32,
241}
242
243impl<S> SampleData<S> {
244 pub fn new(p: &pcm::PCM) -> Result<Self> {
245 let params = p.hw_params_current()?;
246 let bufsize = params.get_buffer_size()?;
247 let channels = params.get_channels()?;
248 if params.get_access()? != pcm::Access::MMapInterleaved {
249 return Err(Error::unsupported("Not MMAP interleaved data"))
250 }
251
252 let fd = pcm_to_fd(p)?;
253 let info = unsafe {
254 let mut info: snd_pcm_channel_info = mem::zeroed();
255 sndrv_pcm_ioctl_channel_info(fd, &mut info)?;
256 info
257 };
258 if (info.step != channels * mem::size_of::<S>() as u32 * 8) || (info.first != 0) {
260 return Err(Error::unsupported("MMAP data size mismatch"))
261 }
262 Ok(SampleData {
263 mem: DriverMemory::new(fd, (bufsize as usize) * (channels as usize), info.offset as libc::off_t, true)?,
264 frames: bufsize,
265 channels,
266 })
267 }
268}
269
270
271pub trait MmapDir: fmt::Debug {
273 const DIR: Direction;
274 fn avail(hwptr: Frames, applptr: Frames, buffersize: Frames, boundary: Frames) -> Frames;
275}
276
277#[derive(Copy, Clone, Debug)]
279pub struct Playback;
280
281impl MmapDir for Playback {
282 const DIR: Direction = Direction::Playback;
283 #[inline]
284 fn avail(hwptr: Frames, applptr: Frames, buffersize: Frames, boundary: Frames) -> Frames {
285 let r = hwptr.wrapping_add(buffersize).wrapping_sub(applptr);
286 let r = if r < 0 { r.wrapping_add(boundary) } else { r };
287 if r as usize >= boundary as usize { r.wrapping_sub(boundary) } else { r }
288 }
289}
290
291#[derive(Copy, Clone, Debug)]
293pub struct Capture;
294
295impl MmapDir for Capture {
296 const DIR: Direction = Direction::Capture;
297 #[inline]
298 fn avail(hwptr: Frames, applptr: Frames, _buffersize: Frames, boundary: Frames) -> Frames {
299 let r = hwptr.wrapping_sub(applptr);
300 if r < 0 { r.wrapping_add(boundary) } else { r }
301 }
302}
303
304pub type MmapPlayback<S> = MmapIO<S, Playback>;
305
306pub type MmapCapture<S> = MmapIO<S, Capture>;
307
308#[derive(Debug)]
309pub struct MmapIO<S, D> {
311 data: SampleData<S>,
312 c: Control,
313 ss: Status,
314 bound: Frames,
315 dir: PhantomData<*const D>,
316}
317
318#[derive(Debug, Clone, Copy)]
319pub struct RawSamples<S> {
321 pub ptr: *mut S,
322 pub frames: Frames,
323 pub channels: u32,
324}
325
326impl<S> RawSamples<S> {
327 #[inline]
328 pub fn samples(&self) -> isize { self.frames as isize * (self.channels as isize) }
330
331 pub unsafe fn write_samples<I: Iterator<Item=S>>(&self, i: &mut I) -> (bool, isize) {
336 let mut z = 0;
337 let max_samples = self.samples();
338 while z < max_samples {
339 let b = if let Some(b) = i.next() { b } else { return (true, z) };
340 ptr::write_volatile(self.ptr.offset(z), b);
341 z += 1;
342 };
343 (false, z)
344 }
345
346}
347
348impl<S, D: MmapDir> MmapIO<S, D> {
349 fn new(p: &pcm::PCM) -> Result<Self> {
350 if p.info()?.get_stream() != D::DIR {
351 return Err(Error::unsupported("Wrong direction"));
352 }
353 let boundary = p.sw_params_current()?.get_boundary()?;
354 Ok(MmapIO {
355 data: SampleData::new(p)?,
356 c: Control::new(p)?,
357 ss: Status::new(p)?,
358 bound: boundary,
359 dir: PhantomData,
360 })
361 }
362}
363
364pub (crate) fn new_mmap<S, D: MmapDir>(p: &pcm::PCM) -> Result<MmapIO<S, D>> { MmapIO::new(p) }
365
366impl<S, D: MmapDir> MmapIO<S, D> {
367 pub fn status(&self) -> &Status { &self.ss }
369
370 #[inline]
374 pub fn appl_ptr(&self) -> Frames { self.c.appl_ptr() }
375
376 #[inline]
380 pub fn hw_ptr(&self) -> Frames { self.ss.hw_ptr() }
381
382 #[inline]
384 pub fn boundary(&self) -> Frames { self.bound }
385
386 #[inline]
388 pub fn buffer_size(&self) -> Frames { self.data.frames }
389
390 #[inline]
392 pub fn channels(&self) -> u32 { self.data.channels }
393
394 pub fn commit(&self, v: Frames) {
398 let mut z = self.appl_ptr() + v;
399 if z + v >= self.boundary() { z -= self.boundary() };
400 self.c.set_appl_ptr(z)
401 }
402
403 pub fn avail(&self) -> Frames { D::avail(self.hw_ptr(), self.appl_ptr(), self.buffer_size(), self.boundary()) }
407
408 pub fn data_ptr(&self) -> (RawSamples<S>, Option<RawSamples<S>>) {
417 let (hwptr, applptr) = (self.hw_ptr(), self.appl_ptr());
418 let c = self.channels();
419 let bufsize = self.buffer_size();
420
421 let offs = applptr % bufsize;
424 let mut a = D::avail(hwptr, applptr, bufsize, self.boundary());
425 a = cmp::min(a, bufsize);
426 let b = bufsize - offs;
427 let more_data = if b < a {
428 let z = a - b;
429 a = b;
430 Some( RawSamples { ptr: self.data.mem.ptr, frames: z, channels: c })
431 } else { None };
432
433 let p = unsafe { self.data.mem.ptr.offset(offs as isize * self.data.channels as isize) };
434 (RawSamples { ptr: p, frames: a, channels: c }, more_data)
435 }
436}
437
438impl<S> MmapPlayback<S> {
439 pub fn write<I: Iterator<Item=S>>(&mut self, i: &mut I) -> Frames {
441 let (data, more_data) = self.data_ptr();
442 let (iter_end, samples) = unsafe { data.write_samples(i) };
443 let mut z = samples / data.channels as isize;
444 if !iter_end {
445 if let Some(data2) = more_data {
446 let (_, samples2) = unsafe { data2.write_samples(i) };
447 z += samples2 / data2.channels as isize;
448 }
449 }
450 let z = z as Frames;
451 self.commit(z);
452 z
453 }
454}
455
456impl<S> MmapCapture<S> {
457 pub fn iter(&mut self) -> CaptureIter<S> {
462 let (data, more_data) = self.data_ptr();
463 CaptureIter {
464 m: self,
465 samples: data,
466 p_offs: 0,
467 read_samples: 0,
468 next_p: more_data,
469 }
470 }
471}
472
473pub struct CaptureIter<'a, S: 'static> {
475 m: &'a MmapCapture<S>,
476 samples: RawSamples<S>,
477 p_offs: isize,
478 read_samples: isize,
479 next_p: Option<RawSamples<S>>,
480}
481
482impl<'a, S: 'static + Copy> CaptureIter<'a, S> {
483 fn handle_max(&mut self) {
484 self.p_offs = 0;
485 if let Some(p2) = self.next_p.take() {
486 self.samples = p2;
487 } else {
488 self.m.commit((self.read_samples / self.samples.channels as isize) as Frames);
489 self.read_samples = 0;
490 self.samples.frames = 0; }
492 }
493}
494
495impl<'a, S: 'static + Copy> Iterator for CaptureIter<'a, S> {
496 type Item = S;
497
498 #[inline]
499 fn next(&mut self) -> Option<Self::Item> {
500 if self.p_offs >= self.samples.samples() {
501 self.handle_max();
502 if self.samples.frames <= 0 { return None; }
503 }
504 let s = unsafe { ptr::read_volatile(self.samples.ptr.offset(self.p_offs)) };
505 self.p_offs += 1;
506 self.read_samples += 1;
507 Some(s)
508 }
509}
510
511impl<'a, S: 'static> Drop for CaptureIter<'a, S> {
512 fn drop(&mut self) {
513 self.m.commit((self.read_samples / self.m.data.channels as isize) as Frames);
514 }
515}
516
517
518#[test]
519#[ignore] fn record_from_plughw_rw() {
521 use crate::pcm::*;
522 use crate::{ValueOr, Direction};
523 use std::ffi::CString;
524 let pcm = PCM::open(&*CString::new("plughw:1").unwrap(), Direction::Capture, false).unwrap();
525 let ss = self::Status::new(&pcm).unwrap();
526 let c = self::Control::new(&pcm).unwrap();
527 let hwp = HwParams::any(&pcm).unwrap();
528 hwp.set_channels(2).unwrap();
529 hwp.set_rate(44100, ValueOr::Nearest).unwrap();
530 hwp.set_format(Format::s16()).unwrap();
531 hwp.set_access(Access::RWInterleaved).unwrap();
532 pcm.hw_params(&hwp).unwrap();
533
534 {
535 let swp = pcm.sw_params_current().unwrap();
536 swp.set_tstamp_mode(true).unwrap();
537 pcm.sw_params(&swp).unwrap();
538 }
539 assert_eq!(ss.state(), State::Prepared);
540 pcm.start().unwrap();
541 assert_eq!(c.appl_ptr(), 0);
542 println!("{:?}, {:?}", ss, c);
543 let mut buf = [0i16; 512*2];
544 assert_eq!(pcm.io_i16().unwrap().readi(&mut buf).unwrap(), 512);
545 assert_eq!(c.appl_ptr(), 512);
546
547 assert_eq!(ss.state(), State::Running);
548 assert!(ss.hw_ptr() >= 512);
549 let t2 = ss.htstamp();
550 assert!(t2.tv_sec > 0 || t2.tv_nsec > 0);
551}
552
553
554#[test]
555#[ignore] fn record_from_plughw_mmap() {
557 use crate::pcm::*;
558 use crate::{ValueOr, Direction};
559 use std::ffi::CString;
560 use std::{thread, time};
561
562 let pcm = PCM::open(&*CString::new("plughw:1").unwrap(), Direction::Capture, false).unwrap();
563 let hwp = HwParams::any(&pcm).unwrap();
564 hwp.set_channels(2).unwrap();
565 hwp.set_rate(44100, ValueOr::Nearest).unwrap();
566 hwp.set_format(Format::s16()).unwrap();
567 hwp.set_access(Access::MMapInterleaved).unwrap();
568 pcm.hw_params(&hwp).unwrap();
569
570 let ss = unsafe { SyncPtrStatus::sync_ptr(pcm_to_fd(&pcm).unwrap(), false, None, None).unwrap() };
571 assert_eq!(ss.state(), State::Prepared);
572
573 let mut m = pcm.direct_mmap_capture::<i16>().unwrap();
574
575 assert_eq!(m.status().state(), State::Prepared);
576 assert_eq!(m.appl_ptr(), 0);
577 assert_eq!(m.hw_ptr(), 0);
578
579
580 println!("{:?}", m);
581
582 let now = time::Instant::now();
583 pcm.start().unwrap();
584 while m.avail() < 256 { thread::sleep(time::Duration::from_millis(1)) };
585 assert!(now.elapsed() >= time::Duration::from_millis(256 * 1000 / 44100));
586 let (ptr1, md) = m.data_ptr();
587 assert_eq!(ptr1.channels, 2);
588 assert!(ptr1.frames >= 256);
589 assert!(md.is_none());
590 println!("Has {:?} frames at {:?} in {:?}", m.avail(), ptr1.ptr, now.elapsed());
591 let samples: Vec<i16> = m.iter().collect();
592 assert!(samples.len() >= ptr1.frames as usize * 2);
593 println!("Collected {} samples", samples.len());
594 let (ptr2, _md) = m.data_ptr();
595 assert!(unsafe { ptr1.ptr.offset(256 * 2) } <= ptr2.ptr);
596}
597
598#[test]
599#[ignore]
600fn playback_to_plughw_mmap() {
601 use crate::pcm::*;
602 use crate::{ValueOr, Direction};
603 use std::ffi::CString;
604
605 let pcm = PCM::open(&*CString::new("plughw:1").unwrap(), Direction::Playback, false).unwrap();
606 let hwp = HwParams::any(&pcm).unwrap();
607 hwp.set_channels(2).unwrap();
608 hwp.set_rate(44100, ValueOr::Nearest).unwrap();
609 hwp.set_format(Format::s16()).unwrap();
610 hwp.set_access(Access::MMapInterleaved).unwrap();
611 pcm.hw_params(&hwp).unwrap();
612 let mut m = pcm.direct_mmap_playback::<i16>().unwrap();
613
614 assert_eq!(m.status().state(), State::Prepared);
615 assert_eq!(m.appl_ptr(), 0);
616 assert_eq!(m.hw_ptr(), 0);
617
618 println!("{:?}", m);
619 let mut i = (0..(m.buffer_size() * 2)).map(|i|
620 (((i / 2) as f32 * 2.0 * ::std::f32::consts::PI / 128.0).sin() * 8192.0) as i16);
621 m.write(&mut i);
622 assert_eq!(m.appl_ptr(), m.buffer_size());
623
624 pcm.start().unwrap();
625 pcm.drain().unwrap();
626 assert_eq!(m.appl_ptr(), m.buffer_size());
627 assert!(m.hw_ptr() >= m.buffer_size());
628}