Files
dusklight/libs/dolphin/src/seq/seq.c
T
Luke Street 4df8ccc871 Reorganize library code into libs/ (#3119)
* Reorganize files into libs/{dolphin,JSystem,PowerPC_EABI_Support,revolution,TRK_MINNOW_DOLPHIN}

* Update configure.py and project.py for new libs structure

* Refactor `#include <dolphin/x.h>` -> `<x.h>`

* Remove `__REVOLUTION_SDK__` forwards from dolphin

* Fix dolphin/ references in revolution

* Wrap `#include <dolphin.h>` in `!__REVOLUTION_SDK__`

* Always build TRK against dolphin headers

* Resolve revolution SDK header resolution issues
2026-03-01 14:35:36 -08:00

469 lines
12 KiB
C

#include <dolphin/dolphin.h>
#include <dolphin/seq.h>
static u8 __SEQMidiEventLength[128] = {
0x02, 0x02, 0x02, 0x02,
0x02, 0x02, 0x02, 0x02,
0x02, 0x02, 0x02, 0x02,
0x02, 0x02, 0x02, 0x02,
0x02, 0x02, 0x02, 0x02,
0x02, 0x02, 0x02, 0x02,
0x02, 0x02, 0x02, 0x02,
0x02, 0x02, 0x02, 0x02,
0x02, 0x02, 0x02, 0x02,
0x02, 0x02, 0x02, 0x02,
0x02, 0x02, 0x02, 0x02,
0x02, 0x02, 0x02, 0x02,
0x02, 0x02, 0x02, 0x02,
0x02, 0x02, 0x02, 0x02,
0x02, 0x02, 0x02, 0x02,
0x02, 0x02, 0x02, 0x02,
0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01,
0x02, 0x02, 0x02, 0x02,
0x02, 0x02, 0x02, 0x02,
0x02, 0x02, 0x02, 0x02,
0x02, 0x02, 0x02, 0x02,
0x00, 0x00, 0x02, 0x01,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00
};
static SEQSEQUENCE* __SEQSequenceList;
// prototypes
static void __SEQPushSequenceList(SEQSEQUENCE* sequence);
static void __SEQRemoveSequenceFromList(SEQSEQUENCE* sequence);
static u32 __SEQGetIntTrack(SEQTRACK* track);
static void __SEQHandleSysExEvent(SEQTRACK* track);
static void __SEQSetTicksPerFrame(SEQTRACK* track, f32 bps);
static void __SEQTempoMetaEvent(SEQTRACK* track);
static void __SEQTrackEnd(SEQTRACK* track);
static void __SEQHandleMetaEvent(SEQTRACK* track);
static void __SEQHandleSynthEvent(SYNSYNTH* synth, SEQTRACK* track);
static void __SEQRunEvent(SYNSYNTH* synth, SEQTRACK* track);
static void __SEQInitTracks(SEQSEQUENCE* sequence, u8* read, int tracks);
static void __SEQReadHeader(SEQSEQUENCE* sequence, u8* midiStream);
static void __SEQPushSequenceList(SEQSEQUENCE* sequence) {
BOOL old;
old = OSDisableInterrupts();
if (__SEQSequenceList) {
sequence->next = __SEQSequenceList;
} else {
sequence->next = NULL;
}
__SEQSequenceList = sequence;
OSRestoreInterrupts(old);
}
static void __SEQRemoveSequenceFromList(SEQSEQUENCE* sequence) {
BOOL old;
SEQSEQUENCE* thisSequence;
SEQSEQUENCE* next;
old = OSDisableInterrupts();
thisSequence = __SEQSequenceList;
__SEQSequenceList = NULL;
while(thisSequence) {
next = thisSequence->next;
if (thisSequence != sequence) {
__SEQPushSequenceList(thisSequence);
}
thisSequence = next;
}
OSRestoreInterrupts(old);
}
static u32 __SEQGetIntTrack(SEQTRACK* track) {
u32 value;
ASSERTLINE(120, track);
for (value = *track->current & 0x7F; *track->current & 0x80; value = (value << 7) + (*track->current & 0x7F)) {
track->current++;
}
track->current++;
return value;
}
static void __SEQHandleSysExEvent(SEQTRACK* track) {
u32 length;
ASSERTLINE(143, track);
length = __SEQGetIntTrack(track);
track->current += length;
}
static void __SEQSetTicksPerFrame(SEQTRACK* track, f32 bps) {
SEQSEQUENCE* sequence;
ASSERTLINE(157, track);
sequence = track->sequence;
track->beatsPerSec = bps;
track->ticksPerFrame = (65536.0f * (160.0f / ((32000.0f / bps) / sequence->timeFormat)));
}
static void __SEQTempoMetaEvent(SEQTRACK* track) {
u32 data;
f32 beatsPerSec;
data = *track->current;
track->current++;
data = (data << 8) + *track->current;
track->current++;
data = (data << 8) + *track->current;
track->current++;
beatsPerSec = 1000000 / (f32)data;
__SEQSetTicksPerFrame(track, beatsPerSec);
}
static void __SEQTrackEnd(SEQTRACK* track) {
SEQSEQUENCE* sequence;
ASSERTLINE(199, track);
sequence = track->sequence;
sequence->tracksRunning--;
track->state = 0;
if (sequence->tracksRunning == 0) {
sequence->end = 1;
}
}
static void __SEQHandleMetaEvent(SEQTRACK* track) {
u8 type;
u32 length;
ASSERTLINE(218, track);
type = *track->current;
track->current++;
switch(type) {
case 0x2F:
__SEQTrackEnd(track);
return;
case 0x51:
length = __SEQGetIntTrack(track);
__SEQTempoMetaEvent(track);
return;
default:
length = __SEQGetIntTrack(track);
track->current += length;
return;
}
}
static void __SEQHandleSynthEvent(SYNSYNTH* synth, SEQTRACK* track) {
u8 ch[3];
u32 bytes;
void (*callback)(void *, u8);
bytes = __SEQMidiEventLength[track->status - 0x80];
ch[0] = track->status;
switch(bytes) {
case 0:
break;
case 1:
ch[1] = *track->current; track->current++;
break;
case 2:
ch[1] = *track->current; track->current++;
ch[2] = *track->current; track->current++;
break;
}
if ((ch[0] & 0xF0) == 0xB0) {
callback = ((SEQSEQUENCE*)track->sequence)->callback[ch[1]];
if (callback) {
callback(track, ch[1]);
}
}
SYNMidiInput(synth, ch);
}
static void __SEQRunEvent(SYNSYNTH* synth, SEQTRACK* track) {
u8 event;
ASSERTLINE(303, synth);
ASSERTLINE(304, track);
event = *track->current;
if (event >= 0x80) {
track->status = event; track->current++;
}
switch(track->status) {
case 0xF7:
case 0xF0:
__SEQHandleSysExEvent(track);
break;
case 0xFF:
__SEQHandleMetaEvent(track);
break;
default:
__SEQHandleSynthEvent(synth, track);
break;
}
if (track->current >= track->end) {
__SEQTrackEnd(track);
}
}
static void __SEQInitTracks(SEQSEQUENCE* sequence, u8* read, int tracks) {
int i;
u8* p;
u32 chunk;
u32 bytes;
SEQTRACK* track;
i = 0;
p = read;
while (tracks) {
while (1) {
chunk = *(u32*)p;
p += 4;
bytes = *(u32*)p;
p += 4;
if (chunk == 'MTrk') {
track = &sequence->track[i];
track->sequence = sequence;
track->start = p;
track->end = &p[bytes];
track->current = p;
track->defaultTicksPerFrame = (u32)(65536.0f * (160.0f / (16000.0f / (f32)sequence->timeFormat)));
track->state = 0;
p += bytes;
break;
}
p += bytes;
}
tracks--;
i++;
}
}
static void __SEQReadHeader(SEQSEQUENCE* sequence, u8* midiStream) {
u8* read;
u32 bytes;
u32 fileType;
read = midiStream;
ASSERTMSGLINE(401, *(u32*)read == 'MThd', "!!!midiStream is not a valid MIDI file\n!!!");
read += 4;
bytes = *(u32*)read;
read += 4;
fileType = *(u16*)read;
read+=2;
sequence->nTracks = *(u16*)read;
read+=2;
sequence->timeFormat = *(s16*)read;
read+=2;
ASSERTMSGLINE(416, sequence->timeFormat >= 0, "!!!SEQ does not support SMPTE time!!!\n");
bytes -= 6;
read += bytes;
switch(fileType) {
case 0:
sequence->nTracks = 1;
__SEQInitTracks(sequence, read, 1);
break;
case 1:
ASSERTMSGLINE(438, sequence->nTracks < 0x40, "exceeded SEQ_MAX_TRACKS, please increase SEQ_MAX_TRACKS\n");
__SEQInitTracks(sequence, read, sequence->nTracks);
break;
default:
ASSERTMSGLINE(446, 0, "!!!Invalid MIDI file type\n!!!");
break;
}
sequence->tracksRunning = sequence->nTracks;
}
void SEQInit(void) {
__SEQSequenceList = NULL;
}
void SEQQuit(void) {
__SEQSequenceList = NULL;
}
void SEQRunAudioFrame(void) {
SEQSEQUENCE* sequence;
u32 i;
SEQTRACK* track;
u32 ticks;
for (sequence = __SEQSequenceList; sequence; sequence = sequence->next) {
if ((sequence->state == 1) || (sequence->state == 2)) {
for (i = 0; i < sequence->nTracks; i++) {
track = &sequence->track[i];
if ((track->state == 1) || (track->state == 2)) {
ticks = track->ticksPerFrame;
if (track->delay > ticks) {
track->delay -= ticks;
} else {
while (ticks >= track->delay) {
ticks -= track->delay;
__SEQRunEvent(&sequence->synth, track);
if (track->state != 0) {
track->delay = __SEQGetIntTrack(track) << 0x10;
} else {
break;
}
}
track->delay -= ticks;
}
}
}
}
if (sequence->end != 0) {
if (sequence->state == 2) {
SEQSetState(sequence, 0);
SEQSetState(sequence, 2);
} else {
SEQSetState(sequence, 0);
}
}
}
}
void SEQAddSequence(SEQSEQUENCE* sequence, u8* midiStream, void* wt, u32 aramBase, u32 zeroBase, u32 priorityVoiceAlloc, u32 priorityNoteOn, u32 priorityNoteRelease) {
int i;
ASSERTLINE(559, sequence);
ASSERTLINE(560, midiStream);
ASSERTLINE(561, wt);
ASSERTLINE(562, aramBase);
ASSERTLINE(563, (priorityVoiceAlloc < 32) && (priorityVoiceAlloc > 0));
ASSERTLINE(564, (priorityNoteOn < 32) && (priorityNoteOn > 0));
ASSERTLINE(565, (priorityNoteRelease < 32) && (priorityNoteRelease > 0));
SYNInitSynth(&sequence->synth, wt, aramBase, zeroBase, priorityVoiceAlloc, priorityNoteOn, priorityNoteRelease);
sequence->state = 0;
for(i = 0; i < 0x80; i++) {
sequence->callback[i] = 0;
}
__SEQReadHeader(sequence, midiStream);
__SEQPushSequenceList(sequence);
}
void SEQRemoveSequence(SEQSEQUENCE* sequence) {
ASSERTLINE(598, sequence);
__SEQRemoveSequenceFromList(sequence);
SYNQuitSynth(&sequence->synth);
}
void SEQRegisterControllerCallback(SEQSEQUENCE* sequence, u8 controller, void (*callback)(void*, u8)) {
ASSERTLINE(617, sequence);
ASSERTLINE(618, controller < 128);
ASSERTLINE(619, callback);
sequence->callback[controller] = callback;
}
void SEQSetState(SEQSEQUENCE* sequence, u32 state) {
int i;
ASSERTLINE(632, sequence);
switch(state) {
case 1:
case 2:
if (sequence->state == 0) {
int old;
old = OSDisableInterrupts();
for (i = 0; i < sequence->nTracks; i++) {
SEQTRACK* track = &sequence->track[i];
track->current = track->start;
track->ticksPerFrame = track->defaultTicksPerFrame;
track->delay = __SEQGetIntTrack(track) << 0x10;
track->state = 1;
}
sequence->tracksRunning = sequence->nTracks;
OSRestoreInterrupts(old);
}
sequence->end = 0;
break;
case 0:
case 3: {
int old;
u8 ch[3];
for (i = 0; i < 16; i++) {
old = OSDisableInterrupts();
ch[0] = (i | 0xB0);
ch[1] = 0x7B;
ch[2] = 0;
SYNMidiInput(&sequence->synth, ch);
OSRestoreInterrupts(old);
}
break;
}
}
sequence->state = state;
}
u32 SEQGetState(SEQSEQUENCE* sequence) {
ASSERTLINE(700, sequence);
return sequence->state;
}
void SEQSetTempo(SEQSEQUENCE* sequence, u32 trackIndex, f32 bpm) {
int i;
ASSERTLINE(711, sequence);
ASSERTLINE(712, (trackIndex < sequence->nTracks) || (trackIndex == SEQ_ALL_TRACKS));
if (trackIndex == -1) {
for (i = 0; i < sequence->nTracks; i++) {
__SEQSetTicksPerFrame(&sequence->track[i], bpm / 60.0f);
}
return;
}
__SEQSetTicksPerFrame(&sequence->track[trackIndex], bpm / 60.0f);
}
f32 SEQGetTempo(SEQSEQUENCE* sequence, u32 trackIndex) {
ASSERTLINE(733, sequence);
ASSERTLINE(734, trackIndex < sequence->nTracks);
return 60.0f* sequence->track[trackIndex].beatsPerSec;
}
void SEQSetVolume(SEQSEQUENCE* sequence, s32 dB) {
ASSERTLINE(745, sequence);
SYNSetMasterVolume(&sequence->synth, dB);
}
long SEQGetVolume(SEQSEQUENCE* sequence) {
ASSERTLINE(756, sequence);
SYNGetMasterVolume(&sequence->synth);
}