summaryrefslogtreecommitdiff
path: root/muse_qt4_evolution/muse/midi.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'muse_qt4_evolution/muse/midi.cpp')
-rw-r--r--muse_qt4_evolution/muse/midi.cpp633
1 files changed, 633 insertions, 0 deletions
diff --git a/muse_qt4_evolution/muse/midi.cpp b/muse_qt4_evolution/muse/midi.cpp
new file mode 100644
index 00000000..a182fe4a
--- /dev/null
+++ b/muse_qt4_evolution/muse/midi.cpp
@@ -0,0 +1,633 @@
+//=============================================================================
+// MusE
+// Linux Music Editor
+// $Id:$
+//
+// Copyright (C) 2002-2006 by Werner Schweer and others
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//=============================================================================
+
+#include "song.h"
+#include "midi.h"
+#include "midiedit/drummap.h"
+#include "event.h"
+#include "globals.h"
+#include "midictrl.h"
+#include "midictrl.h"
+#include "audio.h"
+#include "audiodev.h"
+#include "wave.h"
+#include "synth.h"
+#include "sync.h"
+#include "gconfig.h"
+#include "ticksynth.h"
+#include "al/tempo.h"
+#include "al/sig.h"
+#include "part.h"
+#include "midiplugin.h"
+#include "midiinport.h"
+#include "midioutport.h"
+#include "instruments/minstrument.h"
+
+extern void dump(const unsigned char* p, int n);
+
+unsigned const char gmOnMsg[] = {
+ 0x7e, // Non-Real Time header
+ 0x7f, // ID of target device (7f = all devices)
+ 0x09,
+ 0x01
+ };
+unsigned const char gsOnMsg[] = {
+ 0x41, // roland id
+ 0x10, // Id of target device (default = 10h for roland)
+ 0x42, // model id (42h = gs devices)
+ 0x12, // command id (12h = data set)
+ 0x40, // address & value
+ 0x00,
+ 0x7f,
+ 0x00,
+ 0x41 // checksum?
+ };
+unsigned const char xgOnMsg[] = {
+ 0x43, // yamaha id
+ 0x10, // device number (0)
+ 0x4c, // model id
+ 0x00, // address (high, mid, low)
+ 0x00,
+ 0x7e,
+ 0x00 // data
+ };
+unsigned const int gmOnMsgLen = sizeof(gmOnMsg);
+unsigned const int gsOnMsgLen = sizeof(gsOnMsg);
+unsigned const int xgOnMsgLen = sizeof(xgOnMsg);
+
+/*---------------------------------------------------------
+ * midi_meta_name
+ *---------------------------------------------------------*/
+
+QString midiMetaName(int meta)
+ {
+ const char* s = "";
+ switch (meta) {
+ case 0: s = "Sequence Number"; break;
+ case 1: s = "Text Event"; break;
+ case 2: s = "Copyright"; break;
+ case 3: s = "Sequence/Track Name"; break;
+ case 4: s = "Instrument Name"; break;
+ case 5: s = "Lyric"; break;
+ case 6: s = "Marker"; break;
+ case 7: s = "Cue Point"; break;
+ case 8:
+ case 9:
+ case 0x0a:
+ case 0x0b:
+ case 0x0c:
+ case 0x0d:
+ case 0x0e:
+ case 0x0f: s = "Text"; break;
+ case 0x20: s = "Channel Prefix"; break;
+ case 0x21: s = "Port Change"; break;
+ case 0x2f: s = "End of Track"; break;
+ case 0x51: s = "Set Tempo"; break;
+ case 0x54: s = "SMPTE Offset"; break;
+ case 0x58: s = "Time Signature"; break;
+ case 0x59: s = "Key Signature"; break;
+ case 0x74: s = "Sequencer-Specific1"; break;
+ case 0x7f: s = "Sequencer-Specific2"; break;
+ default:
+ break;
+ }
+ return QString(s);
+ }
+
+//---------------------------------------------------------
+// QString nameSysex
+//---------------------------------------------------------
+
+QString nameSysex(unsigned int len, const unsigned char* buf)
+ {
+ QString s;
+ switch(buf[0]) {
+ case 0x00:
+ if (buf[1] == 0 && buf[2] == 0x41)
+ s = "Microsoft";
+ break;
+ case 0x01: s = "Sequential Circuits: "; break;
+ case 0x02: s = "Big Briar: "; break;
+ case 0x03: s = "Octave / Plateau: "; break;
+ case 0x04: s = "Moog: "; break;
+ case 0x05: s = "Passport Designs: "; break;
+ case 0x06: s = "Lexicon: "; break;
+ case 0x07: s = "Kurzweil"; break;
+ case 0x08: s = "Fender"; break;
+ case 0x09: s = "Gulbransen"; break;
+ case 0x0a: s = "Delta Labas"; break;
+ case 0x0b: s = "Sound Comp."; break;
+ case 0x0c: s = "General Electro"; break;
+ case 0x0d: s = "Techmar"; break;
+ case 0x0e: s = "Matthews Research"; break;
+ case 0x10: s = "Oberheim"; break;
+ case 0x11: s = "PAIA: "; break;
+ case 0x12: s = "Simmons: "; break;
+ case 0x13: s = "DigiDesign"; break;
+ case 0x14: s = "Fairlight: "; break;
+ case 0x15: s = "JL Cooper"; break;
+ case 0x16: s = "Lowery"; break;
+ case 0x17: s = "Lin"; break;
+ case 0x18: s = "Emu"; break;
+ case 0x1b: s = "Peavy"; break;
+ case 0x20: s = "Bon Tempi: "; break;
+ case 0x21: s = "S.I.E.L: "; break;
+ case 0x23: s = "SyntheAxe: "; break;
+ case 0x24: s = "Hohner"; break;
+ case 0x25: s = "Crumar"; break;
+ case 0x26: s = "Solton"; break;
+ case 0x27: s = "Jellinghaus Ms"; break;
+ case 0x28: s = "CTS"; break;
+ case 0x29: s = "PPG"; break;
+ case 0x2f: s = "Elka"; break;
+ case 0x36: s = "Cheetah"; break;
+ case 0x3e: s = "Waldorf"; break;
+ case 0x40: s = "Kawai: "; break;
+ case 0x41: s = "Roland: "; break;
+ case 0x42: s = "Korg: "; break;
+ case 0x43: s = "Yamaha: "; break;
+ case 0x44: s = "Casio"; break;
+ case 0x45: s = "Akai"; break;
+ case 0x7c: s = "MusE Soft Synth"; break;
+ case 0x7d: s = "Educational Use"; break;
+ case 0x7e: s = "Universal: Non Real Time"; break;
+ case 0x7f: s = "Universal: Real Time"; break;
+ default: s = "??: "; break;
+ }
+ //
+ // following messages should not show up in event list
+ // they are filtered while importing midi files
+ //
+ if ((len == gmOnMsgLen) && memcmp(buf, gmOnMsg, gmOnMsgLen) == 0)
+ s += "GM-ON";
+ else if ((len == gsOnMsgLen) && memcmp(buf, gsOnMsg, gsOnMsgLen) == 0)
+ s += "GS-ON";
+ else if ((len == xgOnMsgLen) && memcmp(buf, xgOnMsg, xgOnMsgLen) == 0)
+ s += "XG-ON";
+ return s;
+ }
+
+//---------------------------------------------------------
+// buildMidiEventList
+// TODO:
+// parse data increment/decrement controller
+// NRPN/RPN fine/course data 7/14 Bit
+// must we set datah/datal to zero after change
+// of NRPN/RPN register?
+// generally: how to handle incomplete messages?
+//---------------------------------------------------------
+
+void buildMidiEventList(EventList* del, const MidiEventList* el, MidiTrack* track, int channel,
+ int div, bool addSysexMeta)
+ {
+ QString tname;
+ int hbank = 0xff;
+ int lbank = 0xff;
+ int rpnh = -1;
+ int rpnl = -1;
+ int datah = 0;
+ int datal = 0;
+ int dataType = 0; // 0 : disabled, 0x20000 : rpn, 0x30000 : nrpn
+
+ EventList mel;
+ int metaChannel = -1;
+ for (iMidiEvent i = el->begin(); i != el->end(); ++i) {
+ MidiEvent ev = *i;
+ if (ev.type() == ME_META) {
+ if (ev.dataA() == 0x20) {
+ metaChannel = ((char*)(ev.data()))[0];
+ }
+ if (metaChannel == channel) {
+ if (ev.dataA() == 0x4) {
+ tname = (char*)(ev.data());
+ }
+ }
+ }
+ if (!addSysexMeta && (ev.type() == ME_SYSEX || ev.type() == ME_META))
+ continue;
+ if (!(ev.type() == ME_SYSEX || ev.type() == ME_META || (ev.channel() == channel)))
+ continue;
+ int tick = ev.time();
+ Event e;
+ switch(ev.type()) {
+ case ME_NOTEON:
+ e.setType(Note);
+
+ if (track->useDrumMap()) {
+ int instr = track->drumMap()->inmap(ev.dataA());
+ e.setPitch(instr);
+ }
+ else
+ e.setPitch(ev.dataA());
+
+ e.setVelo(ev.dataB());
+ e.setLenTick(0);
+ break;
+ case ME_NOTEOFF:
+ e.setType(Note);
+ if (track->useDrumMap()) {
+ int instr = track->drumMap()->inmap(ev.dataA());
+ e.setPitch(instr);
+ }
+ else
+ e.setPitch(ev.dataA());
+ e.setVelo(0);
+ e.setVeloOff(ev.dataB());
+ e.setLenTick(0);
+ break;
+ case ME_POLYAFTER:
+ e.setType(PAfter);
+ e.setA(ev.dataA());
+ e.setB(ev.dataB());
+ break;
+ case ME_CONTROLLER:
+ {
+ int val = ev.dataB();
+ switch(ev.dataA()) {
+ case CTRL_HBANK:
+ hbank = val;
+ break;
+
+ case CTRL_LBANK:
+ lbank = val;
+ break;
+
+ case CTRL_HDATA:
+ datah = val;
+ // check if a CTRL_LDATA follows
+ // e.g. wie have a 14 bit controller:
+ {
+ iMidiEvent ii = i;
+ ++ii;
+ bool found = false;
+ for (; ii != el->end(); ++ii) {
+ MidiEvent ev = *ii;
+ if (ev.type() == ME_CONTROLLER) {
+ if (ev.dataA() == CTRL_LDATA) {
+ // handle later
+ found = true;
+ }
+ break;
+ }
+ }
+ if (!found) {
+ if (rpnh == -1 || rpnl == -1) {
+ printf("parameter number not defined, data 0x%x\n", datah);
+ }
+ else {
+ int ctrl = dataType | (rpnh << 8) | rpnl;
+ e.setType(Controller);
+ e.setA(ctrl);
+ e.setB(datah);
+ }
+ }
+ }
+ break;
+
+ case CTRL_LDATA:
+ datal = val;
+
+ if (rpnh == -1 || rpnl == -1) {
+ printf("parameter number not defined, data 0x%x 0x%x, tick %d, channel %d\n",
+ datah, datal, tick, channel);
+ break;
+ }
+ // assume that the sequence is always
+ // CTRL_HDATA - CTRL_LDATA
+ // eg. that LDATA is always send last
+
+ e.setType(Controller);
+ // 14 Bit RPN/NRPN
+ e.setA((dataType+0x30000) | (rpnh << 8) | rpnl);
+ e.setB((datah << 7) | datal);
+ break;
+
+ case CTRL_HNRPN:
+ rpnh = val;
+ dataType = 0x30000;
+ break;
+
+ case CTRL_LNRPN:
+ rpnl = val;
+ dataType = 0x30000;
+ break;
+
+ case CTRL_HRPN:
+ rpnh = val;
+ dataType = 0x20000;
+ break;
+
+ case CTRL_LRPN:
+ rpnl = val;
+ dataType = 0x20000;
+ break;
+
+ default:
+ e.setType(Controller);
+ e.setA(ev.dataA());
+ e.setB(val);
+ break;
+ }
+ }
+ break;
+
+ case ME_PROGRAM:
+ e.setType(Controller);
+ e.setA(CTRL_PROGRAM);
+ e.setB((hbank << 16) | (lbank << 8) | ev.dataA());
+ break;
+
+ case ME_AFTERTOUCH:
+ e.setType(CAfter);
+ e.setA(ev.dataA());
+ break;
+
+ case ME_PITCHBEND:
+ e.setType(Controller);
+ e.setA(CTRL_PITCH);
+ e.setB(ev.dataA());
+ break;
+
+ case ME_SYSEX:
+ e.setType(Sysex);
+ e.setData(ev.data(), ev.len());
+ break;
+
+ case ME_META:
+ {
+ const unsigned char* data = ev.data();
+ switch (ev.dataA()) {
+ case 0x01: // Text
+ if (track->comment().isEmpty())
+ track->setComment(QString((const char*)data));
+ else
+ track->setComment(track->comment() + "\n" + QString((const char*)data));
+ break;
+ case 0x02: // Copyright
+ config.copyright = (char*)data;
+ break;
+ case 0x03: // Sequence-/TrackName
+ tname = (char*)data;
+ break;
+ case 0x6: // Marker
+ {
+ Pos pos((tick * config.division + div/2) / div, AL::TICKS);
+ song->addMarker(QString((const char*)(data)), pos);
+ }
+ break;
+ case 0x4: // Instrument Name
+ case 0x5: // Lyrics
+ case 0x8: // text
+ case 0x9:
+ case 0xa:
+ case 0x20: // channel prefix
+ case 0x21: // port change
+ break;
+
+ case 0x0f: // Track Comment
+ track->setComment(QString((char*)data));
+ break;
+
+ case 0x51: // Tempo
+ {
+ int tempo = data[2] + (data[1] << 8) + (data[0] <<16);
+ int ltick = (tick * config.division + div/2) / div;
+ AL::tempomap.addTempo(ltick, tempo);
+ }
+ break;
+
+ case 0x58: // Time Signature
+ {
+ int timesig_z = data[0];
+ int n = data[1];
+ int timesig_n = 1;
+ for (int i = 0; i < n; i++)
+ timesig_n *= 2;
+ int ltick = (tick * config.division + div/2) / div;
+ AL::sigmap.add(ltick, AL::TimeSignature(timesig_z, timesig_n));
+ }
+ break;
+
+ case 0x59: // Key Signature
+ printf("Meta: Key Signature %d %d\n", data[0], data[1]);
+ // track->scale.set(data[0]);
+ // track->scale.setMajorMinor(data[1]);
+ break;
+
+ case 0x7f: // Sequencer Specific
+ printf("Meta: Seq specific:len %d\n", ev.len());
+ break;
+
+ default:
+ printf("unknown Meta 0x%x %d\n", ev.dataA(), ev.dataA());
+ }
+ }
+ break;
+ } // switch(ev.type()
+ if (!e.empty()) {
+ e.setTick(tick);
+ mel.add(e);
+ }
+ } // i != el->end()
+
+ //---------------------------------------------------
+ // resolve NoteOff events
+ //---------------------------------------------------
+
+ for (iEvent i = mel.begin(); i != mel.end(); ++i) {
+ Event ev = i->second;
+ if (!ev.isNote())
+ continue;
+ if (ev.lenTick())
+ continue;
+// printf("%d note %d %d\n", ev.tick(), ev.pitch(), ev.velo());
+ if (ev.isNoteOff()) {
+ bool found = false;
+ iEvent k = i;
+// printf(" start with note off\n");
+ for (++k; k != mel.end(); ++k) {
+ Event event = k->second;
+ if (event.tick() > ev.tick())
+ break;
+ if (event.isNote() && event.velo() && event.pitch() == ev.pitch()) {
+ ev.setLenTick(1);
+ ev.setVelo(event.velo());
+ ev.setVeloOff(0);
+ found = true;
+// printf(" found on: %d\n", event.tick());
+ break;
+ }
+ }
+ if (!found) {
+ // maybe found note with velocity zero is really a
+ // note-on !
+ k = i;
+ found = true;
+ for (++k; k != mel.end(); ++k) {
+ Event event = k->second;
+ if (!event.isNote())
+ continue;
+ if (event.pitch() == ev.pitch()) {
+ if (event.velo() == 0) {
+ found = true;
+ int t = event.tick() - ev.tick();
+ ev.setLenTick(t);
+ ev.setVelo(1);
+ ev.setVeloOff(0);
+ mel.erase(k);
+ i = mel.begin();
+ }
+ break;
+ }
+ }
+ if (!found) {
+ printf("NOTE OFF without Note ON tick %d type %d %d %d\n",
+ ev.tick(), ev.type(), ev.pitch(), ev.velo());
+ }
+ else
+ continue;
+ }
+ else {
+ mel.erase(k);
+ i = mel.begin(); // DEBUG
+ continue;
+ }
+ }
+ iEvent k;
+ // ev is noteOn
+ for (k = mel.lower_bound(ev.tick()); k != mel.end(); ++k) {
+ Event event = k->second;
+// printf(" - %d(%d,%d)\n", event.tick(), event.pitch(), event.velo());
+ if (ev.isNoteOff(event)) {
+ int t = k->first - i->first;
+ if (t <= 0) {
+ if (debugMsg) {
+ printf("Note len is (%d-%d)=%d, set to 1\n",
+ k->first, i->first, k->first - i->first);
+ ev.dump();
+ event.dump();
+ }
+ t = 1;
+ }
+ ev.setLenTick(t);
+ ev.setVeloOff(event.veloOff());
+ break;
+ }
+ }
+ if (k == mel.end()) {
+ printf(" -no note-off! %d pitch %d velo %d\n",
+ ev.tick(), ev.pitch(), ev.velo());
+ //
+ // switch off at end of measure
+ //
+ int endTick = song->roundUpBar(ev.tick()+1);
+ ev.setLenTick(endTick-ev.tick());
+ }
+ else {
+ mel.erase(k);
+ i = mel.begin();
+ }
+ }
+
+ for (iEvent i = mel.begin(); i != mel.end(); ++i) {
+ Event ev = i->second;
+ if (ev.isNoteOff()) {
+ printf("+extra note-off! %d pitch %d velo %d\n",
+ i->first, ev.pitch(), ev.velo());
+// ev.dump();
+ continue;
+ }
+ int tick = (ev.tick() * config.division + div/2) / div;
+ if (ev.isNote()) {
+ int lenTick = (ev.lenTick() * config.division + div/2) / div;
+ ev.setLenTick(lenTick);
+ }
+ ev.setTick(tick);
+ if (ev.type() == Controller) {
+ int id = ev.dataA();
+ CVal val;
+ val.i = ev.dataB();
+
+// bool found = false;
+ Ctrl* c = track->getController(id);
+ if (c)
+ c->add(tick, val);
+ else
+ // if no managed controller, store as event
+ del->add(ev);
+ }
+ else
+ del->add(ev);
+ }
+ if (!tname.isEmpty())
+ track->setName(tname);
+ }
+
+//---------------------------------------------------------
+// initMidiDevices
+//---------------------------------------------------------
+
+void Audio::initMidiDevices()
+ {
+ //
+ // test for explicit instrument initialization
+ //
+ MidiOutPortList* mpl = song->midiOutPorts();
+ for (iMidiOutPort i = mpl->begin(); i != mpl->end(); ++i) {
+ MidiOutPort* mp = *i;
+ MidiInstrument* instr = mp->instrument();
+ if (!instr)
+ continue; // TODO: use default instrument?
+ EventList* events = instr->midiInit();
+ for (iEvent ie = events->begin(); ie != events->end(); ++ie) {
+ MidiEvent ev(0, 0, ie->second);
+ mp->playMidiEvent(&ev);
+ }
+ }
+ }
+
+//---------------------------------------------------------
+// resetMidiDevices
+//---------------------------------------------------------
+
+void Audio::resetMidiDevices()
+ {
+ MidiTrackList* mcl = song->midis();
+ for (iMidiTrack i = mcl->begin(); i != mcl->end(); ++i) {
+ MidiTrack* mc = *i;
+ if (!mc->mute() && mc->autoRead()) {
+ CtrlList* cl = mc->controller();
+ for (iCtrl ic = cl->begin(); ic != cl->end(); ++ic) {
+ ic->second->setCurVal(CTRL_VAL_UNKNOWN);
+ }
+ }
+ }
+ MidiOutPortList* mpl = song->midiOutPorts();
+ for (iMidiOutPort i = mpl->begin(); i != mpl->end(); ++i) {
+ MidiOutPort* mp = *i;
+ if (!mp->mute() && mp->autoRead()) {
+ CtrlList* cl = mp->controller();
+ for (iCtrl ic = cl->begin(); ic != cl->end(); ++ic) {
+ ic->second->setCurVal(CTRL_VAL_UNKNOWN);
+ }
+ }
+ }
+ }
+