Skip to content

Commit b32afbf

Browse files
committed
Merge branch 'master' into master
2 parents 135eb4c + c5a163e commit b32afbf

26 files changed

+1111
-491
lines changed

.github/workflows/cpal.yml

+10-8
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ jobs:
2424
- name: Run clippy
2525
run: cargo clippy --all --all-features
2626
- name: Run clippy for Android target
27-
run: cargo clippy --all --features asio --features oboe/fetch-prebuilt --target armv7-linux-androideabi
27+
run: cargo clippy --all --features asio --target armv7-linux-androideabi
2828

2929
rustfmt-check:
3030
runs-on: ubuntu-latest
@@ -207,15 +207,16 @@ jobs:
207207
with:
208208
target: armv7-linux-androideabi
209209
- name: Check android
210-
run: cargo check --example android --target armv7-linux-androideabi --features oboe/fetch-prebuilt --verbose
210+
working-directory: examples/android
211+
run: cargo check --target armv7-linux-androideabi --verbose
211212
- name: Check beep
212-
run: cargo check --example beep --target armv7-linux-androideabi --features oboe/fetch-prebuilt --verbose
213+
run: cargo check --example beep --target armv7-linux-androideabi --verbose
213214
- name: Check enumerate
214-
run: cargo check --example enumerate --target armv7-linux-androideabi --features oboe/fetch-prebuilt --verbose
215+
run: cargo check --example enumerate --target armv7-linux-androideabi --verbose
215216
- name: Check feedback
216-
run: cargo check --example feedback --target armv7-linux-androideabi --features oboe/fetch-prebuilt --verbose
217+
run: cargo check --example feedback --target armv7-linux-androideabi --verbose
217218
- name: Check record_wav
218-
run: cargo check --example record_wav --target armv7-linux-androideabi --features oboe/fetch-prebuilt --verbose
219+
run: cargo check --example record_wav --target armv7-linux-androideabi --verbose
219220

