libpulse_binding/
channelmap.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 handing channel mapping.
15//!
16//! # Overview
17//!
18//! Channel maps provide a way to associate channels in a stream with a specific speaker position.
19//! This relieves applications of having to make sure their channel order is identical to the final
20//! output.
21//!
22//! # Initialisation
23//!
24//! A channel map consists of an array of [`Position`] values, one for each channel. This array is
25//! stored together with a channel count in a [`Map`] structure.
26//!
27//! Before filling the structure, the application must initialise it using [`Map::init()`]. There
28//! are also a number of convenience functions for standard channel mappings:
29//!
30//! * [`Map::init_mono()`]: Create a channel map with only mono audio.
31//! * [`Map::init_stereo()`]: Create a standard stereo mapping.
32//! * [`Map::init_auto()`]: Create a standard channel map for a specific number of channels.
33//! * [`Map::init_extend()`]: Similar to [`Map::init_auto()`] but synthesize a channel map if no
34//!   predefined one is known for the specified number of channels.
35
36use std::borrow::{Borrow, BorrowMut};
37use std::ffi::{CStr, CString};
38use std::borrow::Cow;
39use num_derive::{FromPrimitive, ToPrimitive};
40use crate::sample;
41
42pub use capi::pa_channel_map_def_t as MapDef;
43
44/// A mask of channel positions.
45pub type PositionMask = capi::channelmap::pa_channel_position_mask_t;
46
47/// Position mask covering all positions.
48pub const POSITION_MASK_ALL: PositionMask = 0xffffffffffffffffu64;
49
50/// A list of channel labels.
51///
52/// Note, certain aliases, specifically `Left`, `Right`, `Center` and `Subwoofer`, available in the
53/// equivalent C enum are not provided here, since Rust does not allow aliases.
54#[repr(C)]
55#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
56#[derive(FromPrimitive, ToPrimitive)]
57pub enum Position {
58    /* NOTE: This enum’s variants and variant values **must** remain identical to the `sys` crate
59       (C API) equivalent */
60
61    /// Invalid.
62    #[default]
63    Invalid = -1,
64
65    /// Mono.
66    Mono = 0,
67
68    /// Apple, Dolby call this ‘Left’.
69    FrontLeft,
70    /// Apple, Dolby call this ‘Right’.
71    FrontRight,
72    /// Apple, Dolby call this ‘Center’.
73    FrontCenter,
74
75    /// Microsoft calls this ‘Back Center’, Apple calls this ‘Center Surround’,
76    /// Dolby calls this ‘Surround Rear Center’.
77    RearCenter,
78    /// Microsoft calls this ‘Back Left’, Apple calls this ‘Left Surround’,
79    /// Dolby calls this ‘Surround Rear Left’.
80    RearLeft,
81    /// Microsoft calls this ‘Back Right’, Apple calls this ‘Right Surround’,
82    /// Dolby calls this ‘Surround Rear Right’.
83    RearRight,
84
85    /// Aka subwoofer. Microsoft calls this ‘Low Frequency’,
86    /// Apple calls this ‘LFEScreen’.
87    Lfe,
88
89    /// Apple, Dolby call this ‘Left Center’.
90    FrontLeftOfCenter,
91    /// Apple, Dolby call this ‘Right Center’.
92    FrontRightOfCenter,
93
94    /// Apple calls this ‘Left Surround Direct’,
95    /// Dolby calls this ‘Surround Left’.
96    SideLeft,
97    /// Apple calls this ‘Right Surround Direct’,
98    /// Dolby calls this ‘Surround Right’.
99    SideRight,
100
101    /// Auxillary 0.
102    Aux0,
103    /// Auxillary 1.
104    Aux1,
105    /// Auxillary 2.
106    Aux2,
107    /// Auxillary 3.
108    Aux3,
109    /// Auxillary 4.
110    Aux4,
111    /// Auxillary 5.
112    Aux5,
113    /// Auxillary 6.
114    Aux6,
115    /// Auxillary 7.
116    Aux7,
117    /// Auxillary 8.
118    Aux8,
119    /// Auxillary 9.
120    Aux9,
121    /// Auxillary 10.
122    Aux10,
123    /// Auxillary 11.
124    Aux11,
125    /// Auxillary 12.
126    Aux12,
127    /// Auxillary 13.
128    Aux13,
129    /// Auxillary 14.
130    Aux14,
131    /// Auxillary 15.
132    Aux15,
133    /// Auxillary 16.
134    Aux16,
135    /// Auxillary 17.
136    Aux17,
137    /// Auxillary 18.
138    Aux18,
139    /// Auxillary 19.
140    Aux19,
141    /// Auxillary 20.
142    Aux20,
143    /// Auxillary 21.
144    Aux21,
145    /// Auxillary 22.
146    Aux22,
147    /// Auxillary 23.
148    Aux23,
149    /// Auxillary 24.
150    Aux24,
151    /// Auxillary 25.
152    Aux25,
153    /// Auxillary 26.
154    Aux26,
155    /// Auxillary 27.
156    Aux27,
157    /// Auxillary 28.
158    Aux28,
159    /// Auxillary 29.
160    Aux29,
161    /// Auxillary 30.
162    Aux30,
163    /// Auxillary 31.
164    Aux31,
165
166    /// Apple calls this ‘Top Center Surround’.
167    TopCenter,
168
169    /// Apple calls this ‘Vertical Height Left’.
170    TopFrontLeft,
171    /// Apple calls this ‘Vertical Height Right’.
172    TopFrontRight,
173    /// Apple calls this ‘Vertical Height Center’.
174    TopFrontCenter,
175
176    /// Microsoft and Apple call this ‘Top Back Left’.
177    TopRearLeft,
178    /// Microsoft and Apple call this ‘Top Back Right’.
179    TopRearRight,
180    /// Microsoft and Apple call this ‘Top Back Center’.
181    TopRearCenter,
182}
183
184/// Check is equal to `sys` equivalent
185#[test]
186fn pos_compare_capi() {
187    assert_eq!(std::mem::size_of::<Position>(), std::mem::size_of::<capi::pa_channel_position_t>());
188    assert_eq!(std::mem::align_of::<Position>(), std::mem::align_of::<capi::pa_channel_position_t>());
189
190    // Check order and value of variants match
191    // No point checking conversions in both directions since both are a transmute
192    assert_eq!(Position::Invalid,            Position::from(capi::pa_channel_position_t::Invalid));
193    assert_eq!(Position::Mono,               Position::from(capi::pa_channel_position_t::Mono));
194    assert_eq!(Position::FrontLeft,          Position::from(capi::pa_channel_position_t::FrontLeft));
195    assert_eq!(Position::FrontRight,         Position::from(capi::pa_channel_position_t::FrontRight));
196    assert_eq!(Position::FrontCenter,        Position::from(capi::pa_channel_position_t::FrontCenter));
197    assert_eq!(Position::RearCenter,         Position::from(capi::pa_channel_position_t::RearCenter));
198    assert_eq!(Position::RearLeft,           Position::from(capi::pa_channel_position_t::RearLeft));
199    assert_eq!(Position::RearRight,          Position::from(capi::pa_channel_position_t::RearRight));
200    assert_eq!(Position::Lfe,                Position::from(capi::pa_channel_position_t::Lfe));
201    assert_eq!(Position::FrontLeftOfCenter,  Position::from(capi::pa_channel_position_t::FrontLeftOfCenter));
202    assert_eq!(Position::FrontRightOfCenter, Position::from(capi::pa_channel_position_t::FrontRightOfCenter));
203    assert_eq!(Position::SideLeft,           Position::from(capi::pa_channel_position_t::SideLeft));
204    assert_eq!(Position::SideRight,          Position::from(capi::pa_channel_position_t::SideRight));
205    assert_eq!(Position::Aux0,               Position::from(capi::pa_channel_position_t::Aux0));
206    assert_eq!(Position::Aux1,               Position::from(capi::pa_channel_position_t::Aux1));
207    assert_eq!(Position::Aux2,               Position::from(capi::pa_channel_position_t::Aux2));
208    assert_eq!(Position::Aux3,               Position::from(capi::pa_channel_position_t::Aux3));
209    assert_eq!(Position::Aux4,               Position::from(capi::pa_channel_position_t::Aux4));
210    assert_eq!(Position::Aux5,               Position::from(capi::pa_channel_position_t::Aux5));
211    assert_eq!(Position::Aux6,               Position::from(capi::pa_channel_position_t::Aux6));
212    assert_eq!(Position::Aux7,               Position::from(capi::pa_channel_position_t::Aux7));
213    assert_eq!(Position::Aux8,               Position::from(capi::pa_channel_position_t::Aux8));
214    assert_eq!(Position::Aux9,               Position::from(capi::pa_channel_position_t::Aux9));
215    assert_eq!(Position::Aux10,              Position::from(capi::pa_channel_position_t::Aux10));
216    assert_eq!(Position::Aux11,              Position::from(capi::pa_channel_position_t::Aux11));
217    assert_eq!(Position::Aux12,              Position::from(capi::pa_channel_position_t::Aux12));
218    assert_eq!(Position::Aux13,              Position::from(capi::pa_channel_position_t::Aux13));
219    assert_eq!(Position::Aux14,              Position::from(capi::pa_channel_position_t::Aux14));
220    assert_eq!(Position::Aux15,              Position::from(capi::pa_channel_position_t::Aux15));
221    assert_eq!(Position::Aux16,              Position::from(capi::pa_channel_position_t::Aux16));
222    assert_eq!(Position::Aux17,              Position::from(capi::pa_channel_position_t::Aux17));
223    assert_eq!(Position::Aux18,              Position::from(capi::pa_channel_position_t::Aux18));
224    assert_eq!(Position::Aux19,              Position::from(capi::pa_channel_position_t::Aux19));
225    assert_eq!(Position::Aux20,              Position::from(capi::pa_channel_position_t::Aux20));
226    assert_eq!(Position::Aux21,              Position::from(capi::pa_channel_position_t::Aux21));
227    assert_eq!(Position::Aux22,              Position::from(capi::pa_channel_position_t::Aux22));
228    assert_eq!(Position::Aux23,              Position::from(capi::pa_channel_position_t::Aux23));
229    assert_eq!(Position::Aux24,              Position::from(capi::pa_channel_position_t::Aux24));
230    assert_eq!(Position::Aux25,              Position::from(capi::pa_channel_position_t::Aux25));
231    assert_eq!(Position::Aux26,              Position::from(capi::pa_channel_position_t::Aux26));
232    assert_eq!(Position::Aux27,              Position::from(capi::pa_channel_position_t::Aux27));
233    assert_eq!(Position::Aux28,              Position::from(capi::pa_channel_position_t::Aux28));
234    assert_eq!(Position::Aux29,              Position::from(capi::pa_channel_position_t::Aux29));
235    assert_eq!(Position::Aux30,              Position::from(capi::pa_channel_position_t::Aux30));
236    assert_eq!(Position::Aux31,              Position::from(capi::pa_channel_position_t::Aux31));
237    assert_eq!(Position::TopCenter,          Position::from(capi::pa_channel_position_t::TopCenter));
238    assert_eq!(Position::TopFrontLeft,       Position::from(capi::pa_channel_position_t::TopFrontLeft));
239    assert_eq!(Position::TopFrontRight,      Position::from(capi::pa_channel_position_t::TopFrontRight));
240    assert_eq!(Position::TopFrontCenter,     Position::from(capi::pa_channel_position_t::TopFrontCenter));
241    assert_eq!(Position::TopRearLeft,        Position::from(capi::pa_channel_position_t::TopRearLeft));
242    assert_eq!(Position::TopRearRight,       Position::from(capi::pa_channel_position_t::TopRearRight));
243    assert_eq!(Position::TopRearCenter,      Position::from(capi::pa_channel_position_t::TopRearCenter));
244}
245
246impl From<Position> for capi::pa_channel_position_t {
247    #[inline]
248    fn from(p: Position) -> Self {
249        unsafe { std::mem::transmute(p) }
250    }
251}
252impl From<capi::pa_channel_position_t> for Position {
253    #[inline]
254    fn from(p: capi::pa_channel_position_t) -> Self {
255        unsafe { std::mem::transmute(p) }
256    }
257}
258
259/// A channel map which can be used to attach labels to specific channels of a stream.
260///
261/// These values are relevant for conversion and mixing of streams.
262#[repr(C)]
263#[derive(Debug, Copy, Clone)]
264pub struct Map {
265    /* NOTE: This struct must be directly usable by the C API, thus same attributes/layout/etc */
266    /// Number of channels mapped.
267    channels: u8,
268    /// Channel labels.
269    map: [Position; Self::CHANNELS_MAX as usize],
270}
271
272impl Borrow<[Position]> for Map {
273    fn borrow(&self) -> &[Position] {
274        &self.map[..self.channels as usize]
275    }
276}
277
278impl BorrowMut<[Position]> for Map {
279    fn borrow_mut(&mut self) -> &mut [Position] {
280        &mut self.map[..self.channels as usize]
281    }
282}
283
284/// Test size is equal to `sys` equivalent
285#[test]
286fn map_compare_capi() {
287    assert_eq!(std::mem::size_of::<Map>(), std::mem::size_of::<capi::pa_channel_map>());
288    assert_eq!(std::mem::align_of::<Map>(), std::mem::align_of::<capi::pa_channel_map>());
289}
290
291impl AsRef<capi::pa_channel_map> for Map {
292    #[inline]
293    fn as_ref(&self) -> &capi::pa_channel_map {
294        unsafe { &*(self as *const Self as *const capi::pa_channel_map) }
295    }
296}
297impl AsMut<capi::pa_channel_map> for Map {
298    #[inline]
299    fn as_mut(&mut self) -> &mut capi::pa_channel_map {
300        unsafe { &mut *(self as *mut Self as *mut capi::pa_channel_map) }
301    }
302}
303impl AsRef<Map> for capi::pa_channel_map {
304    #[inline]
305    fn as_ref(&self) -> &Map {
306        unsafe { &*(self as *const Self as *const Map) }
307    }
308}
309
310impl From<capi::pa_channel_map> for Map {
311    #[inline]
312    fn from(m: capi::pa_channel_map) -> Self {
313        unsafe { std::mem::transmute(m) }
314    }
315}
316
317impl Default for Map {
318    fn default() -> Self {
319        Self { channels: 0, map: [Position::Invalid; Self::CHANNELS_MAX as usize] }
320    }
321}
322
323impl PartialEq for Map {
324    #[inline]
325    fn eq(&self, other: &Self) -> bool {
326        unsafe { capi::pa_channel_map_equal(self.as_ref(), other.as_ref()) == 1 }
327    }
328}
329
330impl Position {
331    /// Makes a bit mask from a channel position.
332    pub const fn to_mask(self) -> PositionMask {
333        match self {
334            Position::Invalid => 0,
335            _ => (1 as PositionMask) << (self as PositionMask),
336        }
337    }
338
339    /// Gets a text label for the specified channel position.
340    pub fn to_string(pos: Self) -> Option<Cow<'static, str>> {
341        let ptr = unsafe { capi::pa_channel_position_to_string(pos.into()) };
342        match ptr.is_null() {
343            false => Some(unsafe { CStr::from_ptr(ptr).to_string_lossy() }),
344            true => None,
345        }
346    }
347
348    /// Gets a human readable text label for the specified channel position.
349    pub fn to_pretty_string(pos: Self) -> Option<String> {
350        let ptr = unsafe { capi::pa_channel_position_to_pretty_string(pos.into()) };
351        match ptr.is_null() {
352            false => Some(unsafe { CStr::from_ptr(ptr).to_string_lossy().into_owned() }),
353            true => None,
354        }
355    }
356
357    /// Creates a new instance from a string representation, as given by
358    /// [`to_string()`](Self::to_string).
359    pub fn from_string(s: &str) -> Self {
360        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
361        // as_ptr() giving dangling pointers!
362        let c_str = CString::new(s).unwrap();
363        unsafe { capi::pa_channel_position_from_string(c_str.as_ptr()).into() }
364    }
365}
366
367impl Map {
368    /// Maximum number of allowed channels.
369    pub const CHANNELS_MAX: u8 = capi::PA_CHANNELS_MAX;
370
371    /// Parses a channel position list or well-known mapping name into a channel map structure.
372    ///
373    /// This turns the output of [`print()`](Self::print) and [`to_name()`](Self::to_name) back into
374    /// a `Map`.
375    pub fn new_from_string(s: &str) -> Result<Self, ()> {
376        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
377        // as_ptr() giving dangling pointers!
378        let c_str = CString::new(s).unwrap();
379        let mut map: Self = Self::default();
380        unsafe {
381            if capi::pa_channel_map_parse((&mut map).as_mut(), c_str.as_ptr()).is_null() {
382                return Err(());
383            }
384        }
385        Ok(map)
386    }
387
388    /// Initializes the specified channel map and returns a pointer to it.
389    ///
390    /// The map will have a defined state but [`is_valid()`](Self::is_valid) will fail for it.
391    #[inline]
392    pub fn init(&mut self) -> &mut Self {
393        unsafe { capi::pa_channel_map_init(self.as_mut()) };
394        self
395    }
396
397    /// Initializes the specified channel map for monaural audio and returns a pointer to it.
398    #[inline]
399    pub fn init_mono(&mut self) -> &mut Self {
400        unsafe { capi::pa_channel_map_init_mono(self.as_mut()) };
401        self
402    }
403
404    /// Initializes the specified channel map for stereophonic audio and returns a pointer to it.
405    #[inline]
406    pub fn init_stereo(&mut self) -> &mut Self {
407        unsafe { capi::pa_channel_map_init_stereo(self.as_mut()) };
408        self
409    }
410
411    /// Initializes the specified channel map for the specified number of channels using default
412    /// labels and returns a pointer to it.
413    ///
414    /// This call will fail (return `None`) if there is no default channel map known for this
415    /// specific number of channels and mapping.
416    pub fn init_auto(&mut self, channels: u8, def: MapDef) -> Option<&mut Self> {
417        debug_assert!(channels <= Self::CHANNELS_MAX);
418        unsafe {
419            if capi::pa_channel_map_init_auto(self.as_mut(), channels as u32, def).is_null() {
420                return None;
421            }
422        }
423        Some(self)
424    }
425
426    /// Similar to [`init_auto()`](Self::init_auto) but instead of failing if no default mapping is
427    /// known with the specified parameters it will synthesize a mapping based on a known mapping
428    /// with fewer channels and fill up the rest with AUX0...AUX31 channels.
429    pub fn init_extend(&mut self, channels: u8, def: MapDef) -> &mut Self {
430        debug_assert!(channels <= Self::CHANNELS_MAX);
431        unsafe { capi::pa_channel_map_init_extend(self.as_mut(), channels as u32, def) };
432        self
433    }
434
435    /// Checks whether or not the map is considered valid.
436    #[inline]
437    pub fn is_valid(&self) -> bool {
438        unsafe { capi::pa_channel_map_valid(self.as_ref()) != 0 }
439    }
440
441    /// Gets the number of active channels.
442    #[inline]
443    pub const fn len(&self) -> u8 {
444        self.channels
445    }
446
447    /// Sets the number of active channels.
448    ///
449    /// Positions for up to [`Self::CHANNELS_MAX`] channels can be held. This sets the portion of
450    /// the internal array considered “active” and thus available for reading/writing (i.e. when
451    /// borrowing `self` as a slice).
452    ///
453    /// **Panics** if the number of channels specified is greater than [`Self::CHANNELS_MAX`].
454    #[inline]
455    pub fn set_len(&mut self, channels: u8) {
456        assert!(channels <= Self::CHANNELS_MAX);
457        self.channels = channels;
458    }
459
460    /// Gets an immutable slice of the set of “active” channels.
461    #[inline]
462    pub fn get(&self) -> &[Position] {
463        self.borrow()
464    }
465
466    /// Gets a mutable slice of the set of “active” channels.
467    #[inline]
468    pub fn get_mut(&mut self) -> &mut [Position] {
469        self.borrow_mut()
470    }
471
472    /// Makes a human readable string from the map.
473    pub fn print(&self) -> String {
474        const PRINT_MAX: usize = capi::PA_CHANNEL_MAP_SNPRINT_MAX;
475        let mut tmp = Vec::with_capacity(PRINT_MAX);
476        unsafe {
477            capi::pa_channel_map_snprint(tmp.as_mut_ptr(), PRINT_MAX, self.as_ref());
478            CStr::from_ptr(tmp.as_mut_ptr()).to_string_lossy().into_owned()
479        }
480    }
481
482    /// Checks whether or not the specified map is compatible with the specified sample spec.
483    #[inline]
484    pub fn is_compatible_with_sample_spec(&self, ss: &sample::Spec) -> bool {
485        unsafe { capi::pa_channel_map_compatible(self.as_ref(), ss.as_ref()) != 0 }
486    }
487
488    /// Checks whether every channel defined in `of` is also defined in self.
489    #[inline]
490    pub fn is_superset_of(&self, of: &Self) -> bool {
491        unsafe { capi::pa_channel_map_superset(self.as_ref(), of.as_ref()) != 0 }
492    }
493
494    /// Checks whether or not it makes sense to apply a volume “balance” with this mapping, i.e. if
495    /// there are left/right channels available.
496    #[inline]
497    pub fn can_balance(&self) -> bool {
498        unsafe { capi::pa_channel_map_can_balance(self.as_ref()) != 0 }
499    }
500
501    /// Checks whether or not it makes sense to apply a volume “fade” (i.e. “balance” between front
502    /// and rear) with this mapping, i.e. if there are front/rear channels available.
503    #[inline]
504    pub fn can_fade(&self) -> bool {
505        unsafe { capi::pa_channel_map_can_fade(self.as_ref()) != 0 }
506    }
507
508    /// Checks whether or not it makes sense to apply a volume “LFE balance” (i.e. “balance” between
509    /// LFE and non-LFE channels) with this mapping, i.e. if there are LFE and non-LFE channels
510    /// available.
511    #[inline]
512    #[cfg(any(doc, feature = "pa_v8"))]
513    #[cfg_attr(docsrs, doc(cfg(feature = "pa_v8")))]
514    pub fn can_lfe_balance(&self) -> bool {
515        unsafe { capi::pa_channel_map_can_lfe_balance(self.as_ref()) != 0 }
516    }
517
518    /// Tries to find a well-known channel mapping name for this channel mapping, i.e. “stereo”,
519    /// “surround-71” and so on. This name can be parsed with
520    /// [`new_from_string()`](Self::new_from_string).
521    pub fn to_name(&self) -> Option<Cow<'static, str>> {
522        let ptr = unsafe { capi::pa_channel_map_to_name(self.as_ref()) };
523        match ptr.is_null() {
524            false => Some(unsafe { CStr::from_ptr(ptr).to_string_lossy() }),
525            true => None,
526        }
527    }
528
529    /// Similar to [`to_name()`](Self::to_name), but returning prettier, human readable text labels,
530    /// i.e. “Stereo”, “Surround 7.1” and so on.
531    pub fn to_pretty_name(&self) -> Option<String> {
532        let ptr = unsafe { capi::pa_channel_map_to_pretty_name(self.as_ref()) };
533        match ptr.is_null() {
534            false => Some(unsafe { CStr::from_ptr(ptr).to_string_lossy().into_owned() }),
535            true => None,
536        }
537    }
538
539    /// Checks whether or not the specified channel position is available at least once in the map.
540    #[inline]
541    pub fn has_position(&self, p: Position) -> bool {
542        unsafe { capi::pa_channel_map_has_position(self.as_ref(), p.into()) != 0 }
543    }
544
545    /// Generates a bit mask from a map.
546    #[inline]
547    pub fn get_mask(&self) -> PositionMask {
548        unsafe { capi::pa_channel_map_mask(self.as_ref()) }
549    }
550}