diff options
| author | Rubens Brandao <git@rubens.io> | 2021-04-08 22:14:29 +0200 |
|---|---|---|
| committer | Rubens Brandao <git@rubens.io> | 2021-04-08 22:14:29 +0200 |
| commit | 07d06b6946e23ecffbf5549376cf464013222274 (patch) | |
| tree | 8f26ef42b36c6dd87539c04111ce6a0ca06f7848 /mumd/src/audio | |
| parent | 7fed8f81222de570d864487605e42b5cbb023218 (diff) | |
| download | mum-07d06b6946e23ecffbf5549376cf464013222274.tar.gz | |
Create a trait and default device audio output
Also removed add/remove/clear client from audio interface, it is done on
demand now.
Diffstat (limited to 'mumd/src/audio')
| -rw-r--r-- | mumd/src/audio/output.rs | 165 |
1 files changed, 118 insertions, 47 deletions
diff --git a/mumd/src/audio/output.rs b/mumd/src/audio/output.rs index 797cf84..3664be8 100644 --- a/mumd/src/audio/output.rs +++ b/mumd/src/audio/output.rs @@ -1,10 +1,13 @@ use crate::network::VoiceStreamType; -use log::*; +use crate::audio::SAMPLE_RATE; +use crate::error::{AudioError, AudioStream}; -use cpal::{OutputCallbackInfo, Sample}; +use log::*; +use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; +use cpal::{SampleFormat, SampleRate, StreamConfig, OutputCallbackInfo, Sample}; use mumble_protocol::voice::VoicePacketPayload; use opus::Channels; -use std::collections::{HashMap, VecDeque, hash_map::Entry}; +use std::collections::{HashMap, VecDeque}; use std::ops::AddAssign; use std::sync::{Arc, Mutex}; use tokio::sync::watch; @@ -58,53 +61,13 @@ impl ClientStream { F: FnOnce(ConsumerInput) { let iter = self.buffer.iter_mut(); consumer(iter); + //remove empty Vec + self.buffer.retain(|_, v| v.is_empty()); } pub fn extend(&mut self, key: ClientStreamKey, values: &[f32]) { - match self.buffer.entry(key) { - Entry::Occupied(mut entry) => { - entry.get_mut().extend(values.iter().copied()); - } - Entry::Vacant(_) => { - match key { - None => warn!("Can't find session None"), - Some(key) => warn!("Can't find session id {}", key.1), - } - } - } - } - - pub fn add_client(&mut self, session_id: u32) { - for stream_type in [VoiceStreamType::TCP, VoiceStreamType::UDP].iter() { - match self.buffer.entry(Some((*stream_type, session_id))) { - Entry::Occupied(_) => { - warn!("Session id {} already exists", session_id); - } - Entry::Vacant(entry) => { - entry.insert(VecDeque::new()); - } - } - } - } - - pub fn remove_client(&mut self, session_id: u32) { - for stream_type in [VoiceStreamType::TCP, VoiceStreamType::UDP].iter() { - match self.buffer.entry(Some((*stream_type, session_id))) { - Entry::Occupied(entry) => { - entry.remove(); - } - Entry::Vacant(_) => { - warn!( - "Tried to remove session id {} that doesn't exist", - session_id - ); - } - } - } - } - - pub fn clear_clients(&mut self) { - self.buffer.retain(|k , _| k.is_none()); + let entry = self.buffer.entry(key).or_insert(VecDeque::new()); + entry.extend(values.iter().copied()); } } @@ -134,6 +97,114 @@ impl SaturatingAdd for u16 { } } +pub trait AudioOutputDevice { + fn play(&self) -> Result<(), AudioError>; + fn pause(&self) -> Result<(), AudioError>; + fn set_volume(&self, volume: f32); + fn get_num_channels(&self) -> usize; + fn get_client_streams(&self) -> Arc<Mutex<ClientStream>>; +} + +pub struct DefaultAudioOutputDevice { + config: StreamConfig, + _stream: cpal::Stream, + client_streams: Arc<Mutex<ClientStream>>, + volume_sender: watch::Sender<f32>, +} + +impl DefaultAudioOutputDevice { + pub fn new( + output_volume: f32, + user_volumes: Arc<Mutex<HashMap<u32, (f32, bool)>>>, + ) -> Result<Self, AudioError> { + let sample_rate = SampleRate(SAMPLE_RATE); + + let host = cpal::default_host(); + let output_device = host + .default_output_device() + .ok_or(AudioError::NoDevice(AudioStream::Output))?; + let output_supported_config = output_device + .supported_output_configs() + .map_err(|e| AudioError::NoConfigs(AudioStream::Output, e))? + .find_map(|c| { + if c.min_sample_rate() <= sample_rate && c.max_sample_rate() >= sample_rate { + Some(c) + } else { + None + } + }) + .ok_or(AudioError::NoSupportedConfig(AudioStream::Output))? + .with_sample_rate(sample_rate); + let output_supported_sample_format = output_supported_config.sample_format(); + let output_config: StreamConfig = output_supported_config.into(); + let client_streams = Arc::new(std::sync::Mutex::new(ClientStream::new(sample_rate.0, output_config.channels))); + + let err_fn = |err| error!("An error occurred on the output audio stream: {}", err); + + let (output_volume_sender, output_volume_receiver) = watch::channel::<f32>(output_volume); + + let output_stream = match output_supported_sample_format { + SampleFormat::F32 => output_device.build_output_stream( + &output_config, + curry_callback::<f32>( + Arc::clone(&client_streams), + output_volume_receiver, + user_volumes, + ), + err_fn, + ), + SampleFormat::I16 => output_device.build_output_stream( + &output_config, + curry_callback::<i16>( + Arc::clone(&client_streams), + output_volume_receiver, + user_volumes, + ), + err_fn, + ), + SampleFormat::U16 => output_device.build_output_stream( + &output_config, + curry_callback::<u16>( + Arc::clone(&client_streams), + output_volume_receiver, + user_volumes, + ), + err_fn, + ), + } + .map_err(|e| AudioError::InvalidStream(AudioStream::Output, e))?; + + Ok(Self { + config: output_config, + _stream: output_stream, + volume_sender: output_volume_sender, + client_streams, + }) + } +} + +impl AudioOutputDevice for DefaultAudioOutputDevice { + fn play(&self) -> Result<(), AudioError> { + self._stream.play().map_err(|e| AudioError::OutputPlayError(e)) + } + + fn pause(&self) -> Result<(), AudioError> { + self._stream.pause().map_err(|e| AudioError::OutputPauseError(e)) + } + + fn set_volume(&self, volume: f32) { + self.volume_sender.send(volume).unwrap(); + } + + fn get_num_channels(&self) -> usize { + self.config.channels as usize + } + + fn get_client_streams(&self) -> Arc<Mutex<ClientStream>> { + Arc::clone(&self.client_streams) + } +} + pub fn curry_callback<T: Sample + AddAssign + SaturatingAdd + std::fmt::Display>( user_bufs: Arc<Mutex<ClientStream>>, output_volume_receiver: watch::Receiver<f32>, |
