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