libpulse_binding/
sample.rs

1// Copyright 2017 Lyndon Brown
2//
3// This file is part of the PulseAudio Rust language binding.
4//
5// Licensed under the MIT license or the Apache license (version 2.0), at your option. You may not
6// copy, modify, or distribute this file except in compliance with said license. You can find copies
7// of these licenses either in the LICENSE-MIT and LICENSE-APACHE files, or alternatively at
8// <http://opensource.org/licenses/MIT> and <http://www.apache.org/licenses/LICENSE-2.0>
9// respectively.
10//
11// Portions of documentation are copied from the LGPL 2.1+ licensed PulseAudio C headers on a
12// fair-use basis, as discussed in the overall project readme (available in the git repository).
13
14//! Constants and routines for sample type handling.
15//!
16//! # Overview
17//!
18//! PulseAudio is capable of handling a multitude of sample formats, rates and channels,
19//! transparently converting and mixing them as needed.
20//!
21//! # Sample Formats
22//!
23//! PulseAudio supports the following sample formats:
24//!
25//! * `U8` - Unsigned 8 bit integer PCM.
26//! * `S16LE` - Signed 16 integer bit PCM, little endian.
27//! * `S16BE` - Signed 16 integer bit PCM, big endian.
28//! * `FLOAT32LE` - 32 bit IEEE floating point PCM, little endian.
29//! * `FLOAT32BE` - 32 bit IEEE floating point PCM, big endian.
30//! * `ALAW` - 8 bit a-Law.
31//! * `ULAW` - 8 bit mu-Law.
32//! * `S32LE` - Signed 32 bit integer PCM, little endian.
33//! * `S32BE` - Signed 32 bit integer PCM, big endian.
34//! * `S24LE` - Signed 24 bit integer PCM packed, little endian.
35//! * `S24BE` - Signed 24 bit integer PCM packed, big endian.
36//! * `S24_32LE` - Signed 24 bit integer PCM in LSB of 32 bit words, little endian.
37//! * `S24_32BE` - Signed 24 bit integer PCM in LSB of 32 bit words, big endian.
38//!
39//! The floating point sample formats have the range from `-1.0` to `1.0`.
40//!
41//! # Sample Rates
42//!
43//! PulseAudio supports any sample rate between 1 Hz and 192000 Hz. There is no point trying to
44//! exceed the sample rate of the output device though as the signal will only get downsampled,
45//! consuming CPU on the machine running the server.
46//!
47//! # Channels
48//!
49//! PulseAudio supports up to 32 individual channels. The order of the channels is up to the
50//! application, but they must be continuous. To map channels to speakers, see
51//! [`channelmap`](mod@crate::channelmap).
52//!
53//! # Calculations
54//!
55//! The PulseAudio library contains a number of convenience functions to do calculations on sample
56//! formats:
57//!
58//! * [`Spec::bytes_per_second()`]: The number of bytes one second of audio will take given a sample
59//!   format.
60//! * [`Spec::frame_size()`]: The size, in bytes, of one frame (i.e. one set of samples, one for
61//!   each channel).
62//! * [`Spec::sample_size()`]: The size, in bytes, of one sample.
63//! * [`Spec::bytes_to_usec()`]: Calculate the time it would take to play a buffer of a certain
64//!   size.
65
66use std::ffi::{CStr, CString};
67use std::borrow::Cow;
68use num_derive::{FromPrimitive, ToPrimitive};
69use crate::time::MicroSeconds;
70
71/// Sample format.
72///
73/// Note, native-endian (endian-independent) associated constants are available on this type which
74/// should be preferred over direct use of the endian-specific variants, for improved flexibility
75/// and avoidance of mistakes.
76#[repr(C)]
77#[derive(Debug, Copy, Clone, PartialEq, Eq)]
78#[derive(FromPrimitive, ToPrimitive)]
79#[allow(non_camel_case_types)]
80pub enum Format {
81    /* NOTE: This enum’s variants and variant values **must** remain identical to the `sys` crate
82       (C API) equivalent */
83    /// Unsigned 8 Bit PCM.
84    U8,
85    /// 8 Bit a-Law.
86    ALaw,
87    /// 8 Bit mu-Law.
88    ULaw,
89    /// Signed 16 Bit PCM, little endian (PC).
90    S16le,
91    /// Signed 16 Bit PCM, big endian.
92    S16be,
93    /// 32 Bit IEEE floating point, little endian (PC), range -1.0 to 1.0.
94    F32le,
95    /// 32 Bit IEEE floating point, big endian, range -1.0 to 1.0.
96    F32be,
97    /// Signed 32 Bit PCM, little endian (PC).
98    S32le,
99    /// Signed 32 Bit PCM, big endian.
100    S32be,
101    /// Signed 24 Bit PCM packed, little endian (PC).
102    S24le,
103    /// Signed 24 Bit PCM packed, big endian.
104    S24be,
105    /// Signed 24 Bit PCM in LSB of 32 Bit words, little endian (PC).
106    S24_32le,
107    /// Signed 24 Bit PCM in LSB of 32 Bit words, big endian.
108    S24_32be,
109
110    /// An invalid value.
111    Invalid = -1,
112}
113
114/// Check is equal to `sys` equivalent
115#[test]
116fn format_compare_capi() {
117    assert_eq!(std::mem::size_of::<Format>(), std::mem::size_of::<capi::pa_sample_format_t>());
118    assert_eq!(std::mem::align_of::<Format>(), std::mem::align_of::<capi::pa_sample_format_t>());
119
120    // Check order and value of variants match
121    // No point checking conversions in both directions since both are a transmute
122    assert_eq!(Format::U8,       Format::from(capi::pa_sample_format_t::U8));
123    assert_eq!(Format::ALaw,     Format::from(capi::pa_sample_format_t::ALaw));
124    assert_eq!(Format::ULaw,     Format::from(capi::pa_sample_format_t::ULaw));
125    assert_eq!(Format::S16le,    Format::from(capi::pa_sample_format_t::S16le));
126    assert_eq!(Format::S16be,    Format::from(capi::pa_sample_format_t::S16be));
127    assert_eq!(Format::F32le,    Format::from(capi::pa_sample_format_t::F32le));
128    assert_eq!(Format::F32be,    Format::from(capi::pa_sample_format_t::F32be));
129    assert_eq!(Format::S32le,    Format::from(capi::pa_sample_format_t::S32le));
130    assert_eq!(Format::S32be,    Format::from(capi::pa_sample_format_t::S32be));
131    assert_eq!(Format::S24le,    Format::from(capi::pa_sample_format_t::S24le));
132    assert_eq!(Format::S24be,    Format::from(capi::pa_sample_format_t::S24be));
133    assert_eq!(Format::S24_32le, Format::from(capi::pa_sample_format_t::S24_32le));
134    assert_eq!(Format::S24_32be, Format::from(capi::pa_sample_format_t::S24_32be));
135    assert_eq!(Format::Invalid,  Format::from(capi::pa_sample_format_t::Invalid));
136}
137
138impl From<Format> for capi::pa_sample_format_t {
139    #[inline]
140    fn from(f: Format) -> Self {
141        unsafe { std::mem::transmute(f) }
142    }
143}
144impl From<capi::pa_sample_format_t> for Format {
145    #[inline]
146    fn from(f: capi::pa_sample_format_t) -> Self {
147        unsafe { std::mem::transmute(f) }
148    }
149}
150
151impl Default for Format {
152    #[inline(always)]
153    fn default() -> Self {
154        Format::Invalid
155    }
156}
157
158// The following are endian-independant format references.
159
160/// A shortcut for [`SAMPLE_FLOAT32NE`].
161#[allow(deprecated)]
162#[deprecated(since = "2.20.0", note = "use the `FLOAT32NE` associated constant on `Format` instead")]
163pub const SAMPLE_FLOAT32: Format = SAMPLE_FLOAT32NE;
164
165/// Signed 16-bit PCM, native endian.
166#[deprecated(since = "2.20.0", note = "use the associated constant on `Format` instead")]
167pub const SAMPLE_S16NE:     Format = self::ei_formats::SAMPLE_S16NE;
168/// 32-bit IEEE floating point, native endian.
169#[deprecated(since = "2.20.0", note = "use the associated constant on `Format` instead")]
170pub const SAMPLE_FLOAT32NE: Format = self::ei_formats::SAMPLE_FLOAT32NE;
171/// Signed 32-bit PCM, native endian.
172#[deprecated(since = "2.20.0", note = "use the associated constant on `Format` instead")]
173pub const SAMPLE_S32NE:     Format = self::ei_formats::SAMPLE_S32NE;
174/// Signed 24-bit PCM packed, native endian.
175#[deprecated(since = "2.20.0", note = "use the associated constant on `Format` instead")]
176pub const SAMPLE_S24NE:     Format = self::ei_formats::SAMPLE_S24NE;
177/// Signed 24-bit PCM in LSB of 32-bit words, native endian.
178#[deprecated(since = "2.20.0", note = "use the associated constant on `Format` instead")]
179pub const SAMPLE_S24_32NE:  Format = self::ei_formats::SAMPLE_S24_32NE;
180
181/// Signed 16-bit PCM reverse endian.
182#[deprecated(since = "2.20.0", note = "use the associated constant on `Format` instead")]
183pub const SAMPLE_S16RE:     Format = self::ei_formats::SAMPLE_S16RE;
184/// 32-bit IEEE floating point, reverse endian.
185#[deprecated(since = "2.20.0", note = "use the associated constant on `Format` instead")]
186pub const SAMPLE_FLOAT32RE: Format = self::ei_formats::SAMPLE_FLOAT32RE;
187/// Signed 32-bit PCM, reverse endian.
188#[deprecated(since = "2.20.0", note = "use the associated constant on `Format` instead")]
189pub const SAMPLE_S32RE:     Format = self::ei_formats::SAMPLE_S32RE;
190/// Signed 24-bit PCM, packed reverse endian.
191#[deprecated(since = "2.20.0", note = "use the associated constant on `Format` instead")]
192pub const SAMPLE_S24RE:     Format = self::ei_formats::SAMPLE_S24RE;
193/// Signed 24-bit PCM, in LSB of 32-bit words, reverse endian.
194#[deprecated(since = "2.20.0", note = "use the associated constant on `Format` instead")]
195pub const SAMPLE_S24_32RE:  Format = self::ei_formats::SAMPLE_S24_32RE;
196
197/// Endian-independent format identifiers, for big-endian systems.
198#[cfg(target_endian = "big")]
199mod ei_formats {
200    use super::Format;
201
202    pub const SAMPLE_S16NE:     Format = Format::S16be;
203    pub const SAMPLE_FLOAT32NE: Format = Format::F32be;
204    pub const SAMPLE_S32NE:     Format = Format::S32be;
205    pub const SAMPLE_S24NE:     Format = Format::S24be;
206    pub const SAMPLE_S24_32NE:  Format = Format::S24_32be;
207
208    pub const SAMPLE_S16RE:     Format = Format::S16le;
209    pub const SAMPLE_FLOAT32RE: Format = Format::F32le;
210    pub const SAMPLE_S32RE:     Format = Format::S32le;
211    pub const SAMPLE_S24RE:     Format = Format::S24le;
212    pub const SAMPLE_S24_32RE:  Format = Format::S24_32le;
213}
214
215/// Endian-independent format identifiers, for little-endian systems.
216#[cfg(target_endian = "little")]
217mod ei_formats {
218    use super::Format;
219
220    pub const SAMPLE_S16NE:     Format = Format::S16le;
221    pub const SAMPLE_FLOAT32NE: Format = Format::F32le;
222    pub const SAMPLE_S32NE:     Format = Format::S32le;
223    pub const SAMPLE_S24NE:     Format = Format::S24le;
224    pub const SAMPLE_S24_32NE:  Format = Format::S24_32le;
225
226    pub const SAMPLE_S16RE:     Format = Format::S16be;
227    pub const SAMPLE_FLOAT32RE: Format = Format::F32be;
228    pub const SAMPLE_S32RE:     Format = Format::S32be;
229    pub const SAMPLE_S24RE:     Format = Format::S24be;
230    pub const SAMPLE_S24_32RE:  Format = Format::S24_32be;
231}
232
233/// A sample format and attribute specification.
234#[repr(C)]
235#[derive(Debug, Copy, Clone, Eq)]
236pub struct Spec {
237    /* NOTE: This struct must be directly usable by the C API, thus same attributes/layout/etc */
238    /// The sample format.
239    pub format: Format,
240    /// The sample rate. (e.g. 44100).
241    pub rate: u32,
242    /// Audio channels. (1 for mono, 2 for stereo, ...).
243    pub channels: u8,
244}
245
246/// Test size is equal to `sys` equivalent
247#[test]
248fn spec_compare_capi() {
249    assert_eq!(std::mem::size_of::<Spec>(), std::mem::size_of::<capi::pa_sample_spec>());
250    assert_eq!(std::mem::align_of::<Spec>(), std::mem::align_of::<capi::pa_sample_spec>());
251}
252
253impl AsRef<capi::pa_sample_spec> for Spec {
254    #[inline]
255    fn as_ref(&self) -> &capi::pa_sample_spec {
256        unsafe { &*(self as *const Self as *const capi::pa_sample_spec) }
257    }
258}
259impl AsMut<capi::pa_sample_spec> for Spec {
260    #[inline]
261    fn as_mut(&mut self) -> &mut capi::pa_sample_spec {
262        unsafe { &mut *(self as *mut Self as *mut capi::pa_sample_spec) }
263    }
264}
265impl AsRef<Spec> for capi::pa_sample_spec {
266    #[inline]
267    fn as_ref(&self) -> &Spec {
268        unsafe { &*(self as *const Self as *const Spec) }
269    }
270}
271
272impl From<capi::pa_sample_spec> for Spec {
273    #[inline]
274    fn from(s: capi::pa_sample_spec) -> Self {
275        unsafe { std::mem::transmute(s) }
276    }
277}
278
279impl PartialEq for Spec {
280    #[inline]
281    fn eq(&self, other: &Self) -> bool {
282        unsafe { capi::pa_sample_spec_equal(self.as_ref(), other.as_ref()) != 0 }
283    }
284}
285
286impl Spec {
287    /// Maximum number of allowed channels.
288    pub const CHANNELS_MAX: u8 = capi::PA_CHANNELS_MAX;
289    /// Maximum allowed sample rate.
290    pub const RATE_MAX: u32 = capi::PA_RATE_MAX;
291
292    /// Initializes the specified sample spec.
293    ///
294    /// The sample spec will have a defined state but [`is_valid()`](Self::is_valid) will fail for
295    /// it.
296    #[inline]
297    pub fn init(&mut self) {
298        unsafe { capi::pa_sample_spec_init(self.as_mut()); }
299    }
300
301    /// Checks if the whole sample type specification is valid.
302    #[inline]
303    pub fn is_valid(&self) -> bool {
304        unsafe { capi::pa_sample_spec_valid(self.as_ref()) != 0 }
305    }
306
307    /// Checks only if the format attribute is valid.
308    ///
309    /// Or in other words that the client library running on the end user system accepts it.
310    #[inline]
311    pub fn format_is_valid(&self) -> bool {
312        unsafe { capi::pa_sample_format_valid(self.format as u32) != 0 }
313    }
314
315    /// Checks only if the rate is within the supported range.
316    #[inline]
317    pub fn rate_is_valid(&self) -> bool {
318        unsafe { capi::pa_sample_rate_valid(self.rate) != 0 }
319    }
320
321    /// Checks only if the channel count is within the supported range.
322    #[inline]
323    pub fn channels_are_valid(&self) -> bool {
324        unsafe { capi::pa_channels_valid(self.channels) != 0 }
325    }
326
327    /// Gets the amount of bytes that constitute playback of one second of audio, with the specified
328    /// sample type.
329    #[inline]
330    pub fn bytes_per_second(&self) -> usize {
331        unsafe { capi::pa_bytes_per_second(self.as_ref()) }
332    }
333
334    /// Gets the size of a frame.
335    #[inline]
336    pub fn frame_size(&self) -> usize {
337        unsafe { capi::pa_frame_size(self.as_ref()) }
338    }
339
340    /// Gets the size of a sample.
341    #[inline]
342    pub fn sample_size(&self) -> usize {
343        unsafe { capi::pa_sample_size(self.as_ref()) }
344    }
345
346    /// Calculates the time it would take to play a buffer of the specified size.
347    ///
348    /// The return value will always be rounded down for non-integral return values.
349    ///
350    /// Note, the underlying calculation may overflow for very large values.
351    #[inline]
352    pub fn bytes_to_usec(&self, length: u64) -> MicroSeconds {
353        MicroSeconds(unsafe { capi::pa_bytes_to_usec(length, self.as_ref()) })
354    }
355
356    /// Calculates the size of a buffer required, for playback duration of the time specified.
357    ///
358    /// The return value will always be rounded down for non-integral return values.
359    ///
360    /// Note, the underlying calculation may overflow for very large values.
361    #[inline]
362    pub fn usec_to_bytes(&self, t: MicroSeconds) -> usize {
363        unsafe { capi::pa_usec_to_bytes(t.0, self.as_ref()) }
364    }
365
366    /// Pretty prints a sample type specification to a string.
367    pub fn print(&self) -> String {
368        const PRINT_MAX: usize = capi::PA_SAMPLE_SPEC_SNPRINT_MAX;
369        let mut tmp = Vec::with_capacity(PRINT_MAX);
370        unsafe {
371            capi::pa_sample_spec_snprint(tmp.as_mut_ptr(), PRINT_MAX, self.as_ref());
372            CStr::from_ptr(tmp.as_mut_ptr()).to_string_lossy().into_owned()
373        }
374    }
375}
376
377/// Pretty print a byte size value (i.e. “2.5 MiB”).
378pub fn bytes_print(bytes: u32) -> String {
379    const PRINT_MAX: usize = capi::PA_BYTES_SNPRINT_MAX;
380    let mut tmp = Vec::with_capacity(PRINT_MAX);
381    unsafe {
382        capi::pa_bytes_snprint(tmp.as_mut_ptr(), PRINT_MAX, bytes);
383        CStr::from_ptr(tmp.as_mut_ptr()).to_string_lossy().into_owned()
384    }
385}
386
387impl Format {
388    /// Signed 16-bit PCM, native endian.
389    pub const S16NE:     Self = self::ei_formats::SAMPLE_S16NE;
390    /// 32-bit IEEE floating point, native endian.
391    pub const FLOAT32NE: Self = self::ei_formats::SAMPLE_FLOAT32NE;
392    /// Signed 32-bit PCM, native endian.
393    pub const S32NE:     Self = self::ei_formats::SAMPLE_S32NE;
394    /// Signed 24-bit PCM packed, native endian.
395    pub const S24NE:     Self = self::ei_formats::SAMPLE_S24NE;
396    /// Signed 24-bit PCM in LSB of 32-bit words, native endian.
397    pub const S24_32NE:  Self = self::ei_formats::SAMPLE_S24_32NE;
398
399    /// Signed 16-bit PCM reverse endian.
400    pub const S16RE:     Self = self::ei_formats::SAMPLE_S16RE;
401    /// 32-bit IEEE floating point, reverse endian.
402    pub const FLOAT32RE: Self = self::ei_formats::SAMPLE_FLOAT32RE;
403    /// Signed 32-bit PCM, reverse endian.
404    pub const S32RE:     Self = self::ei_formats::SAMPLE_S32RE;
405    /// Signed 24-bit PCM, packed reverse endian.
406    pub const S24RE:     Self = self::ei_formats::SAMPLE_S24RE;
407    /// Signed 24-bit PCM, in LSB of 32-bit words, reverse endian.
408    pub const S24_32RE:  Self = self::ei_formats::SAMPLE_S24_32RE;
409
410    /// Similar to [`Spec::sample_size()`] but take a sample format instead of full sample spec.
411    #[inline]
412    pub fn size(&self) -> usize {
413        unsafe { capi::pa_sample_size_of_format((*self).into()) }
414    }
415
416    /// Gets a descriptive string for the specified sample format.
417    pub fn to_string(&self) -> Option<Cow<'static, str>> {
418        let ptr = unsafe { capi::pa_sample_format_to_string((*self).into()) };
419        match ptr.is_null() {
420            false => Some(unsafe { CStr::from_ptr(ptr).to_string_lossy() }),
421            true => None,
422        }
423    }
424
425    /// Parses a sample format text. Inverse of [`to_string()`](Self::to_string).
426    pub fn parse(format: &str) -> Self {
427        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
428        // as_ptr() giving dangling pointers!
429        let c_format = CString::new(format).unwrap();
430        unsafe { capi::pa_parse_sample_format(c_format.as_ptr()).into() }
431    }
432
433    /// Checks if format is little endian.
434    ///
435    /// Returns `true` when the specified format is little endian, `false` if big endian. Returns
436    /// `None` when endianness does not apply to this format, or if unknown.
437    pub fn is_le(&self) -> Option<bool> {
438        match unsafe { capi::pa_sample_format_is_le((*self).into()) } {
439            0 => Some(false),
440            1 => Some(true),
441            _ => None,
442        }
443    }
444
445    /// Checks if format is big endian.
446    ///
447    /// Returns `true` when the specified format is big endian, `false` if little endian. Returns
448    /// `None` when endianness does not apply to this format, or if unknown.
449    pub fn is_be(&self) -> Option<bool> {
450        match unsafe { capi::pa_sample_format_is_be((*self).into()) } {
451            0 => Some(false),
452            1 => Some(true),
453            _ => None,
454        }
455    }
456
457    /// Checks if format is native endian.
458    ///
459    /// Returns `true` when the specified format is native endian, `false` when not. Returns `None`
460    /// when endianness does not apply to the specified format, or endianness is unknown.
461    #[inline]
462    pub fn is_ne(&self) -> Option<bool> {
463        #[cfg(target_endian = "big")]
464        { Format::is_be(self) }
465        #[cfg(target_endian = "little")]
466        { Format::is_le(self) }
467    }
468
469    /// Checks if format is reverse of native endian.
470    ///
471    /// Returns `true` when the specified format is reverse endian, `false` when not. Returns `None`
472    /// when endianness does not apply to the specified format, or endianness is unknown.
473    #[inline]
474    pub fn is_re(&self) -> Option<bool> {
475        self.is_ne().and_then(|b| Some(!b))
476    }
477}