Rev. | 4 |
---|---|
大小 | 33,807 字节 |
时间 | 2011-09-03 15:22:57 |
作者 | toshinagata1964 |
Log Message | initial import |
/*
MDSequenceSMF.c
Created by Toshi Nagata, 2000.11.24.
Copyright (c) 2000-2011 Toshi Nagata. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#include "MDHeaders.h"
#include <stdlib.h> /* for malloc(), realloc(), and free() */
#include <string.h> /* for memset() */
#include <stdio.h>
#include <ctype.h>
#define DEBUG_PRINT 0
/* An internal struct for converting SMF to MD format */
typedef struct MDSMFConvert MDSMFConvert;
struct MDSMFConvert {
/* The information for the whole sequence */
STREAM stream;
MDSequence * sequence; /* the resulting sequence */
long timebase; /* the SMF timebase */
long max_tick; /* the maximum tick (the length of the sequence) */
long trkno; /* the number of tracks */
/* The information for each track */
MDTrack * temptrk; /* the current track */
long track_index; /* the current track number */
long tick; /* the tick of the last event */
long deltatime; /* the deltatime of the current event */
unsigned char status; /* running status */
unsigned char track_channel; /* track channel (16 if the sequence is multi-track mode) */
long pos; /* the file position where the track size is to be written */
MDSequenceCallback callback; /* A callback function. Periodically called, and abort if 0 */
long filesize; /* total file size */
void * cbdata; /* callback data */
};
/* SMF コントロールで特別扱いするもの */
enum {
kMDEventSMFBankSelectMSB = 0,
kMDEventSMFDataEntryMSB = 6,
kMDEventSMFBankSelectLSB = 32,
kMDEventSMFDataEntryLSB = 38,
kMDEventSMFDataIncrement = 96,
kMDEventSMFDataDecrement = 97,
kMDEventSMFNRPNLSB = 98,
kMDEventSMFNRPNMSB = 99,
kMDEventSMFRPNLSB = 100,
kMDEventSMFRPNMSB = 101
};
#define kMDMaxSMFTempo 16777215 /* 2^24 - 1 */
static long
MDSequenceTempoToSMFTempo(float tempo)
{
if (tempo < kMDMinTempo)
return kMDMaxSMFTempo;
if (tempo > kMDMaxTempo)
return (long)(60000000.0 / kMDMaxTempo);
else return (long)(60000000.0 / tempo);
}
static long
MDSequenceSMFTempoToTempo(long smfTempo)
{
if (smfTempo <= (long)(60000000.0 / kMDMaxTempo))
return kMDMaxTempo;
else if (smfTempo > (long)(60000000.0 / kMDMinTempo))
return kMDMinTempo;
else return 60000000.0 / smfTempo;
}
#pragma mark ====== Reading SMF ======
static MDStatus
MDSequenceReadSMFReadMessage(MDSMFConvert *cref, MDEvent *eref)
{
long length;
unsigned char *msg;
/* Read the message length */
if (MDReadStreamFormat(cref->stream, "w", &length) != 1)
return kMDErrorUnexpectedEOF;
/* Allocate the memory */
/* For kMDEventSysex, append the beginning 'F0'. */
if (MDGetKind(eref) == kMDEventSysex)
length++;
if (MDSetMessageLength(eref, length) < 0)
return kMDErrorOutOfMemory;
msg = MDGetMessagePtr(eref, NULL);
if (MDGetKind(eref) == kMDEventSysex) {
msg[0] = 0xf0;
if (FREAD_(msg + 1, length - 1, cref->stream) < length - 1)
return kMDErrorUnexpectedEOF;
} else {
/* For messages other than Sysex, the data can be always treated as a C string
as MDSetMessageLength() automatically appends a terminating null byte */
if (FREAD_(msg, length, cref->stream) < length)
return kMDErrorUnexpectedEOF;
}
return kMDNoError;
}
/* Read one meta event */
static MDStatus
MDSequenceReadSMFMetaEvent(MDSMFConvert *cref, MDEvent *eref)
{
MDStatus result = kMDNoError;
long length;
int n;
unsigned char s[8], *metaDataPtr;
n = GETC(cref->stream);
if (n == EOF)
return kMDErrorUnexpectedEOF;
MDSetCode(eref, n);
switch (n) {
case kMDMetaEndOfTrack:
if (cref->max_tick < cref->tick)
cref->max_tick = cref->tick;
/* Read the message length (should be always zero, but not checked) */
if (MDReadStreamFormat(cref->stream, "w", &length) != 1) {
result = kMDErrorUnexpectedEOF;
break;
}
MDSetKind(eref, kMDEventStop);
break;
case kMDMetaDuration: {
MDTickType duration;
MDSetKind(eref, kMDEventInternalDuration);
if (MDReadStreamFormat(cref->stream, "ww", &length, &duration) != 2) {
result = kMDErrorUnexpectedEOF;
break;
}
MDSetDuration(eref, duration);
break;
}
case kMDMetaPortNumber:
case kMDMetaTempo:
case kMDMetaSMPTE:
case kMDMetaTimeSignature:
case kMDMetaKey:
MDSetKind(eref, kMDEventMeta);
if (MDReadStreamFormat(cref->stream, "w", &length) != 1) {
result = kMDErrorUnexpectedEOF;
break;
}
if (length >= 8) {
result = kMDErrorWrongMetaEvent;
break;
}
memset(s, 0, sizeof(s));
if (FREAD_(s, length, cref->stream) < length) {
result = kMDErrorUnexpectedEOF;
break;
}
metaDataPtr = MDGetMetaDataPtr(eref);
if (n == kMDMetaPortNumber) {
MDSetKind(eref, kMDEventPortNumber);
MDSetData1(eref, s[0]);
} else if (n == kMDMetaTempo) {
MDSetKind(eref, kMDEventTempo);
MDSetTempo(eref, MDSequenceSMFTempoToTempo(s[0] * 65536.0 + s[1] * 256.0 + s[2]));
} else if (n == kMDMetaSMPTE) {
MDSMPTERecord *smp = MDGetSMPTERecordPtr(eref);
MDSetKind(eref, kMDEventSMPTE);
smp->hour = s[0];
smp->min = s[1];
smp->sec = s[2];
smp->frame = s[3];
smp->subframe = s[4];
} else if (n == kMDMetaTimeSignature) {
MDSetKind(eref, kMDEventTimeSignature);
metaDataPtr[0] = s[0];
metaDataPtr[1] = s[1];
metaDataPtr[2] = s[2];
metaDataPtr[3] = s[3];
} else if (n == kMDMetaKey) {
MDSetKind(eref, kMDEventKey);
metaDataPtr[0] = s[0];
metaDataPtr[1] = s[1];
} else {
result = kMDErrorWrongMetaEvent;
break;
}
break;
case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8:
case 9: case 10: case 11: case 12: case 13: case 14: case 15:
/* code 1-15 are reserved for various meta events */
MDSetKind(eref, kMDEventMetaText);
result = MDSequenceReadSMFReadMessage(cref, eref);
break;
default:
MDSetKind(eref, kMDEventMetaMessage);
result = MDSequenceReadSMFReadMessage(cref, eref);
break;
}
return result;
}
static int
MDSequenceReadOneChar(void *ptr)
{
return GETC((STREAM)ptr);
}
/* Read one channel event. n is the first byte (status byte, or the first data
byte if running status is used) */
static MDStatus
MDSequenceReadSMFChannelEvent(MDSMFConvert *cref, MDEvent *eref, int n)
{
return MDEventFromMIDIMessage(eref, n, cref->status, MDSequenceReadOneChar, cref->stream, &(cref->status));
#if 0
MDStatus result = kMDNoError;
int data1, data2;
unsigned char ch; /* MIDI channel */
/* Get the status byte */
if (n < 0x80) {
/* running status */
data1 = n;
n = cref->status;
} else {
cref->status = n;
data1 = GETC(cref->stream);
if (data1 == EOF)
return kMDErrorUnexpectedEOF;
}
/* Get the MD track number */
ch = (n & 0x0f); /* MIDI channel */
MDSetChannel(eref, ch);
switch (n & 0xf0) {
case kMDEventSMFNoteOff:
case kMDEventSMFNoteOn:
data2 = GETC(cref->stream);
if (data2 == EOF) {
result = kMDErrorUnexpectedEOF;
break;
}
data2 &= 0xff;
if ((n & 0xf0) == kMDEventSMFNoteOn && data2 != 0) {
/* Note on */
MDSetKind(eref, kMDEventNote);
MDSetCode(eref, data1);
MDSetNoteOnVelocity(eref, data2);
MDSetNoteOffVelocity(eref, 0);
MDSetDuration(eref, 0); /* Set temporary duration: 0 indicates "note-on that is not paired yet" */
} else {
/* Note off */
MDSetKind(eref, kMDEventInternalNoteOff);
MDSetCode(eref, data1);
MDSetNoteOnVelocity(eref, 0);
MDSetNoteOffVelocity(eref, data2);
}
break;
case kMDEventSMFKeyPressure:
data2 = GETC(cref->stream);
if (data2 == EOF) {
result = kMDErrorUnexpectedEOF;
break;
}
MDSetKind(eref, kMDEventKeyPres);
MDSetCode(eref, data1);
MDSetData1(eref, data2);
break;
case kMDEventSMFControl:
data2 = GETC(cref->stream);
if (data2 == EOF) {
result = kMDErrorUnexpectedEOF;
break;
}
MDSetKind(eref, kMDEventControl);
MDSetCode(eref, data1);
MDSetData1(eref, data2);
break;
case kMDEventSMFProgram:
MDSetKind(eref, kMDEventProgram);
MDSetData1(eref, data1);
break;
case kMDEventSMFChannelPressure:
MDSetKind(eref, kMDEventChanPres);
MDSetData1(eref, data1);
break;
case kMDEventSMFPitchBend:
data2 = GETC(cref->stream);
if (data2 == EOF) {
result = kMDErrorUnexpectedEOF;
break;
}
MDSetKind(eref, kMDEventPitchBend);
MDSetData1(eref, ((data1 & 0x7f) + ((data2 & 0x7f) << 7)) - 8192);
break;
default:
result = kMDErrorUnknownChannelEvent;
break;
} /* end switch */
return result;
#endif
}
/* Read one SMF track */
static MDStatus
MDSequenceReadSMFTrack(MDSMFConvert *cref)
{
MDEvent event;
MDStatus result = kMDNoError;
MDPointer *ptr;
int n, count;
unsigned char quitFlag, skipFlag;
MDTickType maxTick = 0;
MDTickType metaDuration = 0;
// MDTrack *noteOffTrack;
// MDPointer *noteOffPtr;
/* Initialize track info */
cref->tick = 0;
cref->deltatime = 0;
cref->temptrk = MDTrackNew();
if (cref->temptrk == NULL)
return kMDErrorOutOfMemory;
ptr = MDPointerNew(cref->temptrk);
if (ptr == NULL)
return kMDErrorOutOfMemory;
// noteOffTrack = MDTrackNew();
// if (noteOffTrack == NULL)
// return kMDErrorOutOfMemory;
// noteOffPtr = MDPointerNew(noteOffTrack);
// if (noteOffPtr == NULL)
// return kMDErrorOutOfMemory;
quitFlag = 0;
MDEventInit(&event);
count = 0;
while (quitFlag == 0) {
if (++count >= 1000) {
if (cref->callback != NULL) {
n = (*cref->callback)((double)FTELL(cref->stream) / cref->filesize * 100, cref->cbdata);
if (n == 0) {
result = kMDErrorUserInterrupt;
break;
}
}
count = 0;
}
/* Read the delta time */
if (MDReadStreamFormat(cref->stream, "w", &cref->deltatime) != 1) {
result = kMDErrorUnexpectedEOF;
break;
}
cref->tick += cref->deltatime;
MDSetTick(&event, cref->tick);
MDSetChannel(&event, 0);
skipFlag = 0;
/* Read the status byte */
n = GETC(cref->stream);
if (n == EOF) {
result = kMDErrorUnexpectedEOF;
break;
}
if (n == kMDEventSMFSysex) { /* sysex events */
MDSetKind(&event, kMDEventSysex);
MDSetChannel(&event, 16);
result = MDSequenceReadSMFReadMessage(cref, &event);
} else if (n == kMDEventSMFSysexF7) { /* sysex events (continued) */
MDSetKind(&event, kMDEventSysexCont);
MDSetChannel(&event, 16);
result = MDSequenceReadSMFReadMessage(cref, &event);
} else if (n == kMDEventSMFMeta) { /* meta events */
MDSetChannel(&event, 17); /* not a MIDI event */
result = MDSequenceReadSMFMetaEvent(cref, &event);
if (MDGetKind(&event) == kMDEventStop) {
skipFlag = quitFlag = 1;
} else if (MDGetKind(&event) == kMDEventMetaText) {
char buf[256];
switch (MDGetCode(&event)) {
case kMDMetaSequenceName:
MDTrackGetName(cref->temptrk, buf, sizeof buf);
if (buf[0] == 0) {
result = MDTrackSetName(cref->temptrk, (const char *)MDGetMessageConstPtr(&event, NULL));
skipFlag = 1;
}
break;
case kMDMetaDeviceName:
MDTrackGetDeviceName(cref->temptrk, buf, sizeof buf);
if (buf[0] == 0) {
result = MDTrackSetDeviceName(cref->temptrk, (const char *)MDGetMessageConstPtr(&event, NULL));
skipFlag = 1;
}
break;
}
} else if (MDGetKind(&event) == kMDEventInternalDuration) {
metaDuration = MDGetDuration(&event);
skipFlag = 1;
}
} else { /* channel events */
result = MDSequenceReadSMFChannelEvent(cref, &event, n);
}
if (result != kMDNoError)
break;
if (MDGetKind(&event) == kMDEventInternalNoteOn) {
if (metaDuration > 0)
MDSetDuration(&event, metaDuration);
} else if (MDGetKind(&event) == kMDEventInternalNoteOff) {
result = MDTrackMatchNoteOff(cref->temptrk, &event);
if (result != kMDNoError)
break;
skipFlag = 1;
/* Register note-off into the separate track */
/* dprintf(2, "Note-off event encountered: tick %ld code %d vel %d\n", MDGetTick(&event), MDGetCode(&event), MDGetNoteOffVelocity(&event));
if (MDTrackAppendEvents(noteOffTrack, &event, 1) < 1) {
result = kMDErrorOutOfMemory;
break;
}
skipFlag = 1; */
}
metaDuration = 0;
if (!skipFlag) {
if (MDTrackAppendEvents(cref->temptrk, &event, 1) < 1) {
result = kMDErrorOutOfMemory;
break;
}
#if DEBUG_PRINT
{
char buf[1024];
printf("%s\n", MDEventToString(&event, buf, sizeof buf));
}
#endif
}
MDEventClear(&event);
} /* end while (quitFlag == 0) */
/* Set the track duration */
if (maxTick < cref->tick)
maxTick = cref->tick;
MDTrackSetDuration(cref->temptrk, maxTick);
/* Match the note-on/note-off pairs */
/* result = MDTrackMatchNoteOffInTrack(cref->temptrk, noteOffTrack); */
if (result == kMDNoError) {
char buf[256];
/* Guess track name */
MDTrackGetName(cref->temptrk, buf, sizeof buf);
if (buf[0] == 0) {
MDTrackGuessName(cref->temptrk, buf, 256);
result = MDTrackSetName(cref->temptrk, buf);
}
}
if (result == kMDNoError) {
char buf[256];
/* Guess device name */
MDTrackGetDeviceName(cref->temptrk, buf, sizeof buf);
if (buf[0] == 0) {
MDTrackGuessDeviceName(cref->temptrk, buf, 256);
result = MDTrackSetDeviceName(cref->temptrk, buf);
}
/* Guess the device number from the device name */
MDTrackSetDevice(cref->temptrk, MDPlayerGetDestinationNumberFromName(buf));
}
if (result != kMDErrorOutOfMemory) {
MDPointerRelease(ptr);
MDSequenceInsertTrack(cref->sequence, -1, cref->temptrk);
MDTrackRelease(cref->temptrk); /* the track will be retained by the sequence */
cref->temptrk = NULL;
}
// MDPointerRelease(noteOffPtr);
// MDTrackRelease(noteOffTrack);
return result;
}
MDStatus
MDSequenceReadSMF(MDSequence *inSequence, STREAM stream, MDSequenceCallback callback, void *cbdata)
{
MDStatus result = kMDNoError;
MDSMFConvert conv;
short fmt, trkno, timebase; /* SMF format, track number, timebase */
long size, pos;
char tag[8];
if (inSequence == NULL || stream == NULL)
return kMDErrorInternalError;
/* Initialize the convert record */
memset(&conv, 0, sizeof(conv));
conv.stream = stream;
conv.sequence = inSequence;
conv.callback = callback;
pos = FTELL(stream);
FSEEK(stream, 0, SEEK_END);
conv.track_channel = 0; /* Not to be used */
conv.filesize = FTELL(stream) - pos;
conv.cbdata = cbdata;
FSEEK(stream, pos, SEEK_SET);
/* Read the file header */
if (MDReadStreamFormat(conv.stream, "A4Nn3", tag, &size, &fmt, &trkno, &timebase) == 5
&& size == 6 && strcmp(tag, "MThd") == 0) {
if (fmt != 0 && fmt != 1) {
result = kMDErrorUnsupportedSMFFormat;
} else {
conv.timebase = timebase;
conv.trkno = trkno;
MDSequenceSetTimebase(inSequence, timebase);
}
} else {
result = kMDErrorHeaderChunkNotFound;
}
/* Read each track */
while (result == kMDNoError && MDReadStreamFormat(conv.stream, "A4N", tag, &size) == 2) {
/* Check the tag */
if (strcmp(tag, "MTrk") == 0) {
result = MDSequenceReadSMFTrack(&conv);
if (result != kMDErrorOutOfMemory)
conv.track_index++;
else break;
if (--trkno <= 0)
break;
} else {
/* Skip this block */
FSEEK(stream, size, SEEK_CUR);
}
}
conv.trkno = conv.track_index;
#if DEBUG_PRINT
{ /* for debug */
int i;
char buf[1024];
/* MDTrackPrintOneEvent(NULL, NULL); */
for (i = 0; i < MDSequenceGetNumberOfTracks(inSequence); i++) {
MDPointer *ptr;
MDEvent *ev;
MDTrack *track;
track = MDSequenceGetTrack(inSequence, i);
ptr = MDPointerNew(track);
while ((ev = MDPointerForward(ptr)) != NULL) {
printf("%ld: %s\n", (long)MDPointerGetPosition(ptr), MDEventToString(ev, buf, sizeof buf));
}
MDPointerRelease(ptr);
}
/* MDTrackPrintOneEvent((MDEvent *)(-1), NULL); */
}
#endif
return result;
}
#pragma mark ====== Writing SMF ======
static MDStatus
MDSequenceWriteSMFDeltaTime(MDSMFConvert *cref, MDTickType tick)
{
cref->deltatime = tick - cref->tick;
if (MDWriteStreamFormat(cref->stream, "w", cref->deltatime) != 1)
return kMDErrorCannotWriteToStream;
cref->tick += cref->deltatime;
return kMDNoError;
}
static MDStatus
MDSequenceWriteSMFWriteMessage(MDSMFConvert *cref, const unsigned char *p, long length)
{
/* Write the message length */
if (MDWriteStreamFormat(cref->stream, "w", length) != 1)
return kMDErrorCannotWriteToStream;
/* Write the message body */
if (FWRITE_(p, length, cref->stream) < length)
return kMDErrorCannotWriteToStream;
return kMDNoError;
}
/* Write a special "duration" event */
static MDStatus
MDSequenceWriteSMFSpecialDurationEvent(MDSMFConvert *cref, MDEvent *eref)
{
MDTickType d;
int i;
unsigned char s[12];
if (MDGetKind(eref) != kMDEventNote)
return kMDErrorInternalError;
/* Convert the duration to BER-compressed form */
d = MDGetDuration(eref);
i = sizeof(s) - 1;
s[i] = (d & 0x7f);
while (i > 4) {
d >>= 7;
if (d == 0)
break;
s[--i] = ((d & 0x7f) | 0x80);
}
s[i - 1] = sizeof(s) - i; /* Message length */
i--;
s[--i] = kMDMetaDuration;
s[--i] = kMDEventSMFMeta;
/* s[--i] = 0; *//* delta time */
if (FWRITE_(s + i, sizeof(s) - i, cref->stream) < sizeof(s) - i)
return kMDErrorCannotWriteToStream;
return kMDNoError;
}
/* Write one meta event */
static MDStatus
MDSequenceWriteSMFMetaEvent(MDSMFConvert *cref, MDEvent *eref)
{
long length;
int n;
unsigned char s[8], *metaDataPtr;
const unsigned char *p;
MDEventKind kind = MDGetKind(eref);
n = PUTC(kMDEventSMFMeta, cref->stream);
if (n == EOF)
return kMDErrorCannotWriteToStream;
switch (kind) {
case kMDEventMetaText:
case kMDEventMetaMessage:
n = PUTC(MDGetCode(eref), cref->stream);
if (n == EOF)
return kMDErrorCannotWriteToStream;
else {
p = MDGetMessageConstPtr(eref, &length);
return MDSequenceWriteSMFWriteMessage(cref, p, length);
}
case kMDEventTempo: {
long ntempo;
ntempo = MDSequenceTempoToSMFTempo(MDGetTempo(eref));
s[0] = ntempo / 65536;
s[1] = ntempo / 256;
s[2] = ntempo;
n = kMDMetaTempo;
length = 3;
break;
}
case kMDEventTimeSignature:
metaDataPtr = MDGetMetaDataPtr(eref);
n = kMDMetaTimeSignature;
s[0] = metaDataPtr[0];
s[1] = metaDataPtr[1];
s[2] = metaDataPtr[2];
s[3] = metaDataPtr[3];
length = 4;
break;
case kMDEventKey:
metaDataPtr = MDGetMetaDataPtr(eref);
n = kMDMetaKey;
s[0] = metaDataPtr[0];
s[1] = metaDataPtr[1];
length = 2;
break;
case kMDEventSMPTE: {
MDSMPTERecord *smp = MDGetSMPTERecordPtr(eref);
n = kMDMetaSMPTE;
s[0] = smp->hour;
s[1] = smp->min;
s[2] = smp->sec;
s[3] = smp->frame;
s[4] = smp->subframe;
length = 5;
break;
}
case kMDEventPortNumber:
n = kMDMetaPortNumber;
s[0] = MDGetData1(eref);
length = 1;
break;
default:
return kMDErrorWrongMetaEvent;
}
if (PUTC(n, cref->stream) == EOF)
return kMDErrorCannotWriteToStream;
return MDSequenceWriteSMFWriteMessage(cref, s, length);
}
/* Write one channel event */
static MDStatus
MDSequenceWriteSMFChannelEvent(MDSMFConvert *cref, MDEvent *eref)
{
unsigned char s[4];
int n;
int data1;
s[1] = MDGetCode(eref);
s[2] = data1 = MDGetData1(eref);
n = 3;
switch (MDGetKind(eref)) {
case kMDEventNote:
s[0] = kMDEventSMFNoteOn;
s[2] = MDGetNoteOnVelocity(eref);
dprintf(2, "kMDEventNote, %02x %02x %02x\n", s[0], s[1], s[2]);
break;
case kMDEventInternalNoteOff:
s[0] = kMDEventSMFNoteOff;
s[2] = MDGetNoteOffVelocity(eref);
dprintf(2, "kMDEventInternalNoteOff, %02x %02x %02x\n", s[0], s[1], s[2]);
break;
case kMDEventControl:
s[0] = kMDEventSMFControl;
break;
case kMDEventPitchBend:
s[0] = kMDEventSMFPitchBend;
s[1] = (data1 & 0x7f);
s[2] = ((data1 + 8192) >> 7) & 0x7f;
break;
case kMDEventProgram:
s[0] = kMDEventSMFProgram;
s[1] = data1;
n = 2;
break;
case kMDEventChanPres:
s[0] = kMDEventSMFChannelPressure;
s[1] = data1;
n = 2;
break;
case kMDEventKeyPres:
s[0] = kMDEventSMFKeyPressure;
break;
default:
return kMDErrorUnknownChannelEvent;
}
if (cref->track_channel < 16)
s[0] |= cref->track_channel;
else
s[0] |= (MDGetChannel(eref) & 0x0f);
if (FWRITE_(s, n, cref->stream) < n)
return kMDErrorCannotWriteToStream;
return kMDNoError;
}
/* Write sequence name and device information as meta events */
static MDStatus
MDSequenceWriteSMFTrackNameAndDevice(MDSMFConvert *cref)
{
MDStatus result;
char buf[256];
long dev;
/* Sequence name */
MDTrackGetName(cref->temptrk, buf, sizeof buf);
result = MDSequenceWriteSMFDeltaTime(cref, 0);
if (result != kMDNoError)
return result;
if (PUTC(kMDEventSMFMeta, cref->stream) == EOF)
return kMDErrorCannotWriteToStream;
if (PUTC(kMDMetaSequenceName, cref->stream) == EOF)
return kMDErrorCannotWriteToStream;
result = MDSequenceWriteSMFWriteMessage(cref, (unsigned char *)buf, strlen(buf));
if (result != kMDNoError)
return result;
/* Device name */
dev = MDTrackGetDevice(cref->temptrk);
if (dev < 0 || MDPlayerGetDestinationName(dev, buf, sizeof buf) != kMDNoError)
MDTrackGetDeviceName(cref->temptrk, buf, sizeof buf);
result = MDSequenceWriteSMFDeltaTime(cref, 0);
if (result != kMDNoError)
return result;
if (PUTC(kMDEventSMFMeta, cref->stream) == EOF)
return kMDErrorCannotWriteToStream;
if (PUTC(kMDMetaDeviceName, cref->stream) == EOF)
return kMDErrorCannotWriteToStream;
result = MDSequenceWriteSMFWriteMessage(cref, (unsigned char *)buf, strlen(buf));
if (result != kMDNoError)
return result;
return result;
}
/* Write one SMF track */
static MDStatus
MDSequenceWriteSMFTrackWithSelection(MDSMFConvert *cref, MDPointSet *pset, char eotSelected)
{
MDPointer *ptr;
MDEvent *eref;
MDStatus result = kMDNoError;
MDTrack *noteOffTrack;
MDPointer *noteOffPtr;
MDEvent *noteOffRef;
MDTickType noteOffTick;
int n, count;
long nevents;
long idx;
const unsigned char sEndOfTrack[3] = { kMDEventSMFMeta, kMDMetaEndOfTrack, 0 };
/* Initialize track info */
cref->tick = 0;
cref->deltatime = 0;
ptr = MDPointerNew(cref->temptrk);
if (ptr == NULL)
return kMDErrorOutOfMemory;
noteOffTrack = MDTrackNew();
if (noteOffTrack == NULL)
return kMDErrorOutOfMemory;
noteOffPtr = MDPointerNew(noteOffTrack);
if (noteOffPtr == NULL)
return kMDErrorOutOfMemory;
noteOffTick = kMDMaxTick;
count = 0;
idx = -1;
if (pset == NULL || pset == (MDPointSet *)(-1)) {
nevents = MDTrackGetNumberOfEvents(cref->temptrk);
result = MDSequenceWriteSMFTrackNameAndDevice(cref);
if (result != kMDNoError)
return result;
} else
nevents = MDPointSetGetCount(pset);
while (
(eref =
((pset == NULL || pset == (MDPointSet *)(-1))
? MDPointerForward(ptr)
: MDPointerForwardWithPointSet(ptr, pset, &idx)
)
) != NULL && MDGetKind(eref) != kMDEventStop) {
if (++count >= 1000) {
if (cref->callback != NULL) {
n = (*cref->callback)(100.0 * (cref->track_index + ((double)MDPointerGetPosition(ptr) / nevents)) / cref->trkno, cref->cbdata);
if (n == 0) {
result = kMDErrorUserInterrupt;
break;
}
}
count = 0;
}
/* Check the note off events and output if necessary */
if (noteOffTick <= MDGetTick(eref)) {
while (1) {
MDPointerSetPosition(noteOffPtr, 0);
if ((noteOffRef = MDPointerCurrent(noteOffPtr)) != NULL && MDGetTick(noteOffRef) <= MDGetTick(eref)) {
dprintf(2, "a pending note-off output, tick %ld code %d vel %d\n", MDGetTick(noteOffRef), MDGetCode(noteOffRef), MDGetNoteOffVelocity(noteOffRef));
result = MDSequenceWriteSMFDeltaTime(cref, MDGetTick(noteOffRef));
if (result == kMDNoError)
result = MDSequenceWriteSMFChannelEvent(cref, noteOffRef);
if (result != kMDNoError)
goto last;
MDPointerDeleteAnEvent(noteOffPtr, NULL);
dprintf(2, "num of pending note-offs = %ld\n", MDTrackGetNumberOfEvents(noteOffTrack));
} else {
if (noteOffRef != NULL)
noteOffTick = MDGetTick(noteOffRef);
else
noteOffTick = kMDMaxTick;
break;
}
}
}
/* Write the delta time */
result = MDSequenceWriteSMFDeltaTime(cref, MDGetTick(eref));
if (result != kMDNoError)
break;
/* Write the status byte */
if (MDIsSysexEvent(eref)) { /* sysex events */
const unsigned char *p;
long length;
p = MDGetMessageConstPtr(eref, &length);
if (MDGetKind(eref) == kMDEventSysexCont)
n = kMDEventSMFSysexF7;
else {
n = kMDEventSMFSysex;
/* Skip 'F0' at the top */
if (*p == 0xf0) {
p++;
length--;
}
}
if (PUTC(n, cref->stream) == EOF) {
result = kMDErrorCannotWriteToStream;
break;
}
result = MDSequenceWriteSMFWriteMessage(cref, p, length);
} else if (MDIsMetaEvent(eref)) { /* meta events */
result = MDSequenceWriteSMFMetaEvent(cref, eref);
} else { /* channel events */
int overlap = 0;
if (MDGetKind(eref) == kMDEventNote) {
/* Register the note-off for later output */
MDEvent noteOffEvent, *ep;
MDEventInit(¬eOffEvent);
MDSetKind(¬eOffEvent, kMDEventInternalNoteOff);
MDSetCode(¬eOffEvent, MDGetCode(eref));
MDSetChannel(¬eOffEvent, MDGetChannel(eref));
MDSetNoteOffVelocity(¬eOffEvent, MDGetNoteOffVelocity(eref));
MDSetTick(¬eOffEvent, MDGetTick(eref) + MDGetDuration(eref));
#if 1
/* Search from the end of registered note-off */
MDPointerSetPosition(noteOffPtr, MDTrackGetNumberOfEvents(noteOffTrack));
while ((ep = MDPointerBackward(noteOffPtr)) != NULL && MDGetTick(ep) > MDGetTick(¬eOffEvent)) {
if (MDGetCode(ep) == MDGetCode(eref) && MDGetChannel(ep) == MDGetChannel(eref))
/* Two note events are overlapping and first-in is NOT first-out */
overlap = 1;
}
#else
/* MDPointerSetPosition(noteOffPtr, 0);
MDPointerJumpToTick(noteOffPtr, MDGetTick(¬eOffEvent) + 1); */
#endif
result = MDPointerInsertAnEvent(noteOffPtr, ¬eOffEvent);
if (result != kMDNoError)
break;
if (noteOffTick > MDGetTick(¬eOffEvent))
noteOffTick = MDGetTick(¬eOffEvent);
dprintf(2, "a note-off registered, tick %ld code %d vel %d\n", MDGetTick(¬eOffEvent), MDGetCode(¬eOffEvent), MDGetNoteOffVelocity(¬eOffEvent));
dprintf(2, "num of pending note-offs = %ld\n", MDTrackGetNumberOfEvents(noteOffTrack));
}
if (overlap) {
/* Write a special 'duration' meta-event */
result = MDSequenceWriteSMFSpecialDurationEvent(cref, eref);
if (result != kMDNoError)
break;
/* Write deltatime 0 */
if (PUTC(0, cref->stream) == EOF) {
result = kMDErrorCannotWriteToStream;
break;
}
}
result = MDSequenceWriteSMFChannelEvent(cref, eref);
}
if (result != kMDNoError)
break;
} /* end while */
/* Write the remaining note-off events */
if (result == kMDNoError && noteOffTick < kMDMaxTick) {
MDPointerSetPosition(noteOffPtr, -1);
while ((noteOffRef = MDPointerForward(noteOffPtr)) != NULL) {
dprintf(2, "a pending note-off output, tick %ld code %d vel %d\n", MDGetTick(noteOffRef), MDGetCode(noteOffRef), MDGetNoteOffVelocity(noteOffRef));
result = MDSequenceWriteSMFDeltaTime(cref, MDGetTick(noteOffRef));
if (result == kMDNoError)
result = MDSequenceWriteSMFChannelEvent(cref, noteOffRef);
if (result != kMDNoError)
break;
}
}
/* Write the end-of-track event */
if (result == kMDNoError) {
if (eotSelected || pset == NULL || pset == (MDPointSet *)(-1))
cref->deltatime = MDTrackGetDuration(cref->temptrk) - cref->tick;
else
cref->deltatime = 1;
if (MDWriteStreamFormat(cref->stream, "w", cref->deltatime) != 1 || FWRITE_(sEndOfTrack, sizeof sEndOfTrack, cref->stream) < sizeof sEndOfTrack)
result = kMDErrorCannotWriteToStream;
}
last:
MDPointerRelease(ptr);
MDPointerRelease(noteOffPtr);
MDTrackRelease(noteOffTrack);
return result;
}
MDStatus
MDSequenceWriteSMFWithSelection(MDSequence *inSequence, MDPointSet **psetArray, char *eotSelectFlags, STREAM stream, MDSequenceCallback callback, void *cbdata)
{
MDStatus result = kMDNoError;
MDSMFConvert conv;
short trkno, trkmax;
long size, pos;
if (inSequence == NULL || stream == NULL)
return kMDErrorInternalError;
/* Initialize the convert record */
memset(&conv, 0, sizeof(conv));
conv.stream = stream;
conv.sequence = inSequence;
conv.timebase = MDSequenceGetTimebase(inSequence);
trkmax = MDSequenceGetNumberOfTracks(inSequence);
if (psetArray != NULL) {
conv.trkno = 0;
for (trkno = 0; trkno < trkmax; trkno++) {
if (psetArray[trkno] != NULL)
conv.trkno++;
}
} else {
conv.trkno = trkmax;
}
conv.callback = callback;
conv.cbdata = cbdata;
conv.track_index = 0;
/* if (conv.trkno < 1)
return kMDErrorInternalError; */
/* Write the file header */
if (MDWriteStreamFormat(conv.stream, "A4Nn3", "MThd", 6L,
(short)(conv.trkno == 1 ? 0 : 1), (short)conv.trkno, (short)conv.timebase) != 5)
result = kMDErrorCannotWriteToStream;
/* Write each track */
for (trkno = 0; trkno < trkmax; trkno++) {
MDPointSet *pset;
char eotSelected;
if (psetArray != NULL) {
pset = psetArray[trkno];
if (pset == NULL) {
/* if (trkno == 0)
pset = (MDPointSet *)(-1); *//* An empty selection; conductor track will always be written */
/* else */
continue;
}
} else pset = NULL;
if (eotSelectFlags != NULL)
eotSelected = eotSelectFlags[trkno];
else eotSelected = 0;
/* Get the track */
conv.temptrk = MDSequenceGetTrack(inSequence, trkno);
if (conv.temptrk == NULL) {
result = kMDErrorInternalError;
break;
}
/* conv.track_index = trkno; */
if (MDSequenceIsSingleChannelMode(inSequence))
conv.track_channel = MDTrackGetTrackChannel(conv.temptrk) & 15;
else
conv.track_channel = 16;
if (MDWriteStreamFormat(conv.stream, "A4N", "MTrk", 0L) == 2) {
conv.pos = FTELL(conv.stream) - 4;
/* if (pset == (MDPointSet *)(-1))
result = kMDNoError;
else */
result = MDSequenceWriteSMFTrackWithSelection(&conv, pset, eotSelected);
if (result == kMDNoError) {
pos = FTELL(conv.stream);
size = pos - conv.pos - 4;
FSEEK(conv.stream, conv.pos, SEEK_SET);
MDWriteStreamFormat(conv.stream, "N", size);
FSEEK(conv.stream, pos, SEEK_SET);
}
} else {
result = kMDErrorCannotWriteToStream;
break;
}
conv.track_index++;
}
return result;
}
MDStatus
MDSequenceWriteSMF(MDSequence *inSequence, STREAM stream, MDSequenceCallback callback, void *cbdata)
{
return MDSequenceWriteSMFWithSelection(inSequence, NULL, NULL, stream, callback, cbdata);
}
#pragma mark ====== Read/Write Catalog ======
MDStatus
MDSequenceWriteCatalog(MDCatalog *inCatalog, STREAM stream)
{
char buf[64];
int i;
off_t pos0, pos1;
pos0 = FTELL(stream);
MDWriteStreamFormat(stream, "N", (long)0); /* Dummy */
MDWriteStreamFormat(stream, "N", (long)inCatalog->num);
MDWriteStreamFormat(stream, "NN", (long)inCatalog->startTick, (long)inCatalog->endTick);
pos1 = FTELL(stream);
FSEEK(stream, pos0, SEEK_SET);
MDWriteStreamFormat(stream, "N", (long)(pos1 - pos0 - 4));
FSEEK(stream, pos1, SEEK_SET);
for (i = 0; i < inCatalog->num; i++) {
MDCatalogTrack *cat = inCatalog->catTrack + i;
pos0 = FTELL(stream);
MDWriteStreamFormat(stream, "N", (long)0); /* Dummy */
memset(buf, 0, 64);
strncpy(buf, cat->name, 63);
MDWriteStreamFormat(stream, "NA64NN", (long)cat->originalTrackNo, buf, (long)cat->numEvents, (long)cat->numMIDIEvents);
pos1 = FTELL(stream);
FSEEK(stream, pos0, SEEK_SET);
MDWriteStreamFormat(stream, "N", (long)(pos1 - pos0 - 4));
FSEEK(stream, pos1, SEEK_SET);
}
return kMDNoError;
}
MDCatalog *
MDSequenceReadCatalog(STREAM stream)
{
int i;
long num, num2, num3;
off_t pos0;
MDCatalog *catalog;
pos0 = FTELL(stream);
if (MDReadStreamFormat(stream, "N", &num) < 1)
return NULL;
pos0 += num - 4;
MDReadStreamFormat(stream, "NNN", &num, &num2, &num3);
catalog = (MDCatalog *)malloc(sizeof(MDCatalog) + (num - 1) * sizeof(MDCatalogTrack));
if (catalog == NULL)
return NULL;
memset(catalog, 0, sizeof(MDCatalog) + (num - 1) * sizeof(MDCatalogTrack));
catalog->num = num;
catalog->startTick = num2;
catalog->endTick = num3;
FSEEK(stream, pos0, SEEK_SET);
for (i = 0; i < catalog->num; i++) {
MDCatalogTrack *cat = catalog->catTrack + i;
MDReadStreamFormat(stream, "N", &num);
pos0 += num - 4;
MDReadStreamFormat(stream, "NA64NN", &num, cat->name, &num2, &num3);
cat->name[63] = 0;
cat->originalTrackNo = num;
cat->numEvents = num2;
cat->numMIDIEvents = num3;
FSEEK(stream, pos0, SEEK_SET);
}
return catalog;
}