summaryrefslogtreecommitdiff
path: root/muse2/muse/midifile.cpp
diff options
context:
space:
mode:
authorRobert Jonsson <spamatica@gmail.com>2010-10-13 19:34:22 +0000
committerRobert Jonsson <spamatica@gmail.com>2010-10-13 19:34:22 +0000
commit8a2c2824a59d7644e13bc52c9a0ecbd641f21f95 (patch)
tree064ad3f2bf8daab0ad27b128abd86a9bbdb1e496 /muse2/muse/midifile.cpp
parenta27706d9629e8b592cca4659f865b70adef24e6d (diff)
new branch muse2, first checkin
Diffstat (limited to 'muse2/muse/midifile.cpp')
-rw-r--r--muse2/muse/midifile.cpp678
1 files changed, 678 insertions, 0 deletions
diff --git a/muse2/muse/midifile.cpp b/muse2/muse/midifile.cpp
new file mode 100644
index 00000000..319152d7
--- /dev/null
+++ b/muse2/muse/midifile.cpp
@@ -0,0 +1,678 @@
+//=========================================================
+// MusE
+// Linux Music Editor
+// $Id: midifile.cpp,v 1.17 2004/06/18 08:36:43 wschweer Exp $
+//
+// (C) Copyright 1999-2003 Werner Schweer (ws@seh.de)
+//=========================================================
+
+#include <errno.h>
+#include <values.h>
+#include <assert.h>
+
+#include "song.h"
+#include "midi.h"
+#include "midifile.h"
+#include "drummap.h"
+#include "event.h"
+#include "globals.h"
+#include "midictrl.h"
+#include "marker/marker.h"
+#include "midiport.h"
+#include "midictrl.h"
+#include "mpevent.h"
+#include "gconfig.h"
+
+const char* errString[] = {
+ "no Error",
+ "unexpected EOF",
+ "read Error",
+ "write Error",
+ "bad midifile: 'MTrk' expected",
+ "bad midifile: 'MThd' expected",
+ "bad midi fileformat",
+ };
+
+enum ERROR {
+ MF_NO_ERROR,
+ MF_EOF,
+ MF_READ,
+ MF_WRITE,
+ MF_MTRK,
+ MF_MTHD,
+ MF_FORMAT,
+ };
+
+//---------------------------------------------------------
+// error
+//---------------------------------------------------------
+
+QString MidiFile::error()
+ {
+ return QString(errString[_error]);
+ }
+
+//---------------------------------------------------------
+// MidiFile
+//---------------------------------------------------------
+
+MidiFile::MidiFile(FILE* f)
+ {
+ fp = f;
+ curPos = 0;
+ _mtype = MT_UNKNOWN;
+ _error = MF_NO_ERROR;
+ _tracks = new MidiFileTrackList;
+ }
+
+MidiFile::~MidiFile()
+ {
+ delete _tracks;
+ }
+
+//---------------------------------------------------------
+// read
+// return true on error
+//---------------------------------------------------------
+
+bool MidiFile::read(void* p, size_t len)
+ {
+ for (;;) {
+ curPos += len;
+ size_t rv = fread(p, 1, len, fp);
+ if (rv == len)
+ return false;
+ if (feof(fp)) {
+ _error = MF_EOF;
+ return true;
+ }
+ _error = MF_READ;
+ return true;
+ }
+ return false;
+ }
+
+//---------------------------------------------------------
+// write
+// return true on error
+//---------------------------------------------------------
+
+bool MidiFile::write(const void* p, size_t len)
+ {
+ size_t rv = fwrite(p, 1, len, fp);
+ if (rv == len)
+ return false;
+ _error = MF_WRITE;
+ return true;
+ }
+
+//---------------------------------------------------------
+// writeShort
+// return true on error
+//---------------------------------------------------------
+
+bool MidiFile::writeShort(int i)
+ {
+ short format = BE_SHORT(i);
+ return write(&format, 2);
+ }
+
+//---------------------------------------------------------
+// writeLong
+// return true on error
+//---------------------------------------------------------
+
+bool MidiFile::writeLong(int i)
+ {
+ int format = BE_LONG(i);
+ return write(&format, 4);
+ }
+
+//---------------------------------------------------------
+// readShort
+//---------------------------------------------------------
+
+int MidiFile::readShort()
+ {
+ short format;
+ read(&format, 2);
+ return BE_SHORT(format);
+ }
+
+//---------------------------------------------------------
+// readLong
+// writeLong
+//---------------------------------------------------------
+
+int MidiFile::readLong()
+ {
+ int format;
+ read(&format, 4);
+ return BE_LONG(format);
+ }
+
+/*---------------------------------------------------------
+ * skip
+ * This is meant for skipping a few bytes in a
+ * file or fifo.
+ *---------------------------------------------------------*/
+
+bool MidiFile::skip(size_t len)
+ {
+ char tmp[len];
+ return read(tmp, len);
+ }
+
+/*---------------------------------------------------------
+ * getvl
+ * Read variable-length number (7 bits per byte, MSB first)
+ *---------------------------------------------------------*/
+
+int MidiFile::getvl()
+ {
+ int l = 0;
+ for (int i = 0; i < 16; i++) {
+ uchar c;
+ if (read(&c, 1))
+ return -1;
+ l += (c & 0x7f);
+ if (!(c & 0x80))
+ return l;
+ l <<= 7;
+ }
+ return -1;
+ }
+
+/*---------------------------------------------------------
+ * putvl
+ * Write variable-length number (7 bits per byte, MSB first)
+ *---------------------------------------------------------*/
+
+void MidiFile::putvl(unsigned val)
+ {
+ unsigned long buf = val & 0x7f;
+ while ((val >>= 7) > 0) {
+ buf <<= 8;
+ buf |= 0x80;
+ buf += (val & 0x7f);
+ }
+ for (;;) {
+ put(buf);
+ if (buf & 0x80)
+ buf >>= 8;
+ else
+ break;
+ }
+ }
+
+//---------------------------------------------------------
+// readTrack
+// return true on error
+//---------------------------------------------------------
+
+bool MidiFile::readTrack(MidiFileTrack* t)
+ {
+ MPEventList* el = &(t->events);
+ char tmp[4];
+ if (read(tmp, 4))
+ return true;
+ if (memcmp(tmp, "MTrk", 4)) {
+ _error = MF_MTRK;
+ return true;
+ }
+ int len = readLong(); // len
+ int endPos = curPos + len;
+ status = -1;
+ sstatus = -1; // running status, not reset scanning meta or sysex
+ click = 0;
+
+ int port = 0;
+ int channel = 0;
+
+ for (;;) {
+ MidiPlayEvent event;
+ lastport = -1;
+ lastchannel = -1;
+
+ int rv = readEvent(&event, t);
+ if (lastport != -1) {
+ port = lastport;
+ if (port >= MIDI_PORTS) {
+ printf("port %d >= %d, reset to 0\n", port, MIDI_PORTS);
+ port = 0;
+ }
+ }
+ if (lastchannel != -1) {
+ channel = lastchannel;
+ if (channel >= MIDI_CHANNELS) {
+ printf("channel %d >= %d, reset to 0\n", port, MIDI_CHANNELS);
+ channel = 0;
+ }
+ }
+ if (rv == 0)
+ break;
+ else if (rv == -1)
+ continue;
+ else if (rv == -2) // error
+ return true;
+
+ event.setPort(port);
+ if (event.type() == ME_SYSEX || event.type() == ME_META)
+ event.setChannel(channel);
+ else
+ channel = event.channel();
+ el->add(event);
+ }
+ int end = curPos;
+ if (end != endPos) {
+ printf("MidiFile::readTrack(): TRACKLEN does not fit %d+%d != %d, %d too much\n",
+ endPos-len, len, end, endPos-end);
+ if (end < endPos)
+ skip(endPos - end);
+ }
+ return false;
+ }
+
+//---------------------------------------------------------
+// readEvent
+// returns:
+// 0 End of track
+// -1 Event filtered
+// -2 Error
+//---------------------------------------------------------
+
+int MidiFile::readEvent(MidiPlayEvent* event, MidiFileTrack* t)
+ {
+ uchar me, type, a, b;
+
+ int nclick = getvl();
+ if (nclick == -1) {
+ printf("readEvent: error 1\n");
+ return 0;
+ }
+ click += nclick;
+ for (;;) {
+ if (read(&me, 1)) {
+ printf("readEvent: error 2\n");
+ return 0;
+ }
+ if (me >= 0xf8 && me <= 0xfe)
+ printf("Midi: Real Time Message 0x%02x??\n", me & 0xff);
+ else
+ break;
+ }
+
+ event->setTime(click);
+ int len;
+ unsigned char* buffer;
+
+ if ((me & 0xf0) == 0xf0) {
+ if (me == 0xf0 || me == 0xf7) {
+ //
+ // SYSEX
+ //
+ status = -1; // no running status
+ len = getvl();
+ if (len == -1) {
+ printf("readEvent: error 3\n");
+ return -2;
+ }
+ buffer = new unsigned char[len];
+ if (read(buffer, len)) {
+ printf("readEvent: error 4\n");
+ delete[] buffer;
+ return -2;
+ }
+ if (buffer[len-1] != 0xf7) {
+ printf("SYSEX endet nicht mit 0xf7!\n");
+ // Forstsetzung folgt?
+ }
+ else
+ --len; // don't count 0xf7
+ event->setType(ME_SYSEX);
+ event->setData(buffer, len);
+ if (((unsigned)len == gmOnMsgLen) && memcmp(buffer, gmOnMsg, gmOnMsgLen) == 0) {
+ setMType(MT_GM);
+ return -1;
+ }
+ if (((unsigned)len == gsOnMsgLen) && memcmp(buffer, gsOnMsg, gsOnMsgLen) == 0) {
+ setMType(MT_GS);
+ return -1;
+ }
+ if (((unsigned)len == xgOnMsgLen) && memcmp(buffer, xgOnMsg, xgOnMsgLen) == 0) {
+ setMType(MT_XG);
+ return -1;
+ }
+ if (buffer[0] == 0x41) { // Roland
+ if (mtype() != MT_UNKNOWN)
+ setMType(MT_GS);
+ }
+ else if (buffer[0] == 0x43) { // Yamaha
+ if (mtype() == MT_UNKNOWN || mtype() == MT_GM)
+ setMType(MT_XG);
+ int type = buffer[1] & 0xf0;
+ switch (type) {
+ case 0x00: // bulk dump
+ buffer[1] = 0;
+ break;
+ case 0x10:
+ if (buffer[1] != 0x10) {
+ buffer[1] = 0x10; // fix to Device 1
+ }
+ if (len == 7 && buffer[2] == 0x4c && buffer[3] == 0x08 && buffer[5] == 7) {
+ // part mode
+ // 0 - normal
+ // 1 - DRUM
+ // 2 - DRUM 1
+ // 3 - DRUM 2
+ // 4 - DRUM 3
+ // 5 - DRUM 4
+ printf("xg set part mode channel %d to %d\n", buffer[4]+1, buffer[6]);
+ if (buffer[6] != 0)
+ t->isDrumTrack = true;
+ }
+ break;
+ case 0x20:
+ printf("YAMAHA DUMP REQUEST\n");
+ return -1;
+ case 0x30:
+ printf("YAMAHA PARAMETER REQUEST\n");
+ return -1;
+ default:
+ printf("YAMAHA unknown SYSEX: data[2]=%02x\n", buffer[1]);
+ return -1;
+ }
+ }
+ return 3;
+ }
+ if (me == 0xff) {
+ //
+ // META
+ //
+ status = -1; // no running status
+ if (read(&type, 1)) { // read type
+ printf("readEvent: error 5\n");
+ return -2;
+ }
+ len = getvl(); // read len
+ if (len == -1) {
+ printf("readEvent: error 6\n");
+ return -2;
+ }
+ buffer = new unsigned char[len+1];
+ if (len) {
+ if (read(buffer, len)) {
+ printf("readEvent: error 7\n");
+ delete[] buffer;
+ return -2;
+ }
+ }
+ buffer[len] = 0;
+ switch(type) {
+ case 0x21: // switch port
+ lastport = buffer[0];
+ delete[] buffer;
+ return -1;
+ case 0x20: // switch channel
+ lastchannel = buffer[0];
+ delete[] buffer;
+ return -1;
+ case 0x2f: // End of Track
+ delete[] buffer;
+ return 0;
+ default:
+ event->setType(ME_META);
+ event->setData(buffer, len+1);
+ event->setA(type);
+ return 3;
+ }
+ }
+ else {
+ printf("Midi: unknown Message 0x%02x\n", me & 0xff);
+ return -1;
+ }
+ }
+
+ if (me & 0x80) { // status byte
+ status = me;
+ sstatus = status;
+ if (read(&a, 1)) {
+ printf("readEvent: error 9\n");
+ return -2;
+ }
+ a &= 0x7F;
+ }
+ else {
+ if (status == -1) {
+ printf("readEvent: no running status, read 0x%02x sstatus %x\n", me, sstatus);
+ if (sstatus == -1)
+ return -1;
+ status = sstatus;
+ }
+ a = me;
+ }
+ b = 0;
+ switch (status & 0xf0) {
+ case ME_NOTEOFF:
+ case ME_NOTEON:
+ case ME_POLYAFTER:
+ case ME_CONTROLLER:
+ case ME_PITCHBEND:
+ if (read(&b, 1)) {
+ printf("readEvent: error 15\n");
+ return -2;
+ }
+ event->setB(b & 0x80 ? 0 : b);
+ break;
+ case ME_PROGRAM:
+ case ME_AFTERTOUCH:
+ break;
+ default: // f1 f2 f3 f4 f5 f6 f7 f8 f9
+ printf("BAD STATUS 0x%02x, me 0x%02x\n", status, me);
+ return -2;
+ }
+ event->setA(a & 0x7f);
+ event->setType(status & 0xf0);
+ event->setChannel(status & 0xf);
+ if ((a & 0x80) || (b & 0x80)) {
+ printf("8'tes Bit in Daten(%02x %02x): tick %d read 0x%02x status:0x%02x\n",
+ a & 0xff, b & 0xff, click, me, status);
+ printf("readEvent: error 16\n");
+ if (b & 0x80) {
+ // Try to fix: interpret as channel byte
+ status = b & 0xf0;
+ sstatus = status;
+ return 3;
+ }
+ return -1;
+ }
+ if (event->type() == ME_PITCHBEND) {
+ int val = (event->dataB() << 7) + event->dataA();
+ val -= 8192;
+ event->setA(val);
+ }
+ return 3;
+ }
+
+//---------------------------------------------------------
+// writeTrack
+//---------------------------------------------------------
+
+bool MidiFile::writeTrack(const MidiFileTrack* t)
+ {
+ //FIXME: By T356 01/19/2010
+ // If saving as a compressed file (gz or bz2),
+ // the file is a pipe, and pipes can't seek !
+ // This results in a corrupted midi file.
+ // So exporting compressed midi has been disabled (elsewhere)
+ // for now...
+
+ const MPEventList* events = &(t->events);
+ write("MTrk", 4);
+ int lenpos = ftell(fp);
+ writeLong(0); // dummy len
+
+ status = -1;
+ int tick = 0;
+ for (iMPEvent i = events->begin(); i != events->end(); ++i) {
+ int ntick = i->time();
+ if (ntick < tick) {
+ printf("MidiFile::writeTrack: ntick %d < tick %d\n", ntick, tick);
+ ntick = tick;
+ }
+ putvl(((ntick - tick) * config.midiDivision + config.division/2)/config.division);
+ tick = ntick;
+ writeEvent(&(*i));
+ }
+
+ //---------------------------------------------------
+ // write "End Of Track" Meta
+ // write Track Len
+ //
+
+ putvl(0);
+ put(0xff); // Meta
+ put(0x2f); // EOT
+ putvl(0); // len 0
+ int endpos = ftell(fp);
+ fseek(fp, lenpos, SEEK_SET);
+ writeLong(endpos-lenpos-4); // tracklen
+ fseek(fp, endpos, SEEK_SET);
+ return false;
+ }
+
+//---------------------------------------------------------
+// writeEvent
+//---------------------------------------------------------
+
+void MidiFile::writeEvent(const MidiPlayEvent* event)
+ {
+ int c = event->channel();
+ int nstat = event->type();
+
+ // we dont save meta data into smf type 0 files:
+
+ if (config.smfFormat == 0 && nstat == ME_META)
+ return;
+
+ nstat |= c;
+ //
+ // running status; except for Sysex- and Meta Events
+ //
+ if (((nstat & 0xf0) != 0xf0) && (nstat != status)) {
+ status = nstat;
+ put(nstat);
+ }
+ switch (event->type()) {
+ case ME_NOTEOFF:
+ case ME_NOTEON:
+ case ME_POLYAFTER:
+ case ME_CONTROLLER:
+ case ME_PITCHBEND:
+ put(event->dataA());
+ put(event->dataB());
+ break;
+ case ME_PROGRAM: // Program Change
+ case ME_AFTERTOUCH: // Channel Aftertouch
+ put(event->dataA());
+ break;
+ case ME_SYSEX:
+ put(0xf0);
+ putvl(event->len() + 1); // including 0xf7
+ write(event->data(), event->len());
+ put(0xf7);
+ status = -1; // invalidate running status
+ break;
+ case ME_META:
+ put(0xff);
+ put(event->dataA());
+ putvl(event->len());
+ write(event->data(), event->len());
+ status = -1;
+ break;
+ }
+ }
+
+//---------------------------------------------------------
+// write
+// returns true on error
+//---------------------------------------------------------
+
+bool MidiFile::write()
+ {
+ write("MThd", 4);
+ writeLong(6); // header len
+ writeShort(config.smfFormat);
+ if (config.smfFormat == 0) {
+ writeShort(1);
+ MidiFileTrack dst;
+ for (iMidiFileTrack i = _tracks->begin(); i != _tracks->end(); ++i) {
+ MPEventList* sl = &((*i)->events);
+ for (iMPEvent ie = sl->begin(); ie != sl->end(); ++ie)
+ dst.events.add(*ie);
+ }
+ writeShort(1);
+ writeShort(_division);
+ writeTrack(&dst);
+ }
+ else {
+ writeShort(ntracks);
+
+ writeShort(_division);
+ for (ciMidiFileTrack i = _tracks->begin(); i != _tracks->end(); ++i)
+ writeTrack(*i);
+ }
+ return (ferror(fp) != 0);
+ }
+
+//---------------------------------------------------------
+// readMidi
+// returns true on error
+//---------------------------------------------------------
+
+bool MidiFile::read()
+ {
+ _error = MF_NO_ERROR;
+ int i;
+ char tmp[4];
+
+ if (read(tmp, 4))
+ return true;
+ int len = readLong();
+ if (memcmp(tmp, "MThd", 4) || len < 6) {
+ _error = MF_MTHD;
+ return true;
+ }
+ format = readShort();
+ ntracks = readShort();
+ _division = readShort();
+
+ if (_division < 0)
+ _division = (-(_division/256)) * (_division & 0xff);
+ if (len > 6)
+ skip(len-6); // skip excess bytes
+
+ switch (format) {
+ case 0:
+ {
+ MidiFileTrack* t = new MidiFileTrack;
+ _tracks->push_back(t);
+ if (readTrack(t))
+ return true;
+ }
+ break;
+ case 1:
+ for (i = 0; i < ntracks; i++) {
+ MidiFileTrack* t = new MidiFileTrack;
+ _tracks->push_back(t);
+ if (readTrack(t))
+ return true;
+ }
+ break;
+ default:
+ _error = MF_FORMAT;
+ return true;
+ }
+ return false;
+ }
+