1use 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
44pub type PositionMask = capi::channelmap::pa_channel_position_mask_t;
46
47pub const POSITION_MASK_ALL: PositionMask = 0xffffffffffffffffu64;
49
50#[repr(C)]
55#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
56#[derive(FromPrimitive, ToPrimitive)]
57pub enum Position {
58 #[default]
63 Invalid = -1,
64
65 Mono = 0,
67
68 FrontLeft,
70 FrontRight,
72 FrontCenter,
74
75 RearCenter,
78 RearLeft,
81 RearRight,
84
85 Lfe,
88
89 FrontLeftOfCenter,
91 FrontRightOfCenter,
93
94 SideLeft,
97 SideRight,
100
101 Aux0,
103 Aux1,
105 Aux2,
107 Aux3,
109 Aux4,
111 Aux5,
113 Aux6,
115 Aux7,
117 Aux8,
119 Aux9,
121 Aux10,
123 Aux11,
125 Aux12,
127 Aux13,
129 Aux14,
131 Aux15,
133 Aux16,
135 Aux17,
137 Aux18,
139 Aux19,
141 Aux20,
143 Aux21,
145 Aux22,
147 Aux23,
149 Aux24,
151 Aux25,
153 Aux26,
155 Aux27,
157 Aux28,
159 Aux29,
161 Aux30,
163 Aux31,
165
166 TopCenter,
168
169 TopFrontLeft,
171 TopFrontRight,
173 TopFrontCenter,
175
176 TopRearLeft,
178 TopRearRight,
180 TopRearCenter,
182}
183
184#[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 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#[repr(C)]
263#[derive(Debug, Copy, Clone)]
264pub struct Map {
265 channels: u8,
268 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]
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 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 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 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 pub fn from_string(s: &str) -> Self {
360 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 pub const CHANNELS_MAX: u8 = capi::PA_CHANNELS_MAX;
370
371 pub fn new_from_string(s: &str) -> Result<Self, ()> {
376 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 #[inline]
392 pub fn init(&mut self) -> &mut Self {
393 unsafe { capi::pa_channel_map_init(self.as_mut()) };
394 self
395 }
396
397 #[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 #[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 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 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 #[inline]
437 pub fn is_valid(&self) -> bool {
438 unsafe { capi::pa_channel_map_valid(self.as_ref()) != 0 }
439 }
440
441 #[inline]
443 pub const fn len(&self) -> u8 {
444 self.channels
445 }
446
447 #[inline]
455 pub fn set_len(&mut self, channels: u8) {
456 assert!(channels <= Self::CHANNELS_MAX);
457 self.channels = channels;
458 }
459
460 #[inline]
462 pub fn get(&self) -> &[Position] {
463 self.borrow()
464 }
465
466 #[inline]
468 pub fn get_mut(&mut self) -> &mut [Position] {
469 self.borrow_mut()
470 }
471
472 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 #[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 #[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 #[inline]
497 pub fn can_balance(&self) -> bool {
498 unsafe { capi::pa_channel_map_can_balance(self.as_ref()) != 0 }
499 }
500
501 #[inline]
504 pub fn can_fade(&self) -> bool {
505 unsafe { capi::pa_channel_map_can_fade(self.as_ref()) != 0 }
506 }
507
508 #[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 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 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 #[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 #[inline]
547 pub fn get_mask(&self) -> PositionMask {
548 unsafe { capi::pa_channel_map_mask(self.as_ref()) }
549 }
550}