Skip to content

Conversation

@will3942
Copy link
Contributor

@will3942 will3942 commented Oct 27, 2025

AAudio buffer size minimum should be detected using the PROPERTY_OUTPUT_FRAMES_PER_BUFFER on the AudioManager not using AudioTrack.

Android documentation reference: https://developer.android.com/ndk/guides/audio/audio-latency#buffer-size

This allows for a sensible buffer size minimum to be used in comparison to the 1 second+ buffer sizes reported by AudioTrack.

Tested on a Samsung Galaxy Tab A9: SupportedStreamConfigRange { channels: 1, min_sample_rate: SampleRate(5512), max_sample_rate: SampleRate(5512), buffer_size: Range { min: 256, max: 2147483647 }, sample_format: I16 }

Closes: #890

@will3942 will3942 changed the title fix(aaudio): Correctly detect minimum buffer size. fix(aaudio): Correctly detect minimum buffer size Oct 27, 2025
Copy link
Member

@roderickvd roderickvd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the contribution!

@will3942
Copy link
Contributor Author

Thanks for the contribution!

Thanks for the review - will address all feedback in the morning. The Q around input is an interesting one, I couldn't see anything different for input in their docs but also I agree the property is named after output so...

@will3942
Copy link
Contributor Author

will3942 commented Oct 29, 2025

This works well now - longer term I think we should look at a few feature requests:

  1. Allow querying the configuration that the stream is using - e.g. if you use BufferSize::Default, you can see the buffer size the underlying host has used. AAudio supports this, I'm sure others do too.
  2. Edit the stream configuration (where supported) after construction - AAudio supports/recommends this for buffer size tuning
  3. Bringing back Oboe support - Oboe does some magic around buffer size / latency setting that we could leverage. The Android docs for ndk/aaudio recommend this.

irh added a commit to irh/cpal that referenced this pull request Nov 8, 2025
irh added a commit to irh/cpal that referenced this pull request Nov 8, 2025
Copy link
Member

@roderickvd roderickvd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for addressing the earlier comments. Here's a couple of what should be the last nits.


frames_per_buffer_string
.parse::<i32>()
.map_err(|e| jni::errors::Error::JniCall(jni::errors::JniError::Unknown))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to retain the error message and return something like jni::errors::Error::ParseError(format!("Failed to parse frames per buffer: {}", e))?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've fixed this, the master version of jni is nicer, to do this with the current supported version I have had to add combine as a dependency which I don't love. Let me know what you think? I can't see a great solution here for passing the error back through.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, if we can prevent pulling in another dependency then let's put in that effort. How about JniCall(JniError::Other(String))?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JniError::Other(jint) is the signature so it won't accept a string.

We only really have ParseFailed with the additional dependency or FieldAlreadySet(String)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@roderickvd The crate jni will get released at some point which will allow us to use ParseFailed(String) without an external dependency. In the meantime I think we have three choices:

  1. Pull in combine dependency and use ParseFailed
  2. Use FieldAlreadySet(String)
  3. Add a TODO to update to ParseFailed(String) on jni release and just return the error without the string.

I'm leaning towards 3 or 2 (also with a TODO).

What's your preference? I can then implement this - I'd love to get this across the line with the next release so we don't have to run a patch for cpal.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated this and gone with 3 - this feels the best in my mind, let me know if you want me to change.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense. Let's not spend too much time over it. If you'd want to track it and deal with it later?

@roderickvd
Copy link
Member

This works well now - longer term I think we should look at a few feature requests:

  1. Allow querying the configuration that the stream is using - e.g. if you use BufferSize::Default, you can see the buffer size the underlying host has used. AAudio supports this, I'm sure others do too.

Yes. #964 also added querying latency - we could go from there.

  1. Edit the stream configuration (where supported) after construction - AAudio supports/recommends this for buffer size tuning

That's interesting for mobile platforms. I believe iOS does too?
Question is how this could work elegantly in cpal.

  1. Bringing back Oboe support - Oboe does some magic around buffer size / latency setting that we could leverage. The Android docs for ndk/aaudio recommend this.

You know why Oboe was removed in the first place? (I don't.)

@will3942
Copy link
Contributor Author

Yes. #964 also added querying latency - we could go from there.

Agreed. Documeted as an issue / feature request - #1042

That's interesting for mobile platforms. I believe iOS does too? Question is how this could work elegantly in cpal.

This could work as functions on the Stream trait? Although I'm not sure if this is universally supported functionality across platforms.

You know why Oboe was removed in the first place? (I don't.)

Pros/cons where detailed here - #961

@will3942
Copy link
Contributor Author

@roderickvd addressed the nits - let me know your feedback on the last one.

@roderickvd
Copy link
Member

That's interesting for mobile platforms. I believe iOS does too? Question is how this could work elegantly in cpal.

This could work as functions on the Stream trait? Although I'm not sure if this is universally supported functionality across platforms.

We'd have to map out the API and judge how it'd impact the various hosts. Preferably, it'd keep things simple.

The ALSA host would also be a candidate, because we can play the same game: requesting a large buffer with X periods but only using the first Y periods of it, and dynamically tuning Y. But, to the point of keeping things simple, we'd have to play around to see how easy it is to do that in the poll cycle.

You know why Oboe was removed in the first place? (I don't.)

Pros/cons where detailed here - #961

Ah, thanks, could've looked for that myself 😊 That makes it seem like there's almost no cons and all pros, yet you refer to something that Oboe does by itself and NDK doesn't? If we can implement dynamic buffers without the weight of Oboe, that'd also be nice, no?

@roderickvd addressed the nits - let me know your feedback on the last one.

👍 I'll take a look.

const CHANNEL_CONFIGS: [(i32, u16); 2] = [(CHANNEL_OUT_MONO, 1), (CHANNEL_OUT_STEREO, 2)];

const SAMPLE_RATES: [i32; 13] = [
5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000, 88200, 96000, 176_400, 192_000,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this isn't yours, but now I read it I think that it should also support 12 and 24 kHz.

@roderickvd
Copy link
Member

Thanks for your PR and efforts. I'm gonna merge this now, and add the 12/24 kHz after manually. Let me know if I'm wrong.

@roderickvd roderickvd merged commit a23a954 into RustAudio:master Nov 14, 2025
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Min buffer size range detected on Android is huge (~1sec)

2 participants