prime/src/musyx/runtime/hardware.c

502 lines
14 KiB
C

#include "musyx/musyx_priv.h"
extern void DCStoreRange(void* addr, u32 nBytes);
static volatile const u16 itdOffTab[128] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4,
5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 12, 12, 12, 13, 13, 13, 14, 14, 15, 15, 15, 16,
16, 17, 17, 17, 18, 18, 19, 19, 19, 20, 20, 20, 21, 21, 22, 22, 22, 23, 23, 23, 24, 24, 24, 25, 25, 25, 26, 26, 26, 27, 27, 27,
28, 28, 28, 28, 29, 29, 29, 29, 29, 30, 30, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
};
//SND_PROFILE_INFO prof;
u8 salFrame;
u8 salAuxFrame;
u8 salNumVoices;
u8 salMaxStudioNum;
SND_HOOKS salHooks;
u8 salTimeOffset;
void hwSetTimeOffset(u8 offset);
static void snd_handle_irq() {
u8 r; // r31
u8 i; // r30
u8 v; // r29
if (sndActive == 0) {
return;
}
streamCorrectLoops();
hwIRQEnterCritical();
// sndProfStartPCM(&prof.dspCtrl);
salCtrlDsp(salAiGetDest());
// sndProfStopPMC(&prof.dspCtrl);
hwIRQLeaveCritical();
hwIRQEnterCritical();
// sndProfStartPCM(&prof.auxProcessing);
salHandleAuxProcessing();
// sndProfStopPMC(&prof.auxProcessing);
hwIRQLeaveCritical();
hwIRQEnterCritical();
salFrame ^= 1;
salAuxFrame = (salAuxFrame + 1) % 3;
for (v = 0; v < salNumVoices; ++v) {
for (i = 0; i < 5; ++i) {
dspVoice[v].changed[i] = 0;
}
}
// sndProfStartPMC(&prof.sequencer);
// sndProfPausePMC(&prof.sequencer);
// sndProfStartPMC(&prof.synthesizer);
// sndProfPausePMC(&prof.synthesizer);
hwIRQLeaveCritical();
for (r = 0; r < 5; ++r) {
hwIRQEnterCritical();
hwSetTimeOffset(r);
// sndProfStartPMC(&prof.sequencer);
seqHandle(256);
// sndProfPausePMC(&prof.sequencer);
// sndProfStartPMC(&prof.synthesizer);
synthHandle(256);
// sndProfPausePMC(&prof.synthesizer);
hwIRQLeaveCritical();
}
// sndProfStopPMC(&prof.sequencer);
// sndProfStopPMC(&prof.synthesizer);
hwIRQEnterCritical();
hwSetTimeOffset(0);
// sndProfStartPMC(&prof.emitters);
s3dHandle();
// sndProfStopPMC(&prof.emitters);
hwIRQLeaveCritical();
hwIRQEnterCritical();
// sndProfStartPMC(&prof.streaming);
streamHandle();
// sndProfStopPMC(&prof.streaming);
hwIRQLeaveCritical();
hwIRQEnterCritical();
vsSampleUpdates();
hwIRQLeaveCritical();
// sndProfUpdateMisc(&prof);
// if (sndProfUserCallback) {
// sndProfUserCallback(&prof);
// }
}
s32 hwInit(u32* frq, u16 numVoices, u16 numStudios, u32 flags) {
MUSY_DEBUG("Entering hwInit()\n\n");
hwInitIrq();
salFrame = 0;
salAuxFrame = 0;
salMessageCallback = NULL;
if (salInitAi(snd_handle_irq, flags, frq) != 0) {
MUSY_DEBUG("salInitAi() is done.\n\n");
if (salInitDspCtrl(numVoices, numStudios, (flags & 1) != 0) != 0) {
MUSY_DEBUG("salInitDspCtrl() is done.\n\n");
if (salInitDsp(flags)) {
MUSY_DEBUG("salInitDsp() is done.\n\n");
hwEnableIrq();
MUSY_DEBUG("Starting AI DMA...\n\n");
salStartAi();
MUSY_DEBUG("hwInit() done.\n\n");
return 0;
}
MUSY_DEBUG("Could not initialize DSP.\n");
} else {
MUSY_DEBUG("Could not initialize DSP control logic.\n");
}
} else {
MUSY_DEBUG("Could not initialize AI.\n");
}
return -1;
}
void hwExit() {
hwDisableIrq();
salExitDsp();
salExitDspCtrl();
salExitAi();
hwEnableIrq();
hwExitIrq();
}
void hwSetTimeOffset(u8 offset) { salTimeOffset = offset; }
u8 hwGetTimeOffset() { return salTimeOffset; }
u32 hwIsActive(u32 v) { return dspVoice[v].state != 0; }
u32 hwGlobalActivity() { return 0; }
void hwSetMesgCallback(SND_MESSAGE_CALLBACK callback) { salMessageCallback = callback; }
void hwSetPriority(u32 v, u32 prio) { dspVoice[v].prio = prio; }
void hwInitSamplePlayback(u32 v, u16 smpID, void* newsmp, u32 set_defadsr, u32 prio, u32 callbackUserValue, u32 setSRC, u8 itdMode) {
unsigned char i; // r30
unsigned long bf; // r29
bf = 0;
for (i = 0; i <= salTimeOffset; ++i) {
bf |= dspVoice[v].changed[i] & 0x20;
dspVoice[v].changed[i] = 0;
}
dspVoice[v].changed[0] = bf;
dspVoice[v].prio = prio;
dspVoice[v].mesgCallBackUserValue = callbackUserValue;
dspVoice[v].flags = 0;
dspVoice[v].smp_id = smpID;
dspVoice[v].smp_info = *(SAMPLE_INFO*)newsmp;
if (set_defadsr != 0) {
dspVoice[v].adsr.mode = 0;
dspVoice[v].adsr.data.dls.aTime = 0;
dspVoice[v].adsr.data.dls.dTime = 0;
dspVoice[v].adsr.data.dls.sLevel = 0x7FFF;
dspVoice[v].adsr.data.dls.rTime = 0;
}
dspVoice[v].lastUpdate.pitch = 0xff;
dspVoice[v].lastUpdate.vol = 0xff;
dspVoice[v].lastUpdate.volA = 0xff;
dspVoice[v].lastUpdate.volB = 0xff;
if (setSRC != 0) {
hwSetSRCType(v, 0);
hwSetPolyPhaseFilter(v, 1);
}
hwSetITDMode(v, itdMode);
}
void hwBreak(s32 vid) {
if (dspVoice[vid].state == 1 && salTimeOffset == 0) {
dspVoice[vid].startupBreak = 1;
}
dspVoice[vid].changed[salTimeOffset] |= 0x20;
}
void hwSetADSR(u32 v, void* _adsr, u8 mode) {
u32 sl; // r29
ADSR_INFO* adsr = (ADSR_INFO*)_adsr; // r30
switch (mode) {
case 0: {
dspVoice[v].adsr.mode = 0;
dspVoice[v].adsr.data.linear.aTime = adsr->data.linear.atime;
dspVoice[v].adsr.data.linear.dTime = adsr->data.linear.dtime;
sl = adsr->data.linear.slevel << 3;
if (sl > 0x7fff) {
sl = 0x7fff;
}
dspVoice[v].adsr.data.linear.sLevel = sl;
dspVoice[v].adsr.data.linear.rTime = adsr->data.linear.rtime;
break;
}
case 1:
case 2:
dspVoice[v].adsr.mode = 1;
dspVoice[v].adsr.data.dls.aMode = 0;
if (mode == 1) {
dspVoice[v].adsr.data.dls.aTime = adsrConvertTimeCents(adsr->data.dls.atime) & 0xFFFF;
dspVoice[v].adsr.data.dls.dTime = adsrConvertTimeCents(adsr->data.dls.dtime) & 0xFFFF;
sl = adsr->data.dls.slevel >> 2;
if (sl > 0x3ff) {
sl = 0x3ff;
}
dspVoice[v].adsr.data.dls.sLevel = 193 - dspScale2IndexTab[sl];
} else {
dspVoice[v].adsr.data.dls.aTime = adsr->data.dls.atime & 0xFFFF;
dspVoice[v].adsr.data.dls.dTime = adsr->data.dls.dtime & 0xFFFF;
dspVoice[v].adsr.data.dls.sLevel = adsr->data.dls.slevel;
}
dspVoice[v].adsr.data.dls.rTime = adsr->data.dls.rtime;
}
dspVoice[v].changed[0] |= 0x10;
}
void hwSetVirtualSampleLoopBuffer(u32 voice, void* addr, u32 len) {
dspVoice[voice].vSampleInfo.loopBufferAddr = addr;
dspVoice[voice].vSampleInfo.loopBufferLength = len;
}
u32 hwGetVirtualSampleState(u32 voice) { return dspVoice[voice].vSampleInfo.inLoopBuffer; }
u8 hwGetSampleType(u32 voice) { return dspVoice[voice].smp_info.compType; }
u16 hwGetSampleID(u32 voice) { return dspVoice[voice].smp_id; }
void hwSetStreamLoopPS(u32 voice, u8 ps) { dspVoice[voice].streamLoopPS = ps; }
void hwStart(u32 v, u8 studio) {
dspVoice[v].singleOffset = salTimeOffset;
salActivateVoice(&dspVoice[v], studio);
}
void hwKeyOff(u32 v) { dspVoice[v].changed[salTimeOffset] |= 0x40; }
void hwSetPitch(unsigned long v, unsigned short speed) {
struct DSPvoice* dsp_vptr = &dspVoice[v];
if (speed >= 0x4000) {
speed = 0x3fff;
}
if (dsp_vptr->lastUpdate.pitch != 0xff && dsp_vptr->pitch[dsp_vptr->lastUpdate.pitch] == speed * 16) {
return;
}
dsp_vptr->pitch[salTimeOffset] = speed * 16;
dsp_vptr->changed[salTimeOffset] |= 8;
dsp_vptr->lastUpdate.pitch = salTimeOffset;
}
void hwSetSRCType(u32 v, u8 salSRCType) {
static u16 dspSRCType[3] = {0, 1, 2};
struct DSPvoice* dsp_vptr = &dspVoice[v];
dsp_vptr->srcTypeSelect = dspSRCType[salSRCType];
dsp_vptr->changed[0] |= 0x100;
}
void hwSetPolyPhaseFilter(unsigned long v, unsigned char salCoefSel) {
static u16 dspCoefSel[3] = {0, 1, 2};
struct DSPvoice* dsp_vptr = &dspVoice[v];
dsp_vptr->srcCoefSelect = dspCoefSel[salCoefSel];
dsp_vptr->changed[0] |= 0x80;
}
static void SetupITD(DSPvoice* dsp_vptr, u8 pan) {
dsp_vptr->itdShiftL = itdOffTab[pan];
dsp_vptr->itdShiftR = 32 - itdOffTab[pan];
dsp_vptr->changed[0] |= 0x200;
}
void hwSetITDMode(u32 v, u8 mode) {
if (!mode) {
dspVoice[v].flags |= 0x80000000;
dspVoice[v].itdShiftL = 16;
dspVoice[v].itdShiftR = 16;
return;
}
dspVoice[v].flags &= ~0x80000000;
}
#define hwGetITDMode(dsp_vptr) (dsp_vptr->flags & 0x80000000)
void hwSetVolume(unsigned long v, unsigned char table, float vol, unsigned long pan, unsigned long span, float auxa, float auxb) {
SAL_VOLINFO vi; // r1+0x24
unsigned short il; // r30
unsigned short ir; // r29
unsigned short is; // r28
DSPvoice* dsp_vptr = &dspVoice[v]; // r31
if (vol >= 1.f) {
vol = 1.f;
}
if (auxa >= 1.f) {
auxa = 1.f;
}
if (auxb >= 1.f) {
auxb = 1.f;
}
salCalcVolume(table, &vi, vol, pan, span, auxa, auxb, (dsp_vptr->flags & 0x80000000) != 0,
dspStudio[dsp_vptr->studio].type == SND_STUDIO_TYPE_RESERVED0);
il = 32767.f * vi.volL;
ir = 32767.f * vi.volR;
is = 32767.f * vi.volS;
if (dsp_vptr->lastUpdate.vol == 0xff || dsp_vptr->volL != il || dsp_vptr->volR != ir || dsp_vptr->volS != is) {
dsp_vptr->volL = il;
dsp_vptr->volR = ir;
dsp_vptr->volS = is;
dsp_vptr->changed[0] |= 1;
dsp_vptr->lastUpdate.vol = 0;
}
il = 32767.f * vi.volAuxAL;
ir = 32767.f * vi.volAuxAR;
is = 32767.f * vi.volAuxAS;
if (dsp_vptr->lastUpdate.volA == 0xff || dsp_vptr->volLa != il || dsp_vptr->volRa != ir || dsp_vptr->volSa != is) {
dsp_vptr->volLa = il;
dsp_vptr->volRa = ir;
dsp_vptr->volSa = is;
dsp_vptr->changed[0] |= 2;
dsp_vptr->lastUpdate.volA = 0;
}
il = 32767.f * vi.volAuxBL;
ir = 32767.f * vi.volAuxBR;
is = 32767.f * vi.volAuxBS;
if (dsp_vptr->lastUpdate.volB == 0xff || dsp_vptr->volLb != il || dsp_vptr->volRb != ir || dsp_vptr->volSb != is) {
dsp_vptr->volLb = il;
dsp_vptr->volRb = ir;
dsp_vptr->volSb = is;
dsp_vptr->changed[0] |= 4;
dsp_vptr->lastUpdate.volB = 0;
}
if (hwGetITDMode(dsp_vptr)) {
SetupITD(dsp_vptr, (pan >> 16));
}
}
void hwOff(s32 vid) { salDeactivateVoice(&dspVoice[vid]); }
void hwSetAUXProcessingCallbacks(u8 studio, SND_AUX_CALLBACK auxA, void* userA, SND_AUX_CALLBACK auxB, void* userB) {
dspStudio[studio].auxAHandler = auxA;
dspStudio[studio].auxAUser = userA;
dspStudio[studio].auxBHandler = auxB;
dspStudio[studio].auxBUser = userB;
}
void hwActivateStudio(unsigned char studio, unsigned long isMaster, SND_STUDIO_TYPE type) { salActivateStudio(studio, isMaster, type); }
void hwDeactivateStudio(u8 studio) { salDeactivateStudio(studio); }
void hwChangeStudioMix(u8 studio, u32 isMaster) { dspStudio[studio].isMaster = isMaster; }
bool hwIsStudioActive(u8 studio) { return dspStudio[studio].state == 1; }
bool hwAddInput(u8 studio, SND_STUDIO_INPUT* in_desc) { return salAddStudioInput(&dspStudio[studio], in_desc); }
bool hwRemoveInput(u8 studio, SND_STUDIO_INPUT* in_desc) { return salRemoveStudioInput(&dspStudio[studio], in_desc); }
void hwChangeStudio(u32 v, u8 studio) { salReconnectVoice(&dspVoice[v], studio); }
u32 hwGetPos(u32 v) {
unsigned long pos; // r31
unsigned long off; // r30
if (dspVoice[v].state != 2) {
return 0;
}
switch (dspVoice[v].smp_info.compType) {
case 0:
case 1:
case 4:
case 5:
pos = ((dspVoice[v].currentAddr - (u32)dspVoice[v].smp_info.addr * 2) / 16) * 14;
off = dspVoice[v].currentAddr & 0xf;
if (off >= 2) {
pos += off - 2;
}
break;
case 3:
pos = dspVoice[v].currentAddr - (u32)dspVoice[v].smp_info.addr;
break;
case 2:
pos = dspVoice[v].currentAddr - ((u32)dspVoice[v].smp_info.addr / 2);
break;
}
return pos;
}
void hwFlushStream(void* base, unsigned long offset, unsigned long bytes, unsigned char hwStreamHandle, void (*callback)(unsigned long),
unsigned long user) {
u32 aram; // r28
u32 mram; // r29
u32 len;
aram = aramGetStreamBufferAddress(hwStreamHandle, &len);
bytes += (offset & 31);
offset &= ~31;
bytes = (bytes + 31) & ~31;
mram = (u32)base + offset;
DCStoreRange((void*)mram, bytes);
aramUploadData((void*)mram, aram + offset, bytes, 1, callback, user);
}
void hwPrepareStreamBuffer() {}
u8 hwInitStream(u32 len) { return aramAllocateStreamBuffer(len); }
void hwExitStream(u8 id) { aramFreeStreamBuffer(id); }
void* hwGetStreamPlayBuffer(u8 hwStreamHandle) { return (void*)aramGetStreamBufferAddress(hwStreamHandle, NULL); }
void* hwTransAddr(void* samples) { return samples; }
u32 hwFrq2Pitch(u32 frq) { return (frq * 4096.f) / synthInfo.mixFrq; }
void hwInitSampleMem(u32 baseAddr, u32 length) {
#line 940
MUSY_ASSERT(baseAddr==0x00000000);
aramInit(length);
}
void hwExitSampleMem() { aramExit(); }
static u32 convert_length(u32 len, u8 type) {
switch (type) {
case 0:
case 1:
case 4:
case 5:
return (((u32)((len + 0xD) / 0xe))) * 8;
case 2:
return len * 2;
}
return len;
}
void hwSaveSample(void* header, void* data) {
u32 len = ((u32*)*((u32*)header))[1] & 0xFFFFFF;
u8 type = ((u32*)*((u32*)header))[1] >> 0x18;
len = convert_length(len, type);
*((u32*)data) = (u32)aramStoreData((void*)*((u32*)data), len);
}
void hwSetSaveSampleCallback(ARAMUploadCallback callback, unsigned long chunckSize) { aramSetUploadCallback(callback, chunckSize); }
void hwRemoveSample(void* header, void* data) {
#if MUSY_VERSION <= MUSY_VERSION_CHECK(1, 5, 3)
u32 len = (((u32*)header))[1] & 0xFFFFFF;
u8 type = (((u32*)header))[1] >> 0x18;
len = convert_length(len, type);
#else
u8 type = (((u32*)header))[1] >> 0x18;
u32 len = convert_length((((u32*)header))[1] & 0xFFFFFF, type);
#endif
aramRemoveData(data, len);
}
void hwSyncSampleMem() { aramSyncTransferQueue(); }
void hwFrameDone() {}
void sndSetHooks(SND_HOOKS* hooks) { salHooks = *hooks; }
void hwEnableHRTF() {
if (dspHRTFOn != FALSE) {
return;
}
dspHRTFOn = TRUE;
salInitHRTFBuffer();
}
void hwDisableHRTF() { dspHRTFOn = FALSE; }
u32 hwGetVirtualSampleID(u32 v) {
if (dspVoice[v].state == 0) {
return 0xFFFFFFFF;
}
return dspVoice[v].virtualSampleID;
}
bool hwVoiceInStartup(u32 v) { return dspVoice[v].state == 1; }