audio_device/alsa/
pcm.rs

1#[cfg(feature = "poll-driver")]
2use crate::alsa::AsyncWriter;
3use crate::alsa::{
4    ChannelArea, Configurator, Error, HardwareParameters, HardwareParametersMut, Result, Sample,
5    SoftwareParameters, SoftwareParametersMut, State, Stream, Writer,
6};
7use crate::libc as c;
8use crate::unix::poll::PollFlags;
9use alsa_sys as alsa;
10use std::ffi::CStr;
11use std::mem;
12use std::ptr;
13
14/// An opened PCM device.
15pub struct Pcm {
16    pub(super) tag: ste::Tag,
17    pub(super) handle: ptr::NonNull<alsa::snd_pcm_t>,
18}
19
20impl Pcm {
21    /// Open the given pcm device identified by name.
22    ///
23    /// # Examples
24    ///
25    /// ```rust,no_run
26    /// use audio_device::alsa;
27    /// use std::ffi::CStr;
28    ///
29    /// # fn main() -> anyhow::Result<()> {
30    /// let name = CStr::from_bytes_with_nul(b"hw:0\0")?;
31    ///
32    /// let pcm = alsa::Pcm::open(name, alsa::Stream::Playback)?;
33    /// # Ok(()) }
34    /// ```
35    pub fn open(name: &CStr, stream: Stream) -> Result<Self> {
36        Self::open_inner(name, stream, 0)
37    }
38
39    /// Open the default pcm device.
40    ///
41    /// # Examples
42    ///
43    /// ```rust,no_run
44    /// use audio_device::alsa;
45    /// use std::ffi::CStr;
46    ///
47    /// # fn main() -> anyhow::Result<()> {
48    /// let pcm = alsa::Pcm::open_default(alsa::Stream::Playback)?;
49    /// # Ok(()) }
50    /// ```
51    pub fn open_default(stream: Stream) -> Result<Self> {
52        static DEFAULT: &[u8] = b"default\0";
53        Self::open(
54            unsafe { CStr::from_bytes_with_nul_unchecked(DEFAULT) },
55            stream,
56        )
57    }
58
59    /// Open the given pcm device identified by name in a nonblocking manner.
60    ///
61    /// # Examples
62    ///
63    /// ```rust,no_run
64    /// use audio_device::alsa;
65    /// use std::ffi::CStr;
66    ///
67    /// # fn main() -> anyhow::Result<()> {
68    /// let name = CStr::from_bytes_with_nul(b"hw:0\0")?;
69    ///
70    /// let pcm = alsa::Pcm::open_nonblocking(name, alsa::Stream::Playback)?;
71    /// # Ok(()) }
72    /// ```
73    pub fn open_nonblocking(name: &CStr, stream: Stream) -> Result<Self> {
74        Self::open_inner(name, stream, alsa::SND_PCM_NONBLOCK)
75    }
76
77    /// Open the default pcm device in a nonblocking mode.
78    ///
79    /// # Examples
80    ///
81    /// ```rust,no_run
82    /// use audio_device::alsa;
83    /// use std::ffi::CStr;
84    ///
85    /// # fn main() -> anyhow::Result<()> {
86    /// let pcm = alsa::Pcm::open_default_nonblocking(alsa::Stream::Playback)?;
87    /// # Ok(()) }
88    /// ```
89    pub fn open_default_nonblocking(stream: Stream) -> Result<Self> {
90        static DEFAULT: &[u8] = b"default\0";
91        Self::open_nonblocking(
92            unsafe { CStr::from_bytes_with_nul_unchecked(DEFAULT) },
93            stream,
94        )
95    }
96
97    fn open_inner(name: &CStr, stream: Stream, flags: i32) -> Result<Self> {
98        unsafe {
99            let mut handle = mem::MaybeUninit::uninit();
100
101            errno!(alsa::snd_pcm_open(
102                handle.as_mut_ptr(),
103                name.as_ptr(),
104                stream as c::c_uint,
105                flags
106            ))?;
107
108            Ok(Self {
109                tag: ste::Tag::current_thread(),
110                handle: ptr::NonNull::new_unchecked(handle.assume_init()),
111            })
112        }
113    }
114
115    /// Get the state of the PCM.
116    ///
117    /// # Examples
118    ///
119    /// ```rust,no_run
120    /// use audio_device::alsa;
121    ///
122    /// # fn main() -> anyhow::Result<()> {
123    /// let pcm = alsa::Pcm::open_default(alsa::Stream::Playback)?;
124    /// dbg!(pcm.state());
125    /// # Ok(()) }
126    /// ```
127    pub fn state(&self) -> State {
128        self.tag.ensure_on_thread();
129
130        unsafe {
131            let state = alsa::snd_pcm_state(self.handle.as_ptr());
132            State::from_value(state).unwrap_or(State::Private1)
133        }
134    }
135
136    /// Construct a simple stream [Configurator].
137    ///
138    /// It will be initialized with a set of default parameters which are
139    /// usually suitable for simple playback or recording for the given sample
140    /// type `T`.
141    ///
142    /// See [Configurator].
143    ///
144    /// # Examples
145    ///
146    /// ```rust,no_run
147    /// use audio_device::alsa;
148    ///
149    /// # fn main() -> anyhow::Result<()> {
150    /// let mut pcm = alsa::Pcm::open_default(alsa::Stream::Playback)?;
151    /// let config = pcm.configure::<i16>().install()?;
152    /// # Ok(()) }
153    /// ```
154    pub fn configure<T>(&mut self) -> Configurator<'_, T>
155    where
156        T: Sample,
157    {
158        self.tag.ensure_on_thread();
159        Configurator::new(self)
160    }
161
162    /// Start a PCM.
163    ///
164    /// # Examples
165    ///
166    /// ```rust,no_run
167    /// use audio_device::alsa;
168    ///
169    /// # fn main() -> anyhow::Result<()> {
170    /// let mut pcm = alsa::Pcm::open_default(alsa::Stream::Playback)?;
171    /// pcm.start()?;
172    /// # Ok(()) }
173    /// ```
174    pub fn start(&mut self) -> Result<()> {
175        self.tag.ensure_on_thread();
176
177        unsafe {
178            errno!(alsa::snd_pcm_start(self.handle.as_mut()))?;
179            Ok(())
180        }
181    }
182
183    /// Pause a PCM.
184    ///
185    /// # Examples
186    ///
187    /// ```rust,no_run
188    /// use audio_device::alsa;
189    ///
190    /// # fn main() -> anyhow::Result<()> {
191    /// let mut pcm = alsa::Pcm::open_default(alsa::Stream::Playback)?;
192    /// pcm.pause()?;
193    /// # Ok(()) }
194    /// ```
195    pub fn pause(&mut self) -> Result<()> {
196        self.tag.ensure_on_thread();
197
198        unsafe {
199            errno!(alsa::snd_pcm_pause(self.handle.as_mut(), 1))?;
200            Ok(())
201        }
202    }
203
204    /// Resume a PCM.
205    ///
206    /// # Examples
207    ///
208    /// ```rust,no_run
209    /// use audio_device::alsa;
210    ///
211    /// # fn main() -> anyhow::Result<()> {
212    /// let mut pcm = alsa::Pcm::open_default(alsa::Stream::Playback)?;
213    /// pcm.resume()?;
214    /// # Ok(()) }
215    /// ```
216    pub fn resume(&mut self) -> Result<()> {
217        self.tag.ensure_on_thread();
218
219        unsafe {
220            errno!(alsa::snd_pcm_pause(self.handle.as_mut(), 0))?;
221            Ok(())
222        }
223    }
224
225    /// Open all available hardware parameters for the current handle.
226    ///
227    /// # Examples
228    ///
229    /// ```rust,no_run
230    /// use audio_device::alsa;
231    ///
232    /// # fn main() -> anyhow::Result<()> {
233    /// let mut pcm = alsa::Pcm::open_default(alsa::Stream::Playback)?;
234    /// let mut hw = pcm.hardware_parameters_any()?;
235    /// hw.set_rate_last()?;
236    /// hw.install()?;
237    /// # Ok(()) }
238    /// ```
239    pub fn hardware_parameters_any(&mut self) -> Result<HardwareParametersMut<'_>> {
240        self.tag.ensure_on_thread();
241
242        unsafe { HardwareParametersMut::any(&mut self.handle) }
243    }
244
245    /// Open current hardware parameters for the current handle for mutable access.
246    ///
247    /// # Examples
248    ///
249    /// ```rust,no_run
250    /// use audio_device::alsa;
251    ///
252    /// # fn main() -> anyhow::Result<()> {
253    /// let mut pcm = alsa::Pcm::open_default(alsa::Stream::Playback)?;
254    ///
255    /// let mut hw = pcm.hardware_parameters_mut()?;
256    /// let actual_rate = hw.set_rate(44100, alsa::Direction::Nearest)?;
257    /// hw.install()?;
258    ///
259    /// dbg!(actual_rate);
260    /// # Ok(()) }
261    /// ```
262    pub fn hardware_parameters_mut(&mut self) -> Result<HardwareParametersMut<'_>> {
263        self.tag.ensure_on_thread();
264
265        unsafe { HardwareParametersMut::current(&mut self.handle) }
266    }
267
268    /// Open current hardware parameters for the current handle.
269    ///
270    /// # Examples
271    ///
272    /// ```rust,no_run
273    /// use audio_device::alsa;
274    ///
275    /// # fn main() -> anyhow::Result<()> {
276    /// let mut pcm = alsa::Pcm::open_default(alsa::Stream::Playback)?;
277    /// let sw = pcm.hardware_parameters()?;
278    /// dbg!(sw.rate()?);
279    /// # Ok(()) }
280    /// ```
281    pub fn hardware_parameters(&mut self) -> Result<HardwareParameters> {
282        self.tag.ensure_on_thread();
283
284        unsafe { HardwareParameters::current(&mut self.handle) }
285    }
286
287    /// Open current software parameters for the current handle.
288    ///
289    /// # Examples
290    ///
291    /// ```rust,no_run
292    /// use audio_device::alsa;
293    ///
294    /// # fn main() -> anyhow::Result<()> {
295    /// let mut pcm = alsa::Pcm::open_default(alsa::Stream::Playback)?;
296    /// let sw = pcm.software_parameters()?;
297    ///
298    /// dbg!(sw.boundary()?);
299    /// # Ok(()) }
300    /// ```
301    pub fn software_parameters(&mut self) -> Result<SoftwareParameters> {
302        self.tag.ensure_on_thread();
303
304        unsafe { SoftwareParameters::new(&mut self.handle) }
305    }
306
307    /// Open current software parameters for the current handle for mutable access.
308    ///
309    /// # Examples
310    ///
311    /// ```rust,no_run
312    /// use audio_device::alsa;
313    ///
314    /// # fn main() -> anyhow::Result<()> {
315    /// let mut pcm = alsa::Pcm::open_default(alsa::Stream::Playback)?;
316    /// let mut sw = pcm.software_parameters_mut()?;
317    ///
318    /// sw.set_timestamp_mode(alsa::Timestamp::Enable)?;
319    /// sw.install()?;
320    /// # Ok(()) }
321    /// ```
322    pub fn software_parameters_mut(&mut self) -> Result<SoftwareParametersMut<'_>> {
323        self.tag.ensure_on_thread();
324
325        unsafe { SoftwareParametersMut::new(&mut self.handle) }
326    }
327
328    /// Get count of poll descriptors for PCM handle.
329    ///
330    /// # Examples
331    ///
332    /// ```rust,no_run
333    /// use audio_device::alsa;
334    ///
335    /// # fn main() -> anyhow::Result<()> {
336    /// let mut pcm = alsa::Pcm::open_default(alsa::Stream::Playback)?;
337    /// let count = pcm.poll_descriptors_count();
338    /// dbg!(count);
339    /// # Ok(()) }
340    /// ```
341    pub fn poll_descriptors_count(&mut self) -> usize {
342        self.tag.ensure_on_thread();
343
344        unsafe { alsa::snd_pcm_poll_descriptors_count(self.handle.as_mut()) as usize }
345    }
346
347    /// Get poll descriptors.
348    ///
349    /// This function fills the given poll descriptor structs for the specified
350    /// PCM handle. The poll desctiptor array should have the size returned by
351    /// [poll_descriptors_count()][Pcm::poll_descriptors_count()] function.
352    ///
353    /// The result is intended for direct use with the `poll()` syscall.
354    ///
355    /// For reading the returned events of poll descriptor after `poll()` system
356    /// call, use ::snd_pcm_poll_descriptors_revents() function. The field
357    /// values in pollfd structs may be bogus regarding the stream direction
358    /// from the application perspective (`POLLIN` might not imply read
359    /// direction and `POLLOUT` might not imply write), but the
360    /// [poll_descriptors_revents()][Pcm::poll_descriptors_revents()] function
361    /// does the right "demangling".
362    ///
363    /// You can use output from this function as arguments for the select()
364    /// syscall, too. Do not forget to translate `POLLIN` and `POLLOUT` events
365    /// to corresponding `FD_SET` arrays and demangle events using
366    /// [poll_descriptors_revents()][Pcm::poll_descriptors_revents()].
367    ///
368    /// # Examples
369    ///
370    /// ```rust,no_run
371    /// use audio_device::alsa;
372    ///
373    /// # fn main() -> anyhow::Result<()> {
374    /// let mut pcm = alsa::Pcm::open_default(alsa::Stream::Playback)?;
375    ///
376    /// let mut fds = Vec::with_capacity(pcm.poll_descriptors_count());
377    /// pcm.poll_descriptors_vec(&mut fds)?;
378    /// # Ok(()) }
379    /// ```
380    pub fn poll_descriptors_vec(&mut self, fds: &mut Vec<c::pollfd>) -> Result<()> {
381        self.tag.ensure_on_thread();
382
383        unsafe {
384            let count = self.poll_descriptors_count();
385
386            if fds.capacity() < count {
387                fds.reserve(count - fds.capacity());
388            }
389
390            let result = errno!(alsa::snd_pcm_poll_descriptors(
391                self.handle.as_mut(),
392                fds.as_mut_ptr() as *mut c::pollfd,
393                fds.capacity() as c::c_uint
394            ))?;
395
396            let result = result as usize;
397
398            assert!(result <= fds.capacity());
399            fds.set_len(result);
400            Ok(())
401        }
402    }
403
404    /// Get returned events from poll descriptors.
405    ///
406    /// This function does "demangling" of the revents mask returned from the
407    /// `poll()` syscall to correct semantics ([PollFlags::POLLIN] = read,
408    /// [PollFlags::POLLOUT] = write).
409    ///
410    /// Note: The null event also exists. Even if `poll()` or `select()` syscall
411    /// returned that some events are waiting, this function might return empty
412    /// set of events. In this case, application should do next event waiting
413    /// using `poll()` or `select()`.
414    ///
415    /// Note: Even if multiple poll descriptors are used (i.e. `fds.len() > 1`),
416    /// this function returns only a single event.
417    pub fn poll_descriptors_revents(&mut self, fds: &mut [c::pollfd]) -> Result<PollFlags> {
418        self.tag.ensure_on_thread();
419
420        unsafe {
421            let mut revents = mem::MaybeUninit::uninit();
422            errno!(alsa::snd_pcm_poll_descriptors_revents(
423                self.handle.as_mut(),
424                // NB: PollFd is `#[repr(transparent)]` around pollfd.
425                fds.as_mut_ptr(),
426                fds.len() as c::c_uint,
427                revents.as_mut_ptr(),
428            ))?;
429            let revents = revents.assume_init();
430            Ok(PollFlags::from_bits_truncate(revents as c::c_short))
431        }
432    }
433
434    /// Write unchecked interleaved frames to a PCM.
435    ///
436    /// Note: that the `len` must be the number of frames in the `buf` which
437    /// *does not* account for the number of channels. So if `len` is 100, and
438    /// the number of configured channels is 2, the `buf` must contain **at
439    /// least** 200 bytes.
440    ///
441    /// See [HardwareParameters::channels].
442    pub unsafe fn write_interleaved_unchecked(
443        &mut self,
444        buf: *const c::c_void,
445        len: c::c_ulong,
446    ) -> Result<c::c_long> {
447        self.tag.ensure_on_thread();
448        Ok(errno!(alsa::snd_pcm_writei(
449            self.handle.as_mut(),
450            buf,
451            len
452        ))?)
453    }
454
455    /// Construct a checked safe writer with the given number of channels and
456    /// the specified sample type.
457    ///
458    /// This will error if the type `T` is not appropriate for this device, or
459    /// if the number of channels does not match the number of configured
460    /// channels.
461    ///
462    /// # Examples
463    ///
464    /// ```rust,no_run
465    /// use audio_device::alsa;
466    ///
467    /// # fn main() -> anyhow::Result<()> {
468    /// let mut pcm = alsa::Pcm::open_default(alsa::Stream::Playback)?;
469    /// let config = pcm.configure::<i16>().install()?;
470    ///
471    /// let mut writer = pcm.writer::<i16>()?;
472    /// // use writer with the resulting config.
473    /// # Ok(()) }
474    /// ```
475    pub fn writer<T>(&mut self) -> Result<Writer<'_, T>>
476    where
477        T: Sample,
478    {
479        self.tag.ensure_on_thread();
480
481        let hw = self.hardware_parameters()?;
482        let channels = hw.channels()? as usize;
483
484        // NB: here we check that `T` is appropriate for the current format.
485        let format = hw.format()?;
486
487        if !T::test(format) {
488            return Err(Error::FormatMismatch {
489                ty: T::describe(),
490                format,
491            });
492        }
493
494        unsafe { Ok(Writer::new(self, channels)) }
495    }
496
497    cfg_poll_driver! {
498        /// Construct a checked safe writer with the given number of channels and
499        /// the specified sample type.
500        ///
501        /// This will error if the type `T` is not appropriate for this device, or
502        /// if the number of channels does not match the number of configured
503        /// channels.
504        ///
505        /// # Panics
506        ///
507        /// Panics if the audio runtime is not available.
508        ///
509        /// See [Runtime][crate::runtime::Runtime] for more.
510        ///
511        /// # Examples
512        ///
513        /// ```rust,no_run
514        /// use audio_device::alsa;
515        ///
516        /// # fn main() -> anyhow::Result<()> {
517        /// let mut pcm = alsa::Pcm::open_default(alsa::Stream::Playback)?;
518        /// let config = pcm.configure::<i16>().install()?;
519        ///
520        /// let mut writer = pcm.writer::<i16>()?;
521        /// // use writer with the resulting config.
522        /// # Ok(()) }
523        /// ```
524        pub fn async_writer<T>(&mut self) -> Result<AsyncWriter<'_, T>>
525        where
526            T: Sample,
527        {
528            self.tag.ensure_on_thread();
529
530            let hw = self.hardware_parameters()?;
531            let channels = hw.channels()? as usize;
532
533            // NB: here we check that `T` is appropriate for the current format.
534            let format = hw.format()?;
535
536            if !T::test(format) {
537                return Err(Error::FormatMismatch {
538                    ty: T::describe(),
539                    format,
540                });
541            }
542
543            let mut fds = Vec::new();
544            self.poll_descriptors_vec(&mut fds)?;
545
546            if fds.len() != 1 {
547                return Err(Error::MissingPollFds);
548            }
549
550            let fd = fds[0];
551
552            Ok(unsafe { AsyncWriter::new(self, fd, channels)? })
553        }
554    }
555
556    /// Return number of frames ready to be read (capture) / written (playback).
557    ///
558    /// # Examples
559    ///
560    /// ```rust,no_run
561    /// use audio_device::alsa;
562    ///
563    /// # fn main() -> anyhow::Result<()> {
564    /// let mut pcm = alsa::Pcm::open_default(alsa::Stream::Playback)?;
565    ///
566    /// let avail = pcm.available_update()?;
567    /// dbg!(avail);
568    /// # Ok(()) }
569    /// ```
570    pub fn available_update(&mut self) -> Result<usize> {
571        self.tag.ensure_on_thread();
572
573        unsafe { Ok(errno!(alsa::snd_pcm_avail_update(self.handle.as_mut()))? as usize) }
574    }
575
576    /// Application request to access a portion of direct (mmap) area.
577    #[doc(hidden)] // incomplete feature
578    pub fn mmap_begin(&mut self, mut frames: c::c_ulong) -> Result<ChannelArea<'_>> {
579        self.tag.ensure_on_thread();
580
581        unsafe {
582            let mut area = mem::MaybeUninit::uninit();
583            let mut offset = mem::MaybeUninit::uninit();
584            errno!(alsa::snd_pcm_mmap_begin(
585                self.handle.as_mut(),
586                area.as_mut_ptr(),
587                offset.as_mut_ptr(),
588                &mut frames
589            ))?;
590            let area = area.assume_init();
591            let offset = offset.assume_init();
592
593            Ok(ChannelArea {
594                pcm: &mut self.handle,
595                area,
596                offset,
597                frames,
598            })
599        }
600    }
601}
602
603// Safety: [Pcm] is tagged with the thread its created it and is ensured not to
604// leave it.
605unsafe impl Send for Pcm {}
606
607impl Drop for Pcm {
608    fn drop(&mut self) {
609        unsafe { alsa::snd_pcm_close(self.handle.as_ptr()) };
610    }
611}