Skip to content

Commit a23a954

Browse files
authored
fix(aaudio): Correctly detect minimum buffer size (#1039)
AAudio buffer size minimum should be detected using the PROPERTY_OUTPUT_FRAMES_PER_BUFFER on the AudioManager not using AudioTrack.
1 parent 99aabd0 commit a23a954

File tree

6 files changed

+64
-131
lines changed

6 files changed

+64
-131
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
- Add `Sample::bits_per_sample` method.
55
- Update `audio_thread_priority` to 0.34.
66
- AAudio: Configure buffer to ensure consistent callback buffer sizes.
7+
- AAudio: Fix the buffer size range detection by querying the AudioService property correctly.
78
- ALSA: Improve `BufferSize::Fixed` precision and audio callback performance.
89
- ALSA: Change `BufferSize::Default` to use the device defaults.
910
- ALSA: Change card enumeration to work like `aplay -L` does.

src/host/aaudio/android_media.rs

Lines changed: 0 additions & 62 deletions
This file was deleted.

src/host/aaudio/java_interface.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
mod audio_features;
2+
mod audio_manager;
23
mod definitions;
34
mod devices_info;
45
mod utils;
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
use super::{
2+
utils::{
3+
get_context, get_property, get_system_service, with_attached, JNIEnv, JObject, JResult,
4+
},
5+
AudioManager, Context,
6+
};
7+
8+
impl AudioManager {
9+
/// Get the frames per buffer using Android Java API
10+
pub fn get_frames_per_buffer() -> Result<i32, String> {
11+
let context = get_context();
12+
13+
with_attached(context, |env, context| get_frames_per_buffer(env, &context))
14+
.map_err(|error| error.to_string())
15+
}
16+
}
17+
18+
fn get_frames_per_buffer<'j>(env: &mut JNIEnv<'j>, context: &JObject<'j>) -> JResult<i32> {
19+
let audio_manager = get_system_service(env, context, Context::AUDIO_SERVICE)?;
20+
21+
let frames_per_buffer = get_property(
22+
env,
23+
&audio_manager,
24+
AudioManager::PROPERTY_OUTPUT_FRAMES_PER_BUFFER,
25+
)?;
26+
27+
let frames_per_buffer_string = String::from(env.get_string(&frames_per_buffer)?);
28+
29+
// TODO: Use jni::errors::Error::ParseFailed instead of jni::errors::Error::JniCall once jni > v0.21.1 is released
30+
frames_per_buffer_string
31+
.parse::<i32>()
32+
.map_err(|e| jni::errors::Error::JniCall(jni::errors::JniError::Unknown))
33+
}

