diff --git a/src/audio/data.c b/src/audio/data.c index b9b0f2a72..1b134a3ca 100644 --- a/src/audio/data.c +++ b/src/audio/data.c @@ -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 }, diff --git a/src/audio/data.h b/src/audio/data.h index c66b750a2..f0600048a 100644 --- a/src/audio/data.h +++ b/src/audio/data.h @@ -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[]; diff --git a/src/audio/effects.c b/src/audio/effects.c index 45b67ce7f..659a8190a 100644 --- a/src/audio/effects.c +++ b/src/audio/effects.c @@ -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; diff --git a/src/audio/external.c b/src/audio/external.c index 3cd1f07b1..3ddc5c23e 100644 --- a/src/audio/external.c +++ b/src/audio/external.c @@ -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); } /** diff --git a/src/audio/heap.c b/src/audio/heap.c index bff3517f1..8957625e4 100644 --- a/src/audio/heap.c +++ b/src/audio/heap.c @@ -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 diff --git a/src/audio/playback.c b/src/audio/playback.c index a20e39b2d..945731bdb 100644 --- a/src/audio/playback.c +++ b/src/audio/playback.c @@ -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 diff --git a/src/audio/seqplayer.c b/src/audio/seqplayer.c index 64d161f64..02cbf4bc9 100644 --- a/src/audio/seqplayer.c +++ b/src/audio/seqplayer.c @@ -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; } diff --git a/src/buffers/buffers.c b/src/buffers/buffers.c index 77bcc36d2..063aa7bdd 100644 --- a/src/buffers/buffers.c +++ b/src/buffers/buffers.c @@ -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 diff --git a/src/buffers/gfx_output_buffer.c b/src/buffers/gfx_output_buffer.c index d948d420d..c33aa143f 100644 --- a/src/buffers/gfx_output_buffer.c +++ b/src/buffers/gfx_output_buffer.c @@ -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 diff --git a/src/buffers/zbuffer.c b/src/buffers/zbuffer.c index 7c0ab6ec8..cccba7811 100644 --- a/src/buffers/zbuffer.c +++ b/src/buffers/zbuffer.c @@ -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 }; diff --git a/src/game/sound_init.c b/src/game/sound_init.c index b927e6c52..abaef0c65 100644 --- a/src/game/sound_init.c +++ b/src/game/sound_init.c @@ -1,6 +1,7 @@ #include #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); } /** diff --git a/src/pc/loading.c b/src/pc/loading.c index 82b518ce6..2cf6f61c6 100644 --- a/src/pc/loading.c +++ b/src/pc/loading.c @@ -2,6 +2,8 @@ #ifdef LOADING_SCREEN_SUPPORTED +#include + #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 \ No newline at end of file diff --git a/src/pc/loading.h b/src/pc/loading.h index 11fdb0b5a..45e16f0cc 100644 --- a/src/pc/loading.h +++ b/src/pc/loading.h @@ -7,9 +7,7 @@ #ifdef LOADING_SCREEN_SUPPORTED -#include - -#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); diff --git a/src/pc/pc_main.c b/src/pc/pc_main.c index f3e0809dc..22a47c867 100644 --- a/src/pc/pc_main.c +++ b/src/pc/pc_main.c @@ -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(); diff --git a/src/pc/thread.c b/src/pc/thread.c new file mode 100644 index 000000000..6827c3da9 --- /dev/null +++ b/src/pc/thread.c @@ -0,0 +1,132 @@ +#include "thread.h" + +#include +#include + +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); +} \ No newline at end of file diff --git a/src/pc/thread.h b/src/pc/thread.h new file mode 100644 index 000000000..717f6945a --- /dev/null +++ b/src/pc/thread.h @@ -0,0 +1,45 @@ +#ifndef THREADING_H +#define THREADING_H + +#include + +#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