• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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