220221
android-apk-build:
221222
runs-on: ubuntu-latest
@@ -227,11 +228,12 @@ jobs:
227228
targets: armv7-linux-androideabi,aarch64-linux-android,i686-linux-android,x86_64-linux-android
228229
- name: Set Up Android tools
229230
run: |
230-
${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager --sdk_root=$ANDROID_SDK_ROOT --install "platforms;android-30"
231+
${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager --sdk_root=$ANDROID_SDK_ROOT --install "build-tools;30.0.2" "platforms;android-30"
231232
- name: Install Cargo APK
232233
run: cargo install cargo-apk
233234
- name: Build APK
234-
run: cargo apk build --example android
235+
working-directory: examples/android
236+
run: cargo apk build
235237

236238
ios-build:
237239
runs-on: macOS-latest

Cargo.toml

+3-11
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ rust-version = "1.70"
1111

1212
[features]
1313
asio = ["asio-sys", "num-traits"] # Only available on Windows. See README for setup instructions.
14-
oboe-shared-stdcxx = ["oboe/shared-stdcxx"] # Only available on Android. See README for what it does.
1514
jack = ["dep:jack"]
1615
pipewire = ["dep:pipewire-client", "dep:tokio"]
1716

@@ -24,9 +23,6 @@ hound = "3.5"
2423
ringbuf = "0.4.1"
2524
clap = { version = "4.0", features = ["derive"] }
2625

27-
[target.'cfg(target_os = "android")'.dev-dependencies]
28-
ndk-glue = "0.7"
29-
3026
[target.'cfg(target_os = "windows")'.dependencies]
3127
windows = { version = "0.54.0", features = [
3228
"Win32_Media_Audio",
@@ -73,15 +69,11 @@ js-sys = { version = "0.3.35" }
7369
web-sys = { version = "0.3.35", features = [ "AudioContext", "AudioContextOptions", "AudioBuffer", "AudioBufferSourceNode", "AudioNode", "AudioDestinationNode", "Window", "AudioContextState"] }
7470

7571
[target.'cfg(target_os = "android")'.dependencies]
76-
oboe = { version = "0.6", features = [ "java-interface" ] }
77-
ndk = { version = "0.8", default-features = false }
72+
ndk = { version = "0.9", default-features = false, features = ["audio", "api-level-26"]}
7873
ndk-context = "0.1"
7974
jni = "0.21"
80-
81-
[[example]]
82-
name = "android"
83-
path = "examples/android.rs"
84-
crate-type = ["cdylib"]
75+
num-derive = "0.4"
76+
num-traits = "0.2"
8577

8678
[[example]]
8779
name = "beep"

README.md

+1-5
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ Currently, supported hosts include:
2020
- Windows (via WASAPI by default, see ASIO instructions below)
2121
- macOS (via CoreAudio)
2222
- iOS (via CoreAudio)
23-
- Android (via Oboe)
23+
- Android (via AAudio)
2424
- Emscripten
2525

2626
Note that on Linux, the ALSA development files are required. These are provided
@@ -43,10 +43,6 @@ Some audio backends are optional and will only be compiled with a [feature flag]
4343
- PipeWire (on Linux): `pipewire` (currently in testing, feel free to share your feedback!)
4444
- ASIO (on Windows): `asio`
4545

46-
Oboe can either use a shared or static runtime. The static runtime is used by default, but activating the
47-
`oboe-shared-stdcxx` feature makes it use the shared runtime, which requires `libc++_shared.so` from the Android NDK to
48-
be present during execution.
49-
5046
## ASIO on Windows
5147

5248
[ASIO](https://en.wikipedia.org/wiki/Audio_Stream_Input/Output) is an audio

examples/android/.gitignore

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/target
2+
/Cargo.lock
3+
.cargo/
4+
.DS_Store
5+
recorded.wav
6+
rls*.log

examples/android/Cargo.toml

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
[package]
2+
name = "android"
3+
version = "0.1.0"
4+
edition = "2024"
5+
6+
[dependencies]
7+
cpal = { path = "../../" }
8+
anyhow = "1.0"
9+
ndk-glue = "0.7"
10+
11+
[lib]
12+
name = "android"
13+
path = "src/lib.rs"
14+
crate-type = ["cdylib"]
15+
16+
[package.metadata.android]
17+
# Specifies the package property of the manifest.
18+
package = "com.foo.bar"
19+
20+
# Specifies the array of targets to build for.
21+
build_targets = [ "armv7-linux-androideabi", "aarch64-linux-android", "i686-linux-android", "x86_64-linux-android" ]
22+
23+
# Name for final APK file.
24+
# Defaults to package name.
25+
apk_name = "myapp"
26+
27+
[package.metadata.android.sdk]
28+
min_sdk_version = 26
29+
target_sdk_version = 30
30+
max_sdk_version = 29

examples/android/README.md

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
## How to install
2+
3+
```sh
4+
rustup target add aarch64-linux-android armv7-linux-androideabi i686-linux-android x86_64-linux-android
5+
```
6+
7+
## How to build apk
8+
9+
```sh
10+
# Builds the project in release mode and places it into a `apk` file.
11+
cargo apk build --release
12+
```
13+
14+
more information at: https://github.com/rust-mobile/cargo-apk

examples/android.rs renamed to examples/android/src/lib.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ extern crate cpal;
55

66
use cpal::{
77
traits::{DeviceTrait, HostTrait, StreamTrait},
8-
SizedSample,
8+
SizedSample, I24,
99
};
1010
use cpal::{FromSample, Sample};
1111

@@ -22,7 +22,7 @@ fn main() {
2222
match config.sample_format() {
2323
cpal::SampleFormat::I8 => run::<i8>(&device, &config.into()).unwrap(),
2424
cpal::SampleFormat::I16 => run::<i16>(&device, &config.into()).unwrap(),
25-
// cpal::SampleFormat::I24 => run::<I24>(&device, &config.into()).unwrap(),
25+
cpal::SampleFormat::I24 => run::<I24>(&device, &config.into()).unwrap(),
2626
cpal::SampleFormat::I32 => run::<i32>(&device, &config.into()).unwrap(),
2727
// cpal::SampleFormat::I48 => run::<I48>(&device, &config.into()).unwrap(),
2828
cpal::SampleFormat::I64 => run::<i64>(&device, &config.into()).unwrap(),

examples/beep.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use clap::Parser;
22
use cpal::{
33
traits::{DeviceTrait, HostTrait, StreamTrait},
4-
FromSample, Sample, SizedSample,
4+
FromSample, Sample, SizedSample, I24,
55
};
66

77
#[derive(Parser, Debug)]
@@ -118,7 +118,7 @@ fn main() -> anyhow::Result<()> {
118118
match config.sample_format() {
119119
cpal::SampleFormat::I8 => run::<i8>(&device, &config.into()),
120120
cpal::SampleFormat::I16 => run::<i16>(&device, &config.into()),
121-
// cpal::SampleFormat::I24 => run::<I24>(&device, &config.into()),
121+
cpal::SampleFormat::I24 => run::<I24>(&device, &config.into()),
122122
cpal::SampleFormat::I32 => run::<i32>(&device, &config.into()),
123123
// cpal::SampleFormat::I48 => run::<I48>(&device, &config.into()),
124124
cpal::SampleFormat::I64 => run::<i64>(&device, &config.into()),

examples/synth_tones.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ extern crate cpal;
88

99
use cpal::{
1010
traits::{DeviceTrait, HostTrait, StreamTrait},
11-
SizedSample,
11+
SizedSample, I24,
1212
};
1313
use cpal::{FromSample, Sample};
1414

@@ -98,6 +98,7 @@ where
9898
match config.sample_format() {
9999
cpal::SampleFormat::I8 => make_stream::<i8>(&device, &config.into()),
100100
cpal::SampleFormat::I16 => make_stream::<i16>(&device, &config.into()),
101+
cpal::SampleFormat::I24 => make_stream::<I24>(&device, &config.into()),
101102
cpal::SampleFormat::I32 => make_stream::<i32>(&device, &config.into()),
102103
cpal::SampleFormat::I64 => make_stream::<i64>(&device, &config.into()),
103104
cpal::SampleFormat::U8 => make_stream::<u8>(&device, &config.into()),
File renamed without changes.

src/host/oboe/convert.rs renamed to src/host/aaudio/convert.rs

+23-24
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::convert::TryInto;
22
use std::time::Duration;
33

4-
extern crate oboe;
4+
extern crate ndk;
55

66
use crate::{
77
BackendSpecificError, BuildStreamError, PauseStreamError, PlayStreamError, StreamError,
@@ -15,22 +15,21 @@ pub fn to_stream_instant(duration: Duration) -> StreamInstant {
1515
)
1616
}
1717

18-
pub fn stream_instant<T: oboe::AudioStreamSafe + ?Sized>(stream: &mut T) -> StreamInstant {
19-
const CLOCK_MONOTONIC: i32 = 1;
18+
pub fn stream_instant(stream: &ndk::audio::AudioStream) -> StreamInstant {
2019
let ts = stream
21-
.get_timestamp(CLOCK_MONOTONIC)
22-
.unwrap_or(oboe::FrameTimestamp {
23-
position: 0,
24-
timestamp: 0,
20+
.timestamp(ndk::audio::Clockid::Monotonic)
21+
.unwrap_or(ndk::audio::Timestamp {
22+
frame_position: 0,
23+
time_nanoseconds: 0,
2524
});
26-
to_stream_instant(Duration::from_nanos(ts.timestamp as u64))
25+
to_stream_instant(Duration::from_nanos(ts.time_nanoseconds as u64))
2726
}
2827

29-
impl From<oboe::Error> for StreamError {
30-
fn from(error: oboe::Error) -> Self {
31-
use self::oboe::Error::*;
28+
impl From<ndk::audio::AudioError> for StreamError {
29+
fn from(error: ndk::audio::AudioError) -> Self {
30+
use self::ndk::audio::AudioError::*;
3231
match error {
33-
Disconnected | Unavailable | Closed => Self::DeviceNotAvailable,
32+
Disconnected | Unavailable => Self::DeviceNotAvailable,
3433
e => (BackendSpecificError {
3534
description: e.to_string(),
3635
})
@@ -39,11 +38,11 @@ impl From<oboe::Error> for StreamError {
3938
}
4039
}
4140

42-
impl From<oboe::Error> for PlayStreamError {
43-
fn from(error: oboe::Error) -> Self {
44-
use self::oboe::Error::*;
41+
impl From<ndk::audio::AudioError> for PlayStreamError {
42+
fn from(error: ndk::audio::AudioError) -> Self {
43+
use self::ndk::audio::AudioError::*;
4544
match error {
46-
Disconnected | Unavailable | Closed => Self::DeviceNotAvailable,
45+
Disconnected | Unavailable => Self::DeviceNotAvailable,
4746
e => (BackendSpecificError {
4847
description: e.to_string(),
4948
})
@@ -52,11 +51,11 @@ impl From<oboe::Error> for PlayStreamError {
5251
}
5352
}
5453

55-
impl From<oboe::Error> for PauseStreamError {
56-
fn from(error: oboe::Error) -> Self {
57-
use self::oboe::Error::*;
54+
impl From<ndk::audio::AudioError> for PauseStreamError {
55+
fn from(error: ndk::audio::AudioError) -> Self {
56+
use self::ndk::audio::AudioError::*;
5857
match error {
59-
Disconnected | Unavailable | Closed => Self::DeviceNotAvailable,
58+
Disconnected | Unavailable => Self::DeviceNotAvailable,
6059
e => (BackendSpecificError {
6160
description: e.to_string(),
6261
})
@@ -65,11 +64,11 @@ impl From<oboe::Error> for PauseStreamError {
6564
}
6665
}
6766

68-
impl From<oboe::Error> for BuildStreamError {
69-
fn from(error: oboe::Error) -> Self {
70-
use self::oboe::Error::*;
67+
impl From<ndk::audio::AudioError> for BuildStreamError {
68+
fn from(error: ndk::audio::AudioError) -> Self {
69+
use self::ndk::audio::AudioError::*;
7170
match error {
72-
Disconnected | Unavailable | Closed => Self::DeviceNotAvailable,
71+
Disconnected | Unavailable => Self::DeviceNotAvailable,
7372
NoFreeHandles => Self::StreamIdOverflow,
7473
InvalidFormat | InvalidRate => Self::StreamConfigNotSupported,
7574
IllegalArgument => Self::InvalidArgument,

src/host/aaudio/java_interface.rs

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
mod audio_features;
2+
mod definitions;
3+
mod devices_info;
4+
mod utils;
5+
6+
pub use self::audio_features::*;
7+
pub use self::definitions::*;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
use super::{
2+
utils::{
3+
get_context, get_package_manager, has_system_feature, with_attached, JNIEnv, JObject,
4+
JResult,
5+
},
6+
PackageManager,
7+
};
8+
9+
/**
10+
* The Android audio features
11+
*/
12+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
13+
pub enum AudioFeature {
14+
LowLatency,
15+
Output,
16+
Pro,
17+
Microphone,
18+
Midi,
19+
}
20+
21+
impl From<AudioFeature> for &'static str {
22+
fn from(feature: AudioFeature) -> Self {
23+
use AudioFeature::*;
24+
match feature {
25+
LowLatency => PackageManager::FEATURE_AUDIO_LOW_LATENCY,
26+
Output => PackageManager::FEATURE_AUDIO_OUTPUT,
27+
Pro => PackageManager::FEATURE_AUDIO_PRO,
28+
Microphone => PackageManager::FEATURE_MICROPHONE,
29+
Midi => PackageManager::FEATURE_MIDI,
30+
}
31+
}
32+
}
33+
34+
impl AudioFeature {
35+
/**
36+
* Check availability of an audio feature using Android Java API
37+
*/
38+
pub fn has(&self) -> Result<bool, String> {
39+
let context = get_context();
40+
41+
with_attached(context, |env, activity| {
42+
try_check_system_feature(env, &activity, (*self).into())
43+
})
44+
.map_err(|error| error.to_string())
45+
}
46+
}
47+
48+
fn try_check_system_feature<'j>(
49+
env: &mut JNIEnv<'j>,
50+
activity: &JObject<'j>,
51+
feature: &str,
52+
) -> JResult<bool> {
53+
let package_manager = get_package_manager(env, activity)?;
54+
55+
has_system_feature(env, &package_manager, feature)
56+
}

0 commit comments

Comments
 (0)