android_audio/
lib.rs

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")]
6mod libaaudio_stub;
7
8use std::os::raw::c_void;
9use std::time::Duration;
10use std::time::Instant;
11
12use async_trait::async_trait;
13use audio_streams::capture::AsyncCaptureBuffer;
14use audio_streams::capture::AsyncCaptureBufferStream;
15use audio_streams::capture::CaptureBuffer;
16use audio_streams::capture::CaptureBufferStream;
17use audio_streams::AsyncBufferCommit;
18use audio_streams::AsyncPlaybackBuffer;
19use audio_streams::AsyncPlaybackBufferStream;
20use audio_streams::AudioStreamsExecutor;
21use audio_streams::BoxError;
22use audio_streams::BufferCommit;
23use audio_streams::NoopStreamControl;
24use audio_streams::PlaybackBuffer;
25use audio_streams::PlaybackBufferStream;
26use audio_streams::SampleFormat;
27use audio_streams::StreamControl;
28use audio_streams::StreamEffect;
29use audio_streams::StreamSource;
30use audio_streams::StreamSourceGenerator;
31use base::warn;
32use thiserror::Error;
33
34#[derive(Clone, Copy)]
35enum AndroidAudioStreamDirection {
36    Input = 1,
37    Output = 0,
38}
39
40#[derive(Error, Debug)]
41pub enum AAudioError {
42    #[error("Failed to create stream builder")]
43    StreamBuilderCreation,
44    #[error("Failed to open stream")]
45    StreamOpen,
46    #[error("Failed to start stream")]
47    StreamStart,
48    #[error("Failed to delete stream builder")]
49    StreamBuilderDelete,
50}
51
52// Opaque blob
53#[repr(C)]
54struct AAudioStream {
55    _data: [u8; 0],
56    _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
57}
58
59// Opaque blob
60#[repr(C)]
61struct AAudioStreamBuilder {
62    _data: [u8; 0],
63    _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
64}
65
66type AaudioFormatT = i32;
67type AaudioResultT = i32;
68const AAUDIO_OK: AaudioResultT = 0;
69
70extern "C" {
71    fn AAudio_createStreamBuilder(builder: *mut *mut AAudioStreamBuilder) -> AaudioResultT;
72    fn AAudioStreamBuilder_delete(builder: *mut AAudioStreamBuilder) -> AaudioResultT;
73    fn AAudioStreamBuilder_setBufferCapacityInFrames(
74        builder: *mut AAudioStreamBuilder,
75        num_frames: i32,
76    );
77    fn AAudioStreamBuilder_setDirection(builder: *mut AAudioStreamBuilder, direction: u32);
78    fn AAudioStreamBuilder_setFormat(builder: *mut AAudioStreamBuilder, format: AaudioFormatT);
79    fn AAudioStreamBuilder_setSampleRate(builder: *mut AAudioStreamBuilder, sample_rate: i32);
80    fn AAudioStreamBuilder_setChannelCount(builder: *mut AAudioStreamBuilder, channel_count: i32);
81    fn AAudioStreamBuilder_openStream(
82        builder: *mut AAudioStreamBuilder,
83        stream: *mut *mut AAudioStream,
84    ) -> AaudioResultT;
85    fn AAudioStream_getBufferSizeInFrames(stream: *mut AAudioStream) -> i32;
86    fn AAudioStream_requestStart(stream: *mut AAudioStream) -> AaudioResultT;
87    fn AAudioStream_read(
88        stream: *mut AAudioStream,
89        buffer: *mut c_void,
90        num_frames: i32,
91        timeout_nanoseconds: i64,
92    ) -> AaudioResultT;
93    fn AAudioStream_write(
94        stream: *mut AAudioStream,
95        buffer: *const c_void,
96        num_frames: i32,
97        timeout_nanoseconds: i64,
98    ) -> AaudioResultT;
99    fn AAudioStream_close(stream: *mut AAudioStream) -> AaudioResultT;
100}
101
102struct AAudioStreamPtr {
103    // TODO: Use callback function to avoid possible thread preemption and glitches cause by
104    // using AAudio APIs in different threads.
105    stream_ptr: *mut AAudioStream,
106}
107
108// SAFETY:
109// AudioStream.drop.buffer_ptr: *const u8 points to AudioStream.buffer, which would be alive
110// whenever AudioStream.drop.buffer_ptr is alive.
111unsafe impl Send for AndroidAudioStreamCommit {}
112
113struct AudioStream {
114    buffer: Box<[u8]>,
115    frame_size: usize,
116    frame_rate: u32,
117    next_frame: Instant,
118    start_time: Option<Instant>,
119    total_frames: i32,
120    buffer_drop: AndroidAudioStreamCommit,
121    read_count: i32,
122    aaudio_buffer_size: usize,
123}
124
125struct AndroidAudioStreamCommit {
126    buffer_ptr: *const u8,
127    stream: AAudioStreamPtr,
128    direction: AndroidAudioStreamDirection,
129}
130
131impl BufferCommit for AndroidAudioStreamCommit {
132    fn commit(&mut self, _nwritten: usize) {
133        // This traits function is never called.
134        unimplemented!();
135    }
136}
137
138#[async_trait(?Send)]
139impl AsyncBufferCommit for AndroidAudioStreamCommit {
140    async fn commit(&mut self, nwritten: usize) {
141        match self.direction {
142            AndroidAudioStreamDirection::Input => {}
143            AndroidAudioStreamDirection::Output => {
144                // SAFETY:
145                // The AAudioStream_write reads buffer for nwritten * frame_size bytes
146                // It is safe since nwritten < buffer_size and the buffer.len() == buffer_size *
147                // frame_size
148                let frames_written: i32 = unsafe {
149                    AAudioStream_write(
150                        self.stream.stream_ptr,
151                        self.buffer_ptr as *const c_void,
152                        nwritten as i32,
153                        0, // this call will not wait.
154                    )
155                };
156                if frames_written < 0 {
157                    warn!("AAudio stream write failed.");
158                } else if (frames_written as usize) < nwritten {
159                    // Currently, the frames unable to write by the AAudio API are dropped.
160                    warn!(
161                        "Android Audio Stream:  Drop {} frames",
162                        nwritten - (frames_written as usize)
163                    );
164                }
165            }
166        }
167    }
168}
169
170impl AudioStream {
171    pub fn new(
172        num_channels: usize,
173        format: SampleFormat,
174        frame_rate: u32,
175        buffer_size: usize,
176        direction: AndroidAudioStreamDirection,
177    ) -> Result<Self, BoxError> {
178        let frame_size = format.sample_bytes() * num_channels;
179
180        let mut stream_ptr: *mut AAudioStream = std::ptr::null_mut();
181        let mut builder: *mut AAudioStreamBuilder = std::ptr::null_mut();
182        // SAFETY:
183        // Interfacing with the AAudio C API. Assumes correct linking
184        // and `builder` and `stream_ptr` pointers are valid and properly initialized.
185        unsafe {
186            if AAudio_createStreamBuilder(&mut builder) != AAUDIO_OK {
187                return Err(Box::new(AAudioError::StreamBuilderCreation));
188            }
189            AAudioStreamBuilder_setDirection(builder, direction as u32);
190            AAudioStreamBuilder_setBufferCapacityInFrames(builder, buffer_size as i32 * 2);
191            AAudioStreamBuilder_setFormat(builder, format as AaudioFormatT);
192            AAudioStreamBuilder_setSampleRate(builder, frame_rate as i32);
193            AAudioStreamBuilder_setChannelCount(builder, num_channels as i32);
194            if AAudioStreamBuilder_openStream(builder, &mut stream_ptr) != AAUDIO_OK {
195                return Err(Box::new(AAudioError::StreamOpen));
196            }
197            if AAudioStreamBuilder_delete(builder) != AAUDIO_OK {
198                return Err(Box::new(AAudioError::StreamBuilderDelete));
199            }
200            if AAudioStream_requestStart(stream_ptr) != AAUDIO_OK {
201                return Err(Box::new(AAudioError::StreamStart));
202            }
203        }
204        // SAFETY:
205        // Interfacing with the AAudio C API. Assumes correct linking
206        // and `stream_ptr` pointers are valid and properly initialized.
207        let aaudio_buffer_size = unsafe { AAudioStream_getBufferSizeInFrames(stream_ptr) } as usize;
208        let buffer = vec![0; buffer_size * frame_size].into_boxed_slice();
209        let stream = AAudioStreamPtr { stream_ptr };
210        let buffer_drop = AndroidAudioStreamCommit {
211            stream,
212            buffer_ptr: buffer.as_ptr(),
213            direction,
214        };
215        Ok(AudioStream {
216            buffer,
217            frame_size,
218            frame_rate,
219            next_frame: Instant::now(),
220            start_time: None,
221            total_frames: 0,
222            buffer_drop,
223            read_count: 0,
224            aaudio_buffer_size,
225        })
226    }
227}
228
229impl PlaybackBufferStream for AudioStream {
230    fn next_playback_buffer<'b, 's: 'b>(&'s mut self) -> Result<PlaybackBuffer<'b>, BoxError> {
231        // This traits function is never called.
232        unimplemented!();
233    }
234}
235
236#[async_trait(?Send)]
237impl AsyncPlaybackBufferStream for AudioStream {
238    async fn next_playback_buffer<'a>(
239        &'a mut self,
240        ex: &dyn AudioStreamsExecutor,
241    ) -> Result<AsyncPlaybackBuffer<'a>, BoxError> {
242        self.total_frames += (self.buffer.len() / self.frame_size) as i32;
243        let start_time = match self.start_time {
244            Some(time) => {
245                ex.delay(self.next_frame.saturating_duration_since(Instant::now()))
246                    .await?;
247                time
248            }
249            None => {
250                let now = Instant::now();
251                self.start_time = Some(now);
252                now
253            }
254        };
255        self.next_frame = start_time
256            + Duration::from_millis(self.total_frames as u64 * 1000 / self.frame_rate as u64);
257        Ok(
258            AsyncPlaybackBuffer::new(self.frame_size, self.buffer.as_mut(), &mut self.buffer_drop)
259                .map_err(Box::new)?,
260        )
261    }
262}
263
264#[async_trait(?Send)]
265impl CaptureBufferStream for AudioStream {
266    fn next_capture_buffer<'b, 's: 'b>(&'s mut self) -> Result<CaptureBuffer<'b>, BoxError> {
267        // This traits function is never called.
268        unimplemented!()
269    }
270}
271
272#[async_trait(?Send)]
273impl AsyncCaptureBufferStream for AudioStream {
274    async fn next_capture_buffer<'a>(
275        &'a mut self,
276        ex: &dyn AudioStreamsExecutor,
277    ) -> Result<AsyncCaptureBuffer<'a>, BoxError> {
278        let buffer_size = self.buffer.len() / self.frame_size;
279        self.read_count += 1;
280        self.total_frames += buffer_size as i32;
281        let start_time = match self.start_time {
282            Some(time) => {
283                ex.delay(self.next_frame.saturating_duration_since(Instant::now()))
284                    .await?;
285                time
286            }
287            None => {
288                let now = Instant::now();
289                self.start_time = Some(now);
290                now
291            }
292        };
293        self.next_frame = start_time
294            + Duration::from_millis(self.total_frames as u64 * 1000 / self.frame_rate as u64);
295
296        // Skip for at least (1.5x aaudio buffer size - buffer_size) to ensure there is always a
297        // aaudio buffer available for read.
298        if self.read_count < (self.aaudio_buffer_size * 3 / 2 / buffer_size) as i32 + 1 {
299            self.buffer.fill(0);
300            return Ok(AsyncCaptureBuffer::new(
301                buffer_size,
302                self.buffer.as_mut(),
303                &mut self.buffer_drop,
304            )
305            .map_err(Box::new)?);
306        }
307
308        // SAFETY:
309        // The AAudioStream_read writes buffer for buffer.len() / frame_size * frame_size bytes
310        let frames_read = unsafe {
311            AAudioStream_read(
312                self.buffer_drop.stream.stream_ptr,
313                self.buffer.as_mut_ptr() as *mut c_void,
314                (buffer_size) as i32,
315                0,
316            )
317        };
318
319        if frames_read < 0 {
320            warn!("AAudio stream read failed: {frames_read}");
321            self.buffer.fill(0);
322        } else if (frames_read as usize) < buffer_size {
323            warn!(
324                "AAudio stream read data not enough. frames read: {frames_read}, buffer size: {buffer_size}",
325            );
326            self.buffer[frames_read as usize * self.frame_size..].fill(0);
327        }
328
329        Ok(
330            AsyncCaptureBuffer::new(buffer_size, self.buffer.as_mut(), &mut self.buffer_drop)
331                .map_err(Box::new)?,
332        )
333    }
334}
335
336impl Drop for AAudioStreamPtr {
337    fn drop(&mut self) {
338        // SAFETY:
339        // Interfacing with the AAudio C API. Assumes correct linking
340        // and `stream_ptr` are valid and properly initialized.
341        if unsafe { AAudioStream_close(self.stream_ptr) } != AAUDIO_OK {
342            warn!("AAudio stream close failed.");
343        }
344    }
345}
346
347#[derive(Default)]
348struct AndroidAudioStreamSource;
349
350impl StreamSource for AndroidAudioStreamSource {
351    #[allow(clippy::type_complexity)]
352    fn new_playback_stream(
353        &mut self,
354        _num_channels: usize,
355        _format: SampleFormat,
356        _frame_rate: u32,
357        _buffer_size: usize,
358    ) -> Result<(Box<dyn StreamControl>, Box<dyn PlaybackBufferStream>), BoxError> {
359        // This traits function is never called.
360        unimplemented!();
361    }
362
363    #[allow(clippy::type_complexity)]
364    fn new_async_playback_stream(
365        &mut self,
366        num_channels: usize,
367        format: SampleFormat,
368        frame_rate: u32,
369        buffer_size: usize,
370        _ex: &dyn AudioStreamsExecutor,
371    ) -> Result<(Box<dyn StreamControl>, Box<dyn AsyncPlaybackBufferStream>), BoxError> {
372        let audio_stream = AudioStream::new(
373            num_channels,
374            format,
375            frame_rate,
376            buffer_size,
377            AndroidAudioStreamDirection::Output,
378        )?;
379        Ok((Box::new(NoopStreamControl::new()), Box::new(audio_stream)))
380    }
381
382    #[allow(clippy::type_complexity)]
383    fn new_capture_stream(
384        &mut self,
385        _num_channels: usize,
386        _format: SampleFormat,
387        _frame_rate: u32,
388        _buffer_size: usize,
389        _effects: &[StreamEffect],
390    ) -> std::result::Result<(Box<dyn StreamControl>, Box<dyn CaptureBufferStream>), BoxError> {
391        // This traits function is never called.
392        unimplemented!();
393    }
394
395    #[allow(clippy::type_complexity)]
396    fn new_async_capture_stream(
397        &mut self,
398        num_channels: usize,
399        format: SampleFormat,
400        frame_rate: u32,
401        buffer_size: usize,
402        _effects: &[StreamEffect],
403        _ex: &dyn AudioStreamsExecutor,
404    ) -> std::result::Result<(Box<dyn StreamControl>, Box<dyn AsyncCaptureBufferStream>), BoxError>
405    {
406        let audio_stream = AudioStream::new(
407            num_channels,
408            format,
409            frame_rate,
410            buffer_size,
411            AndroidAudioStreamDirection::Input,
412        )?;
413        Ok((Box::new(NoopStreamControl::new()), Box::new(audio_stream)))
414    }
415}
416
417#[derive(Default)]
418pub struct AndroidAudioStreamSourceGenerator;
419
420impl AndroidAudioStreamSourceGenerator {
421    pub fn new() -> Self {
422        AndroidAudioStreamSourceGenerator {}
423    }
424}
425
426/// `AndroidAudioStreamSourceGenerator` is a struct that implements [`StreamSourceGenerator`]
427/// for `AndroidAudioStreamSource`.
428impl StreamSourceGenerator for AndroidAudioStreamSourceGenerator {
429    fn generate(&self) -> Result<Box<dyn StreamSource>, BoxError> {
430        Ok(Box::new(AndroidAudioStreamSource))
431    }
432}