feat(audio): implement AAudio C-ABI function bindings
This commit is contained in:
parent
370a13c422
commit
7f6828df0c
3 changed files with 139 additions and 0 deletions
113
crates/molotov-core/src/audio/c_api.rs
Normal file
113
crates/molotov-core/src/audio/c_api.rs
Normal file
|
|
@ -0,0 +1,113 @@
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
use super::builder::AAudioStreamBuilder;
|
||||||
|
use super::pipewire::PipeWireStreamController;
|
||||||
|
use super::types::{AAudioDirection, AAudioResult};
|
||||||
|
|
||||||
|
pub struct AAudioStreamHandle {
|
||||||
|
pub controller: PipeWireStreamController,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn AAudio_createStreamBuilder(
|
||||||
|
builder: *mut *mut AAudioStreamBuilder,
|
||||||
|
) -> i32 {
|
||||||
|
if builder.is_null() {
|
||||||
|
return AAudioResult::ErrorNull as i32;
|
||||||
|
}
|
||||||
|
let b = Box::new(AAudioStreamBuilder::new());
|
||||||
|
*builder = Box::into_raw(b);
|
||||||
|
AAudioResult::Ok as i32
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn AAudioStreamBuilder_setDirection(
|
||||||
|
builder: *mut AAudioStreamBuilder,
|
||||||
|
direction: i32,
|
||||||
|
) {
|
||||||
|
if let Some(b) = builder.as_mut() {
|
||||||
|
let dir = if direction == 1 {
|
||||||
|
AAudioDirection::Input
|
||||||
|
} else {
|
||||||
|
AAudioDirection::Output
|
||||||
|
};
|
||||||
|
b.set_direction(dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn AAudioStreamBuilder_openStream(
|
||||||
|
builder: *mut AAudioStreamBuilder,
|
||||||
|
stream: *mut *mut AAudioStreamHandle,
|
||||||
|
) -> i32 {
|
||||||
|
if builder.is_null() || stream.is_null() {
|
||||||
|
return AAudioResult::ErrorNull as i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
let b = match builder.as_ref() {
|
||||||
|
Some(val) => val,
|
||||||
|
None => return AAudioResult::ErrorNull as i32,
|
||||||
|
};
|
||||||
|
|
||||||
|
let controller = PipeWireStreamController::new(b.direction(), b.sample_rate(), b.channel_count());
|
||||||
|
let handle = Box::new(AAudioStreamHandle { controller });
|
||||||
|
*stream = Box::into_raw(handle);
|
||||||
|
|
||||||
|
AAudioResult::Ok as i32
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn AAudioStream_requestStart(stream: *mut AAudioStreamHandle) -> i32 {
|
||||||
|
if let Some(s) = stream.as_mut() {
|
||||||
|
match s.controller.request_start() {
|
||||||
|
Ok(()) => AAudioResult::Ok as i32,
|
||||||
|
Err(e) => e as i32,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
AAudioResult::ErrorNull as i32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn AAudioStream_requestPause(stream: *mut AAudioStreamHandle) -> i32 {
|
||||||
|
if let Some(s) = stream.as_mut() {
|
||||||
|
match s.controller.request_pause() {
|
||||||
|
Ok(()) => AAudioResult::Ok as i32,
|
||||||
|
Err(e) => e as i32,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
AAudioResult::ErrorNull as i32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn AAudioStream_requestStop(stream: *mut AAudioStreamHandle) -> i32 {
|
||||||
|
if let Some(s) = stream.as_mut() {
|
||||||
|
match s.controller.request_stop() {
|
||||||
|
Ok(()) => AAudioResult::Ok as i32,
|
||||||
|
Err(e) => e as i32,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
AAudioResult::ErrorNull as i32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn AAudioStream_close(stream: *mut AAudioStreamHandle) -> i32 {
|
||||||
|
if !stream.is_null() {
|
||||||
|
let mut handle = Box::from_raw(stream);
|
||||||
|
handle.controller.close();
|
||||||
|
AAudioResult::Ok as i32
|
||||||
|
} else {
|
||||||
|
AAudioResult::ErrorNull as i32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn AAudioStreamBuilder_delete(builder: *mut AAudioStreamBuilder) -> i32 {
|
||||||
|
if !builder.is_null() {
|
||||||
|
drop(Box::from_raw(builder));
|
||||||
|
AAudioResult::Ok as i32
|
||||||
|
} else {
|
||||||
|
AAudioResult::ErrorNull as i32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
pub mod builder;
|
pub mod builder;
|
||||||
pub mod buffer;
|
pub mod buffer;
|
||||||
|
pub mod c_api;
|
||||||
pub mod pipewire;
|
pub mod pipewire;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
|
||||||
25
crates/molotov-core/tests/aaudio_c_api_test.rs
Normal file
25
crates/molotov-core/tests/aaudio_c_api_test.rs
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
use molotov_core::audio::c_api::*;
|
||||||
|
use molotov_core::audio::types::{AAudioDirection, AAudioResult};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_c_api_stream_lifecycle() {
|
||||||
|
let mut builder_ptr = std::ptr::null_mut();
|
||||||
|
unsafe {
|
||||||
|
let res = AAudio_createStreamBuilder(&mut builder_ptr);
|
||||||
|
assert_eq!(res, AAudioResult::Ok as i32);
|
||||||
|
assert!(!builder_ptr.is_null());
|
||||||
|
|
||||||
|
AAudioStreamBuilder_setDirection(builder_ptr, AAudioDirection::Input as i32);
|
||||||
|
|
||||||
|
let mut stream_ptr = std::ptr::null_mut();
|
||||||
|
let res = AAudioStreamBuilder_openStream(builder_ptr, &mut stream_ptr);
|
||||||
|
assert_eq!(res, AAudioResult::Ok as i32);
|
||||||
|
assert!(!stream_ptr.is_null());
|
||||||
|
|
||||||
|
assert_eq!(AAudioStream_requestStart(stream_ptr), AAudioResult::Ok as i32);
|
||||||
|
assert_eq!(AAudioStream_requestPause(stream_ptr), AAudioResult::Ok as i32);
|
||||||
|
assert_eq!(AAudioStream_close(stream_ptr), AAudioResult::Ok as i32);
|
||||||
|
assert_eq!(AAudioStreamBuilder_delete(builder_ptr), AAudioResult::Ok as i32);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in a new issue