mirror of
https://github.com/touchHLE/touchHLE.git
synced 2026-01-31 01:25:24 +01:00
Begin implementing OpenAL (currently just context setup etc)
This commit is contained in:
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -4,3 +4,6 @@
|
||||
[submodule "dynarmic"]
|
||||
path = vendor/dynarmic
|
||||
url = https://github.com/merryhime/dynarmic.git
|
||||
[submodule "openal-soft"]
|
||||
path = vendor/openal-soft
|
||||
url = https://github.com/kcat/openal-soft
|
||||
|
||||
8
Cargo.lock
generated
8
Cargo.lock
generated
@@ -271,6 +271,7 @@ dependencies = [
|
||||
"sdl2-sys",
|
||||
"touchHLE_dynarmic_wrapper",
|
||||
"touchHLE_gl_bindings",
|
||||
"touchHLE_openal_soft_wrapper",
|
||||
"touchHLE_stb_image_wrapper",
|
||||
]
|
||||
|
||||
@@ -289,6 +290,13 @@ dependencies = [
|
||||
"gl_generator",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "touchHLE_openal_soft_wrapper"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cmake",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "touchHLE_stb_image_wrapper"
|
||||
version = "0.1.0"
|
||||
|
||||
@@ -26,6 +26,7 @@ sdl2 = { version = "=0.35.1", features = ["bundled", "static-link"] }
|
||||
sdl2-sys = "=0.35.1"
|
||||
touchHLE_dynarmic_wrapper = { path = "src/cpu/dynarmic_wrapper" }
|
||||
touchHLE_gl_bindings = { path = "src/window/gl_bindings" }
|
||||
touchHLE_openal_soft_wrapper = { path = "src/audio/openal_soft_wrapper" }
|
||||
touchHLE_stb_image_wrapper = { path = "src/image/stb_image_wrapper" }
|
||||
|
||||
[build-dependencies]
|
||||
|
||||
@@ -65,7 +65,7 @@ TBD
|
||||
|
||||
We stand on the shoulders of giants. Thank you to:
|
||||
|
||||
* The authors of and contributors to the many libraries used by this project: [dynarmic](https://github.com/merryhime/dynarmic), [rust-macho](https://github.com/flier/rust-macho), [SDL](https://libsdl.org/), [rust-sdl2](https://github.com/Rust-SDL2/rust-sdl2), [stb\_image](https://github.com/nothings/stb), [rust-plist](https://github.com/ebarnard/rust-plist), [gl-rs](https://github.com/brendanzab/gl-rs), [cc-rs](https://github.com/rust-lang/cc-rs), [cmake-rs](https://github.com/rust-lang/cmake-rs), and the Rust standard library.
|
||||
* The authors of and contributors to the many libraries used by this project: [dynarmic](https://github.com/merryhime/dynarmic), [rust-macho](https://github.com/flier/rust-macho), [SDL](https://libsdl.org/), [rust-sdl2](https://github.com/Rust-SDL2/rust-sdl2), [stb\_image](https://github.com/nothings/stb), [openal-soft](https://github.com/kcat/openal-soft), [rust-plist](https://github.com/ebarnard/rust-plist), [gl-rs](https://github.com/brendanzab/gl-rs), [cc-rs](https://github.com/rust-lang/cc-rs), [cmake-rs](https://github.com/rust-lang/cmake-rs), and the Rust standard library.
|
||||
* The [Rust project](https://www.rust-lang.org/) generally.
|
||||
* The various people out there who've documented the iPhone OS platform, officially or otherwise. Much of this documentation is linked to within this codebase!
|
||||
* The Free Software Foundation, for making libgcc and libstdc++ copyleft and therefore saving this project from ABI hell.
|
||||
|
||||
3
src/audio.rs
Normal file
3
src/audio.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
//! Audio utilities, currently just OpenAL bindings.
|
||||
|
||||
pub mod openal;
|
||||
79
src/audio/openal.rs
Normal file
79
src/audio/openal.rs
Normal file
@@ -0,0 +1,79 @@
|
||||
//! OpenAL bindings (linked to OpenAL Soft via [touchHLE_openal_soft_wrapper]).
|
||||
//!
|
||||
//! See `vendor/openal-soft/AL/` for the headers this should mirror.
|
||||
|
||||
// === alc.h ===
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub mod alc_types {
|
||||
use std::ffi;
|
||||
|
||||
// TODO: If Rust ever stabilises a good way to do opaque types, use that
|
||||
// instead of a typedef of void.
|
||||
/// Opaque type.
|
||||
pub type ALCdevice = ffi::c_void;
|
||||
/// Opaque type.
|
||||
pub type ALCcontext = ffi::c_void;
|
||||
|
||||
pub type ALCboolean = ffi::c_char;
|
||||
pub type ALCchar = ffi::c_char;
|
||||
pub type ALCbyte = ffi::c_schar;
|
||||
pub type ALCubyte = ffi::c_uchar;
|
||||
pub type ALCshort = ffi::c_short;
|
||||
pub type ALCushort = ffi::c_ushort;
|
||||
pub type ALCint = ffi::c_int;
|
||||
pub type ALCuint = ffi::c_uint;
|
||||
pub type ALCsizei = ffi::c_int;
|
||||
pub type ALCenum = ffi::c_int;
|
||||
pub type ALCfloat = ffi::c_float;
|
||||
pub type ALCdouble = ffi::c_double;
|
||||
pub type ALCvoid = ffi::c_void;
|
||||
}
|
||||
use alc_types::*;
|
||||
|
||||
pub const ALC_FALSE: ALCboolean = 0;
|
||||
#[allow(dead_code)]
|
||||
pub const ALC_TRUE: ALCboolean = 1;
|
||||
|
||||
#[link(name = "openal")] // see also src/audio/openal_soft_wrapper/build.rs
|
||||
extern "C" {
|
||||
pub fn alcOpenDevice(devicename: *const ALCchar) -> *mut ALCdevice;
|
||||
pub fn alcCloseDevice(device: *mut ALCdevice) -> ALCboolean;
|
||||
|
||||
pub fn alcCreateContext(device: *mut ALCdevice, attrlist: *const ALCint) -> *mut ALCcontext;
|
||||
pub fn alcDestroyContext(context: *mut ALCcontext);
|
||||
|
||||
pub fn alcMakeContextCurrent(context: *mut ALCcontext) -> ALCboolean;
|
||||
pub fn alcGetCurrentContext() -> *mut ALCcontext;
|
||||
|
||||
pub fn alcGetError(device: *mut ALCdevice) -> ALCenum;
|
||||
}
|
||||
|
||||
// === al.h ===
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub mod al_types {
|
||||
use std::ffi;
|
||||
|
||||
pub type ALboolean = ffi::c_char;
|
||||
pub type ALchar = ffi::c_char;
|
||||
pub type ALbyte = ffi::c_schar;
|
||||
pub type ALubyte = ffi::c_uchar;
|
||||
pub type ALshort = ffi::c_short;
|
||||
pub type ALushort = ffi::c_ushort;
|
||||
pub type ALint = ffi::c_int;
|
||||
pub type ALuint = ffi::c_uint;
|
||||
pub type ALsizei = ffi::c_int;
|
||||
pub type ALenum = ffi::c_int;
|
||||
pub type ALfloat = ffi::c_float;
|
||||
pub type ALdouble = ffi::c_double;
|
||||
pub type ALvoid = ffi::c_void;
|
||||
}
|
||||
use al_types::*;
|
||||
|
||||
pub const AL_NO_ERROR: ALenum = 0;
|
||||
|
||||
#[link(name = "openal")] // see also src/audio/openal_soft_wrapper/build.rs
|
||||
extern "C" {
|
||||
pub fn alGetError() -> ALenum;
|
||||
}
|
||||
10
src/audio/openal_soft_wrapper/Cargo.toml
Normal file
10
src/audio/openal_soft_wrapper/Cargo.toml
Normal file
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "touchHLE_openal_soft_wrapper"
|
||||
version = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
|
||||
[lib]
|
||||
path = "lib.rs"
|
||||
|
||||
[build-dependencies]
|
||||
cmake = { workspace = true }
|
||||
27
src/audio/openal_soft_wrapper/build.rs
Normal file
27
src/audio/openal_soft_wrapper/build.rs
Normal file
@@ -0,0 +1,27 @@
|
||||
use std::path::Path;
|
||||
|
||||
/*fn rerun_if_changed(path: &Path) {
|
||||
println!("cargo:rerun-if-changed={}", path.to_str().unwrap());
|
||||
}*/
|
||||
fn link_search(path: &Path) {
|
||||
println!("cargo:rustc-link-search=native={}", path.to_str().unwrap());
|
||||
}
|
||||
fn link_lib(lib: &str) {
|
||||
println!("cargo:rustc-link-lib=static={}", lib);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// TODO: test this on Windows
|
||||
|
||||
let package_root = Path::new(env!("CARGO_MANIFEST_DIR"));
|
||||
let workspace_root = package_root.join("../../..");
|
||||
|
||||
let mut build = cmake::Config::new(workspace_root.join("vendor/openal-soft"));
|
||||
build.define("LIBTYPE", "STATIC");
|
||||
let openal_soft_out = build.build();
|
||||
|
||||
link_search(&openal_soft_out.join("lib"));
|
||||
link_lib("openal");
|
||||
// rerun-if-changed seems to not work if pointed to a directory :(
|
||||
//rerun_if_changed(&workspace_root.join("vendor/openal-soft"));
|
||||
}
|
||||
8
src/audio/openal_soft_wrapper/lib.rs
Normal file
8
src/audio/openal_soft_wrapper/lib.rs
Normal file
@@ -0,0 +1,8 @@
|
||||
//! This is separated out into its own package so that we can avoid rebuilding
|
||||
//! OpenAL Soft more often than necessary, and to improve build-time
|
||||
//! parallelism. This package **only** exists to build OpenAL Soft, it exports
|
||||
//! no Rust symbols at all! The bindings are found in the main crate.
|
||||
|
||||
// Allow the crate to have a non-snake-case name (touchHLE).
|
||||
// This also allows items in the crate to have non-snake-case names.
|
||||
#![allow(non_snake_case)]
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Separate module just for the function lists, since this will probably be a
|
||||
//! very long and frequently-updated list.
|
||||
|
||||
use crate::frameworks::{core_foundation, opengles, uikit};
|
||||
use crate::frameworks::{core_foundation, openal, opengles, uikit};
|
||||
use crate::libc;
|
||||
|
||||
/// All the lists of functions that the linker should search through.
|
||||
@@ -17,6 +17,7 @@ pub const FUNCTION_LISTS: &[super::FunctionExports] = &[
|
||||
core_foundation::cf_bundle::FUNCTIONS,
|
||||
core_foundation::cf_type::FUNCTIONS,
|
||||
core_foundation::cf_url::FUNCTIONS,
|
||||
openal::FUNCTIONS,
|
||||
opengles::FUNCTIONS,
|
||||
uikit::FUNCTIONS,
|
||||
];
|
||||
|
||||
@@ -18,6 +18,7 @@ pub mod core_animation;
|
||||
pub mod core_foundation;
|
||||
pub mod core_graphics;
|
||||
pub mod foundation;
|
||||
pub mod openal;
|
||||
pub mod opengles;
|
||||
pub mod uikit;
|
||||
|
||||
@@ -25,6 +26,7 @@ pub mod uikit;
|
||||
#[derive(Default)]
|
||||
pub struct State {
|
||||
foundation: foundation::State,
|
||||
openal: openal::State,
|
||||
opengles: opengles::State,
|
||||
uikit: uikit::State,
|
||||
}
|
||||
|
||||
163
src/frameworks/openal.rs
Normal file
163
src/frameworks/openal.rs
Normal file
@@ -0,0 +1,163 @@
|
||||
//! OpenAL.
|
||||
//!
|
||||
//! This is a thin layer on top of OpenAL Soft, see [crate::audio::openal].
|
||||
//!
|
||||
//! Resources:
|
||||
//! - [OpenAL 1.1 specification](https://www.openal.org/documentation/openal-1.1-specification.pdf)
|
||||
|
||||
use crate::audio::openal as al;
|
||||
use crate::audio::openal::alc_types::*;
|
||||
use crate::dyld::{export_c_func, FunctionExports};
|
||||
use crate::mem::{ConstPtr, ConstVoidPtr, MutPtr, Ptr, SafeWrite};
|
||||
use crate::Environment;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct State {
|
||||
devices: HashMap<MutPtr<GuestALCdevice>, *mut ALCdevice>,
|
||||
contexts: HashMap<MutPtr<GuestALCcontext>, *mut ALCcontext>,
|
||||
}
|
||||
impl State {
|
||||
pub fn get(env: &mut Environment) -> &mut Self {
|
||||
&mut env.framework_state.openal
|
||||
}
|
||||
}
|
||||
|
||||
/// Opaque type in guest memory standing in for [ALCdevice] in host memory.
|
||||
pub struct GuestALCdevice {
|
||||
_filler: u8,
|
||||
}
|
||||
impl SafeWrite for GuestALCdevice {}
|
||||
/// Opaque type in guest memory standing in for [ALCcontext] in host memory.
|
||||
pub struct GuestALCcontext {
|
||||
_filler: u8,
|
||||
}
|
||||
impl SafeWrite for GuestALCcontext {}
|
||||
|
||||
// === alc.h ===
|
||||
|
||||
pub fn alcOpenDevice(env: &mut Environment, devicename: ConstPtr<u8>) -> MutPtr<GuestALCdevice> {
|
||||
// NULL means you don't care what device is opened. If an app tries to use
|
||||
// a specific device name, it's probably going to be something specific to
|
||||
// Apple and fail, so let's assert just in case that happens.
|
||||
assert!(devicename.is_null());
|
||||
|
||||
let res = unsafe { al::alcOpenDevice(std::ptr::null()) };
|
||||
if res.is_null() {
|
||||
log_dbg!("alcOpenDevice(NULL) returned NULL");
|
||||
return Ptr::null();
|
||||
}
|
||||
|
||||
let guest_res = env.mem.alloc_and_write(GuestALCdevice { _filler: 0 });
|
||||
State::get(env).devices.insert(guest_res, res);
|
||||
log_dbg!("alcOpenDevice(NULL) => {:?} (host: {:?})", guest_res, res,);
|
||||
guest_res
|
||||
}
|
||||
pub fn alcCloseDevice(env: &mut Environment, device: MutPtr<GuestALCdevice>) -> bool {
|
||||
let host_device = State::get(env).devices.remove(&device).unwrap();
|
||||
env.mem.free(device.cast());
|
||||
let res = unsafe { al::alcCloseDevice(host_device) };
|
||||
log_dbg!("alcCloseDevice({:?}) => {:?}", device, res,);
|
||||
res != al::ALC_FALSE
|
||||
}
|
||||
|
||||
pub fn alcGetError(env: &mut Environment, device: MutPtr<GuestALCdevice>) -> i32 {
|
||||
let &host_device = State::get(env).devices.get(&device).unwrap();
|
||||
|
||||
let res = unsafe { al::alcGetError(host_device) };
|
||||
log_dbg!("alcGetError({:?}) => {:#x}", host_device, res);
|
||||
res
|
||||
}
|
||||
|
||||
pub fn alcCreateContext(
|
||||
env: &mut Environment,
|
||||
device: MutPtr<GuestALCdevice>,
|
||||
attrlist: ConstPtr<i32>,
|
||||
) -> MutPtr<GuestALCcontext> {
|
||||
assert!(attrlist.is_null()); // unimplemented
|
||||
|
||||
let &host_device = State::get(env).devices.get(&device).unwrap();
|
||||
|
||||
let res = unsafe { al::alcCreateContext(host_device, std::ptr::null()) };
|
||||
if res.is_null() {
|
||||
log_dbg!("alcCreateContext({:?}, NULL) returned NULL", device);
|
||||
return Ptr::null();
|
||||
}
|
||||
|
||||
let guest_res = env.mem.alloc_and_write(GuestALCcontext { _filler: 0 });
|
||||
State::get(env).contexts.insert(guest_res, res);
|
||||
log_dbg!(
|
||||
"alcCreateContext({:?}, NULL) => {:?} (host: {:?})",
|
||||
device,
|
||||
guest_res,
|
||||
res,
|
||||
);
|
||||
guest_res
|
||||
}
|
||||
pub fn alcDestroyContext(env: &mut Environment, context: MutPtr<GuestALCcontext>) {
|
||||
let host_context = State::get(env).contexts.remove(&context).unwrap();
|
||||
env.mem.free(context.cast());
|
||||
unsafe { al::alcDestroyContext(host_context) };
|
||||
log_dbg!("alcDestroyContext({:?})", context);
|
||||
}
|
||||
|
||||
pub fn alcMakeContextCurrent(env: &mut Environment, context: MutPtr<GuestALCcontext>) -> bool {
|
||||
let host_context = if context.is_null() {
|
||||
std::ptr::null_mut()
|
||||
} else {
|
||||
State::get(env).contexts.get(&context).copied().unwrap()
|
||||
};
|
||||
let res = unsafe { al::alcMakeContextCurrent(host_context) };
|
||||
log_dbg!("alcMakeContextCurrent({:?}) => {:?}", context, res);
|
||||
res != al::ALC_FALSE
|
||||
}
|
||||
|
||||
pub fn alcGetProcAddress(
|
||||
env: &mut Environment,
|
||||
_device: ConstPtr<GuestALCdevice>,
|
||||
func_name: ConstPtr<u8>,
|
||||
) -> ConstVoidPtr {
|
||||
// Apple-specific extension that Super Monkey Ball tries to use.
|
||||
// Conveniently, if NULL is returned, it just skips trying to use it, so
|
||||
// let's do that.
|
||||
if env.mem.cstr_at_utf8(func_name) == "alcMacOSXMixerOutputRate" {
|
||||
// Warn in case other apps don't check for NULL. The spec doesn't even
|
||||
// mention that as a possibility.
|
||||
log!("Returning NULL for alcGetProcAddress(..., \"alcMacOSXMixerOutputRate\").");
|
||||
return Ptr::null();
|
||||
}
|
||||
unimplemented!(); // TODO general implementation
|
||||
}
|
||||
|
||||
// TODO: more functions
|
||||
|
||||
// === al.h ===
|
||||
|
||||
pub fn alGetError(_env: &mut Environment) -> i32 {
|
||||
// Super Monkey Ball tries to use this function (rather than alcGetError) to
|
||||
// figure out whether opening the device succeeded. This is not correct and
|
||||
// seems to be a bug. Presumably iPhone OS doesn't mind this, but OpenAL
|
||||
// Soft returns an error in this case, and the game skips the rest of its
|
||||
// audio initialization.
|
||||
if unsafe { al::alcGetCurrentContext() }.is_null() {
|
||||
log!("alGetError() called with no current context. Ignoring and returning AL_NO_ERROR for compatibility with Super Monkey Ball.");
|
||||
return al::AL_NO_ERROR;
|
||||
}
|
||||
|
||||
let res = unsafe { al::alGetError() };
|
||||
log_dbg!("alGetError() => {:#x}", res);
|
||||
res
|
||||
}
|
||||
|
||||
// TODO: more functions
|
||||
|
||||
pub const FUNCTIONS: FunctionExports = &[
|
||||
export_c_func!(alcOpenDevice(_)),
|
||||
export_c_func!(alcCloseDevice(_)),
|
||||
export_c_func!(alcGetError(_)),
|
||||
export_c_func!(alcCreateContext(_, _)),
|
||||
export_c_func!(alcDestroyContext(_)),
|
||||
export_c_func!(alcMakeContextCurrent(_)),
|
||||
export_c_func!(alcGetProcAddress(_, _)),
|
||||
export_c_func!(alGetError()),
|
||||
];
|
||||
@@ -18,6 +18,7 @@
|
||||
#[macro_use]
|
||||
mod log;
|
||||
mod abi;
|
||||
mod audio;
|
||||
mod bundle;
|
||||
mod cpu;
|
||||
mod dyld;
|
||||
|
||||
1
vendor/openal-soft
vendored
Submodule
1
vendor/openal-soft
vendored
Submodule
Submodule vendor/openal-soft added at 23c8a35505
Reference in New Issue
Block a user