summaryrefslogtreecommitdiff
path: root/muse_qt4_evolution/muse/miditrack.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'muse_qt4_evolution/muse/miditrack.cpp')
-rw-r--r--muse_qt4_evolution/muse/miditrack.cpp716
1 files changed, 716 insertions, 0 deletions
diff --git a/muse_qt4_evolution/muse/miditrack.cpp b/muse_qt4_evolution/muse/miditrack.cpp
new file mode 100644
index 00000000..276eeccb
--- /dev/null
+++ b/muse_qt4_evolution/muse/miditrack.cpp
@@ -0,0 +1,716 @@
+//=============================================================================
+// 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 "miditrack.h"
+#include "event.h"
+#include "song.h"
+#include "midi.h"
+#include "midictrl.h"
+#include "audio.h"
+#include "part.h"
+#include "al/tempo.h"
+#include "midiedit/drummap.h"
+
+//---------------------------------------------------------
+// MidiTrack
+//---------------------------------------------------------
+
+MidiTrack::MidiTrack()
+ : MidiTrackBase()
+ {
+ _transposition = 0;
+ _velocity = 0;
+ _delay = 0;
+ _len = 100; // percent
+ _compression = 100; // percent
+
+ initMidiController();
+ recordPart = 0;
+ _drumMap = 0;
+ _useDrumMap = false;
+
+ //
+ // create minimal set of managed controllers
+ // to make midi mixer operational
+ //
+ MidiInstrument* mi = genericMidiInstrument;
+ addMidiController(mi, CTRL_PROGRAM);
+ addMidiController(mi, CTRL_VOLUME);
+ addMidiController(mi, CTRL_PANPOT);
+ addMidiController(mi, CTRL_REVERB_SEND);
+ addMidiController(mi, CTRL_CHORUS_SEND);
+ addMidiController(mi, CTRL_VARIATION_SEND);
+ }
+
+MidiTrack::~MidiTrack()
+ {
+ }
+
+//---------------------------------------------------------
+// newPart
+//---------------------------------------------------------
+
+Part* MidiTrack::newPart(Part* p, bool clone)
+ {
+ Part* part = new Part(this);
+ if (p) {
+ if (clone)
+ part->clone(p->events());
+ else
+ part->ref();
+ part->setName(p->name());
+ part->setColorIndex(p->colorIndex());
+
+ *(AL::PosLen*)part = *(AL::PosLen*)p;
+ part->setMute(p->mute());
+ }
+ else
+ part->ref();
+ return part;
+ }
+
+//---------------------------------------------------------
+// MidiTrack::write
+//---------------------------------------------------------
+
+void MidiTrack::write(Xml& xml) const
+ {
+ xml.stag("miditrack");
+ MidiTrackBase::writeProperties(xml);
+
+ xml.tag("transposition", _transposition);
+ xml.tag("velocity", _velocity);
+ xml.tag("delay", _delay);
+ xml.tag("len", _len);
+ xml.tag("compression", _compression);
+ xml.tag("useDrumMap", _useDrumMap);
+
+ const PartList* pl = parts();
+ for (ciPart p = pl->begin(); p != pl->end(); ++p)
+ p->second->write(xml);
+ xml.etag("miditrack");
+ }
+
+//---------------------------------------------------------
+// MidiTrack::read
+//---------------------------------------------------------
+
+void MidiTrack::read(QDomNode node)
+ {
+ for (; !node.isNull(); node = node.nextSibling()) {
+ QDomElement e = node.toElement();
+ QString tag(e.tagName());
+ QString s(e.text());
+ int i = s.toInt();
+ if (tag == "transposition")
+ _transposition = i;
+ else if (tag == "velocity")
+ _velocity = i;
+ else if (tag == "delay")
+ _delay = i;
+ else if (tag == "len")
+ _len = i;
+ else if (tag == "compression")
+ _compression = i;
+ else if (tag == "part") {
+ Part* p = newPart();
+ p->read(node, true);
+ parts()->add(p);
+ }
+ else if (tag == "locked")
+ _locked = i;
+ else if (tag == "useDrumMap")
+ _useDrumMap = e.text().toInt();
+ else if (MidiTrackBase::readProperties(node))
+ printf("MusE:MidiTrack: unknown tag %s\n", e.tagName().toLatin1().data());
+ }
+ }
+
+//---------------------------------------------------------
+// playMidiEvent
+//---------------------------------------------------------
+
+void MidiTrack::playMidiEvent(MidiEvent* ev)
+ {
+ foreach (const Route& r, _outRoutes) {
+ Track* track = r.dst.track;
+ ev->setChannel(r.dst.channel);
+ if (track->type() == MIDI_OUT)
+ ((MidiOutPort*)track)->playMidiEvent(ev);
+ else if (track->type() == AUDIO_SOFTSYNTH)
+ ((SynthI*)track)->playMidiEvent(ev);
+ }
+ }
+
+//---------------------------------------------------------
+// startRecording
+// gui context
+//---------------------------------------------------------
+
+void MidiTrack::startRecording()
+ {
+ hbank = 0;
+ lbank = 0;
+ datah = 0;
+ datal = 0;
+ rpnh = 0;
+ rpnl = 0;
+ dataType = 0;
+ recordedEvents = 0;
+ partCreated = false;
+ recordPart = 0;
+ recordFifo.clear();
+ keyDown.clear();
+
+ AL::Pos start = song->punchin() ? song->lPos() : song->cPos();
+
+ for (iPart ip = parts()->begin(); ip != parts()->end(); ++ip) {
+ Part* part = ip->second;
+ unsigned partStart = part->tick();
+ unsigned partEnd = partStart + part->lenTick();
+ if (start.tick() >= partStart && start.tick() < partEnd) {
+ recordPart = part;
+ }
+ }
+ if (recordPart == 0) {
+ //
+ // create new part for recording
+ //
+ recordPart = new Part(this);
+ recordPart->ref();
+ recordPart->setTrack(this);
+ int startTick = song->roundDownBar(start.tick());
+ int endTick = song->roundUpBar(start.tick());
+ recordPart->setTick(startTick);
+ recordPart->setLenTick(endTick - startTick);
+ recordPart->setName(name());
+ song->addPart(recordPart);
+ partCreated = true;
+ }
+ }
+
+//---------------------------------------------------------
+// recordBeat
+// gui context
+// update current recording
+//---------------------------------------------------------
+
+void MidiTrack::recordBeat()
+ {
+ int updateFlags = 0;
+ unsigned cpos = song->cpos();
+ unsigned ptick = recordPart->tick();
+
+ if (song->punchout()) {
+ if (song->rPos() >= song->cPos()) {
+ while (!recordFifo.isEmpty())
+ recordFifo.get();
+ return;
+ }
+ }
+ QList<Event> el;
+ while (!recordFifo.isEmpty()) {
+ MidiEvent me(recordFifo.get());
+
+ unsigned time = me.time();
+ if (song->punchin() && time < song->lpos())
+ continue;
+ bool isOff = me.isNoteOff();
+
+ if (song->punchout() && (time >= song->rpos()) && !isOff)
+ continue;
+
+ if (!partCreated && song->recMode() == Song::REC_REPLACE) {
+ // TODO: remove old events
+ }
+
+ time -= ptick;
+ if (isOff) {
+ //
+ // process note off
+ //
+ for (std::list<Event>::iterator i = keyDown.begin(); i != keyDown.end(); ++i) {
+ if (i->pitch() == me.dataA()) {
+ unsigned tl = time - i->tick();
+ if (tl != i->lenTick()) {
+ i->setLenTick(tl);
+ updateFlags |= SC_EVENT_MODIFIED;
+ }
+ keyDown.erase(i);
+ break;
+ }
+ }
+ }
+ else if (me.type() == ME_NOTEON && me.dataB() != 0) {
+ //
+ // create Note event on "note on"
+ //
+ Event event(Note);
+ event.setTick(time);
+ event.setLenTick(1);
+ event.setPitch(me.dataA());
+ event.setVelo(me.dataB());
+ keyDown.push_front(event);
+ el.append(event);
+ }
+ else if (me.type() == ME_POLYAFTER) {
+ Event event(PAfter);
+ event.setTick(time);
+ event.setA(me.dataA());
+ event.setB(me.dataB());
+ }
+ else if (me.type() == ME_CONTROLLER) {
+ Event event(Controller);
+ event.setTick(time + ptick);
+ switch(me.dataA()) {
+ case CTRL_HBANK:
+ hbank = me.dataB();
+ break;
+
+ case CTRL_LBANK:
+ lbank = me.dataB();
+ break;
+
+ case CTRL_HDATA:
+ datah = me.dataB();
+ event.setA(dataType | (rpnh << 8) | rpnl);
+ event.setB(datah);
+ el.append(event);
+ break;
+
+ case CTRL_LDATA:
+ datal = me.dataB();
+ if (dataType == CTRL_NRPN_OFFSET)
+ dataType = CTRL_NRPN14_OFFSET;
+ else if (dataType == CTRL_RPN_OFFSET)
+ dataType = CTRL_RPN14_OFFSET;
+ break;
+
+ case CTRL_HNRPN:
+ rpnh = me.dataB();
+ dataType = CTRL_NRPN_OFFSET;
+ break;
+
+ case CTRL_LNRPN:
+ rpnl = me.dataB();
+ dataType = CTRL_NRPN_OFFSET;
+ break;
+
+ case CTRL_HRPN:
+ rpnh = me.dataB();
+ dataType = CTRL_RPN_OFFSET;
+ break;
+
+ case CTRL_LRPN:
+ rpnl = me.dataB();
+ dataType = CTRL_RPN_OFFSET;
+ break;
+
+ default:
+ event.setA(me.dataA());
+ event.setB(me.dataB());
+ el.append(event);
+ break;
+ }
+ }
+ else if (me.type() == ME_PROGRAM) {
+ Event event(Controller);
+ event.setTick(time + ptick);
+ event.setA(CTRL_PROGRAM);
+ event.setB((hbank << 16) | (lbank << 8) | me.dataA());
+ el.append(event);
+ }
+ else if (me.type() == ME_PITCHBEND) {
+ Event event(Controller);
+ event.setTick(time + ptick);
+ event.setA(CTRL_PITCH);
+ event.setB(me.dataA());
+ el.append(event);
+ }
+ else if (me.type() == ME_SYSEX) {
+ Event event(Sysex);
+ event.setTick(time + ptick);
+ event.setData(me.data(), me.len());
+ el.append(event);
+ }
+ else if (me.type() == ME_AFTERTOUCH) {
+ Event event(CAfter);
+ event.setTick(time + ptick);
+ event.setA(me.dataA());
+ el.append(event);
+ }
+ }
+ if (!el.isEmpty()) {
+ for (int i = 0; i < el.size(); ++i)
+ el[i].setRecorded(true);
+ audio->msgAddEvents(&el, recordPart);
+ recordedEvents += el.size();
+ updateFlags |= SC_EVENT_INSERTED;
+ }
+
+ if (partCreated) {
+ recordPart->setLenTick(cpos - ptick);
+ updateFlags |= SC_PART_MODIFIED;
+ }
+ //
+ // modify len of all hold keys
+ //
+ for (std::list<Event>::iterator i = keyDown.begin(); i != keyDown.end(); ++i) {
+ if (cpos > (i->tick() + ptick))
+ i->setLenTick(cpos - (i->tick() + ptick));
+ updateFlags |= SC_EVENT_MODIFIED;
+ }
+ song->update(updateFlags);
+ }
+
+//---------------------------------------------------------
+// stopRecording
+// gui context
+//---------------------------------------------------------
+
+void MidiTrack::stopRecording()
+ {
+ for (iEvent e = recordPart->events()->begin(); e != recordPart->events()->end(); ++e) {
+ e->second.setRecorded(false);
+ }
+ if (recordedEvents == 0 && partCreated) {
+ // TD: remove empty part?
+ }
+ //
+ // modify len of all hold keys
+ //
+ unsigned ptick = recordPart->tick();
+ unsigned cpos = song->cpos();
+ for (std::list<Event>::iterator i = keyDown.begin(); i != keyDown.end(); ++i) {
+ i->setLenTick(cpos - (i->tick() + ptick));
+ }
+ //
+ // adjust part len && song len
+ //
+ if (recordPart->lenTick() < (cpos-ptick)) {
+ //
+ // TODO: check for events outside part boundaries
+ //
+ int endTick = song->roundUpBar(cpos);
+ recordPart->setLenTick(endTick - ptick);
+ }
+
+ unsigned etick = recordPart->endTick();
+ if (song->len() < etick)
+ song->setLen(etick);
+ }
+
+//---------------------------------------------------------
+// clone
+//---------------------------------------------------------
+
+void MidiTrack::clone(MidiTrack* t)
+ {
+ QString name;
+ for (int i = 1; ; ++i) {
+ name.sprintf("%s-%d", t->name().toLatin1().data(), i);
+ TrackList* tl = song->tracks();
+ bool found = false;
+ for (iTrack it = tl->begin(); it != tl->end(); ++it) {
+ if ((*it)->name() == name) {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ break;
+ }
+ setName(name);
+ _transposition = t->_transposition;
+ _velocity = t->_velocity;
+ _delay = t->_delay;
+ _len = t->_len;
+ _compression = t->_compression;
+ _recordFlag = t->_recordFlag;
+ _mute = t->_mute;
+ _solo = t->_solo;
+ _off = t->_off;
+ _monitor = t->_monitor;
+ _channels = t->_channels;
+ _locked = t->_locked;
+ _inRoutes = t->_inRoutes;
+ _outRoutes = t->_outRoutes;
+ _controller = t->_controller;
+ _autoRead = t->_autoRead;
+ _autoWrite = t->_autoWrite;
+ }
+
+//---------------------------------------------------------
+// isMute
+//---------------------------------------------------------
+
+bool MidiTrack::isMute() const
+ {
+ if (_solo)
+ return false;
+ if (song->solo())
+ return true;
+ return _mute;
+ }
+
+//---------------------------------------------------------
+// processMidi
+//---------------------------------------------------------
+
+void MidiTrack::processMidi(SeqTime* t)
+ {
+ schedEvents.clear();
+ //
+ // collect events only when transport is rolling
+ //
+ if (t->curTickPos < t->nextTickPos) {
+ for (iPart p = parts()->begin(); p != parts()->end(); ++p) {
+ Part* part = p->second;
+ if (part->mute())
+ continue;
+ DrumMap* dm = drumMap();
+ unsigned offset = _delay + part->tick();
+
+ if (offset > t->nextTickPos)
+ break;
+
+ EventList* events = part->events();
+
+ iEvent ie = events->lower_bound((offset > t->curTickPos) ? 0 : t->curTickPos - offset);
+ iEvent iend = events->lower_bound(t->nextTickPos - offset);
+
+ for (; ie != iend; ++ie) {
+ Event ev = ie->second;
+ if (ev.recorded())
+ continue;
+ if (ev.type() == Meta) // ignore meta events
+ continue;
+ unsigned tick = ev.tick() + offset;
+ unsigned frame = t->tick2frame(tick);
+ if (ev.type() == Note) {
+ if (dm) {
+ if (dm->entry(dm->outmap(ev.pitch()))->mute)
+ continue;
+ }
+ //
+ // maybe we should skip next lines if using a
+ // drummap
+
+ int pitch = ev.pitch() + _transposition + song->globalPitchShift();
+ if (pitch > 127)
+ pitch = 127;
+ if (pitch < 0)
+ pitch = 0;
+ int velo = ev.velo();
+ velo += _velocity;
+ velo = (velo * _compression) / 100;
+ if (velo > 127)
+ velo = 127;
+ if (velo < 1) // no off event
+ velo = 1;
+ int elen = (ev.lenTick() * _len)/100;
+ if (elen <= 0) // dont allow zero length
+ elen = 1;
+ int veloOff = ev.veloOff();
+
+ unsigned eframe = t->tick2frame(tick+elen);
+ schedEvents.insert(MidiEvent(frame, 0, ME_NOTEON, pitch, velo));
+ schedEvents.insert(MidiEvent(eframe, 0, veloOff ? ME_NOTEOFF : ME_NOTEON, pitch, veloOff));
+ _meter[0] += velo/2;
+ if (_meter[0] > 127.0f)
+ _meter[0] = 127.0f;
+ }
+ else {
+ schedEvents.insert(MidiEvent(frame, 0, ev));
+ }
+ }
+ }
+ //
+ // collect controller
+ //
+ if (autoRead()) {
+ for (iCtrl ic = controller()->begin(); ic != controller()->end(); ++ic) {
+ Ctrl* c = ic->second;
+ iCtrlVal is = c->lowerBound(t->curTickPos);
+ iCtrlVal ie = c->lowerBound(t->nextTickPos);
+ for (iCtrlVal ic = is; ic != ie; ++ic) {
+ unsigned frame = t->tick2frame(ic.key());
+ Event ev(Controller);
+ ev.setA(c->id());
+ ev.setB(ic.value().i);
+ schedEvents.insert(MidiEvent(frame, -1, ev));
+ c->setCurVal(ic.value().i);
+ }
+ }
+ }
+ }
+
+ //
+ // process input routing
+ //
+
+ foreach(const Route& r, *inRoutes()) {
+ MidiTrackBase* track = (MidiTrackBase*)r.src.track;
+ if (track->isMute())
+ continue;
+ MidiEventList el;
+ track->getEvents(t->curTickPos, t->nextTickPos, r.src.channel, &el);
+
+ for (iMidiEvent ie = el.begin(); ie != el.end(); ++ie) {
+ MidiEvent event(*ie);
+ unsigned eventTime = event.time();
+ if (recordFlag() && audio->isRecording()) {
+ unsigned time = t->frame2tick(eventTime);
+ event.setTime(time); // set tick time
+ recordFifo.put(event);
+ }
+ if (event.type() == ME_NOTEON && (monitor() || recordFlag()))
+ addMidiMeter(event.dataB());
+ if (monitor()) {
+ if (event.type() == ME_NOTEON) {
+ int pitch = event.dataA() + _transposition + song->globalPitchShift();
+ if (pitch > 127)
+ pitch = 127;
+ if (pitch < 0)
+ pitch = 0;
+ event.setA(pitch);
+ if (!event.isNoteOff()) {
+ int velo = event.dataB() + _velocity;
+ velo = (velo * _compression) / 100;
+ if (velo > 127)
+ velo = 127;
+ if (velo < 1)
+ velo = 1;
+ event.setB(velo);
+ }
+ }
+ event.setTime(eventTime + segmentSize);
+ schedEvents.insert(event);
+ }
+ }
+ }
+ }
+
+//---------------------------------------------------------
+// getEvents
+// from/to - midi ticks
+//---------------------------------------------------------
+
+void MidiTrack::getEvents(unsigned /*from*/, unsigned /*to*/, int, MidiEventList* dst)
+ {
+ for (iMidiEvent i = schedEvents.begin(); i != schedEvents.end(); ++i) {
+ dst->insert(*i);
+ }
+ }
+
+//---------------------------------------------------------
+// emitControllerChanged
+//---------------------------------------------------------
+
+void MidiTrack::emitControllerChanged(int id)
+ {
+ if (id == CTRL_PROGRAM && _useDrumMap) {
+ int val = ctrlVal(id).i;
+ MidiInstrument* mi = instrument();
+ DrumMap* dm = mi->getDrumMap(val);
+ if (dm == 0)
+ dm = &gmDrumMap;
+ if (dm != _drumMap)
+ _drumMap = dm;
+ emit drumMapChanged();
+ }
+ emit controllerChanged(id);
+ }
+
+//---------------------------------------------------------
+// setUseDrumMap
+//---------------------------------------------------------
+
+void MidiTrack::setUseDrumMap(bool val)
+ {
+ if (_useDrumMap != val) {
+ _useDrumMap = val;
+ if (_useDrumMap) {
+ MidiInstrument* mi = instrument();
+ DrumMap* dm;
+ if (mi) {
+ int val = ctrlVal(CTRL_PROGRAM).i;
+ dm = mi->getDrumMap(val);
+ if (dm == 0)
+ dm = &gmDrumMap;
+ }
+ _drumMap = dm;
+ }
+ else
+ _drumMap = &noDrumMap;
+ emit drumMapChanged();
+ emit useDrumMapChanged(_useDrumMap);
+ }
+ }
+
+//---------------------------------------------------------
+// instrument
+//---------------------------------------------------------
+
+MidiInstrument* MidiTrack::instrument()
+ {
+ if (_outRoutes.isEmpty())
+ return genericMidiInstrument;
+ return _outRoutes[0].dst.track->instrument();
+ }
+
+//---------------------------------------------------------
+// channelNo
+//---------------------------------------------------------
+
+int MidiTrack::channelNo() const
+ {
+ if (_outRoutes.isEmpty()) // TODO: better: remember old channel setting
+ return 0;
+ return _outRoutes[0].dst.channel;
+ }
+
+//---------------------------------------------------------
+// midiOut
+//---------------------------------------------------------
+
+MidiOut* MidiTrack::midiOut()
+ {
+ if (_outRoutes.isEmpty())
+ return 0;
+ return _outRoutes[0].dst.track->midiOut();
+ }
+
+//---------------------------------------------------------
+// setChannel
+//---------------------------------------------------------
+
+void MidiTrack::setChannel(int n)
+ {
+ if (_outRoutes.isEmpty())
+ return;
+ Route r = _outRoutes[0];
+ if (r.dst.channel == n)
+ return;
+ audio->msgRemoveRoute(r);
+ r.dst.channel = n;
+ audio->msgAddRoute(r);
+ emit channelChanged(n);
+ }