shithub: cstory

Download patch

ref: 0d7ad3358b40dfa454586302567cdcf1c1fc02ee
parent: d391ab77f9a3dfcf2635029c18ac190e41a5a2a0
author: Clownacy <Clownacy@users.noreply.github.com>
date: Wed Oct 14 19:22:16 EDT 2020

Make some filenames more consistent

--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -361,7 +361,7 @@
 	target_link_libraries(CSE2 PRIVATE ${CMAKE_DL_LIBS})
 elseif(BACKEND_AUDIO MATCHES "WiiU-Hardware")
 	target_sources(CSE2 PRIVATE
-		"src/Backends/Audio/WiiU-Hardware.cpp"
+		"src/Backends/Audio/WiiU.cpp"
 	)
 elseif(BACKEND_AUDIO MATCHES "WiiU-Software")
 	target_sources(CSE2 PRIVATE
@@ -369,7 +369,7 @@
 		"src/Backends/Audio/SoftwareMixer/Mixer.cpp"
 		"src/Backends/Audio/SoftwareMixer/Mixer.h"
 		"src/Backends/Audio/SoftwareMixer/Backend.h"
-		"src/Backends/Audio/SoftwareMixer/WiiU-Software.cpp"
+		"src/Backends/Audio/SoftwareMixer/WiiU.cpp"
 	)
 elseif(BACKEND_AUDIO MATCHES "3DS-Hardware")
 	target_sources(CSE2 PRIVATE
--- a/src/Backends/Audio/SoftwareMixer/WiiU-Software.cpp
+++ /dev/null
@@ -1,234 +1,0 @@
-#include "Backend.h"
-
-#include <math.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <coreinit/cache.h>
-#include <coreinit/mutex.h>
-#include <coreinit/thread.h>
-#include <sndcore2/core.h>
-#include <sndcore2/voice.h>
-#include <sndcore2/drcvs.h>
-
-#define AUDIO_BUFFERS 2	// Double-buffer
-
-#define MIN(a, b) ((a) < (b) ? (a) : (b))
-#define MAX(a, b) ((a) > (b) ? (a) : (b))
-#define CLAMP(x, y, z) MIN(MAX((x), (y)), (z))
-
-static void (*parent_callback)(long *stream, size_t frames_total);
-
-static OSMutex sound_list_mutex;
-static OSMutex organya_mutex;
-
-static AXVoice *voices[2];
-
-static short *stream_buffers[2];
-static long *stream_buffer_long;
-static size_t buffer_length;
-
-static void FrameCallback(void)
-{
-	// We use a double-buffer: while the Wii U is busy playing one half of the buffer, we update the other.
-	// The buffer is 10ms long in total, and this function runs every 3ms.
-
-	// Just assume both voices are in-sync, and only check the first one
-	AXVoiceOffsets offsets;
-	AXGetVoiceOffsets(voices[0], &offsets);
-
-	unsigned int current_buffer = offsets.currentOffset / buffer_length;
-
-	static unsigned int last_buffer = 1;
-
-	if (current_buffer != last_buffer)
-	{
-		// Clear the mixer buffer
-		memset(stream_buffer_long, 0, buffer_length * sizeof(long) * 2);
-
-		// Fill mixer buffer
-		parent_callback(stream_buffer_long, buffer_length);
-
-		// Deinterlate samples, convert them to S16, and write them to the double-buffers
-		short *left_output_buffer = &stream_buffers[0][buffer_length * last_buffer];
-		short *right_output_buffer = &stream_buffers[1][buffer_length * last_buffer];
-
-		long *mixer_buffer_pointer = stream_buffer_long;
-		short *left_output_buffer_pointer = left_output_buffer;
-		short *right_output_buffer_pointer = right_output_buffer;
-
-		for (unsigned int i = 0; i < buffer_length; ++i)
-		{
-			const long left_sample = *mixer_buffer_pointer++;
-			const long right_sample = *mixer_buffer_pointer++;
-
-			// Clamp samples to sane limits, convert to S16, and store in double-buffers
-			if (left_sample > 0x7FFF)
-				*left_output_buffer_pointer++ = 0x7FFF;
-			else if (left_sample < -0x7FFF)
-				*left_output_buffer_pointer++ = -0x7FFF;
-			else
-				*left_output_buffer_pointer++ = (short)left_sample;
-
-			if (right_sample > 0x7FFF)
-				*right_output_buffer_pointer++ = 0x7FFF;
-			else if (right_sample < -0x7FFF)
-				*right_output_buffer_pointer++ = -0x7FFF;
-			else
-				*right_output_buffer_pointer++ = (short)right_sample;
-		}
-
-		// Make sure the sound hardware can see our data
-		DCStoreRange(left_output_buffer, buffer_length * sizeof(short));
-		DCStoreRange(right_output_buffer, buffer_length * sizeof(short));
-
-		last_buffer = current_buffer;
-	}
-}
-
-unsigned long SoftwareMixerBackend_Init(void (*callback)(long *stream, size_t frames_total))
-{
-	if (!AXIsInit())
-	{
-		AXInitParams initparams = {
-			.renderer = AX_INIT_RENDERER_48KHZ,
-			.pipeline = AX_INIT_PIPELINE_SINGLE,
-		};
-
-		AXInitWithParams(&initparams);
-	}
-
-	OSInitMutex(&sound_list_mutex);
-	OSInitMutex(&organya_mutex);
-
-	unsigned long output_frequency = AXGetInputSamplesPerSec();
-
-	buffer_length = output_frequency / 100;	// 10ms buffer
-
-	// Create and initialise two 'voices': each one will stream its own
-	// audio - one for the left speaker, and one for the right. 
-
-	// The software-mixer outputs interlaced samples into a buffer of `long`s,
-	// so create a buffer for it here.
-	stream_buffer_long = (long*)malloc(buffer_length * sizeof(long) * 2);	// `* 2` because it's an interlaced stereo buffer
-
-	if (stream_buffer_long != NULL)
-	{
-		stream_buffers[0] = (short*)malloc(buffer_length * sizeof(short) * AUDIO_BUFFERS);
-
-		if (stream_buffers[0] != NULL)
-		{
-			stream_buffers[1] = (short*)malloc(buffer_length * sizeof(short) * AUDIO_BUFFERS);
-
-			if (stream_buffers[1] != NULL)
-			{
-				voices[0] = AXAcquireVoice(31, NULL, NULL);
-
-				if (voices[0] != NULL)
-				{
-					voices[1] = AXAcquireVoice(31, NULL, NULL);
-
-					if (voices[1] != NULL)
-					{
-						for (unsigned int i = 0; i < 2; ++i)
-						{
-							AXVoiceBegin(voices[i]);
-
-							AXSetVoiceType(voices[i], 0);
-
-							AXVoiceVeData vol = {.volume = 0x8000};
-							AXSetVoiceVe(voices[i], &vol);
-
-							AXVoiceDeviceMixData mix_data[6];
-							memset(mix_data, 0, sizeof(mix_data));
-							mix_data[i].bus[0].volume = 0x8000;	// Voice 1 goes on the left speaker - voice 2 goes on the right speaker
-
-							AXSetVoiceDeviceMix(voices[i], AX_DEVICE_TYPE_DRC, 0, mix_data);
-							AXSetVoiceDeviceMix(voices[i], AX_DEVICE_TYPE_TV, 0, mix_data);
-
-							AXSetVoiceSrcRatio(voices[i], 1.0f);	// We use the native sample rate
-							AXSetVoiceSrcType(voices[i], AX_VOICE_SRC_TYPE_NONE);
-
-							AXVoiceOffsets offs = {
-								.dataType = AX_VOICE_FORMAT_LPCM16,
-								.loopingEnabled = AX_VOICE_LOOP_ENABLED,
-								.loopOffset = 0,
-								.endOffset = (buffer_length * AUDIO_BUFFERS) - 1,	// -1 or else you'll get popping!
-								.currentOffset = 0,
-								.data = stream_buffers[i]
-							};
-							AXSetVoiceOffsets(voices[i], &offs);
-
-							AXVoiceEnd(voices[i]);
-						}
-
-						parent_callback = callback;
-
-						// Register the frame callback.
-						// Apparently, this fires every 3ms - we will use
-						// it to update the stream buffers when needed.
-						AXRegisterAppFrameCallback(FrameCallback);
-
-						return output_frequency;
-					}
-
-					AXFreeVoice(voices[0]);
-				}
-
-				free(stream_buffers[1]);
-			}
-
-			free(stream_buffers[0]);
-		}
-
-		free(stream_buffer_long);
-	}
-
-	AXQuit();
-
-	return 0;
-}
-
-void SoftwareMixerBackend_Deinit(void)
-{
-	AXRegisterAppFrameCallback(NULL);
-
-	AXFreeVoice(voices[1]);
-	AXFreeVoice(voices[0]);
-
-	free(stream_buffers[1]);
-	free(stream_buffers[0]);
-
-	free(stream_buffer_long);
-
-	AXQuit();
-}
-
-bool SoftwareMixerBackend_Start(void)
-{
-	AXSetVoiceState(voices[0], AX_VOICE_STATE_PLAYING);
-	AXSetVoiceState(voices[1], AX_VOICE_STATE_PLAYING);
-
-	return true;
-}
-
-void SoftwareMixerBackend_LockMixerMutex(void)
-{
-	OSLockMutex(&sound_list_mutex);
-}
-
-void SoftwareMixerBackend_UnlockMixerMutex(void)
-{
-	OSUnlockMutex(&sound_list_mutex);
-}
-
-void SoftwareMixerBackend_LockOrganyaMutex(void)
-{
-	OSLockMutex(&organya_mutex);
-}
-
-void SoftwareMixerBackend_UnlockOrganyaMutex(void)
-{
-	OSUnlockMutex(&organya_mutex);
-}
--- /dev/null
+++ b/src/Backends/Audio/SoftwareMixer/WiiU.cpp
@@ -1,0 +1,234 @@
+#include "Backend.h"
+
+#include <math.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <coreinit/cache.h>
+#include <coreinit/mutex.h>
+#include <coreinit/thread.h>
+#include <sndcore2/core.h>
+#include <sndcore2/voice.h>
+#include <sndcore2/drcvs.h>
+
+#define AUDIO_BUFFERS 2	// Double-buffer
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#define CLAMP(x, y, z) MIN(MAX((x), (y)), (z))
+
+static void (*parent_callback)(long *stream, size_t frames_total);
+
+static OSMutex sound_list_mutex;
+static OSMutex organya_mutex;
+
+static AXVoice *voices[2];
+
+static short *stream_buffers[2];
+static long *stream_buffer_long;
+static size_t buffer_length;
+
+static void FrameCallback(void)
+{
+	// We use a double-buffer: while the Wii U is busy playing one half of the buffer, we update the other.
+	// The buffer is 10ms long in total, and this function runs every 3ms.
+
+	// Just assume both voices are in-sync, and only check the first one
+	AXVoiceOffsets offsets;
+	AXGetVoiceOffsets(voices[0], &offsets);
+
+	unsigned int current_buffer = offsets.currentOffset / buffer_length;
+
+	static unsigned int last_buffer = 1;
+
+	if (current_buffer != last_buffer)
+	{
+		// Clear the mixer buffer
+		memset(stream_buffer_long, 0, buffer_length * sizeof(long) * 2);
+
+		// Fill mixer buffer
+		parent_callback(stream_buffer_long, buffer_length);
+
+		// Deinterlate samples, convert them to S16, and write them to the double-buffers
+		short *left_output_buffer = &stream_buffers[0][buffer_length * last_buffer];
+		short *right_output_buffer = &stream_buffers[1][buffer_length * last_buffer];
+
+		long *mixer_buffer_pointer = stream_buffer_long;
+		short *left_output_buffer_pointer = left_output_buffer;
+		short *right_output_buffer_pointer = right_output_buffer;
+
+		for (unsigned int i = 0; i < buffer_length; ++i)
+		{
+			const long left_sample = *mixer_buffer_pointer++;
+			const long right_sample = *mixer_buffer_pointer++;
+
+			// Clamp samples to sane limits, convert to S16, and store in double-buffers
+			if (left_sample > 0x7FFF)
+				*left_output_buffer_pointer++ = 0x7FFF;
+			else if (left_sample < -0x7FFF)
+				*left_output_buffer_pointer++ = -0x7FFF;
+			else
+				*left_output_buffer_pointer++ = (short)left_sample;
+
+			if (right_sample > 0x7FFF)
+				*right_output_buffer_pointer++ = 0x7FFF;
+			else if (right_sample < -0x7FFF)
+				*right_output_buffer_pointer++ = -0x7FFF;
+			else
+				*right_output_buffer_pointer++ = (short)right_sample;
+		}
+
+		// Make sure the sound hardware can see our data
+		DCStoreRange(left_output_buffer, buffer_length * sizeof(short));
+		DCStoreRange(right_output_buffer, buffer_length * sizeof(short));
+
+		last_buffer = current_buffer;
+	}
+}
+
+unsigned long SoftwareMixerBackend_Init(void (*callback)(long *stream, size_t frames_total))
+{
+	if (!AXIsInit())
+	{
+		AXInitParams initparams = {
+			.renderer = AX_INIT_RENDERER_48KHZ,
+			.pipeline = AX_INIT_PIPELINE_SINGLE,
+		};
+
+		AXInitWithParams(&initparams);
+	}
+
+	OSInitMutex(&sound_list_mutex);
+	OSInitMutex(&organya_mutex);
+
+	unsigned long output_frequency = AXGetInputSamplesPerSec();
+
+	buffer_length = output_frequency / 100;	// 10ms buffer
+
+	// Create and initialise two 'voices': each one will stream its own
+	// audio - one for the left speaker, and one for the right. 
+
+	// The software-mixer outputs interlaced samples into a buffer of `long`s,
+	// so create a buffer for it here.
+	stream_buffer_long = (long*)malloc(buffer_length * sizeof(long) * 2);	// `* 2` because it's an interlaced stereo buffer
+
+	if (stream_buffer_long != NULL)
+	{
+		stream_buffers[0] = (short*)malloc(buffer_length * sizeof(short) * AUDIO_BUFFERS);
+
+		if (stream_buffers[0] != NULL)
+		{
+			stream_buffers[1] = (short*)malloc(buffer_length * sizeof(short) * AUDIO_BUFFERS);
+
+			if (stream_buffers[1] != NULL)
+			{
+				voices[0] = AXAcquireVoice(31, NULL, NULL);
+
+				if (voices[0] != NULL)
+				{
+					voices[1] = AXAcquireVoice(31, NULL, NULL);
+
+					if (voices[1] != NULL)
+					{
+						for (unsigned int i = 0; i < 2; ++i)
+						{
+							AXVoiceBegin(voices[i]);
+
+							AXSetVoiceType(voices[i], 0);
+
+							AXVoiceVeData vol = {.volume = 0x8000};
+							AXSetVoiceVe(voices[i], &vol);
+
+							AXVoiceDeviceMixData mix_data[6];
+							memset(mix_data, 0, sizeof(mix_data));
+							mix_data[i].bus[0].volume = 0x8000;	// Voice 1 goes on the left speaker - voice 2 goes on the right speaker
+
+							AXSetVoiceDeviceMix(voices[i], AX_DEVICE_TYPE_DRC, 0, mix_data);
+							AXSetVoiceDeviceMix(voices[i], AX_DEVICE_TYPE_TV, 0, mix_data);
+
+							AXSetVoiceSrcRatio(voices[i], 1.0f);	// We use the native sample rate
+							AXSetVoiceSrcType(voices[i], AX_VOICE_SRC_TYPE_NONE);
+
+							AXVoiceOffsets offs = {
+								.dataType = AX_VOICE_FORMAT_LPCM16,
+								.loopingEnabled = AX_VOICE_LOOP_ENABLED,
+								.loopOffset = 0,
+								.endOffset = (buffer_length * AUDIO_BUFFERS) - 1,	// -1 or else you'll get popping!
+								.currentOffset = 0,
+								.data = stream_buffers[i]
+							};
+							AXSetVoiceOffsets(voices[i], &offs);
+
+							AXVoiceEnd(voices[i]);
+						}
+
+						parent_callback = callback;
+
+						// Register the frame callback.
+						// Apparently, this fires every 3ms - we will use
+						// it to update the stream buffers when needed.
+						AXRegisterAppFrameCallback(FrameCallback);
+
+						return output_frequency;
+					}
+
+					AXFreeVoice(voices[0]);
+				}
+
+				free(stream_buffers[1]);
+			}
+
+			free(stream_buffers[0]);
+		}
+
+		free(stream_buffer_long);
+	}
+
+	AXQuit();
+
+	return 0;
+}
+
+void SoftwareMixerBackend_Deinit(void)
+{
+	AXRegisterAppFrameCallback(NULL);
+
+	AXFreeVoice(voices[1]);
+	AXFreeVoice(voices[0]);
+
+	free(stream_buffers[1]);
+	free(stream_buffers[0]);
+
+	free(stream_buffer_long);
+
+	AXQuit();
+}
+
+bool SoftwareMixerBackend_Start(void)
+{
+	AXSetVoiceState(voices[0], AX_VOICE_STATE_PLAYING);
+	AXSetVoiceState(voices[1], AX_VOICE_STATE_PLAYING);
+
+	return true;
+}
+
+void SoftwareMixerBackend_LockMixerMutex(void)
+{
+	OSLockMutex(&sound_list_mutex);
+}
+
+void SoftwareMixerBackend_UnlockMixerMutex(void)
+{
+	OSUnlockMutex(&sound_list_mutex);
+}
+
+void SoftwareMixerBackend_LockOrganyaMutex(void)
+{
+	OSLockMutex(&organya_mutex);
+}
+
+void SoftwareMixerBackend_UnlockOrganyaMutex(void)
+{
+	OSUnlockMutex(&organya_mutex);
+}
--- a/src/Backends/Audio/WiiU-Hardware.cpp
+++ /dev/null
@@ -1,422 +1,0 @@
-// This darned thing doesn't work - sounds just randomly refuse to play,
-// particularly the ones used by Organya.
-
-// If anyone could figure out what causes this, that would be great.
-
-#include "../Audio.h"
-
-#include <math.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <coreinit/cache.h>
-#include <coreinit/mutex.h>
-#include <coreinit/thread.h>
-#include <sndcore2/core.h>
-#include <sndcore2/voice.h>
-#include <sndcore2/drcvs.h>
-
-#define MIN(a, b) ((a) < (b) ? (a) : (b))
-#define MAX(a, b) ((a) > (b) ? (a) : (b))
-#define CLAMP(x, y, z) MIN(MAX((x), (y)), (z))
-
-struct AudioBackend_Sound
-{
-	signed char *samples;
-	size_t length;
-	AXVoice *voice;
-	unsigned int frequency;
-	unsigned short volume;
-	unsigned short pan_l;
-	unsigned short pan_r;
-
-	struct AudioBackend_Sound *next;
-};
-
-static void (*organya_callback)(void);
-static unsigned int organya_milliseconds;
-
-static unsigned long ticks_per_second;
-
-static OSMutex sound_list_mutex;
-static OSMutex organya_mutex;
-
-static AudioBackend_Sound *sound_list_head;
-
-static void CullVoices(void)
-{
-	// Free any voices that aren't playing anymore
-	OSLockMutex(&sound_list_mutex);
-
-	for (AudioBackend_Sound *sound = sound_list_head; sound != NULL; sound = sound->next)
-	{
-		if (sound->voice != NULL)
-		{
-			if (!AXIsVoiceRunning(sound->voice))
-			{
-				AXVoiceBegin(sound->voice);
-				AXFreeVoice(sound->voice);
-				AXVoiceEnd(sound->voice);
-				sound->voice = NULL;
-			}
-		}
-	}
-
-	OSUnlockMutex(&sound_list_mutex);
-}
-
-static double MillibelToScale(long volume)
-{
-	// Volume is in hundredths of a decibel, from 0 to -10000
-	volume = CLAMP(volume, -10000, 0);
-	return pow(10.0, volume / 2000.0);
-}
-
-static unsigned long GetTicksMilliseconds(void)
-{
-	static uint64_t accumulator;
-
-	static unsigned long last_tick;
-
-	unsigned long current_tick = OSGetTick();
-
-	accumulator += current_tick - last_tick;
-
-	last_tick = current_tick;
-
-	return (accumulator * 1000) / ticks_per_second;
-}
-
-static int ThreadFunction(int argc, const char *argv[])
-{
-	for (;;)
-	{
-		OSTestThreadCancel();
-
-		OSLockMutex(&organya_mutex);
-
-		if (organya_milliseconds == 0)
-		{
-			OSUnlockMutex(&organya_mutex);
-
-			// Do nothing
-			OSSleepTicks(ticks_per_second / 1000);
-		}
-		else
-		{
-			OSUnlockMutex(&organya_mutex);
-
-			// Update Organya
-			static unsigned long next_ticks;
-
-			for (;;)
-			{
-				unsigned long ticks = GetTicksMilliseconds();
-
-				if (ticks >= next_ticks)
-					break;
-
-				OSSleepTicks(ticks_per_second / 1000);
-			}
-
-			OSLockMutex(&organya_mutex);
-			next_ticks += organya_milliseconds;
-			OSUnlockMutex(&organya_mutex);
-
-			OSLockMutex(&sound_list_mutex);
-			organya_callback();
-			OSUnlockMutex(&sound_list_mutex);
-		}
-	}
-
-	return 0;
-}
-
-bool AudioBackend_Init(void)
-{
-	if (!AXIsInit())
-	{
-		AXInitParams initparams = {
-			.renderer = AX_INIT_RENDERER_48KHZ,
-			.pipeline = AX_INIT_PIPELINE_SINGLE,
-		};
-
-		AXInitWithParams(&initparams);
-	}
-
-	ticks_per_second = OSGetSystemInfo()->busClockSpeed / 4;
-
-	OSInitMutex(&sound_list_mutex);
-	OSInitMutex(&organya_mutex);
-
-	OSRunThread(OSGetDefaultThread(0), ThreadFunction, 0, NULL);
-
-	return true;
-}
-
-void AudioBackend_Deinit(void)
-{
-	OSCancelThread(OSGetDefaultThread(0));
-
-	OSJoinThread(OSGetDefaultThread(0), NULL);
-
-	AXQuit();
-}
-
-AudioBackend_Sound* AudioBackend_CreateSound(unsigned int frequency, const unsigned char *samples, size_t length)
-{
-	AudioBackend_Sound *sound = (AudioBackend_Sound*)malloc(sizeof(AudioBackend_Sound));
-
-	if (sound != NULL)
-	{
-		signed char *samples_copy = (signed char*)malloc(length);
-
-		if (samples_copy != NULL)
-		{
-			// Convert to signed
-			for (size_t i = 0; i < length; ++i)
-				samples_copy[i] = samples[i] - 0x80;
-
-			DCStoreRange(samples_copy, length);
-
-			sound->samples = samples_copy;
-			sound->length = length;
-			sound->voice = NULL;
-			sound->frequency = frequency;
-			sound->volume = 0x8000;
-			sound->pan_l = 0x8000;
-			sound->pan_r = 0x8000;
-
-			OSLockMutex(&sound_list_mutex);
-			sound->next = sound_list_head;
-			sound_list_head = sound;
-			OSUnlockMutex(&sound_list_mutex);
-
-			return sound;
-		}
-
-		free(sound);
-	}
-
-	return NULL;
-}
-
-void AudioBackend_DestroySound(AudioBackend_Sound *sound)
-{
-	if (sound == NULL)
-		return;
-
-	OSLockMutex(&sound_list_mutex);
-
-	// Unhook sound from the linked-list
-	for (AudioBackend_Sound **sound_pointer = &sound_list_head; *sound_pointer != NULL; sound_pointer = &(*sound_pointer)->next)
-	{
-		if (*sound_pointer == sound)
-		{
-			*sound_pointer = sound->next;
-			break;
-		}
-	}
-
-	OSUnlockMutex(&sound_list_mutex);
-
-	if (sound->voice != NULL)
-	{
-		AXVoiceBegin(sound->voice);
-		AXFreeVoice(sound->voice);
-		AXVoiceEnd(sound->voice);
-	}
-
-	free(sound->samples);
-	free(sound);
-}
-
-void AudioBackend_PlaySound(AudioBackend_Sound *sound, bool looping)
-{
-	if (sound == NULL)
-		return;
-
-	CullVoices();
-
-	OSLockMutex(&sound_list_mutex);
-
-	if (sound->voice == NULL)
-	{
-		AXVoice *voice = AXAcquireVoice(31, NULL, NULL);
-
-		if (voice != NULL)
-		{
-			AXVoiceBegin(voice);
-
-			AXSetVoiceType(voice, 0);
-
-			AXVoiceVeData vol = {.volume = sound->volume};
-			AXSetVoiceVe(voice, &vol);
-
-			AXVoiceDeviceMixData mix_data[6];
-			memset(mix_data, 0, sizeof(mix_data));
-			mix_data[0].bus[0].volume = sound->pan_l;
-			mix_data[1].bus[0].volume = sound->pan_r;
-
-			AXSetVoiceDeviceMix(voice, AX_DEVICE_TYPE_DRC, 0, mix_data);
-			AXSetVoiceDeviceMix(voice, AX_DEVICE_TYPE_TV, 0, mix_data);
-
-			float srcratio = (float)sound->frequency / (float)AXGetInputSamplesPerSec();
-			AXSetVoiceSrcRatio(voice, srcratio);
-			AXSetVoiceSrcType(voice, AX_VOICE_SRC_TYPE_LINEAR);
-
-			AXVoiceOffsets offs;
-			offs.dataType = AX_VOICE_FORMAT_LPCM8;
-			offs.endOffset = sound->length;
-			offs.loopingEnabled = AX_VOICE_LOOP_DISABLED;
-			offs.loopOffset = 0;
-			offs.currentOffset = 0;
-			offs.data = sound->samples;
-			AXSetVoiceOffsets(voice, &offs);
-
-			AXVoiceEnd(voice);
-
-			sound->voice = voice;
-		}
-	}
-
-	if (sound->voice != NULL)
-	{
-		AXVoiceBegin(sound->voice);
-
-		AXSetVoiceLoop(sound->voice, looping ? AX_VOICE_LOOP_ENABLED : AX_VOICE_LOOP_DISABLED);
-		AXSetVoiceState(sound->voice, AX_VOICE_STATE_PLAYING);
-
-		AXVoiceEnd(sound->voice);
-	}
-
-	OSUnlockMutex(&sound_list_mutex);
-}
-
-void AudioBackend_StopSound(AudioBackend_Sound *sound)
-{
-	if (sound == NULL)
-		return;
-
-	OSLockMutex(&sound_list_mutex);
-
-	if (sound->voice != NULL)
-	{
-		AXVoiceBegin(sound->voice);
-
-		AXSetVoiceState(sound->voice, AX_VOICE_STATE_STOPPED);
-
-		AXVoiceEnd(sound->voice);
-	}
-
-	OSUnlockMutex(&sound_list_mutex);
-}
-
-void AudioBackend_RewindSound(AudioBackend_Sound *sound)
-{
-	if (sound == NULL)
-		return;
-
-	OSLockMutex(&sound_list_mutex);
-
-	if (sound->voice != NULL)
-	{
-		AXVoiceBegin(sound->voice);
-
-		AXSetVoiceCurrentOffset(sound->voice, 0);
-
-		AXVoiceEnd(sound->voice);
-	}
-
-	OSUnlockMutex(&sound_list_mutex);
-}
-
-void AudioBackend_SetSoundFrequency(AudioBackend_Sound *sound, unsigned int frequency)
-{
-	if (sound == NULL)
-		return;
-
-	OSLockMutex(&sound_list_mutex);
-
-	sound->frequency = frequency;
-
-	if (sound->voice != NULL)
-	{
-		AXVoiceBegin(sound->voice);
-
-		float srcratio = (float)frequency / (float)AXGetInputSamplesPerSec();
-		AXSetVoiceSrcRatio(sound->voice, srcratio);
-
-		AXVoiceEnd(sound->voice);
-	}
-
-	OSUnlockMutex(&sound_list_mutex);
-}
-
-void AudioBackend_SetSoundVolume(AudioBackend_Sound *sound, long volume)
-{
-	if (sound == NULL)
-		return;
-
-	OSLockMutex(&sound_list_mutex);
-
-	sound->volume = (unsigned short)(0x8000 * MillibelToScale(volume));
-
-	if (sound->voice != NULL)
-	{
-		AXVoiceBegin(sound->voice);
-
-		AXVoiceVeData vol = {.volume = sound->volume};
-
-		AXSetVoiceVe(sound->voice, &vol);
-
-		AXVoiceEnd(sound->voice);
-	}
-
-	OSUnlockMutex(&sound_list_mutex);
-}
-
-void AudioBackend_SetSoundPan(AudioBackend_Sound *sound, long pan)
-{
-	if (sound == NULL)
-		return;
-
-	OSLockMutex(&sound_list_mutex);
-
-	sound->pan_l = (unsigned short)(0x8000 * MillibelToScale(-pan));
-	sound->pan_r = (unsigned short)(0x8000 * MillibelToScale(pan));
-
-	if (sound->voice != NULL)
-	{
-		AXVoiceBegin(sound->voice);
-
-		AXVoiceDeviceMixData mix_data[6];
-		memset(mix_data, 0, sizeof(mix_data));
-		mix_data[0].bus[0].volume = sound->pan_l;
-		mix_data[1].bus[0].volume = sound->pan_r;
-
-		AXSetVoiceDeviceMix(sound->voice, AX_DEVICE_TYPE_DRC, 0, mix_data);
-		AXSetVoiceDeviceMix(sound->voice, AX_DEVICE_TYPE_TV, 0, mix_data);
-
-		AXVoiceEnd(sound->voice);
-	}
-
-	OSUnlockMutex(&sound_list_mutex);
-}
-
-void AudioBackend_SetOrganyaCallback(void (*callback)(void))
-{
-	// As far as thread-safety goes - this is guarded by
-	// `organya_milliseconds`, which is guarded by `organya_mutex`.
-	organya_callback = callback;
-}
-
-void AudioBackend_SetOrganyaTimer(unsigned int milliseconds)
-{
-	OSLockMutex(&organya_mutex);
-
-	organya_milliseconds = milliseconds;
-
-	OSUnlockMutex(&organya_mutex);
-}
--- /dev/null
+++ b/src/Backends/Audio/WiiU.cpp
@@ -1,0 +1,422 @@
+// This darned thing doesn't work - sounds just randomly refuse to play,
+// particularly the ones used by Organya.
+
+// If anyone could figure out what causes this, that would be great.
+
+#include "../Audio.h"
+
+#include <math.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <coreinit/cache.h>
+#include <coreinit/mutex.h>
+#include <coreinit/thread.h>
+#include <sndcore2/core.h>
+#include <sndcore2/voice.h>
+#include <sndcore2/drcvs.h>
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#define CLAMP(x, y, z) MIN(MAX((x), (y)), (z))
+
+struct AudioBackend_Sound
+{
+	signed char *samples;
+	size_t length;
+	AXVoice *voice;
+	unsigned int frequency;
+	unsigned short volume;
+	unsigned short pan_l;
+	unsigned short pan_r;
+
+	struct AudioBackend_Sound *next;
+};
+
+static void (*organya_callback)(void);
+static unsigned int organya_milliseconds;
+
+static unsigned long ticks_per_second;
+
+static OSMutex sound_list_mutex;
+static OSMutex organya_mutex;
+
+static AudioBackend_Sound *sound_list_head;
+
+static void CullVoices(void)
+{
+	// Free any voices that aren't playing anymore
+	OSLockMutex(&sound_list_mutex);
+
+	for (AudioBackend_Sound *sound = sound_list_head; sound != NULL; sound = sound->next)
+	{
+		if (sound->voice != NULL)
+		{
+			if (!AXIsVoiceRunning(sound->voice))
+			{
+				AXVoiceBegin(sound->voice);
+				AXFreeVoice(sound->voice);
+				AXVoiceEnd(sound->voice);
+				sound->voice = NULL;
+			}
+		}
+	}
+
+	OSUnlockMutex(&sound_list_mutex);
+}
+
+static double MillibelToScale(long volume)
+{
+	// Volume is in hundredths of a decibel, from 0 to -10000
+	volume = CLAMP(volume, -10000, 0);
+	return pow(10.0, volume / 2000.0);
+}
+
+static unsigned long GetTicksMilliseconds(void)
+{
+	static uint64_t accumulator;
+
+	static unsigned long last_tick;
+
+	unsigned long current_tick = OSGetTick();
+
+	accumulator += current_tick - last_tick;
+
+	last_tick = current_tick;
+
+	return (accumulator * 1000) / ticks_per_second;
+}
+
+static int ThreadFunction(int argc, const char *argv[])
+{
+	for (;;)
+	{
+		OSTestThreadCancel();
+
+		OSLockMutex(&organya_mutex);
+
+		if (organya_milliseconds == 0)
+		{
+			OSUnlockMutex(&organya_mutex);
+
+			// Do nothing
+			OSSleepTicks(ticks_per_second / 1000);
+		}
+		else
+		{
+			OSUnlockMutex(&organya_mutex);
+
+			// Update Organya
+			static unsigned long next_ticks;
+
+			for (;;)
+			{
+				unsigned long ticks = GetTicksMilliseconds();
+
+				if (ticks >= next_ticks)
+					break;
+
+				OSSleepTicks(ticks_per_second / 1000);
+			}
+
+			OSLockMutex(&organya_mutex);
+			next_ticks += organya_milliseconds;
+			OSUnlockMutex(&organya_mutex);
+
+			OSLockMutex(&sound_list_mutex);
+			organya_callback();
+			OSUnlockMutex(&sound_list_mutex);
+		}
+	}
+
+	return 0;
+}
+
+bool AudioBackend_Init(void)
+{
+	if (!AXIsInit())
+	{
+		AXInitParams initparams = {
+			.renderer = AX_INIT_RENDERER_48KHZ,
+			.pipeline = AX_INIT_PIPELINE_SINGLE,
+		};
+
+		AXInitWithParams(&initparams);
+	}
+
+	ticks_per_second = OSGetSystemInfo()->busClockSpeed / 4;
+
+	OSInitMutex(&sound_list_mutex);
+	OSInitMutex(&organya_mutex);
+
+	OSRunThread(OSGetDefaultThread(0), ThreadFunction, 0, NULL);
+
+	return true;
+}
+
+void AudioBackend_Deinit(void)
+{
+	OSCancelThread(OSGetDefaultThread(0));
+
+	OSJoinThread(OSGetDefaultThread(0), NULL);
+
+	AXQuit();
+}
+
+AudioBackend_Sound* AudioBackend_CreateSound(unsigned int frequency, const unsigned char *samples, size_t length)
+{
+	AudioBackend_Sound *sound = (AudioBackend_Sound*)malloc(sizeof(AudioBackend_Sound));
+
+	if (sound != NULL)
+	{
+		signed char *samples_copy = (signed char*)malloc(length);
+
+		if (samples_copy != NULL)
+		{
+			// Convert to signed
+			for (size_t i = 0; i < length; ++i)
+				samples_copy[i] = samples[i] - 0x80;
+
+			DCStoreRange(samples_copy, length);
+
+			sound->samples = samples_copy;
+			sound->length = length;
+			sound->voice = NULL;
+			sound->frequency = frequency;
+			sound->volume = 0x8000;
+			sound->pan_l = 0x8000;
+			sound->pan_r = 0x8000;
+
+			OSLockMutex(&sound_list_mutex);
+			sound->next = sound_list_head;
+			sound_list_head = sound;
+			OSUnlockMutex(&sound_list_mutex);
+
+			return sound;
+		}
+
+		free(sound);
+	}
+
+	return NULL;
+}
+
+void AudioBackend_DestroySound(AudioBackend_Sound *sound)
+{
+	if (sound == NULL)
+		return;
+
+	OSLockMutex(&sound_list_mutex);
+
+	// Unhook sound from the linked-list
+	for (AudioBackend_Sound **sound_pointer = &sound_list_head; *sound_pointer != NULL; sound_pointer = &(*sound_pointer)->next)
+	{
+		if (*sound_pointer == sound)
+		{
+			*sound_pointer = sound->next;
+			break;
+		}
+	}
+
+	OSUnlockMutex(&sound_list_mutex);
+
+	if (sound->voice != NULL)
+	{
+		AXVoiceBegin(sound->voice);
+		AXFreeVoice(sound->voice);
+		AXVoiceEnd(sound->voice);
+	}
+
+	free(sound->samples);
+	free(sound);
+}
+
+void AudioBackend_PlaySound(AudioBackend_Sound *sound, bool looping)
+{
+	if (sound == NULL)
+		return;
+
+	CullVoices();
+
+	OSLockMutex(&sound_list_mutex);
+
+	if (sound->voice == NULL)
+	{
+		AXVoice *voice = AXAcquireVoice(31, NULL, NULL);
+
+		if (voice != NULL)
+		{
+			AXVoiceBegin(voice);
+
+			AXSetVoiceType(voice, 0);
+
+			AXVoiceVeData vol = {.volume = sound->volume};
+			AXSetVoiceVe(voice, &vol);
+
+			AXVoiceDeviceMixData mix_data[6];
+			memset(mix_data, 0, sizeof(mix_data));
+			mix_data[0].bus[0].volume = sound->pan_l;
+			mix_data[1].bus[0].volume = sound->pan_r;
+
+			AXSetVoiceDeviceMix(voice, AX_DEVICE_TYPE_DRC, 0, mix_data);
+			AXSetVoiceDeviceMix(voice, AX_DEVICE_TYPE_TV, 0, mix_data);
+
+			float srcratio = (float)sound->frequency / (float)AXGetInputSamplesPerSec();
+			AXSetVoiceSrcRatio(voice, srcratio);
+			AXSetVoiceSrcType(voice, AX_VOICE_SRC_TYPE_LINEAR);
+
+			AXVoiceOffsets offs;
+			offs.dataType = AX_VOICE_FORMAT_LPCM8;
+			offs.endOffset = sound->length;
+			offs.loopingEnabled = AX_VOICE_LOOP_DISABLED;
+			offs.loopOffset = 0;
+			offs.currentOffset = 0;
+			offs.data = sound->samples;
+			AXSetVoiceOffsets(voice, &offs);
+
+			AXVoiceEnd(voice);
+
+			sound->voice = voice;
+		}
+	}
+
+	if (sound->voice != NULL)
+	{
+		AXVoiceBegin(sound->voice);
+
+		AXSetVoiceLoop(sound->voice, looping ? AX_VOICE_LOOP_ENABLED : AX_VOICE_LOOP_DISABLED);
+		AXSetVoiceState(sound->voice, AX_VOICE_STATE_PLAYING);
+
+		AXVoiceEnd(sound->voice);
+	}
+
+	OSUnlockMutex(&sound_list_mutex);
+}
+
+void AudioBackend_StopSound(AudioBackend_Sound *sound)
+{
+	if (sound == NULL)
+		return;
+
+	OSLockMutex(&sound_list_mutex);
+
+	if (sound->voice != NULL)
+	{
+		AXVoiceBegin(sound->voice);
+
+		AXSetVoiceState(sound->voice, AX_VOICE_STATE_STOPPED);
+
+		AXVoiceEnd(sound->voice);
+	}
+
+	OSUnlockMutex(&sound_list_mutex);
+}
+
+void AudioBackend_RewindSound(AudioBackend_Sound *sound)
+{
+	if (sound == NULL)
+		return;
+
+	OSLockMutex(&sound_list_mutex);
+
+	if (sound->voice != NULL)
+	{
+		AXVoiceBegin(sound->voice);
+
+		AXSetVoiceCurrentOffset(sound->voice, 0);
+
+		AXVoiceEnd(sound->voice);
+	}
+
+	OSUnlockMutex(&sound_list_mutex);
+}
+
+void AudioBackend_SetSoundFrequency(AudioBackend_Sound *sound, unsigned int frequency)
+{
+	if (sound == NULL)
+		return;
+
+	OSLockMutex(&sound_list_mutex);
+
+	sound->frequency = frequency;
+
+	if (sound->voice != NULL)
+	{
+		AXVoiceBegin(sound->voice);
+
+		float srcratio = (float)frequency / (float)AXGetInputSamplesPerSec();
+		AXSetVoiceSrcRatio(sound->voice, srcratio);
+
+		AXVoiceEnd(sound->voice);
+	}
+
+	OSUnlockMutex(&sound_list_mutex);
+}
+
+void AudioBackend_SetSoundVolume(AudioBackend_Sound *sound, long volume)
+{
+	if (sound == NULL)
+		return;
+
+	OSLockMutex(&sound_list_mutex);
+
+	sound->volume = (unsigned short)(0x8000 * MillibelToScale(volume));
+
+	if (sound->voice != NULL)
+	{
+		AXVoiceBegin(sound->voice);
+
+		AXVoiceVeData vol = {.volume = sound->volume};
+
+		AXSetVoiceVe(sound->voice, &vol);
+
+		AXVoiceEnd(sound->voice);
+	}
+
+	OSUnlockMutex(&sound_list_mutex);
+}
+
+void AudioBackend_SetSoundPan(AudioBackend_Sound *sound, long pan)
+{
+	if (sound == NULL)
+		return;
+
+	OSLockMutex(&sound_list_mutex);
+
+	sound->pan_l = (unsigned short)(0x8000 * MillibelToScale(-pan));
+	sound->pan_r = (unsigned short)(0x8000 * MillibelToScale(pan));
+
+	if (sound->voice != NULL)
+	{
+		AXVoiceBegin(sound->voice);
+
+		AXVoiceDeviceMixData mix_data[6];
+		memset(mix_data, 0, sizeof(mix_data));
+		mix_data[0].bus[0].volume = sound->pan_l;
+		mix_data[1].bus[0].volume = sound->pan_r;
+
+		AXSetVoiceDeviceMix(sound->voice, AX_DEVICE_TYPE_DRC, 0, mix_data);
+		AXSetVoiceDeviceMix(sound->voice, AX_DEVICE_TYPE_TV, 0, mix_data);
+
+		AXVoiceEnd(sound->voice);
+	}
+
+	OSUnlockMutex(&sound_list_mutex);
+}
+
+void AudioBackend_SetOrganyaCallback(void (*callback)(void))
+{
+	// As far as thread-safety goes - this is guarded by
+	// `organya_milliseconds`, which is guarded by `organya_mutex`.
+	organya_callback = callback;
+}
+
+void AudioBackend_SetOrganyaTimer(unsigned int milliseconds)
+{
+	OSLockMutex(&organya_mutex);
+
+	organya_milliseconds = milliseconds;
+
+	OSUnlockMutex(&organya_mutex);
+}