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