papermario/src/audio/sfx_player.c

1773 lines
71 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "audio.h"
#include "audio/core.h"
static void au_sfx_play_sound(SoundManager* manager, SoundPlayer* player, s8* readPos, SoundRequest* request, s32 priority, s32 exclusiveID);
static void au_sfx_set_triggers(SoundManager* manager, u32 soundID);
static void au_sfx_stop_by_id(SoundManager* manager, u32 soundID);
static void au_sfx_stop_by_exlusive_id(SoundManager* manager, u32 soundID);
static void au_sfx_set_modifiers(SoundManager* manager, SoundRequest* request);
static void au_sfx_set_player_modifiers(SoundPlayer* player, SoundRequest* request);
static void au_sfx_update_basic(SoundManager* manager, SoundPlayer* player, AuVoice* arg2, u8 arg3);
static s16 au_sfx_get_scaled_volume(SoundManager* manager, SoundPlayer* player);
static void au_sfx_update_sequence(SoundManager* manager, SoundPlayer* player, AuVoice* arg2, u8 arg3);
static void au_sfx_set_voice_volume(AuVoice* voice, SoundManager* manager, SoundPlayer* player);
static u8 au_sfx_get_random_pan(s32 arg0, s32 arg1, s32 arg2);
static s32 au_sfx_get_random_pitch(s32 arg0, s32 arg1, s32 arg2);
static u8 au_sfx_get_random_vol(s32 arg0, s32 arg1, s32 arg2);
static void au_sfx_reset_players(SoundManager* manager);
static void au_SEFCmd_00_SetVolume(SoundManager* manager, SoundPlayer* player);
static void au_SEFCmd_01_SetPan(SoundManager* manager, SoundPlayer* player);
static void au_SEFCmd_02_SetInstrument(SoundManager* manager, SoundPlayer* player);
static void au_SEFCmd_03_SetReverb(SoundManager* manager, SoundPlayer* player);
static void au_SEFCmd_04_SetEnvelope(SoundManager* manager, SoundPlayer* player);
static void au_SEFCmd_05_CoarseTune(SoundManager* manager, SoundPlayer* player);
static void au_SEFCmd_06_FineTune(SoundManager* manager, SoundPlayer* player);
static void au_SEFCmd_07_WaitForEnd(SoundManager* manager, SoundPlayer* player);
static void au_SEFCmd_08_PitchSweep(SoundManager* manager, SoundPlayer* player);
static void au_SEFCmd_09_StartLoop(SoundManager* manager, SoundPlayer* player);
static void au_SEFCmd_0A_EndLoop(SoundManager* manager, SoundPlayer* player);
static void au_SEFCmd_0B_WaitForRelease(SoundManager* manager, SoundPlayer* player);
static void au_SEFCmd_0C_SetCurrentVolume(SoundManager* manager, SoundPlayer* player);
static void au_SEFCmd_0D_VolumeRamp(SoundManager* manager, SoundPlayer* player);
static void au_SEFCmd_0E_SetAlternativeSound(SoundManager* manager, SoundPlayer* player);
static void au_SEFCmd_0F_Stop(SoundManager* manager, SoundPlayer* player);
static void au_SEFCmd_10_Jump(SoundManager* manager, SoundPlayer* player);
static void au_SEFCmd_11_Restart(SoundManager* manager, SoundPlayer* player);
static void au_SEFCmd_12_NOP(SoundManager* manager, SoundPlayer* player);
static void au_SEFCmd_13_SetRandomPitch(SoundManager* manager, SoundPlayer* player);
static void au_SEFCmd_14_SetRandomVelocity(SoundManager* manager, SoundPlayer* player);
static void au_SEFCmd_15_SetRandomUnused(SoundManager* manager, SoundPlayer* player);
static void au_SEFCmd_16_SetEnvelopePress(SoundManager* manager, SoundPlayer* player);
static void au_SEFCmd_17_PlaySound(SoundManager* manager, SoundPlayer* player);
static void au_SEFCmd_18_SetAlternativeVolume(SoundManager* manager, SoundPlayer* player);
typedef struct MusicTriggeredSound {
/* 0x00 */ u16 sound;
/* 0x02 */ u16 prereq; // when nonzero, sound may only play if this is already playing
/* 0x04 */ u8 flags;
} MusicTriggeredSound; // size = 0x5
// This flag field prefixes every SEF stream: two low bits choose a mode
// (BASIC vs SEQUENCE vs COMPACT) and extra bits lock parameters while playing.
enum SoundEffectParamFlags {
// 8 bytes: flags, instrument, volume, pan, reverb, pitch, randomPitch
SFX_PARAM_MODE_BASIC = 0x00000000,
// arbitrary sequence of commands
SFX_PARAM_MODE_SEQUENCE = 0x00000001,
// 4 bytes: flags, instrument, volume and randomPitch
SFX_PARAM_MODE_COMPACT = 0x00000002,
SFX_PARAM_MODE_MASK = 0x00000003,
// when flags below are set, these params can't be changed from api functions like sfx_play_sound_with_params
SFX_PARAM_LOCK_VOLUME = 0x00000004,
SFX_PARAM_LOCK_PAN = 0x00000008,
SFX_PARAM_LOCK_PITCH = 0x00000010,
SFX_PARAM_LOCK_REVERB = 0x00000020 // ignored SetReverb command
};
s16 DummyInstrumentCodebook[32] = {
0xF803, 0x0125, 0x07D0, 0xFDBC, 0xF886, 0x0355, 0x06FC, 0xFBAB,
0xFEDA, 0xF82D, 0x0245, 0x077D, 0xFCA9, 0xF901, 0x0456, 0x065D,
0xFC33, 0xFBB2, 0xFCEF, 0xFE94, 0xFFD8, 0x0080, 0x00A4, 0x007D,
0x090E, 0x0673, 0x02FF, 0x0053, 0xFEF2, 0xFEA7, 0xFEF9, 0xFF7B
};
u8 DummyInstrumentWavData[190] = {
0xB1, 0x01, 0x11, 0x10, 0x00, 0xFF, 0xFE, 0x34, 0xBB, 0x90, 0xE2, 0x1E, 0x00, 0xFB, 0x10, 0xEF,
0xF2, 0xD1, 0x80, 0xC4, 0xB3, 0xB1, 0xD3, 0xCF, 0xD1, 0xFD, 0xFE, 0x80, 0x1D, 0x2D, 0x3D, 0x3B,
0x2C, 0x3B, 0xFC, 0x1D, 0x80, 0xDE, 0xF0, 0xD0, 0xD3, 0xD2, 0xB3, 0xD1, 0xF4, 0x80, 0xA2, 0x03,
0xD0, 0x0D, 0xA9, 0xEA, 0xCB, 0x72, 0x90, 0x41, 0x4E, 0x1D, 0x2D, 0x0C, 0x1E, 0x10, 0x2F, 0x90,
0xF2, 0x12, 0x03, 0xF0, 0xC2, 0xD1, 0xD4, 0xF3, 0x80, 0xB0, 0xA1, 0xBF, 0xD2, 0x1E, 0x12, 0x70,
0x4D, 0x80, 0x4C, 0x39, 0x2C, 0x7E, 0x30, 0x6D, 0xB9, 0xCF, 0x90, 0xE1, 0xF2, 0xF3, 0xF2, 0xE1,
0xE2, 0x16, 0x22, 0xC1, 0xE7, 0x28, 0xF4, 0xF0, 0x21, 0x10, 0x10, 0xFF, 0xA1, 0xED, 0x9F, 0x2F,
0xF5, 0x61, 0x33, 0x3C, 0xD0, 0xA1, 0xDA, 0xC2, 0xFF, 0x14, 0x41, 0x22, 0x2D, 0xEF, 0xA1, 0xFA,
0xE1, 0x0E, 0x23, 0x30, 0x32, 0x0E, 0xF0, 0x91, 0x9A, 0xF2, 0xCF, 0x55, 0x13, 0x61, 0xEE, 0x1C,
0x91, 0x9D, 0x0F, 0xD2, 0x52, 0x06, 0x4D, 0xE1, 0x09, 0x91, 0xD0, 0x1B, 0x15, 0x2E, 0x36, 0xFD,
0x12, 0xCB, 0x81, 0x22, 0xBC, 0x65, 0xF0, 0x73, 0xCE, 0x3F, 0xAE, 0x71, 0x4E, 0x93, 0x70, 0xF5,
0x6E, 0xD2, 0x1B, 0xD1, 0x61, 0x0A, 0x5D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
// copy of SMALL_ROOM_PARAMS
s32 CUSTOM_SMALL_ROOM_PARAMS[] = {
/* sections length */
3, 11,
/* chorus chorus filter
input output fbcoef ffcoef gain rate depth coef */
0, 9, 9830, -9830, 0, 0, 0, 0,
3, 7, 3276, -3276, 0x3FFF, 0, 0, 0,
0, 10, 5000, 0, 0, 0, 0, 0x5000
};
// modified ECHO_PARAMS -- length and output changed
s32 CUSTOM_ECHO_PARAMS_1[] = {
/* sections length */
1, 11,
/* chorus chorus filter
input output fbcoef ffcoef gain rate depth coef */
0, 10, 20000, 0, 0x7FFF, 0, 0, 0x7FFF
};
// modified ECHO_PARAMS -- length and output changed
s32 CUSTOM_ECHO_PARAMS_2[] = {
/* sections length */
1, 13,
/* chorus chorus filter
input output fbcoef ffcoef gain rate depth coef */
0, 12, 20000, 0, 0x7FFF, 0, 0, 0x7FFF
};
// modified ECHO_PARAMS -- length and output changed
s32 CUSTOM_ECHO_PARAMS_3[] = {
/* sections length */
1, 14,
/* chorus chorus filter
input output fbcoef ffcoef gain rate depth coef */
0, 13, 20000, 0, 0x7FFF, 0, 0, 0x7FFF
};
MusicTriggeredSound MusicSounds[] = {
{
.sound = SOUND_SHORT_CLAP,
.prereq = SOUND_LRAW_CHEERING,
.flags = 1
}
};
// TODO: figure out how to make struct properly
EnvelopePreset SFXEnvelopeFast = {
.count = 1,
.offsets = { { 0x08, 0x12 } },
};
u8 SFXEnvelopeFastData[] = {
// press
ENV_TIME_290MS, 127,
ENV_TIME_900MS, 127,
ENV_TIME_1S, 95,
ENV_TIME_3S, 0,
ENV_CMD_END, 0,
// release
ENV_TIME_1400MS, 0,
ENV_CMD_END, 0
};
EnvelopePreset SFXEnvelopeSlow = {
.count = 1,
.offsets = { { 0x08, 0x1A } },
};
u8 SFXEnvelopeSlowData[] = {
// press
ENV_TIME_290MS, 127,
ENV_TIME_1800MS, 127,
ENV_TIME_290MS, 63,
ENV_TIME_1100MS, 31,
ENV_TIME_1100MS, 15,
ENV_TIME_1100MS, 7,
ENV_TIME_1100MS, 3,
ENV_TIME_450MS, 0,
ENV_CMD_END, 0,
// release
ENV_TIME_450MS, 0,
ENV_CMD_END, 0,
};
// looks like envelope data, but it's not aligned and not used
s32 D_800783B0[] = {
0x34337F26, 0x3F1600FF, 0x343B7F3B, 0x3FFF0000
};
EnvelopePreset* SFXEnvelopePresets[] = {
&SFXEnvelopeFast,
&SFXEnvelopeSlow,
&SFXEnvelopeSlow,
&SFXEnvelopeSlow,
&SFXEnvelopeSlow,
&SFXEnvelopeSlow,
&SFXEnvelopeSlow,
&SFXEnvelopeSlow,
&SFXEnvelopeSlow,
&SFXEnvelopeSlow,
&SFXEnvelopeSlow,
&SFXEnvelopeSlow,
&SFXEnvelopeSlow,
&SFXEnvelopeSlow,
&SFXEnvelopeSlow,
&SFXEnvelopeSlow
};
void (*SefCmdHandlers[])(SoundManager*, SoundPlayer*) = {
au_SEFCmd_00_SetVolume,
au_SEFCmd_01_SetPan,
au_SEFCmd_02_SetInstrument,
au_SEFCmd_03_SetReverb,
au_SEFCmd_04_SetEnvelope,
au_SEFCmd_05_CoarseTune,
au_SEFCmd_06_FineTune,
au_SEFCmd_07_WaitForEnd,
au_SEFCmd_08_PitchSweep,
au_SEFCmd_09_StartLoop,
au_SEFCmd_0A_EndLoop,
au_SEFCmd_0B_WaitForRelease,
au_SEFCmd_0C_SetCurrentVolume,
au_SEFCmd_0D_VolumeRamp,
au_SEFCmd_0E_SetAlternativeSound,
au_SEFCmd_0F_Stop,
au_SEFCmd_10_Jump,
au_SEFCmd_11_Restart,
au_SEFCmd_12_NOP,
au_SEFCmd_13_SetRandomPitch,
au_SEFCmd_14_SetRandomVelocity,
au_SEFCmd_15_SetRandomUnused,
au_SEFCmd_16_SetEnvelopePress,
au_SEFCmd_17_PlaySound,
au_SEFCmd_18_SetAlternativeVolume
};
u8 BlankSEFData[] = {
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00
};
// --------------------------------------------
// the following are only referenced in audio/bgm_player
void (*SeqCmdHandlers[])(BGMPlayer*, BGMPlayerTrack*) = {
au_BGMCmd_E0_MasterTempo,
au_BGMCmd_E1_MasterVolume,
au_BGMCmd_E2_MasterDetune,
au_BGMCmd_E3,
au_BGMCmd_E4_MasterTempoFade,
au_BGMCmd_E5_MasterVolumeFade,
au_BGMCmd_E6_MasterEffect,
au_BGMCmd_NOP,
au_BGMCmd_E8_TrackOverridePatch,
au_BGMCmd_E9_InstrumentVolume,
au_BGMCmd_EA_InstrumentPan,
au_BGMCmd_EB_InstrumentReverb,
au_BGMCmd_EC_TrackVolume,
au_BGMCmd_ED_InstrumentCoarseTune,
au_BGMCmd_EE_InstrumentFineTune,
au_BGMCmd_EC_TrackDetune,
au_BGMCmd_F0_TrackTremolo,
au_BGMCmd_F1_TrackTremoloRate,
au_BGMCmd_F2_TrackTremoloDepth,
au_BGMCmd_F3_TrackTremoloStop,
au_BGMCmd_F4_SubTrackRandomPan,
au_BGMCmd_F5_UseInstrument,
au_BGMCmd_F6_InstrumentVolumeLerp,
au_BGMCmd_F7_ReverbType,
au_BGMCmd_NOP,
au_BGMCmd_NOP,
au_BGMCmd_NOP,
au_BGMCmd_NOP,
au_BGMCmd_FC_Branch,
au_BGMCmd_FD_EventTrigger,
au_BGMCmd_FE_Detour,
au_BGMCmd_FF_Special,
};
s8 SeqCmdArgCounts[] = {
2, 1, 1, 1, 4, 3, 2, 0,
2, 1, 1, 1, 1, 1, 1, 2,
3, 1, 1, 0, 2, 1, 3, 1,
0, 0, 0, 0, 3, 3, 3, 3
};
s8 BgmTicksRates[] = {
48, 24, 32, 40, 48, 56, 64, 48,
0, 0, 0, 0, 0, 0, 0, 0
};
// --------------------------------------------
// the following are only referenced in audio/mseq_player
u8 BlankMseqData[] = {
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00
};
// --------------------------------------------
// the following are only referenced in audio/core/engine
/// Volume steps use squared values so each level represents linear power increase,
/// matching loudness perception. This makes each step sound evenly spaced.
u16 PerceptualVolumeLevels[] = {
[VOL_LEVEL_MUTE] 0, // 0.0 %
[VOL_LEVEL_1] AU_MAX_BUS_VOLUME * SQ(0.125), // 1.5625 %
[VOL_LEVEL_2] AU_MAX_BUS_VOLUME * SQ(0.250), // 6.25 %
[VOL_LEVEL_3] AU_MAX_BUS_VOLUME * SQ(0.375), // 14.0625 %
[VOL_LEVEL_4] AU_MAX_BUS_VOLUME * SQ(0.500), // 25.0 %
[VOL_LEVEL_5] AU_MAX_BUS_VOLUME * SQ(0.625), // 39.0625 %
[VOL_LEVEL_6] AU_MAX_BUS_VOLUME * SQ(0.750), // 56.25 %
[VOL_LEVEL_7] AU_MAX_BUS_VOLUME * SQ(0.875), // 76.5625 %
[VOL_LEVEL_FULL] AU_MAX_BUS_VOLUME, // 100.0 %
};
// TODO: figure out how to make struct properly
EnvelopePreset DummyInstrumentEnvelope = {
.count = 1,
.offsets = { { 0x8, 0xC } }, // EnvelopePressDefault, EnvelopePressDefault
};
u8 EnvelopePressDefault[] = {
ENV_TIME_280MS, 127,
ENV_CMD_END, 0
};
u8 EnvelopeReleaseDefault[] = {
ENV_TIME_550MS, 0,
ENV_CMD_END, 0
};
u8 EnvelopeReleaseDefaultFast[] = {
ENV_TIME_2UNITS, 0,
ENV_CMD_END, 0
};
// --------------------------------------------
// the following are only referenced in audio/bgm_player
s8 BgmCustomEnvLookup[] = {
0x5E, 0x5D, 0x5C, 0x5B, 0x5A, 0x58, 0x56, 0x53,
0x51, 0x4F, 0x4A, 0x45, 0x40, 0x3B, 0x37, 0x35,
0x33, 0x31, 0x2F, 0x2D, 0x2B, 0x29, 0x27, 0x26,
0x25, 0x23, 0x21, 0x20, 0x1F, 0x1E, 0x1D, 0x1C,
0x1B, 0x1A, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14
};
// --------------------------------------------
// the following are only referenced in audio/core/engine
u8 AmbientSoundIDtoMSEQFileIndex[] = {
0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A,
0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12,
0x13, 0x14, 0x15, 0x16, 0x17, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
// --------------------------------------------
// the following are only referenced in audio/voice_envelope
// convert seconds to microseconds and round to number multiple to 5750
#define SEC(x) (((s32)(x * 1000000) / AU_FRAME_USEC) * AU_FRAME_USEC)
s32 AuEnvelopeIntervals[] = {
SEC(60), SEC(55), SEC(50), SEC(45), SEC(40), SEC(35), SEC(30), SEC(27.5), SEC(25), SEC(22.5),
SEC(20), SEC(19), SEC(18), SEC(17), SEC(16), SEC(15), SEC(14), SEC(13), SEC(12), SEC(11),
SEC(10), SEC(9), SEC(8), SEC(7), SEC(6), SEC(5), SEC(4.5), SEC(4), SEC(3.5), SEC(3),
SEC(2.75), SEC(2.5), SEC(2.25), SEC(2), SEC(1.9), SEC(1.8), SEC(1.7), SEC(1.6), SEC(1.5), SEC(1.4),
SEC(1.3), SEC(1.2), SEC(1.1), SEC(1), SEC(0.95), SEC(0.9), SEC(0.85), SEC(0.8), SEC(0.75), SEC(0.7),
SEC(0.65), SEC(0.6), SEC(0.55), SEC(0.5), SEC(0.45), SEC(0.4), SEC(0.375), SEC(0.35), SEC(0.325), SEC(0.3),
SEC(0.29), SEC(0.28), SEC(0.27), SEC(0.26), SEC(0.25), SEC(0.24), SEC(0.23), SEC(0.22), SEC(0.21), SEC(0.2),
SEC(0.19), SEC(0.18), SEC(0.17), SEC(0.16), SEC(0.15), SEC(0.14), SEC(0.13), SEC(0.12), SEC(0.11), SEC(0.1),
16 * AU_FRAME_USEC, 14 * AU_FRAME_USEC, 12 * AU_FRAME_USEC, 11 * AU_FRAME_USEC, 10 * AU_FRAME_USEC,
9 * AU_FRAME_USEC, 8 * AU_FRAME_USEC, 7 * AU_FRAME_USEC, 6 * AU_FRAME_USEC, 5 * AU_FRAME_USEC,
4 * AU_FRAME_USEC, 3 * AU_FRAME_USEC, 2 * AU_FRAME_USEC, 1 * AU_FRAME_USEC, 0, 0, 0, 0, 0, 0,
};
#undef SEC
// --------------------------------------------
// the following are only referenced in audio/core/engine
f32 AlTuneScaling[] = {
// ---------------------------------------------------------
// TUNE_SCALING_ARR_AMPLIFY_FINE (offset 0, size 128)
// ---------------------------------------------------------
// Pitch scaling for fine positive tuning.
// Each increment represents a pitch increase of 1 cent (1/100 semitone).
//
// Formula: 2^((i / 100) / 12) = 2^(i / 1200)
// i.e., 1 semitone of amplification at i = 100 (2^(1/12) ~ 1.059463)
1.00000000f, 1.00057781f, 1.00115597f, 1.00173450f, 1.00231326f, 1.00289237f, 1.00347185f, 1.00405169f,
1.00463188f, 1.00521231f, 1.00579309f, 1.00637424f, 1.00695574f, 1.00753760f, 1.00811982f, 1.00870228f,
1.00928509f, 1.00986826f, 1.01045179f, 1.01103568f, 1.01161981f, 1.01220429f, 1.01278913f, 1.01337433f,
1.01395988f, 1.01454580f, 1.01513207f, 1.01571858f, 1.01630545f, 1.01689267f, 1.01748025f, 1.01806819f,
1.01865649f, 1.01924503f, 1.01983392f, 1.02042317f, 1.02101278f, 1.02160275f, 1.02219307f, 1.02278376f,
1.02337468f, 1.02396595f, 1.02455759f, 1.02514958f, 1.02574193f, 1.02633464f, 1.02692771f, 1.02752113f,
1.02811480f, 1.02870882f, 1.02930319f, 1.02989793f, 1.03049302f, 1.03108847f, 1.03168428f, 1.03228045f,
1.03287685f, 1.03347361f, 1.03407073f, 1.03466821f, 1.03526604f, 1.03586423f, 1.03646278f, 1.03706169f,
1.03766096f, 1.03826058f, 1.03886044f, 1.03946066f, 1.04006124f, 1.04066217f, 1.04126346f, 1.04186511f,
1.04246712f, 1.04306948f, 1.04367220f, 1.04427528f, 1.04487872f, 1.04548252f, 1.04608655f, 1.04669094f,
1.04729569f, 1.04790080f, 1.04850626f, 1.04911208f, 1.04971826f, 1.05032480f, 1.05093169f, 1.05153894f,
1.05214655f, 1.05275452f, 1.05336285f, 1.05397153f, 1.05458057f, 1.05518997f, 1.05579972f, 1.05640972f,
1.05702007f, 1.05763078f, 1.05824184f, 1.05885327f, 1.05946505f, 1.06007719f, 1.06068969f, 1.06130254f,
1.06191576f, 1.06252933f, 1.06314325f, 1.06375754f, 1.06437218f, 1.06498718f, 1.06560254f, 1.06621826f,
1.06683433f, 1.06745076f, 1.06806755f, 1.06868470f, 1.06930220f, 1.06992006f, 1.07053828f, 1.07115686f,
1.07177579f, 1.07239509f, 1.07301474f, 1.07363474f, 1.07425511f, 1.07487583f, 1.07549691f, 1.07611835f,
// ---------------------------------------------------------
// TUNE_SCALING_ARR_AMPLIFY_COARSE (offset 128, size 32)
// ---------------------------------------------------------
// Pitch scaling for coarse positive tuning.
// Each increment represents a pitch increase of 128 cents (1.28 semitones)
//
// Formula: 2^((128 * i / 100) / 12)
1.00000000f, 1.07674015f, 1.15936935f, 1.24833953f, 1.34413731f, 1.44728661f, 1.55835164f, 1.67793977f,
1.80670512f, 1.94535196f, 2.09463859f, 2.25538135f, 2.42845964f, 2.61482000f, 2.81548166f, 3.03154206f,
3.26418304f, 3.51467681f, 3.78439355f, 4.07480860f, 4.38750982f, 4.72420788f, 5.08674431f, 5.47710180f,
5.89741516f, 6.34998369f, 6.83728218f, 7.36197615f, 7.92693520f, 8.53524971f, 9.19024563f, 9.89550686f,
// ---------------------------------------------------------
// TUNE_SCALING_ARR_ATTENUATE_FINE (offset 160, size 128)
// ---------------------------------------------------------
// Pitch scaling for fine downward tuning.
// Each increment represents a pitch decrease of 1 cent (1/100 semitone).
//
// Formula: 2^(-(i / 100) / 12)
1.00000000f, 0.99942255f, 0.99884546f, 0.99826866f, 0.99769223f, 0.99711609f, 0.99654031f, 0.99596488f,
0.99538976f, 0.99481499f, 0.99424052f, 0.99366641f, 0.99309260f, 0.99251914f, 0.99194598f, 0.99137318f,
0.99080074f, 0.99022859f, 0.98965681f, 0.98908532f, 0.98851418f, 0.98794335f, 0.98737288f, 0.98680270f,
0.98623288f, 0.98566335f, 0.98509419f, 0.98452532f, 0.98395681f, 0.98338860f, 0.98282075f, 0.98225319f,
0.98168600f, 0.98111910f, 0.98055255f, 0.97998631f, 0.97942042f, 0.97885484f, 0.97828960f, 0.97772467f,
0.97716010f, 0.97659582f, 0.97603190f, 0.97546828f, 0.97490501f, 0.97434205f, 0.97377944f, 0.97321713f,
0.97265512f, 0.97209346f, 0.97153211f, 0.97097111f, 0.97041041f, 0.96985006f, 0.96929002f, 0.96873033f,
0.96817094f, 0.96761185f, 0.96705312f, 0.96649468f, 0.96593660f, 0.96537882f, 0.96482134f, 0.96426421f,
0.96370739f, 0.96315092f, 0.96259475f, 0.96203887f, 0.96148336f, 0.96092814f, 0.96037328f, 0.95981872f,
0.95926446f, 0.95871055f, 0.95815694f, 0.95760363f, 0.95705068f, 0.95649803f, 0.95594567f, 0.95539367f,
0.95484197f, 0.95429057f, 0.95373952f, 0.95318878f, 0.95263839f, 0.95208830f, 0.95153850f, 0.95098901f,
0.95043987f, 0.94989103f, 0.94934249f, 0.94879431f, 0.94824642f, 0.94769883f, 0.94715160f, 0.94660467f,
0.94605803f, 0.94551176f, 0.94496578f, 0.94442010f, 0.94387472f, 0.94332969f, 0.94278497f, 0.94224054f,
0.94169647f, 0.94115269f, 0.94060922f, 0.94006604f, 0.93952322f, 0.93898070f, 0.93843848f, 0.93789655f,
0.93735498f, 0.93681371f, 0.93627274f, 0.93573207f, 0.93519175f, 0.93465173f, 0.93411201f, 0.93357259f,
0.93303353f, 0.93249476f, 0.93195629f, 0.93141812f, 0.93088025f, 0.93034273f, 0.92980552f, 0.92926860f,
// ---------------------------------------------------------
// TUNE_SCALING_ARR_ATTENUATE_COARSE (offset 288, size 128)
// ---------------------------------------------------------
// Pitch scaling for coarse downward tuning.
// Each increment represents a pitch decrease of 128 cents (1.28 semitones)
//
// Formula: 2^(-(128 * i / 100) / 12)
1.00000000f, 0.92873198f, 0.86254311f, 0.80107135f, 0.74398059f, 0.69095856f, 0.64171529f, 0.59598154f,
0.55350709f, 0.51405972f, 0.47742370f, 0.44339865f, 0.41179851f, 0.38245043f, 0.35519394f, 0.32987997f,
0.30637008f, 0.28453568f, 0.26425737f, 0.24542427f, 0.22793336f, 0.21168900f, 0.19660234f, 0.18259089f,
0.16957800f, 0.15749252f, 0.14626834f, 0.13584408f, 0.12616274f, 0.11717137f, 0.10882080f, 0.10106535f,
0.09386262f, 0.08717322f, 0.08096056f, 0.07519066f, 0.06983197f, 0.06485518f, 0.06023308f, 0.05594039f,
0.05195362f, 0.04825099f, 0.04481224f, 0.04161856f, 0.03865249f, 0.03589780f, 0.03333944f, 0.03096340f,
0.02875670f, 0.02670727f, 0.02480390f, 0.02303617f, 0.02139443f, 0.01986969f, 0.01845361f, 0.01713846f,
0.01591704f, 0.01478266f, 0.01372913f, 0.01275068f, 0.01184197f, 0.01099801f, 0.01021421f, 0.00948626f,
0.00881019f, 0.00818231f, 0.00759917f, 0.00705759f, 0.00655461f, 0.00608748f, 0.00565364f, 0.00525071f,
0.00487650f, 0.00452897f, 0.00420620f, 0.00390643f, 0.00362802f, 0.00336946f, 0.00312933f, 0.00290631f,
0.00269918f, 0.00250681f, 0.00232816f, 0.00216224f, 0.00200814f, 0.00186502f, 0.00173211f, 0.00160866f,
0.00149402f, 0.00138754f, 0.00128865f, 0.00119681f, 0.00111152f, 0.00103230f, 0.00095873f, 0.00089041f,
0.00082695f, 0.00076801f, 0.00071328f, 0.00066244f, 0.00061523f, 0.00057139f, 0.00053067f, 0.00049285f,
0.00045772f, 0.00042510f, 0.00039480f, 0.00036667f, 0.00034054f, 0.00031627f, 0.00029373f, 0.00027279f,
0.00025335f, 0.00023530f, 0.00021853f, 0.00020295f, 0.00018849f, 0.00017506f, 0.00016258f, 0.00015099f,
0.00014023f, 0.00013024f, 0.00012096f, 0.00011234f, 0.00010433f, 0.00009689f, 0.00008999f, 0.00008358f
};
extern s32* AU_FX_CUSTOM_PARAMS[0]; // points to 80078290
void (*CurrentSefCmdHandler)(SoundManager*, SoundPlayer*);
void au_sfx_init(SoundManager* manager, u8 priority, u8 busID, AuGlobals* globals, u8 minVoiceIdx) {
// odd choice of parameters, perhaps chosen for a very large LCM (67934687500)?
// gives a pseudo-random sfx update pattern
s32 c = 434782;
u32 i;
manager->globals = globals;
manager->nextUpdateStep = 312500;
manager->nextUpdateCounter = c;
manager->nextUpdateInterval = c;
manager->priority = priority;
manager->busID = busID;
if (minVoiceIdx > 16) {
manager->firstVoice = 16;
} else {
manager->firstVoice = minVoiceIdx;
}
manager->busVolume = AU_MAX_BUS_VOLUME;
manager->baseVolume = AU_MAX_BUS_VOLUME;
manager->frameCounter = 0;
manager->randomValue = 0;
for (i = 0; i < ARRAY_COUNT(manager->players); i++) {
SoundPlayer* player = &manager->players[i];
player->sefDataReadPos = NULL;
player->sfxVolume = 0;
player->delay = 0;
player->playLength = 0;
player->coarseTune = 0;
player->fineTune = 0;
player->sfxPan = 0;
player->reverb = 0;
player->instrumentIndex = 0;
player->envelopePreset = 0;
player->playVelocity = 0;
player->exclusiveID = 0;
player->cmdListOneShot[0] = 0; // will be set to bank
player->cmdListOneShot[1] = 0; // will be set to patch
player->cmdListOneShot[2] = 0; // will be set to volume
player->cmdListOneShot[3] = AU_PAN_MID; // default pan
player->cmdListOneShot[4] = 0; // reverb
player->cmdListOneShot[5] = 0x80 + (DEFAULT_KEYBASE / 100); // default tune, higher bit is meaningless
player->cmdListOneShot[6] = 0; // random pitch
player->cmdListOneShot[7] = 0; // unused
}
for (i = 0; i < ARRAY_COUNT(manager->bgmSounds); i++) {
manager->bgmSounds[i].raw = 0;
}
for (i = 0; i < ARRAY_COUNT(manager->customCmdList); i++) {
manager->customCmdList[i].data[0] = 0;
}
manager->resetPending = FALSE;
au_sfx_set_state(manager, SND_MANAGER_STATE_ENABLED);
au_sfx_clear_queue(manager);
au_fade_init(&manager->fadeInfo, 0, AU_MAX_VOLUME_16, AU_MAX_VOLUME_16);
au_fade_set_volume(manager->busID, manager->fadeInfo.baseVolume >> 16, manager->busVolume);
manager->lastCustomEffectIdx = 0xFF;
manager->customReverbParams[0] = CUSTOM_SMALL_ROOM_PARAMS;
manager->customReverbParams[1] = CUSTOM_ECHO_PARAMS_1;
manager->customReverbParams[2] = CUSTOM_ECHO_PARAMS_2;
manager->customReverbParams[3] = CUSTOM_ECHO_PARAMS_3;
manager->customReverbParams[4] = CUSTOM_ECHO_PARAMS_3;
manager->customReverbParams[5] = CUSTOM_ECHO_PARAMS_3;
manager->customReverbParams[6] = CUSTOM_ECHO_PARAMS_3;
manager->customReverbParams[7] = CUSTOM_ECHO_PARAMS_3;
manager->customReverbAmounts[0] = 0x10;
manager->customReverbAmounts[1] = 0x20;
manager->customReverbAmounts[2] = 0x20;
manager->customReverbAmounts[3] = 0x20;
manager->customReverbAmounts[4] = 0x20;
manager->customReverbAmounts[5] = 0x30;
manager->customReverbAmounts[6] = 0x40;
manager->customReverbAmounts[7] = 0x50;
au_sfx_set_reverb_type(manager, 0);
}
void au_sfx_load_groups_from_SEF(SoundManager* manager) {
SEFHeader* sefData = manager->globals->dataSEF;
s32 sections = ARRAY_COUNT(sefData->sections);
u32 i;
manager->sefData = (u8*)sefData;
for (i = 0; i < sections; i++) {
if (sefData->sections[i] != 0) {
manager->normalSounds[i] = AU_FILE_RELATIVE(sefData, sefData->sections[i]);
}
}
if (sefData->hasExtraSection == 1) {
if (sefData->section2000 != 0) {
manager->extraSounds = AU_FILE_RELATIVE(sefData, sefData->section2000);
}
}
}
void au_sfx_clear_queue(SoundManager* manager) {
s32 i;
for (i = 0; i < SFX_QUEUE_SIZE; i++) {
manager->soundQueue[i].soundID = SOUND_NONE;
manager->soundQueue[i].toReplaceID = SOUND_NONE;
manager->soundQueue[i].volume = 0;
manager->soundQueue[i].pitchShift = 0;
manager->soundQueue[i].pan = 0;
}
manager->unused_165 = 0;
manager->sfxQueueWritePos = 0;
manager->sfxQueueReadPos = 0;
manager->unused_162 = 0;
}
// Registers a new sound effect for playback from the main/game thread.
// The new event is dropped if the buffer is full.
void au_sfx_enqueue_event(SoundManager* manager, u32 soundID, s16 volume, s16 pitchShift, u8 pan) {
// Determine the number of pending sound effects in the queue
s32 pending = manager->sfxQueueWritePos - manager->sfxQueueReadPos;
if (pending < 0) {
pending += SFX_QUEUE_SIZE;
}
// Only enqueue if there's room in the buffer
if (pending < SFX_QUEUE_SIZE) {
u32 nextPos = manager->sfxQueueWritePos;
manager->soundQueue[nextPos].soundID = soundID & (SOUND_ID_LOWER | SOUND_ID_STOP | SOUND_ID_ADJUST | SOUND_ID_TRIGGER_MASK);
manager->soundQueue[nextPos].toReplaceID = (soundID & SOUND_ID_UPPER_MASK) >> 0x10;
manager->soundQueue[nextPos].volume = volume;
manager->soundQueue[nextPos].pitchShift = pitchShift;
manager->soundQueue[nextPos].pan = pan;
nextPos++;
if (nextPos >= SFX_QUEUE_SIZE) {
nextPos = 0;
}
manager->sfxQueueWritePos = nextPos;
}
}
// Called from the audio thread once per video frame.
// Consumes and processes sound events from the queue.
void au_sfx_begin_video_frame(SoundManager* manager) {
SoundRequest newRequest;
SoundRequest* request;
u32 i, j, k;
s32 pending;
manager->frameCounter++;
if (manager->resetPending) {
au_sfx_reset_players(manager);
manager->resetPending = FALSE;
}
// Process sound effects triggered by music via BGM_SPECIAL_TRIGGER_SOUND
for (i = 0; i < ARRAY_COUNT(manager->bgmSounds); i++) {
k = manager->bgmSounds[i].index;
if (k == 0) {
// do nothing
} else if (k <= ARRAY_COUNT(MusicSounds)) {
MusicTriggeredSound* triggered = &MusicSounds[k-1];
u16 prereq = triggered->prereq;
if (prereq != 0) {
// try playing sound ONLY IF prereq sound is already playing
for (j = 0; j < ARRAY_COUNT(manager->players); j++) {
if (manager->players[j].curSoundID == prereq) {
newRequest.soundID = triggered->sound;
newRequest.toReplaceID = SOUND_NONE;
newRequest.pitchShift = 0;
if ((triggered->flags & 1) && (manager->bgmSounds[i].volume != 0)) {
newRequest.volume = (manager->bgmSounds[i].volume << 8) + 0xFF;
} else {
newRequest.volume = 0;
}
newRequest.pan = 0;
au_sfx_try_sound(manager, &newRequest, NULL);
break;
}
}
} else {
// no prereq defined, unconditionally play the sound
newRequest.soundID = triggered->sound;
newRequest.toReplaceID = SOUND_NONE;
newRequest.volume = 0;
newRequest.pitchShift = 0;
newRequest.pan = 0;
au_sfx_try_sound(manager, &newRequest, NULL);
}
}
// clear the event
manager->bgmSounds[i].raw = 0;
}
// Process sound effects triggered by SEF command 17
for (i = 0; i < ARRAY_COUNT(manager->customCmdList); i++) {
if (manager->customCmdList[i].data[0] != 0) {
newRequest.soundID = 1;
newRequest.toReplaceID = SOUND_NONE;
newRequest.volume = 0;
newRequest.pitchShift = 0;
newRequest.pan = 0;
au_sfx_try_sound(manager, &newRequest, &manager->customCmdList[i]);
}
manager->customCmdList[i].data[0] = 0;
}
// Determine the number of pending sound effects in the queue
pending = manager->sfxQueueWritePos - manager->sfxQueueReadPos;
if (pending < 0) {
pending += SFX_QUEUE_SIZE;
}
// Process each pending sound effect in the queue
j = manager->sfxQueueReadPos;
if (pending > 0 && pending < SFX_QUEUE_SIZE) {
for (i = 0; i < pending; i++) {
request = &manager->soundQueue[j];
if (request->soundID & SOUND_ID_LOWER) {
if (request->soundID & SOUND_ID_STOP) {
au_sfx_stop_by_id(manager, request->soundID);
} else if (!(request->soundID & (SOUND_ID_ADJUST | SOUND_ID_TRIGGER_MASK))) {
au_sfx_try_sound(manager, request, NULL);
} else {
if (request->soundID & SOUND_ID_TRIGGER_MASK) {
au_sfx_set_triggers(manager, request->soundID);
}
if (request->soundID & SOUND_ID_ADJUST) {
au_sfx_set_modifiers(manager, request);
}
}
}
request->soundID = SOUND_NONE;
j++;
if (j >= SFX_QUEUE_SIZE) {
j = 0;
}
}
manager->sfxQueueReadPos = manager->sfxQueueWritePos;
}
}
// also affects ambience because it uses same fx bus
s32 au_sfx_set_reverb_type(SoundManager* manager, s32 arg1) {
s32 customIdx = (u8) arg1;
if (customIdx != 0xF0) {
if (customIdx < ARRAY_COUNT(manager->customReverbParams)) {
if (manager->lastCustomEffectIdx != customIdx) {
manager->lastCustomEffectIdx = customIdx;
manager->globals->effectChanges[FX_BUS_SOUND].type = AU_FX_CUSTOM_0;
manager->globals->effectChanges[FX_BUS_SOUND].changed = TRUE;
AU_FX_CUSTOM_PARAMS[0] = manager->customReverbParams[customIdx];
}
manager->defaultReverbAmt = manager->customReverbAmounts[customIdx];
} else {
manager->lastCustomEffectIdx = 0xFF;
manager->defaultReverbAmt = 0;
}
}
return manager->lastCustomEffectIdx;
}
void au_sfx_set_state(SoundManager* manager, s32 state) {
if (state == SND_MANAGER_STATE_ENABLED) {
manager->state = state;
} else if (state == SND_MANAGER_STATE_DISABLED) {
manager->state = state;
}
}
void au_sfx_try_sound(SoundManager* manager, SoundRequest* request, SoundManagerCustomCmdList* customSEF) {
SoundPlayer* player;
s32 playerIndex;
u16* cmdList;
s32 trackCount;
s32 foundPlayer = FALSE;
u32 exclusiveID;
s32 sectionIndex;
u16 soundInfo;
u32 priority, polyphonyMode, useSpecificPlayerMode;
s32 v1;
s32* normalSounds;
#define NEXT_POLY_TRACK trackCount--; if (trackCount <= 0 ) { break; } cmdList += 2;
u32 soundIndex = (request->soundID - 1) & 0xFF;
u16 soundIDLower = request->soundID & SOUND_ID_LOWER;
u16 soundID = request->soundID;
if (soundID & SOUND_ID_UNK) {
// sound from extra section
soundIndex = (request->soundID - 1) & SOUND_ID_UNK_INDEX_MASK;
if (soundIndex < 0x140) {
cmdList = (u16*)&manager->extraSounds[soundIndex];
if (*cmdList != 0) {
// check if any player is playing this sound
for (playerIndex = 7; playerIndex >= 0; playerIndex--) {
player = &manager->players[playerIndex];
if (player->curSoundID == soundIDLower) {
foundPlayer = TRUE;
break;
}
}
if (!foundPlayer) {
//find free player
for (playerIndex = 7; playerIndex >= 0; playerIndex--) {
player = &manager->players[playerIndex];
if (player->sefDataReadPos == NULL) {
foundPlayer = TRUE;
break;
}
}
}
if (foundPlayer) {
au_sfx_play_sound(manager, player, (s8*)cmdList, request, 0, 0);
}
}
}
} else {
if (soundIndex >= 0xC0) {
if (customSEF != NULL) {
cmdList = (u16*)customSEF;
} else {
sectionIndex = ((soundIDLower - 1) >> 8) + 4;
normalSounds = manager->normalSounds[sectionIndex];
v1 = soundIndex - 0xC0;
cmdList = (u16*)&manager->normalSounds[sectionIndex][v1];
}
if (*cmdList != 0) {
// check if any player is playing this sound
for (playerIndex = 7; playerIndex >= 0; playerIndex--) {
player = &manager->players[playerIndex];
if (player->curSoundID == soundIDLower) {
foundPlayer = TRUE;
break;
}
}
if (!foundPlayer) {
//find free player
for (playerIndex = 7; playerIndex >= 0; playerIndex--) {
player = &manager->players[playerIndex];
if (player->sefDataReadPos == NULL) {
foundPlayer = TRUE;
break;
}
}
}
if (foundPlayer) {
au_sfx_play_sound(manager, player, (u8*)cmdList, request, 0, 0);
}
}
} else {
if (customSEF != NULL) {
cmdList = (u16*)customSEF;
} else {
sectionIndex = ((soundID) >> 8) & 3;
cmdList = (u16*)&manager->normalSounds[sectionIndex][soundIndex];
}
if (*cmdList != 0) {
// read sound info chunk
soundInfo = cmdList[1];
priority = (soundInfo & 0x300) >> 8; // bits 8, 9
polyphonyMode = (soundInfo & 0x60) >> 5; // bits 5, 6,
useSpecificPlayerMode = soundInfo;
useSpecificPlayerMode = (useSpecificPlayerMode & 0x80) >> 7; // bit 7
if (polyphonyMode == 0) {
if (request->toReplaceID != SOUND_NONE) {
for (playerIndex = 0; playerIndex < 8; playerIndex++) {
player = &manager->players[playerIndex];
if (player->curSoundID == request->toReplaceID) {
foundPlayer = TRUE;
break;
}
}
}
if (useSpecificPlayerMode == 0) {
if (!foundPlayer) {
playerIndex = soundInfo & 0x7;
player = &manager->players[playerIndex];
if (player->sefDataReadPos == NULL || priority >= player->priority) {
au_sfx_play_sound(manager, player, AU_FILE_RELATIVE(manager->sefData, *cmdList), request, priority, 0);
}
return;
}
}
if (!foundPlayer) {
// lower 4 bits of soundInfo: max playerIndex
// check if any player is playing this sound
for (playerIndex = soundInfo & 0x7; playerIndex >= 0; playerIndex--) {
player = &manager->players[playerIndex];
if (player->curSoundID == soundIDLower) {
foundPlayer = TRUE;
break;
}
}
}
if (!foundPlayer) {
// find free player
for (playerIndex = soundInfo & 0x7; playerIndex >= 0; playerIndex--) {
player = &manager->players[playerIndex];
if (player->sefDataReadPos == NULL) {
foundPlayer = TRUE;
break;
}
}
}
if (!foundPlayer) {
// if there is no free player try stealing one with lower priority
for (playerIndex = soundInfo & 0x7; playerIndex >= 0; playerIndex--) {
player = &manager->players[playerIndex];
if (player->priority < priority) {
foundPlayer = TRUE;
break;
}
}
}
if (!foundPlayer) {
playerIndex = soundInfo & 0x7;
player = &manager->players[playerIndex];
if (player->priority <= priority) {
foundPlayer = TRUE;
}
}
if (foundPlayer) {
au_sfx_play_sound(manager, player, AU_FILE_RELATIVE(manager->sefData, *cmdList), request, priority, 0);
}
} else {
cmdList = AU_FILE_RELATIVE(manager->sefData, *cmdList);
exclusiveID = (soundInfo & 0x1800) >> 11; // bits 11, 12
if (exclusiveID != 0) {
au_sfx_stop_by_exlusive_id(manager, exclusiveID);
} else {
au_sfx_stop_by_id(manager, request->soundID);
}
trackCount = 2 << (polyphonyMode - 1); // 2 or 4 or 8
if (useSpecificPlayerMode != 0) {
for (playerIndex = 7; playerIndex >= 0; playerIndex--) {
if (*cmdList != 0) {
// first check the players that were stopped just now
player = &manager->players[playerIndex];
if (player->sefDataReadPos == BlankSEFData) {
au_sfx_play_sound(manager, player, AU_FILE_RELATIVE(manager->sefData, *cmdList), request, priority, exclusiveID);
NEXT_POLY_TRACK;
}
} else {
NEXT_POLY_TRACK;
}
}
if (trackCount != 0) {
for (playerIndex = 7; playerIndex >= 0;){
if (*cmdList != 0) {
// then check any free players
player = &manager->players[playerIndex];
if (player->sefDataReadPos == NULL) {
au_sfx_play_sound(manager, player, AU_FILE_RELATIVE(manager->sefData, *cmdList), request, priority, exclusiveID);
NEXT_POLY_TRACK;
}
playerIndex--;
} else {
NEXT_POLY_TRACK;
}
}
}
if (trackCount != 0) {
for (playerIndex = 7; playerIndex >= 0; playerIndex--) {
if (*cmdList != 0) {
// then try to steal
player = &manager->players[playerIndex];
if (exclusiveID > player->exclusiveID && priority >= player->priority) {
au_sfx_play_sound(manager, player, AU_FILE_RELATIVE(manager->sefData, *cmdList), request, priority, exclusiveID);
NEXT_POLY_TRACK;
}
} else {
NEXT_POLY_TRACK;
}
}
}
} else {
s32 i;
for (i = 0; i < trackCount; i++) {
if (*cmdList != 0) {
soundInfo = cmdList[1];
playerIndex = soundInfo & 7;
player = &manager->players[playerIndex];
priority = (soundInfo & 0x300) >> 8; // bits 8, 9
if (player->sefDataReadPos == NULL || priority >= player->priority) {
au_sfx_play_sound(manager, player, AU_FILE_RELATIVE(manager->sefData, *cmdList), request, priority, exclusiveID);
}
cmdList += 2;
}
}
}
}
}
}
}
#undef NEXT_POLY_TRACK
}
static void au_sfx_play_sound(SoundManager* manager, SoundPlayer* player, s8* readPos, SoundRequest* request, s32 priority, s32 exclusiveID) {
if (manager->state == SND_MANAGER_STATE_ENABLED) {
player->sefDataReadPos = readPos;
player->sefReadStart = readPos;
player->sfxInstrumentRef = manager->globals->defaultInstrument;
player->sfxPan = AU_PAN_MID;
player->sfxVolume = AU_MAX_VOLUME_16;
player->alternativeVolume = AU_MAX_VOLUME_16;
player->reverb = 0;
player->instrumentIndex = 0;
player->envelopePreset = 0;
player->playVelocity = AU_MAX_VOLUME_8;
player->coarseTune = 0;
player->fineTune = 0;
player->loopStartPos = NULL;
player->loopIterCount = 0;
player->delay = 1;
player->playLength = 0;
player->curSoundID = request->soundID & SOUND_ID_LOWER;
player->priority = priority;
player->exclusiveID = exclusiveID;
player->envelopCustomPressProfile = NULL;
player->changed.all = 0;
player->unused_A0 = 0;
player->randomPitch = 0;
player->randomVelocity = 0;
player->randomUnused = 0;
player->volumeLerp.current = AU_MAX_VOLUME_32;
player->volumeLerp.time = 0;
player->volumeLerp.step = 0;
player->volumeLerp.goal = 0;
player->tuneLerp.current = 0;
player->tuneLerp.time = 0;
player->tuneLerp.step = 0;
player->tuneLerp.goal = 0;
player->alternativeDataPos = NULL;
player->alternativeType = 0;
player->triggers = 0;
player->sfxParamsFlags = *player->sefDataReadPos++;
switch (player->sfxParamsFlags & SFX_PARAM_MODE_MASK) {
case SFX_PARAM_MODE_BASIC:
player->state = SND_PLAYER_STATE_INIT;
break;
case SFX_PARAM_MODE_SEQUENCE:
player->state = SND_PLAYER_STATE_INIT;
break;
case SFX_PARAM_MODE_COMPACT:
// change mode to SFX_PARAM_MODE_BASIC
player->sfxParamsFlags &= ~SFX_PARAM_MODE_MASK;
player->sfxParamsFlags |= SFX_PARAM_MODE_BASIC;
player->state = SND_PLAYER_STATE_INIT;
// populate preset 8‑byte SEF stream in cmdListOneShot:
player->cmdListOneShot[0] = player->sefDataReadPos[0]; // instrument bank
player->cmdListOneShot[1] = player->sefDataReadPos[1]; // instrument patch
// bottom 3 bits are irrelevant for volume, maps to a range 3 to 127
player->cmdListOneShot[2] = (player->sefDataReadPos[2] >> 1) | 3; // volume
// use bottom 3 bits for random pitch amount
player->cmdListOneShot[6] = player->sefDataReadPos[2] & 7; // random pitch
player->sefDataReadPos = player->cmdListOneShot;
break;
}
au_sfx_set_player_modifiers(player, request);
}
}
static void au_sfx_set_triggers(SoundManager* manager, u32 soundID) {
s32 triggers = (soundID & SOUND_ID_TRIGGER_MASK) >> 0xA;
s32 i;
for (i = 0; i < ARRAY_COUNT(manager->players); i++) {
SoundPlayer* player = &manager->players[i];
if (player->curSoundID == (soundID & SOUND_ID_LOWER)) {
player->triggers = triggers;
}
}
}
static void au_sfx_stop_by_id(SoundManager* manager, u32 soundID) {
s32 i;
for (i = 0; i < ARRAY_COUNT(manager->players); i++) {
SoundPlayer* player = &manager->players[i];
if (player->curSoundID == (soundID & SOUND_ID_LOWER)) {
player->sefDataReadPos = BlankSEFData;
player->alternativeDataPos = NULL;
player->sfxParamsFlags = SFX_PARAM_MODE_SEQUENCE;
player->state = SND_PLAYER_STATE_CONTINUE;
player->delay = 1;
player->priority = 0;
player->exclusiveID = 0;
}
}
}
static void au_sfx_stop_by_exlusive_id(SoundManager* manager, u32 exclusiveID) {
s32 i;
for (i = 0; i < ARRAY_COUNT(manager->players); i++) {
SoundPlayer* player = &manager->players[i];
if (exclusiveID == player->exclusiveID) {
player->sefDataReadPos = BlankSEFData;
player->alternativeDataPos = NULL;
player->sfxParamsFlags = SFX_PARAM_MODE_SEQUENCE;
player->state = SND_PLAYER_STATE_CONTINUE;
player->delay = 1;
player->priority = 0;
player->exclusiveID = 0;
}
}
}
static void au_sfx_set_modifiers(SoundManager* manager, SoundRequest* request) {
s32 soundID = request->soundID & SOUND_ID_LOWER;
s32 i;
for (i = 0; i < ARRAY_COUNT(manager->players); i++) {
SoundPlayer* player = &manager->players[i];
if (player->curSoundID == soundID) {
au_sfx_set_player_modifiers(player, request);
}
}
}
static void au_sfx_set_player_modifiers(SoundPlayer* player, SoundRequest* request) {
if (player->sfxParamsFlags & SFX_PARAM_LOCK_VOLUME) {
player->masterVolume = 0;
} else {
player->masterVolume = request->volume;
player->changed.volume = TRUE;
}
if (player->sfxParamsFlags & SFX_PARAM_LOCK_PAN) {
player->masterPan = 0;
} else {
player->masterPan = request->pan;
player->changed.pan = TRUE;
}
if (player->sfxParamsFlags & SFX_PARAM_LOCK_PITCH) {
player->masterPitchShift = 0;
} else {
player->masterPitchShift = request->pitchShift;
player->changed.tune = TRUE;
}
}
s16 au_sfx_manager_audio_frame_update(SoundManager* manager) {
SoundPlayer* player;
AuVoice* voice;
u32 startVoice;
u8 end;
u8 i;
// update pseudorandom number with fast 'good enough' method
manager->randomValue = (u16)manager->randomValue + (u16)manager->frameCounter;
startVoice = manager->firstVoice;
for (i = startVoice, end = startVoice + ARRAY_COUNT(manager->players); i < end; i++) {
player = &manager->players[i - manager->firstVoice];
if (player->sefDataReadPos != NULL) {
voice = &manager->globals->voices[i];
manager->curVoice = voice;
if (voice->priority <= manager->priority) {
manager->curVoiceIndex = i;
switch (player->sfxParamsFlags & SFX_PARAM_MODE_MASK) {
case SFX_PARAM_MODE_BASIC:
au_sfx_update_basic(manager, player, voice, i);
break;
case SFX_PARAM_MODE_SEQUENCE:
au_sfx_update_sequence(manager, player, voice, i);
break;
case SFX_PARAM_MODE_COMPACT:
break;
}
} else {
player->sefDataReadPos = NULL;
player->curSoundID = SOUND_NONE;
player->priority = 0;
}
}
}
return 0;
}
static void au_sfx_update_basic(SoundManager* manager, SoundPlayer* player, AuVoice* voice, u8 voiceIdx) {
s16 volume;
s32 tune;
s32 pan;
s32 a;
s32 b;
switch (player->state) {
case SND_PLAYER_STATE_CONTINUE:
if (voice->priority != manager->priority) {
player->sefDataReadPos = NULL;
player->curSoundID = SOUND_NONE;
player->priority = 0;
} else {
if (!(player->sfxParamsFlags & SFX_PARAM_LOCK_PITCH)) {
player->pitchRatio = au_compute_pitch_ratio(
((player->tuneLerp.current >> 0x10) - player->sfxInstrumentRef->keyBase) + player->masterPitchShift) * player->sfxInstrumentRef->pitchRatio;
if (voice->pitchRatio != player->pitchRatio) {
voice->syncFlags |= AU_VOICE_SYNC_FLAG_PITCH;
voice->pitchRatio = player->pitchRatio;
}
}
if (!(player->sfxParamsFlags & SFX_PARAM_LOCK_PAN) && player->masterPan != 0) {
pan = player->masterPan;
} else {
pan = player->sfxPan;
}
if (voice->pan != pan) {
voice->pan = pan;
voice->syncFlags |= AU_VOICE_SYNC_FLAG_PAN_FXMIX;
}
volume = au_sfx_get_scaled_volume(manager, player);
if (voice->clientVolume != volume) {
voice->clientVolume = volume;
voice->envelopeFlags |= AU_VOICE_ENV_FLAG_VOL_CHANGED;
}
}
break;
case SND_PLAYER_STATE_INIT:
au_SEFCmd_02_SetInstrument(manager, player); // 2 bytes
au_SEFCmd_00_SetVolume(manager, player); // 1 byte
au_SEFCmd_01_SetPan(manager, player); // 1 byte
au_SEFCmd_03_SetReverb(manager, player); // 1 byte
tune = (*player->sefDataReadPos++ & 0x7F) * 100; // 1 byte
player->randomPitch = (*player->sefDataReadPos & 0xF) * 8; // 1 byte
if (player->randomPitch != 0) {
player->tuneLerp.current = au_sfx_get_random_pitch(manager->randomValue, player->randomPitch, tune) << 0x10;
} else {
player->tuneLerp.current = tune << 0x10;
}
if (player->sfxParamsFlags & SFX_PARAM_LOCK_PITCH) {
tune = (player->tuneLerp.current >> 0x10) - player->sfxInstrumentRef->keyBase;
} else {
tune = ((player->tuneLerp.current >> 0x10) - player->sfxInstrumentRef->keyBase) + player->masterPitchShift;
}
player->pitchRatio = au_compute_pitch_ratio(tune) * player->sfxInstrumentRef->pitchRatio;
if (voice->priority <= manager->priority) {
au_reset_nonfree_voice(voice, voiceIdx);
if (!(player->sfxParamsFlags & SFX_PARAM_LOCK_PAN) && player->masterPan != 0) {
voice->pan = player->masterPan;
} else {
voice->pan = player->sfxPan;
}
voice->reverb = player->reverb;
voice->clientVolume = au_sfx_get_scaled_volume(manager, player);
voice->envelope.cmdListPress = player->envelope.cmdListPress;
voice->envelope.cmdListRelease = player->envelope.cmdListRelease;
voice->instrument = player->sfxInstrumentRef;
voice->pitchRatio = player->pitchRatio;
voice->syncFlags = AU_VOICE_SYNC_FLAG_ALL;
voice->priority = manager->priority;
voice->clientPriority = voice->priority;
voice->busID = manager->busID;
}
player->state = SND_PLAYER_STATE_CONTINUE;
break;
default:
player->sefDataReadPos = NULL;
player->curSoundID = SOUND_NONE;
player->priority = 0;
break;
}
}
static s16 au_sfx_get_scaled_volume(SoundManager* manager, SoundPlayer* player) {
s32 outVolume;
outVolume = (manager->baseVolume * player->sfxVolume) >> 15;
if (!(player->sfxParamsFlags & SFX_PARAM_LOCK_VOLUME) && (player->masterVolume != 0)) {
outVolume = (outVolume * player->masterVolume) >> 15;
}
return outVolume;
}
static void au_sfx_update_sequence(SoundManager* manager, SoundPlayer* player, AuVoice* voice, u8 voiceIdx) {
s32* var_v0_3;
s32 pitchShift;
s32 temp_a0;
u8 opcode;
u32 playLength;
s32 startedNewVoice;
void (**CmdHandlers)(SoundManager*, SoundPlayer*);
startedNewVoice = FALSE;
if (player->state == SND_PLAYER_STATE_INIT) {
player->state = SND_PLAYER_STATE_CONTINUE;
if (voice->priority == manager->priority) {
au_reset_voice(voice, voiceIdx);
}
}
if (player->alternativeDataPos != NULL) {
if (player->triggers == 1) {
player->sefDataReadPos = player->alternativeDataPos;
player->alternativeDataPos = NULL;
player->triggers = 0;
player->delay = 1;
}
}
if (player->triggers == 2) {
player->changed.volume = TRUE;
player->sfxVolume = player->alternativeVolume;
}
player->delay--;
while (player->delay == 0) {
CmdHandlers = SefCmdHandlers;
opcode = *player->sefDataReadPos++;
if (opcode < 0x80) {
if (opcode == 0) {
if (voice->priority == manager->priority) {
au_reset_voice(voice, voiceIdx);
}
player->sefDataReadPos = NULL;
player->curSoundID = SOUND_NONE;
player->priority = 0;
player->exclusiveID = 0;
return;
}
if (opcode >= 0x78) {
// long delay
player->delay = (u8)(*player->sefDataReadPos++) + ((opcode & 7) << 8) + 0x78;
} else {
// short delay
player->delay = opcode;
}
} else if (opcode < 0xD8) {
// play sound
// (opcode & 0x7F) = pitch
if (player->randomPitch != 0) {
player->tuneLerp.current = au_sfx_get_random_pitch(manager->randomValue, player->randomPitch, (opcode & 0x7F) * 100) << 0x10;
} else {
player->tuneLerp.current = ((opcode & 0x7F) * 100) << 0x10;
}
if (player->randomVelocity != 0) {
player->playVelocity = au_sfx_get_random_vol(manager->randomValue, player->randomVelocity, player->sefDataReadPos[0] & 0x7F);
} else {
player->playVelocity = player->sefDataReadPos[0] & 0x7F;
}
playLength = player->sefDataReadPos[1];
if (playLength >= 0xC0) {
playLength = player->sefDataReadPos[2] + ((playLength & 0x3F) << 8) + 0xC0;
player->sefDataReadPos += 3;
} else {
player->sefDataReadPos += 2;
}
player->playLength = playLength;
if (voice->priority <= manager->priority) {
au_reset_nonfree_voice(voice, voiceIdx);
if ((player->sfxParamsFlags & SFX_PARAM_LOCK_PAN) || (player->masterPan == 0)) {
voice->pan = player->sfxPan;
} else {
voice->pan = player->masterPan;
}
voice->reverb = player->reverb;
au_sfx_set_voice_volume(voice, manager, player);
if (player->envelopCustomPressProfile == NULL) {
voice->envelope.cmdListPress = player->envelope.cmdListPress;
voice->envelope.cmdListRelease = player->envelope.cmdListRelease;
} else {
voice->envelope.cmdListPress = player->envelopCustomPressProfile;
voice->envelope.cmdListRelease = player->envelope.cmdListRelease;
}
voice->instrument = player->sfxInstrumentRef;
voice->busID = manager->busID;
voice->priority = manager->priority;
voice->syncFlags = AU_VOICE_SYNC_FLAG_ALL;
startedNewVoice = TRUE;
voice->clientPriority = manager->priority;
player->changed.tune = TRUE;
}
} else {
s32 index = opcode - 0xE0;
CurrentSefCmdHandler = CmdHandlers[index];
CurrentSefCmdHandler(manager, player);
}
}
if (player->volumeLerp.time != 0) {
player->volumeLerp.time--;
if (player->volumeLerp.time != 0) {
player->volumeLerp.current += player->volumeLerp.step;
} else {
player->volumeLerp.current = player->volumeLerp.goal << 0x10;
}
player->changed.volume = TRUE;
}
if (!startedNewVoice) {
if (player->playLength != 0) {
player->playLength--;
if ((player->playLength == 0) && (voice->priority == manager->priority)) {
voice->envelopeFlags |= AU_VOICE_ENV_FLAG_KEY_RELEASED;
}
}
if (player->tuneLerp.time != 0) {
player->tuneLerp.time--;
if (player->tuneLerp.time != 0) {
player->tuneLerp.current += player->tuneLerp.step;
} else {
player->tuneLerp.current = player->tuneLerp.goal << 0x10;
}
player->changed.tune = TRUE;
}
if (player->changed.pan || player->changed.reverb) {
if ((player->sfxParamsFlags & SFX_PARAM_LOCK_PAN) || (player->masterPan == 0)) {
voice->pan = player->sfxPan;
} else {
voice->pan = player->masterPan;
}
voice->syncFlags |= AU_VOICE_SYNC_FLAG_PAN_FXMIX;
voice->reverb = player->reverb;
}
}
if (player->changed.volume && voice->priority == manager->priority) {
au_sfx_set_voice_volume(voice, manager, player);
voice->envelopeFlags |= AU_VOICE_ENV_FLAG_VOL_CHANGED;
}
if (player->changed.tune) {
f32 adjustedPitchRatio;
if (player->sfxParamsFlags & SFX_PARAM_LOCK_PITCH) {
pitchShift = (player->coarseTune + (s16)(player->tuneLerp.current >> 0x10)) - player->sfxInstrumentRef->keyBase;
adjustedPitchRatio = au_compute_pitch_ratio(pitchShift + player->fineTune) * player->sfxInstrumentRef->pitchRatio;
player->pitchRatio = adjustedPitchRatio;
} else {
pitchShift = ((player->coarseTune + (s16)(player->tuneLerp.current >> 0x10)) - player->sfxInstrumentRef->keyBase) + player->fineTune;
adjustedPitchRatio = au_compute_pitch_ratio(pitchShift + player->masterPitchShift) * player->sfxInstrumentRef->pitchRatio;
player->pitchRatio = adjustedPitchRatio;
}
if ((voice->priority == manager->priority) && (voice->pitchRatio != adjustedPitchRatio)) {
voice->syncFlags |= AU_VOICE_SYNC_FLAG_PITCH;
voice->pitchRatio = player->pitchRatio;
}
}
player->changed.all = 0;
}
static void au_sfx_set_voice_volume(AuVoice* voice, SoundManager* manager, SoundPlayer* player) {
s32 x = ((((manager->baseVolume
* player->sfxVolume) >> 0xF)
* player->playVelocity) >> 7)
* (player->volumeLerp.current >> 0x10) >> 0xF;
if (player->sfxParamsFlags & SFX_PARAM_LOCK_VOLUME || player->masterVolume == 0) {
voice->clientVolume = x;
} else {
voice->clientVolume = (x * player->masterVolume) >> 0xF;
}
}
/*
Uses bit masks:
1F00 = 0001 1111 0000 0000 -> 0001 1111
001C = 0000 0000 0001 1100 -> 1110 0000
*/
static u8 au_sfx_get_random_pan(s32 seed, s32 pan, s32 amplitude) {
s32 tap5, tap3, parity;
s32 lo, hi, random;
s32 retPan;
pan = pan & 0xFF;
tap5 = seed >> 5;
tap3 = seed >> 3;
parity = (tap5 + tap3) & 1;
lo = (seed >> 8) & 0x1F; // bitmask 0x1F00
hi = (seed << 3) & 0xE0; // bitmask 0x001C
random = lo + hi;
if (parity) {
retPan = pan + (((amplitude & 0xFF) * random) >> 8);
} else {
retPan = pan - (((amplitude & 0xFF) * random) >> 8);
}
if (retPan < AU_PAN_MIN) {
retPan = AU_PAN_MIN;
} else if (retPan > AU_PAN_MAX) {
retPan = AU_PAN_MAX;
}
return retPan;
}
static void au_SEFCmd_00_SetVolume(SoundManager* manager, SoundPlayer* player) {
player->sfxVolume = *player->sefDataReadPos++;
if (player->sfxVolume != 0) {
player->sfxVolume = AU_VOL_8_TO_16(player->sfxVolume);
}
player->changed.volume = TRUE;
}
static void au_SEFCmd_01_SetPan(SoundManager* manager, SoundPlayer* player) {
s32 sfxPan = *player->sefDataReadPos++;
player->changed.pan = TRUE;
player->sfxPan = sfxPan;
}
static void au_SEFCmd_02_SetInstrument(SoundManager* manager, SoundPlayer* player) {
AuFilePos buf = player->sefDataReadPos;
BankSetIndex bank = buf[0];
s32 patch = buf[1];
player->sefDataReadPos += 2;
player->instrumentIndex = patch;
player->sfxInstrumentRef = au_get_instrument(manager->globals, bank, patch, &player->envelope);
}
static void au_SEFCmd_03_SetReverb(SoundManager* manager, SoundPlayer* player) {
u8 reverb = *player->sefDataReadPos++;
if (player->sfxParamsFlags & SFX_PARAM_LOCK_REVERB) {
reverb = manager->defaultReverbAmt;
}
player->reverb = reverb;
player->changed.reverb = TRUE;
}
static void au_SEFCmd_04_SetEnvelope(SoundManager* manager, SoundPlayer* player) {
Instrument* other;
EnvelopePreset* envPreset;
u8 envelope = *player->sefDataReadPos++;
player->envelopePreset = envelope & 0x7F;
other = player->sfxInstrumentRef;
player->sfxInstrument.wavData = other->wavData;
player->sfxInstrument.wavDataLength = other->wavDataLength;
player->sfxInstrument.loopState = other->loopState;
player->sfxInstrument.loopStart = other->loopStart;
player->sfxInstrument.loopEnd = other->loopEnd;
player->sfxInstrument.loopCount = other->loopCount;
player->sfxInstrument.predictor = other->predictor;
player->sfxInstrument.codebookSize = other->codebookSize;
player->sfxInstrument.keyBase = other->keyBase;
player->sfxInstrument.pitchRatio = other->pitchRatio;
player->sfxInstrument.type = other->type;
player->sfxInstrument.useDma = other->useDma;
player->sfxInstrument.envelopes = SFXEnvelopePresets[player->envelopePreset];
player->sfxInstrumentRef = &player->sfxInstrument;
envPreset = player->sfxInstrument.envelopes;
if (envPreset != NULL && envPreset->count != 0) {
player->envelope.cmdListPress = AU_FILE_RELATIVE(envPreset, envPreset->offsets[0].offsetPress);
player->envelope.cmdListRelease = AU_FILE_RELATIVE(envPreset, envPreset->offsets[0].offsetRelease);
}
}
static void au_SEFCmd_05_CoarseTune(SoundManager* manager, SoundPlayer* player) {
player->coarseTune = (*(s8*)player->sefDataReadPos++) * 100;
}
static void au_SEFCmd_06_FineTune(SoundManager* manager, SoundPlayer* player) {
player->fineTune = *player->sefDataReadPos++;
}
static void au_SEFCmd_07_WaitForEnd(SoundManager* manager, SoundPlayer* player) {
if (manager->curVoice->priority == manager->priority) {
player->delay = 2;
player->sefDataReadPos--;
}
}
static void au_SEFCmd_08_PitchSweep(SoundManager* manager, SoundPlayer* player) {
s32 hi = player->sefDataReadPos[0];
s32 lo = player->sefDataReadPos[1];
s16 newValue = (player->sefDataReadPos[2] & 0x7F) * 100;
s16 time = lo + (hi << 8);
player->sefDataReadPos += 3;
if (time <= 0) {
time = 1;
}
player->tuneLerp.time = time;
player->tuneLerp.goal = newValue;
player->tuneLerp.step = ((newValue << 0x10) - player->tuneLerp.current) / time;
}
static void au_SEFCmd_09_StartLoop(SoundManager* manager, SoundPlayer* player) {
s32 loopIterCount = *player->sefDataReadPos++;
player->loopStartPos = player->sefDataReadPos;
player->loopIterCount = loopIterCount;
}
static void au_SEFCmd_0A_EndLoop(SoundManager* manager, SoundPlayer* player) {
if (player->loopIterCount == 0 || --player->loopIterCount != 0) {
player->sefDataReadPos = player->loopStartPos;
}
}
static void au_SEFCmd_0B_WaitForRelease(SoundManager* manager, SoundPlayer* player) {
if (player->playLength != 0) {
player->delay = 3;
player->sefDataReadPos--;
}
}
static void au_SEFCmd_0C_SetCurrentVolume(SoundManager* manager, SoundPlayer* player) {
s32 vol = *player->sefDataReadPos++;
if (vol != 0) {
vol = AU_VOL_8_TO_32(vol);
}
player->volumeLerp.current = vol;
player->changed.volume = TRUE;
}
static void au_SEFCmd_0D_VolumeRamp(SoundManager* manager, SoundPlayer* player) {
s32 hi = player->sefDataReadPos[0];
s32 lo = player->sefDataReadPos[1];
s32 newValue = player->sefDataReadPos[2];
s16 time = lo + (hi << 8);
player->sefDataReadPos += 3;
if (newValue != 0) {
newValue = AU_VOL_8_TO_16(newValue);
}
if (time <= 0) {
time = 1;
}
player->volumeLerp.time = time;
player->volumeLerp.goal = newValue;
player->volumeLerp.step = ((newValue << 0x10) - player->volumeLerp.current) / time;
}
static void au_SEFCmd_0E_SetAlternativeSound(SoundManager* manager, SoundPlayer* player) {
s32 hi = player->sefDataReadPos[1];
s32 lo = player->sefDataReadPos[2];
AuFilePos pos = AU_FILE_RELATIVE(manager->sefData, (hi << 8) + lo);
u8 type = player->sefDataReadPos[0];
player->sefDataReadPos += 3;
player->alternativeType = type;
switch (type) {
case 1:
player->alternativeDataPos = pos;
break;
case 2:
player->alternativeDataPos = pos;
break;
case 3:
player->alternativeDataPos = pos;
break;
default:
player->alternativeDataPos = NULL;
break;
}
}
static void au_SEFCmd_0F_Stop(SoundManager* manager, SoundPlayer* player) {
AuVoice* voice = manager->curVoice;
if (voice->priority == manager->priority) {
au_reset_voice(voice, manager->curVoiceIndex);
}
}
static void au_SEFCmd_10_Jump(SoundManager* manager, SoundPlayer* player) {
AuFilePos buf = player->sefDataReadPos;
player->sefReadStart = &buf[2];
player->sefDataReadPos = AU_FILE_RELATIVE(manager->sefData, (buf[0] << 8) + buf[1]);
}
static void au_SEFCmd_11_Restart(SoundManager* manager, SoundPlayer* player) {
player->sefDataReadPos = player->sefReadStart;
}
static void au_SEFCmd_12_NOP(SoundManager* manager, SoundPlayer* player) {
}
static void au_SEFCmd_13_SetRandomPitch(SoundManager* manager, SoundPlayer* player) {
player->randomPitch = *player->sefDataReadPos++;
}
static void au_SEFCmd_14_SetRandomVelocity(SoundManager* manager, SoundPlayer* player) {
player->randomVelocity = *player->sefDataReadPos++;
}
static void au_SEFCmd_15_SetRandomUnused(SoundManager* manager, SoundPlayer* player) {
player->randomUnused = *player->sefDataReadPos++;
}
static void au_SEFCmd_16_SetEnvelopePress(SoundManager* manager, SoundPlayer* player) {
AuFilePos buf = player->sefDataReadPos;
s32 offset = (buf[0] << 8) + buf[1];
if (offset != 0) {
player->envelopCustomPressProfile = AU_FILE_RELATIVE(manager->sefData, offset);
} else {
player->envelopCustomPressProfile = NULL;
}
player->sefDataReadPos = &buf[2];
}
static void au_SEFCmd_17_PlaySound(SoundManager* manager, SoundPlayer* player) {
AuFilePos buf = player->sefDataReadPos;
u32 i;
for (i = 0; i < ARRAY_COUNT(manager->customCmdList); i++) {
if (manager->customCmdList[i].data[0] == 0) {
manager->customCmdList[i].data[0] = buf[1] + (buf[0] << 8);
manager->customCmdList[i].data[1] = buf[3] + (buf[2] << 8);
break;
}
}
player->sefDataReadPos = &buf[4];
}
static void au_SEFCmd_18_SetAlternativeVolume(SoundManager* manager, SoundPlayer* player) {
player->alternativeVolume = *player->sefDataReadPos++;
if (player->alternativeVolume != 0) {
player->alternativeVolume = AU_VOL_8_TO_16(player->alternativeVolume);
}
}
/*
Uses bit masks:
780 = 0111 1000 0000 -> 0000 1111
01E = 0000 0001 1110 -> 1111 0000
*/
static s32 au_sfx_get_random_pitch(s32 seed, s32 amplitude, s32 pitch) {
s32 lo = (seed >> 7) & 0xF; // bitmask 0x780
s32 hi = (seed << 3) & 0xF0; // bitmask 0x01E
s32 random = (lo + hi);
s32 tap5 = (seed >> 5);
s32 tap2 = (seed >> 2);
s32 parity = (tap5 + tap2) & 1;
if (parity) {
return pitch + ((amplitude * 5 * random) >> 8);
} else {
return pitch - ((amplitude * 5 * random) >> 8);
}
}
/*
Uses bit masks:
CC = 1100 1100 -> 0011 0011
13 = 0001 0011 -> 0100 1100
*/
static u8 au_sfx_get_random_vol(s32 seed, s32 amplitude, s32 volume) {
s32 lo = (seed & 0xCC) >> 2;
s32 hi = (seed & 0x13) << 2;
s32 random = lo + hi;
return volume * (0x8000 - (amplitude * random)) >> 0xF;
}
static void au_sfx_reset_players(SoundManager* manager) {
s32 i;
for (i = 0; i < ARRAY_COUNT(manager->players); i++) {
SoundPlayer* player = &manager->players[i];
player->sefDataReadPos = BlankSEFData;
player->alternativeDataPos = NULL;
player->sfxParamsFlags = SFX_PARAM_MODE_SEQUENCE;
player->state = SND_PLAYER_STATE_CONTINUE;
player->delay = 1;
player->priority = 0;
player->exclusiveID = 0;
}
}