diff --git a/asm/musyx/runtime/hw_dspctrl.s b/asm/musyx/runtime/hw_dspctrl.s index 010eb6a8..c1872d1f 100644 --- a/asm/musyx/runtime/hw_dspctrl.s +++ b/asm/musyx/runtime/hw_dspctrl.s @@ -1118,7 +1118,7 @@ salBuildCommandList: /* 803ABACC 003A8A2C 7C 08 02 A6 */ mflr r0 /* 803ABAD0 003A8A30 90 01 00 84 */ stw r0, 0x84(r1) /* 803ABAD4 003A8A34 39 61 00 80 */ addi r11, r1, 0x80 -/* 803ABAD8 003A8A38 4B FD DF 71 */ bl __save_gpr +/* 803ABAD8 003A8A38 4B FD DF 71 */ bl _savegpr_14 /* 803ABADC 003A8A3C 80 AD AF 40 */ lwz r5, dspCmdList@sda21(r13) /* 803ABAE0 003A8A40 38 00 00 00 */ li r0, 0 /* 803ABAE4 003A8A44 90 0D AF 24 */ stw r0, dspCmdLastLoad@sda21(r13) @@ -3427,7 +3427,7 @@ lbl_803ADC58: /* 803ADC60 003AABC0 7C 83 00 50 */ subf r4, r3, r0 /* 803ADC64 003AABC4 4B FD 0E D9 */ bl DCStoreRangeNoSync /* 803ADC68 003AABC8 39 61 00 80 */ addi r11, r1, 0x80 -/* 803ADC6C 003AABCC 4B FD BE 29 */ bl __restore_gpr +/* 803ADC6C 003AABCC 4B FD BE 29 */ bl _restgpr_14 /* 803ADC70 003AABD0 80 01 00 84 */ lwz r0, 0x84(r1) /* 803ADC74 003AABD4 7C 08 03 A6 */ mtlr r0 /* 803ADC78 003AABD8 38 21 00 80 */ addi r1, r1, 0x80 diff --git a/asm/musyx/runtime/stream.s b/asm/musyx/runtime/stream.s index d08e7c27..507f84a3 100644 --- a/asm/musyx/runtime/stream.s +++ b/asm/musyx/runtime/stream.s @@ -1502,7 +1502,7 @@ sndStreamAllocStereo: /* 8039DD28 0039AC88 7C 08 02 A6 */ mflr r0 /* 8039DD2C 0039AC8C 90 01 00 74 */ stw r0, 0x74(r1) /* 8039DD30 0039AC90 39 61 00 70 */ addi r11, r1, 0x70 -/* 8039DD34 0039AC94 4B FE BD 15 */ bl __save_gpr +/* 8039DD34 0039AC94 4B FE BD 15 */ bl _savegpr_14 /* 8039DD38 0039AC98 55 29 06 3E */ clrlwi r9, r9, 0x18 /* 8039DD3C 0039AC9C 8A E1 00 7B */ lbz r23, 0x7b(r1) /* 8039DD40 0039ACA0 38 09 FF C0 */ addi r0, r9, -64 @@ -1686,7 +1686,7 @@ lbl_8039DFC4: /* 8039DFC8 0039AF28 7E 23 8B 78 */ mr r3, r17 lbl_8039DFCC: /* 8039DFCC 0039AF2C 39 61 00 70 */ addi r11, r1, 0x70 -/* 8039DFD0 0039AF30 4B FE BA C5 */ bl __restore_gpr +/* 8039DFD0 0039AF30 4B FE BA C5 */ bl _restgpr_14 /* 8039DFD4 0039AF34 80 01 00 74 */ lwz r0, 0x74(r1) /* 8039DFD8 0039AF38 7C 08 03 A6 */ mtlr r0 /* 8039DFDC 0039AF3C 38 21 00 70 */ addi r1, r1, 0x70 diff --git a/asm/musyx/runtime/synth.s b/asm/musyx/runtime/synth.s index 643f03e3..a8e86326 100644 --- a/asm/musyx/runtime/synth.s +++ b/asm/musyx/runtime/synth.s @@ -416,7 +416,7 @@ StartLayer: /* 80399034 00395F94 7C 08 02 A6 */ mflr r0 /* 80399038 00395F98 90 01 00 84 */ stw r0, 0x84(r1) /* 8039903C 00395F9C 39 61 00 80 */ addi r11, r1, 0x80 -/* 80399040 00395FA0 4B FF 0A 09 */ bl __save_gpr +/* 80399040 00395FA0 4B FF 0A 09 */ bl _savegpr_14 /* 80399044 00395FA4 8A 61 00 8B */ lbz r19, 0x8b(r1) /* 80399048 00395FA8 7C 8F 23 78 */ mr r15, r4 /* 8039904C 00395FAC 8A 81 00 8F */ lbz r20, 0x8f(r1) @@ -672,7 +672,7 @@ lbl_803993C8: lbl_803993D4: /* 803993D4 00396334 7D C3 73 78 */ mr r3, r14 /* 803993D8 00396338 39 61 00 80 */ addi r11, r1, 0x80 -/* 803993DC 0039633C 4B FF 06 B9 */ bl __restore_gpr +/* 803993DC 0039633C 4B FF 06 B9 */ bl _restgpr_14 /* 803993E0 00396340 80 01 00 84 */ lwz r0, 0x84(r1) /* 803993E4 00396344 7C 08 03 A6 */ mtlr r0 /* 803993E8 00396348 38 21 00 80 */ addi r1, r1, 0x80 @@ -1668,7 +1668,7 @@ lbl_8039A1D4: /* 8039A1E4 00397144 38 21 00 30 */ addi r1, r1, 0x30 /* 8039A1E8 00397148 4E 80 00 20 */ blr -ZeroOffsetHandler: +.fn ZeroOffsetHandler, local /* 8039A1EC 0039714C 94 21 FF 60 */ stwu r1, -0xa0(r1) /* 8039A1F0 00397150 7C 08 02 A6 */ mflr r0 /* 8039A1F4 00397154 90 01 00 A4 */ stw r0, 0xa4(r1) @@ -2141,6 +2141,7 @@ lbl_8039A8A8: /* 8039A8DC 0039783C 7C 08 03 A6 */ mtlr r0 /* 8039A8E0 00397840 38 21 00 A0 */ addi r1, r1, 0xa0 /* 8039A8E4 00397844 4E 80 00 20 */ blr +.endfn ZeroOffsetHandler .global synthAddJob synthAddJob: @@ -3356,8 +3357,7 @@ synthSetMusicVolumeType: /* 8039B98C 003988EC 98 83 00 2D */ stb r4, 0x2d(r3) /* 8039B990 003988F0 4E 80 00 20 */ blr - -synthHWMessageHandler: +.fn synthHWMessageHandler, local /* 8039B994 003988F4 94 21 FF E0 */ stwu r1, -0x20(r1) /* 8039B998 003988F8 7C 08 02 A6 */ mflr r0 /* 8039B99C 003988FC 2C 03 00 02 */ cmpwi r3, 2 @@ -3416,6 +3416,7 @@ lbl_8039BA44: /* 8039BA58 003989B8 7C 08 03 A6 */ mtlr r0 /* 8039BA5C 003989BC 38 21 00 20 */ addi r1, r1, 0x20 /* 8039BA60 003989C0 4E 80 00 20 */ blr +.endfn synthHWMessageHandler .global synthInit synthInit: @@ -3423,7 +3424,7 @@ synthInit: /* 8039BA68 003989C8 7C 08 02 A6 */ mflr r0 /* 8039BA6C 003989CC 90 01 00 54 */ stw r0, 0x54(r1) /* 8039BA70 003989D0 39 61 00 50 */ addi r11, r1, 0x50 -/* 8039BA74 003989D4 4B FE DF D5 */ bl __save_gpr +/* 8039BA74 003989D4 4B FE DF D5 */ bl _savegpr_14 /* 8039BA78 003989D8 7C 8F 23 78 */ mr r15, r4 /* 8039BA7C 003989DC 3C 80 80 55 */ lis r4, synthTicksPerSecond@ha /* 8039BA80 003989E0 3B E4 FE 10 */ addi r31, r4, synthTicksPerSecond@l @@ -3768,7 +3769,7 @@ lbl_8039BFA0: /* 8039BFB8 00398F18 38 63 B9 94 */ addi r3, r3, synthHWMessageHandler@l /* 8039BFBC 00398F1C 48 01 72 01 */ bl hwSetMesgCallback /* 8039BFC0 00398F20 39 61 00 50 */ addi r11, r1, 0x50 -/* 8039BFC4 00398F24 4B FE DA D1 */ bl __restore_gpr +/* 8039BFC4 00398F24 4B FE DA D1 */ bl _restgpr_14 /* 8039BFC8 00398F28 80 01 00 54 */ lwz r0, 0x54(r1) /* 8039BFCC 00398F2C 7C 08 03 A6 */ mtlr r0 /* 8039BFD0 00398F30 38 21 00 50 */ addi r1, r1, 0x50 diff --git a/asm/musyx/runtime/synthmacros.s b/asm/musyx/runtime/synthmacros.s index a8c92267..486d2f9b 100644 --- a/asm/musyx/runtime/synthmacros.s +++ b/asm/musyx/runtime/synthmacros.s @@ -5524,7 +5524,7 @@ macStart: /* 803A71C4 003A4124 7C 08 02 A6 */ mflr r0 /* 803A71C8 003A4128 90 01 00 54 */ stw r0, 0x54(r1) /* 803A71CC 003A412C 39 61 00 50 */ addi r11, r1, 0x50 -/* 803A71D0 003A4130 4B FE 28 79 */ bl __save_gpr +/* 803A71D0 003A4130 4B FE 28 79 */ bl _savegpr_14 /* 803A71D4 003A4134 8A C1 00 5B */ lbz r22, 0x5b(r1) /* 803A71D8 003A4138 7C 6F 1B 78 */ mr r15, r3 /* 803A71DC 003A413C 8A E1 00 5F */ lbz r23, 0x5f(r1) @@ -5807,7 +5807,7 @@ lbl_803A75DC: /* 803A75DC 003A453C 38 60 FF FF */ li r3, -1 lbl_803A75E0: /* 803A75E0 003A4540 39 61 00 50 */ addi r11, r1, 0x50 -/* 803A75E4 003A4544 4B FE 24 B1 */ bl __restore_gpr +/* 803A75E4 003A4544 4B FE 24 B1 */ bl _restgpr_14 /* 803A75E8 003A4548 80 01 00 54 */ lwz r0, 0x54(r1) /* 803A75EC 003A454C 7C 08 03 A6 */ mtlr r0 /* 803A75F0 003A4550 38 21 00 50 */ addi r1, r1, 0x50 diff --git a/configure.py b/configure.py index efc9117d..e542e97f 100755 --- a/configure.py +++ b/configure.py @@ -935,7 +935,7 @@ LIBS = [ "host": False, "objects": [ ["musyx/runtime/seq", True], - ["musyx/runtime/synth", False], + ["musyx/runtime/synth", True], ["musyx/runtime/seq_api", True], ["musyx/runtime/snd_synthapi", True, {"add_to_all": False}], ["musyx/runtime/stream", False], diff --git a/include/musyx/musyx_priv.h b/include/musyx/musyx_priv.h index f33f9f44..ae6a2908 100644 --- a/include/musyx/musyx_priv.h +++ b/include/musyx/musyx_priv.h @@ -384,10 +384,11 @@ typedef struct CALLSTACK { } CALLSTACK; typedef struct SYNTH_QUEUE { - struct SYNTH_QUEUE* next; - struct SYNTH_QUEUE* prev; - u8 voice; - u8 jobTabIndex; + // total size: 0xC + struct SYNTH_QUEUE* next; // offset 0x0, size 0x4 + struct SYNTH_QUEUE* prev; // offset 0x4, size 0x4 + u8 voice; // offset 0x8, size 0x1 + u8 jobTabIndex; // offset 0x9, size 0x1 } SYNTH_QUEUE; typedef enum { @@ -883,13 +884,17 @@ u32 seqStartPlay(PAGE* norm, PAGE* drum, MIDISETUP* midiSetup, u32* song, SND_PL u8 studio, u16 sgid); u32 seqGetPrivateId(u32 seqId); void streamInit(); /* extern */ -void vsInit(); /* extern */ + +void vsInit(); /* extern */ +u32 vsSampleStartNotify(u8 voice); +void vsSampleEndNotify(u32 pubID); + void hwExit(); void dataExit(); void s3dInit(u32); /* extern */ void s3dKillEmitterByFXID(FX_TAB* fxTab, u32 num); void s3dExit(); -void synthInit(u32, u8); /* extern */ +void synthInit(u32 mixFrq, u32 numVoices); void synthSetBpm(u32 pbm, u8 set, u8 section); void synthFXCloneMidiSetup(SYNTH_VOICE* dest, SYNTH_VOICE* src); void synthSetMusicVolumeType(u8 vGroup, u8 type); @@ -907,6 +912,7 @@ void voiceSetLastStarted(SYNTH_VOICE* svoice); void voiceResetLastStarted(SYNTH_VOICE* svoice); void voiceInitLastStarted(); s32 voiceKillSound(u32 voiceid); +void voiceKill(u32 vi); extern u64 synthRealTime; u32 synthGetTicksPerSecond(SYNTH_VOICE* svoice); @@ -931,6 +937,8 @@ u32 hwGlobalActivity(); void hwSetAUXProcessingCallbacks(u8 studio, SND_AUX_CALLBACK auxA, void* userA, SND_AUX_CALLBACK auxB, void* userB); u8 hwInitStream(u32 len); +u8 hwGetTimeOffset(); +u32 hwGetVirtualSampleID(u32 v); s16 varGet(SYNTH_VOICE* svoice, u32 ctrl, u8 index); u32 sndGetPitch(u8 key, u32 sInfo); @@ -1030,9 +1038,8 @@ void streamOutputModeChanged(); u8 inpTranslateExCtrl(u8 ctrl); void inpSetGlobalMIDIDirtyFlag(u8 chan, u8 midiSet, s32 flag); void inpAddCtrl(CTRL_DEST* dest, u8 ctrl, s32 scale, u8 comb, u32 isVar); -void inpSetMidiCtrl(unsigned char ctrl, unsigned char channel, unsigned char set, - unsigned char value); -void inpSetMidiCtrl14(unsigned char ctrl, unsigned char channel, unsigned char set, u16 value); +void inpSetMidiCtrl(u8 ctrl, u8 channel, u8 set, u8 value); +void inpSetMidiCtrl14(u8 ctrl, u8 channel, u8 set, u16 value); void inpSetExCtrl(SYNTH_VOICE* svoice, u8 ctrl, s16 v); CHANNEL_DEFAULTS* inpGetChannelDefaults(u8 midi, u8 midiSet); extern CTRL_DEST inpAuxA[8][4]; @@ -1047,6 +1054,18 @@ void inpResetMidiCtrl(u8 ch, u8 set, u32 coldReset); void inpResetChannelDefaults(u8 midi, u8 midiSet); u16 inpGetPitchBend(SYNTH_VOICE* svoice); u16 inpGetDoppler(SYNTH_VOICE* svoice); +u16 inpGetTremolo(SYNTH_VOICE* svoice); +u16 inpGetPanning(SYNTH_VOICE* svoice); +u16 inpGetSurPanning(SYNTH_VOICE* svoice); +u16 inpGetVolume(SYNTH_VOICE* svoice); +u16 inpGetReverb(SYNTH_VOICE* svoice); +u16 inpGetPreAuxA(SYNTH_VOICE* svoice); +u16 inpGetPostAuxA(SYNTH_VOICE* svoice); +u16 inpGetPreAuxB(SYNTH_VOICE* svoice); +u16 inpGetPostAuxB(SYNTH_VOICE* svoice); +u16 inpGetPedal(SYNTH_VOICE* svoice); +u16 inpGetAuxA(u8 studio, u8 index, u8 midi, u8 midiSet); +u16 inpGetAuxB(u8 studio, u8 index, u8 midi, u8 midiSet); /* TODO: Figure out what `unk` is */ void hwSetSRCType(u32 v, u8 salSRCType); @@ -1056,6 +1075,9 @@ bool hwAddInput(u8 studio, SND_STUDIO_INPUT* in_desc); bool hwRemoveInput(u8 studio, SND_STUDIO_INPUT* in_desc); void hwChangeStudio(u32 v, u8 studio); void hwDisableHRTF(); +void hwStart(u32 v, u8 studio); +void hwKeyOff(u32 v); +void hwFrameDone(); extern u32 dspHRTFOn; @@ -1075,7 +1097,11 @@ u8 aramAllocateStreamBuffer(u32 len); u32 macStart(u16 macid, u8 priority, u8 maxVoices, u16 allocId, u8 key, u8 vol, u8 panning, u8 midi, u8 midiSet, u8 section, u16 step, u16 trackid, u8 new_vid, u8 vGroup, u8 studio, u32 itd); +void macHandle(u32 deltaTime); void macMakeInactive(SYNTH_VOICE* svoice, MAC_STATE); +void macSetPedalState(SYNTH_VOICE* svoice, u32 state); +void macSetExternalKeyoff(SYNTH_VOICE* svoice); +void macSampleEndNotify(SYNTH_VOICE* sv); void sndProfUpdateMisc(SND_PROFILE_INFO* info); void sndProfResetPMC(SND_PROFILE_DATA* info); @@ -1086,6 +1112,7 @@ u32 vidMakeNew(SYNTH_VOICE* svoice, u32 isMaster); u32 vidMakeRoot(SYNTH_VOICE* svoice); u32 adsrHandleLowPrecision(ADSR_VARS* adsr, u16* adsr_start, u16* adsr_delta); +bool adsrRelease(ADSR_VARS* adsr); #ifdef __cplusplus } diff --git a/include/musyx/synth.h b/include/musyx/synth.h index 4c82d366..42311990 100644 --- a/include/musyx/synth.h +++ b/include/musyx/synth.h @@ -26,7 +26,7 @@ SND_VOICEID synthFXStart(u16 fid, u8 vol, u8 pan, u8 studio, u32 itd); void synthVolume(u8 volume, u16 time, u8 vGroup, u8 seqMode, u32 seqId); u32 synthStartSound(u16 id, u8 prio, u8 max, #if MUSY_VERSION >= MUSY_VERSION_CHECK(2, 0, 0) - unsigned long sourceID, + u32 sourceID, #endif u8 key, u8 vol, u8 panning, u8 midi, u8 midiSet, u8 section, u16 step, u16 trackid, u8 vGroup, s16 prioOffset, u8 studio, u32 itd); diff --git a/src/musyx/runtime/synth.c b/src/musyx/runtime/synth.c index caac7bca..bf39e0b8 100644 --- a/src/musyx/runtime/synth.c +++ b/src/musyx/runtime/synth.c @@ -1,4 +1,5 @@ +#include "musyx/synth.h" #include "musyx/musyx_priv.h" static u32 synthTicksPerSecond[9][16]; @@ -91,7 +92,11 @@ static u32 do_voice_portamento(u8 key, u8 midi, u8 midiSet, u32 isMaster, u32* r vid = SND_ID_ERROR; for (i = 0, sv = synthVoice; i < synthInfo.voiceNum; ++i, ++sv) { - if (sv->id != SND_ID_ERROR && sv->midi == midi && sv->midiSet == midiSet) { + if ( +#if MUSY_VERSION >= MUSY_VERSION_CHECK(1, 5, 4) + sv->block == 0 && +#endif + sv->id != SND_ID_ERROR && sv->midi == midi && sv->midiSet == midiSet) { if ((sv->cFlags & 2) != 0) { legatoVoiceIsStarting = TRUE; } @@ -137,7 +142,7 @@ static u32 do_voice_portamento(u8 key, u8 midi, u8 midiSet, u32 isMaster, u32* r static u32 check_portamento(u8 key, u8 midi, u8 midiSet, u32 newVID, u32* vid) { u32 rejected; // r1+0x14 - if (inpGetMidiCtrl(65, midi, midiSet) > 8064) { + if (inpGetMidiCtrl(65 /* TODO SND_MIDICTRL_? */, midi, midiSet) > 8064) { *vid = do_voice_portamento(key & 0x7f, midi, midiSet, newVID, &rejected); return !rejected; } @@ -177,7 +182,13 @@ static u32 StartLayer(u16 layerID, s16 prio, u8 maxVoices, u16 allocId, u8 key, k = CLAMP(k, 0, 127); if ((l->id & 0xC000) == 0) { - if (check_portamento(k, midi, midiSet, vidFlag, &new_id)) { + if (check_portamento(k, midi, midiSet, +#if MUSY_VERSION >= MUSY_VERSION_CHECK(1, 5, 4) + 0, +#else + vidFlag, +#endif + &new_id)) { if (new_id != 0xFFFFFFFF) { goto apply_new_id; } else { @@ -231,8 +242,14 @@ static u32 StartLayer(u16 layerID, s16 prio, u8 maxVoices, u16 allocId, u8 key, } id = new_id; while (synthVoice[id & 0xff].child != SND_ID_ERROR) { +#if MUSY_VERSION >= MUSY_VERSION_CHECK(1, 5, 4) + synthVoice[id & 0xff].block = 1; +#endif id = synthVoice[id & 0xff].child; } +#if MUSY_VERSION >= MUSY_VERSION_CHECK(1, 5, 4) + synthVoice[id & 0xff].block = 1; +#endif } } @@ -291,30 +308,67 @@ static u32 StartKeymap(u16 keymapID, s16 prio, u8 maxVoices, u16 allocId, u8 key return SND_ID_ERROR; } -u32 synthStartSound(u16 id, u8 prio, u8 max, u8 key, u8 vol, u8 panning, u8 midi, u8 midiSet, - u8 section, u16 step, u16 trackid, u8 vGroup, s16 prioOffset, u8 studio, - u32 itd) { - u32 vid; // r1+0x34 +#if MUSY_VERSION >= MUSY_VERSION_CHECK(1, 5, 4) +static void unblockAllAllocatedVoices(u32 vid) { + u32 id; // r31 + id = vidGetInternalId(vid); +#line 501 + MUSY_ASSERT_MSG(id != SND_ID_ERROR, "*** Alloc unblock: ID is illegal"); + while (id != SND_ID_ERROR) { + synthVoice[id & 0xff].block = 0; + id = synthVoice[id & 0xff].child; + } +} +#endif + +u32 synthStartSound(u16 id, u8 prio, u8 max, +#if MUSY_VERSION >= MUSY_VERSION_CHECK(2, 0, 0) + u32 sourceID, +#endif + u8 key, u8 vol, u8 panning, u8 midi, u8 midiSet, u8 section, u16 step, + u16 trackid, u8 vGroup, s16 prioOffset, u8 studio, u32 itd) { prio += prioOffset; prio = CLAMP(prio, 0, 0xff); switch (id & 0xC000) { - case 0: + case 0: { + u32 vid; // r1+0x34 if (!check_portamento(key, midi, midiSet, 1, &vid)) { - return 0xffffffff; + return SND_ID_ERROR; } - if (vid != 0xffffffff) { + if (vid != SND_ID_ERROR) { return vid; } return macStart(id, prio, max, id, key, vol, panning, midi, midiSet, section, step, trackid, 1, vGroup, studio, itd); - case 0x4000: + } + case 0x4000: { +#if MUSY_VERSION >= MUSY_VERSION_CHECK(1, 5, 4) + u32 vid = StartKeymap(id, prio, max, id, key, vol, panning, midi, midiSet, section, step, + trackid, 1, vGroup, studio, itd); + if (vid != SND_ID_ERROR) { + unblockAllAllocatedVoices(vid); + } + return vid; +#else return StartKeymap(id, prio, max, id, key, vol, panning, midi, midiSet, section, step, trackid, 1, vGroup, studio, itd); - case 0x8000: +#endif + } + case 0x8000: { +#if MUSY_VERSION >= MUSY_VERSION_CHECK(1, 5, 4) + u32 vid = StartLayer(id, prio, max, id, key, vol, panning, midi, midiSet, section, step, + trackid, 1, vGroup, studio, itd); + if (vid != SND_ID_ERROR) { + unblockAllAllocatedVoices(vid); + } + return vid; +#else return StartLayer(id, prio, max, id, key, vol, panning, midi, midiSet, section, step, trackid, 1, vGroup, studio, itd); +#endif + } default: return SND_ID_ERROR; } @@ -423,7 +477,13 @@ static void LowPrecisionHandler(u32 i) { } ccents = sv->curNote * 65536 + (sv->curDetune * 65536) / 100; - if ((sv->cFlags & 0x10010) != 0) { + if ((sv->cFlags & +#if MUSY_VERSION >= MUSY_VERSION_CHECK(1, 5, 4) + 0x10030 +#else + 0x10010 +#endif + ) != 0) { if (sv->midi != 0xff) { pbend = inpGetPitchBend(sv); sv->pbLast = pbend; @@ -446,10 +506,18 @@ static void LowPrecisionHandler(u32 i) { Modulation = inpGetModulation(sv); vrange = sv->vibKeyRange * 256 + (sv->vibCentRange * 256) / 100; if (sv->vibModAddScale != 0) { +#if MUSY_VERSION >= MUSY_VERSION_CHECK(1, 5, 4) + vrange += (sv->vibModAddScale * ((Modulation & 0x1ffff) >> 7)) >> 7; +#else vrange += (sv->vibModAddScale * ((Modulation >> 7) & 0x1ff)) >> 7; +#endif } if ((sv->cFlags & 0x4000) != 0) { +#if MUSY_VERSION >= MUSY_VERSION_CHECK(1, 5, 4) + voff = (sv->vibCurOffset * ((Modulation & 0x1ffff) >> 7)) >> 7; +#else voff = (sv->vibCurOffset * ((Modulation >> 7) & 0x1ff)) >> 7; +#endif } else { voff = sv->vibCurOffset; } @@ -493,22 +561,157 @@ static void ZeroOffsetHandler(u32 i) { SYNTH_VOICE* sv; // r31 u32 lowDeltaTime; // r26 u16 Modulation; // r25 - f32 vol; // r62 - f32 auxa; // r57 - f32 auxb; // r56 - f32 f; // r59 - f32 voiceVol; // r60 - u32 volUpdate; // r30 - f32 lfo; // r55 - f32 scale; // r63 - f32 mscale; // r54 + f32 vol; // f30 + f32 auxa; // f25 + f32 auxb; // f24 + f32 f; // f27 + f32 voiceVol; // f28 + bool volUpdate; // r30 + f32 lfo; // f23 + f32 scale; // f31 + f32 mscale; // f22 s32 pan; // r28 - f32 preVol; // r58 - f32 postVol; // r61 + f32 preVol; // f26 + f32 postVol; // f29 + + sv = &synthVoice[i]; + if (!hwIsActive(i) && sv->addr == NULL) { + goto end; + } + + lowDeltaTime = synthRealTime - sv->lastZeroCallTime; + sv->lastZeroCallTime = synthRealTime; + + if ((sv->cFlags & 0x8000) != 0) { + sv->envCurrent += sv->envDelta * (lowDeltaTime >> 8); + if (sv->envDelta < 0) { + if ((s32)sv->envTarget >= (s32)sv->envCurrent) { + sv->envCurrent = sv->envTarget; + sv->cFlags &= ~0x8000; + } + } else if ((s32)sv->envTarget <= (s32)sv->envCurrent) { + sv->envCurrent = sv->envTarget; + sv->cFlags &= ~0x8000; + } + sv->volume = sv->envCurrent; + volUpdate = TRUE; + } else { + volUpdate = (sv->cFlags & 0x100000000000) != 0; + } + + sv->cFlags &= ~0x100000000000; + + f = synthMasterFader[sv->vGroup].pauseVol * synthMasterFader[sv->vGroup].volume * + synthMasterFader[sv->fxFlag ? 22 : 21].volume; + + if (sv->track != 0xff) { + vol = f * (f32)synthTrackVolume[sv->track] * (1.f / 127.f); + } else { + vol = f; + } + + if (vol != sv->lastVolFaderScale) { + sv->lastVolFaderScale = vol; + volUpdate = TRUE; + } + + voiceVol = (f32)sv->volume * (1.f / (8192.f * 1016.f) /* 1.201479e-07 */); + + if ((sv->treScale | sv->treModAddScale) != 0) { + Modulation = inpGetModulation(sv); + lfo = (f32)(8192 - (8192 - ((s16)inpGetTremolo(sv) - 8192) >> 1)) * (1.f / 8192.f); + mscale = 1.f - (f32)Modulation * (4096 - sv->treModAddScale) * 1.490207e-08f /* 1/(8192^2)? */; + scale = (f32)sv->treScale * mscale * (1.f / 4096.f); + if (sv->treCurScale < scale) { + if ((sv->treCurScale += 0.2f) > scale) { + sv->treCurScale = scale; + } + } else if (sv->treCurScale > scale) { + if ((sv->treCurScale -= 0.2f) < scale) { + sv->treCurScale = scale; + } + } + voiceVol *= 1.f - lfo * (1.f - sv->treCurScale); + volUpdate = TRUE; + } + + if ((synthFlags & 1) == 0) { + if ((sv->cFlags & 0x200000000000) != 0 || (sv->midiDirtyFlags & 0x6) != 0) { + sv->cFlags &= ~0x200000000000; + pan = sv->panning[0] + (inpGetPanning(sv) - 8192) * 0x200; + sv->lastPan = CLAMP_INV(pan, 0, 0x7f0000); + + if ((synthFlags & 2) != 0) { + if ((sv->lastSPan = sv->panning[1] + inpGetSurPanning(sv) * 512) > 0x7f0000) { + sv->lastSPan = 0x7f0000; + } + } else { + sv->lastSPan = 0; + } + + volUpdate = TRUE; + } else if ((synthFlags & 2) == 0) { + sv->lastSPan = 0; + } + } else { + sv->lastPan = 0x400000; + sv->lastSPan = 0; + volUpdate |= (sv->cFlags & 0x200000000000) != 0; + sv->cFlags &= ~0x200000000000; + } + + if (volUpdate || (sv->midiDirtyFlags & 0xf01) != 0) { + preVol = voiceVol; + postVol = voiceVol * vol * (f32)inpGetVolume(sv) * 0.00006103888f /* 1/16384? */; + auxa = ((f32)sv->revVolOffset * (1.f / 127.f)) + + ((preVol * (f32)inpGetPreAuxA(sv) * 0.00006103888f /* 1/16384? */) + + ((f32)sv->revVolScale * + (postVol * (f32)inpGetReverb(sv) * 0.00006103888f /* 1/16384? */) * (1.f / 127.f))); + auxb = (preVol * (f32)inpGetPreAuxB(sv) * 0.00006103888f /* 1/16384? */) + + (postVol * (f32)inpGetPostAuxB(sv) * 0.00006103888f /* 1/16384? */); + sv->curOutputVolume = (u16)(postVol * 32767.f); + hwSetVolume(i, sv->volTable, postVol, sv->lastPan, sv->lastSPan, auxa, auxb); + } + + if (sv->age != 0) { + if ((s32)(sv->age -= sv->ageSpeed * lowDeltaTime) < 0) { + sv->age = 0; + } + hwSetPriority(i, sv->prio << 24 | sv->age >> 15); + } + + synthAddJob(sv, SYNTH_JOBTYPE_ZERO, (5 - hwGetTimeOffset()) * 256); + +end: + UpdateTimeMIDICtrl(sv); } static void EventHandler(u32 i) { SYNTH_VOICE* sv; // r31 + + sv = &synthVoice[i]; + if (!hwIsActive(i) && sv->addr == NULL) { + goto end; + } + + macSetPedalState(sv, inpGetPedal(sv) > 0x1f80); + + if ((sv->cFlags & 0x20) != 0) { + sv->cFlags &= ~0x20; + sv->cFlags |= 0x10; + hwStart(i, sv->studio); + } + + if ((sv->cFlags & 0x10000000090) == 0x90) { + sv->cFlags &= ~0x90; + hwKeyOff(i); + if ((sv->cFlags & 0x20000000000) != 0 && adsrRelease(&sv->pitchADSR)) { + sv->cFlags &= ~0x20000000000; + } + } + +end: + UpdateTimeMIDICtrl(sv); } static void synthInitJobQueue() { @@ -523,14 +726,68 @@ static void synthInitJobQueue() { synthJobTableIndex = 0; } -#pragma dont_inline on void synthAddJob(SYNTH_VOICE* svoice, SYNTH_JOBTYPE jobType, u32 deltaTime) { SYNTH_QUEUE* newJq; // r31 SYNTH_QUEUE** root; // r30 u8 jobTabIndex; // r29 SYNTH_JOBTAB* jobTab; // r28 + + jobTabIndex = ((deltaTime / 256) + synthJobTableIndex) & 0x1f; + jobTab = &synthJobTable[jobTabIndex]; + + switch (jobType) { + case SYNTH_JOBTYPE_LOW: + newJq = &svoice->lowPrecisionJob; + if (newJq->jobTabIndex != 0xff) { + if (newJq->jobTabIndex == jobTabIndex) { + return; + } + if (newJq->next != NULL) { + newJq->next->prev = newJq->prev; + } + if (newJq->prev != NULL) { + newJq->prev->next = newJq->next; + } else { + synthJobTable[newJq->jobTabIndex].lowPrecision = newJq->next; + } + } + root = &jobTab->lowPrecision; + break; + case SYNTH_JOBTYPE_ZERO: + newJq = &svoice->zeroOffsetJob; + if (newJq->jobTabIndex != 0xff) { + if (newJq->jobTabIndex == jobTabIndex) { + return; + } + if (newJq->next != NULL) { + newJq->next->prev = newJq->prev; + } + if (newJq->prev != NULL) { + newJq->prev->next = newJq->next; + } else { + synthJobTable[newJq->jobTabIndex].zeroOffset = newJq->next; + } + } + root = &jobTab->zeroOffset; + break; + case SYNTH_JOBTYPE_EVENT: + newJq = &svoice->eventJob; + if (newJq->jobTabIndex != 0xff) { + return; + } + root = &jobTab->event; + break; + default: + break; + } + + newJq->jobTabIndex = jobTabIndex; + if ((newJq->next = *root) != NULL) { + (*root)->prev = newJq; + } + newJq->prev = NULL; + *root = newJq; } -#pragma dont_inline reset void synthStartSynthJobHandling(SYNTH_VOICE* svoice) { svoice->lastLowCallTime = synthRealTime; @@ -572,7 +829,6 @@ void HandleVoices() { } void HandleFaderTermination(SYNTHMasterFader* smf) { - switch (smf->seqMode) { case 1: seqStop(smf->seqId); @@ -591,7 +847,65 @@ void synthHandle(u32 deltaTime) { u32 s; // r30 SYNTHMasterFader* smf; // r31 u32 testFlag; // r27 - SND_AUX_INFO info; // r1+0x18 + + if (synthInfo.numSamples == 0) { + return; + } + + macHandle(deltaTime); + HandleVoices(); + + if (hwGetTimeOffset() == 0) { + if ((synthMasterFaderActiveFlags | synthMasterFaderPauseActiveFlags) != 0) { + for (i = 0, smf = synthMasterFader, testFlag = 1; i < 32; testFlag <<= 1, ++i, ++smf) { + if ((synthMasterFaderActiveFlags & testFlag) != 0) { + smf->volume = smf->target - smf->time * (smf->target - smf->start); + if ((smf->time -= smf->deltaTime) <= 0.f) { + smf->volume = smf->target; + HandleFaderTermination(smf); + if ((synthMasterFaderActiveFlags &= ~testFlag) == 0 && + synthMasterFaderPauseActiveFlags == 0) { + break; + } + } + } + + if ((synthMasterFaderPauseActiveFlags & testFlag) != 0) { + smf->pauseVol = smf->pauseTarget - smf->pauseTime * (smf->pauseTarget - smf->pauseStart); + if ((smf->pauseTime -= smf->pauseDeltaTime) <= 0.f) { + smf->pauseVol = smf->pauseTarget; + if ((synthMasterFaderPauseActiveFlags &= ~testFlag) == 0 && + synthMasterFaderActiveFlags == 0) { + break; + } + } + } + } + } + + for (s = 0; s < 8; ++s) { + if (synthAuxAMIDI[s] != 0xff) { + SND_AUX_INFO info; // r1+0x18 + for (i = 0; i < SND_AUX_NUMPARAMETERS; ++i) { + info.data.parameterUpdate.para[i] = + inpGetAuxA(s, i, synthAuxAMIDI[s], synthAuxAMIDISet[s]); + } + synthAuxACallback[s](SND_AUX_REASON_PARAMETERUPDATE, &info, synthAuxAUser[s]); + } + + if (synthAuxBMIDI[s] != 0xff) { + SND_AUX_INFO info; // r1+0xC + for (i = 0; i < SND_AUX_NUMPARAMETERS; ++i) { + info.data.parameterUpdate.para[i] = + inpGetAuxB(s, i, synthAuxBMIDI[s], synthAuxBMIDISet[s]); + } + synthAuxBCallback[s](SND_AUX_REASON_PARAMETERUPDATE, &info, synthAuxBUser[s]); + } + } + } + + hwFrameDone(); + synthRealTime += deltaTime; } u8 synthFXGetMaxVoices(u16 fid) { @@ -616,9 +930,437 @@ u32 synthFXStart(u16 fid, u8 vol, u8 pan, u8 studio, u32 itd) { pan = fx->panning; } - v = synthStartSound(fx->macro, fx->priority, fx->maxVoices, fx->key | 0x80, vol, pan, 0xFF, - 0xFF, 0, 0, 0xFF, fx->vGroup, 0, studio, itd); + v = synthStartSound(fx->macro, fx->priority, fx->maxVoices, +#if MUSY_VERSION >= MUSY_VERSION_CHECK(2, 0, 0) + 0, // TODO +#endif + fx->key | 0x80, vol, pan, 0xFF, 0xFF, 0, 0, 0xFF, fx->vGroup, 0, studio, + itd); } return v; } + +bool synthFXSetCtrl(u32 vid, u8 ctrl, u8 value) { + u32 i; // r31 + u32 ret; // r29 + + ret = FALSE; + vid = vidGetInternalId(vid); + + while (vid != SND_ID_ERROR) { + i = vid & 0xff; + if (vid == synthVoice[i].id) { + if ((synthVoice[i].cFlags & 0x2) != 0) { + inpSetMidiCtrl(ctrl, i, synthVoice[i].setup.midiSet, value); + } else { + inpSetMidiCtrl(ctrl, i, synthVoice[i].midiSet, value); + } + + vid = synthVoice[i].child; + ret = TRUE; + } else { + return ret; + } + } + + return ret; +} + +bool synthFXSetCtrl14(u32 vid, u8 ctrl, u16 value) { + u32 i; // r31 + u32 ret; // r29 + + ret = FALSE; + vid = vidGetInternalId(vid); + + while (vid != SND_ID_ERROR) { + i = vid & 0xff; + if (vid == synthVoice[i].id) { + if ((synthVoice[i].cFlags & 0x2) != 0) { + inpSetMidiCtrl14(ctrl, i, synthVoice[i].setup.midiSet, value); + } else { + inpSetMidiCtrl14(ctrl, i, synthVoice[i].midiSet, value); + } + + vid = synthVoice[i].child; + ret = TRUE; + } else { + return ret; + } + } + + return ret; +} + +void synthFXCloneMidiSetup(SYNTH_VOICE* dest, SYNTH_VOICE* src) { + inpFXCopyCtrl(SND_MIDICTRL_VOLUME, dest, src); + inpFXCopyCtrl(SND_MIDICTRL_PANNING, dest, src); + inpFXCopyCtrl(SND_MIDICTRL_REVERB, dest, src); + inpFXCopyCtrl(SND_MIDICTRL_PITCHBEND, dest, src); + inpFXCopyCtrl(SND_MIDICTRL_DOPPLER, dest, src); +} + +bool synthFXVolume(u32 vid, u8 vol) { + u32 i; // r31 + u32 ret; // r29 + + ret = FALSE; + vid = vidGetInternalId(vid); + + while (vid != SND_ID_ERROR) { + i = vid & 0xff; + if (vid == synthVoice[i].id) { + if ((synthVoice[i].cFlags & 0x2) != 0) { + inpSetMidiCtrl(SND_MIDICTRL_VOLUME, i, synthVoice[i].setup.midiSet, vol); + } else { + inpSetMidiCtrl(SND_MIDICTRL_VOLUME, i, synthVoice[i].midiSet, vol); + } + + vid = synthVoice[i].child; + ret = TRUE; + } else { + return ret; + } + } + + return ret; +} + +u32 synthSendKeyOff(u32 voiceid) { + u32 i; // r30 + u32 ret; // r29 + + ret = FALSE; + + if (sndActive != 0) { + voiceid = vidGetInternalId(voiceid); + + while (voiceid != SND_ID_ERROR) { + i = voiceid & 0xff; + + if (voiceid == synthVoice[i].id) { + macSetExternalKeyoff(&synthVoice[i]); + ret = TRUE; + } + + voiceid = synthVoice[i].child; + } + } + + return ret; +} + +u16 synthGetVolume(u32 vid) { + u32 i; // r30 + + vid = vidGetInternalId(vid); + if (vid != SND_ID_ERROR) { + i = vid & 0xff; + if (vid == synthVoice[i].id && (synthVoice[i].cFlags & 0x2) == 0) { + return synthVoice[i].curOutputVolume; + } + } + + return 0; +} + +void SetupFader(SYNTHMasterFader* smf, u8 volume, u32 time, u8 seqMode, u32 seqId) { + smf->seqMode = seqMode; + smf->seqId = seqId; + if (time != 0) { + smf->start = smf->volume; + smf->target = (f32)volume * (1.f / 127.f); + smf->time = 1.f; + smf->deltaTime = 1280.f / (f32)time; + } else { + smf->volume = smf->target = (f32)volume * (1.f / 127.f); + if (smf->seqId != SND_ID_ERROR) { + HandleFaderTermination(smf); + } + } +} + +void synthVolume(u8 volume, u16 time, u8 vGroup, u8 seqMode, u32 seqId) { + u32 ltime; // r1+0x14 + u32 i; // r30 + u8 type; // r29 + SYNTHMasterFader* smf; // r31 + + if ((ltime = time) != 0) { + sndConvertMs(<ime); + } + + switch (vGroup) { + case SND_ALL_VOLGROUPS: + for (smf = synthMasterFader, i = 0; i < 32; ++i, ++smf) { + if (smf->type == 0 || smf->type == 1) { + SetupFader(smf, volume, ltime, seqMode, SND_ID_ERROR); + synthMasterFaderActiveFlags |= 1 << i; + } + } + return; + + case SND_USERALL_VOLGROUPS: + for (smf = synthMasterFader, i = 0; i < 32; ++i, ++smf) { + if (smf->type == 2 || smf->type == 3) { + SetupFader(smf, volume, ltime, seqMode, SND_ID_ERROR); + synthMasterFaderActiveFlags |= 1 << i; + } + } + return; + + case SND_USERMUSIC_VOLGROUPS: + type = 2; + goto setup_type; + + case SND_USERFX_VOLGROUPS: + type = 3; + goto setup_type; + + case SND_MUSIC_VOLGROUPS: + type = 0; + goto setup_type; + + case SND_FX_VOLGROUPS: + type = 1; + goto setup_type; + + setup_type: + for (smf = synthMasterFader, i = 0; i < 32; ++i, ++smf) { + if (smf->type == type) { + SetupFader(smf, volume, ltime, seqMode, SND_ID_ERROR); + synthMasterFaderActiveFlags |= 1 << i; + } + } + return; + + default: + SetupFader(&synthMasterFader[vGroup], volume, ltime, seqMode, seqId); + synthMasterFaderActiveFlags |= 1 << vGroup; + return; + } +} + +u32 synthIsFadeOutActive(u8 vGroup) { + if (synthMasterFader[vGroup].type != 4 && (synthMasterFaderActiveFlags & (1 << vGroup)) != 0 && + synthMasterFader[vGroup].start > synthMasterFader[vGroup].target) { + return TRUE; + } + return FALSE; +} + +void synthPauseVolume(u8 volume, u16 time, u8 vGroup) { + u32 i; // r30 + u32 ltime; // r1+0x10 + u8 type; // r28 + SYNTHMasterFader* smf; // r31 + + if (time == 0) { + ++time; + } + ltime = time & 0xffff; + sndConvertMs(<ime); + + switch (vGroup) { + case SND_ALL_VOLGROUPS: + for (smf = synthMasterFader, i = 0; i < 32; ++i, ++smf) { + if (synthMasterFader[i].type == 0 || synthMasterFader[i].type == 1) { + smf->pauseStart = smf->pauseVol; + smf->pauseTarget = (f32)volume * (1.f / 127.f); + smf->pauseTime = 1.f; + smf->pauseDeltaTime = 1280.f / (f32)ltime; + synthMasterFaderActiveFlags |= 1 << i; + } + } + return; + + case SND_USERALL_VOLGROUPS: + for (smf = synthMasterFader, i = 0; i < 32; ++i, ++smf) { + if (synthMasterFader[i].type == 2 || synthMasterFader[i].type == 3) { + smf->pauseStart = smf->pauseVol; + smf->pauseTarget = (f32)volume * (1.f / 127.f); + smf->pauseTime = 1.f; + smf->pauseDeltaTime = 1280.f / (f32)ltime; + synthMasterFaderActiveFlags |= 1 << i; + } + } + return; + + case SND_USERMUSIC_VOLGROUPS: + type = 2; + goto setup_type; + + case SND_USERFX_VOLGROUPS: + type = 3; + goto setup_type; + + case SND_MUSIC_VOLGROUPS: + type = 0; + goto setup_type; + + case SND_FX_VOLGROUPS: + type = 1; + goto setup_type; + + setup_type: + for (smf = synthMasterFader, i = 0; i < 32; ++i, ++smf) { + if (synthMasterFader[i].type == type) { + smf->pauseStart = smf->pauseVol; + smf->pauseTarget = (f32)volume * (1.f / 127.f); + smf->pauseTime = 1.f; + smf->pauseDeltaTime = 1280.f / (f32)ltime; + synthMasterFaderActiveFlags |= 1 << i; + } + } + return; + + default: + smf = &synthMasterFader[vGroup]; + smf->pauseStart = smf->pauseVol; + smf->pauseTarget = (f32)volume * (1.f / 127.f); + smf->pauseTime = 1.f; + smf->pauseDeltaTime = 1280.f / (f32)ltime; + synthMasterFaderActiveFlags |= 1 << vGroup; + return; + } +} + +void synthSetMusicVolumeType(u8 vGroup, u8 type) { + if (sndActive) { + synthMasterFader[vGroup].type = type; + } +} + +u32 synthHWMessageHandler(u32 mesg, u32 voiceID) { + u32 ret; // r30 + + ret = FALSE; + + switch (mesg) { + case 0: + if (synthVoice[voiceID & 0xff].block != 0) { + break; + } + vsSampleEndNotify(hwGetVirtualSampleID(voiceID & 0xff)); + if (voiceID != synthVoice[voiceID & 0xff].id) { + break; + } + macSampleEndNotify(&synthVoice[voiceID & 0xff]); + break; + + case 1: + voiceKill(voiceID & 0xff); + break; + + case 2: + ret = vsSampleStartNotify(voiceID); + break; + + case 3: + vsSampleEndNotify(hwGetVirtualSampleID(voiceID & 0xff)); + break; + + default: +#line 1975 + MUSY_ASSERT(FALSE); + break; + } + + return ret; +} + +void synthInit(u32 mixFrq, u32 numVoices) { + u32 i; // r31 + + synthRealTime = 0; + synthInfo.mixFrq = mixFrq; + synthSetBpm(120, 255, 0); + synthFlags = 0; + synthMessageCallback = NULL; + + synthVoice = salMalloc(numVoices * sizeof(SYNTH_VOICE)); + if (synthVoice == NULL) { +#line 2135 + MUSY_FATAL("Fatal: Could not allocate synthesizer voice array."); + } + memset(synthVoice, 0, numVoices * sizeof(SYNTH_VOICE)); + + for (i = 0; i < numVoices; ++i) { + synthVoice[i].id = 0xffffffff; + synthVoice[i].cFlags = 0; + synthVoice[i].age = 0; + synthVoice[i].prio = 0; + synthVoice[i].midi = 0xff; + synthVoice[i].volume = 0; + synthVoice[i].volTable = 0; + synthVoice[i].revVolScale = 128; + synthVoice[i].revVolOffset = 0; + synthVoice[i].panning[0] = synthVoice[i].panTarget[0] = 0x400000; + synthVoice[i].panning[1] = synthVoice[i].panTarget[1] = 0; + synthVoice[i].sweepOff[0] = 0; + synthVoice[i].sweepOff[1] = 0; + synthVoice[i].sweepNum[0] = 0; + synthVoice[i].sweepNum[1] = 0; + synthVoice[i].block = 0; + synthVoice[i].vGroup = 23; + synthVoice[i].keyGroup = 0; + synthVoice[i].itdMode = 1; + synthVoice[i].lfo[0].period = 0; + synthVoice[i].lfo[0].value = 0; + synthVoice[i].lfo[0].lastValue = 0x7fff; + synthVoice[i].lfo[1].period = 0; + synthVoice[i].lfo[1].value = 0; + synthVoice[i].lfo[1].lastValue = 0x7fff; + synthVoice[i].portTime = 25600; + synthVoice[i].portType = 0; + synthVoice[i].studio = 0; + synthVoice[i].lowPrecisionJob.voice = i; + synthVoice[i].lowPrecisionJob.jobTabIndex = 0xff; + synthVoice[i].zeroOffsetJob.voice = i; + synthVoice[i].zeroOffsetJob.jobTabIndex = 0xff; + synthVoice[i].eventJob.voice = i; + synthVoice[i].eventJob.jobTabIndex = 0xff; + } + + for (i = 0; i < 32; ++i) { + synthMasterFader[i].volume = 0.f; + synthMasterFader[i].pauseVol = 1.f; + synthMasterFader[i].type = 4; + } + + synthMasterFaderActiveFlags = 0; + synthMasterFaderPauseActiveFlags = 0; + synthMasterFader[31].type = 1; + + for (i = 0; i < 8; ++i) { + synthMasterFader[i + 23].type = 0; + } + + synthMasterFader[21].volume = 1.f; + synthMasterFader[22].volume = 1.f; + inpInit(0); + + for (i = 0; i < 8; ++i) { + synthAuxACallback[i] = NULL; + synthAuxAMIDI[i] = 0xff; + synthAuxBCallback[i] = NULL; + synthAuxBMIDI[i] = 0xff; + synthITDDefault[i].sfx = 0; + synthITDDefault[i].music = 0; + } + + macInit(); + vidInit(); + synthInitAllocationAids(); + + for (i = 0; i < 16; ++i) { + synthGlobalVariable[i] = 0; + } + + voiceInitLastStarted(); + synthInitJobQueue(); + hwSetMesgCallback(synthHWMessageHandler); +} + +void synthExit() { salFree(synthVoice); }