mirror of https://github.com/PrimeDecomp/prime.git
347 lines
8.5 KiB
C
347 lines
8.5 KiB
C
|
|
#include "musyx/musyx_priv.h"
|
|
|
|
static VS vs;
|
|
|
|
void vsInit() {
|
|
u32 i;
|
|
vs.numBuffers = 0;
|
|
for (i = 0; i < 64; i++) {
|
|
vs.voices[i] = 0xFF;
|
|
}
|
|
|
|
vs.nextInstID = 0;
|
|
vs.callback = NULL;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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, (void*)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) {
|
|
switch (sb->smpType) {
|
|
case 5:
|
|
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 % vs.bufferLength;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} else if (cpos == 0) {
|
|
switch (sb->smpType) {
|
|
case 5:
|
|
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 % vs.bufferLength;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} else {
|
|
switch (sb->smpType) {
|
|
case 5:
|
|
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 % vs.bufferLength;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void vsSampleUpdates() {
|
|
u32 i; // r29
|
|
u32 cpos; // r27
|
|
u32 realCPos; // r28
|
|
VS_BUFFER* sb; // r31
|
|
u32 nextSamples; // r26
|
|
|
|
if (vs.callback == NULL) {
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < 64; ++i) {
|
|
if (vs.voices[i] != 0xFF && hwGetVirtualSampleState(i) != 0) {
|
|
sb = &vs.streamBuffer[vs.voices[i]];
|
|
realCPos = hwGetPos(i);
|
|
cpos = sb->smpType == 5 ? (realCPos / 14) * 14 : realCPos;
|
|
|
|
switch (sb->state) {
|
|
case 1:
|
|
vsUpdateBuffer(sb, cpos);
|
|
break;
|
|
case 2:
|
|
case 3:
|
|
if (((sb->info.instID << 8) | sb->voice) == hwGetVirtualSampleID(sb->voice)) {
|
|
vsUpdateBuffer(sb, cpos);
|
|
|
|
if (realCPos >= sb->finalLast) {
|
|
sb->finalGoodSamples -= (realCPos - sb->finalLast);
|
|
} else {
|
|
sb->finalGoodSamples -= (vs.bufferLength - (sb->finalLast - realCPos));
|
|
}
|
|
|
|
sb->finalLast = realCPos;
|
|
nextSamples = (synthVoice[sb->voice].curPitch * 160 + 0xFFF) / 4096;
|
|
if ((s32)nextSamples > (s32)sb->finalGoodSamples) {
|
|
if (!hwVoiceInStartup(sb->voice)) {
|
|
#if MUSY_VERSION >= MUSY_VERSION_CHECK(1, 5, 4)
|
|
if (sb->state == 2) {
|
|
hwBreak(sb->voice);
|
|
macSampleEndNotify(&synthVoice[sb->voice]);
|
|
} else {
|
|
voiceKill(sb->voice);
|
|
}
|
|
#else
|
|
hwBreak(sb->voice);
|
|
#endif
|
|
}
|
|
|
|
sb->state = 0;
|
|
vs.voices[sb->voice] = 0xff;
|
|
}
|
|
} else {
|
|
sb->state = 0;
|
|
vs.voices[sb->voice] = 0xff;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
unsigned long sndVirtualSampleAllocateBuffers(unsigned char numInstances,
|
|
unsigned long numSamples) {
|
|
long i; // r31
|
|
unsigned long len; // r28
|
|
#line 437
|
|
MUSY_ASSERT_MSG(sndActive, "Sound system is not initialized.");
|
|
MUSY_ASSERT_MSG(numInstances <= 64, "Parameter exceeded maximum number of instances allowable");
|
|
|
|
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;
|
|
}
|
|
|
|
s32 sndVirtualSampleFreeBuffers() {
|
|
u8 i; // r31
|
|
#line 481
|
|
MUSY_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
|
|
MUSY_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
|
|
MUSY_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,
|
|
(u32)&vs.streamBuffer[i]);
|
|
}
|
|
if (len2 != 0) {
|
|
hwFlushStream(base, off2, len2, vs.streamBuffer[i].hwId, vsARAMDMACallback,
|
|
(u32)&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();
|
|
}
|