2
0
Fork 0
OPL/dro2midi/midiio.cpp

2162 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;
}