1 // Copyright 2022 The ChromiumOS Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 use std::mem::MaybeUninit; 6 7 use async_trait::async_trait; 8 use audio_streams::AsyncBufferCommit; 9 use audio_streams::AsyncPlaybackBuffer; 10 use audio_streams::AsyncPlaybackBufferStream; 11 use audio_streams::BoxError; 12 use audio_streams::NoopStream; 13 use audio_streams::NoopStreamControl; 14 use audio_streams::SampleFormat; 15 use audio_streams::StreamControl; 16 use audio_streams::StreamSource; 17 use audio_streams::StreamSourceGenerator; 18 use base::error; 19 use base::warn; 20 21 use crate::DeviceRenderer; 22 use crate::RenderError; 23 use crate::WinAudio; 24 use crate::WinAudioRenderer; 25 26 pub struct WinAudioStreamSourceGenerator {} 27 28 impl StreamSourceGenerator for WinAudioStreamSourceGenerator { generate(&self) -> std::result::Result<Box<dyn StreamSource>, BoxError>29 fn generate(&self) -> std::result::Result<Box<dyn StreamSource>, BoxError> { 30 Ok(Box::new(WinAudio::new()?)) 31 } 32 } 33 34 impl WinAudio { new_async_playback_stream_helper( num_channels: usize, _format: SampleFormat, frame_rate: u32, buffer_size: usize, ex: &dyn audio_streams::AudioStreamsExecutor, ) -> Result< ( Box<dyn StreamControl>, Box<dyn audio_streams::AsyncPlaybackBufferStream>, ), BoxError, >35 pub(super) fn new_async_playback_stream_helper( 36 num_channels: usize, 37 _format: SampleFormat, 38 frame_rate: u32, 39 buffer_size: usize, 40 ex: &dyn audio_streams::AudioStreamsExecutor, 41 ) -> Result< 42 ( 43 Box<dyn StreamControl>, 44 Box<dyn audio_streams::AsyncPlaybackBufferStream>, 45 ), 46 BoxError, 47 > { 48 let hr = WinAudio::co_init_once_per_thread(); 49 let _ = check_hresult!(hr, RenderError::from(hr), "Co Initialized failed"); 50 51 let playback_buffer_stream: Box<dyn AsyncPlaybackBufferStream> = 52 match WinAudioRenderer::new_async(num_channels, frame_rate, buffer_size, ex) { 53 Ok(renderer) => Box::new(renderer), 54 Err(e) => { 55 warn!( 56 "Failed to create WinAudioRenderer. Fallback to NoopStream with error: {}", 57 e 58 ); 59 Box::new(NoopStream::new( 60 num_channels, 61 SampleFormat::S16LE, 62 frame_rate, 63 buffer_size, 64 )) 65 } 66 }; 67 68 Ok((Box::new(NoopStreamControl::new()), playback_buffer_stream)) 69 } 70 } 71 72 impl WinAudioRenderer { 73 /// Constructor to allow for async audio backend. new_async( num_channels: usize, frame_rate: u32, incoming_buffer_size_in_frames: usize, ex: &dyn audio_streams::AudioStreamsExecutor, ) -> Result<Self, RenderError>74 pub fn new_async( 75 num_channels: usize, 76 frame_rate: u32, 77 incoming_buffer_size_in_frames: usize, 78 ex: &dyn audio_streams::AudioStreamsExecutor, 79 ) -> Result<Self, RenderError> { 80 let device = Self::create_device_renderer_and_log_time( 81 num_channels, 82 frame_rate, 83 incoming_buffer_size_in_frames, 84 Some(ex), 85 )?; 86 Ok(Self { 87 device, 88 num_channels, 89 frame_rate, // guest frame rate 90 incoming_buffer_size_in_frames, // from the guest` 91 }) 92 } 93 } 94 95 #[async_trait(?Send)] 96 impl AsyncPlaybackBufferStream for WinAudioRenderer { next_playback_buffer<'a>( &'a mut self, _ex: &dyn audio_streams::AudioStreamsExecutor, ) -> Result<audio_streams::AsyncPlaybackBuffer<'a>, BoxError>97 async fn next_playback_buffer<'a>( 98 &'a mut self, 99 _ex: &dyn audio_streams::AudioStreamsExecutor, 100 ) -> Result<audio_streams::AsyncPlaybackBuffer<'a>, BoxError> { 101 for _ in 0..Self::MAX_REATTACH_TRIES { 102 match self.device.async_next_win_buffer().await { 103 Ok(_) => { 104 return self 105 .device 106 .async_playback_buffer() 107 .map_err(|e| Box::new(e) as _) 108 } 109 // If the audio device was disconnected, set up whatever is now the default device 110 // and loop to try again. 111 Err(RenderError::DeviceInvalidated) => { 112 warn!("Audio device disconnected, switching to new default device"); 113 self.reattach_device()?; 114 } 115 Err(e) => return Err(Box::new(e)), 116 } 117 } 118 error!("Unable to attach to a working audio device, giving up"); 119 Err(Box::new(RenderError::DeviceInvalidated)) 120 } 121 } 122 123 impl DeviceRenderer { 124 /// Similiar to `next_win_buffer`, this is the async version that will return a wraper around 125 /// the WASAPI buffer. 126 /// 127 /// Unlike `next_win_buffer`, there is no timeout if `async_ready_to_read_event` doesn't fire. 128 /// This should be fine, since the end result with or without the timeout will be no audio. async_next_win_buffer(&mut self) -> Result<(), RenderError>129 async fn async_next_win_buffer(&mut self) -> Result<(), RenderError> { 130 self.win_buffer = MaybeUninit::uninit().as_mut_ptr(); 131 132 // We will wait for windows to tell us when it is ready to take in the next set of 133 // audio samples from the guest 134 loop { 135 let async_ready_to_read_event = self 136 .async_ready_to_read_event 137 .as_ref() 138 .ok_or(RenderError::MissingEventAsync)?; 139 async_ready_to_read_event.wait().await.map_err(|e| { 140 RenderError::AsyncError( 141 e, 142 "Failed to wait for async event to get next playback buffer.".to_string(), 143 ) 144 })?; 145 146 if self.enough_available_frames()? { 147 break; 148 } 149 } 150 151 self.get_buffer()?; 152 153 Ok(()) 154 } 155 156 /// Similar to `playback_buffer`. This will return an `AsyncPlaybackBuffer` that allows for 157 /// async operations. 158 /// 159 /// Due to the way WASAPI is, `AsyncPlaybackBuffer` won't actually have any async operations 160 /// that will await. async_playback_buffer(&mut self) -> Result<AsyncPlaybackBuffer, RenderError>161 fn async_playback_buffer(&mut self) -> Result<AsyncPlaybackBuffer, RenderError> { 162 // Safe because `win_buffer` is allocated and retrieved from WASAPI. The size requested, 163 // which we specified in `next_win_buffer` is exactly 164 // `shared_audio_engine_period_in_frames`, so the size parameter should be valid. 165 let (frame_size_bytes, buffer_slice) = unsafe { 166 Self::get_frame_size_and_buffer_slice( 167 self.audio_shared_format.bit_depth as usize, 168 self.audio_shared_format.channels as usize, 169 self.win_buffer, 170 self.audio_shared_format 171 .shared_audio_engine_period_in_frames, 172 )? 173 }; 174 175 AsyncPlaybackBuffer::new(frame_size_bytes, buffer_slice, self) 176 .map_err(RenderError::PlaybackBuffer) 177 } 178 } 179 180 #[async_trait(?Send)] 181 impl AsyncBufferCommit for DeviceRenderer { commit(&mut self, nframes: usize)182 async fn commit(&mut self, nframes: usize) { 183 // Safe because `audio_render_client` is initialized and parameters passed 184 // into `ReleaseBuffer()` are valid 185 unsafe { 186 let hr = self.audio_render_client.ReleaseBuffer(nframes as u32, 0); 187 let _ = check_hresult!( 188 hr, 189 RenderError::from(hr), 190 "Audio Render Client ReleaseBuffer() failed" 191 ); 192 } 193 } 194 } 195 196 #[cfg(test)] 197 mod tests { 198 use cros_async::Executor; 199 200 use super::*; 201 202 // This test is meant to run through the normal audio playback procedure in order to make 203 // debugging easier. The test needs to be ran with a playback device format of 48KHz, 204 // stereo, 16bit. This test might not pass on AMD, since its period is 513 instead of 480. 205 #[ignore] 206 #[test] test_async()207 fn test_async() { 208 async fn test(ex: &Executor) { 209 let stream_source_generator: Box<dyn StreamSourceGenerator> = 210 Box::new(WinAudioStreamSourceGenerator {}); 211 let mut stream_source = stream_source_generator 212 .generate() 213 .expect("Failed to create stream source."); 214 215 let (_, mut async_pb_stream) = stream_source 216 .new_async_playback_stream(2, SampleFormat::S16LE, 48000, 480, ex) 217 .expect("Failed to create async playback stream."); 218 219 // The `ex` here won't be used, but it will satisfy the trait function requirement. 220 let mut async_pb_buffer = async_pb_stream 221 .next_playback_buffer(ex) 222 .await 223 .expect("Failed to get next playback buffer"); 224 225 // The buffer size is calculated by "period * channels * bit depth". The actual buffer 226 // from `next_playback_buffer` may vary, depending on the device format and the user's 227 // machine. 228 let buffer = [1u8; 480 * 2 * 2]; 229 230 async_pb_buffer 231 .copy_cb(buffer.len(), |out| out.copy_from_slice(&buffer)) 232 .unwrap(); 233 234 async_pb_buffer.commit().await; 235 236 let mut async_pb_buffer = async_pb_stream 237 .next_playback_buffer(ex) 238 .await 239 .expect("Failed to get next playback buffer"); 240 241 let buffer = [1u8; 480 * 2 * 2]; 242 243 async_pb_buffer 244 .copy_cb(buffer.len(), |out| out.copy_from_slice(&buffer)) 245 .unwrap(); 246 247 async_pb_buffer.commit().await; 248 } 249 250 let ex = Executor::new().expect("Failed to create executor."); 251 252 ex.run_until(test(&ex)).unwrap(); 253 } 254 } 255