New way to allocate Wii U voices

The previous method wasn't good enough: we were still maxing-out at
96 (did the Decaf devs misread that as hex? Is that why the
emulator's limit is 150 instead?).

Anyway, this new solution is a little brutish, but I can't think of
anything else that would work: right now, I have the Organya thread
constantly polling a linked-list of the currently-loaded sounds, and
checking if their voices have finished playing or not. If they've
finished, they get freed.

When AudioBackend_PlaySound is called, if checks if the sound's voice
has been freed or not, and reallocates it if it has.

There doesn't seem to be a noticable overhead to this, and it keeps
the number of currently-allocated voices *very* low (from 10 to 20 on
average).
This commit is contained in:
Clownacy 2020-04-17 23:43:17 +01:00
parent 617f7a25d4
commit 5bcc0ec393

View file

@ -6,8 +6,8 @@
#include <string.h> #include <string.h>
#include <coreinit/cache.h> #include <coreinit/cache.h>
#include <coreinit/mutex.h>
#include <coreinit/thread.h> #include <coreinit/thread.h>
#include <sndcore2/core.h> #include <sndcore2/core.h>
#include <sndcore2/voice.h> #include <sndcore2/voice.h>
#include <sndcore2/drcvs.h> #include <sndcore2/drcvs.h>
@ -26,11 +26,20 @@ struct AudioBackend_Sound
unsigned short pan_l; unsigned short pan_l;
unsigned short pan_r; unsigned short pan_r;
AXVoiceDeviceMixData mix_data[6]; AXVoiceDeviceMixData mix_data[6];
struct AudioBackend_Sound *next;
}; };
static void (*organya_callback)(void); static void (*organya_callback)(void);
static unsigned int organya_milliseconds; static unsigned int organya_milliseconds;
static unsigned long ticks_per_second;
static OSMutex mutex;
static OSMutex organya_mutex;
static AudioBackend_Sound *sound_list_head;
static double MillibelToScale(long volume) static double MillibelToScale(long volume)
{ {
// Volume is in hundredths of a decibel, from 0 to -10000 // Volume is in hundredths of a decibel, from 0 to -10000
@ -38,13 +47,11 @@ static double MillibelToScale(long volume)
return pow(10.0, volume / 2000.0); return pow(10.0, volume / 2000.0);
} }
static unsigned long ticks_per_second;
static unsigned long GetTicksMilliseconds(void) static unsigned long GetTicksMilliseconds(void)
{ {
static uint64_t accumulator; static uint64_t accumulator;
static unsigned long last_tick; // For some dumbass reason, OSTick is signed, so force unsigned here instead static unsigned long last_tick;
unsigned long current_tick = OSGetTick(); unsigned long current_tick = OSGetTick();
@ -61,31 +68,57 @@ static int ThreadFunction(int argc, const char *argv[])
{ {
OSTestThreadCancel(); OSTestThreadCancel();
OSLockMutex(&organya_mutex);
if (organya_milliseconds == 0) if (organya_milliseconds == 0)
{ {
OSSleepTicks(1); OSUnlockMutex(&organya_mutex);
// Do nothing
OSSleepTicks(ticks_per_second / 1000);
} }
else else
{ {
OSUnlockMutex(&organya_mutex);
// Update Organya
static unsigned long next_ticks; static unsigned long next_ticks;
unsigned long ticks;
for (;;) for (;;)
{ {
ticks = GetTicksMilliseconds(); unsigned long ticks = GetTicksMilliseconds();
if (ticks >= next_ticks) if (ticks >= next_ticks)
{
next_ticks += organya_milliseconds;
break; break;
}
OSSleepTicks(1); OSSleepTicks(ticks_per_second / 1000);
} }
if (organya_callback != NULL) OSLockMutex(&organya_mutex);
organya_callback(); next_ticks += organya_milliseconds;
OSUnlockMutex(&organya_mutex);
OSLockMutex(&mutex);
organya_callback();
OSUnlockMutex(&mutex);
} }
// Free any voices that aren't playing anymore
OSLockMutex(&mutex);
for (AudioBackend_Sound *sound = sound_list_head; sound != NULL; sound = sound->next)
{
if (sound->voice != NULL)
{
if (!AXIsVoiceRunning(sound->voice))
{
AXFreeVoice(sound->voice);
sound->voice = NULL;
}
}
}
OSUnlockMutex(&mutex);
} }
return 0; return 0;
@ -105,8 +138,13 @@ bool AudioBackend_Init(void)
ticks_per_second = OSGetSystemInfo()->busClockSpeed / 4; ticks_per_second = OSGetSystemInfo()->busClockSpeed / 4;
OSInitMutex(&mutex);
OSInitMutex(&organya_mutex);
OSRunThread(OSGetDefaultThread(0), ThreadFunction, 0, NULL); OSRunThread(OSGetDefaultThread(0), ThreadFunction, 0, NULL);
// AXRegisterFrameCallback(FrameCallback);
return true; return true;
} }
@ -143,6 +181,11 @@ AudioBackend_Sound* AudioBackend_CreateSound(unsigned int frequency, const unsig
sound->pan_l = 0x8000; sound->pan_l = 0x8000;
sound->pan_r = 0x8000; sound->pan_r = 0x8000;
OSLockMutex(&mutex);
sound->next = sound_list_head;
sound_list_head = sound;
OSUnlockMutex(&mutex);
return sound; return sound;
} }
@ -154,7 +197,22 @@ AudioBackend_Sound* AudioBackend_CreateSound(unsigned int frequency, const unsig
void AudioBackend_DestroySound(AudioBackend_Sound *sound) void AudioBackend_DestroySound(AudioBackend_Sound *sound)
{ {
AudioBackend_StopSound(sound); OSLockMutex(&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(&mutex);
if (sound->voice != NULL)
AXFreeVoice(sound->voice);
free(sound->samples); free(sound->samples);
free(sound); free(sound);
@ -162,6 +220,8 @@ void AudioBackend_DestroySound(AudioBackend_Sound *sound)
void AudioBackend_PlaySound(AudioBackend_Sound *sound, bool looping) void AudioBackend_PlaySound(AudioBackend_Sound *sound, bool looping)
{ {
OSLockMutex(&mutex);
if (sound->voice == NULL) if (sound->voice == NULL)
{ {
AXVoice *voice = AXAcquireVoice(31, NULL, NULL); AXVoice *voice = AXAcquireVoice(31, NULL, NULL);
@ -206,26 +266,34 @@ void AudioBackend_PlaySound(AudioBackend_Sound *sound, bool looping)
AXSetVoiceLoop(sound->voice, looping ? AX_VOICE_LOOP_ENABLED : AX_VOICE_LOOP_DISABLED); AXSetVoiceLoop(sound->voice, looping ? AX_VOICE_LOOP_ENABLED : AX_VOICE_LOOP_DISABLED);
AXSetVoiceState(sound->voice, AX_VOICE_STATE_PLAYING); AXSetVoiceState(sound->voice, AX_VOICE_STATE_PLAYING);
} }
OSUnlockMutex(&mutex);
} }
void AudioBackend_StopSound(AudioBackend_Sound *sound) void AudioBackend_StopSound(AudioBackend_Sound *sound)
{ {
OSLockMutex(&mutex);
if (sound->voice != NULL) if (sound->voice != NULL)
{ AXSetVoiceState(sound->voice, AX_VOICE_STATE_STOPPED);
AXVoice *voice = sound->voice;
sound->voice = NULL; OSUnlockMutex(&mutex);
AXFreeVoice(voice);
}
} }
void AudioBackend_RewindSound(AudioBackend_Sound *sound) void AudioBackend_RewindSound(AudioBackend_Sound *sound)
{ {
OSLockMutex(&mutex);
if (sound->voice != NULL) if (sound->voice != NULL)
AXSetVoiceCurrentOffset(sound->voice, 0); AXSetVoiceCurrentOffset(sound->voice, 0);
OSUnlockMutex(&mutex);
} }
void AudioBackend_SetSoundFrequency(AudioBackend_Sound *sound, unsigned int frequency) void AudioBackend_SetSoundFrequency(AudioBackend_Sound *sound, unsigned int frequency)
{ {
OSLockMutex(&mutex);
sound->frequency = frequency; sound->frequency = frequency;
if (sound->voice != NULL) if (sound->voice != NULL)
@ -233,10 +301,14 @@ void AudioBackend_SetSoundFrequency(AudioBackend_Sound *sound, unsigned int freq
float srcratio = (float)frequency / (float)AXGetInputSamplesPerSec(); float srcratio = (float)frequency / (float)AXGetInputSamplesPerSec();
AXSetVoiceSrcRatio(sound->voice, srcratio); AXSetVoiceSrcRatio(sound->voice, srcratio);
} }
OSUnlockMutex(&mutex);
} }
void AudioBackend_SetSoundVolume(AudioBackend_Sound *sound, long volume) void AudioBackend_SetSoundVolume(AudioBackend_Sound *sound, long volume)
{ {
OSLockMutex(&mutex);
sound->volume = (unsigned short)(0x8000 * MillibelToScale(volume)); sound->volume = (unsigned short)(0x8000 * MillibelToScale(volume));
if (sound->voice != NULL) if (sound->voice != NULL)
@ -245,10 +317,14 @@ void AudioBackend_SetSoundVolume(AudioBackend_Sound *sound, long volume)
AXSetVoiceVe(sound->voice, &vol); AXSetVoiceVe(sound->voice, &vol);
} }
OSUnlockMutex(&mutex);
} }
void AudioBackend_SetSoundPan(AudioBackend_Sound *sound, long pan) void AudioBackend_SetSoundPan(AudioBackend_Sound *sound, long pan)
{ {
OSLockMutex(&mutex);
sound->pan_l = (unsigned short)(0x8000 * MillibelToScale(-pan)); sound->pan_l = (unsigned short)(0x8000 * MillibelToScale(-pan));
sound->pan_r = (unsigned short)(0x8000 * MillibelToScale(pan)); sound->pan_r = (unsigned short)(0x8000 * MillibelToScale(pan));
@ -260,10 +336,16 @@ void AudioBackend_SetSoundPan(AudioBackend_Sound *sound, long pan)
AXSetVoiceDeviceMix(sound->voice, AX_DEVICE_TYPE_DRC, 0, sound->mix_data); AXSetVoiceDeviceMix(sound->voice, AX_DEVICE_TYPE_DRC, 0, sound->mix_data);
AXSetVoiceDeviceMix(sound->voice, AX_DEVICE_TYPE_TV, 0, sound->mix_data); AXSetVoiceDeviceMix(sound->voice, AX_DEVICE_TYPE_TV, 0, sound->mix_data);
} }
OSUnlockMutex(&mutex);
} }
void AudioBackend_SetOrganyaCallback(void (*callback)(void), unsigned int milliseconds) void AudioBackend_SetOrganyaCallback(void (*callback)(void), unsigned int milliseconds)
{ {
OSLockMutex(&organya_mutex);
organya_callback = callback; organya_callback = callback;
organya_milliseconds = milliseconds; organya_milliseconds = milliseconds;
OSUnlockMutex(&organya_mutex);
} }