1 // Copyright 2024 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 #[cfg(feature = "libaaudio_stub")] 6 mod libaaudio_stub; 7 8 use std::os::raw::c_void; 9 use std::thread; 10 use std::time::Duration; 11 use std::time::Instant; 12 13 use async_trait::async_trait; 14 use audio_streams::AsyncBufferCommit; 15 use audio_streams::AsyncPlaybackBuffer; 16 use audio_streams::AsyncPlaybackBufferStream; 17 use audio_streams::AudioStreamsExecutor; 18 use audio_streams::BoxError; 19 use audio_streams::BufferCommit; 20 use audio_streams::NoopStreamControl; 21 use audio_streams::PlaybackBuffer; 22 use audio_streams::PlaybackBufferStream; 23 use audio_streams::SampleFormat; 24 use audio_streams::StreamControl; 25 use audio_streams::StreamSource; 26 use audio_streams::StreamSourceGenerator; 27 use base::warn; 28 use thiserror::Error; 29 30 #[derive(Error, Debug)] 31 pub enum AAudioError { 32 #[error("Failed to create stream builder")] 33 StreamBuilderCreation, 34 #[error("Failed to open stream")] 35 StreamOpen, 36 #[error("Failed to start stream")] 37 StreamStart, 38 #[error("Failed to delete stream builder")] 39 StreamBuilderDelete, 40 } 41 42 // Opaque blob 43 #[repr(C)] 44 struct AAudioStream { 45 _data: [u8; 0], 46 _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>, 47 } 48 49 // Opaque blob 50 #[repr(C)] 51 struct AAudioStreamBuilder { 52 _data: [u8; 0], 53 _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>, 54 } 55 56 type AaudioFormatT = i32; 57 type AaudioResultT = i32; 58 const AAUDIO_OK: AaudioResultT = 0; 59 60 extern "C" { AAudio_createStreamBuilder(builder: *mut *mut AAudioStreamBuilder) -> AaudioResultT61 fn AAudio_createStreamBuilder(builder: *mut *mut AAudioStreamBuilder) -> AaudioResultT; AAudioStreamBuilder_delete(builder: *mut AAudioStreamBuilder) -> AaudioResultT62 fn AAudioStreamBuilder_delete(builder: *mut AAudioStreamBuilder) -> AaudioResultT; AAudioStreamBuilder_setBufferCapacityInFrames( builder: *mut AAudioStreamBuilder, num_frames: i32, )63 fn AAudioStreamBuilder_setBufferCapacityInFrames( 64 builder: *mut AAudioStreamBuilder, 65 num_frames: i32, 66 ); AAudioStreamBuilder_setFormat(builder: *mut AAudioStreamBuilder, format: AaudioFormatT)67 fn AAudioStreamBuilder_setFormat(builder: *mut AAudioStreamBuilder, format: AaudioFormatT); AAudioStreamBuilder_setSampleRate(builder: *mut AAudioStreamBuilder, sample_rate: i32)68 fn AAudioStreamBuilder_setSampleRate(builder: *mut AAudioStreamBuilder, sample_rate: i32); AAudioStreamBuilder_setChannelCount(builder: *mut AAudioStreamBuilder, channel_count: i32)69 fn AAudioStreamBuilder_setChannelCount(builder: *mut AAudioStreamBuilder, channel_count: i32); AAudioStreamBuilder_openStream( builder: *mut AAudioStreamBuilder, stream: *mut *mut AAudioStream, ) -> AaudioResultT70 fn AAudioStreamBuilder_openStream( 71 builder: *mut AAudioStreamBuilder, 72 stream: *mut *mut AAudioStream, 73 ) -> AaudioResultT; AAudioStream_requestStart(stream: *mut AAudioStream) -> AaudioResultT74 fn AAudioStream_requestStart(stream: *mut AAudioStream) -> AaudioResultT; AAudioStream_write( stream: *mut AAudioStream, buffer: *const c_void, num_frames: i32, timeout_nanoseconds: i64, ) -> AaudioResultT75 fn AAudioStream_write( 76 stream: *mut AAudioStream, 77 buffer: *const c_void, 78 num_frames: i32, 79 timeout_nanoseconds: i64, 80 ) -> AaudioResultT; AAudioStream_close(stream: *mut AAudioStream) -> AaudioResultT81 fn AAudioStream_close(stream: *mut AAudioStream) -> AaudioResultT; 82 } 83 84 struct AAudioStreamPtr { 85 // TODO: Use callback function to avoid possible thread preemption and glitches cause by 86 // using AAudio APIs in different threads. 87 stream_ptr: *mut AAudioStream, 88 } 89 90 // SAFETY: 91 // AudioStream.drop.buffer_ptr: *const u8 points to AudioStream.buffer, which would be alive 92 // whenever AudioStream.drop.buffer_ptr is alive. 93 unsafe impl Send for AndroidAudioStreamCommit {} 94 95 struct AudioStream { 96 buffer: Box<[u8]>, 97 frame_size: usize, 98 interval: Duration, 99 next_frame: Duration, 100 start_time: Option<Instant>, 101 buffer_drop: AndroidAudioStreamCommit, 102 } 103 104 struct AndroidAudioStreamCommit { 105 buffer_ptr: *const u8, 106 stream: AAudioStreamPtr, 107 } 108 109 impl BufferCommit for AndroidAudioStreamCommit { commit(&mut self, nwritten: usize)110 fn commit(&mut self, nwritten: usize) { 111 // SAFETY: 112 // The AAudioStream_write reads buffer for nwritten * frame_size bytes 113 // It is safe since nwritten < buffer_size and the buffer.len() == buffer_size * frame_size 114 let frames_written: i32 = unsafe { 115 AAudioStream_write( 116 self.stream.stream_ptr, 117 self.buffer_ptr as *const c_void, 118 nwritten as i32, 119 0, // this call will not wait. 120 ) 121 }; 122 if frames_written < 0 { 123 warn!("AAudio stream write failed."); 124 } else if (frames_written as usize) < nwritten { 125 // Currently, the frames unable to write by the AAudio API are dropped. 126 warn!( 127 "Android Audio Stream: Drop {} frames", 128 nwritten - (frames_written as usize) 129 ); 130 } 131 } 132 } 133 134 #[async_trait(?Send)] 135 impl AsyncBufferCommit for AndroidAudioStreamCommit { commit(&mut self, nwritten: usize)136 async fn commit(&mut self, nwritten: usize) { 137 // SAFETY: 138 // The AAudioStream_write reads buffer for nwritten * frame_size bytes 139 // It is safe since nwritten < buffer_size and the buffer.len() == buffer_size * frame_size 140 let frames_written: i32 = unsafe { 141 AAudioStream_write( 142 self.stream.stream_ptr, 143 self.buffer_ptr as *const c_void, 144 nwritten as i32, 145 0, // this call will not wait. 146 ) 147 }; 148 if frames_written < 0 { 149 warn!("AAudio stream write failed."); 150 } else if (frames_written as usize) < nwritten { 151 // Currently, the frames unable to write by the AAudio API are dropped. 152 warn!( 153 "Android Audio Stream: Drop {} frames", 154 nwritten - (frames_written as usize) 155 ); 156 } 157 } 158 } 159 160 impl AudioStream { new( num_channels: usize, format: SampleFormat, frame_rate: u32, buffer_size: usize, ) -> Result<Self, BoxError>161 pub fn new( 162 num_channels: usize, 163 format: SampleFormat, 164 frame_rate: u32, 165 buffer_size: usize, 166 ) -> Result<Self, BoxError> { 167 let frame_size = format.sample_bytes() * num_channels; 168 let interval = Duration::from_millis(buffer_size as u64 * 1000 / frame_rate as u64); 169 170 let mut stream_ptr: *mut AAudioStream = std::ptr::null_mut(); 171 let mut builder: *mut AAudioStreamBuilder = std::ptr::null_mut(); 172 // SAFETY: 173 // Interfacing with the AAudio C API. Assumes correct linking 174 // and `builder` and `stream_ptr` pointers are valid and properly initialized. 175 unsafe { 176 if AAudio_createStreamBuilder(&mut builder) != AAUDIO_OK { 177 return Err(Box::new(AAudioError::StreamBuilderCreation)); 178 } 179 AAudioStreamBuilder_setBufferCapacityInFrames(builder, buffer_size as i32 * 2); 180 AAudioStreamBuilder_setFormat(builder, format as AaudioFormatT); 181 AAudioStreamBuilder_setSampleRate(builder, frame_rate as i32); 182 AAudioStreamBuilder_setChannelCount(builder, num_channels as i32); 183 if AAudioStreamBuilder_openStream(builder, &mut stream_ptr) != AAUDIO_OK { 184 return Err(Box::new(AAudioError::StreamOpen)); 185 } 186 if AAudioStreamBuilder_delete(builder) != AAUDIO_OK { 187 return Err(Box::new(AAudioError::StreamBuilderDelete)); 188 } 189 if AAudioStream_requestStart(stream_ptr) != AAUDIO_OK { 190 return Err(Box::new(AAudioError::StreamStart)); 191 } 192 } 193 let buffer = vec![0; buffer_size * frame_size].into_boxed_slice(); 194 let stream = AAudioStreamPtr { stream_ptr }; 195 let buffer_drop = AndroidAudioStreamCommit { 196 stream, 197 buffer_ptr: buffer.as_ptr(), 198 }; 199 Ok(AudioStream { 200 buffer, 201 frame_size, 202 interval, 203 next_frame: interval, 204 start_time: None, 205 buffer_drop, 206 }) 207 } 208 } 209 210 impl PlaybackBufferStream for AudioStream { next_playback_buffer<'b, 's: 'b>(&'s mut self) -> Result<PlaybackBuffer<'b>, BoxError>211 fn next_playback_buffer<'b, 's: 'b>(&'s mut self) -> Result<PlaybackBuffer<'b>, BoxError> { 212 if let Some(start_time) = self.start_time { 213 let elapsed = start_time.elapsed(); 214 if elapsed < self.next_frame { 215 thread::sleep(self.next_frame - elapsed); 216 } 217 self.next_frame += self.interval; 218 } else { 219 self.start_time = Some(Instant::now()); 220 self.next_frame = self.interval; 221 } 222 Ok( 223 PlaybackBuffer::new(self.frame_size, self.buffer.as_mut(), &mut self.buffer_drop) 224 .map_err(Box::new)?, 225 ) 226 } 227 } 228 229 #[async_trait(?Send)] 230 impl AsyncPlaybackBufferStream for AudioStream { next_playback_buffer<'a>( &'a mut self, ex: &dyn AudioStreamsExecutor, ) -> Result<AsyncPlaybackBuffer<'a>, BoxError>231 async fn next_playback_buffer<'a>( 232 &'a mut self, 233 ex: &dyn AudioStreamsExecutor, 234 ) -> Result<AsyncPlaybackBuffer<'a>, BoxError> { 235 if let Some(start_time) = self.start_time { 236 let elapsed = start_time.elapsed(); 237 if elapsed < self.next_frame { 238 ex.delay(self.next_frame - elapsed).await?; 239 } 240 self.next_frame += self.interval; 241 } else { 242 self.start_time = Some(Instant::now()); 243 self.next_frame = self.interval; 244 } 245 Ok( 246 AsyncPlaybackBuffer::new(self.frame_size, self.buffer.as_mut(), &mut self.buffer_drop) 247 .map_err(Box::new)?, 248 ) 249 } 250 } 251 252 impl Drop for AAudioStreamPtr { drop(&mut self)253 fn drop(&mut self) { 254 // SAFETY: 255 // Interfacing with the AAudio C API. Assumes correct linking 256 // and `stream_ptr` are valid and properly initialized. 257 if unsafe { AAudioStream_close(self.stream_ptr) } != AAUDIO_OK { 258 warn!("AAudio stream close failed."); 259 } 260 } 261 } 262 263 #[derive(Default)] 264 struct AndroidAudioStreamSource; 265 266 impl StreamSource for AndroidAudioStreamSource { 267 #[allow(clippy::type_complexity)] new_playback_stream( &mut self, num_channels: usize, format: SampleFormat, frame_rate: u32, buffer_size: usize, ) -> Result<(Box<dyn StreamControl>, Box<dyn PlaybackBufferStream>), BoxError>268 fn new_playback_stream( 269 &mut self, 270 num_channels: usize, 271 format: SampleFormat, 272 frame_rate: u32, 273 buffer_size: usize, 274 ) -> Result<(Box<dyn StreamControl>, Box<dyn PlaybackBufferStream>), BoxError> { 275 match AudioStream::new(num_channels, format, frame_rate, buffer_size) { 276 Ok(audio_stream) => Ok((Box::new(NoopStreamControl::new()), Box::new(audio_stream))), 277 Err(err) => Err(err), 278 } 279 } 280 281 #[allow(clippy::type_complexity)] new_async_playback_stream( &mut self, num_channels: usize, format: SampleFormat, frame_rate: u32, buffer_size: usize, _ex: &dyn AudioStreamsExecutor, ) -> Result<(Box<dyn StreamControl>, Box<dyn AsyncPlaybackBufferStream>), BoxError>282 fn new_async_playback_stream( 283 &mut self, 284 num_channels: usize, 285 format: SampleFormat, 286 frame_rate: u32, 287 buffer_size: usize, 288 _ex: &dyn AudioStreamsExecutor, 289 ) -> Result<(Box<dyn StreamControl>, Box<dyn AsyncPlaybackBufferStream>), BoxError> { 290 match AudioStream::new(num_channels, format, frame_rate, buffer_size) { 291 Ok(audio_stream) => Ok((Box::new(NoopStreamControl::new()), Box::new(audio_stream))), 292 Err(err) => Err(err), 293 } 294 } 295 } 296 297 #[derive(Default)] 298 pub struct AndroidAudioStreamSourceGenerator; 299 300 impl AndroidAudioStreamSourceGenerator { new() -> Self301 pub fn new() -> Self { 302 AndroidAudioStreamSourceGenerator {} 303 } 304 } 305 306 /// `AndroidAudioStreamSourceGenerator` is a struct that implements [`StreamSourceGenerator`] 307 /// for `AndroidAudioStreamSource`. 308 impl StreamSourceGenerator for AndroidAudioStreamSourceGenerator { generate(&self) -> Result<Box<dyn StreamSource>, BoxError>309 fn generate(&self) -> Result<Box<dyn StreamSource>, BoxError> { 310 Ok(Box::new(AndroidAudioStreamSource)) 311 } 312 } 313