diff options
Diffstat (limited to 'muse_qt4_evolution/muse/midi.cpp')
-rw-r--r-- | muse_qt4_evolution/muse/midi.cpp | 633 |
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); + } + } + } + } + |