Begin implementing OpenAL (currently just context setup etc)

This commit is contained in:
hikari_no_yume
2023-01-06 17:57:46 +01:00
parent cace5e7d9c
commit ac917079e4
14 changed files with 309 additions and 2 deletions

3
.gitmodules vendored
View File

@@ -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
View File

@@ -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"

View File

@@ -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]

View File

@@ -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
View File

@@ -0,0 +1,3 @@
//! Audio utilities, currently just OpenAL bindings.
pub mod openal;

79
src/audio/openal.rs Normal file
View 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;
}

View 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 }

View 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"));
}

View 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)]

View File

@@ -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,
];

View File

@@ -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
View 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()),
];

View File

@@ -18,6 +18,7 @@
#[macro_use]
mod log;
mod abi;
mod audio;
mod bundle;
mod cpu;
mod dyld;

1
vendor/openal-soft vendored Submodule

Submodule vendor/openal-soft added at 23c8a35505