diff options
author | Robert Jonsson <spamatica@gmail.com> | 2010-10-13 19:34:22 +0000 |
---|---|---|
committer | Robert Jonsson <spamatica@gmail.com> | 2010-10-13 19:34:22 +0000 |
commit | 8a2c2824a59d7644e13bc52c9a0ecbd641f21f95 (patch) | |
tree | 064ad3f2bf8daab0ad27b128abd86a9bbdb1e496 /muse2/muse/importmidi.cpp | |
parent | a27706d9629e8b592cca4659f865b70adef24e6d (diff) |
new branch muse2, first checkin
Diffstat (limited to 'muse2/muse/importmidi.cpp')
-rw-r--r-- | muse2/muse/importmidi.cpp | 608 |
1 files changed, 608 insertions, 0 deletions
diff --git a/muse2/muse/importmidi.cpp b/muse2/muse/importmidi.cpp new file mode 100644 index 00000000..2c8bfb24 --- /dev/null +++ b/muse2/muse/importmidi.cpp @@ -0,0 +1,608 @@ +//========================================================= +// MusE +// Linux Music Editor +// $Id: importmidi.cpp,v 1.26.2.10 2009/11/05 03:14:35 terminator356 Exp $ +// +// (C) Copyright 1999-2003 Werner Schweer (ws@seh.de) +//========================================================= + +#include <assert.h> +#include <errno.h> +#include <values.h> +#include <qmessagebox.h> + +#include "app.h" +#include "song.h" +#include "globals.h" +#include "filedialog.h" +#include "midi.h" +#include "midifile.h" +#include "midiport.h" +#include "transport.h" +#include "arranger.h" +#include "mpevent.h" +#include "event.h" +#include "midictrl.h" +#include "instruments/minstrument.h" +#include "drummap.h" +#include "xml.h" +#include "audio.h" +#include "gconfig.h" + +//--------------------------------------------------------- +// importMidi +//--------------------------------------------------------- + +void MusE::importMidi() + { + QString empty(""); + importMidi(empty); + } + +void MusE::importMidi(const QString &file) + { + QString fn; + if (file.isEmpty()) { + fn = getOpenFileName(lastMidiPath, midi_file_pattern, this, + tr("MusE: Import Midi"), 0); + if (fn.isEmpty()) + return; + lastMidiPath = fn; + } + else + fn = file; + + int n = QMessageBox::question(this, appName, + tr("Add midi file to current project?\n"), + tr("&Add to Project"), + tr("&Replace"), + tr("&Abort"), 0, 2); + + switch (n) { + case 0: + importMidi(fn, true); + song->update(); + break; + case 1: + loadProjectFile(fn, false, false); // replace + break; + default: + return; + } + } + +//--------------------------------------------------------- +// importMidi +// return true on error +//--------------------------------------------------------- + +bool MusE::importMidi(const QString name, bool merge) + { + bool popenFlag; + FILE* fp = fileOpen(this, name, QString(".mid"), "r", popenFlag); + if (fp == 0) + return true; + MidiFile mf(fp); + bool rv = mf.read(); + popenFlag ? pclose(fp) : fclose(fp); + if (rv) { + QString s(tr("reading midifile\n ")); + s += name; + s += tr("\nfailed: "); + s += mf.error(); + QMessageBox::critical(this, QString("MusE"), s); + return rv; + } + // + // evaluate song Type (GM, XG, GS, unknown) + // + MType t = song->mtype(); + if (!merge) { + t = mf.mtype(); + song->setMType(t); + } + MidiInstrument* instr = 0; + for (iMidiInstrument i = midiInstruments.begin(); i != midiInstruments.end(); ++i) { + MidiInstrument* mi = *i; + if ((mi->iname() == "GM" && ((t == MT_UNKNOWN) || (t == MT_GM))) + || ((mi->iname() == "GS") && (t == MT_GS)) + || ((mi->iname() == "XG") && (t == MT_XG))) { + instr = mi; + break; + } + } + if (instr == 0) { + // the standard instrument files (GM, GS, XG) must be present + printf("no instrument, type %d\n", t); + abort(); + } + + MidiFileTrackList* etl = mf.trackList(); + int division = mf.division(); + + // + // create MidiTrack and copy events to ->events() + // - combine note on/off events + // - calculate tick value for internal resolution + // + for (iMidiFileTrack t = etl->begin(); t != etl->end(); ++t) { + MPEventList* el = &((*t)->events); + if (el->empty()) + continue; + // + // if we split the track, SYSEX and META events go into + // the first target track + + bool first = true; + // somewhat silly and slooow: + for (int port = 0; port < MIDI_PORTS; ++port) { + for (int channel = 0; channel < MIDI_CHANNELS; ++channel) { + // + // check if there are any events for port/channel in track: + // + iMPEvent i; + for (i = el->begin(); i != el->end(); ++i) { + MidiPlayEvent ev = *i; + if (ev.type() != ME_SYSEX && ev.type() != ME_META + && ev.channel() == channel && ev.port() == port) + break; + } + if (i == el->end()) + continue; + MidiTrack* track = new MidiTrack(); + if ((*t)->isDrumTrack) + { + track->setType(Track::DRUM); + } + + track->setOutChannel(channel); + track->setOutPort(port); + + MidiPort* mport = &midiPorts[track->outPort()]; + // this overwrites any instrument set for this port: + mport->setInstrument(instr); + + EventList* mel = track->events(); + //buildMidiEventList(mel, el, track, division, first); + // Don't do loops. + buildMidiEventList(mel, el, track, division, first, false); + first = false; + + // Removed by T356. Handled by addPortCtrlEvents() below. + //for (iEvent i = mel->begin(); i != mel->end(); ++i) { + // Event event = i->second; + // if (event.type() == Controller) { + // importController(channel, mport, event.dataA()); + // midiPorts[track->outPort()].setCtrl(channel, event.tick(), event.dataA(), event.dataB()); + // } + // } + + // Comment Added by T356. + // Hmm. buildMidiEventList already takes care of this. + // But it seems to work. How? Must test. + if (channel == 9 && song->mtype() != MT_UNKNOWN) { + track->setType(Track::DRUM); + // + // remap drum pitch with drumInmap + // + EventList* tevents = track->events(); + for (iEvent i = tevents->begin(); i != tevents->end(); ++i) { + Event ev = i->second; + if (ev.isNote()) { + int pitch = drumInmap[ev.pitch()]; + ev.setPitch(pitch); + } + else + if(ev.type() == Controller) + { + int ctl = ev.dataA(); + MidiController *mc = mport->drumController(ctl); + if(mc) + ev.setA((ctl & ~0xff) | drumInmap[ctl & 0x7f]); + } + } + } + + processTrack(track); + + // Added by T356. Send all controller values to the port's controller value list. + // No, done in song->insertTrack2() now. + //track->addPortCtrlEvents(); + + song->insertTrack0(track, -1); + } + } + if (first) { + // + // track does only contain non-channel messages + // (SYSEX or META) + // + MidiTrack* track = new MidiTrack(); + track->setOutChannel(0); + track->setOutPort(0); + EventList* mel = track->events(); + //buildMidiEventList(mel, el, track, division, true); + // Do SysexMeta. Don't do loops. + buildMidiEventList(mel, el, track, division, true, false); + processTrack(track); + song->insertTrack0(track, -1); + } + } + + if (!merge) { + TrackList* tl = song->tracks(); + if (!tl->empty()) { + Track* track = tl->front(); + track->setSelected(true); + } + song->initLen(); + + int z, n; + sigmap.timesig(0, z, n); + + int tempo = tempomap.tempo(0); + transport->setTimesig(z, n); + transport->setTempo(tempo); + + bool masterF = !tempomap.empty(); + song->setMasterFlag(masterF); + transport->setMasterFlag(masterF); + + song->updatePos(); + + arranger->reset(); + arranger->setMode(int(song->mtype())); + } + else { + song->initLen(); + } + + return false; + } + +//--------------------------------------------------------- +// processTrack +// divide events into parts +//--------------------------------------------------------- + +void MusE::processTrack(MidiTrack* track) + { + EventList* tevents = track->events(); + if (tevents->empty()) + return; + + //--------------------------------------------------- + // Parts ermitteln + // die Midi-Spuren werden in Parts aufgebrochen; + // ein neuer Part wird bei einer L�cke von einem + // Takt gebildet; die L�nge wird jeweils auf + // Takte aufgerundet und aligned + //--------------------------------------------------- + + PartList* pl = track->parts(); + + int lastTick = 0; + for (iEvent i = tevents->begin(); i != tevents->end(); ++i) { + Event event = i->second; + int epos = event.tick() + event.lenTick(); + if (epos > lastTick) + lastTick = epos; + } + + QString partname = track->name(); + int len = song->roundUpBar(lastTick+1); + + // p3.3.27 + if(config.importMidiSplitParts) + { + + int bar2, beat; + unsigned tick; + sigmap.tickValues(len, &bar2, &beat, &tick); + + int lastOff = 0; + int st = -1; // start tick current part + int x1 = 0; // start tick current measure + int x2 = 0; // end tick current measure + + for (int bar = 0; bar < bar2; ++bar, x1 = x2) { + x2 = sigmap.bar2tick(bar+1, 0, 0); + if (lastOff > x2) { + // this measure is busy! + continue; + } + iEvent i1 = tevents->lower_bound(x1); + iEvent i2 = tevents->lower_bound(x2); + + if (i1 == i2) { // empty? + if (st != -1) { + MidiPart* part = new MidiPart(track); + part->setTick(st); + part->setLenTick(x1-st); + // printf("new part %d len: %d\n", st, x1-st); + part->setName(partname); + pl->add(part); + st = -1; + } + } + else { + if (st == -1) + st = x1; // begin new part + //HACK: + //lastOff: + for (iEvent i = i1; i != i2; ++i) { + Event event = i->second; + if (event.type() == Note) { + int off = event.tick() + event.lenTick(); + if (off > lastOff) + lastOff = off; + } + } + } + } + if (st != -1) { + MidiPart* part = new MidiPart(track); + part->setTick(st); + // printf("new part %d len: %d\n", st, x2-st); + part->setLenTick(x2-st); + part->setName(partname); + pl->add(part); + } + } + else + { + // Just one long part... + MidiPart* part = new MidiPart(track); + //part->setTick(st); + part->setTick(0); + part->setLenTick(len); + part->setName(partname); + pl->add(part); + } + + //------------------------------------------------------------- + // assign events to parts + //------------------------------------------------------------- + + for (iPart p = pl->begin(); p != pl->end(); ++p) { + MidiPart* part = (MidiPart*)(p->second); + int stick = part->tick(); + int etick = part->tick() + part->lenTick(); + iEvent r1 = tevents->lower_bound(stick); + iEvent r2 = tevents->lower_bound(etick); + int startTick = part->tick(); + + EventList* el = part->events(); + for (iEvent i = r1; i != r2; ++i) { + Event ev = i->second; + int ntick = ev.tick() - startTick; + ev.setTick(ntick); + el->add(ev); + } + tevents->erase(r1, r2); + } + + if (tevents->size()) + printf("-----------events left: %zd\n", tevents->size()); + for (iEvent i = tevents->begin(); i != tevents->end(); ++i) { + printf("%d===\n", i->first); + i->second.dump(); + } + // all events should be processed: + assert(tevents->empty()); + } + +//--------------------------------------------------------- +// importController +//--------------------------------------------------------- + +void MusE::importController(int channel, MidiPort* mport, int n) + { + MidiInstrument* instr = mport->instrument(); + MidiCtrlValListList* vll = mport->controller(); + iMidiCtrlValList i = vll->find(channel, n); + if (i != vll->end()) + return; // controller does already exist + MidiController* ctrl = 0; + MidiControllerList* mcl = instr->controller(); +// printf("import Ctrl\n"); + for (iMidiController i = mcl->begin(); i != mcl->end(); ++i) { + MidiController* mc = i->second; + int cn = mc->num(); +// printf(" %x %x\n", n, cn); + if (cn == n) { + ctrl = mc; + break; + } + // wildcard? + if (((cn & 0xff) == 0xff) && ((cn & ~0xff) == (n & ~0xff))) { + ctrl = i->second; + break; + } + } + if (ctrl == 0) { + printf("controller 0x%x not defined for instrument %s, channel %d\n", + n, instr->iname().latin1(), channel); +// TODO: register default Controller +// MidiController* MidiPort::midiController(int num) const + } + MidiCtrlValList* newValList = new MidiCtrlValList(n); + vll->add(channel, newValList); + } + + +//--------------------------------------------------------- +// importPart +//--------------------------------------------------------- +void MusE::importPart() + { + unsigned curPos = song->cpos(); + TrackList* tracks = song->tracks(); + Track* track = 0; + // Get first selected track: + for (iTrack i = tracks->begin(); i != tracks->end(); i++) { + Track* t = *i; + if (t->selected()) { + // Changed by T356. Support mixed .mpt files. + //if (t->isMidiTrack()) { + if (t->isMidiTrack() || t->type() == Track::WAVE) { + track = t; + break; + } + else { + //QMessageBox::warning(this, QString("MusE"), tr("Import part is only valid for midi tracks!")); + QMessageBox::warning(this, QString("MusE"), tr("Import part is only valid for midi and wave tracks!")); + return; + } + } + } + + if (track) { + bool loadAll; + QString filename = getOpenFileName(QString(""), part_file_pattern, this, tr("MusE: load part"), &loadAll); + if (!filename.isEmpty()){ + // Make a backup of the current clone list, to retain any 'copy' items, + // so that pasting works properly after. + CloneList copyCloneList = cloneList; + // Clear the clone list to prevent any dangerous associations with + // current non-original parts. + cloneList.clear(); + + importPartToTrack(filename, curPos, track); + + // Restore backup of the clone list, to retain any 'copy' items, + // so that pasting works properly after. + cloneList.clear(); + cloneList = copyCloneList; + } + } + else { + QMessageBox::warning(this, QString("MusE"), tr("No track selected for import")); + } + } + +//--------------------------------------------------------- +// importPartToTrack +//--------------------------------------------------------- +void MusE::importPartToTrack(QString& filename, unsigned tick, Track* track) +{ + // Changed by T356 + /* + bool popenFlag = false; + FILE* fp = fileOpen(this, filename, ".mpt", "r", popenFlag, false, false); + + if(fp) + { + MidiPart* importedPart = new MidiPart((MidiTrack*)track); + Xml tmpXml = Xml(fp); + + Xml::Token token = tmpXml.parse(); + const QString& tag = tmpXml.s1(); + if (token == Xml::TagStart && tag == "part") + { + // Make a backup of the current clone list, to retain any 'copy' items, + // so that pasting works properly after. + CloneList copyCloneList = cloneList; + // Clear the clone list to prevent any dangerous associations with + // current non-original parts. + cloneList.clear(); + + importedPart->read(tmpXml); + importedPart->setTick(tick); + + // Restore backup of the clone list, to retain any 'copy' items, + // so that pasting works properly after. + cloneList.clear(); + cloneList = copyCloneList; + + audio->msgAddPart(importedPart); + } + else + { + printf("Unknown tag: %s\n", tag.latin1()); + } + fclose(fp); + } + return; + */ + + + bool popenFlag = false; + FILE* fp = fileOpen(this, filename, ".mpt", "r", popenFlag, false, false); + + if(fp) + { + Xml xml = Xml(fp); + bool firstPart = true; + int posOffset = 0; + int notDone = 0; + int done = 0; + + bool end = false; + song->startUndo(); + for (;;) + { + Xml::Token token = xml.parse(); + const QString& tag = xml.s1(); + switch (token) + { + case Xml::Error: + case Xml::End: + end = true; + break; + case Xml::TagStart: + if (tag == "part") { + //MidiPart* p = new MidiPart((MidiTrack*)track); + //p->read(xml); + + // Read the part. + Part* p = 0; + p = readXmlPart(xml, track); + // If it could not be created... + if(!p) + { + // Increment the number of parts not done and break. + ++notDone; + break; + } + + // Increment the number of parts done. + ++done; + + if(firstPart) + { + firstPart=false; + posOffset = tick - p->tick(); + } + p->setTick(p->tick() + posOffset); + //finalPos=p->tick() + p->lenTick(); + ////pos += p->lenTick(); + audio->msgAddPart(p,false); + } + else + xml.unknown("MusE::importPartToTrack"); + break; + case Xml::TagEnd: + break; + default: + end = true; + break; + } + if(end) + break; + } + fclose(fp); + song->endUndo(SC_PART_INSERTED); + + if(notDone) + { + int tot = notDone + done; + QMessageBox::critical(this, QString("MusE"), + QString().setNum(notDone) + (tot > 1 ? (tr(" out of ") + QString().setNum(tot)) : QString("")) + + (tot > 1 ? tr(" parts") : tr(" part")) + + tr(" could not be imported.\nLikely the track is the wrong type.")); + } + + return; + } +} |