Files
jak-project/game/overlord/jak3/vag.cpp
T
Hat Kid 8a73c3e997 989snd/overlord: implement PLUGIN_MESSAGE grain and STRV plugin system (#4319)
This implements the functionality for the `PLUGIN_MESSAGE` grain and the
989snd VAG plugin system, allowing sounds to queue up VAG streams.

Closes #2582
2026-06-30 21:21:34 +02:00

1059 lines
28 KiB
C++

#include "vag.h"
#include <cstring>
#include "common/log/log.h"
#include "common/util/Assert.h"
#include "game/overlord/jak3/basefile.h"
#include "game/overlord/jak3/iso.h"
#include "game/overlord/jak3/iso_queue.h"
#include "game/overlord/jak3/spustreams.h"
#include "game/overlord/jak3/srpc.h"
#include "game/overlord/jak3/ssound.h"
#include "game/overlord/jak3/streamlist.h"
#include "game/sce/iop.h"
#include "game/sound/sdshim.h"
#include "game/sound/sndshim.h"
#define VOICE_BIT(voice) (1 << ((voice) >> 1))
namespace jak3 {
using namespace iop;
bool g_bExtPause = false;
bool g_bExtResume = false;
ISO_VAGCommand g_aVagCmds[6];
int g_anMasterVolume[32];
bool voice_key_flags[0x30];
u32 voice_key_times[0x30];
u32 g_nTimeOfLastVoiceKey = 0;
bool g_bRecentlyKeyedVoice = false;
u32 g_nActiveVagStreams = 0;
u32 g_nPlaybackMode = 2;
struct VagCmdPriListEntry {
ISO_VAGCommand* cmds[6];
};
VagCmdPriListEntry g_aapVagCmdsPriList[11];
u32 g_anVagCmdPriCounter[11];
void jak3_overlord_init_globals_vag() {
g_bExtPause = false;
g_bExtResume = false;
for (auto& c : g_aVagCmds) {
c = {};
}
for (auto& x : g_anMasterVolume) {
x = 0x400;
}
g_nActiveVagStreams = 0;
g_nPlaybackMode = 2;
}
void ISO_VAGCommand::set_all_flags_zero() {
flags.bit0 = 0;
flags.saw_chunks1 = 0;
flags.paused = 0;
flags.bit3 = 0;
flags.running = 0;
flags.clocks_set = 0;
flags.file_disappeared = 0;
flags.scanned = 0;
flags.bit8 = 0;
flags.stop = 0;
flags.art = 0;
flags.stereo_secondary = 0;
flags.bit12 = 0;
flags.bit13 = 0;
flags.bit14 = 0;
flags.bit15 = 0;
flags.bit16 = 0;
flags.bit17 = 0;
flags.dma_complete_even_chunk_count = 0;
flags.dma_complete_odd_chunk_count = 0;
flags.bit20 = 0;
flags.bit21 = 0;
flags.bit22 = 0;
flags.nostart = 0;
flags.movie = 0;
flags.bit25 = 0;
}
u32 ISO_VAGCommand::pack_flags() {
u32 ret = 0;
u8* ptr = &flags.bit0;
u32 mask = 1;
for (; ptr <= &flags.bit25; ptr++) {
if (*ptr) {
ret |= mask;
}
mask <<= 1;
}
return ret;
}
void InitVAGCmd(ISO_VAGCommand* cmd, int paused) {
set_active_a(cmd, 0);
set_active_b(cmd, 0);
set_active_c(cmd, 0);
cmd->set_all_flags_zero();
cmd->unk_gvsp_state2 = 0;
cmd->lfo_callback = nullptr;
if (paused == 0) {
cmd->flags.paused = 0;
} else {
cmd->flags.paused = 1;
}
cmd->safe_to_modify_dma = 1;
cmd->unk_spu_mem_offset = 0x4000;
cmd->callback = NullCallback;
cmd->dma_chan = -1;
cmd->fo_min = 5;
cmd->fo_max = 0x1e;
cmd->unk_gvsp_len = 0;
cmd->position_for_ee = 0;
cmd->unk_gvsp_cntr = 0;
cmd->clocka = 0;
cmd->clockb = 0;
cmd->clockc = 0;
cmd->clockd = 0;
cmd->flags.clocks_set = 0;
cmd->flags.file_disappeared = 0;
cmd->num_isobuffered_chunks = 0;
cmd->xfer_size = 0;
cmd->vag_file_rate = 0;
cmd->error = 0;
cmd->unk_gvsp_flag = 0;
cmd->m_pBaseFile = nullptr;
cmd->stereo_sibling = nullptr;
cmd->dma_iop_mem_ptr = nullptr;
cmd->pitch_cmd = 0;
cmd->updated_trans = 0;
cmd->trans[0] = 0;
cmd->trans[1] = 0;
cmd->trans[2] = 0;
cmd->fo_curve = GetFalloffCurve(1);
cmd->play_group = 2;
set_active_a(cmd, 1);
set_active_b(cmd, 1);
}
void InitVagCmds() {
auto now = GetSystemTimeLow();
for (auto& x : voice_key_flags) {
x = false;
}
for (auto& x : voice_key_times) {
x = now;
}
g_nTimeOfLastVoiceKey = 0;
g_bRecentlyKeyedVoice = false;
for (int i = 0; i < 6; i++) {
auto* cmd = &g_aVagCmds[i];
InitVAGCmd(cmd, 1);
cmd->info_idx = i;
cmd->stream_sram = g_auStrmSRAM[i];
cmd->trap_sram = g_auTrapSRAM[i];
cmd->voice = g_anStreamVoice[i];
cmd->music_flag = i >= 4;
cmd->file_def = nullptr;
cmd->vag_file_def = nullptr;
cmd->vag_dir_entry = nullptr;
cmd->flags.saw_chunks1 = 0;
cmd->play_volume = 0;
cmd->pitch_cmd = 0;
cmd->id = 0;
cmd->plugin_id = 0;
cmd->maybe_sound_handler = 0;
cmd->oog = 0;
cmd->dolby_pan_angle = 0;
cmd->priority_pq = 0;
cmd->art_flag = 0;
cmd->movie_flag = 0;
}
for (auto& level : g_aapVagCmdsPriList) {
for (auto& cmd : level.cmds) {
cmd = nullptr;
}
}
for (auto& x : g_anVagCmdPriCounter) {
x = 0;
}
// oops - wrong value from jak 2 maybe?
g_anVagCmdPriCounter[0] = 4;
}
void RemoveVagCmd(ISO_VAGCommand* cmd) {
ASSERT(cmd);
ASSERT(!cmd->music_flag);
ASSERT(cmd->info_idx < 4);
ASSERT(cmd->priority_pq >= 0 && cmd->priority_pq <= 10);
g_aapVagCmdsPriList[cmd->priority_pq].cmds[cmd->info_idx] = nullptr;
if (g_anVagCmdPriCounter[cmd->priority_pq]) {
g_anVagCmdPriCounter[cmd->priority_pq]--;
} else {
ASSERT_NOT_REACHED();
}
g_anVagCmdPriCounter[0]++;
}
ISO_VAGCommand* FindFreeVagCmd() {
for (int i = 0; i < 4; i++) {
if (g_aVagCmds[i].id == 0) {
return &g_aVagCmds[i];
}
}
return nullptr;
}
void SetNewVagCmdPri(ISO_VAGCommand* cmd, int pri) {
ASSERT(cmd);
ASSERT(!cmd->music_flag);
ASSERT(cmd->info_idx < 4);
ASSERT(pri >= 0 && pri <= 10);
auto old_pri = cmd->priority_pq;
g_aapVagCmdsPriList[old_pri].cmds[cmd->info_idx] = nullptr;
if (0 < g_anVagCmdPriCounter[old_pri]) {
g_anVagCmdPriCounter[old_pri]--;
} else {
ASSERT_NOT_REACHED();
}
g_anVagCmdPriCounter[pri]++;
cmd->priority_pq = pri;
g_aapVagCmdsPriList[pri].cmds[cmd->info_idx] = cmd;
}
int HowManyBelowThisPriority(int max_pri) {
int count = 0;
for (int i = 0; i < max_pri; i++) {
count += g_anVagCmdPriCounter[i];
}
return count;
}
/*!
* The "smart"ness of this function is less than advertised.
*/
ISO_VAGCommand* SmartAllocMusicVagCommand(const ISO_VAGCommand* user_command, int flag) {
ASSERT(user_command);
int idx = flag ? 5 : 4;
auto* cmd = &g_aVagCmds[idx];
if (cmd->id == 0) {
g_nActiveMusicStreams++;
}
return cmd;
}
ISO_VAGCommand* SmartAllocVagCmd(ISO_VAGCommand* user_cmd) {
ASSERT(user_cmd);
ISO_VAGCommand* cmd = FindFreeVagCmd();
if (!cmd && (cmd = FindNotQueuedVagCmd(), !cmd)) {
u32 pri = 0;
if ((user_cmd->priority_pq != 0) && (pri = 0, user_cmd->priority_pq != 0)) {
do {
u32 cmd_idx = 0;
do {
auto* pIVar1 = g_aapVagCmdsPriList[pri].cmds[cmd_idx];
if (pIVar1 && (cmd = pIVar1, pIVar1->flags.stereo_secondary == 0)) {
pri = user_cmd->priority_pq;
cmd_idx = 4;
cmd = pIVar1;
}
cmd_idx = cmd_idx + 1;
} while (cmd_idx < 4);
pri = pri + 1;
} while (pri < (u32)user_cmd->priority_pq);
}
if (!cmd) {
return nullptr;
}
}
g_nActiveVagStreams = g_nActiveVagStreams + 1;
return cmd;
}
void FreeVagCmd(ISO_VAGCommand* cmd) {
set_active_a(cmd, 0);
set_active_b(cmd, 0);
set_active_c(cmd, 0);
cmd->set_all_flags_zero();
cmd->flags.paused = 1;
cmd->flags.saw_chunks1 = 0;
cmd->flags.scanned = 0;
cmd->clocka = 0;
cmd->clockb = 0;
cmd->clockc = 0;
cmd->clockd = 0;
SetVagStreamName(cmd, 0);
cmd->safe_to_modify_dma = 1;
cmd->unk_spu_mem_offset = 0x4000;
cmd->callback = NullCallback;
cmd->dma_chan = -1;
cmd->lfo_callback = 0;
cmd->pitch1 = 0;
cmd->pitch1_file = 0;
cmd->file_def = nullptr;
cmd->vag_file_def = nullptr;
cmd->vag_dir_entry = nullptr;
cmd->unk_gvsp_len = 0;
cmd->position_for_ee = 0;
cmd->unk_gvsp_cntr = 0;
cmd->name[0] = 0;
cmd->num_isobuffered_chunks = 0;
cmd->xfer_size = 0;
cmd->vag_file_rate = 0;
cmd->error = 0;
cmd->unk_gvsp_flag = 0;
cmd->play_volume = 0;
cmd->pitch_cmd = 0;
cmd->id = 0;
cmd->plugin_id = 0;
cmd->maybe_sound_handler = 0;
cmd->priority_pq = 0;
cmd->art_flag = 0;
cmd->movie_flag = 0;
cmd->updated_trans = 0;
cmd->m_pBaseFile = nullptr;
cmd->dma_iop_mem_ptr = nullptr;
cmd->unk_gvsp_state2 = 0;
if (!cmd->music_flag) {
if (0 < g_nActiveVagStreams) {
g_nActiveVagStreams = g_nActiveVagStreams + -1;
} else {
ASSERT_NOT_REACHED();
}
} else {
if (0 < g_nActiveMusicStreams) {
g_nActiveMusicStreams = g_nActiveMusicStreams + -1;
} else {
ASSERT_NOT_REACHED();
}
}
// CpuResumeIntr(local_10[0]);
}
void SetVagStreamsNotScanned() {
for (int i = 0; i < 4; i++) {
g_aVagCmds[i].flags.scanned = false;
}
}
void SetVagStreamsNoStart(int value) {
for (int i = 0; i < 4; i++) {
g_aVagCmds[i].flags.nostart = value;
}
}
ISO_VAGCommand* FindNotQueuedVagCmd() {
for (int i = 0; i < 4; i++) {
auto* cmd = &g_aVagCmds[i];
if (!cmd->flags.scanned && !cmd->flags.stereo_secondary && !cmd->flags.running) {
return cmd;
}
}
return nullptr;
}
int AnyVagRunning() {
int count = 0;
for (int i = 0; i < 4; i++) {
if (g_aVagCmds[i].flags.running) {
count++;
}
}
return count;
}
ISO_VAGCommand* FindVagStreamPluginId(int id) {
if (id == 0) {
return nullptr;
}
for (int i = 0; i < 4; i++) {
auto* cmd = &g_aVagCmds[i];
if (cmd->plugin_id == id) {
return cmd;
}
}
return nullptr;
}
ISO_VAGCommand* FindVagStreamId(int id) {
if (id == 0) {
return nullptr;
}
for (int i = 0; i < 4; i++) {
auto* cmd = &g_aVagCmds[i];
if (cmd->id == id) {
return cmd;
}
}
return nullptr;
}
ISO_VAGCommand* FindVagStreamName(const char* name) {
for (int i = 0; i < 4; i++) {
auto* cmd = &g_aVagCmds[i];
if (strcmp(cmd->name, name) == 0) {
return cmd;
}
}
return nullptr;
}
ISO_VAGCommand* FindThisVagStream(const char* name, int id) {
for (int i = 0; i < 4; i++) {
auto* cmd = &g_aVagCmds[i];
if (strcmp(cmd->name, name) == 0 && cmd->id == id) {
return cmd;
}
}
return nullptr;
}
ISO_VAGCommand* FindMusicStreamName(const char* name) {
for (int i = 4; i < 6; i++) {
auto* cmd = &g_aVagCmds[i];
if (strcmp(cmd->name, name) == 0) {
return cmd;
}
}
return nullptr;
}
ISO_VAGCommand* FindThisMusicStream(const char* name, int id) {
for (int i = 4; i < 6; i++) {
auto* cmd = &g_aVagCmds[i];
if (strcmp(cmd->name, name) == 0 && cmd->id == id) {
return cmd;
}
}
return nullptr;
}
/*!
* Immediately stop the audio playback of a command, without the ability to resume.
* This will key off voices.
*/
void StopVAG(ISO_VAGCommand* cmd) {
// mark buffer inactive, so we don't stream more data
set_active_a(cmd, 0);
set_active_b(cmd, 0);
// safely pause playback
PauseVAG(cmd);
// deal with SPU voices:
if (cmd->flags.clocks_set) {
// do individual voice blocks
u32 voice_mask = VOICE_BIT(cmd->voice);
BlockUntilVoiceSafe(cmd->voice, 0x900);
auto* sibling = cmd->stereo_sibling;
if (sibling) {
voice_mask |= VOICE_BIT(sibling->voice);
BlockUntilVoiceSafe(sibling->voice, 0x900);
}
BlockUntilAllVoicesSafe();
sceSdSetSwitch((cmd->voice & 1) | SD_S_KOFF, voice_mask);
auto now = GetSystemTimeLow();
MarkVoiceKeyedOnOff(cmd->voice, now);
if (sibling) {
MarkVoiceKeyedOnOff(sibling->voice, now);
}
}
// clear out command
cmd->set_all_flags_zero();
cmd->callback = NullCallback;
cmd->clockd = 0;
cmd->play_volume = 0;
cmd->pitch_cmd = 0;
cmd->id = 0;
cmd->plugin_id = 0;
cmd->maybe_sound_handler = 0;
cmd->lfo_callback = 0;
cmd->pitch1 = 0;
cmd->pitch1_file = 0;
cmd->clocka = 0;
cmd->clockb = 0;
cmd->clockc = 0;
}
/*!
* Stop a VAG, also remove it from lists of active commands.
*/
void TerminateVAG(ISO_VAGCommand* in_cmd) {
// ISO_VAGCommand *sibling;
// VagStreamData vsd;
// undefined auStack64 [36];
// int local_1c;
// int local_18;
// bool not_music;
ASSERT(in_cmd);
bool not_music = !in_cmd->music_flag;
VagStreamData vsd;
strncpy(vsd.name, in_cmd->name, 0x30);
vsd.id = in_cmd->id;
StopVAG(in_cmd);
ReleaseMessage(in_cmd);
if (not_music) {
RemoveVagCmd(in_cmd);
}
FreeVagCmd(in_cmd);
auto* sibling = in_cmd->stereo_sibling;
if (sibling) {
sibling->flags.scanned = 0;
if (not_music) {
RemoveVagCmd(sibling);
}
FreeVagCmd(sibling);
}
if (not_music) {
if (in_cmd->maybe_sound_handler != 0) {
RemoveVagStreamFromList(&vsd, &g_PluginStreamsList);
// TODO LFO support
// RemoveLfoStreamFromList(auStack64, &g_LfoStreamsList);
}
RemoveVagStreamFromList(&vsd, &g_EEPlayList);
}
}
/*!
* Pause a vag stream by setting the volume to 0, the pitch to 0, then keying off the voice.
*/
void PauseVAG(ISO_VAGCommand* cmd) {
if (cmd->flags.paused == 0) {
// CpuSuspendIntr(local_18);
if (cmd->flags.stereo_secondary == 0) {
auto* sibling = cmd->stereo_sibling;
if (!sibling) {
if (cmd->flags.saw_chunks1) {
// this means we enabled volume before, so disable it.
static_assert(SD_VP_VOLL == 0);
static_assert(SD_VP_VOLR == 0x100);
static_assert(SD_VP_PITCH == 0x200);
sceSdSetParam(cmd->voice | SD_VP_VOLL, 0);
sceSdSetParam(cmd->voice | SD_VP_VOLR, 0);
}
// in either case, set pitch to 0 to stop playback.
sceSdSetParam(cmd->voice | SD_VP_PITCH, 0);
auto voice = cmd->voice;
BlockUntilVoiceSafe(voice, 0x900);
BlockUntilAllVoicesSafe();
// key off
sceSdSetSwitch((cmd->voice & 1) | SD_S_KOFF, VOICE_BIT(voice));
MarkVoiceKeyedOnOff(cmd->voice, GetSystemTimeLow());
// remember where to resume from
if (cmd->flags.clocks_set == 0) {
cmd->current_spu_address = 0;
} else {
cmd->current_spu_address = GetSpuRamAddress(cmd);
}
cmd->flags.paused = 1;
} else {
if (cmd->flags.saw_chunks1 != 0) {
sceSdSetParam(cmd->voice | SD_VP_VOLL, 0);
sceSdSetParam(cmd->voice | SD_VP_VOLR, 0);
sceSdSetParam(sibling->voice | SD_VP_VOLL, 0);
sceSdSetParam(sibling->voice | SD_VP_VOLR, 0);
}
sceSdSetParam(sibling->voice | SD_VP_PITCH, 0);
sceSdSetParam(cmd->voice | SD_VP_PITCH, 0);
auto sibling_voice = sibling->voice;
auto voice = cmd->voice;
BlockUntilVoiceSafe(voice, 0x900);
BlockUntilVoiceSafe(sibling->voice, 0x900);
BlockUntilAllVoicesSafe();
sceSdSetSwitch((cmd->voice & 1) | SD_S_KOFF,
(VOICE_BIT(voice)) | (VOICE_BIT(sibling_voice)));
auto now = GetSystemTimeLow();
MarkVoiceKeyedOnOff(cmd->voice, now);
MarkVoiceKeyedOnOff(sibling->voice, now);
if (cmd->flags.clocks_set == 0) {
cmd->current_spu_address = 0;
sibling->current_spu_address = 0;
} else {
u32 spu_ram_addr = GetSpuRamAddress(cmd);
cmd->current_spu_address = spu_ram_addr & 0xfffffff8;
sibling->current_spu_address =
((spu_ram_addr & 0xfffffff8) - cmd->stream_sram) + sibling->stream_sram;
}
cmd->flags.paused = 1;
sibling->flags.paused = 1;
}
}
// CpuResumeIntr(local_18[0]);
}
}
namespace {
u32 read_rate_calc(u32 pitch) {
u64 pitch1 = (pitch >> 3);
u64 mult_result = pitch1 * 0x2492'4925ull;
return mult_result >> 32;
}
} // namespace
/*!
* Start up a VAG stream after it was paused. Also, used to start a vag stream for the first time.
*/
void UnPauseVAG(ISO_VAGCommand* cmd) {
if (cmd->flags.paused) {
// CpuSuspendIntr(&local_28);
if (cmd->flags.stereo_secondary == 0) {
auto* sibling = cmd->stereo_sibling;
auto pitch = CalculateVAGPitch(cmd->pitch1, cmd->pitch_cmd);
// calculate read rate
if (cmd->m_pBaseFile) {
auto p2 = CalculateVAGPitch(cmd->pitch1_file, cmd->pitch_cmd);
u32 rate;
if (sibling == (ISO_VAGCommand*)0x0) {
rate = p2 * 0x177;
} else {
rate = p2 * 0x2ee;
}
cmd->m_pBaseFile->m_ReadRate = read_rate_calc(rate);
}
int lvol, rvol;
CalculateVAGVolumes(cmd, &lvol, &rvol);
if (!sibling) {
// only update volume here if we've actually started the stream.
// otherwise, the stream is still getting set up, and the unpause will occur in
// the SPU interrupt handler.
if (cmd->flags.saw_chunks1) {
BlockUntilVoiceSafe(cmd->voice, 0x900);
if (cmd->current_spu_address != 0) {
sceSdSetAddr(cmd->voice | SD_VA_SSA, cmd->current_spu_address);
}
sceSdSetParam(cmd->voice | SD_VP_PITCH, pitch & 0xffff);
BlockUntilAllVoicesSafe();
// lg::error("keying on 1!");
sceSdSetSwitch((cmd->voice & 1) | SD_S_KON, VOICE_BIT(cmd->voice));
MarkVoiceKeyedOnOff(cmd->voice, GetSystemTimeLow());
sceSdSetParam(cmd->voice | SD_VP_VOLL, lvol);
sceSdSetParam(cmd->voice | SD_VP_VOLR, rvol);
}
cmd->flags.paused = 0;
} else {
if (cmd->flags.saw_chunks1) {
BlockUntilVoiceSafe(cmd->voice, 0x900);
BlockUntilVoiceSafe(sibling->voice, 0x900);
sceSdSetParam(cmd->voice | SD_VP_PITCH, pitch & 0xffff);
sceSdSetParam(sibling->voice | SD_VP_PITCH, pitch & 0xffff);
if (cmd->current_spu_address != 0) {
sceSdSetAddr(cmd->voice | SD_VA_SSA, cmd->current_spu_address);
sceSdSetAddr(sibling->voice | SD_VA_SSA, sibling->current_spu_address);
}
BlockUntilAllVoicesSafe();
sceSdSetSwitch((cmd->voice & 1) | SD_S_KON,
(VOICE_BIT(cmd->voice)) | (VOICE_BIT(sibling->voice)));
auto now = GetSystemTimeLow();
MarkVoiceKeyedOnOff(cmd->voice, now);
MarkVoiceKeyedOnOff(sibling->voice, now);
sceSdSetParam(cmd->voice | SD_VP_VOLL, lvol);
sceSdSetParam(sibling->voice | SD_VP_VOLL, 0);
sceSdSetParam(cmd->voice | SD_VP_VOLR, 0);
sceSdSetParam(sibling->voice | SD_VP_VOLR, rvol);
}
cmd->flags.paused = 0;
sibling->flags.paused = 0;
}
}
// CpuResumeIntr(local_28);
}
}
// TODO: needs some cleanup
void RestartVag(ISO_VAGCommand* cmd, int p) {
(void)p;
// CpuSuspendIntr(&local_28);
int lvol, rvol;
CalculateVAGVolumes(cmd, &lvol, &rvol);
if (cmd->flags.stereo_secondary == 0) {
auto* sibling = cmd->stereo_sibling;
u32 voice_mask = VOICE_BIT(cmd->voice);
BlockUntilVoiceSafe(cmd->voice, 0x900);
if (sibling) {
voice_mask |= VOICE_BIT(sibling->voice);
BlockUntilVoiceSafe(sibling->voice, 0x900);
}
BlockUntilAllVoicesSafe();
sceSdSetSwitch((cmd->voice & 1) | SD_S_KOFF, voice_mask);
sceSdSetParam(cmd->voice | SD_VP_VOLL, 0);
sceSdSetParam(cmd->voice | SD_VP_VOLR, 0);
// wtf is this crap
// CpuResumeIntr(local_28);
DelayThread(100);
// CpuSuspendIntr(&local_28);
u32 bVar1;
u32 uVar4;
if (sibling == (ISO_VAGCommand*)0x0) {
bVar1 = cmd->voice;
uVar4 = cmd->stream_sram;
} else {
sceSdSetParam(sibling->voice, 0);
sceSdSetParam(sibling->voice | 0x100, 0);
sceSdSetAddr(cmd->voice | 0x2040, cmd->stream_sram);
bVar1 = sibling->voice;
uVar4 = sibling->stream_sram;
}
sceSdSetAddr(bVar1 | 0x2040, uVar4);
BlockUntilAllVoicesSafe();
sceSdSetSwitch((cmd->voice & 1) | 0x1500, voice_mask);
auto uVar3 = GetSystemTimeLow();
MarkVoiceKeyedOnOff(cmd->voice, uVar3);
u32 uVar2;
if (!sibling) {
sceSdSetParam(cmd->voice, lvol);
uVar2 = cmd->voice;
} else {
MarkVoiceKeyedOnOff(sibling->voice, uVar3);
sceSdSetParam(cmd->voice, lvol);
sceSdSetParam(sibling->voice, 0);
sceSdSetParam(cmd->voice | 0x100, 0);
uVar2 = sibling->voice;
}
sceSdSetParam(uVar2 | 0x100, rvol);
}
// CpuResumeIntr(local_28);
}
void PauseVagStreams(bool music) {
if (music != 0) {
WaitSema(g_nMusicSemaphore);
g_bAnotherMusicPauseFlag = true;
g_bMusicIsPaused = true;
}
for (int i = 0; i < (music ? 6 : 4); i++) {
auto* cmd = &g_aVagCmds[i];
if (cmd->flags.running && !cmd->flags.paused) {
PauseVAG(cmd);
}
}
if (music != 0) {
SignalSema(g_nMusicSemaphore);
}
}
void UnPauseVagStreams(bool music) {
int iVar1;
ISO_VAGCommand* cmd;
if (music != 0) {
WaitSema(g_nMusicSemaphore);
}
cmd = g_aVagCmds;
iVar1 = 4;
if ((music != 0) && (iVar1 = 6, g_bMusicPause != 0)) {
iVar1 = 4;
}
while (iVar1 != 0) {
iVar1 = iVar1 + -1;
if ((cmd->flags.running != 0) && (cmd->flags.paused != 0)) {
UnPauseVAG(cmd);
}
cmd = cmd + 1;
}
if (music != 0) {
g_bAnotherMusicPauseFlag = false;
if (g_bMusicPause == 0) {
g_bMusicIsPaused = false;
}
SignalSema(g_nMusicSemaphore);
}
}
int CalculateDolbyPanAngle(ISO_VAGCommand* cmd, u32 angle) {
int iVar1;
int iVar2;
iVar1 = 0;
if (cmd != (ISO_VAGCommand*)0x0) {
if (0x167 < angle) {
// TODO: sus 64-bit multiply
angle = angle + (u32)((u64)(angle >> 3) * 0x16c16c17 >> 0x22) * -0x168;
}
iVar1 = cmd->dolby_pan_angle;
iVar2 = iVar1;
if (0x167 < iVar1) {
iVar2 = iVar1 + -0x168;
}
iVar2 = angle - iVar2;
if (iVar2 < -0xb4) {
iVar2 = iVar2 + 0x168;
} else {
if (0xb4 < iVar2) {
iVar2 = iVar2 + -0x168;
}
}
iVar1 = iVar1 + iVar2;
if (iVar1 < 0) {
iVar1 = iVar1 + 0x2d0;
}
if (0x2cf < iVar1) {
iVar1 = iVar1 + -0x2d0;
}
cmd->dolby_pan_angle = iVar1;
}
return iVar1;
}
void CalculateVAGVolumes(ISO_VAGCommand* cmd, int* lvol, int* rvol) {
u32 angle;
int iVar3;
int iVar4;
int iVar5;
u32 uVar7;
u32 uVar8;
int iVar9;
ASSERT(cmd && lvol && rvol);
iVar9 = g_nPlaybackMode;
iVar3 = 0x3fff;
iVar4 = 0x3fff;
uVar8 = 0;
if (cmd->music_flag == 0) {
iVar5 = cmd->maybe_sound_handler;
if (iVar5 == 0) {
if (cmd->updated_trans == 0) {
uVar7 = (u32)(cmd->play_volume * g_anMasterVolume[cmd->play_group]) >> 6;
goto LAB_0000bb58;
}
uVar7 = CalculateFalloffVolume(cmd->trans,
(cmd->play_volume * g_anMasterVolume[cmd->play_group]) >> 10,
cmd->fo_curve, cmd->fo_min, cmd->fo_max, 0, 0);
iVar3 = CalculateAngle(cmd->trans, cmd->fo_curve, 0);
angle = iVar3 + 0x5aU + (u32)((u64)((iVar3 + 0x5aU) >> 3) * 0x16c16c17 >> 0x22) * -0x168;
if (0x400 < uVar7) {
uVar7 = 0x400;
}
uVar7 = uVar7 << 4 | uVar7 >> 6;
} else {
s32 group = snd_GetSoundGroup(cmd->id) & 0x1f;
u32 vol = 0x3fff * (u32)((cmd->play_volume * g_anMasterVolume[group]) >> 10);
s32 pan_angle = (cmd->dolby_pan_angle + 90) % 360;
if (pan_angle < 0) {
pan_angle += 360;
}
s32 l = (g_aPanTable[pan_angle].left * (s32)(vol >> 10)) >> 10;
s32 r = (g_aPanTable[pan_angle].right * (s32)(vol >> 10)) >> 10;
if (l >= 0x4000) {
l = 0x3fff;
}
if (r >= 0x4000) {
r = 0x3fff;
}
*lvol = l;
*rvol = r;
return;
}
if (iVar9 == 1) {
iVar3 = 0x2d41;
iVar4 = 0x2d41;
} else {
if (iVar9 < 2) {
iVar3 = 0x2d41;
if (iVar9 == 0) {
// ASSERT_NOT_REACHED(); // dolby crap
// TODO:
iVar4 = 0x2d41;
// uVar8 = CalculateDolbyPanAngle(cmd, angle);
// iVar9 = (uint)((u64)(uVar8 >> 1) * 0xb60b60b7 >> 0x25) * 4;
// if (0x167 < uVar8) {
// uVar8 = uVar8 - 0x168;
// }
// if (uVar8 < 0xb4) {
// psVar6 = (short*)(g_aPanTable + uVar8 * 4);
// sVar1 = psVar6[1];
// sVar2 = *psVar6;
// } else {
// iVar3 = g_aPanTable + uVar8 * 4;
// sVar1 = *(short*)(iVar3 + -0x2d0);
// sVar2 = *(short*)(iVar3 + -0x2ce);
// }
// iVar4 = (int)sVar1;
// iVar3 = (int)sVar2;
// if ((int)((uint) * (ushort*)(iVar9 + 0x15800) << 0x10) < 0) {
// iVar3 = -iVar3;
// }
// if (*(short*)(iVar9 + 0x15802) < 0) {
// iVar4 = -iVar4;
// }
// uVar8 = 0xffffc001;
} else {
iVar4 = 0x2d41;
}
} else {
iVar3 = 0x2d41;
if (iVar9 == 2) {
cmd->dolby_pan_angle = angle;
if (angle < 0xb4) {
auto pte = g_aPanTable[angle];
// psVar6 = (short*)(g_aPanTable + angle * 4);
iVar4 = pte.right;
iVar3 = pte.left;
} else {
// iVar3 = g_aPanTable + angle * 4;
u32 angle2 = (angle - 180);
ASSERT(angle2 < 360);
auto pte = g_aPanTable[angle2];
iVar4 = pte.left;
iVar3 = pte.right;
}
} else {
iVar4 = 0x2d41;
}
}
}
} else {
uVar7 = ((u32)(g_nMusicFade * g_anMasterVolume[1]) >> 0xc) * g_nMusicTweak >> 7;
}
LAB_0000bb58:
iVar9 = iVar4;
if (g_CameraInvert != 0) {
iVar9 = iVar3;
iVar3 = iVar4;
}
iVar3 = iVar3 * uVar7;
iVar9 = iVar9 * uVar7;
if (iVar3 < 0) {
iVar3 = iVar3 + 0x3fff;
}
uVar7 = iVar3 >> 0xe;
if (iVar9 < 0) {
iVar9 = iVar9 + 0x3fff;
}
angle = iVar9 >> 0xe;
if (0x3fff < (int)uVar7) {
uVar7 = 0x3fff;
}
if ((int)uVar7 < (int)uVar8) {
uVar7 = uVar8;
}
*lvol = uVar7 & 0x7fff;
if (0x3fff < (int)angle) {
angle = 0x3fff;
}
if ((int)angle < (int)uVar8) {
angle = uVar8;
}
*rvol = angle & 0x7fff;
}
s32 CalculateVAGPitch(int param_1, int param_2) {
if (param_2) {
if (param_2 <= 0) {
return 0x5f4 * param_1 / (0x5f4 - param_2);
} else {
return param_1 * (param_2 + 0x5f4) / 0x5f4;
}
}
return param_1;
// if (b != 0) {
// if (0 < b) {
// return (a * (b + 0x5f4)) / 0x5f4;
// }
// a = (a * 0x5f4) / (0x5f4U - b);
// }
// return a;
}
void SetVAGVol(ISO_VAGCommand* cmd) {
if (cmd && cmd->flags.running && !cmd->flags.paused && !cmd->flags.stereo_secondary) {
auto* sibling = cmd->stereo_sibling;
int lvol, rvol;
CalculateVAGVolumes(cmd, &lvol, &rvol);
auto* file = cmd->m_pBaseFile;
if (file) {
u32 rate = CalculateVAGPitch(cmd->pitch1_file, cmd->pitch_cmd);
if (!sibling) {
rate = rate * 0x177;
} else {
rate = rate * 0x2ee;
}
file->m_ReadRate = read_rate_calc(rate);
}
// like jak2, rewritten to not use sceSdProcBatch
if (!sibling) {
sceSdSetParam(SD_VP_VOLL | cmd->voice, lvol);
sceSdSetParam(SD_VP_VOLR | cmd->voice, rvol);
sceSdSetParam(SD_VP_PITCH | cmd->voice, CalculateVAGPitch(cmd->pitch1, cmd->pitch_cmd));
} else {
// left channel, left vol
if (g_CameraInvert) {
sceSdSetParam(SD_VP_VOLL | cmd->voice, 0);
} else {
sceSdSetParam(SD_VP_VOLL | cmd->voice, lvol);
}
// right channel, left vol
if (g_CameraInvert) {
sceSdSetParam(SD_VP_VOLL | sibling->voice, rvol);
} else {
sceSdSetParam(SD_VP_VOLL | sibling->voice, 0);
}
if (g_CameraInvert) {
sceSdSetParam(SD_VP_VOLR | cmd->voice, lvol);
} else {
sceSdSetParam(SD_VP_VOLR | cmd->voice, 0);
}
if (g_CameraInvert) {
sceSdSetParam(SD_VP_VOLR | sibling->voice, 0);
} else {
sceSdSetParam(SD_VP_VOLR | sibling->voice, rvol);
}
sceSdSetParam(SD_VP_PITCH | cmd->voice, CalculateVAGPitch(cmd->pitch1, cmd->pitch_cmd));
}
// CpuSuspendIntr(local_20);
// sceSdProcBatch(batch, 0, count);
// CpuResumeIntr(local_20[0]);
}
}
void SetAllVagsVol(int x) {
if (x < 0) {
for (int i = 0; i < 4; i++) {
SetVAGVol(&g_aVagCmds[i]);
}
} else {
for (int i = 0; i < 4; i++) {
if (g_aVagCmds[i].maybe_sound_handler && (snd_GetSoundGroup(g_aVagCmds[i].id) & 0x1f) == x) {
SetVAGVol(&g_aVagCmds[i]);
}
}
}
}
void VAG_MarkLoopStart(uint8_t* data) {
data[0x11] = 2;
data[1] = 6;
}
void VAG_MarkLoopEnd(uint8_t* data, int offset) {
data[offset + -0xf] = 3;
}
} // namespace jak3