2161 lines
40 KiB
C++
2161 lines
40 KiB
C++
// midiio.cpp written by Günter Nagler 1995 (gnagler@ihm.tu-graz.ac.at)
|
|
#include "midiio.hpp"
|
|
#include <assert.h>
|
|
#ifdef __MSDOS__
|
|
#include <mem.h>
|
|
#endif
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
static const char* copyright = "midiio v1.4 (c) 1995 by Günter Nagler (" __DATE__ ")";
|
|
|
|
int compress = 1;
|
|
|
|
#define NOTREALISTIC_PAUSE 0x1000000UL
|
|
|
|
// common sysex events
|
|
unsigned char sysex_gmreset[] = { 0xF0, 0x05, 0x7E, 0x7F, 0x09, 0x01, 0xF7 };
|
|
unsigned char sysex_gsreset[] = { 0xF0, 0x0A, 0x41, 0x10, 0x42, 0x12, 0x40, 0x00, 0x7F, 0x00, 0x41, 0xF7 };
|
|
unsigned char sysex_gsexit[] = { 0xF0, 0x0A, 0x41, 0x10, 0x42, 0x12, 0x40, 0x00, 0x7F, 0x7F, 0x42, 0xF7 };
|
|
unsigned char sysex_xgreset[] = { 0xF0, 0x08, 0x43, 0x10, 0x4C, 0x00, 0x00, 0x7E, 0x00, 0xF7 };
|
|
|
|
static int issysex(const unsigned char* sysex, const unsigned char* sysdata, int syslen)
|
|
{
|
|
if (*sysex == 0xF0)
|
|
sysex++;
|
|
while (syslen > 0)
|
|
{
|
|
if (*sysex != *sysdata)
|
|
return 0;
|
|
syslen--;
|
|
if (*sysex == 0xF7)
|
|
break;
|
|
sysex++;
|
|
sysdata++;
|
|
}
|
|
return (syslen != 0) ? 0 : 1;
|
|
}
|
|
|
|
static int sysexlen(unsigned const char* sysex)
|
|
{
|
|
int len = 0;
|
|
|
|
len = 0;
|
|
while (*sysex != 0xF7)
|
|
{
|
|
sysex++;
|
|
len++;
|
|
}
|
|
return len+1; // incl. F7
|
|
}
|
|
|
|
// class MidiRead
|
|
|
|
const char* MidiRead::copyright()
|
|
{
|
|
return (const char*)::copyright;
|
|
}
|
|
|
|
MidiRead::MidiRead(const char* filename, FILE* f)
|
|
{
|
|
midiname_ = filename;
|
|
if (f)
|
|
{
|
|
f_ = f;
|
|
shouldclose_ = 0;
|
|
}
|
|
else
|
|
{
|
|
shouldclose_ = 1;
|
|
if (!filename)
|
|
f_ = 0;
|
|
else
|
|
f_ = fopen(filename, READ_BINARY);
|
|
}
|
|
|
|
buflen_ = 0;
|
|
bufpos_ = 0;
|
|
curpos_ = 0;
|
|
pos_ = 0;
|
|
curchannel_ = NOCHANNEL;
|
|
curtime_ = 0;
|
|
options_ = 0;
|
|
if (f_)
|
|
{
|
|
fseek(f_, 0L, SEEK_END);
|
|
filesize_ = ftell(f_);
|
|
fseek(f_, 0L, SEEK_SET);
|
|
}
|
|
else
|
|
filesize_ = 0;
|
|
|
|
version_ = tracks_ = clicks_ = trackno_ = 0;
|
|
tracklen_ = 0;
|
|
}
|
|
|
|
|
|
MidiRead::~MidiRead()
|
|
{
|
|
if (f_ && shouldclose_)
|
|
fclose(f_);
|
|
}
|
|
|
|
int MidiRead::runhead()
|
|
{
|
|
if (!f_)
|
|
{
|
|
error("file not open");
|
|
return 0;
|
|
}
|
|
seek(0);
|
|
if (getlong() != MThd)
|
|
{
|
|
error("missing midi header MThd");
|
|
return 0;
|
|
}
|
|
|
|
if (getlong() == 6)
|
|
{
|
|
version_ = getword();
|
|
tracks_ = getword();
|
|
clicks_ = getword();
|
|
head(version_, tracks_, clicks_);
|
|
}
|
|
else
|
|
{
|
|
error("illegal midi header");
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int MidiRead::run()
|
|
{
|
|
pos_ = curpos_;
|
|
if (!runhead())
|
|
return 0;
|
|
pos_ = curpos_;
|
|
for (trackno_ = 1; trackno_ <= tracks_; trackno_++)
|
|
if (!runtrack(trackno_))
|
|
return 0;
|
|
if (curpos_ >= filesize_)
|
|
percent(percent_ = 100);
|
|
endmidi();
|
|
return 1;
|
|
}
|
|
|
|
int MidiRead::runevent(long trackend)
|
|
{
|
|
int midicode;
|
|
|
|
pos_ = curpos_;
|
|
|
|
unsigned char *c = need(1);
|
|
if (!c || c[0] >= 0x80 || lastcode_ < 0)
|
|
midicode = getbyte();
|
|
else
|
|
midicode = lastcode_;
|
|
|
|
if (midicode < 0)
|
|
return 0;
|
|
|
|
switch(midicode)
|
|
{
|
|
case 0xf0: // sysex
|
|
{
|
|
int syslen = scansysevent(trackend - curpos_);
|
|
if (!syslen)
|
|
{
|
|
error("end of sysex not found or sysex too large");
|
|
return 0;
|
|
}
|
|
unsigned char* sysdata = get(syslen);
|
|
if ((options_ & OPTION_NOSYSEVENTS) == 0)
|
|
{
|
|
if (issysex(sysex_gmreset, sysdata, syslen))
|
|
gmreset();
|
|
else if (issysex(sysex_gsreset, sysdata, syslen))
|
|
gsreset();
|
|
else if (issysex(sysex_gsexit, sysdata, syslen))
|
|
gsexit();
|
|
else if (issysex(sysex_xgreset, sysdata, syslen))
|
|
xgreset();
|
|
else
|
|
sysex(syslen, sysdata);
|
|
}
|
|
else if ((options_ & OPTION_NOREALTIMEEVENTS) == 0)
|
|
sysex(syslen, sysdata);
|
|
else
|
|
event(0xf0, syslen, sysdata);
|
|
}
|
|
break;
|
|
case 0xf2:
|
|
{
|
|
c = get(2); if (!c) return 0;
|
|
if ((options_ & OPTION_NOREALTIMEEVENTS) == 0)
|
|
songpos((unsigned(c[1]) << 7) + unsigned(c[0]));
|
|
else
|
|
event(0xf2, 2, c);
|
|
break;
|
|
}
|
|
case 0xf3:
|
|
c = get(1); if (!c) return 0;
|
|
if ((options_ & OPTION_NOREALTIMEEVENTS) == 0)
|
|
songselect(*c);
|
|
else
|
|
event(0xf3, 1, c);
|
|
break;
|
|
case 0xf6:
|
|
if ((options_ & OPTION_NOREALTIMEEVENTS) == 0)
|
|
tunerequest();
|
|
else
|
|
event(0xf6);
|
|
break;
|
|
case 0xf8:
|
|
if ((options_ & OPTION_NOREALTIMEEVENTS) == 0)
|
|
timingclock();
|
|
else
|
|
event(0xf8);
|
|
break;
|
|
case 0xfa:
|
|
if ((options_ & OPTION_NOREALTIMEEVENTS) == 0)
|
|
start();
|
|
else
|
|
event(0xfa);
|
|
break;
|
|
case 0xfb:
|
|
if ((options_ & OPTION_NOREALTIMEEVENTS) == 0)
|
|
cont();
|
|
else
|
|
event(0xfb);
|
|
break;
|
|
case 0xfc:
|
|
if ((options_ & OPTION_NOREALTIMEEVENTS) == 0)
|
|
stop();
|
|
else
|
|
event(0xfc);
|
|
break;
|
|
case 0xfe:
|
|
if ((options_ & OPTION_NOREALTIMEEVENTS) == 0)
|
|
activesense();
|
|
else
|
|
event(0xfe);
|
|
break;
|
|
case 0xff:
|
|
{
|
|
int c;
|
|
unsigned long endpos;
|
|
int len;
|
|
|
|
c = getbyte();
|
|
len = (int)getdelta();
|
|
endpos = curpos_ + len;
|
|
if (options_ & OPTION_NOREALTIMEEVENTS)
|
|
{
|
|
len += curdeltalen_ + 1;
|
|
seek(curpos_ - curdeltalen_ - 1);
|
|
event(0xff, len, get(len));
|
|
}
|
|
else if (options_ & OPTION_NOMETAEVENTS)
|
|
meta(c, len, get(len));
|
|
else
|
|
switch(c)
|
|
{
|
|
case 0:
|
|
if (len == 2)
|
|
seqnumber(getword());
|
|
else
|
|
meta(c, len, get(len));
|
|
break;
|
|
case meta_text:
|
|
text(c, len, "text", get(len)); break;
|
|
case meta_copyright:
|
|
text(c, len, "copyright", get(len)); break;
|
|
case meta_trackname:
|
|
text(c, len, "trackname", get(len)); break;
|
|
case meta_instrument:
|
|
text(c, len, "instrument", get(len)); break;
|
|
case meta_lyric:
|
|
text(c, len, "lyric", get(len)); break;
|
|
case meta_marker:
|
|
text(c, len, "marker", get(len)); break;
|
|
case meta_cuepoint:
|
|
text(c, len, "cuepoint", get(len)); break;
|
|
case 8:
|
|
case 9:
|
|
case 10:
|
|
case 11:
|
|
case 12:
|
|
case 13:
|
|
case 14:
|
|
case 15:
|
|
text(c, len, 0, get(len)); break;
|
|
case 0x20:
|
|
if (len == 1)
|
|
prefixchannel(getbyte());
|
|
else
|
|
meta(c, len, get(len));
|
|
break;
|
|
case 0x21:
|
|
if (len == 1)
|
|
prefixport(getbyte());
|
|
else
|
|
meta(c, len, get(len));
|
|
break;
|
|
case 0x2F:
|
|
end();
|
|
break;
|
|
case 0x51:
|
|
tempo(gettri());
|
|
break;
|
|
case 0x54:
|
|
if (len == 5)
|
|
{
|
|
unsigned char *s = get(len);
|
|
|
|
smpteofs(s[0], s[1], s[2], s[3], s[4]);
|
|
}
|
|
else
|
|
meta(c, len, get(len));
|
|
break;
|
|
case 0x58:
|
|
if (len == 4)
|
|
{
|
|
int nom, clicksperbeat, notes32perbeat, log2denom;
|
|
|
|
nom = getbyte();
|
|
log2denom = getbyte();
|
|
|
|
clicksperbeat = getbyte();
|
|
notes32perbeat = getbyte();
|
|
tact(nom, 1 << log2denom, clicksperbeat, notes32perbeat);
|
|
}
|
|
else
|
|
meta(c, len, get(len));
|
|
break;
|
|
case 0x59:
|
|
if (len == 2)
|
|
{
|
|
signed char s[2];
|
|
|
|
s[0] = (signed char)getbyte(); // sf
|
|
s[1] = (signed char)getbyte(); // mi
|
|
if (s[0] >= -7 && s[0] <= +7 && s[1] >= 0 && s[1] <= 1)
|
|
key(s[0], s[1]);
|
|
else
|
|
meta(c, len, (unsigned char*)s);
|
|
}
|
|
else
|
|
meta(c, len, get(len));
|
|
break;
|
|
default:
|
|
meta(c, len, get(len));
|
|
break;
|
|
}
|
|
seek(endpos);
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
if (midicode < 0)
|
|
return 0;
|
|
if (midicode < 0x80)
|
|
{
|
|
char msg[30];
|
|
|
|
sprintf(msg, "illegal midi command %02X", midicode);
|
|
error(msg);
|
|
return 0;
|
|
}
|
|
int channel = midicode & 0x0F;
|
|
int cmd = midicode & 0xF0;
|
|
|
|
switch(cmd)
|
|
{
|
|
case 0x80:
|
|
case 0x90:
|
|
{
|
|
unsigned char* c;
|
|
|
|
lastcode_ = midicode;
|
|
c = get(2); if (!c) return 0;
|
|
if (options_ & OPTION_NONOTEEVENTS)
|
|
event(midicode, 2, c);
|
|
else
|
|
{
|
|
if (cmd == 0x80 || c[1] == 0)
|
|
noteoff(channel, c[0], c[1]);
|
|
else
|
|
noteon(channel, c[0], c[1]);
|
|
}
|
|
}
|
|
break;
|
|
case 0xA0:
|
|
{
|
|
unsigned char* p = get(2); if (!p) return 0;
|
|
|
|
lastcode_ = midicode;
|
|
|
|
if (options_ & OPTION_NOPOLYEVENTS)
|
|
event(midicode, 2, p);
|
|
else
|
|
{
|
|
polyaftertouch(channel, p[0], p[1]);
|
|
}
|
|
}
|
|
break;
|
|
case 0xB0:
|
|
{
|
|
unsigned char* p = get(2); if (!p) return 0;
|
|
|
|
lastcode_ = midicode;
|
|
if (options_ & OPTION_NOCONTROLEVENTS)
|
|
event(midicode, 2, p);
|
|
else if (options_ & OPTION_NOCONTROLS)
|
|
control(channel, p[0], p[1]);
|
|
else
|
|
switch(p[0])
|
|
{
|
|
case ctrl_highbank: highbank(channel, p[1]); break;
|
|
case ctrl_wheel: wheel(channel, p[1]); break;
|
|
case ctrl_breath: breath(channel, p[1]); break;
|
|
case ctrl_foot: foot(channel, p[1]); break;
|
|
case ctrl_portamentotime: portamentotime(channel, p[1]); break;
|
|
case ctrl_data: data(channel, p[1]); break;
|
|
case ctrl_volume: volume(channel, p[1]); break;
|
|
case ctrl_balance: balance(channel, p[1]); break;
|
|
case ctrl_expression: expression(channel, p[1]); break;
|
|
case ctrl_lowbank: lowbank(channel, p[1]); break;
|
|
case ctrl_hold: hold(channel, p[1]); break;
|
|
case ctrl_reverb: reverb(channel, p[1]); break;
|
|
case ctrl_chorus: chorus(channel, p[1]); break;
|
|
case ctrl_datainc: datainc(channel, p[1]); break;
|
|
case ctrl_datadec: datadec(channel, p[1]); break;
|
|
case ctrl_lowrpn: lowrpn(channel, p[1]); break;
|
|
case ctrl_highrpn:
|
|
case ctrl_resetctrlrs: resetctrlrs(channel, p[1]); break;
|
|
case ctrl_allnotesoff: allnotesoff(channel, p[1]); break;
|
|
{
|
|
unsigned char *c = need(8);
|
|
|
|
if (c &&
|
|
c[0] == 0 && c[1] == midicode && c[2] == ctrl_lowrpn && c[3] == 0 &&
|
|
c[4] == 0 && c[5] == midicode && c[6] == ctrl_data)
|
|
{
|
|
c = get(8);
|
|
pitchbendrange(channel, c[7]);
|
|
}
|
|
else
|
|
highrpn(channel, p[1]);
|
|
break;
|
|
}
|
|
default:
|
|
control(channel, p[0], p[1]);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 0xC0:
|
|
{
|
|
unsigned char* p = get(1); if (!p) return 0;
|
|
|
|
lastcode_ = midicode;
|
|
if (options_ & OPTION_NOPROGRAMEVENTS)
|
|
event(midicode, 1, p);
|
|
else
|
|
program(channel, p[0]);
|
|
}
|
|
break;
|
|
case 0xD0:
|
|
{
|
|
unsigned char* p = get(1); if (!p) return 0;
|
|
|
|
lastcode_ = midicode;
|
|
if (options_ & OPTION_NOAFTERTOUCHEVENTS)
|
|
event(midicode, 1, p);
|
|
else
|
|
aftertouch(channel, p[0]);
|
|
}
|
|
break;
|
|
case 0xE0:
|
|
{
|
|
unsigned char* p = get(2); if (!p) return 0;
|
|
unsigned val = unsigned(p[0]) + (unsigned(p[1]) << 7);
|
|
|
|
lastcode_ = midicode;
|
|
if (options_ & OPTION_NOPITCHBENDEVENTS)
|
|
event(midicode, 2, p);
|
|
else
|
|
pitchbend(channel, val);
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
char msg[30];
|
|
|
|
sprintf(msg, "unexpected command byte %02X", midicode);
|
|
error(msg);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
return (int)(curpos_ - pos_);
|
|
}
|
|
|
|
int MidiRead::runtrack(int trackno)
|
|
{
|
|
unsigned long trackpos = curpos_, trackend;
|
|
|
|
curtime_ = 0;
|
|
lastcode_ = -1;
|
|
pos_ = curpos_;
|
|
if (!f_)
|
|
{
|
|
error("file not open");
|
|
return 0;
|
|
}
|
|
|
|
if (getlong() != MTrk)
|
|
{
|
|
error("missing midi track MTrk");
|
|
return 0;
|
|
}
|
|
tracklen_ = getlong();
|
|
track(trackno, tracklen_, curchannel_ = scanchannel(tracklen_));
|
|
trackpos = curpos_;
|
|
trackend = trackpos + tracklen_;
|
|
lastcode_ = -1;
|
|
if ((options_ & OPTION_NOEVENTS) == 0)
|
|
while (curpos_ < trackpos + tracklen_)
|
|
{
|
|
int newpercent = (int)((curpos_ * 100) / filesize_);
|
|
if (newpercent != percent_)
|
|
percent(percent_ = newpercent);
|
|
|
|
unsigned long delta = getdelta();
|
|
if ( delta >= NOTREALISTIC_PAUSE )
|
|
warning("Unrealistic large pause found");
|
|
time(delta);
|
|
curtime_ += delta;
|
|
if (runevent(trackend) <= 0)
|
|
return 0;
|
|
}
|
|
seek(trackend);
|
|
pos_ = curpos_;
|
|
endtrack(trackno);
|
|
return 1;
|
|
}
|
|
|
|
void MidiRead::setchannel(int channel)
|
|
{
|
|
assert(channel >= -1 && channel <= 15);
|
|
curchannel_ = channel;
|
|
}
|
|
|
|
int MidiRead::scansysevent(unsigned long maxlen)
|
|
{
|
|
int n = sizeof(buf_);
|
|
unsigned char* c, *p;
|
|
long savepos = curpos_;
|
|
int len;
|
|
|
|
if (maxlen < n)
|
|
n = (int)maxlen;
|
|
c = need(n);
|
|
if (!c)
|
|
{
|
|
n = buflen_;
|
|
c = need(n);
|
|
if (!c)
|
|
return 0;
|
|
}
|
|
|
|
if (c[0] < 0x80)
|
|
{
|
|
// short sysex 0..127 bytes
|
|
len = c[0];
|
|
if (n >= len+1)
|
|
{
|
|
if (c[len] == 0xF7)
|
|
return len+1;
|
|
}
|
|
}
|
|
else if (n >= 2 && c[1] < 0x80)
|
|
{
|
|
// 128..16383 bytes
|
|
int len = (int(c[0] & 0x7f) << 7) + c[1];
|
|
if (n >= len + 2)
|
|
{
|
|
if (c[len+1] == 0xF7)
|
|
return len+2;
|
|
}
|
|
}
|
|
// sysex events without length information?
|
|
p = (unsigned char*)memchr(c, 0xF7, n);
|
|
if (p)
|
|
return (int)(p - c + 1);
|
|
seek(savepos);
|
|
return 0;
|
|
}
|
|
|
|
int MidiRead::scanchannel(unsigned long maxlen)
|
|
{
|
|
int n = 512;
|
|
unsigned char* c;
|
|
int firstchannel = NOCHANNEL;
|
|
int channel, code;
|
|
long savepos = curpos_, endpos;
|
|
int lastcode = -1;
|
|
|
|
if (maxlen < n)
|
|
n = (int)maxlen;
|
|
c = need(n);
|
|
if (!c)
|
|
return -1;
|
|
|
|
endpos = curpos_ + n;
|
|
while (curpos_ < endpos)
|
|
{
|
|
channel = NOCHANNEL;
|
|
getdelta();
|
|
c = need(1);
|
|
if (!c || *c >= 0x80 || lastcode < 0)
|
|
code = getbyte();
|
|
else
|
|
code = lastcode;
|
|
switch(code & 0xF0)
|
|
{
|
|
case 0xF0:
|
|
switch(code)
|
|
{
|
|
case 0xFF:
|
|
getbyte();
|
|
get((int)getdelta());
|
|
break;
|
|
case 0xf0: // sysex
|
|
{
|
|
int len = scansysevent(endpos-curpos_);
|
|
if (!get(len))
|
|
goto endscan;
|
|
}
|
|
break;
|
|
case 0xf2: get(2); break;
|
|
case 0xf3: getbyte(); break;
|
|
case 0xf6:
|
|
case 0xf8:
|
|
case 0xfa:
|
|
case 0xfb:
|
|
case 0xfc:
|
|
case 0xfe:
|
|
break;
|
|
default:
|
|
goto endscan;
|
|
}
|
|
break;
|
|
case 0x80:
|
|
case 0x90:
|
|
case 0xA0:
|
|
case 0xB0:
|
|
case 0xE0:
|
|
channel = code & 15;
|
|
get(2);
|
|
break;
|
|
case 0xC0:
|
|
case 0xD0:
|
|
channel = code & 15;
|
|
getbyte();
|
|
break;
|
|
default:
|
|
goto endscan;
|
|
}
|
|
if (code < 0xf0)
|
|
lastcode = code;
|
|
if (channel >= 0)
|
|
{
|
|
if (firstchannel < 0)
|
|
firstchannel = channel;
|
|
else if (channel != firstchannel)
|
|
{
|
|
firstchannel = MULTICHANNEL;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
endscan:
|
|
seek(savepos);
|
|
return firstchannel;
|
|
}
|
|
|
|
int MidiRead::getbyte()
|
|
{
|
|
unsigned char* c = get(1);
|
|
|
|
if (c)
|
|
return *c;
|
|
return -1;
|
|
}
|
|
|
|
unsigned MidiRead::getword()
|
|
{
|
|
unsigned char* c = get(2);
|
|
unsigned n = 0;
|
|
|
|
if (c)
|
|
{
|
|
n = *c++;
|
|
n = (n << 8) + *c++;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
unsigned long MidiRead::gettri()
|
|
{
|
|
unsigned char* c = get(3);
|
|
unsigned long n = 0;
|
|
|
|
if (c)
|
|
{
|
|
n = *c++;
|
|
n = (n << 8) + *c++;
|
|
n = (n << 8) + *c++;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
unsigned long MidiRead::getlong()
|
|
{
|
|
unsigned char* c = get(4);
|
|
unsigned long n = 0;
|
|
|
|
if (c)
|
|
{
|
|
n = *c++;
|
|
n = (n << 8) + *c++;
|
|
n = (n << 8) + *c++;
|
|
n = (n << 8) + *c++;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
unsigned long MidiRead::getdelta()
|
|
{
|
|
unsigned long n = 0;
|
|
int i = 0, c;
|
|
|
|
curdeltalen_ = 0;
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
c = getbyte();
|
|
if (c < 0)
|
|
{
|
|
error("unexpected end of file");
|
|
return 0;
|
|
}
|
|
curdeltalen_++;
|
|
n = (n << 7) + (c & 0x7f);
|
|
if ((c & 0x80) == 0)
|
|
break;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
unsigned char* MidiRead::need(int n)
|
|
{
|
|
assert(n >= 0);
|
|
if (n == 0)
|
|
return 0;
|
|
if (n > buflen_)
|
|
{
|
|
if (!f_)
|
|
return 0;
|
|
if (n > sizeof(buf_))
|
|
return 0;
|
|
if (n > sizeof(buf_) - bufpos_)
|
|
{
|
|
// move to beginning of buf
|
|
memmove(buf_, buf_ + bufpos_, buflen_);
|
|
bufpos_ = 0;
|
|
}
|
|
// add new data at end
|
|
if (sizeof(buf_) - bufpos_ - buflen_ > 0)
|
|
{
|
|
fseek(f_, curpos_+buflen_, SEEK_SET);
|
|
int l = fread(buf_+bufpos_+buflen_, 1, sizeof(buf_) - bufpos_ - buflen_, f_);
|
|
if (l > 0)
|
|
buflen_ += l;
|
|
else if (l == 0)
|
|
;
|
|
}
|
|
}
|
|
if (n <= buflen_)
|
|
return buf_ + bufpos_;
|
|
return 0;
|
|
}
|
|
|
|
unsigned char* MidiRead::get(int n)
|
|
{
|
|
unsigned char* s;
|
|
|
|
s = need(n);
|
|
if (s)
|
|
{
|
|
buflen_ -= n;
|
|
bufpos_ += n;
|
|
curpos_ += n;
|
|
}
|
|
else if (n > sizeof(buf_))
|
|
warning("midi event larger than internal bufsize ignored");
|
|
else if (n > 0)
|
|
{
|
|
error("unexpected end of file");
|
|
exit(1);
|
|
}
|
|
return s;
|
|
}
|
|
|
|
void MidiRead::seek(long pos)
|
|
{
|
|
if (pos == curpos_ || pos < 0)
|
|
return;
|
|
if (pos >= curpos_ - bufpos_ && pos < curpos_ - bufpos_ + buflen_)
|
|
{
|
|
int n = (int)(pos - curpos_ + bufpos_);
|
|
|
|
if (n < bufpos_)
|
|
{
|
|
buflen_ += bufpos_ - n;
|
|
bufpos_ -= bufpos_ - n;
|
|
}
|
|
else
|
|
{
|
|
buflen_ -= n - bufpos_;
|
|
bufpos_ += n - bufpos_;
|
|
}
|
|
curpos_ = pos;
|
|
}
|
|
else
|
|
{
|
|
fseek(f_, curpos_ = pos, SEEK_SET);
|
|
bufpos_ = buflen_ = 0;
|
|
}
|
|
}
|
|
|
|
unsigned long MidiRead::microsec(unsigned long units, unsigned long msperbeat)
|
|
{
|
|
assert(clicks_ != 0); // call runhead() or run() first!
|
|
|
|
if (units > msperbeat)
|
|
return (units / clicks_) * msperbeat;
|
|
else
|
|
return units * (msperbeat / clicks_);
|
|
}
|
|
|
|
|
|
long MidiRead::units(unsigned long microsec, unsigned long msperbeat)
|
|
{
|
|
assert(clicks_ != 0); // call runhead() or run() first!
|
|
assert(msperbeat > 0); // invalid tempo!
|
|
int clicks = clicks_;
|
|
while ((msperbeat & 1) == 0)
|
|
{
|
|
if ((clicks & 1) == 0)
|
|
clicks >>= 1;
|
|
else if ((microsec & 1) == 0)
|
|
microsec >>= 1;
|
|
else
|
|
break;
|
|
msperbeat >>= 1;
|
|
}
|
|
if (microsec >= 0x10000L)
|
|
return (microsec / msperbeat) * clicks;
|
|
else
|
|
return (microsec * clicks) / msperbeat;
|
|
}
|
|
|
|
|
|
static const char* GMProg[128] =
|
|
{
|
|
"Piano", "BritePiano", "HammerPiano", "HonkeyTonk", "NewTines", "DigiPiano", "Harpsicord", "Clav",
|
|
"Celesta", "Glocken", "MusicBox", "Vibes", "Marimba", "Xylophon", "Tubular", "Santur",
|
|
"FullOrgan", "PercOrgan", "BX-3Organ", "ChurchPipe", "Positive", "Musette", "Harmonica", "Tango",
|
|
"ClassicGtr", "A.Guitar", "JazzGuitar", "CleanGtr", "MuteGuitar", "OverDrive", "DistGuitar", "RockMonics",
|
|
"JazzBass", "DeepBass", "PickBass", "FretLess", "SlapBass1", "SlapBass2", "SynthBass1", "SynthBass2",
|
|
"Violin", "Viola", "Cello", "ContraBass", "TremoloStr", "Pizzicato", "Harp", "Timpani",
|
|
"Marcato", "SlowString", "AnalogPad", "StringPad", "Choir", "DooVoice", "Voices", "OrchHit",
|
|
"Trumpet", "Trombone", "Tuba", "MutedTrumpet", "FrenchHorn", "Brass", "SynBrass1", "SynBrass2",
|
|
"SopranoSax", "AltoSax", "TenorSax", "BariSax", "SweetOboe", "EnglishHorn", "BasoonOboe", "Clarinet",
|
|
"Piccolo", "Flute", "Recorder", "PanFlute", "Bottle", "Shakuhachi","Whistle", "Ocarina",
|
|
"SquareWave", "SawWave", "SynCalinope", "SynChiff", "Charang", "AirChorus", "Rezzo4ths", "Bass&Lead",
|
|
"Fantasia", "WarmPad", "PolyPad", "GhostPad", "BowedGlas", "MetalPad", "HaloPad", "Sweep",
|
|
"IceRain", "SoundTrack", "Crystal", "Atmosphere", "Brightness", "Goblin", "EchoDrop", "StarTheme",
|
|
"Sitar", "Banjo", "Shamisen", "Koto", "Kalimba","Scotland","Fiddle", "Shanai",
|
|
"MetalBell", "Agogo", "SteelDrums", "Woodblock", "Taiko", "Tom", "SynthTom", "RevCymbal",
|
|
"FretNoise", "NoiseChiff", "Seashore", "Birds", "Telephone", "Helicopter", "Stadium!!", "GunShot"
|
|
};
|
|
|
|
const char* MidiRead::notename(unsigned char note)
|
|
{
|
|
static char name[5];
|
|
char* s = name;
|
|
|
|
switch(note % 12)
|
|
{
|
|
case 0: *s++ = 'c'; break;
|
|
case 1: *s++ = 'c'; *s++ = '#'; break;
|
|
case 2: *s++ = 'd'; break;
|
|
case 3: *s++ = 'd'; *s++ = '#'; break;
|
|
case 4: *s++ = 'e'; break;
|
|
case 5: *s++ = 'f'; break;
|
|
case 6: *s++ = 'f'; *s++ = '#'; break;
|
|
case 7: *s++ = 'g'; break;
|
|
case 8: *s++ = 'g'; *s++ = '#'; break;
|
|
case 9: *s++ = 'a'; break;
|
|
case 10: *s++ = 'a'; *s++ = '#'; break;
|
|
case 11: *s++ = 'b'; break; // former 'h': German language only
|
|
}
|
|
|
|
sprintf(s, "%d", (note / 12)-1); // octave (assuming Piano C4 is 60)
|
|
return (const char*) name;
|
|
}
|
|
|
|
|
|
const char* MidiRead::progname(int n, int channel)
|
|
{
|
|
static char defname[10] = "";
|
|
|
|
if (channel == 9) // drum programs
|
|
{
|
|
switch(n)
|
|
{
|
|
case 0: return "Dr1";
|
|
case 0x10: return "Dr2";
|
|
case 0x19: return "Dr3";
|
|
case 0x20: return "Dr4";
|
|
case 0x28: return "Dr5";
|
|
case 0x40: return "Dr6";
|
|
case 0x18: return "Dr7";
|
|
case 0x30: return "Dr8";
|
|
}
|
|
}
|
|
else if (n >= 0 && n <= 127)
|
|
return GMProg[n];
|
|
def:
|
|
sprintf(defname, "%d", n);
|
|
return (const char*)defname;
|
|
}
|
|
|
|
void MidiRead::head(unsigned version, unsigned tracks, unsigned clicksperquarter)
|
|
{
|
|
}
|
|
|
|
void MidiRead::track(int trackno, long length, int channel)
|
|
{
|
|
}
|
|
|
|
void MidiRead::endtrack(int trackno)
|
|
{
|
|
}
|
|
|
|
void MidiRead::event(int what, int len, unsigned char* data)
|
|
{
|
|
}
|
|
|
|
void MidiRead::seqnumber(unsigned int seqno)
|
|
{
|
|
}
|
|
|
|
void MidiRead::smpteofs(int hour, int min, int sec, int frame, int fracframe)
|
|
{
|
|
}
|
|
|
|
void MidiRead::key(int signature, int isminor)
|
|
{
|
|
}
|
|
|
|
void MidiRead::prefixchannel(unsigned char channel)
|
|
{
|
|
}
|
|
|
|
void MidiRead::prefixport(unsigned char port)
|
|
{
|
|
}
|
|
|
|
void MidiRead::text(int what, int len, const char* whattext, const unsigned char* txt)
|
|
{
|
|
}
|
|
|
|
void MidiRead::meta(int what, int len, const unsigned char* data)
|
|
{
|
|
}
|
|
|
|
void MidiRead::end()
|
|
{
|
|
}
|
|
|
|
void MidiRead::tact(int nom, int denom, int v1, int v2)
|
|
{
|
|
}
|
|
|
|
void MidiRead::tempo(unsigned long ticks)
|
|
{
|
|
}
|
|
|
|
void MidiRead::program(int channel, int program)
|
|
{
|
|
}
|
|
|
|
void MidiRead::control(int channel, int what, int value)
|
|
{
|
|
}
|
|
|
|
void MidiRead::highbank(int channel, int val)
|
|
{
|
|
control(channel, ctrl_highbank, val);
|
|
}
|
|
|
|
void MidiRead::wheel(int channel, int val)
|
|
{
|
|
control(channel, ctrl_wheel, val);
|
|
}
|
|
|
|
void MidiRead::breath(int channel, int val)
|
|
{
|
|
control(channel, ctrl_breath, val);
|
|
}
|
|
|
|
void MidiRead::foot(int channel, int val)
|
|
{
|
|
control(channel, ctrl_foot, val);
|
|
}
|
|
|
|
void MidiRead::portamentotime(int channel, int val)
|
|
{
|
|
control(channel, ctrl_portamentotime, val);
|
|
}
|
|
|
|
void MidiRead::data(int channel, int val)
|
|
{
|
|
control(channel, ctrl_data, val);
|
|
}
|
|
|
|
void MidiRead::volume(int channel, int val)
|
|
{
|
|
control(channel, ctrl_volume, val);
|
|
}
|
|
|
|
void MidiRead::balance(int channel, int val)
|
|
{
|
|
control(channel, ctrl_balance, val);
|
|
}
|
|
|
|
void MidiRead::expression(int channel, int val)
|
|
{
|
|
control(channel, ctrl_expression, val);
|
|
}
|
|
|
|
void MidiRead::lowbank(int channel, int val)
|
|
{
|
|
control(channel, ctrl_lowbank, val);
|
|
}
|
|
|
|
void MidiRead::hold(int channel, int val)
|
|
{
|
|
control(channel, ctrl_hold, val);
|
|
}
|
|
|
|
void MidiRead::reverb(int channel, int val)
|
|
{
|
|
control(channel, ctrl_reverb, val);
|
|
}
|
|
|
|
void MidiRead::chorus(int channel, int val)
|
|
{
|
|
control(channel, ctrl_chorus, val);
|
|
}
|
|
|
|
void MidiRead::datainc(int channel, int val)
|
|
{
|
|
control(channel, ctrl_datainc, val);
|
|
}
|
|
|
|
void MidiRead::datadec(int channel, int val)
|
|
{
|
|
control(channel, ctrl_datadec, val);
|
|
}
|
|
|
|
void MidiRead::lowrpn(int channel, int val)
|
|
{
|
|
control(channel, ctrl_lowrpn, val);
|
|
}
|
|
|
|
void MidiRead::highrpn(int channel, int val)
|
|
{
|
|
control(channel, ctrl_highrpn, val);
|
|
}
|
|
|
|
void MidiRead::resetctrlrs(int channel, int val)
|
|
{
|
|
control(channel, ctrl_resetctrlrs, val);
|
|
}
|
|
|
|
void MidiRead::allnotesoff(int channel, int val)
|
|
{
|
|
control(channel, ctrl_allnotesoff, val);
|
|
}
|
|
|
|
void MidiRead::pitchbendrange(int channel, int halfnotes)
|
|
{
|
|
highrpn(channel, 0);
|
|
time(0);
|
|
lowrpn(channel, 0);
|
|
time(0);
|
|
data(channel, halfnotes);
|
|
}
|
|
|
|
void MidiRead::noteon(int channel, int note, int vel)
|
|
{
|
|
}
|
|
|
|
void MidiRead::noteoff(int channel, int note, int vel)
|
|
{
|
|
}
|
|
|
|
void MidiRead::time(unsigned long ticks)
|
|
{
|
|
}
|
|
|
|
void MidiRead::pitchbend(int channel, int val)
|
|
{
|
|
}
|
|
|
|
void MidiRead::polyaftertouch(int channel, int note, int val)
|
|
{
|
|
}
|
|
|
|
void MidiRead::aftertouch(int channel, int val)
|
|
{
|
|
}
|
|
|
|
void MidiRead::songpos(unsigned pos)
|
|
{
|
|
}
|
|
|
|
void MidiRead::songselect(unsigned char song)
|
|
{
|
|
}
|
|
|
|
void MidiRead::tunerequest()
|
|
{
|
|
}
|
|
|
|
void MidiRead::timingclock()
|
|
{
|
|
}
|
|
|
|
void MidiRead::start()
|
|
{
|
|
}
|
|
|
|
void MidiRead::cont()
|
|
{
|
|
}
|
|
|
|
void MidiRead::stop()
|
|
{
|
|
}
|
|
|
|
void MidiRead::activesense()
|
|
{
|
|
}
|
|
|
|
void MidiRead::sysex(int syslen, const unsigned char* sysdata)
|
|
{
|
|
}
|
|
|
|
void MidiRead::gmreset()
|
|
{
|
|
}
|
|
|
|
void MidiRead::gsreset()
|
|
{
|
|
}
|
|
|
|
void MidiRead::gsexit()
|
|
{
|
|
}
|
|
|
|
void MidiRead::xgreset()
|
|
{
|
|
}
|
|
|
|
void MidiRead::endmidi()
|
|
{
|
|
}
|
|
|
|
void MidiRead::percent(int perc)
|
|
{
|
|
}
|
|
|
|
void MidiRead::error(const char* msg)
|
|
{
|
|
fprintf(stderr, "error: %s\n", msg);
|
|
}
|
|
|
|
void MidiRead::warning(const char* msg)
|
|
{
|
|
fprintf(stderr, "warning: %s\n", msg);
|
|
}
|
|
|
|
FILE* MidiRead::getf()
|
|
{
|
|
return f_;
|
|
}
|
|
|
|
// class MidiWrite
|
|
|
|
const char* MidiWrite::copyright()
|
|
{
|
|
return (const char*)::copyright;
|
|
}
|
|
|
|
MidiWrite::MidiWrite(const char* filename)
|
|
{
|
|
midiname_ = filename;
|
|
if (midiname_)
|
|
f_ = fopen(midiname_, WRITE_BINARY);
|
|
else
|
|
f_ = 0;
|
|
trackpos_ = -1;
|
|
curpos_ = 0;
|
|
trackchannel_ = -1;
|
|
|
|
bufpos_ = 0;
|
|
buflen_ = 0;
|
|
filesize_ = 0;
|
|
trackcount_ = 0;
|
|
curtime_ = curdelta_ = 0;
|
|
lastcode_ = -1;
|
|
clicks_ = 0;
|
|
}
|
|
|
|
MidiWrite::~MidiWrite()
|
|
{
|
|
if (trackcount_ > 0)
|
|
{
|
|
seek(10);
|
|
putword(trackcount_);
|
|
}
|
|
if (trackpos_ > 0)
|
|
endtrack();
|
|
flush();
|
|
if (f_)
|
|
fclose(f_);
|
|
}
|
|
|
|
void MidiWrite::head(int version, int tracks, unsigned clicksperquarter)
|
|
{
|
|
seek(0);
|
|
putlong(MThd);
|
|
putlong(6);
|
|
putword(version);
|
|
putword(tracks); // unknown
|
|
putword(clicks_ = clicksperquarter);
|
|
}
|
|
|
|
void MidiWrite::track()
|
|
{
|
|
if (trackpos_ > 0)
|
|
endtrack();
|
|
endtrack_ = 0;
|
|
lastcode_ = -1;
|
|
curtime_ = curdelta_ = 0;
|
|
seek(trackpos_ = filesize_);
|
|
putlong(MTrk);
|
|
putlong(0); // unknown yet
|
|
trackcount_++;
|
|
}
|
|
|
|
void MidiWrite::endtrack()
|
|
{
|
|
seek(filesize_);
|
|
if (!endtrack_)
|
|
end();
|
|
if (trackpos_ <= 0)
|
|
return;
|
|
seek(trackpos_+4);
|
|
putlong(filesize_ - trackpos_ - 8);
|
|
trackpos_ = 0;
|
|
}
|
|
|
|
void MidiWrite::event(int what, int len, const unsigned char* data)
|
|
{
|
|
puttime();
|
|
putcode(what);
|
|
put(len, data);
|
|
}
|
|
|
|
void MidiWrite::prefixchannel(unsigned char channel)
|
|
{
|
|
meta(0x20, 1, &channel);
|
|
}
|
|
|
|
void MidiWrite::prefixport(unsigned char port)
|
|
{
|
|
meta(0x21, 1, &port);
|
|
}
|
|
|
|
void MidiWrite::text(int what, int len, const unsigned char* txt)
|
|
{
|
|
if (len < 0)
|
|
len = strlen((char *)txt);
|
|
meta(what, len, txt);
|
|
}
|
|
|
|
void MidiWrite::meta(int what, int len, const unsigned char* data)
|
|
{
|
|
puttime();
|
|
putcode(0xff);
|
|
putbyte(what);
|
|
putdelta(len);
|
|
put(len, data);
|
|
}
|
|
|
|
void MidiWrite::end()
|
|
{
|
|
if (endtrack_) // don't need end of track event twice
|
|
return;
|
|
endtrack_ = 1;
|
|
meta(0x2f, 0, 0);
|
|
}
|
|
|
|
void MidiWrite::tact(int nom, int denom, int v1, int v2)
|
|
{
|
|
int log2denom;
|
|
|
|
switch(denom)
|
|
{
|
|
case 1: log2denom = 0; break;
|
|
case 2: log2denom = 1; break;
|
|
case 4: log2denom = 2; break;
|
|
case 8: log2denom = 3; break;
|
|
case 16: log2denom = 4; break;
|
|
case 32: log2denom = 5; break;
|
|
case 64: log2denom = 6; break;
|
|
case 128: log2denom = 7; break;
|
|
case 256: log2denom = 8; break;
|
|
default:
|
|
log2denom = 2;
|
|
assert((1 << log2denom) == denom);
|
|
}
|
|
puttime();
|
|
putcode(0xff);
|
|
putbyte(0x58);
|
|
putbyte(4);
|
|
putbyte(nom);
|
|
putbyte(log2denom);
|
|
putbyte(v1);
|
|
putbyte(v2);
|
|
}
|
|
|
|
void MidiWrite::seqnumber(unsigned int seqno)
|
|
{
|
|
puttime();
|
|
putcode(0xff);
|
|
putbyte(0x00);
|
|
putbyte(2);
|
|
putword(seqno);
|
|
}
|
|
|
|
void MidiWrite::smpteofs(int hour, int min, int sec, int frame, int fracframe)
|
|
{
|
|
puttime();
|
|
putcode(0xff);
|
|
putbyte(0x54);
|
|
putbyte(5);
|
|
putbyte(hour);
|
|
putbyte(min);
|
|
putbyte(sec);
|
|
putbyte(frame);
|
|
putbyte(fracframe);
|
|
}
|
|
|
|
void MidiWrite::key(int signature, int isminor)
|
|
{
|
|
puttime();
|
|
putcode(0xff);
|
|
putbyte(0x59);
|
|
putbyte(2);
|
|
putbyte((signed char)signature);
|
|
putbyte((signed char)isminor);
|
|
}
|
|
|
|
void MidiWrite::tempo(unsigned long ticks)
|
|
{
|
|
puttime();
|
|
putcode(0xff);
|
|
putbyte(0x51);
|
|
putbyte(3);
|
|
puttri(ticks);
|
|
}
|
|
|
|
void MidiWrite::program(int channel, int prg)
|
|
{
|
|
assert(channel >= 0 && channel < 16);
|
|
puttime();
|
|
putcode(0xC0 + channel);
|
|
putbyte(prg);
|
|
}
|
|
|
|
void MidiWrite::control(int channel, int what, int val)
|
|
{
|
|
assert(channel >= 0 && channel < 16);
|
|
puttime();
|
|
putcode(0xB0 + channel);
|
|
putbyte(what);
|
|
putbyte(val);
|
|
}
|
|
|
|
void MidiWrite::highbank(int channel, int val)
|
|
{
|
|
control(channel, ctrl_highbank, val);
|
|
}
|
|
|
|
void MidiWrite::wheel(int channel, int val)
|
|
{
|
|
control(channel, ctrl_wheel, val);
|
|
}
|
|
|
|
void MidiWrite::breath(int channel, int val)
|
|
{
|
|
control(channel, ctrl_breath, val);
|
|
}
|
|
|
|
void MidiWrite::foot(int channel, int val)
|
|
{
|
|
control(channel, ctrl_foot, val);
|
|
}
|
|
|
|
void MidiWrite::portamentotime(int channel, int val)
|
|
{
|
|
control(channel, ctrl_portamentotime, val);
|
|
}
|
|
|
|
void MidiWrite::data(int channel, int val)
|
|
{
|
|
control(channel, ctrl_data, val);
|
|
}
|
|
|
|
void MidiWrite::volume(int channel, int val)
|
|
{
|
|
control(channel, ctrl_volume, val);
|
|
}
|
|
|
|
void MidiWrite::balance(int channel, int val)
|
|
{
|
|
control(channel, ctrl_balance, val);
|
|
}
|
|
|
|
void MidiWrite::expression(int channel, int val)
|
|
{
|
|
control(channel, ctrl_expression, val);
|
|
}
|
|
|
|
void MidiWrite::lowbank(int channel, int val)
|
|
{
|
|
control(channel, ctrl_lowbank, val);
|
|
}
|
|
|
|
void MidiWrite::hold(int channel, int val)
|
|
{
|
|
control(channel, ctrl_hold, val);
|
|
}
|
|
|
|
void MidiWrite::reverb(int channel, int val)
|
|
{
|
|
control(channel, ctrl_reverb, val);
|
|
}
|
|
|
|
void MidiWrite::chorus(int channel, int val)
|
|
{
|
|
control(channel, ctrl_chorus, val);
|
|
}
|
|
|
|
void MidiWrite::datainc(int channel, int val)
|
|
{
|
|
control(channel, ctrl_datainc, val);
|
|
}
|
|
|
|
void MidiWrite::datadec(int channel, int val)
|
|
{
|
|
control(channel, ctrl_datadec, val);
|
|
}
|
|
|
|
void MidiWrite::lowrpn(int channel, int val)
|
|
{
|
|
control(channel, ctrl_lowrpn, val);
|
|
}
|
|
|
|
void MidiWrite::highrpn(int channel, int val)
|
|
{
|
|
control(channel, ctrl_highrpn, val);
|
|
}
|
|
|
|
void MidiWrite::resetctrlrs(int channel, int val)
|
|
{
|
|
control(channel, ctrl_resetctrlrs, val);
|
|
}
|
|
|
|
void MidiWrite::allnotesoff(int channel, int val)
|
|
{
|
|
control(channel, ctrl_allnotesoff, val);
|
|
}
|
|
|
|
void MidiWrite::noteon(int channel, int note, int vel)
|
|
{
|
|
assert(channel >= 0 && channel < 16);
|
|
puttime();
|
|
putcode(0x90+channel);
|
|
putbyte(note);
|
|
putbyte(vel);
|
|
}
|
|
|
|
void MidiWrite::noteoff(int channel, int note, int vel)
|
|
{
|
|
assert(channel >= 0 && channel < 16);
|
|
puttime();
|
|
if (vel != 0 || lastcode_ < 0 || (lastcode_ & 0xF0) != 0x90)
|
|
putcode(0x80+channel);
|
|
else
|
|
putcode(0x90+channel); // vel == 0!
|
|
putbyte(note);
|
|
putbyte(vel);
|
|
}
|
|
|
|
void MidiWrite::time(unsigned long ticks)
|
|
{
|
|
if ( ticks >= NOTREALISTIC_PAUSE )
|
|
warning("generating unrealistic large pause");
|
|
curdelta_ += ticks;
|
|
curtime_ += ticks;
|
|
}
|
|
|
|
void MidiWrite::cleardelta()
|
|
{
|
|
curtime_ -= curdelta_;
|
|
curdelta_ = 0;
|
|
}
|
|
|
|
void MidiWrite::pitchbend(int channel, int val)
|
|
{
|
|
assert(channel >= 0 && channel < 16);
|
|
puttime();
|
|
putcode(0xE0 + channel);
|
|
putbyte(val & 0x7F);
|
|
putbyte((val >> 7) & 0x7F);
|
|
}
|
|
|
|
void MidiWrite::polyaftertouch(int channel, int note, int val)
|
|
{
|
|
assert(channel >= 0 && channel < 16);
|
|
puttime();
|
|
putcode(0xA0 + channel);
|
|
putbyte(note);
|
|
putbyte(val);
|
|
}
|
|
|
|
void MidiWrite::aftertouch(int channel, int val)
|
|
{
|
|
assert(channel >= 0 && channel < 16);
|
|
puttime();
|
|
putcode(0xD0 + channel);
|
|
putbyte(val);
|
|
}
|
|
|
|
void MidiWrite::songpos(unsigned pos)
|
|
{
|
|
puttime();
|
|
putcode(0xF2);
|
|
putbyte(pos & 0x7F);
|
|
putbyte((pos >> 7) & 0x7F);
|
|
}
|
|
|
|
void MidiWrite::songselect(unsigned char song)
|
|
{
|
|
puttime();
|
|
putcode(0xF3);
|
|
putbyte(song);
|
|
}
|
|
|
|
void MidiWrite::tunerequest()
|
|
{
|
|
puttime();
|
|
putcode(0xf6);
|
|
}
|
|
|
|
void MidiWrite::timingclock()
|
|
{
|
|
puttime();
|
|
putcode(0xf8);
|
|
}
|
|
|
|
void MidiWrite::start()
|
|
{
|
|
puttime();
|
|
putcode(0xfa);
|
|
}
|
|
|
|
void MidiWrite::cont()
|
|
{
|
|
puttime();
|
|
putcode(0xfb);
|
|
}
|
|
|
|
void MidiWrite::stop()
|
|
{
|
|
puttime();
|
|
putcode(0xfc);
|
|
}
|
|
|
|
void MidiWrite::activesense()
|
|
{
|
|
puttime();
|
|
putcode(0xfe);
|
|
}
|
|
|
|
void MidiWrite::sysex(int syslen, const unsigned char* sysdata)
|
|
{
|
|
puttime();
|
|
putcode(0xf0);
|
|
if (*sysdata == 0xF0)
|
|
{
|
|
syslen--;
|
|
sysdata++;
|
|
}
|
|
put(syslen, sysdata);
|
|
}
|
|
|
|
void MidiWrite::gmreset()
|
|
{
|
|
sysex(sysexlen(sysex_gmreset), sysex_gmreset);
|
|
}
|
|
|
|
void MidiWrite::xgreset()
|
|
{
|
|
sysex(sysexlen(sysex_xgreset), sysex_xgreset);
|
|
}
|
|
|
|
void MidiWrite::gsreset()
|
|
{
|
|
sysex(sysexlen(sysex_gsreset), sysex_gsreset);
|
|
}
|
|
|
|
void MidiWrite::gsexit()
|
|
{
|
|
sysex(sysexlen(sysex_gsexit), sysex_gsexit);
|
|
}
|
|
|
|
void MidiWrite::pitchbendrange(int channel, int halfnotes)
|
|
{
|
|
highrpn(channel, 0);
|
|
lowrpn(channel, 0);
|
|
data(channel, halfnotes);
|
|
}
|
|
|
|
void MidiWrite::putbyte(unsigned char val)
|
|
{
|
|
put(1, &val);
|
|
}
|
|
|
|
void MidiWrite::putcode(unsigned char code)
|
|
{
|
|
int put;
|
|
|
|
assert(code >= 0x80);
|
|
|
|
if (compress)
|
|
put = !(code == lastcode_ && code <= 0x9f);
|
|
else
|
|
put = 1;
|
|
if (put)
|
|
putbyte(code);
|
|
lastcode_ = code;
|
|
}
|
|
|
|
static unsigned char c[4];
|
|
|
|
void MidiWrite::putword(unsigned val)
|
|
{
|
|
c[1] = (unsigned char)(val & 0xff); val >>= 8;
|
|
c[0] = (unsigned char)(val & 0xff);
|
|
put(2, c);
|
|
}
|
|
|
|
void MidiWrite::puttri(unsigned long val)
|
|
{
|
|
c[2] = (unsigned char)(val & 0xff); val >>= 8;
|
|
c[1] = (unsigned char)(val & 0xff); val >>= 8;
|
|
c[0] = (unsigned char)(val & 0xff);
|
|
put(3, c);
|
|
}
|
|
|
|
void MidiWrite::putlong(unsigned long val)
|
|
{
|
|
c[3] = (unsigned char)(val & 0xff); val >>= 8;
|
|
c[2] = (unsigned char)(val & 0xff); val >>= 8;
|
|
c[1] = (unsigned char)(val & 0xff); val >>= 8;
|
|
c[0] = (unsigned char)(val & 0xff);
|
|
put(4, c);
|
|
}
|
|
|
|
void MidiWrite::putdelta(unsigned long val)
|
|
{
|
|
int i = 0, j = 3;
|
|
while (i < 4)
|
|
{
|
|
c[j] = val & 0x7F;
|
|
if (j < 3)
|
|
c[j] |= 0x80;
|
|
val >>= 7;
|
|
i++;
|
|
if (!val)
|
|
break;
|
|
j--;
|
|
}
|
|
put(i, c+j);
|
|
}
|
|
|
|
void MidiWrite::puttime()
|
|
{
|
|
putdelta(curdelta_);
|
|
curdelta_ = 0;
|
|
}
|
|
|
|
void MidiWrite::flush()
|
|
{
|
|
if (buflen_ > 0)
|
|
{
|
|
fseek(f_, curpos_ - bufpos_, SEEK_SET);
|
|
if (fwrite(buf_, buflen_, 1, f_) != 1)
|
|
error("write error (maybe disk full)");
|
|
assert(ftell(f_) == curpos_ - bufpos_ + buflen_);
|
|
bufpos_ = buflen_ = 0;
|
|
}
|
|
}
|
|
|
|
void MidiWrite::put(int len, const unsigned char* c)
|
|
{
|
|
if (len <= 0)
|
|
return;
|
|
if (c == 0 || len > sizeof(buf_))
|
|
return;
|
|
if (sizeof(buf_) - bufpos_ < len)
|
|
flush();
|
|
memcpy(buf_+bufpos_, c, len);
|
|
bufpos_+=len;
|
|
if (bufpos_ > buflen_)
|
|
buflen_ = bufpos_;
|
|
curpos_+= len;
|
|
if (curpos_ > filesize_)
|
|
filesize_ = curpos_;
|
|
}
|
|
|
|
void MidiWrite::seek(long pos)
|
|
{
|
|
assert(pos >= 0 && pos <= filesize_);
|
|
if (curpos_ == pos)
|
|
return;
|
|
if (pos >= curpos_-bufpos_ && pos <= curpos_-bufpos_+buflen_)
|
|
{
|
|
bufpos_ = (int)(pos - (curpos_-bufpos_));
|
|
curpos_ = pos;
|
|
return;
|
|
}
|
|
flush();
|
|
curpos_ = pos;
|
|
}
|
|
|
|
FILE* MidiWrite::getf()
|
|
{
|
|
return f_;
|
|
}
|
|
|
|
void MidiWrite::error(const char* msg)
|
|
{
|
|
fprintf(stderr, "midi write error: %s\n", msg);
|
|
}
|
|
|
|
void MidiWrite::warning(const char* msg)
|
|
{
|
|
fprintf(stderr, "midi write warning: %s\n", msg);
|
|
}
|
|
|
|
int MidiWrite::unitsperquarter()
|
|
{
|
|
return clicks_;
|
|
}
|
|
|
|
MidiCopy::MidiCopy(const char* filename, FILE* f) : MidiRead(filename, f)
|
|
{
|
|
dest_ = NULL;
|
|
for (int c = 0; c < 16; c++)
|
|
mapchannel_[c] = c; // no change
|
|
}
|
|
|
|
void MidiCopy::head(unsigned version, unsigned tracks, unsigned clicksperquarter)
|
|
{
|
|
if (dest_)
|
|
dest_->head(version, 0, clicksperquarter);
|
|
}
|
|
|
|
void MidiCopy::track(int trackno, long length, int channel)
|
|
{
|
|
if (dest_)
|
|
dest_->track();
|
|
}
|
|
|
|
void MidiCopy::endtrack(int trackno)
|
|
{
|
|
if (dest_)
|
|
dest_->endtrack();
|
|
}
|
|
|
|
void MidiCopy::event(int what, int len, unsigned char* data)
|
|
{
|
|
if (dest_)
|
|
dest_->event(what, len, data);
|
|
}
|
|
|
|
|
|
void MidiCopy::seqnumber(unsigned int seqno)
|
|
{
|
|
if (dest_)
|
|
dest_->seqnumber(seqno);
|
|
}
|
|
|
|
void MidiCopy::smpteofs(int hour, int min, int sec, int frame, int fracframe)
|
|
{
|
|
if (dest_)
|
|
dest_->smpteofs(hour, min, sec, frame, fracframe);
|
|
}
|
|
|
|
void MidiCopy::key(int signature, int isminor)
|
|
{
|
|
if (dest_)
|
|
dest_->key(signature, isminor);
|
|
}
|
|
|
|
void MidiCopy::prefixchannel(unsigned char channel)
|
|
{
|
|
if (dest_ && mapchannel_[channel] >= 0)
|
|
dest_->prefixchannel(mapchannel_[channel]);
|
|
}
|
|
|
|
void MidiCopy::prefixport(unsigned char port)
|
|
{
|
|
if (dest_)
|
|
dest_->prefixport(port);
|
|
}
|
|
|
|
void MidiCopy::text(int what, int len, char* whattext, unsigned char* txt)
|
|
{
|
|
if (dest_)
|
|
dest_->text(what, len, txt);
|
|
}
|
|
|
|
void MidiCopy::meta(int what, int len, unsigned char* data)
|
|
{
|
|
if (dest_)
|
|
dest_->meta(what, len, data);
|
|
}
|
|
|
|
void MidiCopy::end()
|
|
{
|
|
if (dest_)
|
|
dest_->end();
|
|
}
|
|
|
|
void MidiCopy::tact(int nom, int denom, int v1, int v2)
|
|
{
|
|
if (dest_)
|
|
dest_->tact(nom, denom, v1,v2);
|
|
}
|
|
|
|
void MidiCopy::tempo(unsigned long ticks)
|
|
{
|
|
if (dest_)
|
|
dest_->tempo(ticks);
|
|
}
|
|
|
|
void MidiCopy::program(int channel, int program)
|
|
{
|
|
if (dest_ && mapchannel_[channel] >= 0)
|
|
dest_->program(mapchannel_[channel], program);
|
|
}
|
|
|
|
void MidiCopy::control(int channel, int what, int value)
|
|
{
|
|
if (dest_ && mapchannel_[channel] >= 0)
|
|
dest_->control(mapchannel_[channel], what, value);
|
|
}
|
|
|
|
void MidiCopy::highbank(int channel, int val)
|
|
{
|
|
if (dest_ && mapchannel_[channel] >= 0)
|
|
dest_->highbank(mapchannel_[channel], val);
|
|
}
|
|
|
|
void MidiCopy::wheel(int channel, int val)
|
|
{
|
|
if (dest_ && mapchannel_[channel] >= 0)
|
|
dest_->wheel(mapchannel_[channel], val);
|
|
}
|
|
|
|
void MidiCopy::breath(int channel, int val)
|
|
{
|
|
if (dest_ && mapchannel_[channel] >= 0)
|
|
dest_->wheel(mapchannel_[channel], val);
|
|
}
|
|
|
|
void MidiCopy::foot(int channel, int val)
|
|
{
|
|
if (dest_ && mapchannel_[channel] >= 0)
|
|
dest_->foot(mapchannel_[channel], val);
|
|
}
|
|
|
|
void MidiCopy::portamentotime(int channel, int val)
|
|
{
|
|
if (dest_ && mapchannel_[channel] >= 0)
|
|
dest_->portamentotime(mapchannel_[channel], val);
|
|
}
|
|
|
|
void MidiCopy::data(int channel, int val)
|
|
{
|
|
if (dest_ && mapchannel_[channel] >= 0)
|
|
dest_->data(mapchannel_[channel], val);
|
|
}
|
|
|
|
void MidiCopy::volume(int channel, int val)
|
|
{
|
|
if (dest_ && mapchannel_[channel] >= 0)
|
|
dest_->volume(mapchannel_[channel], val);
|
|
}
|
|
|
|
void MidiCopy::balance(int channel, int val)
|
|
{
|
|
if (dest_ && mapchannel_[channel] >= 0)
|
|
dest_->balance(mapchannel_[channel], val);
|
|
}
|
|
|
|
void MidiCopy::expression(int channel, int val)
|
|
{
|
|
if (dest_ && mapchannel_[channel] >= 0)
|
|
dest_->expression(mapchannel_[channel], val);
|
|
}
|
|
|
|
void MidiCopy::lowbank(int channel, int val)
|
|
{
|
|
if (dest_ && mapchannel_[channel] >= 0)
|
|
dest_->lowbank(mapchannel_[channel], val);
|
|
}
|
|
|
|
void MidiCopy::hold(int channel, int val)
|
|
{
|
|
if (dest_ && mapchannel_[channel] >= 0)
|
|
dest_->hold(mapchannel_[channel], val);
|
|
}
|
|
|
|
void MidiCopy::reverb(int channel, int val)
|
|
{
|
|
if (dest_ && mapchannel_[channel] >= 0)
|
|
dest_->reverb(mapchannel_[channel], val);
|
|
}
|
|
|
|
void MidiCopy::chorus(int channel, int val)
|
|
{
|
|
if (dest_ && mapchannel_[channel] >= 0)
|
|
dest_->chorus(mapchannel_[channel], val);
|
|
}
|
|
|
|
void MidiCopy::datainc(int channel, int val)
|
|
{
|
|
if (dest_ && mapchannel_[channel] >= 0)
|
|
dest_->datainc(mapchannel_[channel], val);
|
|
}
|
|
|
|
void MidiCopy::datadec(int channel, int val)
|
|
{
|
|
if (dest_ && mapchannel_[channel] >= 0)
|
|
dest_->datadec(mapchannel_[channel], val);
|
|
}
|
|
|
|
void MidiCopy::lowrpn(int channel, int val)
|
|
{
|
|
if (dest_ && mapchannel_[channel] >= 0)
|
|
dest_->lowrpn(mapchannel_[channel], val);
|
|
}
|
|
|
|
void MidiCopy::highrpn(int channel, int val)
|
|
{
|
|
if (dest_ && mapchannel_[channel] >= 0)
|
|
dest_->highrpn(mapchannel_[channel], val);
|
|
}
|
|
|
|
void MidiCopy::resetctrlrs(int channel, int val)
|
|
{
|
|
if (dest_ && mapchannel_[channel] >= 0)
|
|
dest_->resetctrlrs(mapchannel_[channel], val);
|
|
}
|
|
|
|
void MidiCopy::allnotesoff(int channel, int val)
|
|
{
|
|
if (dest_ && mapchannel_[channel] >= 0)
|
|
dest_->allnotesoff(mapchannel_[channel], val);
|
|
}
|
|
|
|
|
|
void MidiCopy::noteon(int channel, int note, int vel)
|
|
{
|
|
if (dest_ && mapchannel_[channel] >= 0)
|
|
dest_->noteon(mapchannel_[channel], note, vel);
|
|
}
|
|
|
|
void MidiCopy::noteoff(int channel, int note, int vel)
|
|
{
|
|
if (dest_ && mapchannel_[channel] >= 0)
|
|
dest_->noteoff(mapchannel_[channel], note, vel);
|
|
}
|
|
|
|
void MidiCopy::time(unsigned long ticks)
|
|
{
|
|
if (dest_)
|
|
dest_->time(ticks);
|
|
}
|
|
|
|
void MidiCopy::pitchbend(int channel, int val)
|
|
{
|
|
if (dest_ && mapchannel_[channel] >= 0)
|
|
dest_->pitchbend(mapchannel_[channel],val);
|
|
}
|
|
|
|
void MidiCopy::polyaftertouch(int channel, int note, int val)
|
|
{
|
|
if (dest_ && mapchannel_[channel] >= 0)
|
|
dest_->polyaftertouch(mapchannel_[channel], note, val);
|
|
}
|
|
|
|
void MidiCopy::aftertouch(int channel, int val)
|
|
{
|
|
if (dest_ && mapchannel_[channel] >= 0)
|
|
dest_->aftertouch(mapchannel_[channel], val);
|
|
}
|
|
|
|
void MidiCopy::songpos(unsigned pos)
|
|
{
|
|
if (dest_)
|
|
dest_->songpos(pos);
|
|
}
|
|
|
|
void MidiCopy::songselect(unsigned char song)
|
|
{
|
|
if (dest_)
|
|
dest_->songselect(song);
|
|
}
|
|
|
|
void MidiCopy::tunerequest()
|
|
{
|
|
if (dest_)
|
|
dest_->tunerequest();
|
|
|
|
}
|
|
|
|
void MidiCopy::timingclock()
|
|
{
|
|
if (dest_)
|
|
dest_->timingclock();
|
|
}
|
|
|
|
void MidiCopy::start()
|
|
{
|
|
if (dest_)
|
|
dest_->start();
|
|
}
|
|
|
|
void MidiCopy::cont()
|
|
{
|
|
if (dest_)
|
|
dest_->cont();
|
|
}
|
|
|
|
void MidiCopy::stop()
|
|
{
|
|
if (dest_)
|
|
dest_->stop();
|
|
}
|
|
|
|
void MidiCopy::activesense()
|
|
{
|
|
if (dest_)
|
|
dest_->activesense();
|
|
}
|
|
|
|
void MidiCopy::sysex(int syslen, unsigned char* sysdata)
|
|
{
|
|
if (dest_)
|
|
dest_->sysex(syslen, sysdata);
|
|
}
|
|
|
|
void MidiCopy::xgreset()
|
|
{
|
|
if (dest_)
|
|
dest_->xgreset();
|
|
}
|
|
|
|
void MidiCopy::gmreset()
|
|
{
|
|
if (dest_)
|
|
dest_->gmreset();
|
|
}
|
|
|
|
void MidiCopy::gsreset()
|
|
{
|
|
if (dest_)
|
|
dest_->gsreset();
|
|
}
|
|
|
|
void MidiCopy::gsexit()
|
|
{
|
|
if (dest_)
|
|
dest_->gsexit();
|
|
}
|
|
|
|
void MidiCopy::setoutput(MidiWrite* dest)
|
|
{
|
|
assert(dest != NULL && dest->getf() != NULL); // need a valid MidiWrite instance
|
|
dest_ = dest;
|
|
}
|
|
|
|
void MidiCopy::stopoutput()
|
|
{
|
|
dest_ = NULL; // owner is responsible for deleting the MidiWrite object
|
|
}
|
|
|
|
MidiWrite* MidiCopy::getoutput()
|
|
{
|
|
return dest_;
|
|
}
|
|
|
|
void MidiCopy::mapchannel(int channel, int newchannel)
|
|
{
|
|
assert(channel >= 0 && channel < 16);
|
|
assert(newchannel >= 0 && newchannel < 16);
|
|
mapchannel_[channel] = newchannel;
|
|
}
|
|
|
|
void MidiCopy::ignorechannel(int channel)
|
|
{
|
|
assert(channel >= 0 && channel < 16);
|
|
mapchannel_[channel] = -1;
|
|
}
|