dxx-rebirth/misc/hmp.c

561 lines
12 KiB
C
Raw Normal View History

/*
* This code handles HMP files. It can:
* - Open/read/close HMP files
* - Play HMP via Windows MIDI
* - Convert HMP to MIDI for further use
* Based on work of Arne de Bruijn and the JFFEE project
*/
#include <string.h>
2006-03-20 16:43:15 +00:00
#include <stdlib.h>
#include <stdio.h>
#if !(defined(__APPLE__) && defined(__MACH__))
#include <physfs.h>
2006-03-20 16:43:15 +00:00
#else
#include <physfs/physfs.h>
#endif
#include "hmp.h"
#include "u_mem.h"
2006-03-20 16:43:15 +00:00
#include "cfile.h"
#ifdef WORDS_BIGENDIAN
#define MIDIINT(x) (x)
#define MIDISHORT(x) (x)
#else
#define MIDIINT(x) SWAPINT(x)
#define MIDISHORT(x) SWAPSHORT(x)
2006-03-20 16:43:15 +00:00
#endif
#ifdef _WIN32
void hmp_stop(hmp_file *hmp);
#endif
// READ/OPEN/CLOSE HMP
void hmp_close(hmp_file *hmp)
{
int i;
2006-03-20 16:43:15 +00:00
#ifdef _WIN32
hmp_stop(hmp);
#endif
for (i = 0; i < hmp->num_trks; i++)
if (hmp->trks[i].data)
d_free(hmp->trks[i].data);
d_free(hmp);
}
2006-03-20 16:43:15 +00:00
hmp_file *hmp_open(const char *filename) {
int i;
char buf[256];
long data;
CFILE *fp;
hmp_file *hmp;
int num_tracks;
unsigned char *p;
if (!(fp = cfopen((char *)filename, "rb")))
return NULL;
hmp = d_malloc(sizeof(hmp_file));
2006-03-20 16:43:15 +00:00
if (!hmp) {
cfclose(fp);
return NULL;
}
memset(hmp, 0, sizeof(*hmp));
if ((cfread(buf, 1, 8, fp) != 8) || (memcmp(buf, "HMIMIDIP", 8)))
{
cfclose(fp);
hmp_close(hmp);
return NULL;
}
2006-03-20 16:43:15 +00:00
if (cfseek(fp, 0x30, SEEK_SET))
{
cfclose(fp);
hmp_close(hmp);
return NULL;
}
2006-03-20 16:43:15 +00:00
if (cfread(&num_tracks, 4, 1, fp) != 1)
{
cfclose(fp);
hmp_close(hmp);
return NULL;
}
2006-03-20 16:43:15 +00:00
if ((num_tracks < 1) || (num_tracks > HMP_TRACKS))
{
cfclose(fp);
hmp_close(hmp);
return NULL;
}
2006-03-20 16:43:15 +00:00
hmp->num_trks = num_tracks;
hmp->tempo = 120;
2006-03-20 16:43:15 +00:00
if (cfseek(fp, 0x308, SEEK_SET))
{
cfclose(fp);
hmp_close(hmp);
return NULL;
}
2006-03-20 16:43:15 +00:00
for (i = 0; i < num_tracks; i++) {
2006-03-20 16:43:15 +00:00
if ((cfseek(fp, 4, SEEK_CUR)) || (cfread(&data, 4, 1, fp) != 1))
{
cfclose(fp);
hmp_close(hmp);
return NULL;
}
2006-03-20 16:43:15 +00:00
data -= 12;
hmp->trks[i].len = data;
if (!(p = hmp->trks[i].data = d_malloc(data)))
{
cfclose(fp);
hmp_close(hmp);
return NULL;
2006-03-20 16:43:15 +00:00
}
/* finally, read track data */
2006-03-20 16:43:15 +00:00
if ((cfseek(fp, 4, SEEK_CUR)) || (cfread(p, data, 1, fp) != 1))
{
cfclose(fp);
hmp_close(hmp);
return NULL;
}
}
cfclose(fp);
return hmp;
2006-03-20 16:43:15 +00:00
}
#ifdef WIN32
// PLAY HMP AS MIDI
void hmp_stop(hmp_file *hmp)
{
2006-03-20 16:43:15 +00:00
MIDIHDR *mhdr;
if (!hmp->stop) {
hmp->stop = 1;
//PumpMessages();
midiStreamStop(hmp->hmidi);
while (hmp->bufs_in_mm)
2006-03-20 16:43:15 +00:00
Sleep(0);
}
while ((mhdr = hmp->evbuf)) {
midiOutUnprepareHeader((HMIDIOUT)hmp->hmidi, mhdr, sizeof(MIDIHDR));
hmp->evbuf = mhdr->lpNext;
d_free(mhdr);
2006-03-20 16:43:15 +00:00
}
if (hmp->hmidi) {
midiStreamClose(hmp->hmidi);
hmp->hmidi = NULL;
}
}
/*
* read a HMI type variabele length number
*/
static int get_var_num_hmi(unsigned char *data, int datalen, unsigned long *value) {
unsigned char *p;
unsigned long v = 0;
int shift = 0;
p = data;
while ((datalen > 0) && !(*p & 0x80)) {
v += *(p++) << shift;
shift += 7;
datalen --;
}
2006-03-20 16:43:15 +00:00
if (!datalen)
return 0;
v += (*(p++) & 0x7f) << shift;
2006-03-20 16:43:15 +00:00
if (value) *value = v;
return p - data;
2006-03-20 16:43:15 +00:00
}
/*
* read a MIDI type variabele length number
*/
static int get_var_num(unsigned char *data, int datalen,
unsigned long *value) {
2006-03-20 16:43:15 +00:00
unsigned char *orgdata = data;
unsigned long v = 0;
while ((datalen > 0) && (*data & 0x80))
v = (v << 7) + (*(data++) & 0x7f);
if (!datalen)
return 0;
v = (v << 7) + *(data++);
if (value) *value = v;
return data - orgdata;
2006-03-20 16:43:15 +00:00
}
static int get_event(hmp_file *hmp, event *ev) {
static int cmdlen[7]={3,3,3,3,2,2,3};
2006-03-20 16:43:15 +00:00
unsigned long got;
unsigned long mindelta, delta;
int i, ev_num;
hmp_track *trk, *fndtrk;
mindelta = INT_MAX;
fndtrk = NULL;
for (trk = hmp->trks, i = hmp->num_trks; (i--) > 0; trk++) {
if (!trk->left)
continue;
if (!(got = get_var_num_hmi(trk->cur, trk->left, &delta)))
return HMP_INVALID_FILE;
if (trk->left > got + 2 && *(trk->cur + got) == 0xff
&& *(trk->cur + got + 1) == 0x2f) {/* end of track */
trk->left = 0;
continue;
}
delta += trk->cur_time - hmp->cur_time;
if (delta < mindelta) {
mindelta = delta;
fndtrk = trk;
}
}
if (!(trk = fndtrk))
return HMP_EOF;
got = get_var_num_hmi(trk->cur, trk->left, &delta);
trk->cur_time += delta;
ev->delta = trk->cur_time - hmp->cur_time;
hmp->cur_time = trk->cur_time;
if ((trk->left -= got) < 3)
return HMP_INVALID_FILE;
2006-03-20 16:43:15 +00:00
trk->cur += got;
/*memset(ev, 0, sizeof(*ev));*/ev->datalen = 0;
ev->msg[0] = ev_num = *(trk->cur++);
trk->left--;
if (ev_num < 0x80)
return HMP_INVALID_FILE; /* invalid command */
2006-03-20 16:43:15 +00:00
if (ev_num < 0xf0) {
ev->msg[1] = *(trk->cur++);
trk->left--;
if (cmdlen[((ev_num) >> 4) - 8] == 3) {
ev->msg[2] = *(trk->cur++);
trk->left--;
}
} else if (ev_num == 0xff) {
ev->msg[1] = *(trk->cur++);
trk->left--;
if (!(got = get_var_num(ev->data = trk->cur,
trk->left, (unsigned long *)&ev->datalen)))
return HMP_INVALID_FILE;
trk->cur += ev->datalen;
2006-03-20 16:43:15 +00:00
if (trk->left <= ev->datalen)
return HMP_INVALID_FILE;
trk->left -= ev->datalen;
} else /* sysex -> error */
return HMP_INVALID_FILE;
return 0;
}
static int fill_buffer(hmp_file *hmp) {
MIDIHDR *mhdr = hmp->evbuf;
unsigned int *p = (unsigned int *)(mhdr->lpData + mhdr->dwBytesRecorded);
unsigned int *pend = (unsigned int *)(mhdr->lpData + mhdr->dwBufferLength);
unsigned int i;
event ev;
while (p + 4 <= pend) {
if (hmp->pending_size) {
i = (p - pend) * 4;
if (i > hmp->pending_size)
i = hmp->pending_size;
*(p++) = hmp->pending_event | i;
*(p++) = 0;
memcpy((unsigned char *)p, hmp->pending, i);
hmp->pending_size -= i;
p += (i + 3) / 4;
} else {
if ((i = get_event(hmp, &ev))) {
mhdr->dwBytesRecorded = ((unsigned char *)p) - ((unsigned char *)mhdr->lpData);
return i;
}
if (ev.datalen) {
hmp->pending_size = ev.datalen;
hmp->pending = ev.data;
hmp->pending_event = ev.msg[0] << 24;
} else {
*(p++) = ev.delta;
*(p++) = 0;
*(p++) = (((DWORD)MEVT_SHORTMSG) << 24) |
((DWORD)ev.msg[0]) |
(((DWORD)ev.msg[1]) << 8) |
(((DWORD)ev.msg[2]) << 16);
}
}
}
mhdr->dwBytesRecorded = ((unsigned char *)p) - ((unsigned char *)mhdr->lpData);
return 0;
}
static int setup_buffers(hmp_file *hmp) {
int i;
MIDIHDR *buf, *lastbuf;
lastbuf = NULL;
for (i = 0; i < HMP_BUFFERS; i++) {
if (!(buf = d_malloc(HMP_BUFSIZE + sizeof(MIDIHDR))))
2006-03-20 16:43:15 +00:00
return HMP_OUT_OF_MEM;
memset(buf, 0, sizeof(MIDIHDR));
buf->lpData = (unsigned char *)buf + sizeof(MIDIHDR);
buf->dwBufferLength = HMP_BUFSIZE;
buf->dwUser = (DWORD)hmp;
buf->lpNext = lastbuf;
lastbuf = buf;
}
hmp->evbuf = lastbuf;
return 0;
}
static void reset_tracks(struct hmp_file *hmp)
{
int i;
for (i = 0; i < hmp->num_trks; i++) {
hmp->trks [i].cur = hmp->trks [i].data;
hmp->trks [i].left = hmp->trks [i].len;
hmp->trks [i].cur_time = 0;
}
hmp->cur_time=0;
}
static void _stdcall midi_callback(HMIDISTRM hms, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2) {
MIDIHDR *mhdr;
hmp_file *hmp;
int rc;
if (uMsg != MOM_DONE)
return;
mhdr = ((MIDIHDR *)dw1);
mhdr->dwBytesRecorded = 0;
hmp = (hmp_file *)(mhdr->dwUser);
mhdr->lpNext = hmp->evbuf;
hmp->evbuf = mhdr;
hmp->bufs_in_mm--;
if (!hmp->stop) {
while (fill_buffer(hmp) == HMP_EOF) {
if (hmp->bLoop)
2006-03-20 16:43:15 +00:00
hmp->stop = 0;
2006-05-05 14:14:55 +00:00
else
hmp->stop = 1;
2006-03-20 16:43:15 +00:00
reset_tracks(hmp);
}
if ((rc = midiStreamOut(hmp->hmidi, hmp->evbuf,
sizeof(MIDIHDR))) != MMSYSERR_NOERROR) {
/* ??? */
} else {
hmp->evbuf = hmp->evbuf->lpNext;
hmp->bufs_in_mm++;
}
}
}
static void setup_tempo(hmp_file *hmp, unsigned long tempo) {
MIDIHDR *mhdr = hmp->evbuf;
unsigned int *p = (unsigned int *)(mhdr->lpData + mhdr->dwBytesRecorded);
*(p++) = 0;
*(p++) = 0;
*(p++) = (((DWORD)MEVT_TEMPO)<<24) | tempo;
mhdr->dwBytesRecorded += 12;
}
int hmp_play(hmp_file *hmp, int bLoop)
{
int rc;
MIDIPROPTIMEDIV mptd;
#if 1
unsigned int numdevs;
int i=0; // ZICO - LOOP HERE
numdevs=midiOutGetNumDevs();
hmp->devid=-1;
do
{
MIDIOUTCAPS devcaps;
midiOutGetDevCaps(i,&devcaps,sizeof(MIDIOUTCAPS));
if ((devcaps.wTechnology==MOD_FMSYNTH) || (devcaps.wTechnology==MOD_SYNTH))
hmp->devid=i;
i++;
} while ((i<(int)numdevs) && (hmp->devid==-1));
2006-03-20 16:43:15 +00:00
if (hmp->devid == -1)
#endif
hmp->bLoop = bLoop;
2006-03-20 16:43:15 +00:00
hmp->devid = MIDI_MAPPER;
if ((rc = setup_buffers(hmp)))
return rc;
if ((midiStreamOpen(&hmp->hmidi, &hmp->devid,1, (DWORD) (size_t) midi_callback, 0, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR) {
2006-03-20 16:43:15 +00:00
hmp->hmidi = NULL;
return HMP_MM_ERR;
}
mptd.cbStruct = sizeof(mptd);
mptd.dwTimeDiv = hmp->tempo;
if ((midiStreamProperty(hmp->hmidi,(LPBYTE)&mptd,MIDIPROP_SET|MIDIPROP_TIMEDIV)) != MMSYSERR_NOERROR) {
2006-03-20 16:43:15 +00:00
return HMP_MM_ERR;
}
reset_tracks(hmp);
setup_tempo(hmp, 0x0f4240);
hmp->stop = 0;
while (hmp->evbuf) {
if ((rc = fill_buffer(hmp))) {
if (rc == HMP_EOF) {
reset_tracks(hmp);
continue;
} else
return rc;
}
if ((rc = midiOutPrepareHeader((HMIDIOUT)hmp->hmidi, hmp->evbuf,
sizeof(MIDIHDR))) != MMSYSERR_NOERROR) {
return HMP_MM_ERR;
}
if ((rc = midiStreamOut(hmp->hmidi, hmp->evbuf,
sizeof(MIDIHDR))) != MMSYSERR_NOERROR) {
return HMP_MM_ERR;
}
hmp->evbuf = hmp->evbuf->lpNext;
hmp->bufs_in_mm++;
}
midiStreamRestart(hmp->hmidi);
return 0;
}
#endif
// CONVERSION FROM HMP TO MIDI
static int hmptrk2mid(ubyte* data, int size, PHYSFS_file *mid)
{
ubyte *dptr = data;
ubyte lc1 = 0,last_com = 0;
uint t = 0, d;
int n1, n2;
int offset = cftell(mid);
while (data < dptr + size)
{
if (data[0] & 0x80) {
ubyte b = (data[0] & 0x7F);
PHYSFS_write(mid, &b, sizeof (b), 1);
t+=b;
}
else {
d = (data[0] & 0x7F);
n1 = 0;
while ((data[n1] & 0x80) == 0) {
n1++;
d += (data[n1] & 0x7F) << (n1 * 7);
}
t += d;
n1 = 1;
while ((data[n1] & 0x80) == 0) {
n1++;
if (n1 == 4)
return 0;
}
for(n2 = 0; n2 <= n1; n2++) {
ubyte b = (data[n1 - n2] & 0x7F);
if (n2 != n1)
b |= 0x80;
PHYSFS_write(mid, &b, sizeof(b), 1);
}
data += n1;
}
data++;
if (*data == 0xFF) { //meta?
PHYSFS_write(mid, data, 3 + data [2], 1);
if (data[1] == 0x2F)
break;
}
else {
lc1=data[0] ;
if ((lc1&0x80) == 0)
return 0;
switch (lc1 & 0xF0) {
case 0x80:
case 0x90:
case 0xA0:
case 0xB0:
case 0xE0:
if (lc1 != last_com)
PHYSFS_write(mid, &lc1, sizeof (lc1), 1);
PHYSFS_write(mid, data + 1, 2, 1);
data += 3;
break;
case 0xC0:
case 0xD0:
if (lc1 != last_com)
PHYSFS_write(mid, &lc1, sizeof (lc1), 1);
PHYSFS_write(mid, data + 1, 1, 1);
data += 2;
break;
default:
return 0;
}
last_com = lc1;
}
}
return (cftell(mid) - offset);
}
ubyte tempo [19] = {'M','T','r','k',0,0,0,11,0,0xFF,0x51,0x03,0x18,0x80,0x00,0,0xFF,0x2F,0};
void hmp2mid(char *hmp_name, char *mid_name)
{
PHYSFS_file *mid=NULL;
int mi, i, loc;
short ms;
hmp_file *hmp=NULL;
hmp = hmp_open(hmp_name);
if (hmp == NULL)
return;
mid = PHYSFSX_openWriteBuffered(mid_name);
if (mid == NULL)
return;
// write MIDI-header
PHYSFS_write(mid, "MThd", 4, 1);
mi = MIDIINT(6);
PHYSFS_write(mid, &mi, sizeof(mi), 1);
ms = MIDISHORT(1);
PHYSFS_write(mid, &ms, sizeof(mi), 1);
ms = MIDISHORT(hmp->num_trks);
PHYSFS_write(mid, &ms, sizeof(ms), 1);
ms = MIDISHORT((short) 0xC0);
PHYSFS_write(mid, &ms, sizeof(ms), 1);
PHYSFS_write(mid, tempo, sizeof(tempo), 1);
// tracks
for (i = 1; i < hmp->num_trks; i++)
{
PHYSFS_write(mid, "MTrk", 4, 1);
loc = cftell(mid);
mi = 0;
PHYSFS_write(mid, &mi, sizeof(mi), 1);
mi = hmptrk2mid(hmp->trks[i].data, hmp->trks[i].len, mid);
mi = MIDIINT(mi);
cfseek(mid, loc, SEEK_SET);
PHYSFS_write(mid, &mi, sizeof(mi), 1);
cfseek(mid, 0, SEEK_END);
}
cfclose(mid);
}