diff --git a/.gitignore b/.gitignore index f53f91c3b9231..6942862a202ef 100644 --- a/.gitignore +++ b/.gitignore @@ -63,6 +63,9 @@ cmake-build-* xcuserdata *.xcworkspace +# for VSCode +/.vscode + # for QtCreator CMakeLists.txt.user build*/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 518c1f4c45965..c62742e9c3353 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -244,6 +244,7 @@ if(WIIU) # Prefer coreinit atomics on Wii U due to a hardware bug in load-exclusive and store-exclusive instructions set(OPT_DEF_GCC_ATOMICS OFF) # Needed for nn::swkbd API + enable_language(CXX) set(LINKER_LANGUAGE CXX) set (CMAKE_CXX_STANDARD 23) endif() diff --git a/include/SDL_audio.h b/include/SDL_audio.h index cb76e93e43a81..158d984bbdbab 100644 --- a/include/SDL_audio.h +++ b/include/SDL_audio.h @@ -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. * diff --git a/src/audio/wiiu/SDL_wiiuaudio.c b/src/audio/wiiu/SDL_wiiuaudio.c index da02839dc3036..bf0043be94b0a 100644 --- a/src/audio/wiiu/SDL_wiiuaudio.c +++ b/src/audio/wiiu/SDL_wiiuaudio.c @@ -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 @@ -47,9 +48,19 @@ #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 int mirroredHandle; +static int tvHandle; +static int drcHandle; + 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 */ @@ -78,16 +89,6 @@ static int _WIIUAUDIO_OpenDeviceFunction(_THIS) { SDL_zerop(this->hidden); -/* Take a quick aside to init the wiiu audio */ - if (!AXIsInit()) { - /* Init the AX audio engine */ - AXInitParams initparams = { - .renderer = AX_INIT_RENDERER_48KHZ, - .pipeline = AX_INIT_PIPELINE_SINGLE, - }; - AXInitWithParams(&initparams); - } else printf("DEBUG: AX already up?\n"); - if (this->spec.channels < 1) this->spec.channels = 1; if (this->spec.channels > WIIU_MAX_VALID_CHANNELS) this->spec.channels = WIIU_MAX_VALID_CHANNELS; @@ -159,7 +160,6 @@ static int _WIIUAUDIO_OpenDeviceFunction(_THIS) { return SDL_SetError("Couldn't allocate deinterleave buffer"); } - for (int i = 0; i < this->spec.channels; i++) { /* Get a voice, top priority */ this->hidden->voice[i] = AXAcquireVoice(31, NULL, NULL); @@ -177,16 +177,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; } @@ -235,8 +255,8 @@ 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; + deviceCount++; return 0; } @@ -249,9 +269,37 @@ static void _WIIUAUDIO_ThreadDeallocator(OSThread *thread, void *stack) { static void _WIIUAUDIO_ThreadCleanup(OSThread *thread, void *stack) { } +static void WIIUAUDIO_DetectDevices(void) { + /* 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, &mirroredHandle); + 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 (this->handle == &tvHandle) { + deviceType = WIIU_DEVICE_TV; + } + else if (this->handle == &drcHandle) { + 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. */ @@ -300,78 +348,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) { @@ -427,19 +481,18 @@ static Uint8* WIIUAUDIO_GetDeviceBuf(_THIS) { } static void WIIUAUDIO_CloseDevice(_THIS) { - if (AXIsInit()) { - AXDeregisterAppFrameCallback(_WIIUAUDIO_framecallback); - for (int i = 0; i < SIZEOF_ARR(this->hidden->voice); i++) { - if (this->hidden->voice[i]) { - AXFreeVoice(this->hidden->voice[i]); - this->hidden->voice[i] = NULL; - } + for (int i = 0; i < SIZEOF_ARR(this->hidden->voice); i++) { + if (this->hidden->voice[i]) { + AXFreeVoice(this->hidden->voice[i]); + this->hidden->voice[i] = NULL; } - AXQuit(); } + 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) { @@ -450,15 +503,38 @@ static void WIIUAUDIO_ThreadInit(_THIS) { OSSetThreadPriority(currentThread, priority); } +static void WIIUAUDIO_Deinitialize(void) { + if (AXIsInit()) { + AXDeregisterAppFrameCallback(_WIIUAUDIO_framecallback); + AXQuit(); + } +} + static SDL_bool WIIUAUDIO_Init(SDL_AudioDriverImpl *impl) { + /* Take a quick aside to init the wiiu audio */ + if (!AXIsInit()) { + /* Init the AX audio engine */ + AXInitParams initparams = { + .renderer = AX_INIT_RENDERER_48KHZ, + .pipeline = AX_INIT_PIPELINE_SINGLE, + }; + AXInitWithParams(&initparams); + } else printf("DEBUG: AX already up?\n"); + + AXRegisterAppFrameCallback(_WIIUAUDIO_framecallback); + + 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->Deinitialize = WIIUAUDIO_Deinitialize; + + impl->OnlyHasDefaultOutputDevice = SDL_FALSE; - impl->OnlyHasDefaultOutputDevice = SDL_TRUE; + deviceCount = 0; return SDL_TRUE; } @@ -467,4 +543,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 \ No newline at end of file