librespot/playback/src/mixer/softmixer.rs
Roderick van Domburg eca505c387
Improve volume controls
This is a squashed commit featuring the following:

Connect:
- Synchronize player volume with mixer volume on playback
- Fix step size on volume up/down events
- Remove no-op mixer started/stopped logic

Playback:
- Move from `connect` to `playback` crate
- Make cubic volume control available to all mixers with `--volume-ctrl cubic`
- Normalize volumes to `[0.0..1.0]` instead of `[0..65535]` for greater precision and performance (breaking)
- Add `--volume-range` option to set dB range and control `log` and `cubic` volume control curves
- Fix `log` and `cubic` volume controls to be mute at zero volume

Alsa mixer:
- Complete rewrite (breaking)
- Query card dB range for the `log` volume control unless specified otherwise
- Query dB range from Alsa softvol (previously only from hardware)
- Use `--device` name for `--mixer-card` unless specified otherwise
- Fix consistency for `cubic` between cards that report minimum volume as mute, and cards that report some dB value
- Fix `--volume-ctrl {linear|log}` to work as expected
- Removed `--mixer-linear-volume` option; use `--volume-ctrl linear` instead
2021-05-24 15:53:32 +02:00

58 lines
1.6 KiB
Rust

use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::Arc;
use super::AudioFilter;
use super::{MappedCtrl, VolumeCtrl};
use super::{Mixer, MixerConfig};
#[derive(Clone)]
pub struct SoftMixer {
// There is no AtomicF32, so we store the f32 as bits in a u32 field.
// It's much faster than a Mutex<f32>.
volume: Arc<AtomicU32>,
volume_ctrl: VolumeCtrl,
}
impl Mixer for SoftMixer {
fn open(config: MixerConfig) -> Self {
let volume_ctrl = config.volume_ctrl;
info!("Mixing with softvol and volume control: {:?}", volume_ctrl);
Self {
volume: Arc::new(AtomicU32::new(f32::to_bits(0.5))),
volume_ctrl,
}
}
fn volume(&self) -> u16 {
let mapped_volume = f32::from_bits(self.volume.load(Ordering::Relaxed));
self.volume_ctrl.from_mapped(mapped_volume)
}
fn set_volume(&self, volume: u16) {
let mapped_volume = self.volume_ctrl.to_mapped(volume);
self.volume
.store(mapped_volume.to_bits(), Ordering::Relaxed)
}
fn get_audio_filter(&self) -> Option<Box<dyn AudioFilter + Send>> {
Some(Box::new(SoftVolumeApplier {
volume: self.volume.clone(),
}))
}
}
struct SoftVolumeApplier {
volume: Arc<AtomicU32>,
}
impl AudioFilter for SoftVolumeApplier {
fn modify_stream(&self, data: &mut [f32]) {
let volume = f32::from_bits(self.volume.load(Ordering::Relaxed));
if volume < 1.0 {
for x in data.iter_mut() {
*x = (*x as f64 * volume as f64) as f32;
}
}
}
}