libpulse_binding/
volume.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 volume handling.
15//!
16//! # Overview
17//!
18//! Sinks, sources, sink inputs, source outputs and samples can all have their own volumes. To deal
19//! with these, The PulseAudio library contains a number of functions that ease handling.
20//!
21//! The basic volume type in PulseAudio is the [`Volume`] type. Most of the time, applications will
22//! use the aggregated [`ChannelVolumes`] structure that can store the volume of all channels at
23//! once.
24//!
25//! Volumes commonly span between muted (0%), and normal (100%). It is possible to set volumes to
26//! higher than 100%, but clipping might occur.
27//!
28//! There is no single well-defined meaning attached to the 100% volume for a sink input. In fact,
29//! it depends on the server configuration. With flat volumes enabled, it means the maximum volume
30//! that the sound hardware is capable of, which is usually so high that you absolutely must not set
31//! sink input volume to 100% unless the user explicitly requests that (note that usually you
32//! shouldn’t set the volume anyway if the user doesn’t explicitly request it, instead, let
33//! PulseAudio decide the volume for the sink input). With flat volumes disabled the sink input
34//! volume is relative to the sink volume, so 100% sink input volume means that the sink input is
35//! played at the current sink volume level. In this case 100% is often a good default volume for a
36//! sink input, although you still should let PulseAudio decide the default volume. It is possible
37//! to figure out whether flat volume mode is in effect for a given sink by calling
38//! [`Introspector::get_sink_info_by_name()`].
39//!
40//! # Calculations
41//!
42//! The [`Volume`]s in PulseAudio are cubic in nature and applications should not perform
43//! calculations with them directly. Instead, they should be converted to and from either dB or a
44//! linear scale.
45//!
46//! The [`VolumeDB`] type represents decibel (dB) converted values, and [`VolumeLinear`], linear.
47//! The `From` trait has been implemented for your convenience, allowing such conversions.
48//!
49//! For simple multiplication, [`Volume::multiply()`] and [`ChannelVolumes::sw_multiply()`] can be
50//! used.
51//!
52//! It’s often unknown what scale hardware volumes relate to. Don’t use the above functions on sink
53//! and source volumes, unless the sink or source in question has the
54//! [`SinkFlagSet::DECIBEL_VOLUME`] or [`SourceFlagSet::DECIBEL_VOLUME`] flag set. The conversion
55//! functions are rarely needed anyway, most of the time it’s sufficient to treat all volumes as
56//! opaque with a range from [`Volume::MUTED`] \(0%) to [`Volume::NORMAL`] \(100%).
57//!
58//! [`Introspector::get_sink_info_by_name()`]: crate::context::introspect::Introspector::get_sink_info_by_name
59//! [`SinkFlagSet::DECIBEL_VOLUME`]: crate::def::SinkFlagSet::DECIBEL_VOLUME
60//! [`SourceFlagSet::DECIBEL_VOLUME`]: crate::def::SourceFlagSet::DECIBEL_VOLUME
61
62use std::borrow::{Borrow, BorrowMut};
63use std::ffi::CStr;
64use std::ptr::null;
65use crate::sample;
66use crate::channelmap::{Map, Position, PositionMask, POSITION_MASK_ALL};
67
68/// Software volume expressed as an integer.
69#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
70pub struct Volume(pub capi::pa_volume_t);
71
72impl Default for Volume {
73    fn default() -> Self {
74        Self::NORMAL
75    }
76}
77
78/// Software volume expressed in decibels (dBs).
79#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
80pub struct VolumeDB(pub f64);
81
82impl Default for VolumeDB {
83    fn default() -> Self {
84        VolumeDB(0.0)
85    }
86}
87
88/// Software volume expressed as linear factor.
89#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
90pub struct VolumeLinear(pub f64);
91
92impl Default for VolumeLinear {
93    fn default() -> Self {
94        VolumeLinear(0.0)
95    }
96}
97
98/// A structure encapsulating a per-channel volume
99#[repr(C)]
100#[derive(Debug, Copy, Clone, Default)]
101pub struct ChannelVolumes {
102    /* NOTE: This struct must be directly usable by the C API, thus same attributes/layout/etc */
103    /// Number of channels.
104    channels: u8,
105    /// Per-channel volume.
106    values: [Volume; Self::CHANNELS_MAX as usize],
107}
108
109/// Test size is equal to `sys` equivalent
110#[test]
111fn set_compare_capi() {
112    assert_eq!(std::mem::size_of::<ChannelVolumes>(), std::mem::size_of::<capi::pa_cvolume>());
113    assert_eq!(std::mem::align_of::<ChannelVolumes>(), std::mem::align_of::<capi::pa_cvolume>());
114}
115
116impl Borrow<[Volume]> for ChannelVolumes {
117    fn borrow(&self) -> &[Volume] {
118        &self.values[..self.channels as usize]
119    }
120}
121
122impl BorrowMut<[Volume]> for ChannelVolumes {
123    fn borrow_mut(&mut self) -> &mut [Volume] {
124        &mut self.values[..self.channels as usize]
125    }
126}
127
128impl AsRef<capi::pa_cvolume> for ChannelVolumes {
129    #[inline]
130    fn as_ref(&self) -> &capi::pa_cvolume {
131        unsafe { &*(self as *const Self as *const capi::pa_cvolume) }
132    }
133}
134impl AsMut<capi::pa_cvolume> for ChannelVolumes {
135    #[inline]
136    fn as_mut(&mut self) -> &mut capi::pa_cvolume {
137        unsafe { &mut *(self as *mut Self as *mut capi::pa_cvolume) }
138    }
139}
140
141impl From<capi::pa_cvolume> for ChannelVolumes {
142    #[inline]
143    fn from(cv: capi::pa_cvolume) -> Self {
144        unsafe { std::mem::transmute(cv) }
145    }
146}
147
148impl PartialEq for ChannelVolumes {
149    #[inline]
150    fn eq(&self, other: &Self) -> bool {
151        unsafe { capi::pa_cvolume_equal(self.as_ref(), other.as_ref()) != 0 }
152    }
153}
154
155impl PartialEq<Volume> for ChannelVolumes {
156    /// Returns `true` if the volume of all channels are equal to the specified value.
157    #[inline]
158    fn eq(&self, v: &Volume) -> bool {
159        unsafe { capi::pa_cvolume_channels_equal_to(self.as_ref(), v.0) != 0 }
160    }
161}
162
163/// Converts a decibel value to a volume (amplitude, not power).
164///
165/// This is only valid for software volumes!
166impl From<VolumeDB> for Volume {
167    #[inline]
168    fn from(v: VolumeDB) -> Self {
169        Volume(unsafe { capi::pa_sw_volume_from_dB(v.0) })
170    }
171}
172/// Converts a volume to a decibel value (amplitude, not power).
173///
174/// This is only valid for software volumes!
175impl From<Volume> for VolumeDB {
176    #[inline]
177    fn from(v: Volume) -> Self {
178        VolumeDB(unsafe { capi::pa_sw_volume_to_dB(v.0) })
179    }
180}
181
182/// Converts a linear factor to a volume.
183///
184/// `0.0` and less is muted while `1.0` is [`Volume::NORMAL`].
185///
186/// This is only valid for software volumes!
187///
188/// [`Volume::NORMAL`]: struct.Volume.html#associatedconstant.NORMAL
189impl From<VolumeLinear> for Volume {
190    #[inline]
191    fn from(v: VolumeLinear) -> Self {
192        Volume(unsafe { capi::pa_sw_volume_from_linear(v.0) })
193    }
194}
195/// Converts a volume to a linear factor.
196///
197/// This is only valid for software volumes!
198impl From<Volume> for VolumeLinear {
199    #[inline]
200    fn from(v: Volume) -> Self {
201        VolumeLinear(unsafe { capi::pa_sw_volume_to_linear(v.0) })
202    }
203}
204
205/// Converts a linear factor to a decibel value (amplitude, not power).
206///
207/// `0.0` and less is muted while `1.0` is [`Volume::NORMAL`].
208///
209/// This is only valid for software volumes!
210///
211/// [`Volume::NORMAL`]: struct.Volume.html#associatedconstant.NORMAL
212impl From<VolumeLinear> for VolumeDB {
213    #[inline]
214    fn from(v: VolumeLinear) -> Self {
215        VolumeDB::from(Volume::from(v))
216    }
217}
218/// Converts a decibel value (amplitude, not power) to a linear factor.
219///
220/// This is only valid for software volumes!
221impl From<VolumeDB> for VolumeLinear {
222    #[inline]
223    fn from(v: VolumeDB) -> Self {
224        VolumeLinear::from(Volume::from(v))
225    }
226}
227
228impl VolumeLinear {
229    /// Is a muted volume level.
230    #[inline]
231    pub fn is_muted(&self) -> bool {
232        self.0 <= 0.0
233    }
234
235    /// Is a “normal” volume level.
236    #[inline]
237    pub fn is_normal(&self) -> bool {
238        self.0 == 1.0
239    }
240}
241
242impl Volume {
243    /// A “normal” volume level.
244    pub const NORMAL:  Self = Self(capi::PA_VOLUME_NORM);
245    /// A muted volume level.
246    pub const MUTED:   Self = Self(capi::PA_VOLUME_MUTED);
247    /// A maximum volume level.
248    pub const MAX:     Self = Self(capi::PA_VOLUME_MAX);
249    /// An invalid volume level.
250    pub const INVALID: Self = Self(capi::PA_VOLUME_INVALID);
251
252    /// Is a muted volume level.
253    #[inline]
254    pub fn is_muted(&self) -> bool {
255        *self == Self::MUTED
256    }
257
258    /// Is a “normal” volume level.
259    #[inline]
260    pub fn is_normal(&self) -> bool {
261        *self == Self::NORMAL
262    }
263
264    /// Is a maximum volume level.
265    #[inline]
266    pub fn is_max(&self) -> bool {
267        *self == Self::MAX
268    }
269
270    /// Get the recommended maximum volume to show in user facing UIs.
271    ///
272    /// Note: UIs should deal gracefully with volumes greater than this value and not cause feedback
273    /// loops etc. - i.e. if the volume is more than this, the UI should not limit it and push the
274    /// limited value back to the server.
275    #[inline]
276    pub fn ui_max() -> Self {
277        Volume(capi::pa_volume_ui_max())
278    }
279
280    /// Checks if volume is valid.
281    #[inline]
282    pub const fn is_valid(&self) -> bool {
283        capi::pa_volume_is_valid(self.0)
284    }
285
286    /// Clamps volume to the permitted range.
287    #[inline]
288    pub fn clamp(&mut self) {
289        self.0 = capi::pa_clamp_volume(self.0)
290    }
291
292    /// Multiplies two software volumes, returning the result.
293    ///
294    /// This uses [`Volume::NORMAL`] as neutral element of multiplication.
295    ///
296    /// This is only valid for software volumes!
297    ///
298    /// [`Volume::NORMAL`]: struct.Volume.html#associatedconstant.NORMAL
299    #[inline]
300    pub fn multiply(a: Self, b: Self) -> Self {
301        Volume(unsafe { capi::pa_sw_volume_multiply(a.0, b.0) })
302    }
303
304    /// Divides two software volumes, returning the result.
305    ///
306    /// This uses [`Volume::NORMAL`] as neutral element of division. If a division by zero is tried
307    /// the result will be `0`.
308    ///
309    /// This is only valid for software volumes!
310    ///
311    /// [`Volume::NORMAL`]: struct.Volume.html#associatedconstant.NORMAL
312    #[inline]
313    pub fn divide(a: Self, b: Self) -> Self {
314        Volume(unsafe { capi::pa_sw_volume_divide(a.0, b.0) })
315    }
316
317    /// Pretty prints a volume.
318    pub fn print(&self) -> String {
319        const PRINT_MAX: usize = capi::PA_VOLUME_SNPRINT_MAX;
320        let mut tmp = Vec::with_capacity(PRINT_MAX);
321        unsafe {
322            capi::pa_volume_snprint(tmp.as_mut_ptr(), PRINT_MAX, self.0);
323            CStr::from_ptr(tmp.as_mut_ptr()).to_string_lossy().into_owned()
324        }
325    }
326
327    /// Pretty prints a volume but showing dB values.
328    pub fn print_db(&self) -> String {
329        const PRINT_DB_MAX: usize = capi::PA_SW_VOLUME_SNPRINT_DB_MAX;
330        let mut tmp = Vec::with_capacity(PRINT_DB_MAX);
331        unsafe {
332            capi::pa_sw_volume_snprint_dB(tmp.as_mut_ptr(), PRINT_DB_MAX, self.0);
333            CStr::from_ptr(tmp.as_mut_ptr()).to_string_lossy().into_owned()
334        }
335    }
336
337    /// Pretty prints a volume in a verbose way.
338    ///
339    /// The volume is printed in several formats: the raw volume value, percentage, and if
340    /// `print_db` is true, also the dB value.
341    pub fn print_verbose(&self, print_db: bool) -> String {
342        const PRINT_VERBOSE_MAX: usize = capi::PA_VOLUME_SNPRINT_VERBOSE_MAX;
343        let mut tmp = Vec::with_capacity(PRINT_VERBOSE_MAX);
344        unsafe {
345            capi::pa_volume_snprint_verbose(tmp.as_mut_ptr(), PRINT_VERBOSE_MAX, self.0,
346                print_db as i32);
347            CStr::from_ptr(tmp.as_mut_ptr()).to_string_lossy().into_owned()
348        }
349    }
350}
351
352impl std::fmt::Display for Volume {
353    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
354        write!(f, "{}", &self.print())
355    }
356}
357
358impl VolumeDB {
359    /// Minus Infinity.
360    ///
361    /// This floor value is used / can be used, when using converting between integer software
362    /// volume and decibel (dB, floating point) software volume.
363    pub const MINUS_INFINITY: Self = Self(capi::PA_DECIBEL_MININFTY);
364}
365
366impl ChannelVolumes {
367    /// Maximum number of allowed channels.
368    pub const CHANNELS_MAX: u8 = capi::PA_CHANNELS_MAX;
369
370    /// Initializes the specified volume and return a pointer to it.
371    ///
372    /// The sample spec will have a defined state but [`is_valid()`](Self::is_valid) will fail for
373    /// it.
374    #[inline]
375    pub fn init(&mut self) -> &Self {
376        unsafe { capi::pa_cvolume_init(self.as_mut()) };
377        self
378    }
379
380    /// Checks if the `ChannelVolumes` structure is valid.
381    #[inline]
382    pub fn is_valid(&self) -> bool {
383        unsafe { capi::pa_cvolume_valid(self.as_ref()) != 0 }
384    }
385
386    /// Gets the number of active channels.
387    #[inline]
388    pub fn len(&self) -> u8 {
389        self.channels
390    }
391
392    /// Sets the number of active channels.
393    ///
394    /// Volumes for up to [`Self::CHANNELS_MAX`] channels can be held. This sets the portion of
395    /// the internal array considered “active” and thus available for reading/writing (i.e. when
396    /// borrowing `self` as a slice).
397    ///
398    /// **Panics** if the number of channels specified is greater than [`Self::CHANNELS_MAX`].
399    #[inline]
400    pub fn set_len(&mut self, channels: u8) {
401        assert!(channels <= Self::CHANNELS_MAX);
402        self.channels = channels;
403    }
404
405    /// Gets an immutable slice of the set of “active” channels.
406    #[inline]
407    pub fn get(&self) -> &[Volume] {
408        self.borrow()
409    }
410
411    /// Gets a mutable slice of the set of “active” channels.
412    #[inline]
413    pub fn get_mut(&mut self) -> &mut [Volume] {
414        self.borrow_mut()
415    }
416
417    /// Sets the volume of the specified number of channels to the supplied volume.
418    #[inline]
419    pub fn set(&mut self, channels: u8, v: Volume) -> &Self {
420        unsafe { capi::pa_cvolume_set(self.as_mut(), channels as u32, v.0) };
421        self
422    }
423
424    /// Sets the volume of the first n channels to [`Volume::NORMAL`].
425    #[inline]
426    pub fn reset(&mut self, channels: u8) -> &Self {
427        self.set(channels, Volume::NORMAL)
428    }
429
430    /// Sets the volume of the first n channels to [`Volume::MUTED`].
431    #[inline]
432    pub fn mute(&mut self, channels: u8) -> &Self {
433        self.set(channels, Volume::MUTED)
434    }
435
436    /// Checks if all channels are muted.
437    #[inline]
438    pub fn is_muted(&self) -> bool {
439        self.eq(&Volume::MUTED)
440    }
441
442    /// Checks if all channels are at normal volume level.
443    #[inline]
444    pub fn is_norm(&self) -> bool {
445        self.eq(&Volume::NORMAL)
446    }
447
448    /// Gets the average volume of all channels.
449    #[inline]
450    pub fn avg(&self) -> Volume {
451        Volume(unsafe { capi::pa_cvolume_avg(self.as_ref()) })
452    }
453
454    /// Returns the average volume of all channels that are included in the specified channel map
455    /// with the specified channel position mask.
456    ///
457    /// If no channel is selected the returned value will be [`Volume::MUTED`]. If `mask` is `None`,
458    /// has the same effect as passing [`POSITION_MASK_ALL`].
459    #[inline]
460    pub fn avg_mask(&self, cm: &Map, mask: Option<PositionMask>) -> Volume {
461        let mask_actual = mask.unwrap_or(POSITION_MASK_ALL);
462        Volume(unsafe { capi::pa_cvolume_avg_mask(self.as_ref(), cm.as_ref(), mask_actual) })
463    }
464
465    /// Gets the maximum volume of all channels.
466    #[inline]
467    pub fn max(&self) -> Volume {
468        Volume(unsafe { capi::pa_cvolume_max(self.as_ref()) })
469    }
470
471    /// Gets the maximum volume of all channels that are included in the specified channel map
472    /// with the specified channel position mask.
473    ///
474    /// If no channel is selected the returned value will be [`Volume::MUTED`]. If `mask` is `None`,
475    /// has the same effect as passing [`POSITION_MASK_ALL`].
476    #[inline]
477    pub fn max_mask(&self, cm: &Map, mask: Option<PositionMask>) -> Volume {
478        let mask_actual = mask.unwrap_or(POSITION_MASK_ALL);
479        Volume(unsafe { capi::pa_cvolume_max_mask(self.as_ref(), cm.as_ref(), mask_actual) })
480    }
481
482    /// Gets the minimum volume of all channels.
483    #[inline]
484    pub fn min(&self) -> Volume {
485        Volume(unsafe { capi::pa_cvolume_min(self.as_ref()) })
486    }
487
488    /// Gets the minimum volume of all channels that are included in the specified channel map
489    /// with the specified channel position mask.
490    ///
491    /// If no channel is selected the returned value will be [`Volume::MUTED`]. If `mask` is `None`,
492    /// has the same effect as passing [`POSITION_MASK_ALL`].
493    #[inline]
494    pub fn min_mask(&self, cm: &Map, mask: Option<PositionMask>) -> Volume {
495        let mask_actual = mask.unwrap_or(POSITION_MASK_ALL);
496        Volume(unsafe { capi::pa_cvolume_min_mask(self.as_ref(), cm.as_ref(), mask_actual) })
497    }
498
499    /// Multiplies two per-channel volumes.
500    ///
501    /// If `with` is `None`, multiplies with itself. This is only valid for software volumes!
502    /// Returns pointer to self.
503    #[inline]
504    pub fn sw_multiply(&mut self, with: Option<&Self>) -> &mut Self {
505        unsafe { capi::pa_sw_cvolume_multiply(self.as_mut(), self.as_mut(),
506            with.unwrap_or(self).as_ref()) };
507        self
508    }
509
510    /// Multiplies a per-channel volume with a scalar volume.
511    ///
512    /// This is only valid for software volumes! Returns pointer to self.
513    #[inline]
514    pub fn sw_multiply_scalar(&mut self, with: Volume) -> &mut Self {
515        unsafe { capi::pa_sw_cvolume_multiply_scalar(self.as_mut(), self.as_ref(), with.0) };
516        self
517    }
518
519    /// Divides two per-channel volumes.
520    ///
521    /// If `with` is `None`, divides with itself. This is only valid for software volumes! Returns
522    /// pointer to self.
523    #[inline]
524    pub fn sw_divide(&mut self, with: Option<&Self>) -> &mut Self {
525        unsafe { capi::pa_sw_cvolume_divide(self.as_mut(), self.as_mut(),
526            with.unwrap_or(self).as_ref()) };
527        self
528    }
529
530    /// Divides a per-channel volume by a scalar volume.
531    ///
532    /// This is only valid for software volumes! Returns pointer to self.
533    #[inline]
534    pub fn sw_divide_scalar(&mut self, with: Volume) -> &mut Self {
535        unsafe { capi::pa_sw_cvolume_divide_scalar(self.as_mut(), self.as_ref(), with.0) };
536        self
537    }
538
539    /// Remaps a volume from one channel mapping to a different channel mapping.
540    ///
541    /// Returns pointer to self.
542    #[inline]
543    pub fn remap(&mut self, from: &Map, to: &Map) -> &mut Self {
544        unsafe { capi::pa_cvolume_remap(self.as_mut(), from.as_ref(), to.as_ref()) };
545        self
546    }
547
548    /// Checks if the specified volume is compatible with the specified sample spec.
549    #[inline]
550    pub fn is_compatible_with_ss(&self, ss: &sample::Spec) -> bool {
551        unsafe { capi::pa_cvolume_compatible(self.as_ref(), ss.as_ref()) != 0 }
552    }
553
554    /// Checks if the specified volume is compatible with the specified channel map.
555    #[inline]
556    pub fn is_compatible_with_cm(&self, cm: &Map) -> bool {
557        unsafe { capi::pa_cvolume_compatible_with_channel_map(self.as_ref(), cm.as_ref()) != 0 }
558    }
559
560    /// Calculates a ‘balance’ value for the specified volume with the specified channel map.
561    ///
562    /// The return value will range from `-1.0` (left) to `+1.0` (right). If no balance value is
563    /// applicable to this channel map the return value will always be `0.0`. See
564    /// [`Map::can_balance()`].
565    #[inline]
566    pub fn get_balance(&self, map: &Map) -> f32 {
567        unsafe { capi::pa_cvolume_get_balance(self.as_ref(), map.as_ref()) }
568    }
569
570    /// Adjusts the ‘balance’ value for the specified volume with the specified channel map.
571    ///
572    /// The balance is a value between `-1.0` and `+1.0`. This operation might not be reversible!
573    /// Also, after this call [`get_balance()`](Self::get_balance) is not guaranteed to actually
574    /// return the requested balance value (e.g. when the input volume was zero anyway for all
575    /// channels). If no balance value is applicable to this channel map the volume will not be
576    /// modified. See [`Map::can_balance()`].
577    ///
578    /// Returns pointer to self, or `None` on error.
579    #[inline]
580    pub fn set_balance(&mut self, map: &Map, new_balance: f32) -> Option<&mut Self> {
581        let ptr = unsafe { capi::pa_cvolume_set_balance(self.as_mut(), map.as_ref(), new_balance) };
582        match ptr.is_null() {
583            false => Some(self),
584            true => None,
585        }
586    }
587
588    /// Calculates a ‘fade’ value (i.e. ‘balance’ between front and rear) for the specified volume
589    /// with the specified channel map.
590    ///
591    /// The return value will range from -1.0f (rear) to +1.0f (left). If no fade value is
592    /// applicable to this channel map the return value will always be `0.0`. See
593    /// [`Map::can_fade()`].
594    #[inline]
595    pub fn get_fade(&self, map: &Map) -> f32 {
596        unsafe { capi::pa_cvolume_get_fade(self.as_ref(), map.as_ref()) }
597    }
598
599    /// Adjusts the ‘fade’ value (i.e. ‘balance’ between front and rear) for the specified volume
600    /// with the specified channel map.
601    ///
602    /// The balance is a value between `-1.0` and `+1.0`. This operation might not be reversible!
603    /// Also, after this call [`get_fade()`](Self::get_fade) is not guaranteed to actually return
604    /// the requested fade value (e.g. when the input volume was zero anyway for all channels). If
605    /// no fade value is applicable to this channel map the volume will not be modified. See
606    /// [`Map::can_fade()`].
607    ///
608    /// Returns pointer to self, or `None` on error.
609    #[inline]
610    pub fn set_fade(&mut self, map: &Map, new_fade: f32) -> Option<&mut Self> {
611        let ptr = unsafe { capi::pa_cvolume_set_fade(self.as_mut(), map.as_ref(), new_fade) };
612        match ptr.is_null() {
613            false => Some(self),
614            true => None,
615        }
616    }
617
618    /// Calculates a ‘lfe balance’ value for the specified volume with the specified channel map.
619    ///
620    /// The return value will range from `-1.0` (no lfe) to `+1.0` (only lfe), where `0.0` is
621    /// balanced. If no value is applicable to this channel map the return value will always be
622    /// `0.0`. See [`Map::can_lfe_balance()`].
623    #[inline]
624    #[cfg(any(doc, feature = "pa_v8"))]
625    #[cfg_attr(docsrs, doc(cfg(feature = "pa_v8")))]
626    pub fn get_lfe_balance(&self, map: &Map) -> f32 {
627        unsafe { capi::pa_cvolume_get_lfe_balance(self.as_ref(), map.as_ref()) }
628    }
629
630    /// Adjusts the ‘LFE balance’ value for the specified volume with the specified channel map.
631    ///
632    /// The balance is a value between `-1.0` (no lfe) and `+1.0` (only lfe). This operation might
633    /// not be reversible! Also, after this call [`get_lfe_balance()`] is not guaranteed to actually
634    /// return the requested value (e.g. when the input volume was zero anyway for all channels). If
635    /// no lfe balance value is applicable to this channel map the volume will not be modified. See
636    /// [`Map::can_lfe_balance()`].
637    ///
638    /// Returns pointer to self, or `None` on error.
639    ///
640    /// [`get_lfe_balance()`]: Self::get_lfe_balance
641    #[inline]
642    #[cfg(any(doc, feature = "pa_v8"))]
643    #[cfg_attr(docsrs, doc(cfg(feature = "pa_v8")))]
644    pub fn set_lfe_balance(&mut self, map: &Map, new_balance: f32) -> Option<&mut Self> {
645        let ptr =
646            unsafe { capi::pa_cvolume_set_lfe_balance(self.as_mut(), map.as_ref(), new_balance) };
647        match ptr.is_null() {
648            false => Some(self),
649            true => None,
650        }
651    }
652
653    /// Scales so that the maximum volume of all channels equals `max`.
654    ///
655    /// The proportions between the channel volumes are kept.
656    ///
657    /// Returns pointer to self, or `None` on error.
658    #[inline]
659    pub fn scale(&mut self, max: Volume) -> Option<&mut Self> {
660        let ptr = unsafe { capi::pa_cvolume_scale(self.as_mut(), max.0) };
661        match ptr.is_null() {
662            false => Some(self),
663            true => None,
664        }
665    }
666
667    /// Scales so that the maximum volume of all channels selected via `cm`/`mask` equals `max`.
668    ///
669    /// This also modifies the volume of those channels that are unmasked. The proportions between
670    /// the channel volumes are kept.
671    ///
672    /// If `mask` is `None`, has the same effect as passing [`POSITION_MASK_ALL`].
673    ///
674    /// Returns pointer to self, or `None` on error.
675    #[inline]
676    pub fn scale_mask(&mut self, max: Volume, cm: &mut Map, mask: Option<PositionMask>)
677        -> Option<&mut Self>
678    {
679        let mask_actual = mask.unwrap_or(POSITION_MASK_ALL);
680        let ptr =
681            unsafe { capi::pa_cvolume_scale_mask(self.as_mut(), max.0, cm.as_ref(), mask_actual) };
682        match ptr.is_null() {
683            false => Some(self),
684            true => None,
685        }
686    }
687
688    /// Sets the passed volume to all channels at the specified channel position.
689    ///
690    /// Returns `None` if either invalid data was provided, or if there is no channel at the
691    /// position specified. You can check if a channel map includes a specific position by calling
692    /// [`Map::has_position()`]. On success, returns pointer to self.
693    #[inline]
694    pub fn set_position(&mut self, map: &Map, p: Position, v: Volume) -> Option<&mut Self> {
695        // Note: C function returns NULL on invalid data or no channel at position specified (no
696        // change needed). We could ignore failure and always return self ptr, but it does not seem
697        // ideal to leave callers unaware should they be passing in invalid data.
698        let ptr =
699            unsafe { capi::pa_cvolume_set_position(self.as_mut(), map.as_ref(), p.into(), v.0) };
700        match ptr.is_null() {
701            false => Some(self),
702            true => None,
703        }
704    }
705
706    /// Gets the maximum volume of all channels at the specified channel position.
707    ///
708    /// Will return `0` if there is no channel at the position specified. You can check if a channel
709    /// map includes a specific position by calling [`Map::has_position()`].
710    #[inline]
711    pub fn get_position(&self, map: &Map, p: Position) -> Volume {
712        Volume(unsafe { capi::pa_cvolume_get_position(self.as_ref(), map.as_ref(), p.into()) })
713    }
714
715    /// Merges one set of channel volumes with another.
716    ///
717    /// The channel count is set to the minimum between that of self and that of `with`. Only this
718    /// number of channels are processed. For each channel processed, volume is set to the greatest
719    /// of the values from self and from `with`. I.e if one set has three channels and the other has
720    /// two, the number of channels will be set to two, and only the first two channels will be
721    /// compared, with the greatest values of these two channels being stored. The third channel in
722    /// the one set will be ignored.
723    ///
724    /// Returns pointer to self, or `None` on error.
725    #[inline]
726    pub fn merge(&mut self, with: &Self) -> Option<&mut Self> {
727        let ptr = unsafe { capi::pa_cvolume_merge(self.as_mut(), self.as_ref(), with.as_ref()) };
728        match ptr.is_null() {
729            false => Some(self),
730            true => None,
731        }
732    }
733
734    /// Increases the volume passed in by `inc`, but not exceeding `limit`.
735    ///
736    /// The proportions between the channels are kept.
737    ///
738    /// Returns pointer to self, or `None` on error.
739    #[inline]
740    pub fn inc_clamp(&mut self, inc: Volume, limit: Volume) -> Option<&mut Self> {
741        let ptr = unsafe { capi::pa_cvolume_inc_clamp(self.as_mut(), inc.0, limit.0) };
742        match ptr.is_null() {
743            false => Some(self),
744            true => None,
745        }
746    }
747
748    /// Increases the volume passed in by `inc`.
749    ///
750    /// The proportions between the channels are kept.
751    ///
752    /// Returns pointer to self, or `None` on error.
753    #[inline]
754    pub fn increase(&mut self, inc: Volume) -> Option<&mut Self> {
755        let ptr = unsafe { capi::pa_cvolume_inc(self.as_mut(), inc.0) };
756        match ptr.is_null() {
757            false => Some(self),
758            true => None,
759        }
760    }
761
762    /// Decreases the volume passed in by `dec`.
763    ///
764    /// The proportions between the channels are kept.
765    ///
766    /// Returns pointer to self, or `None` on error.
767    #[inline]
768    pub fn decrease(&mut self, dec: Volume) -> Option<&mut Self> {
769        let ptr = unsafe { capi::pa_cvolume_dec(self.as_mut(), dec.0) };
770        match ptr.is_null() {
771            false => Some(self),
772            true => None,
773        }
774    }
775
776    /// Pretty prints a volume structure.
777    pub fn print(&self) -> String {
778        const PRINT_MAX: usize = capi::PA_CVOLUME_SNPRINT_MAX;
779        let mut tmp = Vec::with_capacity(PRINT_MAX);
780        unsafe {
781            capi::pa_cvolume_snprint(tmp.as_mut_ptr(), PRINT_MAX, self.as_ref());
782            CStr::from_ptr(tmp.as_mut_ptr()).to_string_lossy().into_owned()
783        }
784    }
785
786    /// Pretty prints a volume structure but show dB values.
787    pub fn print_db(&self) -> String {
788        const PRINT_DB_MAX: usize = capi::PA_SW_CVOLUME_SNPRINT_DB_MAX;
789        let mut tmp = Vec::with_capacity(PRINT_DB_MAX);
790        unsafe {
791            capi::pa_sw_cvolume_snprint_dB(tmp.as_mut_ptr(), PRINT_DB_MAX, self.as_ref());
792            CStr::from_ptr(tmp.as_mut_ptr()).to_string_lossy().into_owned()
793        }
794    }
795
796    /// Pretty prints a volume structure in a verbose way.
797    ///
798    /// The volume for each channel is printed in several formats: the raw volume value,
799    /// percentage, and if `print_db` is non-zero, also the dB value. If `map` is provided, the
800    /// channel names will be printed.
801    pub fn print_verbose(&self, map: Option<&Map>, print_db: bool) -> String {
802        const PRINT_VERBOSE_MAX: usize = capi::PA_CVOLUME_SNPRINT_VERBOSE_MAX;
803
804        let p_map = map.map_or(null::<capi::pa_channel_map>(), |m| m.as_ref());
805
806        let mut tmp = Vec::with_capacity(PRINT_VERBOSE_MAX);
807        unsafe {
808            capi::pa_cvolume_snprint_verbose(tmp.as_mut_ptr(), PRINT_VERBOSE_MAX, self.as_ref(),
809                p_map, print_db as i32);
810            CStr::from_ptr(tmp.as_mut_ptr()).to_string_lossy().into_owned()
811        }
812    }
813}
814
815impl std::fmt::Display for ChannelVolumes {
816    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
817        write!(f, "{}", &self.print())
818    }
819}