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, Default, 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    #[default]
112    Invalid = -1,
113}
114
115/// Check is equal to `sys` equivalent
116#[test]
117fn format_compare_capi() {
118    assert_eq!(std::mem::size_of::<Format>(), std::mem::size_of::<capi::pa_sample_format_t>());
119    assert_eq!(std::mem::align_of::<Format>(), std::mem::align_of::<capi::pa_sample_format_t>());
120
121    // Check order and value of variants match
122    // No point checking conversions in both directions since both are a transmute
123    assert_eq!(Format::U8,       Format::from(capi::pa_sample_format_t::U8));
124    assert_eq!(Format::ALaw,     Format::from(capi::pa_sample_format_t::ALaw));
125    assert_eq!(Format::ULaw,     Format::from(capi::pa_sample_format_t::ULaw));
126    assert_eq!(Format::S16le,    Format::from(capi::pa_sample_format_t::S16le));
127    assert_eq!(Format::S16be,    Format::from(capi::pa_sample_format_t::S16be));
128    assert_eq!(Format::F32le,    Format::from(capi::pa_sample_format_t::F32le));
129    assert_eq!(Format::F32be,    Format::from(capi::pa_sample_format_t::F32be));
130    assert_eq!(Format::S32le,    Format::from(capi::pa_sample_format_t::S32le));
131    assert_eq!(Format::S32be,    Format::from(capi::pa_sample_format_t::S32be));
132    assert_eq!(Format::S24le,    Format::from(capi::pa_sample_format_t::S24le));
133    assert_eq!(Format::S24be,    Format::from(capi::pa_sample_format_t::S24be));
134    assert_eq!(Format::S24_32le, Format::from(capi::pa_sample_format_t::S24_32le));
135    assert_eq!(Format::S24_32be, Format::from(capi::pa_sample_format_t::S24_32be));
136    assert_eq!(Format::Invalid,  Format::from(capi::pa_sample_format_t::Invalid));
137}
138
139impl From<Format> for capi::pa_sample_format_t {
140    #[inline]
141    fn from(f: Format) -> Self {
142        unsafe { std::mem::transmute(f) }
143    }
144}
145impl From<capi::pa_sample_format_t> for Format {
146    #[inline]
147    fn from(f: capi::pa_sample_format_t) -> Self {
148        unsafe { std::mem::transmute(f) }
149    }
150}
151
152/// Endian-independent format identifiers, for big-endian systems.
153#[cfg(target_endian = "big")]
154mod ei_formats {
155    use super::Format;
156
157    pub const SAMPLE_S16NE:     Format = Format::S16be;
158    pub const SAMPLE_FLOAT32NE: Format = Format::F32be;
159    pub const SAMPLE_S32NE:     Format = Format::S32be;
160    pub const SAMPLE_S24NE:     Format = Format::S24be;
161    pub const SAMPLE_S24_32NE:  Format = Format::S24_32be;
162
163    pub const SAMPLE_S16RE:     Format = Format::S16le;
164    pub const SAMPLE_FLOAT32RE: Format = Format::F32le;
165    pub const SAMPLE_S32RE:     Format = Format::S32le;
166    pub const SAMPLE_S24RE:     Format = Format::S24le;
167    pub const SAMPLE_S24_32RE:  Format = Format::S24_32le;
168}
169
170/// Endian-independent format identifiers, for little-endian systems.
171#[cfg(target_endian = "little")]
172mod ei_formats {
173    use super::Format;
174
175    pub const SAMPLE_S16NE:     Format = Format::S16le;
176    pub const SAMPLE_FLOAT32NE: Format = Format::F32le;
177    pub const SAMPLE_S32NE:     Format = Format::S32le;
178    pub const SAMPLE_S24NE:     Format = Format::S24le;
179    pub const SAMPLE_S24_32NE:  Format = Format::S24_32le;
180
181    pub const SAMPLE_S16RE:     Format = Format::S16be;
182    pub const SAMPLE_FLOAT32RE: Format = Format::F32be;
183    pub const SAMPLE_S32RE:     Format = Format::S32be;
184    pub const SAMPLE_S24RE:     Format = Format::S24be;
185    pub const SAMPLE_S24_32RE:  Format = Format::S24_32be;
186}
187
188/// A sample format and attribute specification.
189#[repr(C)]
190#[derive(Debug, Copy, Clone, Eq)]
191pub struct Spec {
192    /* NOTE: This struct must be directly usable by the C API, thus same attributes/layout/etc */
193    /// The sample format.
194    pub format: Format,
195    /// The sample rate. (e.g. 44100).
196    pub rate: u32,
197    /// Audio channels. (1 for mono, 2 for stereo, ...).
198    pub channels: u8,
199}
200
201/// Test size is equal to `sys` equivalent
202#[test]
203fn spec_compare_capi() {
204    assert_eq!(std::mem::size_of::<Spec>(), std::mem::size_of::<capi::pa_sample_spec>());
205    assert_eq!(std::mem::align_of::<Spec>(), std::mem::align_of::<capi::pa_sample_spec>());
206}
207
208impl AsRef<capi::pa_sample_spec> for Spec {
209    #[inline]
210    fn as_ref(&self) -> &capi::pa_sample_spec {
211        unsafe { &*(self as *const Self as *const capi::pa_sample_spec) }
212    }
213}
214impl AsMut<capi::pa_sample_spec> for Spec {
215    #[inline]
216    fn as_mut(&mut self) -> &mut capi::pa_sample_spec {
217        unsafe { &mut *(self as *mut Self as *mut capi::pa_sample_spec) }
218    }
219}
220impl AsRef<Spec> for capi::pa_sample_spec {
221    #[inline]
222    fn as_ref(&self) -> &Spec {
223        unsafe { &*(self as *const Self as *const Spec) }
224    }
225}
226
227impl From<capi::pa_sample_spec> for Spec {
228    #[inline]
229    fn from(s: capi::pa_sample_spec) -> Self {
230        unsafe { std::mem::transmute(s) }
231    }
232}
233
234impl PartialEq for Spec {
235    #[inline]
236    fn eq(&self, other: &Self) -> bool {
237        unsafe { capi::pa_sample_spec_equal(self.as_ref(), other.as_ref()) != 0 }
238    }
239}
240
241impl Spec {
242    /// Maximum number of allowed channels.
243    pub const CHANNELS_MAX: u8 = capi::PA_CHANNELS_MAX;
244    /// Maximum allowed sample rate.
245    pub const RATE_MAX: u32 = capi::PA_RATE_MAX;
246
247    /// Initializes the specified sample spec.
248    ///
249    /// The sample spec will have a defined state but [`is_valid()`](Self::is_valid) will fail for
250    /// it.
251    #[inline]
252    pub fn init(&mut self) {
253        unsafe { capi::pa_sample_spec_init(self.as_mut()); }
254    }
255
256    /// Checks if the whole sample type specification is valid.
257    #[inline]
258    pub fn is_valid(&self) -> bool {
259        unsafe { capi::pa_sample_spec_valid(self.as_ref()) != 0 }
260    }
261
262    /// Checks only if the format attribute is valid.
263    ///
264    /// Or in other words that the client library running on the end user system accepts it.
265    #[inline]
266    pub fn format_is_valid(&self) -> bool {
267        unsafe { capi::pa_sample_format_valid(self.format as u32) != 0 }
268    }
269
270    /// Checks only if the rate is within the supported range.
271    #[inline]
272    pub fn rate_is_valid(&self) -> bool {
273        unsafe { capi::pa_sample_rate_valid(self.rate) != 0 }
274    }
275
276    /// Checks only if the channel count is within the supported range.
277    #[inline]
278    pub fn channels_are_valid(&self) -> bool {
279        unsafe { capi::pa_channels_valid(self.channels) != 0 }
280    }
281
282    /// Gets the amount of bytes that constitute playback of one second of audio, with the specified
283    /// sample type.
284    #[inline]
285    pub fn bytes_per_second(&self) -> usize {
286        unsafe { capi::pa_bytes_per_second(self.as_ref()) }
287    }
288
289    /// Gets the size of a frame.
290    #[inline]
291    pub fn frame_size(&self) -> usize {
292        unsafe { capi::pa_frame_size(self.as_ref()) }
293    }
294
295    /// Gets the size of a sample.
296    #[inline]
297    pub fn sample_size(&self) -> usize {
298        unsafe { capi::pa_sample_size(self.as_ref()) }
299    }
300
301    /// Calculates the time it would take to play a buffer of the specified size.
302    ///
303    /// The return value will always be rounded down for non-integral return values.
304    ///
305    /// Note, the underlying calculation may overflow for very large values.
306    #[inline]
307    pub fn bytes_to_usec(&self, length: u64) -> MicroSeconds {
308        MicroSeconds(unsafe { capi::pa_bytes_to_usec(length, self.as_ref()) })
309    }
310
311    /// Calculates the size of a buffer required, for playback duration of the time specified.
312    ///
313    /// The return value will always be rounded down for non-integral return values.
314    ///
315    /// Note, the underlying calculation may overflow for very large values.
316    #[inline]
317    pub fn usec_to_bytes(&self, t: MicroSeconds) -> usize {
318        unsafe { capi::pa_usec_to_bytes(t.0, self.as_ref()) }
319    }
320
321    /// Pretty prints a sample type specification to a string.
322    pub fn print(&self) -> String {
323        const PRINT_MAX: usize = capi::PA_SAMPLE_SPEC_SNPRINT_MAX;
324        let mut tmp = Vec::with_capacity(PRINT_MAX);
325        unsafe {
326            capi::pa_sample_spec_snprint(tmp.as_mut_ptr(), PRINT_MAX, self.as_ref());
327            CStr::from_ptr(tmp.as_mut_ptr()).to_string_lossy().into_owned()
328        }
329    }
330}
331
332/// Pretty print a byte size value (i.e. “2.5 MiB”).
333pub fn bytes_print(bytes: u32) -> String {
334    const PRINT_MAX: usize = capi::PA_BYTES_SNPRINT_MAX;
335    let mut tmp = Vec::with_capacity(PRINT_MAX);
336    unsafe {
337        capi::pa_bytes_snprint(tmp.as_mut_ptr(), PRINT_MAX, bytes);
338        CStr::from_ptr(tmp.as_mut_ptr()).to_string_lossy().into_owned()
339    }
340}
341
342impl Format {
343    /// Signed 16-bit PCM, native endian.
344    pub const S16NE:     Self = self::ei_formats::SAMPLE_S16NE;
345    /// 32-bit IEEE floating point, native endian.
346    pub const FLOAT32NE: Self = self::ei_formats::SAMPLE_FLOAT32NE;
347    /// Signed 32-bit PCM, native endian.
348    pub const S32NE:     Self = self::ei_formats::SAMPLE_S32NE;
349    /// Signed 24-bit PCM packed, native endian.
350    pub const S24NE:     Self = self::ei_formats::SAMPLE_S24NE;
351    /// Signed 24-bit PCM in LSB of 32-bit words, native endian.
352    pub const S24_32NE:  Self = self::ei_formats::SAMPLE_S24_32NE;
353
354    /// Signed 16-bit PCM reverse endian.
355    pub const S16RE:     Self = self::ei_formats::SAMPLE_S16RE;
356    /// 32-bit IEEE floating point, reverse endian.
357    pub const FLOAT32RE: Self = self::ei_formats::SAMPLE_FLOAT32RE;
358    /// Signed 32-bit PCM, reverse endian.
359    pub const S32RE:     Self = self::ei_formats::SAMPLE_S32RE;
360    /// Signed 24-bit PCM, packed reverse endian.
361    pub const S24RE:     Self = self::ei_formats::SAMPLE_S24RE;
362    /// Signed 24-bit PCM, in LSB of 32-bit words, reverse endian.
363    pub const S24_32RE:  Self = self::ei_formats::SAMPLE_S24_32RE;
364
365    /// Similar to [`Spec::sample_size()`] but take a sample format instead of full sample spec.
366    #[inline]
367    pub fn size(&self) -> usize {
368        unsafe { capi::pa_sample_size_of_format((*self).into()) }
369    }
370
371    /// Gets a descriptive string for the specified sample format.
372    pub fn to_string(&self) -> Option<Cow<'static, str>> {
373        let ptr = unsafe { capi::pa_sample_format_to_string((*self).into()) };
374        match ptr.is_null() {
375            false => Some(unsafe { CStr::from_ptr(ptr).to_string_lossy() }),
376            true => None,
377        }
378    }
379
380    /// Parses a sample format text. Inverse of [`to_string()`](Self::to_string).
381    pub fn parse(format: &str) -> Self {
382        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
383        // as_ptr() giving dangling pointers!
384        let c_format = CString::new(format).unwrap();
385        unsafe { capi::pa_parse_sample_format(c_format.as_ptr()).into() }
386    }
387
388    /// Checks if format is little endian.
389    ///
390    /// Returns `true` when the specified format is little endian, `false` if big endian. Returns
391    /// `None` when endianness does not apply to this format, or if unknown.
392    pub fn is_le(&self) -> Option<bool> {
393        match unsafe { capi::pa_sample_format_is_le((*self).into()) } {
394            0 => Some(false),
395            1 => Some(true),
396            _ => None,
397        }
398    }
399
400    /// Checks if format is big endian.
401    ///
402    /// Returns `true` when the specified format is big endian, `false` if little endian. Returns
403    /// `None` when endianness does not apply to this format, or if unknown.
404    pub fn is_be(&self) -> Option<bool> {
405        match unsafe { capi::pa_sample_format_is_be((*self).into()) } {
406            0 => Some(false),
407            1 => Some(true),
408            _ => None,
409        }
410    }
411
412    /// Checks if format is native endian.
413    ///
414    /// Returns `true` when the specified format is native endian, `false` when not. Returns `None`
415    /// when endianness does not apply to the specified format, or endianness is unknown.
416    #[inline]
417    pub fn is_ne(&self) -> Option<bool> {
418        #[cfg(target_endian = "big")]
419        { Format::is_be(self) }
420        #[cfg(target_endian = "little")]
421        { Format::is_le(self) }
422    }
423
424    /// Checks if format is reverse of native endian.
425    ///
426    /// Returns `true` when the specified format is reverse endian, `false` when not. Returns `None`
427    /// when endianness does not apply to the specified format, or endianness is unknown.
428    #[inline]
429    pub fn is_re(&self) -> Option<bool> {
430        self.is_ne().and_then(|b| Some(!b))
431    }
432}