libspa/param/audio/
raw.rs

1// Copyright The pipewire-rs Contributors.
2// SPDX-License-Identifier: MIT
3
4use crate::param::audio::AudioFormat;
5use crate::pod::{Property, Value, ValueArray};
6use crate::utils::{
7    self,
8    result::{Error, SpaResult, SpaSuccess},
9};
10use std::fmt::Debug;
11
12bitflags::bitflags! {
13    #[derive(Debug, PartialEq, Eq, Clone, Copy)]
14    pub struct AudioInfoRawFlags: u32 {
15        /// the position array explicitly contains unpositioned channels.
16        const UNPOSITIONED = 1<<0;
17    }
18}
19
20/// Rust representation of [`spa_sys::spa_audio_info_raw`].
21#[repr(transparent)]
22#[derive(PartialEq, Eq, Clone, Copy)]
23pub struct AudioInfoRaw(spa_sys::spa_audio_info_raw);
24
25impl AudioInfoRaw {
26    pub fn new() -> Self {
27        Self(spa_sys::spa_audio_info_raw {
28            format: AudioFormat::Unknown.as_raw(),
29            flags: AudioInfoRawFlags::UNPOSITIONED.bits(),
30            rate: 0,
31            channels: 0,
32            position: [0; spa_sys::SPA_AUDIO_MAX_CHANNELS as usize],
33        })
34    }
35
36    pub fn set_format(&mut self, format: AudioFormat) {
37        self.0.format = format.as_raw();
38    }
39
40    pub fn format(&self) -> AudioFormat {
41        AudioFormat::from_raw(self.0.format)
42    }
43
44    pub fn set_flags(&mut self, flags: AudioInfoRawFlags) {
45        self.0.flags = flags.bits();
46    }
47
48    pub fn flags(&self) -> AudioInfoRawFlags {
49        AudioInfoRawFlags::from_bits_retain(self.0.flags)
50    }
51
52    pub fn set_rate(&mut self, rate: u32) {
53        self.0.rate = rate;
54    }
55
56    pub fn rate(&self) -> u32 {
57        self.0.rate
58    }
59
60    pub fn set_channels(&mut self, channels: u32) {
61        self.0.channels = channels;
62    }
63
64    pub fn channels(&self) -> u32 {
65        self.0.channels
66    }
67
68    pub fn set_position(&mut self, position: [u32; spa_sys::SPA_AUDIO_MAX_CHANNELS as usize]) {
69        self.0.position = position;
70        if position[0] == 0 {
71            self.0.flags |= AudioInfoRawFlags::UNPOSITIONED.bits();
72        } else {
73            self.0.flags &= AudioInfoRawFlags::UNPOSITIONED.complement().bits();
74        };
75    }
76
77    pub fn position(&self) -> [u32; spa_sys::SPA_AUDIO_MAX_CHANNELS as usize] {
78        self.0.position
79    }
80
81    /// helper function to parse format properties type
82    pub fn parse(&mut self, format: &crate::pod::Pod) -> Result<SpaSuccess, Error> {
83        let res = unsafe { spa_sys::spa_format_audio_raw_parse(format.as_raw_ptr(), &mut self.0) };
84        SpaResult::from_c(res).into_result()
85    }
86
87    /// Obtain an [`AudioInfoRaw`] from a raw `spa_audio_info_raw` variant.
88    pub fn from_raw(raw: spa_sys::spa_audio_info_raw) -> Self {
89        Self(raw)
90    }
91
92    /// Get the raw [`spa_sys::spa_audio_info_raw`] representing this `AudioInfoRaw`.
93    pub fn as_raw(&self) -> spa_sys::spa_audio_info_raw {
94        self.0
95    }
96}
97
98impl Default for AudioInfoRaw {
99    fn default() -> Self {
100        Self::new()
101    }
102}
103
104impl From<AudioInfoRaw> for Vec<Property> {
105    fn from(value: AudioInfoRaw) -> Self {
106        let mut props = Vec::with_capacity(6);
107        props.push(Property::new(
108            spa_sys::SPA_FORMAT_mediaType,
109            Value::Id(utils::Id(spa_sys::SPA_MEDIA_TYPE_audio)),
110        ));
111        props.push(Property::new(
112            spa_sys::SPA_FORMAT_mediaSubtype,
113            Value::Id(utils::Id(spa_sys::SPA_MEDIA_SUBTYPE_raw)),
114        ));
115
116        if value.format() != AudioFormat::Unknown {
117            props.push(Property::new(
118                spa_sys::SPA_FORMAT_AUDIO_format,
119                Value::Id(utils::Id(value.format().as_raw())),
120            ));
121        }
122
123        if value.rate() != 0 {
124            props.push(Property::new(
125                spa_sys::SPA_FORMAT_AUDIO_rate,
126                Value::Int(value.rate() as i32),
127            ));
128        }
129
130        if value.channels() != 0 {
131            props.push(Property::new(
132                spa_sys::SPA_FORMAT_AUDIO_channels,
133                Value::Int(value.channels() as i32),
134            ));
135            if !value.flags().contains(AudioInfoRawFlags::UNPOSITIONED) {
136                let array = value.position()[0..value.channels() as usize]
137                    .iter()
138                    .copied()
139                    .map(utils::Id)
140                    .collect();
141                props.push(Property::new(
142                    spa_sys::SPA_FORMAT_AUDIO_position,
143                    Value::ValueArray(ValueArray::Id(array)),
144                ));
145            }
146        }
147
148        props
149    }
150}
151
152impl Debug for AudioInfoRaw {
153    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
154        f.debug_struct("AudioInfoRaw")
155            .field("format", &self.format())
156            .field("flags", &self.flags())
157            .field("rate", &self.rate())
158            .field("channels", &self.channels())
159            .field("position", &self.position())
160            .finish()
161    }
162}