From 5096cbf77b188cfccbda53adb15b7dca09086f1e Mon Sep 17 00:00:00 2001 From: Phillip Stephens Date: Fri, 24 Feb 2023 09:42:08 -0800 Subject: [PATCH] MusyX progress --- asm/musyx/runtime/snd_midictrl.s | 4 +- asm/musyx/runtime/synth.s | 6 +- asm/musyx/runtime/synth_vsamples.s | 7 + asm/musyx/runtime/synthmacros.s | 6 +- asm/musyx/runtime/synthvoice.s | 83 +++-- configure.py | 6 +- include/musyx/assert.h | 29 +- include/musyx/musyx_priv.h | 114 ++++++- src/musyx/runtime/hw_dspctrl.c | 165 +++++----- src/musyx/runtime/snd_service.c | 24 +- src/musyx/runtime/synth.c | 297 +++++++++++++++++ src/musyx/runtime/synth_vsamples.c | 280 +++++++++++++++- src/musyx/runtime/synthvoice.c | 492 +++++++++++++++++++++++++++++ 13 files changed, 1356 insertions(+), 157 deletions(-) create mode 100644 src/musyx/runtime/synth.c create mode 100644 src/musyx/runtime/synthvoice.c diff --git a/asm/musyx/runtime/snd_midictrl.s b/asm/musyx/runtime/snd_midictrl.s index bce75048..17483260 100644 --- a/asm/musyx/runtime/snd_midictrl.s +++ b/asm/musyx/runtime/snd_midictrl.s @@ -380,7 +380,7 @@ lbl_803B13B4: /* 803B13D4 003AE334 93 63 02 14 */ stw r27, 0x214(r3) /* 803B13D8 003AE338 80 0D AE 78 */ lwz r0, synthVoice@sda21(r13) /* 803B13DC 003AE33C 7C 60 CA 14 */ add r3, r0, r25 -/* 803B13E0 003AE340 4B FE 97 01 */ bl synthkeystateupdate +/* 803B13E0 003AE340 4B FE 97 01 */ bl synthKeyStateUpdate lbl_803B13E4: /* 803B13E4 003AE344 3B 39 04 04 */ addi r25, r25, 0x404 /* 803B13E8 003AE348 3B 9C 00 01 */ addi r28, r28, 1 @@ -590,7 +590,7 @@ lbl_803B169C: /* 803B16BC 003AE61C 93 83 02 14 */ stw r28, 0x214(r3) /* 803B16C0 003AE620 80 0D AE 78 */ lwz r0, synthVoice@sda21(r13) /* 803B16C4 003AE624 7C 60 CA 14 */ add r3, r0, r25 -/* 803B16C8 003AE628 4B FE 94 19 */ bl synthkeystateupdate +/* 803B16C8 003AE628 4B FE 94 19 */ bl synthKeyStateUpdate lbl_803B16CC: /* 803B16CC 003AE62C 3B 39 04 04 */ addi r25, r25, 0x404 /* 803B16D0 003AE630 3B BD 00 01 */ addi r29, r29, 1 diff --git a/asm/musyx/runtime/synth.s b/asm/musyx/runtime/synth.s index c51ab736..acb2d44e 100644 --- a/asm/musyx/runtime/synth.s +++ b/asm/musyx/runtime/synth.s @@ -110,7 +110,7 @@ inpAuxB: .skip 0x480 .global inpAuxA inpAuxA: - .skip 0x484 + .skip 0x480 .section .sbss .balign 8 @@ -2289,8 +2289,8 @@ synthForceLowPrecisionUpdate: /* 8039AAD8 00397A38 38 21 00 10 */ addi r1, r1, 0x10 /* 8039AADC 00397A3C 4E 80 00 20 */ blr -.global synthkeystateupdate -synthkeystateupdate: +.global synthKeyStateUpdate +synthKeyStateUpdate: /* 8039AAE0 00397A40 94 21 FF F0 */ stwu r1, -0x10(r1) /* 8039AAE4 00397A44 7C 08 02 A6 */ mflr r0 /* 8039AAE8 00397A48 38 80 00 02 */ li r4, 2 diff --git a/asm/musyx/runtime/synth_vsamples.s b/asm/musyx/runtime/synth_vsamples.s index a6d7729e..262d5a46 100644 --- a/asm/musyx/runtime/synth_vsamples.s +++ b/asm/musyx/runtime/synth_vsamples.s @@ -1,5 +1,12 @@ .include "macros.inc" +.section .bss +.balign 8 + +.global vs +vs: + .skip 0x950 + .section .text, "ax" .global vsInit diff --git a/asm/musyx/runtime/synthmacros.s b/asm/musyx/runtime/synthmacros.s index 12dc3985..108d025a 100644 --- a/asm/musyx/runtime/synthmacros.s +++ b/asm/musyx/runtime/synthmacros.s @@ -1020,7 +1020,7 @@ lbl_803A3308: /* 803A330C 003A026C 7F C3 F3 78 */ mr r3, r30 /* 803A3310 003A0270 60 00 00 20 */ ori r0, r0, 0x20 /* 803A3314 003A0274 90 1E 01 18 */ stw r0, 0x118(r30) -/* 803A3318 003A0278 4B FF 77 C9 */ bl synthkeystateupdate +/* 803A3318 003A0278 4B FF 77 C9 */ bl synthKeyStateUpdate lbl_803A331C: /* 803A331C 003A027C 80 01 00 24 */ lwz r0, 0x24(r1) /* 803A3320 003A0280 83 E1 00 1C */ lwz r31, 0x1c(r1) @@ -4331,7 +4331,7 @@ lbl_803A6150: /* 803A6154 003A30B4 7F E3 FB 78 */ mr r3, r31 /* 803A6158 003A30B8 60 00 00 80 */ ori r0, r0, 0x80 /* 803A615C 003A30BC 90 1F 01 18 */ stw r0, 0x118(r31) -/* 803A6160 003A30C0 4B FF 49 81 */ bl synthkeystateupdate +/* 803A6160 003A30C0 4B FF 49 81 */ bl synthKeyStateUpdate /* 803A6164 003A30C4 48 00 09 A0 */ b lbl_803A6B04 .global lbl_803A6168 lbl_803A6168: @@ -5633,7 +5633,7 @@ lbl_803A7240: /* 803A724C 003A41AC 7F 84 E3 78 */ mr r4, r28 /* 803A7250 003A41B0 54 06 0F FE */ srwi r6, r0, 0x1f /* 803A7254 003A41B4 7E 25 8B 78 */ mr r5, r17 -/* 803A7258 003A41B8 48 00 0B ED */ bl voiceAllocateFind +/* 803A7258 003A41B8 48 00 0B ED */ bl voiceAllocate /* 803A725C 003A41BC 7C 7D 1B 78 */ mr r29, r3 /* 803A7260 003A41C0 3C 1D 00 01 */ addis r0, r29, 1 /* 803A7264 003A41C4 28 00 FF FF */ cmplwi r0, 0xffff diff --git a/asm/musyx/runtime/synthvoice.s b/asm/musyx/runtime/synthvoice.s index 5bc36fe4..7da90fa0 100644 --- a/asm/musyx/runtime/synthvoice.s +++ b/asm/musyx/runtime/synthvoice.s @@ -2,45 +2,68 @@ .section .bss .balign 8 -.global vidList -vidList: - .skip 0xF00 -.global synth_last_fxstarted -synth_last_fxstarted: +.obj vidList, local + .skip 0x800 +.endobj vidList + +.obj voicePrioSortVoices + .skip 0x100 +.endobj voicePrioSortVoices + +.obj voicePrioSortVoicesRoot + .skip 0x100 +.endobj voicePrioSortVoicesRoot + +.obj voicePrioSortRootList + .skip 0x400 +.endobj voicePrioSortRootList + +.obj voiceList + .skip 0x100 +.endobj voiceList + +.obj synth_last_fxstarted, local .skip 0x40 -.global synth_last_started -synth_last_started: +.endobj synth_last_fxstarted + +.obj synth_last_started, local .skip 0x80 -.global vs -vs: - .skip 0x950 +.endobj synth_last_started .section .sbss .balign 8 -.global vidFree -vidFree: + +.obj vidFree, local .skip 0x4 -.global vidRoot -vidRoot: +.endobj vidFree + +.obj vidRoot, local .skip 0x4 -.global vidCurrentId -vidCurrentId: +.endobj vidRoot + +.obj vidCurrentId, local .skip 0x4 -.global voicePrioSortRootListRoot -voicePrioSortRootListRoot: +.endobj vidCurrentId + +.obj voicePrioSortRootListRoot .skip 0x2 -.global voiceMusicRunning -voiceMusicRunning: +.endobj voicePrioSortRootListRoot + +.obj voiceMusicRunning .skip 0x1 -.global voiceFxRunning -voiceFxRunning: +.endobj voiceMusicRunning + +.obj voiceFxRunning .skip 0x1 -.global voiceListInsert -voiceListInsert: +.endobj voiceFxRunning + +.obj voiceListInsert .skip 0x1 -.global voiceListRoot -voiceListRoot: - .skip 0x7 +.endobj voiceListInsert + +.obj voiceListRoot + .skip 0x1 +.endobj voiceListRoot .section .text, "ax" @@ -637,8 +660,8 @@ lbl_803A7E30: /* 803A7E3C 003A4D9C 38 21 00 10 */ addi r1, r1, 0x10 /* 803A7E40 003A4DA0 4E 80 00 20 */ blr -.global voiceAllocateFind -voiceAllocateFind: +.global voiceAllocate +voiceAllocate: /* 803A7E44 003A4DA4 94 21 FF D0 */ stwu r1, -0x30(r1) /* 803A7E48 003A4DA8 7C 08 02 A6 */ mflr r0 /* 803A7E4C 003A4DAC 90 01 00 34 */ stw r0, 0x34(r1) @@ -1282,7 +1305,7 @@ voiceBlock: /* 803A8728 003A5688 93 C1 00 18 */ stw r30, 0x18(r1) /* 803A872C 003A568C 93 A1 00 14 */ stw r29, 0x14(r1) /* 803A8730 003A5690 7C 7D 1B 78 */ mr r29, r3 -/* 803A8734 003A5694 4B FF F7 11 */ bl voiceAllocateFind +/* 803A8734 003A5694 4B FF F7 11 */ bl voiceAllocate /* 803A8738 003A5698 7C 7E 1B 78 */ mr r30, r3 /* 803A873C 003A569C 3C 1E 00 01 */ addis r0, r30, 1 /* 803A8740 003A56A0 28 00 FF FF */ cmplwi r0, 0xffff diff --git a/configure.py b/configure.py index b3b3ef51..2309f976 100755 --- a/configure.py +++ b/configure.py @@ -920,19 +920,19 @@ LIBS = [ { "lib": "musyx", #"mw_version": "1.2.5", - #"cflags": "-nodefaults -fp hard -O0 -maxerrors 1 -enum int -str reuse -nosyspath -i include -i libc -use_lmw_stmw on -inline off", + #"cflags": "-proc gecko -fp hard -nodefaults -nosyspath -i include -i libc -g -sym on -D_DEBUG=1 -enum int -use_lmw_stmw on", "mw_version": "1.3.2", "cflags": "$cflags_musyx", "host": False, "objects": [ "musyx/runtime/seq", - "musyx/runtime/synth", + ["musyx/runtime/synth", False], ["musyx/runtime/seq_api", True], ["musyx/runtime/snd_synthapi", True, {"add_to_all": False}], ["musyx/runtime/stream", False], "musyx/runtime/synthdata", "musyx/runtime/synthmacros", - "musyx/runtime/synthvoice", + ["musyx/runtime/synthvoice", False], ["musyx/runtime/synth_ac", True], "musyx/runtime/synth_adsr", ["musyx/runtime/synth_vsamples", False], diff --git a/include/musyx/assert.h b/include/musyx/assert.h index 0796ef94..b5f5b76d 100644 --- a/include/musyx/assert.h +++ b/include/musyx/assert.h @@ -2,15 +2,16 @@ #define _MUSYX_ASSERT extern void OSPanic(const char* file, int line, const char* msg, ...); +extern void OSReport(const char* msg, ...); #ifndef ASSERT #ifdef _DEBUG -#define ASSERT(cond) \ - do { \ - if (!cond) { \ - OSPanic(__FILE__, __LINE__, "Failed assertion " #cond); \ - } \ - } while (0) +#define ASSERT(cond) \ + do { \ + if (!(cond)) { \ + OSPanic(__FILE__, __LINE__, "Failed assertion " #cond); \ + } \ + } while(0) #else #define ASSERT(cond) #endif @@ -18,21 +19,21 @@ extern void OSPanic(const char* file, int line, const char* msg, ...); #ifndef ASSERT_MSG #ifdef _DEBUG -#define ASSERT_MSG(cond, msg) \ - do { \ - if (!cond) { \ - OSPanic(__FILE__, __LINE__, msg); \ - } \ - } while (0) +#define ASSERT_MSG(cond, msg) \ + do { \ + if (!(cond)) { \ + OSPanic(__FILE__, __LINE__, msg); \ + } \ + } while(0) #else #define ASSERT_MSG(cond, msg) #endif #endif #ifndef MUSY_DEBUG -#if _DEBUG +#ifdef _DEBUG #define MUSY_DEBUG OSReport -#else +#else #define MUSY_DEBUG #endif #endif diff --git a/include/musyx/musyx_priv.h b/include/musyx/musyx_priv.h index 23a41c53..d8931189 100644 --- a/include/musyx/musyx_priv.h +++ b/include/musyx/musyx_priv.h @@ -15,6 +15,24 @@ typedef struct SND_STUDIO_INPUT { unsigned char srcStudio; // offset 0x3, size 0x1 } SND_STUDIO_INPUT; +typedef struct SYNTH_VOICELIST { + // total size: 0x4 + u8 prev; // offset 0x0, size 0x1 + u8 next; // offset 0x1, size 0x1 + u16 user; // offset 0x2, size 0x2 +} SYNTH_VOICELIST; +extern SYNTH_VOICELIST voicePrioSortVoices[64]; +extern u8 voicePrioSortVoicesRoot[256]; +extern SYNTH_VOICELIST voicePrioSortVoices[64]; + +typedef struct SYNTH_ROOTLIST { + // total size: 0x4 + unsigned short next; // offset 0x0, size 0x2 + unsigned short prev; // offset 0x2, size 0x2 +} SYNTH_ROOTLIST; + +extern SYNTH_ROOTLIST voicePrioSortRootList[256]; + typedef struct synthInfo { u32 mixFrq; u32 numSamples; @@ -287,6 +305,19 @@ typedef struct SYNTH_QUEUE { u8 jobTabIndex; } SYNTH_QUEUE; +typedef enum { + SYNTH_JOBTYPE_LOW = 0, + SYNTH_JOBTYPE_ZERO = 1, + SYNTH_JOBTYPE_EVENT = 2, +} SYNTH_JOBTYPE; + +typedef struct { + // total size: 0xC + SYNTH_QUEUE* lowPrecision; // offset 0x0, size 0x4 + SYNTH_QUEUE* event; // offset 0x4, size 0x4 + SYNTH_QUEUE* zeroOffset; // offset 0x8, size 0x4 +} SYNTH_JOBTAB; + typedef struct SYNTH_LFO { u32 time; u32 period; @@ -294,6 +325,23 @@ typedef struct SYNTH_LFO { s16 lastValue; } SYNTH_LFO; +typedef struct SYNTHMasterFader { + // total size: 0x30 + float volume; // offset 0x0, size 0x4 + float target; // offset 0x4, size 0x4 + float start; // offset 0x8, size 0x4 + float time; // offset 0xC, size 0x4 + float deltaTime; // offset 0x10, size 0x4 + float pauseVol; // offset 0x14, size 0x4 + float pauseTarget; // offset 0x18, size 0x4 + float pauseStart; // offset 0x1C, size 0x4 + float pauseTime; // offset 0x20, size 0x4 + float pauseDeltaTime; // offset 0x24, size 0x4 + unsigned long seqId; // offset 0x28, size 0x4 + unsigned char seqMode; // offset 0x2C, size 0x1 + unsigned char type; // offset 0x2D, size 0x1 +} SYNTHMasterFader; + typedef struct CTRL_SOURCE { u8 midiCtrl; u8 combine; @@ -377,7 +425,7 @@ typedef struct SYNTH_VOICE { u32 parent; u32 id; VID_LIST* vidList; - VID_LIST* vidListMaster; + VID_LIST* vidMasterList; u16 allocId; u16 macroId; u8 keyGroup; @@ -473,8 +521,36 @@ typedef struct SYNTH_VOICE { long mesgQueue[4]; // offset 0x3F0, size 0x10 u16 curOutputVolume; // offset 0x400, size 0x2 } SYNTH_VOICE; + +typedef struct synthITDInfo { + // total size: 0x2 + unsigned char music; // offset 0x0, size 0x1 + unsigned char sfx; // offset 0x1, size 0x1 +} synthITDInfo; + #pragma pop +typedef struct LAYER { + // total size: 0xC + unsigned short id; // offset 0x0, size 0x2 + unsigned char keyLow; // offset 0x2, size 0x1 + unsigned char keyHigh; // offset 0x3, size 0x1 + signed char transpose; // offset 0x4, size 0x1 + unsigned char volume; // offset 0x5, size 0x1 + signed short prioOffset; // offset 0x6, size 0x2 + unsigned char panning; // offset 0x8, size 0x1 + unsigned char reserved[3]; // offset 0x9, size 0x3 +} LAYER; + +typedef struct KEYMAP { + // total size: 0x8 + unsigned short id; // offset 0x0, size 0x2 + signed char transpose; // offset 0x2, size 0x1 + unsigned char panning; // offset 0x3, size 0x1 + signed short prioOffset; // offset 0x4, size 0x2 + unsigned char reserved[2]; // offset 0x6, size 0x2 +} KEYMAP; + typedef struct SAL_VOLINFO { // total size: 0x24 float volL; // offset 0x0, size 0x4 @@ -584,8 +660,27 @@ extern SYNTH_VOICE* synthVoice; extern DSPvoice* dspVoice; typedef s32 (*SND_COMPARE)(u16*, u8*); -void dataInit(u32, s32); /* extern */ -void dataInitStack(); /* extern */ +typedef struct FX_TAB { + // total size: 0xA + unsigned short id; // offset 0x0, size 0x2 + unsigned short macro; // offset 0x2, size 0x2 + unsigned char maxVoices; // offset 0x4, size 0x1 + unsigned char priority; // offset 0x5, size 0x1 + unsigned char volume; // offset 0x6, size 0x1 + unsigned char panning; // offset 0x7, size 0x1 + unsigned char key; // offset 0x8, size 0x1 + unsigned char vGroup; // offset 0x9, size 0x1 +} FX_TAB; +typedef struct FX_DATA { + // total size: 0xE + unsigned short num; // offset 0x0, size 0x2 + unsigned short reserverd; // offset 0x2, size 0x2 + struct FX_TAB fx[1]; // offset 0x4, size 0xA +} FX_DATA; + +void dataInit(u32, s32); /* extern */ +void dataInitStack(); /* extern */ +FX_TAB* dataGetFX(u16 fid); s32 hwInit(u32* frq, u16 numVoices, u16 numStudios, u32 flags); /* extern */ void hwEnableIrq(); void hwDisableIrq(); @@ -598,20 +693,21 @@ void hwExit(); void dataExit(); void s3dExit(); void synthExit(); -u32 synthGetTicksPerSecond(u32 seconds); +u32 synthGetTicksPerSecond(SYNTH_VOICE* svoice); u16 sndRand(void); -s16 sndSin(u32 __x); +s16 sndSin(u16 angle); u8* sndBSearch(u16* key, u8* subTab, s32 mainTab, s32 len, SND_COMPARE cmp); void sndConvertMs(u32* time); -void sndConvertTicks(u32* out, u32 seconds); +void sndConvertTicks(u32* out, SYNTH_VOICE* svoice); u32 sndConvert2Ms(u32 time); void hwActivateStudio(unsigned char studio, unsigned long isMaster, SND_STUDIO_TYPE type); void hwDeactivateStudio(u8); u32 hwIsActive(u32); +u32 sndGetPitch(u8 key, u32 sInfo); extern SND_HOOKS salHooks; extern u8 sndActive; -extern s8 synthIdleWaitActive; +extern u8 synthIdleWaitActive; extern SynthInfo synthInfo; typedef s32 (*SND_MESSAGE_CALLBACK)(u32, u32); typedef void (*SND_SOME_CALLBACK)(); @@ -662,7 +758,7 @@ typedef struct SND_STREAM_INFO { } SND_STREAM_INFO; void streamOutputModeChanged(); - +unsigned short inpGetMidiCtrl(unsigned char ctrl, unsigned char channel, unsigned char set); /* TODO: Figure out what `unk` is */ void hwSetSRCType(u32 v, u8 salSRCType); void hwSetITDMode(u32 v, u8 mode); @@ -687,6 +783,8 @@ void aramUploadData(void* mram, unsigned long aram, unsigned long len, unsigned void aramFreeStreamBuffer(u8 id); void* aramStoreData(void* src, unsigned long len); void aramRemoveData(void* aram, unsigned long len); + +void macMakeInactive(SYNTH_VOICE* svoice, MAC_STATE); #ifdef __cplusplus } #endif diff --git a/src/musyx/runtime/hw_dspctrl.c b/src/musyx/runtime/hw_dspctrl.c index 98b92175..ab5303b9 100644 --- a/src/musyx/runtime/hw_dspctrl.c +++ b/src/musyx/runtime/hw_dspctrl.c @@ -1,17 +1,11 @@ #include "musyx/assert.h" #include "musyx/musyx_priv.h" -u16 dspSRCCycles[3][3] = { - {2990, 2990, 1115}, - {3300, 3300, 1115}, - {3700, 3700, 1115}, -}; - -static const u16 dspMixerCycles[32] = { - 1470, 2940, 2940, 4410, 2230, 4460, 4460, 6690, 2470, 4940, 4940, 7410, 3735, 7470, 7470, 11205, - 2940, 3386, 2940, 3386, 2940, 3386, 2940, 3386, 4940, 5687, 4940, 5687, 4940, 5687, 4940, 5687, -}; +#ifdef _DEBUG +static u32 dbgActiveVoicesMax = 0; +#endif +extern u8 salAuxFrame; DSPstudioinfo dspStudio[8]; static u32 dspARAMZeroBuffer = 0; u16* dspCmdLastLoad = NULL; @@ -36,26 +30,23 @@ u32 salInitDspCtrl(u8 numVoices, u8 numStudios, u32 defaultStudioDPL2) { salNumVoices = numVoices; salMaxStudioNum = numStudios; +#line 66 ASSERT(salMaxStudioNum <= SAL_MAX_STUDIONUM); dspARAMZeroBuffer = aramGetZeroBuffer(); - dspCmdList = salMalloc(1024 * sizeof(u16)); - if (dspCmdList != NULL) { - // OSReport("Allocated dspCmdList.\n\n"); - dspSurround = salMalloc(160 * sizeof(long)); - if (dspSurround != NULL) { - // OSReport("Allocated surround buffer.\n\n"); + if ((dspCmdList = salMalloc(1024 * sizeof(u16))) != NULL) { + MUSY_DEBUG("Allocated dspCmdList.\n\n"); + if ((dspSurround = salMalloc(160 * sizeof(long))) != NULL) { + MUSY_DEBUG("Allocated surround buffer.\n\n"); memset(dspSurround, 0, 160 * sizeof(long)); DCFlushRange(dspSurround, 160 * sizeof(long)); - dspVoice = salMalloc(salNumVoices * sizeof(DSPvoice)); - if (dspVoice != NULL) { - // OSReport("Allocated HW voice array.\n\n"); - dspITDBuffer = salMalloc(salNumVoices * 64); - if (dspITDBuffer != NULL) { - // OSReport("Allocated ITD buffers for voice array.\n\n"); + if ((dspVoice = salMalloc(salNumVoices * sizeof(DSPvoice))) != NULL) { + MUSY_DEBUG("Allocated HW voice array.\n\n"); + if ((dspITDBuffer = salMalloc(salNumVoices * 64)) != NULL) { + MUSY_DEBUG("Allocated ITD buffers for voice array.\n\n"); DCInvalidateRange(dspITDBuffer, salNumVoices * 64); itdPtr = (u32)dspITDBuffer; for (i = 0; i < salNumVoices; ++i) { - // OSReport("Initializing voice %d...\n", i); + MUSY_DEBUG("Initializing voice %d...\n", i); dspVoice[i].state = 0; dspVoice[i].postBreak = 0; dspVoice[i].startupBreak = 0; @@ -82,20 +73,16 @@ u32 salInitDspCtrl(u8 numVoices, u8 numStudios, u32 defaultStudioDPL2) { } } - // OSReport("All voices initialized.\n\n"); + MUSY_DEBUG("All voices initialized.\n\n"); for (i = 0; i < salMaxStudioNum; ++i) { - // OSReport("Initializing studio %d...\n", i); + MUSY_DEBUG("Initializing studio %d...\n", i); dspStudio[i].state = 0; - itdPtr = (u32)salMalloc(sizeof(_SPB)); - dspStudio[i].spb = (void*)itdPtr; - if ((void*)itdPtr == NULL) { + if ((dspStudio[i].spb = (_SPB*)salMalloc(sizeof(_SPB))) == NULL) { return FALSE; } - itdPtr = (u32)salMalloc(0x3c00); - dspStudio[i].main[0] = (void*)itdPtr; - if ((void*)itdPtr == NULL) { + if ((dspStudio[i].main[0] = (void*)salMalloc(0x3c00)) == NULL) { return FALSE; } @@ -109,22 +96,19 @@ u32 salInitDspCtrl(u8 numVoices, u8 numStudios, u32 defaultStudioDPL2) { dspStudio[i].auxB[1] = dspStudio[i].auxB[0] + 0x1e0; dspStudio[i].auxB[2] = dspStudio[i].auxB[1] + 0x1e0; memset(dspStudio[i].spb, 0, sizeof(_SPB)); - dspStudio[i].hostDPopSum.s = 0; - dspStudio[i].hostDPopSum.r = 0; - dspStudio[i].hostDPopSum.l = 0; - dspStudio[i].hostDPopSum.sA = 0; - dspStudio[i].hostDPopSum.rA = 0; - dspStudio[i].hostDPopSum.lA = 0; - dspStudio[i].hostDPopSum.sB = 0; - dspStudio[i].hostDPopSum.rB = 0; - dspStudio[i].hostDPopSum.lB = 0; + dspStudio[i].hostDPopSum.l = dspStudio[i].hostDPopSum.r = dspStudio[i].hostDPopSum.s = + 0; + dspStudio[i].hostDPopSum.lA = dspStudio[i].hostDPopSum.rA = + dspStudio[i].hostDPopSum.sA = 0; + dspStudio[i].hostDPopSum.lB = dspStudio[i].hostDPopSum.rB = + dspStudio[i].hostDPopSum.sB = 0; DCFlushRangeNoSync(dspStudio[i].spb, sizeof(_SPB)); } - // OSReport("All studios are initialized.\n\n"); - salActivateStudio(0, 1, defaultStudioDPL2 != FALSE); - // OSReport("Default studio is active.\n\n"); - dspHrtfHistoryBuffer = salMalloc(0x100); - if (dspHrtfHistoryBuffer == NULL) { + MUSY_DEBUG("All studios are initialized.\n\n"); + salActivateStudio( + 0, 1, defaultStudioDPL2 != FALSE ? SND_STUDIO_TYPE_RESERVED0 : SND_STUDIO_TYPE_STD); + MUSY_DEBUG("Default studio is active.\n\n"); + if ((dspHrtfHistoryBuffer = salMalloc(0x100)) == NULL) { return FALSE; } @@ -168,15 +152,13 @@ void salActivateStudio(u8 studio, u32 isMaster, SND_STUDIO_TYPE type) { memset(dspStudio[studio].main[0], 0, 0x3c00); DCFlushRangeNoSync(dspStudio[studio].main[0], 0x3c00); memset(dspStudio[studio].spb, 0, sizeof(_SPB)); - dspStudio[studio].hostDPopSum.s = 0; - dspStudio[studio].hostDPopSum.r = 0; - dspStudio[studio].hostDPopSum.l = 0; - dspStudio[studio].hostDPopSum.sA = 0; - dspStudio[studio].hostDPopSum.rA = 0; - dspStudio[studio].hostDPopSum.lA = 0; - dspStudio[studio].hostDPopSum.sB = 0; - dspStudio[studio].hostDPopSum.rB = 0; - dspStudio[studio].hostDPopSum.lB = 0; + dspStudio[studio].hostDPopSum.l = dspStudio[studio].hostDPopSum.r = + dspStudio[studio].hostDPopSum.s = 0; + dspStudio[studio].hostDPopSum.lA = dspStudio[studio].hostDPopSum.rA = + dspStudio[studio].hostDPopSum.sA = 0; + dspStudio[studio].hostDPopSum.lB = dspStudio[studio].hostDPopSum.rB = + dspStudio[studio].hostDPopSum.sB = 0; + DCFlushRangeNoSync(dspStudio[studio].spb, sizeof(_SPB)); memset(dspStudio[studio].auxA[0], 0, 0x780); DCFlushRangeNoSync(dspStudio[studio].auxA[0], 0x780); @@ -188,10 +170,20 @@ void salActivateStudio(u8 studio, u32 isMaster, SND_STUDIO_TYPE type) { dspStudio[studio].isMaster = isMaster; dspStudio[studio].numInputs = 0; dspStudio[studio].type = type; - dspStudio[studio].auxBHandler = NULL; - dspStudio[studio].auxAHandler = NULL; + dspStudio[studio].auxAHandler = dspStudio[studio].auxBHandler = NULL; } +u16 dspSRCCycles[3][3] = { + {2990, 2990, 1115}, + {3300, 3300, 1115}, + {3700, 3700, 1115}, +}; + +static const u16 dspMixerCycles[32] = { + 1470, 2940, 2940, 4410, 2230, 4460, 4460, 6690, 2470, 4940, 4940, 7410, 3735, 7470, 7470, 11205, + 2940, 3386, 2940, 3386, 2940, 3386, 2940, 3386, 4940, 5687, 4940, 5687, 4940, 5687, 4940, 5687, +}; + void salDeactivateStudio(u8 studio) { dspStudio[studio].state = 0; } u32 salCheckVolErrorAndResetDelta(u16* dsp_vol, u16* dsp_delta, u16* last_vol, u16 targetVol, @@ -201,9 +193,9 @@ u32 salCheckVolErrorAndResetDelta(u16* dsp_vol, u16* dsp_delta, u16* last_vol, u if (targetVol != *last_vol) { d = (s16)targetVol - (s16)*last_vol; - if (d >= 32 && d < 160) { - d = d >> 5; - if (d < 5) { + if ((s16)d >= 32 && (s16)d < 160) { + d = (s16)d >> 5; + if ((s16)d < 5) { resetFlags[d] |= resetMask; } @@ -212,19 +204,18 @@ u32 salCheckVolErrorAndResetDelta(u16* dsp_vol, u16* dsp_delta, u16* last_vol, u return 1; } - if (-32 >= d && -160 < d) { - d = -d >> 5; + if (-32 >= (s16)d && -160 < (s16)d) { + d = -(s16)d >> 5; if (d < 5) { resetFlags[d] |= resetMask; } *dsp_delta = 0xFFFF; - *last_vol -= d * 32; + *last_vol -= (s16)d * 32; return 1; } - if (targetVol == 0 && d > -32) { - *last_vol = 0; - *dsp_vol = 0; + if (targetVol == 0 && (s16)d > -32) { + *dsp_vol = *last_vol = 0; } } @@ -351,16 +342,13 @@ u32 salSynthSendMessage(DSPvoice* dsp_vptr, u32 mesg) { } void salActivateVoice(DSPvoice* dsp_vptr, u8 studio) { - DSPvoice* tmp; if (dsp_vptr->state != 0) { salDeactivateVoice(dsp_vptr); dsp_vptr->changed[0] |= 0x20; } dsp_vptr->postBreak = 0; - tmp = dspStudio[studio].voiceRoot; - dsp_vptr->next = tmp; - if (tmp != NULL) { + if ((dsp_vptr->next = dspStudio[studio].voiceRoot) != NULL) { dsp_vptr->next->prev = dsp_vptr; } @@ -390,7 +378,6 @@ void salDeactivateVoice(DSPvoice* dsp_vptr) { } void salReconnectVoice(DSPvoice* dsp_vptr, u8 studio) { - DSPvoice* tmp; if (dsp_vptr->state != 0) { if (dsp_vptr->prev != NULL) { dsp_vptr->prev->next = dsp_vptr->next; @@ -402,9 +389,7 @@ void salReconnectVoice(DSPvoice* dsp_vptr, u8 studio) { dsp_vptr->next->prev = dsp_vptr->prev; } - tmp = dspStudio[studio].voiceRoot; - dsp_vptr->next = tmp; - if (tmp != NULL) { + if ((dsp_vptr->next = dspStudio[studio].voiceRoot) != NULL) { dsp_vptr->next->prev = dsp_vptr; } @@ -437,7 +422,7 @@ unsigned long salRemoveStudioInput(DSPstudioinfo* stp, SND_STUDIO_INPUT* desc) { long i; // r31 i = 0; - while(i <= stp->numInputs) { + while (i < stp->numInputs) { if (stp->in[i].desc == desc) { break; } @@ -452,3 +437,35 @@ unsigned long salRemoveStudioInput(DSPstudioinfo* stp, SND_STUDIO_INPUT* desc) { --stp->numInputs; return 1; } + +void salHandleAuxProcessing() { + u8 st; // r29 + long* work; // r30 + DSPstudioinfo* sp; // r31 + SND_AUX_INFO info; // r1+0x8 + sp = &dspStudio[0]; + for (st = 0; st < salMaxStudioNum; ++st, ++sp) { + + if (sp->state != 1) { + continue; + } + + if (sp->auxAHandler != NULL) { + work = sp->auxA[(salAuxFrame + 2) % 3]; + info.data.bufferUpdate.left = work; + info.data.bufferUpdate.right = work + 0xa0; + info.data.bufferUpdate.surround = work + 0x140; + sp->auxAHandler(0, &info, sp->auxAUser); + DCFlushRangeNoSync(work, 0x780); + } + + if (sp->type == 0 && sp->auxBHandler != 0) { + work = sp->auxB[(salAuxFrame + 2) % 3]; + info.data.bufferUpdate.left = work; + info.data.bufferUpdate.right = work + 0xa0; + info.data.bufferUpdate.surround = work + 0x140; + sp->auxBHandler(0, &info, sp->auxBUser); + DCFlushRangeNoSync(work, 0x780); + } + } +} diff --git a/src/musyx/runtime/snd_service.c b/src/musyx/runtime/snd_service.c index a13522e4..f3a0653a 100644 --- a/src/musyx/runtime/snd_service.c +++ b/src/musyx/runtime/snd_service.c @@ -71,7 +71,7 @@ s16 sndSintab[1024] = { 4094, 4094, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, }; -#define SINTAB_ELEMENT_COUNT (sizeof(sndSintab) / sizeof(short) - 1) +#define SINTAB_ELEMENT_COUNT (sizeof(sndSintab) / sizeof(u16) - 1) u32 last_rnd = 1; @@ -80,18 +80,18 @@ u16 sndRand(void) { return last_rnd >> 6; } -s16 sndSin(u32 __x) { - const u16 x = __x & 0xFFF; - if (x < 1024) { - return (int)sndSintab[x]; +s16 sndSin(u16 angle) { + angle &= 0xFFF; + if (angle < 1024) { + return (s16)sndSintab[angle]; } - if (x < 2048) { - return (int)sndSintab[SINTAB_ELEMENT_COUNT - (x & SINTAB_ELEMENT_COUNT)]; + if (angle < 2048) { + return (s16)sndSintab[SINTAB_ELEMENT_COUNT - (angle & SINTAB_ELEMENT_COUNT)]; } - if (x < 3072) { - return (int)-sndSintab[x & SINTAB_ELEMENT_COUNT]; + if (angle < 3072) { + return (s16)-sndSintab[angle & SINTAB_ELEMENT_COUNT]; } - return -sndSintab[SINTAB_ELEMENT_COUNT - (x & SINTAB_ELEMENT_COUNT)]; + return -sndSintab[SINTAB_ELEMENT_COUNT - (angle & SINTAB_ELEMENT_COUNT)]; } u8* sndBSearch(u16* key, u8* subTab, s32 mainTab, s32 len, SND_COMPARE cmp) { @@ -124,8 +124,8 @@ u8* sndBSearch(u16* key, u8* subTab, s32 mainTab, s32 len, SND_COMPARE cmp) { void sndConvertMs(u32* time) { *time = *time * 256; } -void sndConvertTicks(u32* out, u32 seconds) { - *out = (((*out << 16) / synthGetTicksPerSecond(seconds)) * 1000) / 32; +void sndConvertTicks(u32* out, SYNTH_VOICE* svoice) { + *out = (((*out << 16) / synthGetTicksPerSecond(svoice)) * 1000) / 32; } u32 sndConvert2Ms(u32 time) { return time / 256; } diff --git a/src/musyx/runtime/synth.c b/src/musyx/runtime/synth.c new file mode 100644 index 00000000..620c7087 --- /dev/null +++ b/src/musyx/runtime/synth.c @@ -0,0 +1,297 @@ +#include "musyx/assert.h" +#include "musyx/musyx_priv.h" + +static u32 synthTicksPerSecond[9][16]; +static SYNTH_JOBTAB synthJobTable[32]; +CTRL_DEST inpAuxA[8][4]; +CTRL_DEST inpAuxB[8][4]; +long synthGlobalVariable[16]; +synthITDInfo synthITDDefault[8]; +void* synthAuxBUser[8]; +SND_AUX_CALLBACK synthAuxBCallback[8]; +void* synthAuxAUser[8]; +SND_AUX_CALLBACK synthAuxACallback[8]; +u8 synthTrackVolume[64]; +SYNTHMasterFader synthMasterFader[32]; +SynthInfo synthInfo; + +u8 sndActive = 0; +static u8 synthJobTableIndex = 0; +u64 synthRealTime; +u8 synthIdleWaitActive; +SND_MESSAGE_CALLBACK synthMessageCallback; +SYNTH_VOICE* synthVoice; +u32 synthFlags; +u32 synthMasterFaderActiveFlags; +u32 synthMasterFaderPauseActiveFlags; +u8 synthAuxAMIDI[8]; +u8 synthAuxAMIDISet[8]; +u8 synthAuxBMIDI[8]; +u8 synthAuxBMIDISet[8]; +u8 synthTrackVolume[64]; // size: 0x40 + +void synthSetBpm(u32 bpm, u8 set, u8 section) { + if (set == 0xFF) { + set = 8; + } + synthTicksPerSecond[set][section] = ((bpm << 3) * 1536) / 240; +} + +u32 synthGetTicksPerSecond(SYNTH_VOICE* svoice) { + return synthTicksPerSecond[svoice->midiSet == 0xFF ? 8 : svoice->midiSet][svoice->section]; +} + +static u32 apply_portamento(SYNTH_VOICE* svoice, u32 ccents, u32 deltaTime) { + u32 old_portCurPitch; // r31 + + if ((svoice->cFlags & 0x400) != 0 && (int)((svoice->portDuration - svoice->portTime) >> 8) > 0) { + + old_portCurPitch = svoice->portCurPitch; + svoice->portCurPitch += (int)deltaTime * ((int)(ccents - svoice->portCurPitch) >> 8) / + (int)((svoice->portDuration - svoice->portTime) >> 8); + + if ((old_portCurPitch < ccents && svoice->portCurPitch < ccents) || + (old_portCurPitch > ccents && (svoice->portCurPitch > ccents))) { + ccents = svoice->portCurPitch; + svoice->portTime += deltaTime; + } else { + svoice->portTime = svoice->portDuration; + } + } + return ccents; +} + +void synthInitPortamento(SYNTH_VOICE* svoice) { + if (svoice->cFlags & 0x20000) { + return; + } + + if (svoice->portType == 1) { + if (!(svoice->cFlags & 0x1000)) { + svoice->portTime = 0; + } else { + svoice->portTime = svoice->portDuration; + } + } else { + svoice->portTime = svoice->portDuration; + } + + svoice->portCurPitch = svoice->lastNote << 16; +} + +static u32 do_voice_portamento(u8 key, u8 midi, u8 midiSet, u32 isMaster, u32* rejected) { + u32 i; // r30 + u32 vid; // r29 + u32 id; // r27 + SYNTH_VOICE* sv; // r31 + SYNTH_VOICE* last_sv; // r28 + u32 legatoVoiceIsStarting; // r26 +} + +static u32 check_portamento(u8 key, u8 midi, u8 midiSet, u32 newVID, u32* vid) { + u32 rejected; // r1+0x14 + + if (inpGetMidiCtrl(65, midi, midiSet) > 8064) { + *vid = do_voice_portamento(key & 0x7f, midi, midiSet, newVID, &rejected); + return !rejected; + } + *vid = 0xFFFFFFFF; + return 1; +} + +static u32 StartLayer(u16 layerID, s16 prio, u8 maxVoices, u16 allocId, u8 key, u8 vol, u8 panning, + u8 midi, u8 midiSet, u8 section, u16 step, u16 trackid, u32 vidFlag, + u8 vGroup, u8 studio, u32 itd) { + u16 n; // r1+0x38 + u32 vid; // r26 + u32 new_id; // r1+0x34 + u32 id; // r27 + LAYER* l; // r31 + long p; // r30 + long k; // r29 + u8 v; // r25 + u8 mKey; // r24 +} + +static u32 StartKeymap(u16 keymapID, s16 prio, u8 maxVoices, u16 allocId, u8 key, u8 vol, + u8 panning, u8 midi, u8 midiSet, u8 section, u16 step, u16 trackid, + u32 vidFlag, u8 vGroup, u8 studio, u32 itd) { + u8 o; // r30 + struct KEYMAP* keymap; // r31 + long p; // r26 + long k; // r29 + u32 vid; // r1+0x34 +} + +#pragma dont_inline on +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 +} +#pragma dont_inline reset + +static u32 convert_cents(SYNTH_VOICE* svoice, u32 ccents) { + unsigned long curDetune; // r30 + unsigned long cpitch; // r31 +} + +static void UpdateTimeMIDICtrl(SYNTH_VOICE* sv) { + if (!sv->timeUsedByInput) { + return; + } + + sv->timeUsedByInput = 0; + sv->midiDirtyFlags = 0x1fff; +} + +static void LowPrecisionHandler(unsigned long i) { + unsigned long j; // r30 + long pbend; // r29 + unsigned long ccents; // r28 + unsigned long cpitch; // r26 + unsigned short Modulation; // r24 + unsigned short portamento; // r25 + unsigned long lowDeltaTime; // r27 + struct SYNTH_VOICE* sv; // r31 + unsigned long cntDelta; // r20 + unsigned long addFactor; // r19 + unsigned short adsr_start; // r1+0xE + unsigned short adsr_delta; // r1+0xC + long vrange; // r23 + long voff; // r22 +} + +static void ZeroOffsetHandler(unsigned long i) { + struct SYNTH_VOICE* sv; // r31 + unsigned long lowDeltaTime; // r26 + unsigned short Modulation; // r25 + float vol; // r62 + float auxa; // r57 + float auxb; // r56 + float f; // r59 + float voiceVol; // r60 + unsigned long volUpdate; // r30 + float lfo; // r55 + float scale; // r63 + float mscale; // r54 + long pan; // r28 + float preVol; // r58 + float postVol; // r61 +} + +static void EventHandler(unsigned long i) { + SYNTH_VOICE* sv; // r31 +} + +static void synthInitJobQueue() { + u8 i; // r31 + + for (i = 0; i < 32; ++i) { + synthJobTable[i].lowPrecision = NULL; + synthJobTable[i].event = NULL; + synthJobTable[i].zeroOffset = NULL; + } + + synthJobTableIndex = 0; +} + +#pragma dont_inline on +void synthAddJob(SYNTH_VOICE* svoice, SYNTH_JOBTYPE jobType, unsigned long deltaTime) { + SYNTH_QUEUE* newJq; // r31 + SYNTH_QUEUE** root; // r30 + u8 jobTabIndex; // r29 + SYNTH_JOBTAB* jobTab; // r28 +} +#pragma dont_inline reset + +void synthStartSynthJobHandling(SYNTH_VOICE* svoice) { + svoice->lastLowCallTime = synthRealTime; + svoice->lastZeroCallTime = synthRealTime; + synthAddJob(svoice, SYNTH_JOBTYPE_LOW, 0); + synthAddJob(svoice, SYNTH_JOBTYPE_ZERO, 0); +} + +void synthForceLowPrecisionUpdate(SYNTH_VOICE* svoice) { + synthAddJob(svoice, SYNTH_JOBTYPE_LOW, 0); + synthAddJob(svoice, SYNTH_JOBTYPE_ZERO, 0); +} + +void synthKeyStateUpdate(SYNTH_VOICE* svoice) { synthAddJob(svoice, SYNTH_JOBTYPE_EVENT, 0); } + +void HandleJobQueue(SYNTH_QUEUE** queueRoot, void (*handler)(unsigned long)) { + SYNTH_QUEUE* jq; // r31 + SYNTH_QUEUE* nextJq; // r30 + + jq = *queueRoot; + while (jq != NULL) { + nextJq = jq->next; + jq->jobTabIndex = 0xff; + if (!synthVoice[jq->voice].block) { + handler(jq->voice); + } + jq = nextJq; + } + + *queueRoot = NULL; +} + +void HandleVoices() { + SYNTH_JOBTAB* jTab = &synthJobTable[synthJobTableIndex]; // r31 + HandleJobQueue(&jTab->lowPrecision, LowPrecisionHandler); + HandleJobQueue(&jTab->event, EventHandler); + HandleJobQueue(&jTab->zeroOffset, ZeroOffsetHandler); + synthJobTableIndex = synthJobTableIndex + 1 & 0x1f; +} + +void HandleFaderTermination(SYNTHMasterFader* smf) { + + switch (smf->seqMode) { + case 1: + seqStop(smf->seqId); + break; + case 2: + seqPause(smf->seqId); + break; + case 3: + seqMute(smf->seqId, 0, 0); + break; + } +} + +void synthHandle(unsigned long deltaTime) { + unsigned long i; // r29 + unsigned long s; // r30 + SYNTHMasterFader* smf; // r31 + unsigned long testFlag; // r27 + SND_AUX_INFO info; // r1+0x18 +} + +unsigned char synthFXGetMaxVoices(u16 fid) { + FX_TAB* fx; + if ((fx = dataGetFX(fid)) != NULL) { + return fx->maxVoices; + } + + return 0; +} + +u32 synthFXStart(u16 fid, u8 vol, u8 pan, u8 studio, u32 itd) { + FX_TAB* fx; + u32 v; + v = 0xFFFFFFFF; + if ((fx = dataGetFX(fid)) != NULL) { + if (vol == 0xFF) { + vol = fx->volume; + } + + if (pan == 0xFF) { + 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); + } + + return v; +} diff --git a/src/musyx/runtime/synth_vsamples.c b/src/musyx/runtime/synth_vsamples.c index e2495bc5..1a6e8b55 100644 --- a/src/musyx/runtime/synth_vsamples.c +++ b/src/musyx/runtime/synth_vsamples.c @@ -1,21 +1,285 @@ +#include "musyx/assert.h" #include "musyx/musyx_priv.h" +static VS vs; void vsInit() { u32 i; - vs._0 = 0; + vs.numBuffers = 0; for (i = 0; i < 64; i++) { - vs._908[i] = 0xFF; + vs.voices[i] = 0xFF; } - vs._948 = 0; - vs._94c = 0; + vs.nextInstID = 0; + vs.callback = NULL; } -void vsSampleStartNotify() { - +u16 vsNewInstanceID() { + u8 i; // r31 + u16 instID; // r29 + do { + instID = vs.nextInstID++; + i = 0; + for (; i < vs.numBuffers; ++i) { + if (vs.streamBuffer[i].state != 0 && vs.streamBuffer[i].info.instID == instID) { + break; + } + } + } while (i != vs.numBuffers); + + return instID; } -void vsSampleEndNotify() { - +u8 vsAllocateBuffer() { + u8 i; + + for (i = 0; i < vs.numBuffers; ++i) { + if (vs.streamBuffer[i].state != 0) { + continue; + } + vs.streamBuffer[i].state = 1; + vs.streamBuffer[i].last = 0; + return i; + } + + return 0xFF; +} + +void vsFreeBuffer(u8 bufferIndex) { + vs.streamBuffer[bufferIndex].state = 0; + vs.voices[vs.streamBuffer[bufferIndex].voice] = 0xFF; +} + +u32 vsSampleStartNotify(unsigned char voice) { + u8 sb; // r29 + u8 i; // r28 + u32 addr; // r27 + + for (i = 0; i < vs.numBuffers; ++i) { + if (vs.streamBuffer[i].state != 0 && vs.streamBuffer[i].voice == voice) { + vsFreeBuffer(i); + } + } + + sb = vs.voices[voice] = vsAllocateBuffer(); + if (sb != 0xFF) { + addr = aramGetStreamBufferAddress(vs.voices[voice], 0); + hwSetVirtualSampleLoopBuffer(voice, addr, vs.bufferLength); + vs.streamBuffer[sb].info.smpID = hwGetSampleID(voice); + vs.streamBuffer[sb].info.instID = vsNewInstanceID(); + vs.streamBuffer[sb].smpType = hwGetSampleType(voice); + vs.streamBuffer[sb].voice = voice; + if (vs.callback != NULL) { + vs.callback(0, &vs.streamBuffer[sb].info); + + return (vs.streamBuffer[sb].info.instID << 8) | voice; + } + hwSetVirtualSampleLoopBuffer(voice, 0, 0); + } else { + hwSetVirtualSampleLoopBuffer(voice, 0, 0); + } + + return 0xFFFFFFFF; +} + +void vsSampleEndNotify(unsigned long pubID) { + u8 sb; + u8 voice; + + if (pubID != 0xFFFFFFFF) { + u8 id = (u8)pubID; + sb = vs.voices[id]; + if (sb != 0xFF) { + if (vs.streamBuffer[sb].info.instID == ((pubID >> 8) & 0xFFFF)) { + if (vs.callback != NULL) { + vs.callback(2, &vs.streamBuffer[sb].info); + } + vsFreeBuffer(sb); + } + } + } +} + +void vsUpdateBuffer(struct VS_BUFFER* sb, unsigned long cpos) { + u32 len; // r29 + u32 off; // r27 + + if (sb->last == cpos) { + return; + } + if ((s32)sb->last < cpos) { + if ((s8)sb->smpType != 5) { + cpos = cpos; + } else { + sb->info.data.update.off1 = (sb->last / 14) * 8; + sb->info.data.update.len1 = cpos - sb->last; + sb->info.data.update.off2 = 0; + sb->info.data.update.len2 = 0; + if ((len = vs.callback(1, &sb->info)) != 0) { + off = sb->last + len; + sb->last = off - (off / vs.bufferLength) * vs.bufferLength; + } + } + } else if (cpos == 0) { + if ((s8)sb->smpType != 5) { + cpos = cpos; + } else { + sb->info.data.update.off1 = (sb->last / 14) * 8; + sb->info.data.update.len1 = vs.bufferLength - sb->last; + sb->info.data.update.off2 = 0; + sb->info.data.update.len2 = 0; + if ((len = vs.callback(1, &sb->info)) != 0) { + off = sb->last + len; + sb->last = off - (off / vs.bufferLength) * vs.bufferLength; + } + } + } else if ((s8)sb->smpType != 5) { + cpos = cpos; + } else { + sb->info.data.update.off1 = (sb->last / 14) * 8; + sb->info.data.update.len1 = vs.bufferLength - sb->last; + sb->info.data.update.off2 = 0; + sb->info.data.update.len2 = cpos; + if ((len = vs.callback(1, &sb->info)) != 0) { + off = sb->last + len; + sb->last = off - (off / vs.bufferLength) * vs.bufferLength; + } + } +} + +void vsSampleUpdates() { + u32 i; // r29 + u32 cpos; // r27 + u32 realCPos; // r28 + VS_BUFFER* sb; // r31 + u32 nextSamples; // r26 +} + +unsigned long sndVirtualSampleAllocateBuffers(unsigned char numInstances, + unsigned long numSamples) { + long i; // r31 + unsigned long len; // r28 +#line 437 + ASSERT_MSG(sndActive, "Sound system is not initialized."); + ASSERT_MSG(numInstances <= 64, "Parameter exceeded maximum number of instances allowable"); +#line 159 + hwDisableIrq(); + vs.numBuffers = numInstances; + len = sndStreamAllocLength(numSamples, 1); + vs.bufferLength = (len / 8) * 14; + + for (i = 0; i < vs.numBuffers; ++i) { + if ((vs.streamBuffer[i].hwId = aramAllocateStreamBuffer(len)) == 0xFF) { + break; + } + vs.streamBuffer[i].state = 0; + vs.voices[vs.streamBuffer[i].voice] = 0xFF; + } + + if (i >= vs.numBuffers) { + hwEnableIrq(); + return 1; + } + + while ((i - 1) > 0) { + aramFreeStreamBuffer(vs.streamBuffer[i - 1].hwId); + --i; + } + + hwEnableIrq(); + return 0; +} + +void sndVirtualSampleFreeBuffers() { + u8 i; // r31 +#line 481 + ASSERT_MSG(sndActive, "Sound system is not initialized."); + + for (i = 0; i < vs.numBuffers; ++i) { + aramFreeStreamBuffer(vs.streamBuffer[i].hwId); + } + + vs.numBuffers = 0; +} + +void sndVirtualSampleSetCallback(unsigned long (*callback)(unsigned char, + struct SND_VIRTUALSAMPLE_INFO*)) { +#line 0x1ed + ASSERT_MSG(sndActive, "Sound system is not initialized."); + vs.callback = callback; +} + +void vsARAMDMACallback(unsigned long user) { + if (vs.callback == NULL) { + return; + } + + vs.callback(3, &((VS_BUFFER*)user)->info); +} + +void sndVirtualSampleARAMUpdate(unsigned short instID, void* base, unsigned long off1, + unsigned long len1, unsigned long off2, unsigned long len2) { + u8 i; +#line 0x203 + ASSERT_MSG(sndActive, "Sound system is not initialized."); + + hwDisableIrq(); + + for (i = 0; i < vs.numBuffers; ++i) { + if (vs.streamBuffer[i].state == 0 || vs.streamBuffer[i].info.instID != instID) { + continue; + } + if ((s32)vs.streamBuffer[i].smpType != 5) { + i = i; + } else { + off1 = (off1 / 14) * 8; + len1 = ((len1 + 13) / 14) * 8; + off2 = (off2 / 14) * 8; + len2 = ((len2 + 13) / 14) * 8; + } + + if (len1 != 0) { + hwFlushStream(base, off1, len1, vs.streamBuffer[i].hwId, vsARAMDMACallback, + &vs.streamBuffer[i]); + } + if (len2 != 0) { + hwFlushStream(base, off2, len2, vs.streamBuffer[i].hwId, vsARAMDMACallback, + &vs.streamBuffer[i]); + } + + if (vs.streamBuffer[i].smpType == 5) { + hwSetStreamLoopPS(vs.streamBuffer[i].voice, *(u32*)(OSCachedToUncached(base)) >> 24); + } + break; + } + + hwEnableIrq(); +} + +void sndVirtualSampleEndPlayback(unsigned short instID) { + u8 i; // r30 + VS_BUFFER* stream; // r31 + u32 cpos; // r28 + + hwDisableIrq(); + + for (i = 0; i < vs.numBuffers; ++i) { + if (vs.streamBuffer[i].state == 0 || vs.streamBuffer[i].info.instID != instID) { + continue; + } + + stream = &vs.streamBuffer[i]; + cpos = hwGetPos(i); + + if (stream->last < cpos) { + stream->finalGoodSamples = vs.bufferLength - (cpos - stream->last); + } else { + stream->finalGoodSamples = stream->last - cpos; + } + + stream->finalLast = cpos; + stream->state = 2; + break; + } + hwEnableIrq(); } diff --git a/src/musyx/runtime/synthvoice.c b/src/musyx/runtime/synthvoice.c new file mode 100644 index 00000000..87f0d4fb --- /dev/null +++ b/src/musyx/runtime/synthvoice.c @@ -0,0 +1,492 @@ +#include "musyx/assert.h" +#include "musyx/musyx_priv.h" + +void voiceResetLastStarted(SYNTH_VOICE* svoice); + +static VID_LIST vidList[128]; +static u8 synth_last_started[8][16]; +static u8 synth_last_fxstarted[64]; +SYNTH_VOICELIST voiceList[64]; +SYNTH_ROOTLIST voicePrioSortRootList[256]; +u8 voicePrioSortVoicesRoot[256]; +SYNTH_VOICELIST voicePrioSortVoices[64]; +static u32 vidCurrentId = 0; +static VID_LIST* vidRoot = NULL; +static VID_LIST* vidFree = NULL; +u16 voicePrioSortRootListRoot = 0; +u8 voiceMusicRunning = 0; +u8 voiceFxRunning = 0; +u8 voiceListInsert = 0; +u8 voiceListRoot = 0; + +void vidInit() { + int i; + VID_LIST* lvl; + vidCurrentId = 0; + vidRoot = NULL; + vidFree = vidList; + for (lvl = NULL, i = 0; i < 128; lvl = &vidList[i], ++i) { + vidList[i].prev = lvl; + if (lvl != NULL) { + lvl->next = &vidList[i]; + } + } + lvl->next = NULL; +} + +VID_LIST* get_vidlist(u32 vid) { + VID_LIST* vl = vidRoot; + while (vl != NULL) { + if (vl->vid == vid) { + return vl; + } + if (vl->vid > vid) { + break; + } + vl = vl->next; + } + + return NULL; +} + +u32 get_newvid() { + u32 vid; // r31 + do { + vid = vidCurrentId++; + } while (vid == 0xFFFFFFFF); + + return vid; +} + +void vidRemove(VID_LIST** vidList) { + if ((*vidList)->prev != NULL) { + (*vidList)->prev->next = (*vidList)->next; + } else { + vidRoot = (*vidList)->next; + } + + if ((*vidList)->next != NULL) { + (*vidList)->next->prev = (*vidList)->prev; + } + + (*vidList)->next = vidFree; + + if (vidFree != NULL) { + vidFree->prev = *vidList; + } + + (*vidList)->prev = NULL; + vidFree = *vidList; + *vidList = NULL; +} + +void vidRemoveVoiceReferences(SYNTH_VOICE* svoice) { + if (svoice->id == 0xFFFFFFFF) { + return; + } + + voiceResetLastStarted(svoice); + if (svoice->parent != 0xFFFFFFFF) { + synthVoice[svoice->parent & 0xFF].child = svoice->child; + if (svoice->child != 0xFFFFFFFF) { + synthVoice[svoice->child & 0xFF].parent = svoice->parent; + } + + vidRemove(&svoice->vidList); + } else if (svoice->child != 0xFFFFFFFF) { + svoice->vidList->root = svoice->child; + synthVoice[svoice->child & 0xFF].parent = 0xFFFFFFFF; + synthVoice[svoice->child & 0xFF].vidMasterList = svoice->vidMasterList; + if (svoice->vidList != svoice->vidMasterList) { + vidRemove(&svoice->vidList); + } + + svoice->vidMasterList = svoice->vidList = NULL; + } else if (svoice->vidList != svoice->vidMasterList) { + vidRemove(&svoice->vidList); + vidRemove(&svoice->vidMasterList); + } else { + vidRemove(&svoice->vidList); + svoice->vidMasterList = NULL; + } +} + +unsigned long vidMakeRoot(struct SYNTH_VOICE* svoice) { + svoice->vidMasterList = svoice->vidList; + return svoice->vidList->vid; +} + +u32 vidMakeNew(SYNTH_VOICE* svoice, u32 isMaster) { + u32 vid; // r29 + VID_LIST* nvl; // r30 + VID_LIST* lvl; // r28 + VID_LIST* vl; // r31 + + vid = get_newvid(); + lvl = NULL; + nvl = vidRoot; + + while (nvl != NULL) { + if (nvl->vid > vid) { + break; + } + + if (nvl->vid == vid) { + vid = get_newvid(); + } + + lvl = nvl; + nvl = nvl->next; + } + + if ((vl = vidFree) == NULL) { + return 0xFFFFFFFF; + } + + if ((vidFree = vidFree->next) != NULL) { + vidFree->prev = NULL; + } + + if (lvl == NULL) { + vidRoot = vl; + } else { + lvl->next = vl; + } + + vl->prev = lvl; + vl->next = nvl; + + if (nvl != NULL) { + nvl->prev = vl; + } + + vl->vid = vid; + vl->root = svoice->id; + svoice->vidMasterList = isMaster ? vl : NULL; + svoice->vidList = vl; + + if (isMaster != 0) { + return vid; + } + + return svoice->id; +} + +u32 vidGetInternalId(u32 vid) { + VID_LIST* vl; + + if (vid != 0xffffffff) { + if ((vl = get_vidlist(vid)) != NULL) { + return vl->root; + } + } + + return 0xffffffff; +} + +void voiceInitPrioSort() { + u32 i; + + for (i = 0; i < synthInfo.voiceNum; ++i) { + voicePrioSortVoices[i].user = 0; + } + + for (i = 0; i < 256; ++i) { + voicePrioSortVoicesRoot[i] = 0xff; + } + + voicePrioSortRootListRoot = 0xffff; +} + +void voiceRemovePriority(SYNTH_VOICE* svoice) { + SYNTH_VOICELIST* vps = &voicePrioSortVoices[svoice->id & 0xFF]; // r31 + SYNTH_ROOTLIST* rps; // r30 + if (vps->user != 1) { + return; + } + + if (vps->prev != 0xff) { + voicePrioSortVoices[vps->prev].next = vps->next; + } else { + voicePrioSortVoicesRoot[svoice->prio] = vps->next; + } + + if (vps->next != 0xff) { + voicePrioSortVoices[vps->next].prev = vps->prev; + } else if (vps->prev == 0xFF) { + rps = &voicePrioSortRootList[svoice->prio]; + + if (rps->prev != 0xFFFF) { + voicePrioSortRootList[rps->prev].next = rps->next; + + } else { + voicePrioSortRootListRoot = rps->next; + } + + if (rps->next != 0xFFFF) { + voicePrioSortRootList[rps->next].prev = rps->prev; + } + } + + vps->user = 0; +} + +#pragma dont_inline on +void voiceSetPriority(SYNTH_VOICE* svoice, u8 prio) { + u16 i; // r29 + u16 li; // r25 + SYNTH_VOICELIST* vps; // r27 + u32 v; // r26 +} + +u32 voiceAllocate(u8 priority, u8 maxVoices, u16 allocId, u8 fxFlag) { + long i; // r31 + long num; // r26 + long voice; // r30 + u16 p; // r29 + u32 type_alloc; // r25 + SYNTH_VOICELIST* sfv; // r27 +} +#pragma dont_inline reset + +void voiceFree(SYNTH_VOICE* svoice) { + u32 i; // r29 + SYNTH_VOICELIST* sfv; // r30 +#line 628 + ASSERT(svoice->id != 0xFFFFFFFF); +#line 256 + macMakeInactive(svoice, MAC_STATE_STOPPED); + voiceRemovePriority(svoice); + svoice->addr = NULL; + svoice->prio = 0; + sfv = &voiceList[(i = svoice->id & 0xFF)]; + if (sfv->user == 0) { + sfv->user = 1; + if (voiceListRoot != 0xFF) { + sfv->next = 0xFF; + sfv->prev = voiceListInsert; + voiceList[voiceListInsert].next = i; + } else { + sfv->next = 0xFF; + sfv->prev = 0xFF; + voiceListRoot = i; + } + + voiceListInsert = i; + if (svoice->fxFlag != 0) { + --voiceFxRunning; + } else { + --voiceMusicRunning; + } + } + + svoice->id = 0xFFFFFFFF; +} + +void voiceInitFreeList() { + u32 i; // r31 + + for (i = 0; i < synthInfo.voiceNum; ++i) { + voiceList[i].prev = i - 1; + voiceList[i].next = i + 1; + voiceList[i].user = 1; + } + + voiceList[0].prev = 0xff; + voiceList[synthInfo.voiceNum - 1].next = 0xff; + voiceListRoot = 0; + voiceListInsert = synthInfo.voiceNum - 1; +} + +void synthInitAllocationAids() { + voiceInitFreeList(); + voiceInitPrioSort(); + voiceFxRunning = 0; + voiceMusicRunning = 0; +} + +u32 voiceBlock(unsigned char prio) { + u32 voice = voiceAllocate(prio, 0xFF, 0xFFFF, 1); + + if (voice != 0xFFFFFFFF) { + synthVoice[voice].block = 1; + synthVoice[voice].fxFlag = 1; +#ifdef PRIME1 + synthVoice[voice].allocId = 0xFFFF; +#endif + vidRemoveVoiceReferences(&synthVoice[voice]); + synthVoice[voice].id = voice | 0xFFFFFF00; + + if (hwIsActive(voice)) { + hwBreak(voice); + } + + macMakeInactive(&synthVoice[voice], MAC_STATE_STOPPED); + synthVoice[voice].addr = NULL; + voiceSetPriority(&synthVoice[voice], prio); + } + + return voice; +} + +void voiceUnblock(u32 voice) { + if (voice == 0xFFFFFFFF) { + return; + } + + if (hwIsActive(voice)) { + hwBreak(voice); + } + + synthVoice[voice].id = voice; + voiceFree(&synthVoice[voice]); + synthVoice[voice].block = 0; +} + +void voiceKill(u32 vi) { + SYNTH_VOICE* sv = &synthVoice[vi]; // r31 + if (sv->addr != NULL) { + vidRemoveVoiceReferences(sv); + sv->cFlags &= ~3; + sv->age = 0; + voiceFree(sv); + } + + if (sv->block != 0) { + streamKill(vi); + } + + hwBreak(vi); +} + +long voiceKillSound(u32 voiceid) { + long ret; + u32 next_voiceid; + u32 i; + u32 vid; + + ret = -1; + + if (sndActive != FALSE) { + i = vidGetInternalId(voiceid); + + while (i != 0xFFFFFFFF) { + vid = i & 0xFF; + next_voiceid = synthVoice[vid].child; + if (i == synthVoice[vid].id) { + voiceKill(vid); + ret = 0; + } + i = next_voiceid; + } + } + + return ret; +} + +void synthKillAllVoices(unsigned char musiconly) { + u32 i; + + for (i = 0; i < synthInfo.voiceNum; ++i) { + if (synthVoice[i].addr != NULL) { + if (musiconly == 0 || (musiconly != 0 && synthVoice[i].fxFlag == 0)) { + voiceKill(i); + } + } else { + voiceKill(i); + } + } +} + +void synthKillVoicesByMacroReferences(u16* ref) { + u32 i; // r31 + u16 id; // r29 + + for (i = 0; i < synthInfo.voiceNum; ++i) { + if (synthVoice[i].addr == NULL && synthVoice[i].block == 0) { + voiceKill(i); + } + } + + while (*ref != 0xFFFF) { + if ((*ref & 0x8000)) { + id = *ref & 0x3fff; + while (id <= ref[1]) { + for (i = 0; i < synthInfo.voiceNum; ++i) { + if (synthVoice[i].addr != NULL && id == synthVoice[i].macroId) { + voiceKill(i); + } + } + ++id; + } + ref += 2; + } else { + for (i = 0; i < synthInfo.voiceNum; ++i) { + if (synthVoice[i].addr != NULL && *ref == synthVoice[i].macroId) { + voiceKill(i); + } + } + ++ref; + } + } +} + +u32 voiceIsLastStarted(SYNTH_VOICE* svoice) { + u32 i; // r31 + + if (svoice->id != 0xFFFFFFFF && svoice->midi != 0xFF) { + i = svoice->id & 0xFF; + if (svoice->midiSet == 0xFF) { + if (synth_last_fxstarted[i] == i) { + return TRUE; + } + } else if (synth_last_started[svoice->midiSet][svoice->midi] == i) { + return TRUE; + } + } + + return FALSE; +} + +void voiceSetLastStarted(SYNTH_VOICE* svoice) { + u32 i; // r31 + + if (svoice->id != 0xFFFFFFFF && svoice->midi != 0xFF) { + i = svoice->id & 0xFF; + if (svoice->midiSet == 0xFF) { + synth_last_fxstarted[i] = i; + } else { + synth_last_started[svoice->midiSet][svoice->midi] = i; + } + } +} + +void voiceResetLastStarted(struct SYNTH_VOICE* svoice) { + u32 i; + + if ((svoice->id != 0xffffffff) && (svoice->midi != 0xff)) { + i = svoice->id & 0xff; + if (svoice->midiSet == 0xff) { + if (synth_last_fxstarted[i] == i) { + synth_last_fxstarted[i] = 0xff; + } + } else if (i == synth_last_started[svoice->midiSet][svoice->midi]) { + synth_last_started[svoice->midiSet][svoice->midi] = 0xff; + } + } +} + +void voiceInitLastStarted() { + u32 i; + u32 j; + + for (i = 0; i < 8; ++i) { + for (j = 0; j < 16; ++j) { + synth_last_started[i][j] = 0xFF; + } + } + + for (j = 0; j < 64; ++j) { + synth_last_fxstarted[j] = 0xFF; + } +}