Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ cmake-build-*
xcuserdata
*.xcworkspace

# for VSCode
.vscode

# for QtCreator
CMakeLists.txt.user
build*/
Expand Down
5 changes: 5 additions & 0 deletions include/SDL_audio.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,11 @@ typedef Uint16 SDL_AudioFormat;

/* @} *//* Audio flags */

/* Wii U Audio Devices */
#define SDL_AUDIO_DEVICE_WIIU_MIRRORED "Wii U Mirrored"
#define SDL_AUDIO_DEVICE_WIIU_TV "Wii U TV"
#define SDL_AUDIO_DEVICE_WIIU_GAMEPAD "Wii U Gamepad"

/**
* This function is called when the audio device needs more data.
*
Expand Down
229 changes: 153 additions & 76 deletions src/audio/wiiu/SDL_wiiuaudio.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/

#include "../../SDL_internal.h"

#if SDL_AUDIO_DRIVER_WIIU
Expand Down Expand Up @@ -47,9 +48,15 @@

#define AX_MAIN_AFFINITY OS_THREAD_ATTRIB_AFFINITY_CPU1

#define WIIU_DEVICE_TV 0
#define WIIU_DEVICE_GAMEPAD 1
#define WIIU_DEVICE_MIRRORED 2
#define WIIU_MAX_DEVICES 3

static void _WIIUAUDIO_framecallback();
static SDL_AudioDevice* cb_this;
#define cb_hidden cb_this->hidden
static SDL_AudioDevice *wiiuDevices[WIIU_MAX_DEVICES];
static int deviceType;
static int deviceCount;

/* Some helpers for AX-related math */
/* Absolute address to an AXVoiceOffsets offset */
Expand Down Expand Up @@ -78,7 +85,7 @@ static int _WIIUAUDIO_OpenDeviceFunction(_THIS) {

SDL_zerop(this->hidden);

/* Take a quick aside to init the wiiu audio */
/* Take a quick aside to init the wiiu audio */
if (!AXIsInit()) {
/* Init the AX audio engine */
AXInitParams initparams = {
Expand Down Expand Up @@ -177,16 +184,36 @@ static int _WIIUAUDIO_OpenDeviceFunction(_THIS) {
AXSetVoiceVe(this->hidden->voice[i], &vol);
switch (this->spec.channels) {
case 1: /* mono */ {
AXSetVoiceDeviceMix(this->hidden->voice[i],
AX_DEVICE_TYPE_DRC, 0, mono_mix[i]);
AXSetVoiceDeviceMix(this->hidden->voice[i],
AX_DEVICE_TYPE_TV, 0, mono_mix[i]);
if (deviceType == WIIU_DEVICE_MIRRORED) {
AXSetVoiceDeviceMix(this->hidden->voice[i],
AX_DEVICE_TYPE_DRC, 0, mono_mix[i]);
AXSetVoiceDeviceMix(this->hidden->voice[i],
AX_DEVICE_TYPE_TV, 0, mono_mix[i]);
}
else if (deviceType == WIIU_DEVICE_TV) {
AXSetVoiceDeviceMix(this->hidden->voice[i],
AX_DEVICE_TYPE_TV, 0, mono_mix[i]);
}
else if (deviceType == WIIU_DEVICE_GAMEPAD) {
AXSetVoiceDeviceMix(this->hidden->voice[i],
AX_DEVICE_TYPE_DRC, 0, mono_mix[i]);
}
} break;
case 2: /* stereo */ {
AXSetVoiceDeviceMix(this->hidden->voice[i],
AX_DEVICE_TYPE_DRC, 0, stereo_mix[i]);
AXSetVoiceDeviceMix(this->hidden->voice[i],
AX_DEVICE_TYPE_TV, 0, stereo_mix[i]);
if (deviceType == WIIU_DEVICE_MIRRORED) {
AXSetVoiceDeviceMix(this->hidden->voice[i],
AX_DEVICE_TYPE_DRC, 0, stereo_mix[i]);
AXSetVoiceDeviceMix(this->hidden->voice[i],
AX_DEVICE_TYPE_TV, 0, stereo_mix[i]);
}
else if (deviceType == WIIU_DEVICE_TV) {
AXSetVoiceDeviceMix(this->hidden->voice[i],
AX_DEVICE_TYPE_TV, 0, stereo_mix[i]);
}
else if (deviceType == WIIU_DEVICE_GAMEPAD) {
AXSetVoiceDeviceMix(this->hidden->voice[i],
AX_DEVICE_TYPE_DRC, 0, stereo_mix[i]);
}
} break;
}

Expand Down Expand Up @@ -235,8 +262,13 @@ static int _WIIUAUDIO_OpenDeviceFunction(_THIS) {
AXVoiceEnd(this->hidden->voice[i]);
}

cb_this = this; //wish there was a better way
AXRegisterAppFrameCallback(_WIIUAUDIO_framecallback);
wiiuDevices[deviceCount] = this;

if (deviceCount < 1) {
AXRegisterAppFrameCallback(_WIIUAUDIO_framecallback);
}

deviceCount++;

return 0;
}
Expand All @@ -249,9 +281,43 @@ static void _WIIUAUDIO_ThreadDeallocator(OSThread *thread, void *stack) {
static void _WIIUAUDIO_ThreadCleanup(OSThread *thread, void *stack) {
}

static void WIIUAUDIO_DetectDevices(void) {
void *drcHandle;
void *tvHandle;
void *mirrorHandle;

/* This gets reset later anyways */
SDL_AudioSpec spec;

spec.channels = WIIU_MAX_VALID_CHANNELS;
spec.format = AUDIO_S16MSB;
spec.samples = 4096;

SDL_CalculateAudioSpec(&spec);

SDL_AddAudioDevice(SDL_FALSE, SDL_AUDIO_DEVICE_WIIU_MIRRORED, &spec, &mirrorHandle);
SDL_AddAudioDevice(SDL_FALSE, SDL_AUDIO_DEVICE_WIIU_TV, &spec, &tvHandle);
SDL_AddAudioDevice(SDL_FALSE, SDL_AUDIO_DEVICE_WIIU_GAMEPAD, &spec, &drcHandle);
}

static int WIIUAUDIO_OpenDevice(_THIS, const char *devname) {
int result;

if (deviceCount >= WIIU_MAX_DEVICES) {
return SDL_SetError("Too many Wii U audio devices! (Max: %d)", WIIU_MAX_DEVICES);
}

deviceType = WIIU_DEVICE_MIRRORED;

if (devname != NULL) {
if (SDL_strcmp(devname, SDL_AUDIO_DEVICE_WIIU_TV) == 0) {
deviceType = WIIU_DEVICE_TV;
}
else if (SDL_strcmp(devname, SDL_AUDIO_DEVICE_WIIU_GAMEPAD) == 0) {
deviceType = WIIU_DEVICE_GAMEPAD;
}
}

/* AX functions need to run from the same core.
Since we cannot easily change the affinity of the currently running thread, we create a new one if necessary.
This thread only runs on CPU1 (AX_MAIN_AFFINITY) and will be joined after initialization is done. */
Expand Down Expand Up @@ -300,78 +366,84 @@ static int WIIUAUDIO_OpenDevice(_THIS, const char *devname) {

/* Called every 3ms before a frame of audio is rendered. Keep it fast! */
static void _WIIUAUDIO_framecallback() {
int playing_buffer = -1;
AXVoiceOffsets offs[6];
void* endaddr;

for (int i = 0; i < cb_this->spec.channels; i++) {
AXGetVoiceOffsets(cb_hidden->voice[i], &offs[i]);
}
for (int deviceIndex = 0; deviceIndex < deviceCount; ++deviceIndex) {
SDL_AudioDevice *dev = wiiuDevices[deviceIndex];

int playing_buffer = -1;
AXVoiceOffsets offs[6];
void* endaddr;

for (int i = 0; i < dev->spec.channels; i++) {
AXGetVoiceOffsets(dev->hidden->voice[i], &offs[i]);
}

/* Figure out which buffer is being played by the hardware */
for (int i = 0; i < NUM_BUFFERS; i++) {
void* buf = cb_hidden->mixbufs[i];
uint32_t startOffset = calc_ax_offset(offs[0], buf);
uint32_t endOffset = startOffset + cb_this->spec.samples;

/* NOTE endOffset definitely needs to be <= (AX plays the sample at
endOffset), dunno about startOffset */
if (offs[0].currentOffset >= startOffset &&
offs[0].currentOffset <= endOffset) {
playing_buffer = i;
break;
/* Figure out which buffer is being played by the hardware */
for (int i = 0; i < NUM_BUFFERS; i++) {
void* buf = dev->hidden->mixbufs[i];
uint32_t startOffset = calc_ax_offset(offs[0], buf);
uint32_t endOffset = startOffset + dev->spec.samples;

/* NOTE endOffset definitely needs to be <= (AX plays the sample at
endOffset), dunno about startOffset */
if (offs[0].currentOffset >= startOffset &&
offs[0].currentOffset <= endOffset) {
playing_buffer = i;
break;
}
}
}

if (playing_buffer < 0 || playing_buffer >= NUM_BUFFERS) {
/* UM */
/* Uncomment for craploads of debug info */
/*printf("bad buffer %d\n" "|> %08X, %08X-%08X\n" \
"0: xxxxxxxx, %08X-%08X (%08X@%08X)\n" \
"1: xxxxxxxx, %08X-%08X (%08X@%08X)\n", \
playing_buffer, offs.currentOffset, offs.loopOffset, offs.endOffset,
calc_ax_offset(offs, (void*)cb_hidden->mixbufs[0]),
calc_ax_offset(offs, (void*)cb_hidden->mixbufs[0] + cb_this->spec.size),
cb_this->spec.size, (void*)cb_hidden->mixbufs[0],
calc_ax_offset(offs, (void*)cb_hidden->mixbufs[1]),
calc_ax_offset(offs, (void*)cb_hidden->mixbufs[1] + cb_this->spec.size),
cb_this->spec.size, (void*)cb_hidden->mixbufs[1]);*/
printf("DEBUG: Playing an invalid buffer? This is not a good sign.\n");
playing_buffer = 0;
}
if (playing_buffer < 0 || playing_buffer >= NUM_BUFFERS) {
/* UM */
/* Uncomment for craploads of debug info */
/*printf("bad buffer %d\n" "|> %08X, %08X-%08X\n" \
"0: xxxxxxxx, %08X-%08X (%08X@%08X)\n" \
"1: xxxxxxxx, %08X-%08X (%08X@%08X)\n", \
playing_buffer, offs.currentOffset, offs.loopOffset, offs.endOffset,
calc_ax_offset(offs, (void*)cb_hidden->mixbufs[0]),
calc_ax_offset(offs, (void*)cb_hidden->mixbufs[0] + cb_this->spec.size),
cb_this->spec.size, (void*)cb_hidden->mixbufs[0],
calc_ax_offset(offs, (void*)cb_hidden->mixbufs[1]),
calc_ax_offset(offs, (void*)cb_hidden->mixbufs[1] + cb_this->spec.size),
cb_this->spec.size, (void*)cb_hidden->mixbufs[1]);*/
printf("DEBUG: Playing an invalid buffer? This is not a good sign.\n");
playing_buffer = 0;
}

/* Make sure playingid is in sync with the hardware */
cb_hidden->playingid = playing_buffer;
/* Make sure playingid is in sync with the hardware */
dev->hidden->playingid = playing_buffer;

/* Make sure the end offset is correct for the playing buffer */
for (int i = 0; i < cb_this->spec.channels; i++) {
/* Calculate end address, aka start of the next (i+1) channel's buffer */
endaddr = cb_hidden->mixbufs[cb_hidden->playingid] +
(cb_this->spec.samples * sizeof_sample(offs[i]) * (i + 1));
/* Make sure the end offset is correct for the playing buffer */
for (int i = 0; i < dev->spec.channels; i++) {
/* Calculate end address, aka start of the next (i+1) channel's buffer */
endaddr = dev->hidden->mixbufs[dev->hidden->playingid] +
(dev->spec.samples * sizeof_sample(offs[i]) * (i + 1));

/* Trial end error to try and limit popping */
endaddr -= 2;
/* Trial end error to try and limit popping */
endaddr -= 2;

AXSetVoiceEndOffset(
cb_hidden->voice[i],
calc_ax_offset(offs[i], endaddr)
);
AXSetVoiceEndOffset(
dev->hidden->voice[i],
calc_ax_offset(offs[i], endaddr)
);

/* The next buffer is good to go, set the loop offset */
if (cb_hidden->renderingid != next_id(cb_hidden->playingid)) {
/* Calculate start address for this channel's buffer */
void* loopaddr = cb_hidden->mixbufs[next_id(cb_hidden->playingid)] +
(cb_this->spec.samples * sizeof_sample(offs[i]) * i);
/* The next buffer is good to go, set the loop offset */
if (dev->hidden->renderingid != next_id(dev->hidden->playingid)) {
/* Calculate start address for this channel's buffer */
void* loopaddr = dev->hidden->mixbufs[next_id(dev->hidden->playingid)] +
(dev->spec.samples * sizeof_sample(offs[i]) * i);

AXSetVoiceLoopOffset(cb_hidden->voice[i], calc_ax_offset(offs[i], loopaddr));
/* Otherwise, make sure the loop offset is correct for the playing buffer */
} else {
void* loopaddr = cb_hidden->mixbufs[cb_hidden->playingid] +
(cb_this->spec.samples * sizeof_sample(offs[i]) * i);
AXSetVoiceLoopOffset(dev->hidden->voice[i], calc_ax_offset(offs[i], loopaddr));
/* Otherwise, make sure the loop offset is correct for the playing buffer */
} else {
void* loopaddr = dev->hidden->mixbufs[dev->hidden->playingid] +
(dev->spec.samples * sizeof_sample(offs[i]) * i);

AXSetVoiceLoopOffset(cb_hidden->voice[i], calc_ax_offset(offs[i], loopaddr));
AXSetVoiceLoopOffset(dev->hidden->voice[i], calc_ax_offset(offs[i], loopaddr));
}
}
}


}

static void WIIUAUDIO_PlayDevice(_THIS) {
Expand Down Expand Up @@ -427,7 +499,7 @@ static Uint8* WIIUAUDIO_GetDeviceBuf(_THIS) {
}

static void WIIUAUDIO_CloseDevice(_THIS) {
if (AXIsInit()) {
if ((AXIsInit()) && (deviceCount < 1)) {
AXDeregisterAppFrameCallback(_WIIUAUDIO_framecallback);
for (int i = 0; i < SIZEOF_ARR(this->hidden->voice); i++) {
if (this->hidden->voice[i]) {
Expand All @@ -440,6 +512,8 @@ static void WIIUAUDIO_CloseDevice(_THIS) {
if (this->hidden->mixbufs[0]) free(this->hidden->mixbufs[0]);
if (this->hidden->deintvbuf) SDL_free(this->hidden->deintvbuf);
SDL_free(this->hidden);

deviceCount--;
}

static void WIIUAUDIO_ThreadInit(_THIS) {
Expand All @@ -451,14 +525,17 @@ static void WIIUAUDIO_ThreadInit(_THIS) {
}

static SDL_bool WIIUAUDIO_Init(SDL_AudioDriverImpl *impl) {
impl->DetectDevices = WIIUAUDIO_DetectDevices;
impl->OpenDevice = WIIUAUDIO_OpenDevice;
impl->PlayDevice = WIIUAUDIO_PlayDevice;
impl->WaitDevice = WIIUAUDIO_WaitDevice;
impl->GetDeviceBuf = WIIUAUDIO_GetDeviceBuf;
impl->CloseDevice = WIIUAUDIO_CloseDevice;
impl->ThreadInit = WIIUAUDIO_ThreadInit;

impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
impl->OnlyHasDefaultOutputDevice = SDL_FALSE;

deviceCount = 0;

return SDL_TRUE;
}
Expand All @@ -467,4 +544,4 @@ AudioBootStrap WIIUAUDIO_bootstrap = {
WIIUAUDIO_DRIVER_NAME, "Wii U AX Audio Driver", WIIUAUDIO_Init, 0,
};

#endif //SDL_AUDIO_DRIVER_WIIU
#endif //SDL_AUDIO_DRIVER_WIIU