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