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