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}