Run the original macOS ARM64 game code on iOS: - tools/retarget.py: header-only Mach-O retargeting to an iOS dylib - platform/native: UIKit host and macOS API shims (AppKit for SDL's Cocoa backend, CoreGraphics displays, CoreAudio device, Metal device properties, Wuffs PNG decoding, Steam and peripheral stubs) - touch, overlay, iOS keyboard and hardware keyboard input - tools/build_native.py: build, sign, install, data copy, launch - docs: build guide, controls, architecture Also includes the earlier static-translation research (ARM64 -> WASM -> native) under docs/research, runtime/, tests/ and tools/. No game files are included; the build reads them from a local install.
98 lines
4.9 KiB
C
98 lines
4.9 KiB
C
// macOS CoreAudio device model for SDL's coreaudio backend (macOS build).
|
|
// FX_LIB_COREAUDIO: one virtual output device (the iPhone route), answering the AudioObject
|
|
// properties SDL 2.24 queries. Playback itself uses the real iOS AudioQueue.
|
|
// FX_LIB_AUDIOTOOLBOX: AudioQueueSetProperty passes through, except macOS-only CurrentDevice.
|
|
#include <AudioToolbox/AudioToolbox.h>
|
|
#include <CoreFoundation/CoreFoundation.h>
|
|
#include <dlfcn.h>
|
|
#include <string.h>
|
|
#include "fx.h"
|
|
|
|
#define FOURCC(a, b, c, d) ((uint32_t)(a) << 24 | (uint32_t)(b) << 16 | (uint32_t)(c) << 8 | (uint32_t)(d))
|
|
|
|
#ifdef FX_LIB_COREAUDIO
|
|
typedef uint32_t AudioObjectID;
|
|
typedef struct { uint32_t mSelector, mScope, mElement; } FXPropertyAddress;
|
|
typedef OSStatus (*FXPropertyListener)(AudioObjectID, uint32_t, const FXPropertyAddress *, void *);
|
|
|
|
enum {
|
|
kSystemObject = 1, kSpeaker = 0x100,
|
|
kDevices = FOURCC('d', 'e', 'v', '#'), kDefaultOutput = FOURCC('d', 'O', 'u', 't'),
|
|
kDefaultInput = FOURCC('d', 'I', 'n', ' '), kStreamConfiguration = FOURCC('s', 'l', 'a', 'y'),
|
|
kName = FOURCC('l', 'n', 'a', 'm'), kNominalSampleRate = FOURCC('n', 's', 'r', 't'),
|
|
kIsAlive = FOURCC('l', 'i', 'v', 'n'), kHogMode = FOURCC('o', 'i', 'n', 'k'),
|
|
kUID = FOURCC('u', 'i', 'd', ' '), kScopeInput = FOURCC('i', 'n', 'p', 't'),
|
|
kUnknownProperty = FOURCC('w', 'h', 'o', '?'), kBadObject = FOURCC('!', 'o', 'b', 'j'),
|
|
};
|
|
|
|
// Writes the property into `out` (when non-NULL) and returns its size, or 0 if unsupported.
|
|
static uint32_t fx_property(AudioObjectID object, const FXPropertyAddress *a, void *out, OSStatus *status) {
|
|
*status = 0;
|
|
if (object == kSystemObject) {
|
|
switch (a->mSelector) {
|
|
case kDevices: case kDefaultOutput: if (out) *(AudioObjectID *)out = kSpeaker; return 4;
|
|
case kDefaultInput: if (out) *(AudioObjectID *)out = 0; return 4; // no capture device
|
|
}
|
|
} else if (object == kSpeaker) {
|
|
int input = a->mScope == kScopeInput;
|
|
switch (a->mSelector) {
|
|
case kStreamConfiguration:
|
|
if (out) {
|
|
AudioBufferList *list = out;
|
|
list->mNumberBuffers = input ? 0 : 1;
|
|
list->mBuffers[0] = (AudioBuffer){2, 0, NULL};
|
|
}
|
|
return sizeof(AudioBufferList);
|
|
case kName: if (out) *(CFStringRef *)out = CFStringCreateCopy(NULL, CFSTR("iPhone")); return sizeof(CFStringRef);
|
|
case kUID: if (out) *(CFStringRef *)out = CFStringCreateCopy(NULL, CFSTR("fx-ios-output")); return sizeof(CFStringRef);
|
|
case kNominalSampleRate: if (out) *(Float64 *)out = 48000.0; return sizeof(Float64);
|
|
case kIsAlive: if (out) *(uint32_t *)out = !input; return 4;
|
|
case kHogMode: if (out) *(pid_t *)out = -1; return sizeof(pid_t);
|
|
}
|
|
} else {
|
|
*status = kBadObject;
|
|
return 0;
|
|
}
|
|
*status = kUnknownProperty;
|
|
fx_log("[fx] CoreAudio: unsupported property '%c%c%c%c' on object %u", a->mSelector >> 24, a->mSelector >> 16,
|
|
a->mSelector >> 8, a->mSelector, object);
|
|
return 0;
|
|
}
|
|
|
|
FX_EXPORT Boolean AudioObjectHasProperty(AudioObjectID object, const FXPropertyAddress *a) {
|
|
OSStatus status;
|
|
return fx_property(object, a, NULL, &status) && !status;
|
|
}
|
|
FX_EXPORT OSStatus AudioObjectGetPropertyDataSize(AudioObjectID object, const FXPropertyAddress *a, uint32_t qsize,
|
|
const void *q, uint32_t *size) {
|
|
OSStatus status;
|
|
uint32_t n = fx_property(object, a, NULL, &status);
|
|
if (!status && size) *size = n;
|
|
return status;
|
|
}
|
|
FX_EXPORT OSStatus AudioObjectGetPropertyData(AudioObjectID object, const FXPropertyAddress *a, uint32_t qsize,
|
|
const void *q, uint32_t *size, void *data) {
|
|
OSStatus status;
|
|
uint32_t n = fx_property(object, a, NULL, &status);
|
|
if (status) return status;
|
|
if (!size || *size < n) return FOURCC('!', 's', 'i', 'z');
|
|
fx_property(object, a, data, &status);
|
|
*size = n;
|
|
return status;
|
|
}
|
|
FX_EXPORT OSStatus AudioObjectAddPropertyListener(AudioObjectID o, const FXPropertyAddress *a, FXPropertyListener l, void *c) { return 0; }
|
|
FX_EXPORT OSStatus AudioObjectRemovePropertyListener(AudioObjectID o, const FXPropertyAddress *a, FXPropertyListener l, void *c) { return 0; }
|
|
#endif
|
|
|
|
#ifdef FX_LIB_AUDIOTOOLBOX
|
|
FX_EXPORT OSStatus AudioQueueSetProperty(AudioQueueRef queue, AudioQueuePropertyID id, const void *data, UInt32 size) {
|
|
static OSStatus (*real)(AudioQueueRef, AudioQueuePropertyID, const void *, UInt32);
|
|
if (!real) {
|
|
void *toolbox = dlopen("/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox", RTLD_LAZY | RTLD_NOLOAD);
|
|
real = toolbox ? dlsym(toolbox, "AudioQueueSetProperty") : NULL;
|
|
}
|
|
if (id == FOURCC('a', 'q', 'c', 'd')) return 0; // kAudioQueueProperty_CurrentDevice: iOS routes itself
|
|
return real ? real(queue, id, data, size) : -1;
|
|
}
|
|
#endif
|