Threaded Audio Support (#176)

* Initial threaded audio. (WIP)

* Ready for testing threaded audio.

* Remove this, It causes issues on older gcc versions.

* Fix Loading Thread not running properly.

* Fix Loading Screen & Implementation Updates

* Update comment.

* Revamp Thread States
This commit is contained in:
Prince Frizzy 2024-11-26 00:53:09 -05:00 committed by GitHub
parent bf2d7b629e
commit 507f578dbb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 440 additions and 56 deletions

View file

@ -2,12 +2,15 @@
#include "data.h"
#include "effects.h"
#include "src/pc/thread.h"
extern struct OSMesgQueue OSMesgQueue0;
extern struct OSMesgQueue OSMesgQueue1;
extern struct OSMesgQueue OSMesgQueue2;
extern struct OSMesgQueue OSMesgQueue3;
struct ThreadHandle gAudioThread = { 0 };
#ifdef VERSION_EU
struct ReverbSettingsEU sReverbSettings[] = {
{ /*Downsample Rate*/ 0x04, /*Window Size*/ 0x0c, /*Gain*/ 0x2fff },

View file

@ -5,6 +5,7 @@
#include "internal.h"
#include "types.h"
#include "src/pc/thread.h"
#define AUDIO_LOCK_UNINITIALIZED 0
#define AUDIO_LOCK_NOT_LOADING 0x76557364
@ -12,6 +13,8 @@
#define NUMAIBUFFERS 3
extern struct ThreadHandle gAudioThread;
// constant .data
#if defined(VERSION_EU) || defined(VERSION_SH)
extern struct AudioSessionSettingsEU gAudioSessionPresets[];

View file

@ -58,31 +58,35 @@ void sequence_channel_process_sound(struct SequenceChannel *seqChannel, s32 reca
}
#else
static void sequence_channel_process_sound(struct SequenceChannel *seqChannel) {
if (seqChannel == NULL) { return; }
struct SequencePlayer *seqPlayer = seqChannel->seqPlayer;
if (seqPlayer == NULL) { return; }
f32 channelVolume;
f32 panLayerWeight;
f32 panFromChannel;
s32 i;
// Rom-hacks audio fix
// Force the BGM sequence channels to follow the BGM sequence player mute behavior
// Make the audio completely silent if MUTE_BEHAVIOR_STOP_SCRIPT or MUTE_BEHAVIOR_STOP_NOTES is set
if (seqChannel->seqPlayer == &gSequencePlayers[0]) {
seqChannel->muteBehavior = seqChannel->seqPlayer->muteBehavior;
if (seqChannel->seqPlayer->muted && (seqChannel->muteBehavior & (MUTE_BEHAVIOR_STOP_SCRIPT | MUTE_BEHAVIOR_STOP_NOTES)) != 0) {
seqChannel->seqPlayer->muteVolumeScale = 0.f;
if (seqPlayer == &gSequencePlayers[0]) {
seqChannel->muteBehavior = seqPlayer->muteBehavior;
if (seqPlayer->muted && (seqChannel->muteBehavior & (MUTE_BEHAVIOR_STOP_SCRIPT | MUTE_BEHAVIOR_STOP_NOTES)) != 0) {
seqPlayer->muteVolumeScale = 0.f;
}
}
channelVolume = seqChannel->volume * seqChannel->volumeScale *
seqChannel->seqPlayer->fadeVolume * seqChannel->seqPlayer->volumeScale;
if (seqChannel->seqPlayer->muted && (seqChannel->muteBehavior & MUTE_BEHAVIOR_SOFTEN) != 0) {
channelVolume *= seqChannel->seqPlayer->muteVolumeScale;
seqPlayer->fadeVolume * seqPlayer->volumeScale;
if (seqPlayer->muted && (seqChannel->muteBehavior & MUTE_BEHAVIOR_SOFTEN) != 0) {
channelVolume *= seqPlayer->muteVolumeScale;
}
panFromChannel = seqChannel->pan * seqChannel->panChannelWeight;
panLayerWeight = US_FLOAT(1.0) - seqChannel->panChannelWeight;
for (i = 0; i < 4; i++) {
for (s32 i = 0; i < 4; i++) {
struct SequenceChannelLayer *layer = seqChannel->layers[i];
if (layer != NULL && layer->enabled && layer->note != NULL) {
layer->noteFreqScale = layer->freqScale * seqChannel->freqScale;

View file

@ -835,21 +835,29 @@ void create_next_audio_buffer(s16 *samples, u32 num_samples) {
extern f32 *smlua_get_vec3f_for_play_sound(f32 *pos);
void play_sound(s32 soundBits, f32 *pos) {
MUTEX_LOCK(gAudioThread);
pos = smlua_get_vec3f_for_play_sound(pos);
smlua_call_event_hooks_on_play_sound(HOOK_ON_PLAY_SOUND, soundBits, pos, &soundBits);
sSoundRequests[sSoundRequestCount].soundBits = soundBits;
sSoundRequests[sSoundRequestCount].position = pos;
sSoundRequests[sSoundRequestCount].customFreqScale = 0;
sSoundRequestCount++;
MUTEX_UNLOCK(gAudioThread);
}
void play_sound_with_freq_scale(s32 soundBits, f32* pos, f32 freqScale) {
MUTEX_LOCK(gAudioThread);
pos = smlua_get_vec3f_for_play_sound(pos);
smlua_call_event_hooks_on_play_sound(HOOK_ON_PLAY_SOUND, soundBits, pos, &soundBits);
sSoundRequests[sSoundRequestCount].soundBits = soundBits;
sSoundRequests[sSoundRequestCount].position = pos;
sSoundRequests[sSoundRequestCount].customFreqScale = freqScale;
sSoundRequestCount++;
MUTEX_UNLOCK(gAudioThread);
}
/**
@ -986,11 +994,15 @@ static void delete_sound_from_bank(u8 bank, u8 soundIndex) {
* Called from threads: thread3_main, thread4_sound, thread5_game_loop
*/
static void update_background_music_after_sound(u8 bank, u8 soundIndex) {
MUTEX_LOCK(gAudioThread);
if (bank >= SOUND_BANK_COUNT || soundIndex >= SOUND_INDEX_COUNT) { return; }
if (sSoundBanks[bank][soundIndex].soundBits & SOUND_LOWER_BACKGROUND_MUSIC) {
sSoundBanksThatLowerBackgroundMusic &= (1 << bank) ^ 0xffff;
begin_background_music_fade(50);
}
MUTEX_UNLOCK(gAudioThread);
}
/**
@ -1761,6 +1773,8 @@ static void update_game_sound(void) {
* Called from threads: thread4_sound, thread5_game_loop
*/
static void seq_player_play_sequence(u8 player, u8 seqId, u16 arg2) {
MUTEX_LOCK(gAudioThread);
if (player >= SEQUENCE_PLAYERS) { return; }
u8 targetVolume;
u8 i;
@ -1801,12 +1815,16 @@ static void seq_player_play_sequence(u8 player, u8 seqId, u16 arg2) {
seq_player_fade_from_zero_volume(player, arg2);
}
#endif
MUTEX_UNLOCK(gAudioThread);
}
/**
* Called from threads: thread5_game_loop
*/
void seq_player_fade_out(u8 player, u16 fadeDuration) {
MUTEX_LOCK(gAudioThread);
if (player >= SEQUENCE_PLAYERS) { return; }
#if defined(VERSION_EU) || defined(VERSION_SH)
#ifdef VERSION_EU
@ -1824,6 +1842,8 @@ void seq_player_fade_out(u8 player, u16 fadeDuration) {
}
seq_player_fade_to_zero_volume(player, fadeDuration);
#endif
MUTEX_UNLOCK(gAudioThread);
}
/**
@ -1840,6 +1860,8 @@ void fade_volume_scale(u8 player, u8 targetScale, u16 fadeDuration) {
* Called from threads: thread3_main, thread4_sound, thread5_game_loop
*/
static void fade_channel_volume_scale(u8 player, u8 channelIndex, u8 targetScale, u16 fadeDuration) {
MUTEX_LOCK(gAudioThread);
struct ChannelVolumeScaleFade *temp;
if (player >= SEQUENCE_PLAYERS) { return; }
if (channelIndex >= CHANNELS_MAX) { return; }
@ -1853,6 +1875,8 @@ static void fade_channel_volume_scale(u8 player, u8 channelIndex, u8 targetScale
temp->target = targetScale;
temp->current = gSequencePlayers[player].channels[channelIndex]->volumeScale;
}
MUTEX_UNLOCK(gAudioThread);
}
/**
@ -2059,6 +2083,8 @@ void unused_8031FED0(u8 player, u32 bits, s8 arg2) {
* Called from threads: thread5_game_loop
*/
void seq_player_lower_volume(u8 player, u16 fadeDuration, u8 percentage) {
MUTEX_LOCK(gAudioThread);
if (player >= SEQUENCE_PLAYERS) { return; }
if (player == SEQ_PLAYER_LEVEL) {
sLowerBackgroundMusicVolume = TRUE;
@ -2066,6 +2092,8 @@ void seq_player_lower_volume(u8 player, u16 fadeDuration, u8 percentage) {
} else if (gSequencePlayers[player].enabled == TRUE) {
seq_player_fade_to_percentage_of_volume(player, fadeDuration, percentage);
}
MUTEX_UNLOCK(gAudioThread);
}
/**
@ -2077,6 +2105,8 @@ void seq_player_lower_volume(u8 player, u16 fadeDuration, u8 percentage) {
* Called from threads: thread5_game_loop
*/
void seq_player_unlower_volume(u8 player, u16 fadeDuration) {
MUTEX_LOCK(gAudioThread);
if (player >= SEQUENCE_PLAYERS) { return; }
sLowerBackgroundMusicVolume = FALSE;
if (player == SEQ_PLAYER_LEVEL) {
@ -2088,6 +2118,8 @@ void seq_player_unlower_volume(u8 player, u16 fadeDuration) {
seq_player_fade_to_normal_volume(player, fadeDuration);
}
}
MUTEX_UNLOCK(gAudioThread);
}
/**
@ -2106,6 +2138,8 @@ static u8 begin_background_music_fade(u16 fadeDuration) {
|| sCurrentBackgroundMusicSeqId == SEQ_EVENT_CUTSCENE_CREDITS) {
return 0xff;
}
MUTEX_LOCK(gAudioThread);
if (gSequencePlayers[SEQ_PLAYER_LEVEL].volume == 0.0f && fadeDuration) {
gSequencePlayers[SEQ_PLAYER_LEVEL].volume = gSequencePlayers[SEQ_PLAYER_LEVEL].fadeVolume;
@ -2141,6 +2175,8 @@ static u8 begin_background_music_fade(u16 fadeDuration) {
seq_player_fade_to_normal_volume(SEQ_PLAYER_LEVEL, fadeDuration);
}
}
MUTEX_UNLOCK(gAudioThread);
return targetVolume;
}
@ -2149,6 +2185,8 @@ static u8 begin_background_music_fade(u16 fadeDuration) {
* Called from threads: thread5_game_loop
*/
void set_audio_muted(u8 muted) {
MUTEX_LOCK(gAudioThread);
u8 i;
for (i = 0; i < SEQUENCE_PLAYERS; i++) {
@ -2161,12 +2199,16 @@ void set_audio_muted(u8 muted) {
gSequencePlayers[i].muted = muted;
#endif
}
MUTEX_UNLOCK(gAudioThread);
}
/**
* Called from threads: thread4_sound
*/
void sound_init(void) {
MUTEX_LOCK(gAudioThread);
u8 i;
u8 j;
@ -2225,6 +2267,8 @@ void sound_init(void) {
sCurrentSecondaryMusicVolume = 0;
sNumProcessedSoundRequests = 0;
sSoundRequestCount = 0;
MUTEX_UNLOCK(gAudioThread);
}
// (unused)
@ -2253,6 +2297,8 @@ void get_currently_playing_sound(u8 bank, u8 *numPlayingSounds, u8 *numSoundsInB
* Called from threads: thread5_game_loop
*/
void stop_sound(u32 soundBits, f32 *pos) {
MUTEX_LOCK(gAudioThread);
pos = smlua_get_vec3f_for_play_sound(pos);
u8 bank = (soundBits & SOUNDARGS_MASK_BANK) >> SOUNDARGS_SHIFT_BANK;
if (bank >= SOUND_BANK_COUNT) { return; }
@ -2275,12 +2321,16 @@ void stop_sound(u32 soundBits, f32 *pos) {
soundIndex = sSoundBanks[bank][soundIndex].next;
}
}
MUTEX_UNLOCK(gAudioThread);
}
/**
* Called from threads: thread5_game_loop
*/
void stop_sounds_from_source(f32 *pos) {
MUTEX_LOCK(gAudioThread);
u8 bank;
u8 soundIndex;
@ -2294,12 +2344,16 @@ void stop_sounds_from_source(f32 *pos) {
soundIndex = sSoundBanks[bank][soundIndex].next;
}
}
MUTEX_UNLOCK(gAudioThread);
}
/**
* Called from threads: thread3_main, thread5_game_loop
*/
static void stop_sounds_in_bank(u8 bank) {
MUTEX_LOCK(gAudioThread);
if (bank >= SOUND_BANK_COUNT) { return; }
u8 soundIndex = sSoundBanks[bank][0].next;
@ -2308,6 +2362,8 @@ static void stop_sounds_in_bank(u8 bank) {
sSoundBanks[bank][soundIndex].soundBits = NO_SOUND;
soundIndex = sSoundBanks[bank][soundIndex].next;
}
MUTEX_UNLOCK(gAudioThread);
}
/**
@ -2327,6 +2383,8 @@ void stop_sounds_in_continuous_banks(void) {
* Called from threads: thread3_main, thread5_game_loop
*/
void sound_banks_disable(UNUSED u8 player, u16 bankMask) {
MUTEX_LOCK(gAudioThread);
u8 i;
for (i = 0; i < SOUND_BANK_COUNT; i++) {
@ -2335,23 +2393,31 @@ void sound_banks_disable(UNUSED u8 player, u16 bankMask) {
}
bankMask = bankMask >> 1;
}
MUTEX_UNLOCK(gAudioThread);
}
/**
* Called from threads: thread5_game_loop
*/
static void disable_all_sequence_players(void) {
MUTEX_LOCK(gAudioThread);
u8 i;
for (i = 0; i < SEQUENCE_PLAYERS; i++) {
sequence_player_disable(&gSequencePlayers[i]);
}
MUTEX_UNLOCK(gAudioThread);
}
/**
* Called from threads: thread5_game_loop
*/
void sound_banks_enable(UNUSED u8 player, u16 bankMask) {
MUTEX_LOCK(gAudioThread);
u8 i;
for (i = 0; i < SOUND_BANK_COUNT; i++) {
@ -2360,6 +2426,8 @@ void sound_banks_enable(UNUSED u8 player, u16 bankMask) {
}
bankMask = bankMask >> 1;
}
MUTEX_UNLOCK(gAudioThread);
}
u8 unused_803209D8(u8 player, u8 channelIndex, u8 arg2) {
@ -2436,6 +2504,8 @@ void set_sequence_player_volume(s32 player, f32 volume) {
* Called from threads: thread5_game_loop
*/
void play_music(u8 player, u16 seqArgs, u16 fadeTimer) {
MUTEX_LOCK(gAudioThread);
if (player >= SEQUENCE_PLAYERS) { return; }
u8 seqId = seqArgs & 0xff;
u8 priority = seqArgs >> 8;
@ -2498,12 +2568,16 @@ void play_music(u8 player, u16 seqArgs, u16 fadeTimer) {
// Insert item into queue.
sBackgroundMusicQueue[foundIndex].priority = priority;
sBackgroundMusicQueue[foundIndex].seqId = seqId;
MUTEX_UNLOCK(gAudioThread);
}
/**
* Called from threads: thread5_game_loop
*/
void stop_background_music(u16 seqId) {
MUTEX_LOCK(gAudioThread);
u8 foundIndex;
u8 i;
@ -2559,6 +2633,8 @@ void stop_background_music(u16 seqId) {
// @bug? If the sequence queue is full and we attempt to stop a sequence
// that isn't in the queue, this writes out of bounds. Can that happen?
sBackgroundMusicQueue[i].priority = 0;
MUTEX_UNLOCK(gAudioThread);
}
/**
@ -2642,6 +2718,8 @@ void fade_in_env_music(void) {
* Called from threads: thread5_game_loop
*/
void play_secondary_music(u8 seqId, u8 bgMusicVolume, u8 volume, u16 fadeTimer) {
MUTEX_LOCK(gAudioThread);
UNUSED u32 dummy;
sUnused80332118 = 0;
@ -2664,6 +2742,8 @@ void play_secondary_music(u8 seqId, u8 bgMusicVolume, u8 volume, u16 fadeTimer)
seq_player_fade_to_target_volume(SEQ_PLAYER_ENV, fadeTimer, volume);
sCurrentSecondaryMusicVolume = volume;
}
MUTEX_UNLOCK(gAudioThread);
}
/**
@ -2683,6 +2763,8 @@ void stop_secondary_music(u16 fadeTimer) {
* Called from threads: thread3_main, thread5_game_loop
*/
void set_audio_fadeout(u16 fadeDuration) {
MUTEX_LOCK(gAudioThread);
if (sHasStartedFadeOut) {
return;
}
@ -2710,6 +2792,8 @@ void set_audio_fadeout(u16 fadeDuration) {
}
sHasStartedFadeOut = TRUE;
MUTEX_UNLOCK(gAudioThread);
}
/**
@ -2807,6 +2891,8 @@ void play_toads_jingle(void) {
* Called from threads: thread5_game_loop
*/
void sound_reset(u8 presetId) {
MUTEX_LOCK(gAudioThread);
#ifndef VERSION_JP
if (presetId >= 8) {
presetId = 0;
@ -2834,6 +2920,8 @@ void sound_reset(u8 presetId) {
D_80332108 = (D_80332108 & 0xf0) + presetId;
gSoundMode = D_80332108 >> 4;
sHasStartedFadeOut = FALSE;
MUTEX_UNLOCK(gAudioThread);
}
/**

View file

@ -311,6 +311,8 @@ void *sound_alloc_uninitialized(struct SoundAllocPool *pool, u32 size) {
#endif
void sound_alloc_pool_init(struct SoundAllocPool *pool, void *memAddr, u32 size) {
MUTEX_LOCK(gAudioThread);
pool->cur = pool->start = (u8 *) ALIGN16((uintptr_t) memAddr);
#ifdef VERSION_SH
pool->size = size - ((uintptr_t) memAddr & 0xf);
@ -318,15 +320,23 @@ void sound_alloc_pool_init(struct SoundAllocPool *pool, void *memAddr, u32 size)
pool->size = size;
#endif
pool->numAllocatedEntries = 0;
MUTEX_UNLOCK(gAudioThread);
}
void persistent_pool_clear(struct PersistentPool *persistent) {
MUTEX_LOCK(gAudioThread);
persistent->pool.numAllocatedEntries = 0;
persistent->pool.cur = persistent->pool.start;
persistent->numEntries = 0;
MUTEX_UNLOCK(gAudioThread);
}
void temporary_pool_clear(struct TemporaryPool *temporary) {
MUTEX_LOCK(gAudioThread);
temporary->pool.numAllocatedEntries = 0;
temporary->pool.cur = temporary->pool.start;
temporary->nextSide = 0;
@ -338,6 +348,8 @@ void temporary_pool_clear(struct TemporaryPool *temporary) {
#endif
temporary->entries[0].id = -1; // should be at 1e not 1c
temporary->entries[1].id = -1;
MUTEX_UNLOCK(gAudioThread);
}
void unused_803160F8(struct SoundAllocPool *pool) {
@ -347,8 +359,12 @@ void unused_803160F8(struct SoundAllocPool *pool) {
extern s32 D_SH_80315EE8;
void sound_init_main_pools(s32 sizeForAudioInitPool) {
MUTEX_LOCK(gAudioThread);
sound_alloc_pool_init(&gAudioInitPool, gAudioHeap, sizeForAudioInitPool);
sound_alloc_pool_init(&gAudioSessionPool, gAudioHeap + sizeForAudioInitPool, gAudioHeapSize - sizeForAudioInitPool);
MUTEX_UNLOCK(gAudioThread);
}
#ifdef VERSION_SH
@ -358,37 +374,51 @@ void sound_init_main_pools(s32 sizeForAudioInitPool) {
#endif
void session_pools_init(struct PoolSplit *a) {
MUTEX_LOCK(gAudioThread);
gAudioSessionPool.cur = gAudioSessionPool.start;
sound_alloc_pool_init(&gNotesAndBuffersPool, SOUND_ALLOC_FUNC(&gAudioSessionPool, a->wantSeq), a->wantSeq);
sound_alloc_pool_init(&gSeqAndBankPool, SOUND_ALLOC_FUNC(&gAudioSessionPool, a->wantCustom), a->wantCustom);
MUTEX_UNLOCK(gAudioThread);
}
void seq_and_bank_pool_init(struct PoolSplit2 *a) {
MUTEX_LOCK(gAudioThread);
gSeqAndBankPool.cur = gSeqAndBankPool.start;
sound_alloc_pool_init(&gPersistentCommonPool, SOUND_ALLOC_FUNC(&gSeqAndBankPool, a->wantPersistent), a->wantPersistent);
sound_alloc_pool_init(&gTemporaryCommonPool, SOUND_ALLOC_FUNC(&gSeqAndBankPool, a->wantTemporary), a->wantTemporary);
MUTEX_UNLOCK(gAudioThread);
}
void persistent_pools_init(struct PoolSplit *a) {
MUTEX_LOCK(gAudioThread);
gPersistentCommonPool.cur = gPersistentCommonPool.start;
sound_alloc_pool_init(&gSeqLoadedPool.persistent.pool, SOUND_ALLOC_FUNC(&gPersistentCommonPool, a->wantSeq), a->wantSeq);
sound_alloc_pool_init(&gBankLoadedPool.persistent.pool, SOUND_ALLOC_FUNC(&gPersistentCommonPool, a->wantBank), a->wantBank);
sound_alloc_pool_init(&gUnusedLoadedPool.persistent.pool, SOUND_ALLOC_FUNC(&gPersistentCommonPool, a->wantUnused),
a->wantUnused);
sound_alloc_pool_init(&gUnusedLoadedPool.persistent.pool, SOUND_ALLOC_FUNC(&gPersistentCommonPool, a->wantUnused), a->wantUnused);
persistent_pool_clear(&gSeqLoadedPool.persistent);
persistent_pool_clear(&gBankLoadedPool.persistent);
persistent_pool_clear(&gUnusedLoadedPool.persistent);
MUTEX_UNLOCK(gAudioThread);
}
void temporary_pools_init(struct PoolSplit *a) {
MUTEX_LOCK(gAudioThread);
gTemporaryCommonPool.cur = gTemporaryCommonPool.start;
sound_alloc_pool_init(&gSeqLoadedPool.temporary.pool, SOUND_ALLOC_FUNC(&gTemporaryCommonPool, a->wantSeq), a->wantSeq);
sound_alloc_pool_init(&gBankLoadedPool.temporary.pool, SOUND_ALLOC_FUNC(&gTemporaryCommonPool, a->wantBank), a->wantBank);
sound_alloc_pool_init(&gUnusedLoadedPool.temporary.pool, SOUND_ALLOC_FUNC(&gTemporaryCommonPool, a->wantUnused),
a->wantUnused);
sound_alloc_pool_init(&gUnusedLoadedPool.temporary.pool, SOUND_ALLOC_FUNC(&gTemporaryCommonPool, a->wantUnused), a->wantUnused);
temporary_pool_clear(&gSeqLoadedPool.temporary);
temporary_pool_clear(&gBankLoadedPool.temporary);
temporary_pool_clear(&gUnusedLoadedPool.temporary);
MUTEX_UNLOCK(gAudioThread);
}
#undef SOUND_ALLOC_FUNC

View file

@ -1369,10 +1369,13 @@ void reclaim_notes(void) {
struct Note *note;
s32 i;
s32 cond;
MUTEX_LOCK(gAudioThread);
for (i = 0; i < gMaxSimultaneousNotes; i++) {
note = &gNotes[i];
if (note->parentLayer != NO_LAYER) {
if (note == NULL) { continue; }
if (note->parentLayer != NULL && note->parentLayer != NO_LAYER) {
cond = FALSE;
if (!note->parentLayer->enabled && note->priority >= NOTE_PRIORITY_MIN) {
cond = TRUE;
@ -1400,6 +1403,8 @@ void reclaim_notes(void) {
}
}
}
MUTEX_UNLOCK(gAudioThread);
}
#endif

View file

@ -350,6 +350,9 @@ void sequence_player_disable_channels_extended(struct SequencePlayer* seqPlayer,
void sequence_player_disable_all_channels(struct SequencePlayer *seqPlayer) {
if (!seqPlayer) { return; }
MUTEX_LOCK(gAudioThread);
eu_stubbed_printf_0("SUBTRACK DIM\n");
for (u32 i = 0; i < CHANNELS_MAX; i++) {
struct SequenceChannel *seqChannel = seqPlayer->channels[i];
@ -368,11 +371,16 @@ void sequence_player_disable_all_channels(struct SequencePlayer *seqPlayer) {
seqPlayer->channels[i] = &gSequenceChannelNone;
}
}
MUTEX_UNLOCK(gAudioThread);
}
void sequence_channel_enable(struct SequencePlayer *seqPlayer, u8 channelIndex, void *script) {
if (!seqPlayer) { return; }
if (channelIndex >= CHANNELS_MAX) { return; }
MUTEX_LOCK(gAudioThread);
struct SequenceChannel *seqChannel = seqPlayer->channels[channelIndex];
s32 i;
if (IS_SEQUENCE_CHANNEL_VALID(seqChannel) == FALSE) {
@ -404,10 +412,14 @@ void sequence_channel_enable(struct SequencePlayer *seqPlayer, u8 channelIndex,
LOG_DEBUG("Enabled sequence channel %d with script entry of %p", channelIndex, script);
}
MUTEX_UNLOCK(gAudioThread);
}
void sequence_player_disable(struct SequencePlayer *seqPlayer) {
if (!seqPlayer) { return; }
MUTEX_LOCK(gAudioThread);
LOG_DEBUG("Disabling sequence player %p", seqPlayer);
sequence_player_disable_all_channels(seqPlayer);
@ -451,6 +463,8 @@ void sequence_player_disable(struct SequencePlayer *seqPlayer) {
gBankLoadedPool.temporary.nextSide = 0;
}
#endif
MUTEX_UNLOCK(gAudioThread);
}
/**
@ -1659,7 +1673,7 @@ void sequence_channel_process_script(struct SequenceChannel *seqChannel) {
}
seqPlayer = seqChannel->seqPlayer;
if (seqPlayer->muted && (seqChannel->muteBehavior & MUTE_BEHAVIOR_STOP_SCRIPT) != 0) {
if (seqPlayer == NULL || (seqPlayer->muted && (seqChannel->muteBehavior & MUTE_BEHAVIOR_STOP_SCRIPT) != 0)) {
return;
}

View file

@ -3,13 +3,13 @@
#include "buffers.h"
#include "audio/data.h"
ALIGNED8 u8 gDecompressionHeap[0xD000];
ALIGNED8 u8 gDecompressionHeap[0xD000] = { 0 };
#if defined(VERSION_EU)
ALIGNED16 u8 gAudioHeap[DOUBLE_SIZE_ON_64_BIT(AUDIO_HEAP_SIZE) - 0x3800];
ALIGNED16 u8 gAudioHeap[DOUBLE_SIZE_ON_64_BIT(AUDIO_HEAP_SIZE) - 0x3800] = { 0 };
#elif defined(VERSION_SH)
ALIGNED16 u8 gAudioHeap[DOUBLE_SIZE_ON_64_BIT(AUDIO_HEAP_SIZE) - 0x4800];
ALIGNED16 u8 gAudioHeap[DOUBLE_SIZE_ON_64_BIT(AUDIO_HEAP_SIZE) - 0x4800] = { 0 };
#else
ALIGNED16 u8 gAudioHeap[DOUBLE_SIZE_ON_64_BIT(AUDIO_HEAP_SIZE)];
ALIGNED16 u8 gAudioHeap[DOUBLE_SIZE_ON_64_BIT(AUDIO_HEAP_SIZE)] = { 0 };
#endif
ALIGNED8 u8 gIdleThreadStack[0x800];
@ -20,23 +20,23 @@ ALIGNED8 u8 gThread5Stack[0x2000];
ALIGNED8 u8 gThread6Stack[0x2000];
#endif
// 0x400 bytes
ALIGNED8 u8 gGfxSPTaskStack[SP_DRAM_STACK_SIZE8];
ALIGNED8 u8 gGfxSPTaskStack[SP_DRAM_STACK_SIZE8] = { 0 };
// 0xc00 bytes for f3dex, 0x900 otherwise
ALIGNED8 u8 gGfxSPTaskYieldBuffer[OS_YIELD_DATA_SIZE];
ALIGNED8 u8 gGfxSPTaskYieldBuffer[OS_YIELD_DATA_SIZE] = { 0 };
// 0x200 bytes
ALIGNED8 struct SaveBuffer gSaveBuffer;
ALIGNED8 struct SaveBuffer gSaveBuffer = { 0 };
// 0x190a0 bytes
struct GfxPool gGfxPools[GFX_NUM_POOLS];
struct GfxPool gGfxPools[GFX_NUM_POOLS] = { 0 };
// Yield buffer for audio, 0x400 bytes. Stubbed out post-JP since the audio
// task never yields.
#ifdef VERSION_JP
ALIGNED8 u8 gAudioSPTaskYieldBuffer[OS_YIELD_AUDIO_SIZE];
ALIGNED8 u8 gAudioSPTaskYieldBuffer[OS_YIELD_AUDIO_SIZE] = { 0 };
#endif
// Probably Thread 2 stack space. Unreferenced, and stubbed out with f3dex to
// avoid an overflowing .buffers segment.
#if !defined(F3DEX_GBI_SHARED) && !defined(VERSION_EU)
ALIGNED8 u8 gUnusedThread2Stack[0x1400];
ALIGNED8 u8 gUnusedThread2Stack[0x1400] = { 0 };
#endif

View file

@ -4,9 +4,9 @@
#ifdef VERSION_EU
// 0x17e00 bytes, aligned to a 0x200-byte boundary through sm64.ld. The alignment
// wastes 0x100 bytes of space.
u64 gGfxSPTaskOutputBuffer[0x2fc0];
u64 gGfxSPTaskOutputBuffer[0x2fc0] = { 0 };
#else
// 0x1f000 bytes, aligned to a 0x1000-byte boundary through sm64.ld. (This results
// in a bunch of unused space: ~0x100 in JP, ~0x300 in US.)
u64 gGfxSPTaskOutputBuffer[0x3e00];
u64 gGfxSPTaskOutputBuffer[0x3e00] = { 0 };
#endif

View file

@ -3,4 +3,4 @@
#include "config.h"
#include "zbuffer.h"
ALIGNED8 u16 gZBuffer[SCREEN_WIDTH * SCREEN_HEIGHT];
ALIGNED8 u16 gZBuffer[SCREEN_WIDTH * SCREEN_HEIGHT] = { 0 };

View file

@ -1,6 +1,7 @@
#include <ultra64.h>
#include "area.h"
#include "audio/data.h"
#include "audio/external.h"
#include "engine/graph_node.h"
#include "engine/math_util.h"
@ -87,6 +88,8 @@ void reset_volume(void) {
* Called from threads: thread5_game_loop
*/
void lower_background_noise(s32 a) {
MUTEX_LOCK(gAudioThread);
switch (a) {
case 1:
set_audio_muted(TRUE);
@ -96,12 +99,16 @@ void lower_background_noise(s32 a) {
break;
}
sVolumeLoweredState |= a;
MUTEX_UNLOCK(gAudioThread);
}
/**
* Called from threads: thread5_game_loop
*/
void raise_background_noise(s32 a) {
MUTEX_LOCK(gAudioThread);
switch (a) {
case 1:
set_audio_muted(FALSE);
@ -111,26 +118,36 @@ void raise_background_noise(s32 a) {
break;
}
sVolumeLoweredState &= ~a;
MUTEX_UNLOCK(gAudioThread);
}
/**
* Called from threads: thread5_game_loop
*/
void disable_background_sound(void) {
MUTEX_LOCK(gAudioThread);
if (!sBackgroundMusicDisabled) {
sBackgroundMusicDisabled = TRUE;
sound_banks_disable(SEQ_PLAYER_SFX, SOUND_BANKS_BACKGROUND);
}
MUTEX_UNLOCK(gAudioThread);
}
/**
* Called from threads: thread5_game_loop
*/
void enable_background_sound(void) {
MUTEX_LOCK(gAudioThread);
if (sBackgroundMusicDisabled) {
sBackgroundMusicDisabled = FALSE;
sound_banks_enable(SEQ_PLAYER_SFX, SOUND_BANKS_BACKGROUND);
}
MUTEX_UNLOCK(gAudioThread);
}
/**
@ -139,9 +156,13 @@ void enable_background_sound(void) {
* Called from threads: thread5_game_loop
*/
void set_sound_mode(u16 soundMode) {
MUTEX_LOCK(gAudioThread);
if (soundMode < 3) {
audio_set_sound_mode(sSoundMenuModeToSoundMode[soundMode]);
}
MUTEX_UNLOCK(gAudioThread);
}
/**
@ -241,20 +262,28 @@ void set_background_music(u16 a, u16 seqArgs, s16 fadeTimer) {
* Called from threads: thread3_main, thread5_game_loop
*/
void fadeout_music(s16 fadeOutTime) {
MUTEX_LOCK(gAudioThread);
set_audio_fadeout(fadeOutTime);
sCurrentMusic = MUSIC_NONE;
sCurrentShellMusic = MUSIC_NONE;
sCurrentCapMusic = MUSIC_NONE;
MUTEX_UNLOCK(gAudioThread);
}
/**
* Called from threads: thread5_game_loop
*/
void fadeout_level_music(s16 fadeTimer) {
MUTEX_LOCK(gAudioThread);
seq_player_fade_out(SEQ_PLAYER_LEVEL, fadeTimer);
sCurrentMusic = MUSIC_NONE;
sCurrentShellMusic = MUSIC_NONE;
sCurrentCapMusic = MUSIC_NONE;
MUTEX_UNLOCK(gAudioThread);
}
/**

View file

@ -2,6 +2,8 @@
#ifdef LOADING_SCREEN_SUPPORTED
#include <assert.h>
#include "djui/djui.h"
#include "pc/djui/djui_unicode.h"
@ -24,10 +26,7 @@ struct LoadingScreen {
static struct LoadingScreen* sLoading = NULL;
pthread_t gLoadingThreadId;
pthread_mutex_t gLoadingThreadMutex = PTHREAD_MUTEX_INITIALIZER;
bool gIsThreaded = false;
struct ThreadHandle gLoadingThread = { 0 };
void loading_screen_set_segment_text(const char* text) {
snprintf(gCurrLoadingSegment.str, 256, text);
@ -46,7 +45,7 @@ static void loading_screen_produce_one_frame(void) {
}
static bool loading_screen_on_render(struct DjuiBase* base) {
if (gIsThreaded) { pthread_mutex_lock(&gLoadingThreadMutex); }
MUTEX_LOCK(gLoadingThread);
u32 windowWidth, windowHeight;
WAPI.get_dimensions(&windowWidth, &windowHeight);
@ -90,7 +89,7 @@ static bool loading_screen_on_render(struct DjuiBase* base) {
djui_base_compute(base);
if (gIsThreaded) { pthread_mutex_unlock(&gLoadingThreadMutex); }
MUTEX_UNLOCK(gLoadingThread);
return true;
}
@ -183,7 +182,8 @@ void render_loading_screen(void) {
WAPI.main_loop(loading_screen_produce_one_frame);
}
pthread_join(gLoadingThreadId, NULL);
int err = join_thread(&gLoadingThread);
assert(err == 0);
}
void render_rom_setup_screen(void) {
@ -196,4 +196,4 @@ void render_rom_setup_screen(void) {
}
}
#endif
#endif

View file

@ -7,9 +7,7 @@
#ifdef LOADING_SCREEN_SUPPORTED
#include <pthread.h>
#include "cliopts.h"
#include "src/pc/thread.h"
#include "djui/djui_hud_utils.h"
struct LoadingSegment {
@ -20,16 +18,13 @@ struct LoadingSegment {
extern struct LoadingSegment gCurrLoadingSegment;
#define LOADING_SCREEN_MUTEX(...) \
if (!gCLIOpts.hideLoadingScreen && gIsThreaded) { \
pthread_mutex_lock(&gLoadingThreadMutex); \
if (!gCLIOpts.hideLoadingScreen && gLoadingThread.state != INVALID) { \
pthread_mutex_lock(&gLoadingThread.mutex); \
__VA_ARGS__; \
pthread_mutex_unlock(&gLoadingThreadMutex); \
pthread_mutex_unlock(&gLoadingThread.mutex); \
}
extern pthread_t gLoadingThreadId;
extern pthread_mutex_t gLoadingThreadMutex;
extern bool gIsThreaded;
extern struct ThreadHandle gLoadingThread;
void loading_screen_set_segment_text(const char* text);
void loading_screen_reset_progress_bar(void);

View file

@ -10,6 +10,7 @@
#include "pc/lua/smlua.h"
#include "pc/lua/utils/smlua_text_utils.h"
#include "game/memory.h"
#include "audio/data.h"
#include "audio/external.h"
#include "network/network.h"
@ -25,6 +26,7 @@
#include "loading.h"
#include "cliopts.h"
#include "configfile.h"
#include "thread.h"
#include "controller/controller_api.h"
#include "controller/controller_keyboard.h"
#include "fs/fs.h"
@ -230,6 +232,10 @@ void produce_interpolation_frames_and_delay(void) {
gRenderingInterpolated = false;
}
// It's just better to have this off the stack, Because the size isn't small.
// It also may help static analysis and bug catching.
static s16 sAudioBuffer[SAMPLES_HIGH * 2 * 2] = { 0 };
inline static void buffer_audio(void) {
bool shouldMute = configMuteFocusLoss && !WAPI.has_focus();
const f32 masterMod = (f32)configMasterVolume / 127.0f * (f32)gLuaVolumeMaster / 127.0f;
@ -239,11 +245,37 @@ inline static void buffer_audio(void) {
int samplesLeft = audio_api->buffered();
u32 numAudioSamples = samplesLeft < audio_api->get_desired_buffered() ? SAMPLES_HIGH : SAMPLES_LOW;
s16 audioBuffer[SAMPLES_HIGH * 2 * 2];
for (s32 i = 0; i < 2; i++) {
create_next_audio_buffer(audioBuffer + i * (numAudioSamples * 2), numAudioSamples);
create_next_audio_buffer(sAudioBuffer + i * (numAudioSamples * 2), numAudioSamples);
}
audio_api->play((u8 *)audioBuffer, 2 * numAudioSamples * 4);
audio_api->play((u8 *)sAudioBuffer, 2 * numAudioSamples * 4);
}
void *audio_thread(UNUSED void *arg) {
// As long as we have an audio api and that we're threaded, Loop.
while (audio_api) {
f64 curTime = clock_elapsed_f64();
// Buffer the audio.
lock_mutex(&gAudioThread);
buffer_audio();
unlock_mutex(&gAudioThread);
// Delay till the next frame for smooth audio at the correct speed.
// delay
f64 targetDelta = 1.0 / (f64)FRAMERATE;
f64 now = clock_elapsed_f64();
f64 actualDelta = now - curTime;
if (actualDelta < targetDelta) {
f64 delay = ((targetDelta - actualDelta) * 1000.0);
WAPI.delay((u32)delay);
}
}
// Exit the thread if our loop breaks.
exit_thread();
return NULL;
}
void produce_one_frame(void) {
@ -254,8 +286,11 @@ void produce_one_frame(void) {
CTX_EXTENT(CTX_GAME_LOOP, game_loop_one_iteration);
CTX_EXTENT(CTX_SMLUA, smlua_update);
CTX_EXTENT(CTX_AUDIO, buffer_audio);
// If we aren't threaded
if (gAudioThread.state == INVALID) {
CTX_EXTENT(CTX_AUDIO, buffer_audio);
}
CTX_EXTENT(CTX_RENDER, produce_interpolation_frames_and_delay);
}
@ -408,14 +443,12 @@ int main(int argc, char *argv[]) {
// start the thread for setting up the game
#ifdef LOADING_SCREEN_SUPPORTED
bool threadSuccess = false;
if (!gCLIOpts.hideLoadingScreen && pthread_mutex_init(&gLoadingThreadMutex, NULL) == 0) {
gIsThreaded = true;
if (pthread_create(&gLoadingThreadId, NULL, main_game_init, NULL) == 0) {
if (!gCLIOpts.hideLoadingScreen) {
if (init_thread_handle(&gLoadingThread, main_game_init, NULL, NULL, 0) == 0) {
render_loading_screen(); // render the loading screen while the game is setup
threadSuccess = true;
destroy_mutex(&gLoadingThread);
}
gIsThreaded = false;
pthread_mutex_destroy(&gLoadingThreadMutex);
}
if (!threadSuccess)
#endif
@ -431,6 +464,9 @@ int main(int argc, char *argv[]) {
if (!audio_api && audio_sdl.init()) { audio_api = &audio_sdl; }
#endif
if (!audio_api) { audio_api = &audio_null; }
// Initialize the audio thread if possible.
init_thread_handle(&gAudioThread, audio_thread, NULL, NULL, 0);
#ifdef LOADING_SCREEN_SUPPORTED
loading_screen_reset();

132
src/pc/thread.c Normal file
View file

@ -0,0 +1,132 @@
#include "thread.h"
#include <assert.h>
#include <string.h>
int init_thread_handle(struct ThreadHandle *handle, void *(*entry)(void *), void *arg, void *sp, size_t sp_size) {
int err1 = init_mutex(handle);
int err2 = init_thread(handle, entry, arg, sp, sp_size);
return (err1 != 0 || err2 != 0);
}
void free_thread_handle(struct ThreadHandle *handle) {
assert(handle != NULL);
//int err = stop_thread(handle);
//assert(err == 0);
int err = destroy_mutex(handle);
assert(err == 0);
// Reset the memory of the thread handle, We no longer need the thread or mutex.
memset((void *)handle, 0, sizeof(struct ThreadHandle));
}
// Optimally just call init_thread_handle instead.
int init_thread(struct ThreadHandle *handle, void *(*entry)(void *), void *arg, void *sp, size_t sp_size) {
assert(handle != NULL);
// Setup our thread and create it.
pthread_attr_t thattr = { 0 };
// Initialize default attributes for a new thread.
int err = pthread_attr_init(&thattr);
assert(err == 0);
// By default, We want the thread to be joinable.
err = pthread_attr_setdetachstate(&thattr, PTHREAD_CREATE_JOINABLE);
assert(err == 0);
// Set the stack if we have one specified, Otherwise we don't care.
// In truth, This honestly shouldn't be used on PC. But if somebody wants
// a really managed stack? This is possible.
if (sp != NULL && sp_size > 0) {
err = pthread_attr_setstack(&thattr, sp, sp_size);
assert(err == 0);
}
// Create our thread.
int ret = pthread_create(&handle->thread, &thattr, entry, arg);
// Destroy the thread attributes, They are no longer needed
err = pthread_attr_destroy(&thattr);
assert(err == 0);
handle->state = RUNNING;
return ret;
}
int join_thread(struct ThreadHandle *handle) {
assert(handle != NULL);
// Join the thread and wait for it to finish.
return pthread_join(handle->thread, NULL);
}
int detach_thread(struct ThreadHandle *handle) {
assert(handle != NULL);
// Detach the thread, it will no longer be joinable afterwards.
return pthread_detach(handle->thread);
}
// Call from inside the thread you wish to
// terminate.
void exit_thread() {
pthread_exit(NULL);
}
int stop_thread(struct ThreadHandle *handle) {
assert(handle != NULL);
handle->state = STOPPED;
// Stop and or cancel the execution of the thread in question.
return pthread_cancel(handle->thread);
}
// Optimally just call init_thread_handle instead.
int init_mutex(struct ThreadHandle *handle) {
assert(handle != NULL);
pthread_mutexattr_t mtattr;
int err = pthread_mutexattr_init(&mtattr);
assert(err == 0);
err = pthread_mutexattr_settype(&mtattr, PTHREAD_MUTEX_ERRORCHECK);
assert(err == 0);
int ret = pthread_mutex_init(&handle->mutex, &mtattr);
err = pthread_mutexattr_destroy(&mtattr);
assert(err == 0);
return ret;
}
int destroy_mutex(struct ThreadHandle *handle) {
assert(handle != NULL);
return pthread_mutex_destroy(&handle->mutex);
}
int lock_mutex(struct ThreadHandle *handle) {
assert(handle != NULL);
return pthread_mutex_lock(&handle->mutex);
}
int trylock_mutex(struct ThreadHandle *handle) {
assert(handle != NULL);
return pthread_mutex_trylock(&handle->mutex);
}
int unlock_mutex(struct ThreadHandle *handle) {
assert(handle != NULL);
return pthread_mutex_unlock(&handle->mutex);
}

45
src/pc/thread.h Normal file
View file

@ -0,0 +1,45 @@
#ifndef THREADING_H
#define THREADING_H
#include <pthread.h>
#include "cliopts.h"
#include "types.h"
// Macros
#define MUTEX_LOCK(handle) if (handle.state != INVALID) { lock_mutex(&handle); }
#define MUTEX_UNLOCK(handle) if (handle.state != INVALID) { unlock_mutex(&handle); }
// Types
enum ThreadState {
INVALID = 0,
STOPPED = 1,
RUNNING = 2
};
struct ThreadHandle {
pthread_t thread;
pthread_mutex_t mutex;
enum ThreadState state;
};
// Functions
//// Thread Handle
int init_thread_handle(struct ThreadHandle *handle, void *(*entry)(void *), void *arg, void *sp, size_t sp_size);
void cleanup_thread_handle(struct ThreadHandle *handle);
//// Thread
int init_thread(struct ThreadHandle *handle, void *(*entry)(void *), void *arg, void *sp, size_t sp_size);
int join_thread(struct ThreadHandle *handle);
int detach_thread(struct ThreadHandle *handle);
void exit_thread();
int stop_thread(struct ThreadHandle *handle);
//// Mutex
int init_mutex(struct ThreadHandle *handle);
int destroy_mutex(struct ThreadHandle *handle);
int lock_mutex(struct ThreadHandle *handle);
int trylock_mutex(struct ThreadHandle *handle);
int unlock_mutex(struct ThreadHandle *handle);
#endif // THREADING_H