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);
+}