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}