src/host/aaudio/java_interface/definitions.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,6 @@ impl PackageManager {
2121
pub(crate) struct AudioManager;
2222

2323
impl AudioManager {
24-
pub const PROPERTY_OUTPUT_SAMPLE_RATE: &'static str =
25-
"android.media.property.OUTPUT_SAMPLE_RATE";
2624
pub const PROPERTY_OUTPUT_FRAMES_PER_BUFFER: &'static str =
2725
"android.media.property.OUTPUT_FRAMES_PER_BUFFER";
2826

src/host/aaudio/mod.rs

Lines changed: 29 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use std::cell::RefCell;
21
use std::cmp;
32
use std::convert::TryInto;
43
use std::time::{Duration, Instant};
@@ -7,30 +6,30 @@ use std::vec::IntoIter as VecIntoIter;
76
extern crate ndk;
87

98
use convert::{stream_instant, to_stream_instant};
10-
use java_interface::{AudioDeviceDirection, AudioDeviceInfo};
9+
use java_interface::{AudioDeviceDirection, AudioDeviceInfo, AudioManager};
1110

1211
use crate::traits::{DeviceTrait, HostTrait, StreamTrait};
1312
use crate::{
1413
BackendSpecificError, BufferSize, BuildStreamError, Data, DefaultStreamConfigError,
1514
DeviceNameError, DevicesError, InputCallbackInfo, InputStreamTimestamp, OutputCallbackInfo,
1615
OutputStreamTimestamp, PauseStreamError, PlayStreamError, SampleFormat, SampleRate,
17-
SizedSample, StreamConfig, StreamError, SupportedBufferSize, SupportedStreamConfig,
16+
StreamConfig, StreamError, SupportedBufferSize, SupportedStreamConfig,
1817
SupportedStreamConfigRange, SupportedStreamConfigsError,
1918
};
2019

21-
mod android_media;
2220
mod convert;
2321
mod java_interface;
2422

25-
use self::android_media::{get_audio_record_min_buffer_size, get_audio_track_min_buffer_size};
2623
use self::ndk::audio::AudioStream;
2724

25+
// constants from android.media.AudioFormat
26+
const CHANNEL_OUT_MONO: i32 = 4;
27+
const CHANNEL_OUT_STEREO: i32 = 12;
28+
2829
// Android Java API supports up to 8 channels
2930
// TODO: more channels available in native AAudio
30-
const CHANNEL_MASKS: [i32; 2] = [
31-
android_media::CHANNEL_OUT_MONO,
32-
android_media::CHANNEL_OUT_STEREO,
33-
];
31+
// Maps channel masks to their corresponding channel counts
32+
const CHANNEL_CONFIGS: [(i32, u16); 2] = [(CHANNEL_OUT_MONO, 1), (CHANNEL_OUT_STEREO, 2)];
3433

3534
const SAMPLE_RATES: [i32; 13] = [
3635
5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000, 88200, 96000, 176_400, 192_000,
@@ -91,18 +90,8 @@ impl HostTrait for Host {
9190
}
9291
}
9392

94-
fn buffer_size_range_for_params(
95-
is_output: bool,
96-
sample_rate: i32,
97-
channel_mask: i32,
98-
android_format: i32,
99-
) -> SupportedBufferSize {
100-
let min_buffer_size = if is_output {
101-
get_audio_track_min_buffer_size(sample_rate, channel_mask, android_format)
102-
} else {
103-
get_audio_record_min_buffer_size(sample_rate, channel_mask, android_format)
104-
};
105-
if min_buffer_size > 0 {
93+
fn buffer_size_range() -> SupportedBufferSize {
94+
if let Ok(min_buffer_size) = AudioManager::get_frames_per_buffer() {
10695
SupportedBufferSize::Range {
10796
min: min_buffer_size as u32,
10897
max: i32::MAX as u32,
@@ -112,45 +101,29 @@ fn buffer_size_range_for_params(
112101
}
113102
}
114103

115-
fn default_supported_configs(is_output: bool) -> VecIntoIter<SupportedStreamConfigRange> {
116-
// Have to "brute force" the parameter combinations with getMinBufferSize
104+
fn default_supported_configs() -> VecIntoIter<SupportedStreamConfigRange> {
117105
const FORMATS: [SampleFormat; 2] = [SampleFormat::I16, SampleFormat::F32];
118106

119-
let mut output = Vec::with_capacity(SAMPLE_RATES.len() * CHANNEL_MASKS.len() * FORMATS.len());
107+
let buffer_size = buffer_size_range();
108+
let mut output = Vec::with_capacity(SAMPLE_RATES.len() * CHANNEL_CONFIGS.len() * FORMATS.len());
120109
for sample_format in &FORMATS {
121-
let android_format = if *sample_format == SampleFormat::I16 {
122-
android_media::ENCODING_PCM_16BIT
123-
} else {
124-
android_media::ENCODING_PCM_FLOAT
125-
};
126-
for (mask_idx, channel_mask) in CHANNEL_MASKS.iter().enumerate() {
127-
let channel_count = mask_idx + 1;
110+
for (_channel_mask, channel_count) in &CHANNEL_CONFIGS {
128111
for sample_rate in &SAMPLE_RATES {
129-
if let SupportedBufferSize::Range { min, max } = buffer_size_range_for_params(
130-
is_output,
131-
*sample_rate,
132-
*channel_mask,
133-
android_format,
134-
) {
135-
output.push(SupportedStreamConfigRange {
136-
channels: channel_count as u16,
137-
min_sample_rate: SampleRate(*sample_rate as u32),
138-
max_sample_rate: SampleRate(*sample_rate as u32),
139-
buffer_size: SupportedBufferSize::Range { min, max },
140-
sample_format: *sample_format,
141-
});
142-
}
112+
output.push(SupportedStreamConfigRange {
113+
channels: *channel_count,
114+
min_sample_rate: SampleRate(*sample_rate as u32),
115+
max_sample_rate: SampleRate(*sample_rate as u32),
116+
buffer_size: buffer_size.clone(),
117+
sample_format: *sample_format,
118+
});
143119
}
144120
}
145121
}
146122

147123
output.into_iter()
148124
}
149125

150-
fn device_supported_configs(
151-
device: &AudioDeviceInfo,
152-
is_output: bool,
153-
) -> VecIntoIter<SupportedStreamConfigRange> {
126+
fn device_supported_configs(device: &AudioDeviceInfo) -> VecIntoIter<SupportedStreamConfigRange> {
154127
let sample_rates = if !device.sample_rates.is_empty() {
155128
device.sample_rates.as_slice()
156129
} else {
@@ -171,6 +144,7 @@ fn device_supported_configs(
171144
&ALL_FORMATS
172145
};
173146

147+
let buffer_size = buffer_size_range();
174148
let mut output = Vec::with_capacity(sample_rates.len() * channel_counts.len() * formats.len());
175149
for sample_rate in sample_rates {
176150
for channel_count in channel_counts {
@@ -180,25 +154,13 @@ fn device_supported_configs(
180154
// TODO: more channels available in native AAudio
181155
continue;
182156
}
183-
let channel_mask = CHANNEL_MASKS[*channel_count as usize - 1];
184157
for format in formats {
185-
let (android_format, sample_format) = match format {
186-
SampleFormat::I16 => (android_media::ENCODING_PCM_16BIT, SampleFormat::I16),
187-
SampleFormat::F32 => (android_media::ENCODING_PCM_FLOAT, SampleFormat::F32),
188-
_ => panic!("Unexpected format"),
189-
};
190-
let buffer_size = buffer_size_range_for_params(
191-
is_output,
192-
*sample_rate,
193-
channel_mask,
194-
android_format,
195-
);
196158
output.push(SupportedStreamConfigRange {
197159
channels: cmp::min(*channel_count as u16, 2u16),
198160
min_sample_rate: SampleRate(*sample_rate as u32),
199161
max_sample_rate: SampleRate(*sample_rate as u32),
200-
buffer_size,
201-
sample_format,
162+
buffer_size: buffer_size.clone(),
163+
sample_format: *format,
202164
});
203165
}
204166
}
@@ -339,19 +301,19 @@ impl DeviceTrait for Device {
339301
&self,
340302
) -> Result<Self::SupportedInputConfigs, SupportedStreamConfigsError> {
341303
if let Some(info) = &self.0 {
342-
Ok(device_supported_configs(info, false))
304+
Ok(device_supported_configs(info))
343305
} else {
344-
Ok(default_supported_configs(false))
306+
Ok(default_supported_configs())
345307
}
346308
}
347309

348310
fn supported_output_configs(
349311
&self,
350312
) -> Result<Self::SupportedOutputConfigs, SupportedStreamConfigsError> {
351313
if let Some(info) = &self.0 {
352-
Ok(device_supported_configs(info, true))
314+
Ok(device_supported_configs(info))
353315
} else {
354-
Ok(default_supported_configs(true))
316+
Ok(default_supported_configs())
355317
}
356318
}
357319

0 commit comments

Comments
 (0)