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}