//============================================================================= // 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); } } } }