summaryrefslogtreecommitdiff
path: root/muse_qt4_evolution/muse/song.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'muse_qt4_evolution/muse/song.cpp')
-rw-r--r--muse_qt4_evolution/muse/song.cpp1552
1 files changed, 1552 insertions, 0 deletions
diff --git a/muse_qt4_evolution/muse/song.cpp b/muse_qt4_evolution/muse/song.cpp
new file mode 100644
index 00000000..a1f3bff0
--- /dev/null
+++ b/muse_qt4_evolution/muse/song.cpp
@@ -0,0 +1,1552 @@
+//=============================================================================
+// 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 "muse.h"
+#include "song.h"
+#include "track.h"
+#include "undo.h"
+#include "globals.h"
+#include "event.h"
+#include "midiedit/drummap.h"
+#include "audio.h"
+#include "mixer.h"
+#include "audiodev.h"
+#include "gconfig.h"
+#include "al/marker.h"
+#include "al/sig.h"
+#include "al/tempo.h"
+#include "midi.h"
+#include "plugin.h"
+#include "pipeline.h"
+#include "synth.h"
+#include "midiplugin.h"
+#include "midirc.h"
+#include "part.h"
+#include "conf.h"
+#include "midioutport.h"
+#include "midiinport.h"
+#include "instruments/minstrument.h"
+
+Song* song;
+
+//---------------------------------------------------------
+// Song
+//---------------------------------------------------------
+
+Song::Song()
+ :QObject(0)
+ {
+ undoList = new UndoList;
+ redoList = new UndoList;
+ _markerList = new AL::MarkerList;
+ _globalPitchShift = 0;
+ clear(false);
+ }
+
+//---------------------------------------------------------
+// Song
+//---------------------------------------------------------
+
+Song::~Song()
+ {
+ delete undoList;
+ delete redoList;
+ delete _markerList;
+// delete esettingsList;
+ }
+
+//---------------------------------------------------------
+// putEvent
+//---------------------------------------------------------
+
+void Song::putEvent(const MidiEvent& event)
+ {
+ eventFifo.put(event);
+ }
+
+//---------------------------------------------------------
+// setTempo
+// public slot
+//---------------------------------------------------------
+
+void Song::setTempo(int newTempo)
+ {
+ audio->msgSetTempo(pos[0].tick(), newTempo, true);
+ }
+
+//---------------------------------------------------------
+// setSig
+// called from transport window
+//---------------------------------------------------------
+
+void Song::setSig(const AL::TimeSignature& sig)
+ {
+ if (_masterFlag) {
+ audio->msgAddSig(pos[0].tick(), sig);
+ }
+ }
+
+//---------------------------------------------------------
+// addEvent
+// return true if event was added
+//---------------------------------------------------------
+
+bool Song::addEvent(const Event& event, Part* part)
+ {
+ if (event.type() == Controller) {
+ MidiTrack* track = (MidiTrack*)part->track();
+ int tick = event.tick() + part->tick();
+ int cntrl = event.dataA();
+ CVal val;
+ val.i = event.dataB();
+ if (!track->addControllerVal(cntrl, tick, val)) {
+ track->addMidiController(track->instrument(), cntrl);
+ if (!track->addControllerVal(cntrl, tick, val)) {
+ return false;
+ }
+ }
+ }
+ else {
+ part->events()->add(event);
+ }
+ return true;
+ }
+
+//---------------------------------------------------------
+// changeEvent
+//---------------------------------------------------------
+
+void Song::changeEvent(const Event& oldEvent, const Event& newEvent, Part* part)
+ {
+ iEvent i = part->events()->find(oldEvent);
+ if (i == part->events()->end()) {
+ printf("Song::changeEvent(): EVENT not found !! %ld\n", long(part->events()->size()));
+ // abort();
+ return;
+ }
+ part->events()->erase(i);
+ part->events()->add(newEvent);
+
+ if (newEvent.type() == Controller) {
+ MidiTrack* track = (MidiTrack*)part->track();
+ int tick = newEvent.tick() + part->tick();
+ int cntrl = newEvent.dataA();
+ CVal val;
+ val.i = newEvent.dataB();
+ track->addControllerVal(cntrl, tick, val);
+ }
+ }
+
+//---------------------------------------------------------
+// deleteEvent
+//---------------------------------------------------------
+
+void Song::deleteEvent(const Event& event, Part* part)
+ {
+#if 0 //TODO3
+ if (event.type() == Controller) {
+ MidiTrack* track = (MidiTrack*)part->track();
+ int ch = track->outChannel();
+ int tick = event.tick() + part->tick();
+ int cntrl = event.dataA();
+ midiPorts[track->outPort()].deleteController(ch, tick, cntrl);
+ }
+#endif
+ iEvent ev = part->events()->find(event);
+ if (ev == part->events()->end()) {
+ printf("event not found in part\n");
+ return;
+ }
+ part->events()->erase(ev);
+ }
+
+//---------------------------------------------------------
+// setLoop
+// set transport loop flag
+//---------------------------------------------------------
+
+void Song::setLoop(bool f)
+ {
+ if (loopFlag != f) {
+ loopFlag = f;
+ loopAction->setChecked(loopFlag);
+ emit loopChanged(loopFlag);
+ }
+ }
+
+//---------------------------------------------------------
+// setRecord
+// set record flag
+//---------------------------------------------------------
+
+void Song::setRecord(bool f)
+ {
+ if (recordFlag == f)
+ return;
+ if (muse->playAction->isChecked()) {
+ //
+ // dont allow record state changes when rolling
+ //
+ recordAction->setChecked(!f);
+ return;
+ }
+
+ if (f) {
+ bool alreadyRecEnabled = false;
+ Track *selectedTrack = 0;
+
+ // loop through list and check if any track is rec enabled
+ // if not then rec enable the selected track
+
+ WaveTrackList* wtl = waves();
+ for (iWaveTrack i = wtl->begin(); i != wtl->end(); ++i) {
+ if ((*i)->recordFlag()) {
+ alreadyRecEnabled = true;
+ break;
+ }
+ if ((*i)->selected())
+ selectedTrack = (*i);
+ }
+ if (!alreadyRecEnabled) {
+ MidiTrackList* mtl = midis();
+ for (iMidiTrack it = mtl->begin(); it != mtl->end(); ++it) {
+ if ((*it)->recordFlag()) {
+ alreadyRecEnabled = true;
+ break;
+ }
+ if ((*it)->selected())
+ selectedTrack = (*it);
+ }
+ }
+ if (!alreadyRecEnabled && selectedTrack) {
+ // enable recording on selected track:
+ setRecordFlag(selectedTrack, true);
+ }
+ else {
+ if (!alreadyRecEnabled) {
+ // If there are no tracks, do not enable record.
+ // TODO: This forces the user to first enable record on a track
+ // which probably is a bad thing. Maybe we should warn
+ // only when the user actually starts recording by pressing
+ // play.
+
+ QMessageBox::critical(0, "MusE: Record",
+ "No track(s) enabled for recording");
+ f = false;
+ }
+ }
+ }
+ if (!f)
+ bounceTrack = 0;
+ recordAction->setChecked(f);
+ if (f != recordFlag) {
+ recordFlag = f;
+ emit recordChanged(recordFlag);
+ }
+ }
+
+//---------------------------------------------------------
+// setPunchin
+// set punchin flag
+//---------------------------------------------------------
+
+void Song::setPunchin(bool f)
+ {
+ if (punchinFlag != f) {
+ punchinFlag = f;
+ punchinAction->setChecked(punchinFlag);
+ emit punchinChanged(punchinFlag);
+ }
+ }
+
+//---------------------------------------------------------
+// setPunchout
+// set punchout flag
+//---------------------------------------------------------
+
+void Song::setPunchout(bool f)
+ {
+ if (punchoutFlag != f) {
+ punchoutFlag = f;
+ punchoutAction->setChecked(punchoutFlag);
+ emit punchoutChanged(punchoutFlag);
+ }
+ }
+
+//---------------------------------------------------------
+// setClick
+//---------------------------------------------------------
+
+void Song::setClick(bool val)
+ {
+ if (_click != val) {
+ _click = val;
+ emit clickChanged(_click);
+ }
+ }
+
+//---------------------------------------------------------
+// setQuantize
+//---------------------------------------------------------
+
+void Song::setQuantize(bool val)
+ {
+ if (_quantize != val) {
+ _quantize = val;
+ emit quantizeChanged(_quantize);
+ }
+ }
+
+//---------------------------------------------------------
+// setMasterFlag
+//---------------------------------------------------------
+
+void Song::setMasterFlag(bool val)
+ {
+ _masterFlag = val;
+ if (AL::tempomap.setMasterFlag(cpos(), val)) {
+ emit songChanged(SC_MASTER);
+ emit tempoChanged();
+ }
+ }
+
+//---------------------------------------------------------
+// setPlay
+// set transport play flag
+//---------------------------------------------------------
+
+void Song::setPlay(bool f)
+ {
+ // only allow the user to set the button "on"
+ if (!f) {
+ printf(" setPlay checked\n");
+ muse->playAction->setChecked(true);
+ }
+ else {
+ if (recordAction->isChecked()) {
+ startUndo();
+ MidiTrackList* ml = midis();
+ for (iMidiTrack it = ml->begin(); it != ml->end(); ++it) {
+ if ((*it)->recordFlag())
+ (*it)->startRecording();
+ }
+ WaveTrackList* wl = waves();
+ for (iWaveTrack wt = wl->begin(); wt != wl->end(); ++wt) {
+ if ((*wt)->recordFlag())
+ (*wt)->startRecording();
+ }
+ OutputList* ol = outputs();
+ for (iAudioOutput o = ol->begin(); o != ol->end(); ++o) {
+ if ((*o)->recordFlag())
+ (*o)->startRecording();
+ }
+ }
+ audio->msgPlay(true);
+ }
+ }
+
+//---------------------------------------------------------
+// setStop
+//---------------------------------------------------------
+
+void Song::setStop(bool f)
+ {
+ // only allow the user to set the button "on"
+ if (f)
+ audio->msgPlay(false);
+ else
+ muse->stopAction->setChecked(true);
+ }
+
+//---------------------------------------------------------
+// setStopPlay
+//---------------------------------------------------------
+
+void Song::setStopPlay(bool f)
+ {
+ emit playChanged(f); // signal transport window
+ muse->playAction->setChecked(f);
+ muse->stopAction->setChecked(!f);
+ }
+
+//---------------------------------------------------------
+// swapTracks
+//---------------------------------------------------------
+
+void Song::swapTracks(int i1, int i2)
+ {
+ undoOp(UndoOp::SwapTrack, i1, i2);
+ Track* track = _tracks[i1];
+ _tracks[i1] = _tracks[i2];
+ _tracks[i2] = track;
+ }
+
+//---------------------------------------------------------
+// setTickPos
+//---------------------------------------------------------
+/*
+void Song::setTickPos(int idx, unsigned int tick)
+ {
+ Pos pos(tick);
+ setPos(idx, pos);
+ }
+*/
+//---------------------------------------------------------
+// setPos
+// song->setPos(Song::CPOS, pos, true, true, true);
+//---------------------------------------------------------
+
+void Song::setPos(int idx, const AL::Pos& val)
+ {
+ setPos(idx, val, true, true, false);
+ }
+
+void Song::setPos(int idx, const Pos& val, bool sig, bool isSeek, bool follow)
+ {
+// printf("setPos %d sig=%d,seek=%d,scroll=%d\n",
+// idx, sig, isSeek, follow);
+// val.dump(0);
+// printf("\n");
+
+ if (pos[idx] == val)
+ return;
+ if (idx == CPOS) {
+// _vcpos = val;
+ if (isSeek) {
+ seekInProgress = true;
+ audio->msgSeek(val);
+ return;
+ }
+ }
+ pos[idx] = val;
+ bool swap = pos[LPOS] > pos[RPOS];
+ if (swap) { // swap lpos/rpos if lpos > rpos
+ Pos tmp = pos[LPOS];
+ pos[LPOS] = pos[RPOS];
+ pos[RPOS] = tmp;
+ }
+ if (sig) {
+ if (swap) {
+ emit posChanged(LPOS, pos[LPOS], follow);
+ emit posChanged(RPOS, pos[RPOS], follow);
+ if (idx != LPOS && idx != RPOS)
+ emit posChanged(idx, pos[idx], follow);
+ }
+ else
+ emit posChanged(idx, pos[idx], follow);
+ }
+
+ if (idx == CPOS)
+ updateCurrentMarker();
+ }
+
+//---------------------------------------------------------
+// updateCurrentMarker
+//---------------------------------------------------------
+
+void Song::updateCurrentMarker()
+ {
+ AL::iMarker i1 = _markerList->begin();
+ AL::iMarker i2 = i1;
+ bool currentChanged = false;
+ Pos& val = pos[CPOS];
+ for (; i1 != _markerList->end(); ++i1) {
+ ++i2;
+ if (val.tick() >= i1->first && (i2==_markerList->end() || val.tick() < i2->first)) {
+ if (i1->second.current())
+ return;
+ i1->second.setCurrent(true);
+ if (currentChanged) {
+ emit markerChanged(MARKER_CUR);
+ return;
+ }
+ ++i1;
+ for (; i1 != _markerList->end(); ++i1) {
+ if (i1->second.current())
+ i1->second.setCurrent(false);
+ }
+ emit markerChanged(MARKER_CUR);
+ return;
+ }
+ else {
+ if (i1->second.current()) {
+ currentChanged = true;
+ i1->second.setCurrent(false);
+ }
+ }
+ }
+ if (currentChanged)
+ emit markerChanged(MARKER_CUR);
+ }
+
+//---------------------------------------------------------
+// forward
+// roll forward config.division ticks
+//---------------------------------------------------------
+
+void Song::forward()
+ {
+ unsigned newPos = pos[0].tick() + config.division;
+ audio->msgSeek(Pos(newPos, AL::TICKS));
+ }
+
+//---------------------------------------------------------
+// rewind
+// roll back config.division ticks
+//---------------------------------------------------------
+
+void Song::rewind()
+ {
+ unsigned newPos;
+ if (unsigned(config.division) > pos[0].tick())
+ newPos = 0;
+ else
+ newPos = pos[0].tick() - config.division;
+ audio->msgSeek(Pos(newPos, AL::TICKS));
+ }
+
+//---------------------------------------------------------
+// rewindStart
+//---------------------------------------------------------
+
+void Song::rewindStart()
+ {
+ audio->msgSeek(Pos(0, AL::TICKS));
+ }
+
+//---------------------------------------------------------
+// update
+//---------------------------------------------------------
+
+void Song::update(int flags)
+ {
+ if (flags == 0)
+ return;
+ emit songChanged(flags);
+ if (flags & SC_TEMPO)
+ emit tempoChanged();
+ }
+
+//---------------------------------------------------------
+// updatePos
+//---------------------------------------------------------
+
+void Song::updatePos()
+ {
+ emit posChanged(0, pos[0], false);
+ emit posChanged(1, pos[1], false);
+ emit posChanged(2, pos[2], false);
+ }
+
+//---------------------------------------------------------
+// roundUpBar
+//---------------------------------------------------------
+
+int Song::roundUpBar(int t) const
+ {
+ int bar, beat;
+ unsigned tick;
+ AL::sigmap.tickValues(t, &bar, &beat, &tick);
+ if (beat || tick)
+ return AL::sigmap.bar2tick(bar+1, 0, 0);
+ return t;
+ }
+
+//---------------------------------------------------------
+// roundUpBeat
+//---------------------------------------------------------
+
+int Song::roundUpBeat(int t) const
+ {
+ int bar, beat;
+ unsigned tick;
+ AL::sigmap.tickValues(t, &bar, &beat, &tick);
+ if (tick)
+ return AL::sigmap.bar2tick(bar, beat+1, 0);
+ return t;
+ }
+
+//---------------------------------------------------------
+// roundDownBar
+//---------------------------------------------------------
+
+int Song::roundDownBar(int t) const
+ {
+ int bar, beat;
+ unsigned tick;
+ AL::sigmap.tickValues(t, &bar, &beat, &tick);
+ return AL::sigmap.bar2tick(bar, 0, 0);
+ }
+
+//---------------------------------------------------------
+// dumpMaster
+//---------------------------------------------------------
+
+void Song::dumpMaster()
+ {
+ AL::tempomap.dump();
+ AL::sigmap.dump();
+ }
+
+//---------------------------------------------------------
+// getSelectedParts
+//---------------------------------------------------------
+
+PartList* Song::getSelectedMidiParts() const
+ {
+ PartList* parts = new PartList();
+
+ //------------------------------------------------------
+ // wenn ein Part selektiert ist, diesen editieren
+ // wenn ein Track selektiert ist, den Ersten
+ // Part des Tracks editieren, die restlichen sind
+ // 'ghostparts'
+ // wenn mehrere Parts selektiert sind, dann Ersten
+ // editieren, die restlichen sind 'ghostparts'
+ //
+
+ // collect marked parts
+ for (ciMidiTrack t = _midis.begin(); t != _midis.end(); ++t) {
+ MidiTrack* track = *t;
+ PartList* pl = track->parts();
+ for (iPart p = pl->begin(); p != pl->end(); ++p) {
+ if (p->second->selected()) {
+ parts->add(p->second);
+ }
+ }
+ }
+ // if no part is selected, then search for selected track
+ // and collect all parts of this track
+
+ if (parts->empty()) {
+ for (ciMidiTrack t = _midis.begin(); t != _midis.end(); ++t) {
+ if ((*t)->selected()) {
+ MidiTrack* track = *t;
+ PartList* pl = track->parts();
+ for (iPart p = pl->begin(); p != pl->end(); ++p)
+ parts->add(p->second);
+ break;
+ }
+ }
+ }
+ return parts;
+ }
+
+PartList* Song::getSelectedWaveParts() const
+ {
+ PartList* parts = new PartList();
+
+ //------------------------------------------------------
+ // wenn ein Part selektiert ist, diesen editieren
+ // wenn ein Track selektiert ist, den Ersten
+ // Part des Tracks editieren, die restlichen sind
+ // 'ghostparts'
+ // wenn mehrere Parts selektiert sind, dann Ersten
+ // editieren, die restlichen sind 'ghostparts'
+ //
+
+ // markierte Parts sammeln
+ for (ciWaveTrack t = _waves.begin(); t != _waves.end(); ++t) {
+ WaveTrack* track = *t;
+ PartList* pl = track->parts();
+ for (iPart p = pl->begin(); p != pl->end(); ++p) {
+ if (p->second->selected()) {
+ parts->add(p->second);
+ }
+ }
+ }
+ // wenn keine Parts selektiert, dann markierten Track suchen
+ // und alle Parts dieses Tracks zusammensuchen
+
+ if (parts->empty()) {
+ for (ciWaveTrack t = _waves.begin(); t != _waves.end(); ++t) {
+ if ((*t)->selected()) {
+ WaveTrack* track = *t;
+ PartList* pl = track->parts();
+ for (iPart p = pl->begin(); p != pl->end(); ++p)
+ parts->add(p->second);
+ break;
+ }
+ }
+ }
+ return parts;
+ }
+
+//---------------------------------------------------------
+// beat
+// update gui
+//---------------------------------------------------------
+
+void Song::beat()
+ {
+ updateFlags = 0;
+ if (audio->isPlaying()) {
+ int tick = audio->seqTime()->curTickPos;
+ setPos(0, tick, true, false, true);
+ }
+ if (audio->isRecording()) {
+ MidiTrackList* ml = midis();
+ for (iMidiTrack it = ml->begin(); it != ml->end(); ++it) {
+ MidiTrack* mt = *it;
+ if (mt->recordFlag())
+ mt->recordBeat();
+ }
+ WaveTrackList* wl = waves();
+ for (iWaveTrack wt = wl->begin(); wt != wl->end(); ++wt) {
+ WaveTrack* mt = *wt;
+ if (mt->recordFlag())
+ mt->recordBeat();
+ }
+ }
+ while (!eventFifo.isEmpty()) {
+ MidiEvent event(eventFifo.get());
+ if (rcEnable)
+ midiRCList.doAction(event);
+ emit midiEvent(event);
+ }
+ //
+ // update controller guis
+ //
+ TrackList* tl = tracks();
+ for (iTrack it = tl->begin(); it != tl->end(); ++it) {
+ Track* track = *it;
+ if (!track->autoRead())
+ continue;
+ track->updateController();
+ }
+ update(updateFlags);
+ }
+
+//---------------------------------------------------------
+// setLen
+//---------------------------------------------------------
+
+void Song::setLen(int l)
+ {
+ _len = roundUpBar(l);
+ AL::Pos pos(_len);
+ int bar, beat;
+ unsigned tick;
+ AL::sigmap.tickValues(_len, &bar, &beat, &tick);
+ emit measureLenChanged(bar);
+ emit lenChanged(pos);
+ }
+
+//---------------------------------------------------------
+// setMeasureLen
+//---------------------------------------------------------
+
+void Song::setMeasureLen(int b)
+ {
+ setLen(AL::sigmap.bar2tick(b, 0, 0));
+ }
+
+//---------------------------------------------------------
+// addMarker
+//---------------------------------------------------------
+
+AL::Marker* Song::addMarker(const QString& s, const AL::Pos& pos)
+ {
+ AL::Marker* marker = _markerList->add(s, pos);
+ updateCurrentMarker();
+ emit markerChanged(MARKER_ADD);
+ return marker;
+ }
+
+//---------------------------------------------------------
+// removeMarker
+//---------------------------------------------------------
+
+void Song::removeMarker(AL::Marker* marker)
+ {
+ _markerList->remove(marker);
+ updateCurrentMarker();
+ emit markerChanged(MARKER_REMOVE);
+ }
+
+//---------------------------------------------------------
+// setMarkerName
+//---------------------------------------------------------
+
+AL::Marker* Song::setMarkerName(AL::Marker* m, const QString& s)
+ {
+ m->setName(s);
+ emit markerChanged(MARKER_NAME);
+ return m;
+ }
+
+AL::Marker* Song::setMarkerTick(AL::Marker* m, int t)
+ {
+ AL::Marker mm(*m);
+ _markerList->remove(m);
+ mm.setTick(t);
+ m = _markerList->add(mm);
+ updateCurrentMarker();
+ emit markerChanged(MARKER_TICK);
+ return m;
+ }
+
+AL::Marker* Song::setMarkerLock(AL::Marker* m, bool f)
+ {
+ m->setType(f ? AL::FRAMES : AL::TICKS);
+ updateCurrentMarker();
+ emit markerChanged(MARKER_LOCK);
+ return m;
+ }
+
+//---------------------------------------------------------
+// endMsgCmd
+//---------------------------------------------------------
+
+void Song::endMsgCmd()
+ {
+ redoList->clear(); // TODO: delete elements in list
+ undoAction->setEnabled(true);
+ redoAction->setEnabled(false);
+ update(updateFlags);
+ }
+
+//---------------------------------------------------------
+// undo
+//---------------------------------------------------------
+
+void Song::undo()
+ {
+ updateFlags = 0;
+ if (doUndo1())
+ return;
+ audio->msgUndo();
+ doUndo3();
+ redoAction->setEnabled(true);
+ undoAction->setEnabled(!undoList->empty());
+ if (updateFlags) {
+ emit songChanged(updateFlags);
+ if (updateFlags & SC_TEMPO)
+ emit tempoChanged();
+ }
+ }
+
+//---------------------------------------------------------
+// redo
+//---------------------------------------------------------
+
+void Song::redo()
+ {
+ updateFlags = 0;
+ if (doRedo1())
+ return;
+ audio->msgRedo();
+ doRedo3();
+ undoAction->setEnabled(true);
+ redoAction->setEnabled(!redoList->empty());
+ if (updateFlags) {
+ emit songChanged(updateFlags);
+ if (updateFlags & SC_TEMPO)
+ emit tempoChanged();
+ }
+ }
+
+//---------------------------------------------------------
+// processMsg
+// executed in realtime thread context
+//---------------------------------------------------------
+
+void Song::processMsg(AudioMsg* msg)
+ {
+ switch(msg->id) {
+ case SEQM_UNDO:
+ doUndo2();
+ break;
+ case SEQM_REDO:
+ doRedo2();
+ break;
+
+ case SEQM_ADD_EVENT:
+ updateFlags = SC_EVENT_INSERTED;
+ if (addEvent(msg->ev1, (Part*)(msg->p2))) {
+ undoOp(UndoOp::AddEvent, msg->ev1, (Part*)msg->p2);
+ }
+ else
+ updateFlags = 0;
+ break;
+
+ case SEQM_ADD_EVENTS:
+ updateFlags = SC_EVENT_INSERTED;
+ for (int i = 0; i < msg->el->size(); ++i) {
+ if (addEvent(msg->el->at(i), (Part*)(msg->p2))) {
+ undoOp(UndoOp::AddEvent, msg->el->at(i), (Part*)msg->p2);
+ }
+ }
+ break;
+
+ case SEQM_REMOVE_EVENT:
+ {
+ Event event = msg->ev1;
+ Part* part = (Part*)(msg->p2);
+ Event e;
+ undoOp(UndoOp::DeleteEvent, e, event, part);
+ deleteEvent(event, part);
+ updateFlags = SC_EVENT_REMOVED;
+ }
+ break;
+ case SEQM_CHANGE_EVENT:
+ changeEvent(msg->ev1, msg->ev2, (Part*)(msg->p3));
+ undoOp(UndoOp::ModifyEvent, msg->ev2, msg->ev1, (Part*)msg->p3);
+ updateFlags = SC_EVENT_MODIFIED;
+ break;
+
+ case SEQM_ADD_TEMPO:
+ undoOp(UndoOp::AddTempo, msg->a, msg->b);
+ AL::tempomap.addTempo(msg->a, msg->b);
+ updateFlags = SC_TEMPO;
+ break;
+
+ case SEQM_SET_TEMPO:
+ undoOp(UndoOp::AddTempo, msg->a, msg->b);
+ AL::tempomap.setTempo(msg->a, msg->b);
+ updateFlags = SC_TEMPO;
+ break;
+
+ case SEQM_SET_GLOBAL_TEMPO:
+ AL::tempomap.setGlobalTempo(msg->a);
+ break;
+
+ case SEQM_REMOVE_TEMPO:
+ undoOp(UndoOp::DeleteTempo, msg->a, msg->b);
+ AL::tempomap.delTempo(msg->a);
+ updateFlags = SC_TEMPO;
+ break;
+
+ case SEQM_ADD_SIG:
+ undoOp(UndoOp::AddSig, msg->a, msg->b, msg->c);
+ AL::sigmap.add(msg->a, AL::TimeSignature(msg->b, msg->c));
+ updateFlags = SC_SIG;
+ break;
+
+ case SEQM_REMOVE_SIG:
+ //printf("processMsg (SEQM_REMOVE_SIG) UndoOp::DeleteSig. Deleting AL::sigmap at: %d with z=%d n=%d\n", msg->a, msg->b, msg->c);
+ undoOp(UndoOp::DeleteSig, msg->a, msg->b, msg->c);
+ AL::sigmap.del(msg->a);
+ updateFlags = SC_SIG;
+ break;
+
+ case SEQM_ADD_CTRL:
+ msg->track->addControllerVal(msg->a, msg->time, msg->cval1);
+ break;
+
+ case SEQM_REMOVE_CTRL:
+ msg->track->removeControllerVal(msg->a, msg->time);
+ break;
+
+ case SEQM_ADD_TRACK:
+ insertTrack2(msg->track);
+ break;
+
+ case SEQM_REMOVE_TRACK:
+ removeTrack2(msg->track);
+ break;
+
+ case SEQM_ADD_PART:
+ {
+ Part* part = (Part*)(msg->p1);
+ part->track()->addPart(part);
+ }
+ break;
+
+ case SEQM_REMOVE_PART:
+ {
+ Part* part = (Part*)(msg->p1);
+ Track* track = part->track();
+ track->parts()->remove(part);
+ }
+ break;
+
+ case SEQM_CHANGE_PART:
+ {
+ Part* newPart = (Part*)msg->p2;
+ Part* oldPart = (Part*)msg->p1;
+ Part part = *newPart;
+ *newPart = *oldPart;
+ *oldPart = part;
+ }
+ break;
+
+ case SEQM_MOVE_TRACK:
+ moveTrack((Track*)(msg->p1), (Track*)(msg->p2));
+ break;
+
+ default:
+ printf("unknown seq message %d\n", msg->id);
+ break;
+ }
+ }
+
+//---------------------------------------------------------
+// panic
+//---------------------------------------------------------
+
+void Song::panic()
+ {
+ audio->msgPanic();
+ }
+
+//---------------------------------------------------------
+// clear
+// signal - emit signals for changes if true
+// called from constructor as clear(false) and
+// from MusE::clearSong() as clear(false)
+//---------------------------------------------------------
+
+void Song::clear(bool signal)
+ {
+ _created = false;
+ _backupWritten = false;
+ dirty = false;
+ _comment = "";
+ _createDate = QDateTime::currentDateTime();
+
+ seekInProgress = false;
+ bounceTrack = 0;
+
+// for (iTrack i = _tracks.begin(); i != _tracks.end(); ++i)
+// (*i)->deactivate();
+
+ _selectedTrack = 0;
+ _tracks.clear();
+
+ qDeleteAll(_midis);
+ _midis.clear();
+
+ qDeleteAll(_waves);
+ _waves.clear();
+
+ qDeleteAll(_inputs); // audio input ports
+ _inputs.clear();
+
+ qDeleteAll(_outputs); // audio output ports
+ _outputs.clear();
+
+ qDeleteAll(_groups); // mixer groups
+ _groups.clear();
+
+ qDeleteAll(_synthIs);
+ _synthIs.clear();
+
+ qDeleteAll(_midiSyntis);
+ _midiSyntis.clear();
+
+ qDeleteAll(_midiOutPorts);
+ _midiOutPorts.clear();
+
+ qDeleteAll(_midiInPorts);
+ _midiInPorts.clear();
+
+ AL::tempomap.clear();
+ AL::sigmap.clear();
+ undoList->clear();
+ redoList->clear();
+ _markerList->clear();
+ pos[0].setTick(0);
+ pos[1].setTick(0);
+ pos[2].setTick(0);
+
+ _masterFlag = true;
+ loopFlag = false;
+ loopFlag = false;
+ punchinFlag = false;
+ punchoutFlag = false;
+ recordFlag = false;
+ soloFlag = false;
+ // seq
+ _recMode = REC_OVERDUP;
+ _cycleMode = CYCLE_NORMAL;
+ _click = false;
+ _quantize = false;
+ _len = 1; // song len in ticks
+ // _tempo = 500000; // default tempo 120
+ if (signal) {
+ emit loopChanged(false);
+ recordChanged(false);
+ }
+ }
+
+//---------------------------------------------------------
+// seqSignal
+// sequencer message to GUI
+// execution environment: gui thread
+//---------------------------------------------------------
+
+void Song::seqSignal(int fd)
+ {
+ char buffer[16];
+
+ int n = ::read(fd, buffer, 16);
+ if (n < 0) {
+ printf("Song: seqSignal(): READ PIPE failed: %s\n",
+ strerror(errno));
+ return;
+ }
+ bool graphChangedCalled = false;
+ bool seekDone = false;
+
+// printf("seqSignal %d\n", n);
+ for (int i = 0; i < n; ++i) {
+// printf(" seqSignal to gui:<%c>\n", buffer[i]);
+ switch(buffer[i]) {
+ case MSG_STOP:
+ stopRolling();
+ break;
+ case MSG_PLAY:
+ setStopPlay(true);
+ break;
+ case MSG_RECORD:
+ setRecord(true);
+ break;
+ case MSG_SEEK:
+ if (!seekDone) {
+ seekDone = true;
+ setPos(0, audio->seqTime()->curTickPos, true, false, !seekInProgress);
+ seekInProgress = false;
+ beat(); // update controller guis
+ }
+ break;
+ case MSG_JACK_SHUTDOWN:
+ restartJack();
+ break;
+
+ case MSG_START_BOUNCE:
+ {
+ bool useFreewheel = config.useJackFreewheelMode;
+ if (useFreewheel) {
+ // check:
+ // we cannot use freewheel, if there are active audio input
+ // strips
+
+ for (iAudioInput ii = _inputs.begin(); ii != _inputs.end(); ++i) {
+ AudioInput* ai = *ii;
+ if (!(ai->mute() || ai->off())) {
+ useFreewheel = false;
+ break;
+ }
+ }
+ if (useFreewheel)
+ audioDriver->setFreewheel(true);
+ }
+ }
+ break;
+
+ case MSG_STOP_BOUNCE:
+ if (audio->freewheel())
+ audioDriver->setFreewheel(false);
+ audio->msgPlay(false);
+ break;
+
+ case MSG_GRAPH_CHANGED:
+ if (!graphChangedCalled) {
+ printf(" graph changed\n");
+ graphChangedCalled = true;
+ audioDriver->graphChanged();
+ }
+ break;
+
+ default:
+ printf("unknown Seq Signal <%c>\n", buffer[i]);
+ break;
+ }
+ }
+ }
+
+//---------------------------------------------------------
+// stopRolling
+//---------------------------------------------------------
+
+void Song::stopRolling()
+ {
+ setStopPlay(false);
+ if (record()) {
+ audio->msgIdle(true); // gain access to all data structures
+
+ WaveTrackList* wl = waves();
+ for (iWaveTrack it = wl->begin(); it != wl->end(); ++it) {
+ WaveTrack* track = *it;
+ if (track->recordFlag() || bounceTrack == track) {
+ track->stopRecording(audio->getStartRecordPos(), audio->getEndRecordPos());
+ }
+ }
+ MidiTrackList* ml = midis();
+ for (iMidiTrack it = ml->begin(); it != ml->end(); ++it) {
+ if ((*it)->recordFlag())
+ (*it)->stopRecording();
+ }
+ OutputList* ol = outputs();
+ for (iAudioOutput io = ol->begin(); io != ol->end(); ++io) {
+ AudioOutput* ao = *io;
+ if (ao->recordFlag())
+ ao->stopRecording(audio->getStartRecordPos(), audio->getEndRecordPos());
+ }
+ audio->msgIdle(false);
+ updateFlags |= SC_PART_MODIFIED;
+ endUndo(updateFlags);
+ setRecord(false);
+ }
+ //
+ // process recorded automation events
+ //
+ for (iTrack it = tracks()->begin(); it != tracks()->end(); ++it) {
+ Track* track = *it;
+ if (!track->autoWrite())
+ continue;
+ CtrlRecList* crl = track->recEvents();
+ CtrlList* cl = track->controller();
+ for (iCtrl icl = cl->begin(); icl != cl->end(); ++icl) {
+ Ctrl* cl = icl->second;
+ int id = cl->id();
+ //
+ // remove old events from record region
+ //
+ bool hasEvents = false;
+// int start = audio->getStartRecordPos().frame();
+// int end = audio->getEndRecordPos().frame();
+// iCtrlVal s = cl->lower_bound(start);
+// iCtrlVal e = cl->lower_bound(end);
+// cl->erase(s, e);
+// }
+
+ for (iCtrlRec icr = crl->begin(); icr != crl->end(); ++icr) {
+ if (icr->id == id && icr->type == 1) {
+ int start = icr->time;
+ ++icr;
+ for (; icr != crl->end(); ++icr) {
+ if (icr->id == id && icr->type == 2) {
+ int end = icr->time;
+ if (track->timeType() == AL::TICKS) {
+ start = AL::tempomap.frame2tick(start);
+ end = AL::tempomap.frame2tick(end);
+ }
+ iCtrlVal s = cl->lowerBound(start);
+ iCtrlVal e = cl->lowerBound(end);
+ while (s != e)
+ cl->erase(s++);
+ hasEvents = true;
+ break;
+ }
+ }
+ if (icr == crl->end())
+ break;
+ }
+ }
+ //
+ // extract all recorded events for controller "id"
+ // from CtrlRecList and put into cl
+ //
+ for (iCtrlRec icr = crl->begin(); icr != crl->end(); ++icr) {
+ if (icr->id == id && icr->type == 0)
+ cl->add(icr->time, icr->val);
+ }
+ track->emitControllerChanged(id);
+ }
+ crl->clear();
+ track->setAutoWrite(false);
+ }
+ }
+
+//---------------------------------------------------------
+// addControllerVal
+// GUI context
+//---------------------------------------------------------
+
+void Song::cmdAddControllerVal(Track* t, int id, const Pos& pos, CVal val)
+ {
+ Ctrl* c = t->getController(id);
+ if (c == 0) {
+ printf("Song::addControllerVal:: no controller %d found\n", id);
+ return;
+ }
+ cmdAddControllerVal(t, c, pos, val);
+ }
+
+void Song::cmdAddControllerVal(Track* t, Ctrl* c, const Pos& p, CVal val)
+ {
+ unsigned time = t->timeType() == AL::FRAMES ? p.frame() : p.tick();
+ iCtrlVal e = c->find(time);
+ if (e == c->end()) {
+ // add new controller event
+ audio->msgAddController(t, c->id(), time, val);
+ }
+ else {
+ //
+ // change controller is handled inline:
+ //
+ CVal oval = c->value(time);
+ startUndo();
+ undoOp(UndoOp::ModifyCtrl, t, c->id(), time, val, oval);
+ c->add(time, val);
+ endUndo(0);
+ }
+ if (!audio->isPlaying() && t->autoRead()) {
+ // current value may have changed
+ unsigned ctime = t->timeType() == AL::FRAMES ? pos[0].frame() : pos[0].tick();
+ CVal cval = c->value(ctime);
+ if (c->curVal().i != cval.i) {
+ if (t->type() == Track::MIDI) {
+ MidiEvent ev(0, 0, ME_CONTROLLER, c->id(), cval.i);
+ ((MidiTrack*)t)->playMidiEvent(&ev);
+ }
+ c->setCurVal(cval);
+ }
+ }
+ t->emitControllerChanged(c->id()); //moved this out here, otherwise canvas is not updated
+ }
+
+//---------------------------------------------------------
+// setControllerVal
+// GUI context
+//---------------------------------------------------------
+
+void Song::setControllerVal(Track* t, int id, CVal val)
+ {
+ Ctrl* c = t->getController(id);
+ if (c == 0) {
+ printf("Song::addControllerVal:: no controller %d found\n", id);
+ return;
+ }
+ setControllerVal(t, c, val);
+ }
+
+void Song::setControllerVal(Track* t, Ctrl* c, CVal val)
+ {
+ if (t->isMidiTrack()) {
+ if (t->type() == Track::MIDI) {
+ MidiTrack* mt = (MidiTrack*)t;
+ MidiEvent ev(0, 0, ME_CONTROLLER, c->id(), val.i);
+ mt->playMidiEvent(&ev);
+ }
+ else if (t->type() == Track::MIDI_OUT) {
+ MidiOutPort* mp = (MidiOutPort*)t;
+ MidiEvent ev(0, 0, ME_CONTROLLER, c->id(), val.i);
+ mp->playMidiEvent(&ev);
+ }
+ }
+ else {
+ c->setCurVal(val);
+ if (c->id() & 0x3ffff000) {
+ // plugin controller
+ AudioTrack* track = (AudioTrack*) t;
+ bool prefader;
+ int pluginIndex, ctrlIndex;
+ getCtrlPlugin(c->id(), &prefader, &pluginIndex, &ctrlIndex);
+ Pipeline* pipe = prefader ? track->prePipe() : track->postPipe();
+ pipe->plugin(pluginIndex)->setParam(ctrlIndex, val.f);
+ }
+ }
+ c->setCurVal(val);
+
+ if (t->autoWrite()) {
+ unsigned time = t->timeType() == AL::FRAMES ? pos[0].frame() : pos[0].tick();
+ if (audio->isPlaying())
+ t->recEvents()->push_back(CtrlRecVal(time, c->id(), val));
+ else {
+ iCtrlVal e = c->find(time);
+ if (e == c->end()) {
+ // add new controller event
+ audio->msgAddController(t, c->id(), time, val);
+ }
+ else {
+ CVal oval = c->value(time);
+ startUndo();
+ undoOp(UndoOp::ModifyCtrl, t, c->id(), time, val, oval);
+ c->add(time, val);
+ endUndo(0);
+ }
+ }
+ }
+ t->emitControllerChanged(c->id());
+ }
+
+//---------------------------------------------------------
+// cmdRemoveControllerVal
+//---------------------------------------------------------
+
+void Song::cmdRemoveControllerVal(Track* t, int id, unsigned time)
+ {
+ audio->msgRemoveController(t, id, time);
+ t->emitControllerChanged(id);
+ }
+
+//---------------------------------------------------------
+// absoluteProjectPath
+//---------------------------------------------------------
+
+QString Song::absoluteProjectPath() const
+ {
+ return QDir::homePath() + "/" + config.projectPath + "/" + _projectPath;
+ }
+
+//---------------------------------------------------------
+// projectPath
+//---------------------------------------------------------
+
+QString Song::projectPath() const
+ {
+ return _projectPath;
+ }
+
+//---------------------------------------------------------
+// projectName
+//---------------------------------------------------------
+
+QString Song::projectName() const
+ {
+ QString name = _projectPath.split("/").last();
+ return name;
+ }
+
+//---------------------------------------------------------
+// setProjectPath
+//---------------------------------------------------------
+
+void Song::setProjectPath(const QString& s)
+ {
+ _projectPath = s;
+ }
+
+//---------------------------------------------------------
+// read
+// return false on error
+//---------------------------------------------------------
+
+bool Song::read(QFile* qf)
+ {
+ QDomDocument doc;
+
+ int line, column;
+ QString err;
+ if (!doc.setContent(qf, false, &err, &line, &column)) {
+ QString col, ln, error;
+ col.setNum(column);
+ ln.setNum(line);
+ error = err + "\n at line: " + ln + " col: " + col;
+ printf("error reading med file: %s\n", error.toLatin1().data());
+ return false;
+ }
+ for (QDomNode node = doc.documentElement(); !node.isNull(); node = node.nextSibling()) {
+ QDomElement e = node.toElement();
+ if (e.isNull())
+ continue;
+ if (e.tagName() == "muse") {
+ QString sversion = e.attribute("version", "1.0");
+ int major=0, minor=0;
+ sscanf(sversion.toLatin1().data(), "%d.%d", &major, &minor);
+ int version = major << 8 + minor;
+ if (version >= 0x201)
+ read30(node.firstChild());
+ else if (version >= 0x200)
+ read20(node);
+ else if (version == 0x100)
+ read10(node.firstChild());
+ else
+ printf("unsupported *.med file version %s\n", sversion.toLatin1().data());
+ }
+ else
+ printf("MusE: %s not supported\n", e.tagName().toLatin1().data());
+ }
+ dirty = false;
+ return true;
+ }
+
+//---------------------------------------------------------
+// read10
+//---------------------------------------------------------
+
+void Song::read10(QDomNode)
+ {
+ printf("reading type 1.0 *.med files not implemented\n");
+ }
+
+//---------------------------------------------------------
+// read30
+//---------------------------------------------------------
+
+void Song::read30(QDomNode node)
+ {
+ for (; !node.isNull(); node = node.nextSibling()) {
+ QDomElement e = node.toElement();
+ if (e.isNull())
+ continue;
+ if (e.tagName() == "configuration")
+ readConfiguration(node.firstChild());
+ else if (e.tagName() == "song")
+ read(node.firstChild());
+ else if (e.tagName() == "toplevels")
+ muse->readToplevels(node.firstChild());
+ else
+ printf("MusE:read30(): unknown tag %s\n", e.tagName().toLatin1().data());
+ }
+ }
+
+//---------------------------------------------------------
+// restartJack
+//---------------------------------------------------------
+
+void Song::restartJack()
+ {
+ muse->seqStop();
+ audioState = AUDIO_STOP;
+ for (;;) {
+ // give the user a sensible explanation
+ int btn = QMessageBox::critical( muse, tr("Jack shutdown!"),
+ tr("Jack has detected a performance problem which has lead to\n"
+ "MusE being disconnected.\n"
+ "This could happen due to a number of reasons:\n"
+ "- a performance issue with your particular setup.\n"
+ "- a bug in MusE (or possibly in another connected software).\n"
+ "- a random hiccup which might never occur again.\n"
+ "- jack was voluntary stopped by you or someone else\n"
+ "- jack crashed\n"
+ "If there is a persisting problem you are much welcome to discuss it\n"
+ "on the MusE mailinglist.\n"
+ "(there is information about joining the mailinglist on the MusE\n"
+ " homepage which is available through the help menu)\n"
+ "\n"
+ "To proceed check the status of Jack and try to restart it and then .\n"
+ "click on the Restart button."),
+ "restart", "cancel", "save project"
+ );
+ if (btn == 0) {
+ if (!audioDriver->restart())
+ break;
+ }
+ else if (btn == 2)
+ muse->save();
+ else if (btn == 1)
+ exit(-1);
+ }
+ muse->seqRestart();
+ }
+
+//---------------------------------------------------------
+// routeChanged
+//---------------------------------------------------------
+
+void Song::routeChanged(QAction* a)
+ {
+ audio->msgRoute(a->isChecked(), a->data().value<Route>());
+ }
+