mirror of
https://github.com/librespot-org/librespot.git
synced 2024-12-28 17:21:52 +00:00
eca505c387
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
58 lines
1.6 KiB
Rust
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;
|
|
}
|
|
}
|
|
}
|
|
}
|