libpulse_binding/
format.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//! Utility functions for handling a stream or sink format.
15//!
16//! # Note
17//!
18//! Clients using an [`Info`] structure must remember to at least set the encoding attribute, which
19//! can be done through the [`Info::set_encoding()`] method.
20
21use std::os::raw::{c_char, c_void};
22use std::ffi::{CStr, CString};
23use std::ptr::{null, null_mut};
24use std::borrow::Cow;
25use num_derive::{FromPrimitive, ToPrimitive};
26use crate::{sample, channelmap};
27use crate::error::PAErr;
28use crate::proplist::{Proplist, ProplistInternal};
29
30pub use capi::pa_prop_type_t as PropType;
31
32/// Represents the type of encoding used in a stream or accepted by a sink.
33#[repr(C)]
34#[non_exhaustive]
35#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
36#[derive(FromPrimitive, ToPrimitive)]
37#[allow(non_camel_case_types)]
38pub enum Encoding {
39    /* NOTE: This enum’s variants and variant values **must** remain identical to the `sys` crate
40       (C API) equivalent */
41    /// Any encoding format, PCM or compressed.
42    Any,
43    /// Any PCM format.
44    PCM,
45    /// AC3 data encapsulated in IEC 61937 header/padding.
46    AC3_IEC61937,
47    /// EAC3 data encapsulated in IEC 61937 header/padding.
48    EAC3_IEC61937,
49    /// MPEG-1 or MPEG-2 (Part 3, not AAC) data encapsulated in IEC 61937 header/padding.
50    MPEG_IEC61937,
51    /// DTS data encapsulated in IEC 61937 header/padding.
52    DTS_IEC61937,
53    /// MPEG-2 AAC data encapsulated in IEC 61937 header/padding.
54    MPEG2_AAC_IEC61937,
55    /// Dolby TrueHD data encapsulated in IEC 61937 header/padding.
56    #[cfg(any(doc, feature = "pa_v13"))]
57    #[cfg_attr(docsrs, doc(cfg(feature = "pa_v13")))]
58    TRUEHD_IEC61937,
59    /// DTS-HD Master Audio encapsulated in IEC 61937 header/padding.
60    #[cfg(any(doc, feature = "pa_v13"))]
61    #[cfg_attr(docsrs, doc(cfg(feature = "pa_v13")))]
62    DTSHD_IEC61937,
63
64    /// Represents an invalid encoding.
65    #[default]
66    Invalid = -1,
67}
68
69/// Check is equal to `sys` equivalent
70#[test]
71fn enc_compare_capi() {
72    assert_eq!(std::mem::size_of::<Encoding>(), std::mem::size_of::<capi::pa_encoding_t>());
73    assert_eq!(std::mem::align_of::<Encoding>(), std::mem::align_of::<capi::pa_encoding_t>());
74
75    // Check order and value of variants match
76    // No point checking conversions in both directions since both are a transmute
77    assert_eq!(Encoding::Any,                Encoding::from(capi::pa_encoding_t::Any));
78    assert_eq!(Encoding::PCM,                Encoding::from(capi::pa_encoding_t::PCM));
79    assert_eq!(Encoding::AC3_IEC61937,       Encoding::from(capi::pa_encoding_t::AC3_IEC61937));
80    assert_eq!(Encoding::EAC3_IEC61937,      Encoding::from(capi::pa_encoding_t::EAC3_IEC61937));
81    assert_eq!(Encoding::MPEG_IEC61937,      Encoding::from(capi::pa_encoding_t::MPEG_IEC61937));
82    assert_eq!(Encoding::DTS_IEC61937,       Encoding::from(capi::pa_encoding_t::DTS_IEC61937));
83    assert_eq!(Encoding::MPEG2_AAC_IEC61937, Encoding::from(capi::pa_encoding_t::MPEG2_AAC_IEC61937));
84    #[cfg(any(doc, feature = "pa_v13"))]
85    assert_eq!(Encoding::TRUEHD_IEC61937,    Encoding::from(capi::pa_encoding_t::TRUEHD_IEC61937));
86    #[cfg(any(doc, feature = "pa_v13"))]
87    assert_eq!(Encoding::DTSHD_IEC61937,     Encoding::from(capi::pa_encoding_t::DTSHD_IEC61937));
88    assert_eq!(Encoding::Invalid,            Encoding::from(capi::pa_encoding_t::Invalid));
89}
90
91impl From<Encoding> for capi::pa_encoding_t {
92    #[inline]
93    fn from(e: Encoding) -> Self {
94        unsafe { std::mem::transmute(e) }
95    }
96}
97impl From<capi::pa_encoding_t> for Encoding {
98    #[inline]
99    fn from(e: capi::pa_encoding_t) -> Self {
100        unsafe { std::mem::transmute(e) }
101    }
102}
103
104/// Represents the format of data provided in a stream or processed by a sink.
105pub struct Info {
106    /// The actual C object.
107    pub(crate) ptr: *mut InfoInternal,
108    /// A nicely wrapped copy of it’s property list pointer, for convenience.
109    properties: Proplist,
110    /// Used to avoid freeing the internal object when used as a weak wrapper in callbacks.
111    weak: bool,
112}
113
114unsafe impl Send for Info {}
115unsafe impl Sync for Info {}
116
117/// The raw C structure (with documentation).
118#[repr(C)]
119pub(crate) struct InfoInternal {
120    /* NOTE: This struct must be directly usable by the C API, thus same attributes/layout/etc */
121    /// The encoding used for the format.
122    pub encoding: Encoding,
123    /// Additional encoding-specific properties such as sample rate, bitrate, etc.
124    pub list: *mut ProplistInternal,
125}
126
127/// Test size is equal to `sys` equivalent (duplicated here for different documentation)
128#[test]
129fn info_compare_capi() {
130    assert_eq!(std::mem::size_of::<InfoInternal>(), std::mem::size_of::<capi::pa_format_info>());
131    assert_eq!(std::mem::align_of::<InfoInternal>(), std::mem::align_of::<capi::pa_format_info>());
132}
133
134impl std::fmt::Debug for Info {
135    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
136        write!(f, "Info {{ encoding: {:?}, properties: {:?} }}", self.get_encoding(),
137            *self.get_properties())
138    }
139}
140
141impl Encoding {
142    /// Returns a printable string representing the given encoding type.
143    pub fn to_string(e: Self) -> Option<Cow<'static, str>> {
144        let ptr = unsafe { capi::pa_encoding_to_string(e.into()) };
145        match ptr.is_null() {
146            false => Some(unsafe { CStr::from_ptr(ptr).to_string_lossy() }),
147            true => None,
148        }
149    }
150
151    /// Converts a string of the form returned by [`to_string()`](Self::to_string) back to an
152    /// `Encoding`.
153    #[cfg(any(doc, feature = "pa_v12"))]
154    #[cfg_attr(docsrs, doc(cfg(feature = "pa_v12")))]
155    pub fn from_string(encoding: &str) -> Self {
156        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
157        // as_ptr() giving dangling pointers!
158        let c_enc = CString::new(encoding).unwrap();
159        unsafe { capi::pa_encoding_from_string(c_enc.as_ptr()).into() }
160    }
161}
162
163impl Info {
164    /// Allocates a new `Info` structure.
165    ///
166    /// Clients must initialise at least the encoding field themselves. Returns `None` on failure.
167    pub fn new() -> Option<Self> {
168        let ptr = unsafe { capi::pa_format_info_new() };
169        match ptr.is_null() {
170            false => Some(Self::from_raw(ptr as *mut InfoInternal)),
171            true => None,
172        }
173    }
174
175    /// Parses a human-readable string of the form generated by [`print()`](Self::print) into an
176    /// `Info` structure.
177    ///
178    /// Returns `None` on failure.
179    pub fn new_from_string(s: &str) -> Option<Self> {
180        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
181        // as_ptr() giving dangling pointers!
182        let c_str = CString::new(s).unwrap();
183        let ptr = unsafe { capi::pa_format_info_from_string(c_str.as_ptr()) };
184        match ptr.is_null() {
185            false => Some(Self::from_raw(ptr as *mut InfoInternal)),
186            true => None,
187        }
188    }
189
190    /// Utility function to take a [`Spec`] and generate the corresponding `Info`.
191    ///
192    /// Note that if you want the server to choose some of the stream parameters, for example the
193    /// sample rate, so that they match the device parameters, then you shouldn’t use this function.
194    /// In order to allow the server to choose a parameter value, that parameter must be left
195    /// unspecified in the `Info` object, and this function always specifies all parameters. An
196    /// exception is the channel map: if you pass `None` for the channel map, then the channel map
197    /// will be left unspecified, allowing the server to choose it.
198    ///
199    /// Returns `None` on failure.
200    ///
201    /// [`Spec`]: crate::sample::Spec
202    pub fn new_from_sample_spec(ss: &sample::Spec, map: Option<&channelmap::Map>) -> Option<Self> {
203        let p_map = map.map_or(null::<capi::pa_channel_map>(), |m| m.as_ref());
204        let ptr = unsafe { capi::pa_format_info_from_sample_spec(ss.as_ref(), p_map) };
205        match ptr.is_null() {
206            false => Some(Self::from_raw(ptr as *mut InfoInternal)),
207            true => None,
208        }
209    }
210
211    /// Creates a new `Info` from an existing [`InfoInternal`] pointer.
212    pub(crate) fn from_raw(ptr: *mut InfoInternal) -> Self {
213        assert_eq!(false, ptr.is_null());
214        // Note, yes, this should be weak; the ‘free’ function for a format info object free’s its
215        // own proplist!
216        let pl = Proplist::from_raw_weak(unsafe { (*ptr).list });
217        Self { ptr: ptr, properties: pl, weak: false }
218    }
219
220    /// Creates a new `Info` from an existing [`InfoInternal`] pointer.
221    ///
222    /// This is the ‘weak’ version, which avoids destroying the internal object when dropped.
223    pub(crate) fn from_raw_weak(ptr: *mut InfoInternal) -> Self {
224        assert_eq!(false, ptr.is_null());
225        let pl = Proplist::from_raw_weak(unsafe { (*ptr).list });
226        Self { ptr: ptr, properties: pl, weak: true }
227    }
228
229    /// Returns a new `Info` struct representing the same format.
230    ///
231    /// If this is called on a ‘weak’ instance, a non-weak object is returned.
232    #[inline]
233    pub(crate) fn to_owned(&self) -> Self {
234        let ptr = unsafe { capi::pa_format_info_copy(self.ptr as *const capi::pa_format_info) };
235        Self::from_raw(ptr as *mut InfoInternal)
236    }
237
238    /// Checks whether the `Info` structure is valid.
239    #[inline]
240    pub fn is_valid(&self) -> bool {
241        unsafe { capi::pa_format_info_valid(self.ptr as *const capi::pa_format_info) != 0 }
242    }
243
244    /// Checks whether the `Info` structure represents a PCM (i.e. uncompressed data) format.
245    #[inline]
246    pub fn is_pcm(&self) -> bool {
247        unsafe { capi::pa_format_info_is_pcm(self.ptr as *const capi::pa_format_info) != 0 }
248    }
249
250    /// Checks whether the format represented by self is a subset of the format represented by
251    /// `with`.
252    ///
253    /// This means that `with` must have all the fields that self does, but the reverse need not be
254    /// true. This is typically expected to be used to check if a stream’s format is compatible with
255    /// a given sink. In such a case, self would be the sink’s format and `with` would be the
256    /// streams.
257    #[inline]
258    pub fn is_compatible_with(&self, with: &Self) -> bool {
259        unsafe { capi::pa_format_info_is_compatible(self.ptr as *const capi::pa_format_info,
260            with.ptr as *const capi::pa_format_info) != 0 }
261    }
262
263    /// Gets a human-readable string representing the given format.
264    pub fn print(&self) -> String {
265        const PRINT_MAX: usize = capi::PA_FORMAT_INFO_SNPRINT_MAX;
266        let mut tmp = Vec::with_capacity(PRINT_MAX);
267        unsafe {
268            capi::pa_format_info_snprint(tmp.as_mut_ptr(), PRINT_MAX,
269                self.ptr as *const capi::pa_format_info);
270            CStr::from_ptr(tmp.as_mut_ptr()).to_string_lossy().into_owned()
271        }
272    }
273
274    /// Utility function to generate a [`Spec`] and [`Map`] corresponding to a given `Info`.
275    ///
276    /// The conversion for PCM formats is straight-forward. For non-PCM formats, if there is a fixed
277    /// size-time conversion (i.e. all IEC61937-encapsulated formats), a “fake” sample spec whose
278    /// size-time conversion corresponds to this format is provided and the channel map argument is
279    /// ignored. For formats with variable size-time conversion, this function will fail.
280    ///
281    /// [`Spec`]: crate::sample::Spec
282    /// [`Map`]: crate::channelmap::Map
283    pub fn to_sample_spec(&self, ss: &mut sample::Spec, map: &mut channelmap::Map)
284        -> Result<(), PAErr>
285    {
286        match unsafe { capi::pa_format_info_to_sample_spec(
287            self.ptr as *const capi::pa_format_info, ss.as_mut(), map.as_mut()) }
288        {
289            0 => Ok(()),
290            e => Err(PAErr(e)),
291        }
292    }
293
294    /// Gets the encoding.
295    #[inline]
296    pub fn get_encoding(&self) -> Encoding {
297        unsafe { (*self.ptr).encoding }
298    }
299
300    /// Sets the encoding attribute.
301    #[inline]
302    pub fn set_encoding(&mut self, encoding: Encoding) {
303        unsafe { (*self.ptr).encoding = encoding };
304    }
305
306    /// Gets an immutable reference to the property list.
307    #[inline]
308    pub fn get_properties(&self) -> &Proplist {
309        &self.properties
310    }
311
312    /// Gets a mutable reference to the property list.
313    #[inline]
314    pub fn get_properties_mut(&mut self) -> &mut Proplist {
315        &mut self.properties
316    }
317
318    /// Gets the type of property key.
319    pub fn get_prop_type(&self, key: &str) -> PropType {
320        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
321        // as_ptr() giving dangling pointers!
322        let c_key = CString::new(key).unwrap();
323        unsafe { capi::pa_format_info_get_prop_type(self.ptr as *const capi::pa_format_info,
324            c_key.as_ptr()) }
325    }
326
327    /// Gets an integer property.
328    pub fn get_prop_int(&self, key: &str) -> Result<i32, PAErr> {
329        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
330        // as_ptr() giving dangling pointers!
331        let mut i: i32 = 0;
332        let c_key = CString::new(key).unwrap();
333        match unsafe { capi::pa_format_info_get_prop_int(self.ptr as *const capi::pa_format_info,
334            c_key.as_ptr(), &mut i) }
335        {
336            0 => Ok(i),
337            e => Err(PAErr(e)),
338        }
339    }
340
341    /// Gets an integer range property. On success, returns min-max tuple.
342    pub fn get_prop_int_range(&self, key: &str) -> Result<(i32, i32), PAErr> {
343        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
344        // as_ptr() giving dangling pointers!
345        let mut min: i32 = 0;
346        let mut max: i32 = 0;
347        let c_key = CString::new(key).unwrap();
348        match unsafe { capi::pa_format_info_get_prop_int_range(
349            self.ptr as *const capi::pa_format_info, c_key.as_ptr(), &mut min, &mut max) }
350        {
351            0 => Ok((min, max)),
352            e => Err(PAErr(e)),
353        }
354    }
355
356    /// Gets an integer array property.
357    ///
358    /// Returns `None` on error.
359    pub fn get_prop_int_array(&self, key: &str) -> Option<Vec<i32>> {
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_key = CString::new(key).unwrap();
363        let mut count: i32 = 0;
364        let mut p_ints = null_mut::<i32>();
365        let result = unsafe { capi::pa_format_info_get_prop_int_array(
366            self.ptr as *const capi::pa_format_info, c_key.as_ptr(), &mut p_ints, &mut count) };
367        if result != 0 {
368            return None;
369        }
370        // Clone each int in the array
371        let mut values: Vec<i32> = Vec::with_capacity(count as usize);
372        for i in 0..count {
373            values.push(unsafe { *p_ints.offset(i as isize) });
374        }
375        // Free the PA allocated array
376        unsafe { capi::pa_xfree(p_ints as *mut c_void) };
377        // Return vector of ints
378        Some(values)
379    }
380
381    /// Gets a string property.
382    pub fn get_prop_string(&self, key: &str) -> Option<String> {
383        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
384        // as_ptr() giving dangling pointers!
385        let c_key = CString::new(key).unwrap();
386        let mut p_str = null_mut::<c_char>();
387        let result = unsafe { capi::pa_format_info_get_prop_string(
388            self.ptr as *const capi::pa_format_info, c_key.as_ptr(), &mut p_str) };
389        if result != 0 || p_str.is_null() {
390            return None;
391        }
392        unsafe {
393            let ret = Some(CStr::from_ptr(p_str).to_string_lossy().into_owned());
394            capi::pa_xfree(p_str as *mut c_void);
395            ret
396        }
397    }
398
399    /// Gets a string array property.
400    pub fn get_prop_string_array(&self, key: &str) -> Option<Vec<String>> {
401        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
402        // as_ptr() giving dangling pointers!
403        let c_key = CString::new(key).unwrap();
404        let mut count: i32 = 0;
405        let mut pp_str = null_mut::<*mut c_char>();
406        let result = unsafe { capi::pa_format_info_get_prop_string_array(
407            self.ptr as *const capi::pa_format_info, c_key.as_ptr(), &mut pp_str, &mut count) };
408        if result != 0 || pp_str.is_null() {
409            return None;
410        }
411        // Clone each string in the array to owned String
412        let mut values: Vec<String> = Vec::with_capacity(count as usize);
413        for i in 0..count {
414            let p_str = unsafe { *pp_str.offset(i as isize) };
415            if !p_str.is_null() {
416                values.push(unsafe { CStr::from_ptr(p_str).to_string_lossy().into_owned() });
417            }
418        }
419        // Free all PA internally allocated strings
420        unsafe { capi::pa_format_info_free_string_array(pp_str, count) };
421        // Return vector of Strings
422        Some(values)
423    }
424
425    /// Gets the sample format stored in the format info.
426    ///
427    /// Returns `Err` if the sample format property is not set at all, or is invalid.
428    #[cfg(any(doc, feature = "pa_v13"))]
429    #[cfg_attr(docsrs, doc(cfg(feature = "pa_v13")))]
430    pub fn get_sample_format(&self) -> Result<crate::sample::Format, PAErr> {
431        let mut sf: capi::pa_sample_format_t = capi::PA_SAMPLE_INVALID;
432        match unsafe { capi::pa_format_info_get_sample_format(
433            self.ptr as *const capi::pa_format_info, &mut sf) }
434        {
435            0 => Ok(crate::sample::Format::from(sf)),
436            e => Err(PAErr(e)),
437        }
438    }
439
440    /// Gets the sample rate stored in the format info.
441    ///
442    /// Returns `Err` if the sample rate property is not set at all, or is invalid.
443    #[cfg(any(doc, feature = "pa_v13"))]
444    #[cfg_attr(docsrs, doc(cfg(feature = "pa_v13")))]
445    pub fn get_rate(&self) -> Result<u32, PAErr> {
446        let mut rate: u32 = 0;
447        match unsafe { capi::pa_format_info_get_rate(self.ptr as *const capi::pa_format_info,
448            &mut rate) }
449        {
450            0 => Ok(rate),
451            e => Err(PAErr(e)),
452        }
453    }
454
455    /// Gets the channel count stored in the format info.
456    ///
457    /// Returns `Err` if the channels property is not set at all, or is invalid.
458    #[cfg(any(doc, feature = "pa_v13"))]
459    #[cfg_attr(docsrs, doc(cfg(feature = "pa_v13")))]
460    pub fn get_channel_count(&self) -> Result<u8, PAErr> {
461        let mut channels: u8 = 0;
462        match unsafe { capi::pa_format_info_get_channels(self.ptr as *const capi::pa_format_info,
463            &mut channels) }
464        {
465            0 => Ok(channels),
466            e => Err(PAErr(e)),
467        }
468    }
469
470    /// Gets the channel map stored in the format info.
471    ///
472    /// Returns `Err` if the channel map property is not set at all, or if the string form it is
473    /// stored in within the property set fails to parse successfully.
474    #[cfg(any(doc, feature = "pa_v13"))]
475    #[cfg_attr(docsrs, doc(cfg(feature = "pa_v13")))]
476    pub fn get_channel_map(&self) -> Result<crate::channelmap::Map, PAErr> {
477        // Returning the entire struct written to here may be a little less efficient than taking a
478        // pointer like the C API, but we avoid the possibility of leaving the user with an
479        // incomplete struct; parsing from string form is inefficient anyway, and we thus should not
480        // expect this to be done frequently anyway.
481        let mut map: capi::pa_channel_map = capi::pa_channel_map::default();
482        match unsafe { capi::pa_format_info_get_channel_map(
483            self.ptr as *const capi::pa_format_info, &mut map) }
484        {
485            0 => Ok(crate::channelmap::Map::from(map)),
486            e => Err(PAErr(e)),
487        }
488    }
489
490    /// Sets an integer property.
491    pub fn set_prop_int(&mut self, key: &str, value: i32) {
492        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
493        // as_ptr() giving dangling pointers!
494        let c_key = CString::new(key).unwrap();
495        unsafe { capi::pa_format_info_set_prop_int(self.ptr as *mut capi::pa_format_info,
496            c_key.as_ptr(), value); }
497    }
498
499    /// Sets a property with a list of integer values.
500    ///
501    /// Panics if length of array is larger than `i32::MAX`.
502    pub fn set_prop_int_array(&mut self, key: &str, values: &[i32]) {
503        assert!(values.len() <= i32::MAX as usize);
504        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
505        // as_ptr() giving dangling pointers!
506        let c_key = CString::new(key).unwrap();
507        unsafe { capi::pa_format_info_set_prop_int_array(self.ptr as *mut capi::pa_format_info,
508            c_key.as_ptr(), values.as_ptr(), values.len() as i32); }
509    }
510
511    /// Sets a property which can have any value in a given integer range.
512    pub fn set_prop_int_range(&mut self, key: &str, min: i32, max: i32) {
513        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
514        // as_ptr() giving dangling pointers!
515        let c_key = CString::new(key).unwrap();
516        unsafe { capi::pa_format_info_set_prop_int_range(self.ptr as *mut capi::pa_format_info,
517            c_key.as_ptr(), min, max); }
518    }
519
520    /// Sets a string property.
521    pub fn set_prop_string(&mut self, key: &str, value: &str) {
522        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
523        // as_ptr() giving dangling pointers!
524        let c_key = CString::new(key).unwrap();
525        let c_value = CString::new(value).unwrap();
526        unsafe { capi::pa_format_info_set_prop_string(self.ptr as *mut capi::pa_format_info,
527            c_key.as_ptr(), c_value.as_ptr()); }
528    }
529
530    /// Sets a property with a list of string values.
531    ///
532    /// Panics if length of array is larger than `i32::MAX`.
533    pub fn set_prop_string_array(&mut self, key: &str, values: &[&str]) {
534        assert!(values.len() <= i32::MAX as usize);
535        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
536        // as_ptr() giving dangling pointers!
537        let c_key = CString::new(key).unwrap();
538        let mut c_values: Vec<CString> = Vec::with_capacity(values.len());
539        for v in values {
540            c_values.push(CString::new(*v).unwrap());
541        }
542
543        // Capture array of pointers to the above CString values
544        let mut c_value_ptrs: Vec<*const c_char> = Vec::with_capacity(c_values.len());
545        for v in &c_values {
546            c_value_ptrs.push(v.as_ptr());
547        }
548        unsafe {
549            capi::pa_format_info_set_prop_string_array(self.ptr as *mut capi::pa_format_info,
550                c_key.as_ptr(), c_value_ptrs.as_ptr(), c_value_ptrs.len() as i32);
551        }
552    }
553
554    /// Convenience method to set the sample format as a property.
555    ///
556    /// Note for PCM: If the sample format is left unspecified in the `Info` object, then the server
557    /// will select the stream sample format. In that case the stream sample format will most likely
558    /// match the device sample format, meaning that sample format conversion will be avoided.
559    #[inline]
560    pub fn set_sample_format(&mut self, sf: sample::Format) {
561        unsafe { capi::pa_format_info_set_sample_format(self.ptr as *mut capi::pa_format_info,
562            sf.into()); }
563    }
564
565    /// Convenience method to set the sampling rate as a property.
566    ///
567    /// Note for PCM: If the sample rate is left unspecified in the `Info` object, then the server
568    /// will select the stream sample rate. In that case the stream sample rate will most likely
569    /// match the device sample rate, meaning that sample rate conversion will be avoided.
570    #[inline]
571    pub fn set_rate(&mut self, rate: i32) {
572        unsafe { capi::pa_format_info_set_rate(self.ptr as *mut capi::pa_format_info, rate) }
573    }
574
575    /// Convenience method to set the number of channels as a property.
576    ///
577    /// Note for PCM: If the channel count is left unspecified in the `Info` object, then the server
578    /// will select the stream channel count. In that case the stream channel count will most likely
579    /// match the device channel count, meaning that up/downmixing will be avoided.
580    ///
581    /// Panics if `channels` is larger than can be submitted to the C function.
582    #[inline]
583    pub fn set_channels(&mut self, channels: u32) {
584        assert!(channels <= std::i32::MAX as u32);
585        unsafe { capi::pa_format_info_set_channels(self.ptr as *mut capi::pa_format_info,
586            channels as i32) }
587    }
588
589    /// Convenience method to set the channel map as a property.
590    ///
591    /// Note for PCM: If the channel map is left unspecified in the `Info` object, then the server
592    /// will select the stream channel map. In that case the stream channel map will most likely
593    /// match the device channel map, meaning that remixing will be avoided.
594    #[inline]
595    pub fn set_channel_map(&mut self, map: &channelmap::Map) {
596        unsafe { capi::pa_format_info_set_channel_map(self.ptr as *mut capi::pa_format_info,
597            map.as_ref()) }
598    }
599}
600
601impl Drop for Info {
602    fn drop(&mut self) {
603        if !self.weak {
604            unsafe { capi::pa_format_info_free(self.ptr as *mut capi::pa_format_info) };
605        }
606    }
607}
608
609impl Clone for Info {
610    /// Returns a new `Info` struct and representing the same format.
611    ///
612    /// If this is called on a ‘weak’ instance, a non-weak object is returned.
613    fn clone(&self) -> Self {
614        self.to_owned()
615    }
616}