// Copyright 2022 The ChromiumOS Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // Do nothing on unix as win_audio is windows only. #![cfg(windows)] #![allow(non_upper_case_globals)] include!(concat!( env!("CARGO_MANIFEST_DIR"), "/src/r8brain_sys/bindings.rs" )); macro_rules! check_hresult { ($hr: expr, $error: expr, $msg: expr) => { if winapi::shared::winerror::FAILED($hr) { base::warn!("{}: {}", $msg, $hr); Err($error) } else { Ok($hr) } }; } pub mod intermediate_resampler_buffer; mod win_audio_impl; use std::error; use std::sync::Arc; use audio_streams::capture::AsyncCaptureBufferStream; use audio_streams::capture::NoopCaptureStream; use audio_streams::AsyncPlaybackBufferStream; use audio_streams::NoopStream; use audio_streams::NoopStreamSource; use audio_streams::NoopStreamSourceGenerator; use audio_streams::PlaybackBufferStream; use audio_streams::SampleFormat; use audio_streams::StreamSource; use audio_util::FileStreamSourceGenerator; use base::error; use base::info; use base::warn; pub use intermediate_resampler_buffer::ANDROID_CAPTURE_FRAME_SIZE_BYTES; pub use intermediate_resampler_buffer::BYTES_PER_32FLOAT; use sync::Mutex; use win_audio_impl::async_stream::WinAudioStreamSourceGenerator; pub use win_audio_impl::*; pub type BoxError = Box; pub trait WinStreamSourceGenerator: Send + Sync { fn generate(&self) -> Result, BoxError>; } impl WinStreamSourceGenerator for WinAudioStreamSourceGenerator { fn generate(&self) -> std::result::Result, BoxError> { Ok(Box::new(WinAudio::new()?)) } } impl WinStreamSourceGenerator for NoopStreamSourceGenerator { fn generate(&self) -> Result, BoxError> { Ok(Box::new(NoopStreamSource)) } } impl WinStreamSourceGenerator for FileStreamSourceGenerator { fn generate(&self) -> Result, BoxError> { unimplemented!(); } } /// Contains information about the audio engine's properties, such as its audio sample format /// and its period in frames. /// /// This does exclude whether the bit depth is in the form of floats or ints. The bit depth form /// isn't used for sample rate conversion so it's excluded. #[derive(Clone, Copy)] pub struct AudioSharedFormat { pub bit_depth: usize, pub frame_rate: usize, pub shared_audio_engine_period_in_frames: usize, pub channels: usize, // Only available for WAVEFORMATEXTENSIBLE pub channel_mask: Option, } impl AudioSharedFormat { fn get_shared_audio_engine_period_in_bytes(&self) -> usize { let frame_size_bytes = self.bit_depth * self.channels / 8; self.shared_audio_engine_period_in_frames * frame_size_bytes } } /// Implementation of StreamSource which will create the playback stream for the Windows /// audio engine. /// /// Extending the StreamSource trait will allow us to make necessary changes without modifying /// the third party audiostream library. pub trait WinAudioServer: StreamSource { fn new_playback_stream_and_get_shared_format( &mut self, num_channels: usize, format: SampleFormat, frame_rate: usize, buffer_size: usize, ) -> Result<(Arc>>, AudioSharedFormat), BoxError>; fn new_async_playback_stream_and_get_shared_format( &mut self, _num_channels: usize, _format: SampleFormat, _frame_rate: usize, _buffer_size: usize, _ex: &dyn audio_streams::AudioStreamsExecutor, _audio_client_guid: Option, ) -> Result<(Box, AudioSharedFormat), BoxError> { unimplemented!() } fn new_async_capture_stream_and_get_shared_format( &mut self, _num_channels: usize, _format: SampleFormat, _frame_rate: u32, _buffer_size: usize, _ex: &dyn audio_streams::AudioStreamsExecutor, ) -> Result<(Box, AudioSharedFormat), BoxError> { unimplemented!() } /// Evict the playback stream cache so that the audio device can be released, thus allowing /// for machines to go to sleep. fn evict_playback_stream_cache(&mut self) { unimplemented!() } /// Returns true if audio server is a noop stream. This determine if evicting a cache is worth /// doing fn is_noop_stream(&self) -> bool; } impl WinAudioServer for WinAudio { fn new_playback_stream_and_get_shared_format( &mut self, num_channels: usize, format: SampleFormat, frame_rate: usize, buffer_size: usize, ) -> Result<(Arc>>, AudioSharedFormat), BoxError> { let hr = WinAudio::co_init_once_per_thread(); let _ = check_hresult!(hr, WinAudioError::from(hr), "Co Initialized failed"); // Return the existing stream if we have one. // This is mainly to reduce audio skips caused by a buffer underrun on the guest. An // underrun causes the guest to stop the audio stream, but then start it back up when the // guest buffer is filled again. if let Some((playback_buffer_stream, audio_format)) = self.cached_playback_buffer_stream.as_ref() { info!("Reusing playback_buffer_stream."); return Ok((playback_buffer_stream.clone(), *audio_format)); } let (playback_buffer_stream, audio_shared_format): ( Arc>>, AudioSharedFormat, ) = match win_audio_impl::WinAudioRenderer::new( num_channels, format, frame_rate as u32, buffer_size, ) { Ok(renderer) => { let audio_shared_format = renderer.get_audio_shared_format(); let renderer_arc = Arc::new(Mutex::new( Box::new(renderer) as Box )); self.cached_playback_buffer_stream = Some((renderer_arc.clone(), audio_shared_format)); (renderer_arc, audio_shared_format) } Err(e) => { error!( "Failed to create WinAudioRenderer and in an unrecoverable state. Fallback to \ NoopStream with error: {}", e ); ( Arc::new(Mutex::new(Box::new(NoopStream::new( num_channels, SampleFormat::S16LE, frame_rate as u32, buffer_size, )))), AudioSharedFormat { bit_depth: 16, frame_rate, channels: 2, shared_audio_engine_period_in_frames: frame_rate / 100, channel_mask: None, }, ) } }; Ok((playback_buffer_stream, audio_shared_format)) } // TODO(b/275406212): AudioSharedFormat not used outside of this crate anymore. Clean up before // upstreaming. fn new_async_playback_stream_and_get_shared_format( &mut self, num_channels: usize, guest_bit_depth: SampleFormat, frame_rate: usize, buffer_size: usize, ex: &dyn audio_streams::AudioStreamsExecutor, audio_client_guid: Option, ) -> Result<(Box, AudioSharedFormat), BoxError> { let hr = WinAudio::co_init_once_per_thread(); let _ = check_hresult!(hr, WinAudioError::from(hr), "Co Initialized failed"); let (async_playback_buffer_stream, audio_shared_format): ( Box, AudioSharedFormat, ) = match win_audio_impl::WinAudioRenderer::new_async( num_channels, guest_bit_depth, frame_rate as u32, buffer_size, ex, audio_client_guid, ) { Ok(renderer) => { let audio_shared_format = renderer.get_audio_shared_format(); let renderer_box = Box::new(renderer) as Box; (renderer_box, audio_shared_format) } Err(e) => { error!( "Failed to create WinAudioRenderer and in an unrecoverable state. Fallback to \ NoopStream with error: {}", e ); ( Box::new(NoopStream::new( num_channels, SampleFormat::S32LE, frame_rate as u32, buffer_size, )), AudioSharedFormat { bit_depth: 32, frame_rate, channels: 2, shared_audio_engine_period_in_frames: frame_rate / 100, channel_mask: None, }, ) } }; Ok((async_playback_buffer_stream, audio_shared_format)) } // TODO(b/275406212): AudioSharedFormat not used outside of this crate anymore. Clean up before // upstreaming. fn new_async_capture_stream_and_get_shared_format( &mut self, num_channels: usize, guest_bit_depth: SampleFormat, frame_rate: u32, buffer_size: usize, ex: &dyn audio_streams::AudioStreamsExecutor, ) -> Result<(Box, AudioSharedFormat), BoxError> { let hr = WinAudio::co_init_once_per_thread(); let _ = check_hresult!(hr, WinAudioError::from(hr), "Co Initialized failed"); let (capturer, audio_shared_format): ( Box, AudioSharedFormat, ) = match WinAudioCapturer::new_async( num_channels, guest_bit_depth, frame_rate, buffer_size, ex, ) { Ok(capturer) => { let audio_shared_format = capturer.get_audio_shared_format(); (Box::new(capturer), audio_shared_format) } Err(e) => { warn!("Failed to create WinAudioCapturer. Fallback to NoopCaptureStream with error: {}", e); ( Box::new(NoopCaptureStream::new( num_channels, SampleFormat::S32LE, frame_rate, buffer_size, )), AudioSharedFormat { bit_depth: 32, frame_rate: frame_rate as usize, channels: 2, shared_audio_engine_period_in_frames: frame_rate as usize / 100, channel_mask: None, }, ) } }; Ok((capturer, audio_shared_format)) } fn evict_playback_stream_cache(&mut self) { self.cached_playback_buffer_stream = None; } fn is_noop_stream(&self) -> bool { false } } impl WinAudioServer for NoopStreamSource { fn new_playback_stream_and_get_shared_format( &mut self, num_channels: usize, format: SampleFormat, frame_rate: usize, buffer_size: usize, ) -> Result<(Arc>>, AudioSharedFormat), BoxError> { let (_, playback_buffer_stream) = self .new_playback_stream(num_channels, format, frame_rate as u32, buffer_size) .unwrap(); Ok(( Arc::new(Mutex::new(playback_buffer_stream)), AudioSharedFormat { bit_depth: 16, frame_rate, channels: 2, shared_audio_engine_period_in_frames: frame_rate / 100, channel_mask: None, }, )) } fn new_async_playback_stream_and_get_shared_format( &mut self, num_channels: usize, format: SampleFormat, frame_rate: usize, buffer_size: usize, ex: &dyn audio_streams::AudioStreamsExecutor, _audio_client_guid: Option, ) -> Result<(Box, AudioSharedFormat), BoxError> { let (_, playback_stream) = self .new_async_playback_stream(num_channels, format, frame_rate as u32, buffer_size, ex) .unwrap(); // Set shared format to be the same as the incoming audio format. let format = AudioSharedFormat { bit_depth: format.sample_bytes() * 8, frame_rate, channels: num_channels, shared_audio_engine_period_in_frames: buffer_size * format.sample_bytes(), channel_mask: None, }; Ok((playback_stream, format)) } fn is_noop_stream(&self) -> bool { true } } pub fn create_win_audio_device() -> Result { WinAudio::new() }