diff options
author | Robert Jonsson <spamatica@gmail.com> | 2011-03-07 19:01:11 +0000 |
---|---|---|
committer | Robert Jonsson <spamatica@gmail.com> | 2011-03-07 19:01:11 +0000 |
commit | e40fc849149dd97c248866a4a1d026dda5e57b62 (patch) | |
tree | b12b358f3b3a0608001d30403358f8443118ec5f /attic/muse2-oom/muse2/muse/song.cpp | |
parent | 1bd4f2e8d9745cabb667b043171cad22c8577768 (diff) |
clean3
Diffstat (limited to 'attic/muse2-oom/muse2/muse/song.cpp')
-rw-r--r-- | attic/muse2-oom/muse2/muse/song.cpp | 3911 |
1 files changed, 3911 insertions, 0 deletions
diff --git a/attic/muse2-oom/muse2/muse/song.cpp b/attic/muse2-oom/muse2/muse/song.cpp new file mode 100644 index 00000000..9174bbe4 --- /dev/null +++ b/attic/muse2-oom/muse2/muse/song.cpp @@ -0,0 +1,3911 @@ +//========================================================= +// MusE +// Linux Music Editor +// $Id: song.cpp,v 1.59.2.52 2009/12/15 03:39:58 terminator356 Exp $ +// +// (C) Copyright 2000-2004 Werner Schweer (ws@seh.de) +//========================================================= + +#include <unistd.h> +#include <stdio.h> +#include <errno.h> + +#include <QAction> +#include <QDir> +#include <QMenu> +#include <QMessageBox> +#include <QPoint> +#include <QSignalMapper> +#include <QTextStream> + +#include "app.h" +#include "driver/jackmidi.h" +#include "driver/alsamidi.h" +#include "song.h" +#include "track.h" +#include "undo.h" +#include "key.h" +#include "globals.h" +#include "event.h" +#include "drummap.h" +#include "marker/marker.h" +#include "synth.h" +#include "audio.h" +#include "mididev.h" +#include "amixer.h" +#include "midiseq.h" +#include "audiodev.h" +#include "gconfig.h" +#include "sync.h" +#include "midictrl.h" +#include "menutitleitem.h" +#include "midi.h" +///#include "sig.h" +#include "al/sig.h" +#include <sys/wait.h> +#include "trackview.h" + +extern void clearMidiTransforms(); +extern void clearMidiInputTransforms(); +Song* song; + +/* +//--------------------------------------------------------- +// RoutingMenuItem +//--------------------------------------------------------- + +class RoutingMenuItem : public QCustomMenuItem +{ + Route route; + //virtual QSize sizeHint() { return QSize(80, h); } + virtual void paint(QPainter* p, const QColorGroup&, bool, bool, int x, int y, int w, int h) + { + p->fillRect(x, y, w, h, QBrush(lightGray)); + p->drawText(x, y, w, h, AlignCenter, route.name()); + } + + public: + RoutingMenuItem(const Route& r) : route(r) { } +}; +*/ + +//--------------------------------------------------------- +// Song +//--------------------------------------------------------- + +Song::Song(const char* name) + :QObject(0) + { + setObjectName(name); + _arrangerRaster = 0; // Set to measure, the same as Arranger intial value. Arranger snap combo will set this. + noteFifoSize = 0; + noteFifoWindex = 0; + noteFifoRindex = 0; + undoList = new UndoList; + redoList = new UndoList; + _markerList = new MarkerList; + _globalPitchShift = 0; + clear(false); + } + +//--------------------------------------------------------- +// Song +//--------------------------------------------------------- + +Song::~Song() + { + delete undoList; + delete redoList; + delete _markerList; + } + +//--------------------------------------------------------- +// putEvent +//--------------------------------------------------------- + +void Song::putEvent(int pv) + { + if (noteFifoSize < REC_NOTE_FIFO_SIZE) { + recNoteFifo[noteFifoWindex] = pv; + noteFifoWindex = (noteFifoWindex + 1) % REC_NOTE_FIFO_SIZE; + ++noteFifoSize; + } + } + +//--------------------------------------------------------- +// setTempo +// public slot +//--------------------------------------------------------- + +void Song::setTempo(int newTempo) + { + audio->msgSetTempo(pos[0].tick(), newTempo, true); + } + +//--------------------------------------------------------- +// setSig +// called from transport window +//--------------------------------------------------------- + +void Song::setSig(int z, int n) + { + if (_masterFlag) { + audio->msgAddSig(pos[0].tick(), z, n); + } + } + +void Song::setSig(const AL::TimeSignature& sig) + { + if (_masterFlag) { + audio->msgAddSig(pos[0].tick(), sig.z, sig.n); + } + } + +//--------------------------------------------------------- +// addNewTrack +// Called from GUI context +// Besides normal track types, n includes synth menu ids from populateAddTrack() +//--------------------------------------------------------- + +Track* Song::addNewTrack(QAction* action) +{ + int n = action->data().toInt(); + // Ignore negative numbers since this slot could be called by a menu or list etc. passing -1. + if(n < 0) + return 0; + + // Synth sub-menu id? + if(n >= MENU_ADD_SYNTH_ID_BASE) + { + n -= MENU_ADD_SYNTH_ID_BASE; + if(n < (int)synthis.size()) + { + //SynthI* si = createSynthI(synthis[n]->baseName()); + //SynthI* si = createSynthI(synthis[n]->name()); + SynthI* si = createSynthI(synthis[n]->baseName(), synthis[n]->name()); + if(!si) + return 0; + + // Add instance last in midi device list. + for (int i = 0; i < MIDI_PORTS; ++i) + { + MidiPort* port = &midiPorts[i]; + MidiDevice* dev = port->device(); + if (dev==0) + { + midiSeq->msgSetMidiDevice(port, si); + muse->changeConfig(true); // save configuration file + deselectTracks(); + si->setSelected(true); + update(); + return si; + } + } + deselectTracks(); + si->setSelected(true); + update(SC_SELECTION); + return si; + } + else + return 0; + } + // Normal track. + else + { + // Ignore AUDIO_SOFTSYNTH, now that we have it as the synth menu id, since addTrack doesn't like it. + if((Track::TrackType)n == Track::AUDIO_SOFTSYNTH) + return 0; + + Track* t = addTrack((Track::TrackType)n); + deselectTracks(); + t->setSelected(true); + update(SC_SELECTION); + return t; + } +} + + +//---------------------------------------------------------/*{{{*/ +// addTrack +// called from GUI context +//--------------------------------------------------------- + +Track* Song::addTrack(int t) + { + Track::TrackType type = (Track::TrackType) t; + Track* track = 0; + int lastAuxIdx = _auxs.size(); + switch(type) { + case Track::MIDI: + track = new MidiTrack(); + track->setType(Track::MIDI); + break; + case Track::DRUM: + track = new MidiTrack(); + track->setType(Track::DRUM); + ((MidiTrack*)track)->setOutChannel(9); + break; + case Track::WAVE: + track = new WaveTrack(); + ((AudioTrack*)track)->addAuxSend(lastAuxIdx); + break; + case Track::AUDIO_OUTPUT: + track = new AudioOutput(); + break; + case Track::AUDIO_GROUP: + track = new AudioGroup(); + ((AudioTrack*)track)->addAuxSend(lastAuxIdx); + break; + case Track::AUDIO_AUX: + track = new AudioAux(); + break; + case Track::AUDIO_INPUT: + track = new AudioInput(); + ((AudioTrack*)track)->addAuxSend(lastAuxIdx); + break; + case Track::AUDIO_SOFTSYNTH: + printf("not implemented: Song::addTrack(SOFTSYNTH)\n"); + // ((AudioTrack*)track)->addAuxSend(lastAuxIdx); + break; + default: + printf("Song::addTrack() illegal type %d\n", type); + abort(); + } + track->setDefaultName(); + insertTrack1(track, -1); + msgInsertTrack(track, -1, true); + insertTrack3(track, -1); + + // Add default track <-> midiport routes. + if(track->isMidiTrack()) + { + MidiTrack* mt = (MidiTrack*)track; + int c, cbi, ch; + bool defOutFound = false; /// TODO: Remove this when multiple out routes supported. + for(int i = 0; i < MIDI_PORTS; ++i) + { + MidiPort* mp = &midiPorts[i]; + + c = mp->defaultInChannels(); + if(c) + { + audio->msgAddRoute(Route(i, c), Route(track, c)); + updateFlags |= SC_ROUTE; + } + + if(!defOutFound) /// + { + c = mp->defaultOutChannels(); + if(c) + { + + /// TODO: Switch when multiple out routes supported. + #if 0 + audio->msgAddRoute(Route(track, c), Route(i, c)); + updateFlags |= SC_ROUTE; + #else + for(ch = 0; ch < MIDI_CHANNELS; ++ch) + { + cbi = 1 << ch; + if(c & cbi) + { + defOutFound = true; + mt->setOutPort(i); + mt->setOutChannel(ch); + updateFlags |= SC_ROUTE; + break; + } + } + #endif + } + } + } + } + + // + // add default route to master + // + OutputList* ol = song->outputs(); + if (!ol->empty()) { + AudioOutput* ao = ol->front(); + switch(type) { + //case Track::MIDI: + //case Track::DRUM: + //case Track::AUDIO_OUTPUT: + // break; + + case Track::WAVE: + //case Track::AUDIO_GROUP: // Removed by Tim. + case Track::AUDIO_AUX: + //case Track::AUDIO_INPUT: // Removed by Tim. + // p3.3.38 + //case Track::AUDIO_SOFTSYNTH: + audio->msgAddRoute(Route((AudioTrack*)track, -1), Route(ao, -1)); + updateFlags |= SC_ROUTE; + break; + // p3.3.38 It should actually never get here now, but just in case. + case Track::AUDIO_SOFTSYNTH: + audio->msgAddRoute(Route((AudioTrack*)track, 0, ((AudioTrack*)track)->channels()), Route(ao, 0, ((AudioTrack*)track)->channels())); + updateFlags |= SC_ROUTE; + break; + default: + break; + } + } + audio->msgUpdateSoloStates(); + return track; + }/*}}}*/ + +//--------------------------------------------------------- +// cmdRemoveTrack +//--------------------------------------------------------- + +void Song::cmdRemoveTrack(Track* track) + { + int idx = _tracks.index(track); + undoOp(UndoOp::DeleteTrack, idx, track); + removeTrack2(track); + updateFlags |= SC_TRACK_REMOVED; + } + +//--------------------------------------------------------- +// removeMarkedTracks +//--------------------------------------------------------- + +void Song::removeMarkedTracks() + { + bool loop; + do { + loop = false; + for (iTrack t = _tracks.begin(); t != _tracks.end(); ++t) { + if ((*t)->selected()) { + removeTrack2(*t); + loop = true; + break; + } + } + } while (loop); + } + +//--------------------------------------------------------- +// deselectTracks +//--------------------------------------------------------- + +void Song::deselectTracks() + { + for (iTrack t = _tracks.begin(); t != _tracks.end(); ++t) + (*t)->setSelected(false); + } + +//--------------------------------------------------------- +// changeTrack +// oldTrack - copy of the original track befor modification +// newTrack - modified original track +//--------------------------------------------------------- + +void Song::changeTrack(Track* oldTrack, Track* newTrack) + { + oldTrack->setSelected(false); //?? + int idx = _tracks.index(newTrack); + + //undoOp(UndoOp::ModifyTrack, oldTrack, newTrack); + undoOp(UndoOp::ModifyTrack, idx, oldTrack, newTrack); + updateFlags |= SC_TRACK_MODIFIED; + } + +//--------------------------------------------------------- +// addEvent +// return true if event was added +//--------------------------------------------------------- + +bool Song::addEvent(Event& event, Part* part) + { + /* + if (event.type() == Controller) { + MidiTrack* track = (MidiTrack*)part->track(); + int ch = track->outChannel(); + int tick = event.tick() + part->tick(); + int cntrl = event.dataA(); + int val = event.dataB(); + MidiPort* mp = &midiPorts[track->outPort()]; + + // Is it a drum controller event, according to the track port's instrument? + if(track->type() == Track::DRUM) + { + MidiController* mc = mp->drumController(cntrl); + if(mc) + { + int note = cntrl & 0x7f; + cntrl &= ~0xff; + ch = drumMap[note].channel; + mp = &midiPorts[drumMap[note].port]; + cntrl |= drumMap[note].anote; + } + } + + // Changed by T356. + //if (!mp->setCtrl(ch, tick, cntrl, val)) { + // mp->addManagedController(ch, cntrl); + // if (!mp->setCtrl(ch, tick, cntrl, val)) + // return false; + // } + // Changed again. Don't depend on return value of this - search for the event, below. + //if(!mp->setControllerVal(ch, tick, cntrl, val, part)) + // return false; + if(mp->setControllerVal(ch, tick, cntrl, val, part)) + updateFlags |= SC_MIDI_CONTROLLER; + } + */ + + //addPortCtrlEvents(event, part); + + // Return false if the event is already found. + // (But allow a port controller value, above, in case it is not already stored.) + if(part->events()->find(event) != part->events()->end()) + { + // This can be normal for some (redundant) operations. + if(debugMsg) + printf("Song::addEvent event already found in part:%s size:%zd\n", part->name().toLatin1().constData(), part->events()->size()); + return false; + } + + part->events()->add(event); + return true; + } + +//--------------------------------------------------------- +// changeEvent +//--------------------------------------------------------- + +void Song::changeEvent(Event& oldEvent, Event& newEvent, Part* part) +{ + iEvent i = part->events()->find(oldEvent); + + if (i == part->events()->end()) { + // This can be normal for some (redundant) operations. + if(debugMsg) + printf("Song::changeEvent event not found in part:%s size:%zd\n", part->name().toLatin1().constData(), part->events()->size()); + // abort(); + // Removed by T356. Allow it to add the new event. + // (And remove the old one from the midi port controller!) + //return; + } + else + part->events()->erase(i); + + part->events()->add(newEvent); + + /* + if (oldEvent.type() == Controller) { + MidiTrack* track = (MidiTrack*)part->track(); + int ch = track->outChannel(); + int tick = oldEvent.tick() + part->tick(); + int cntrl = oldEvent.dataA(); + MidiPort* mp = &midiPorts[track->outPort()]; + // Is it a drum controller event, according to the track port's instrument? + if(track->type() == Track::DRUM) + { + MidiController* mc = mp->drumController(cntrl); + if(mc) + { + int note = cntrl & 0x7f; + cntrl &= ~0xff; + ch = drumMap[note].channel; + mp = &midiPorts[drumMap[note].port]; + cntrl |= drumMap[note].anote; + } + } + + mp->deleteController(ch, tick, cntrl, part); + } + */ + //removePortCtrlEvents(oldEvent, part); + + /* + if (newEvent.type() == Controller) { + MidiTrack* track = (MidiTrack*)part->track(); + int ch = track->outChannel(); + int tick = newEvent.tick() + part->tick(); + int cntrl = newEvent.dataA(); + int val = newEvent.dataB(); + MidiPort* mp = &midiPorts[track->outPort()]; + // Is it a drum controller event, according to the track port's instrument? + if(track->type() == Track::DRUM) + { + MidiController* mc = mp->drumController(cntrl); + if(mc) + { + int note = cntrl & 0x7f; + cntrl &= ~0xff; + ch = drumMap[note].channel; + mp = &midiPorts[drumMap[note].port]; + cntrl |= drumMap[note].anote; + } + } + + mp->setControllerVal(ch, tick, cntrl, val, part); + } + */ + //addPortCtrlEvents(newEvent, part); +} + +//--------------------------------------------------------- +// deleteEvent +//--------------------------------------------------------- + +void Song::deleteEvent(Event& event, Part* part) + { + /* + if (event.type() == Controller) { + MidiTrack* track = (MidiTrack*)part->track(); + int ch = track->outChannel(); + int tick = event.tick() + part->tick(); + int cntrl = event.dataA(); + + MidiPort* mp = &midiPorts[track->outPort()]; + // Is it a drum controller event, according to the track port's instrument? + if(track->type() == Track::DRUM) + { + MidiController* mc = mp->drumController(cntrl); + if(mc) + { + int note = cntrl & 0x7f; + cntrl &= ~0xff; + ch = drumMap[note].channel; + mp = &midiPorts[drumMap[note].port]; + cntrl |= drumMap[note].anote; + } + } + + mp->deleteController(ch, tick, cntrl, part); + } + */ + //removePortCtrlEvents(event, part); + + iEvent ev = part->events()->find(event); + if (ev == part->events()->end()) { + // This can be normal for some (redundant) operations. + if(debugMsg) + printf("Song::deleteEvent event not found in part:%s size:%zd\n", part->name().toLatin1().constData(), part->events()->size()); + return; + } + part->events()->erase(ev); + } + +//--------------------------------------------------------- +// remapPortDrumCtrlEvents +// Called when drum map anote, channel, or port is changed. +//--------------------------------------------------------- + +void Song::remapPortDrumCtrlEvents(int mapidx, int newnote, int newchan, int newport) +{ + if(mapidx == -1) + return; + + for(ciMidiTrack it = _midis.begin(); it != _midis.end(); ++it) + { + MidiTrack* mt = *it; + if(mt->type() != Track::DRUM) + continue; + + MidiPort* trackmp = &midiPorts[mt->outPort()]; + const PartList* pl = mt->cparts(); + for(ciPart ip = pl->begin(); ip != pl->end(); ++ip) + { + MidiPart* part = (MidiPart*)(ip->second); + const EventList* el = part->cevents(); + unsigned len = part->lenTick(); + for(ciEvent ie = el->begin(); ie != el->end(); ++ie) + { + const Event& ev = ie->second; + // Added by T356. Do not handle events which are past the end of the part. + if(ev.tick() >= len) + break; + + if(ev.type() != Controller) + continue; + + int cntrl = ev.dataA(); + + // Is it a drum controller event, according to the track port's instrument? + MidiController* mc = trackmp->drumController(cntrl); + if(!mc) + continue; + + int note = cntrl & 0x7f; + // Does the index match? + if(note == mapidx) + { + int tick = ev.tick() + part->tick(); + int ch = drumMap[note].channel; + int port = drumMap[note].port; + MidiPort* mp = &midiPorts[port]; + cntrl = (cntrl & ~0xff) | drumMap[note].anote; + + // Remove the port controller value. + mp->deleteController(ch, tick, cntrl, part); + + if(newnote != -1 && newnote != drumMap[note].anote) + cntrl = (cntrl & ~0xff) | newnote; + if(newchan != -1 && newchan != ch) + ch = newchan; + if(newport != -1 && newport != port) + port = newport; + + mp = &midiPorts[port]; + + // Add the port controller value. + mp->setControllerVal(ch, tick, cntrl, ev.dataB(), part); + } + } + } + } +} + +//--------------------------------------------------------- +// changeAllPortDrumCtlEvents +// add true: add events. false: remove events +// drumonly true: Do drum controller events ONLY. false (default): Do ALL controller events. +//--------------------------------------------------------- + +void Song::changeAllPortDrumCtrlEvents(bool add, bool drumonly) +{ + int ch, trackch, cntrl, tick; + MidiPort* mp, *trackmp; + for(ciMidiTrack it = _midis.begin(); it != _midis.end(); ++it) + { + MidiTrack* mt = *it; + if(mt->type() != Track::DRUM) + continue; + + trackmp = &midiPorts[mt->outPort()]; + trackch = mt->outChannel(); + const PartList* pl = mt->cparts(); + for(ciPart ip = pl->begin(); ip != pl->end(); ++ip) + { + MidiPart* part = (MidiPart*)(ip->second); + const EventList* el = part->cevents(); + unsigned len = part->lenTick(); + for(ciEvent ie = el->begin(); ie != el->end(); ++ie) + { + const Event& ev = ie->second; + // Added by T356. Do not handle events which are past the end of the part. + if(ev.tick() >= len) + break; + + if(ev.type() != Controller) + continue; + + cntrl = ev.dataA(); + mp = trackmp; + ch = trackch; + + // Is it a drum controller event, according to the track port's instrument? + if(trackmp->drumController(cntrl)) + { + int note = cntrl & 0x7f; + ch = drumMap[note].channel; + mp = &midiPorts[drumMap[note].port]; + cntrl = (cntrl & ~0xff) | drumMap[note].anote; + } + else + { + if(drumonly) + continue; + } + + tick = ev.tick() + part->tick(); + + if(add) + // Add the port controller value. + mp->setControllerVal(ch, tick, cntrl, ev.dataB(), part); + else + // Remove the port controller value. + mp->deleteController(ch, tick, cntrl, part); + } + } + } +} + +//--------------------------------------------------------- +// cmdAddRecordedEvents +// add recorded Events into part +//--------------------------------------------------------- + +void Song::cmdAddRecordedEvents(MidiTrack* mt, EventList* events, unsigned startTick) + { + if (events->empty()) { + if (debugMsg) + printf("no events recorded\n"); + return; + } + iEvent s; + iEvent e; + unsigned endTick; + + // Changed by Tim. p3.3.8 + + //if (punchin()) + if((audio->loopCount() > 0 && startTick > lPos().tick()) || (punchin() && startTick < lPos().tick())) + { + startTick = lpos(); + s = events->lower_bound(startTick); + } + else + { + s = events->begin(); +// startTick = s->first; + } + + // Changed by Tim. p3.3.8 + + //if (punchout()) + //{ + // endTick = rpos(); + // e = events->lower_bound(endTick); + //} + //else + //{ + // search for last noteOff: + endTick = 0; + for (iEvent i = events->begin(); i != events->end(); ++i) { + Event ev = i->second; + unsigned l = ev.endTick(); + if (l > endTick) + endTick = l; + } + // e = events->end(); + //} + if((audio->loopCount() > 0) || (punchout() && endTick > rPos().tick()) ) + { + endTick = rpos(); + e = events->lower_bound(endTick); + } + else + e = events->end(); + + if (startTick > endTick) { + if (debugMsg) + printf("no events in record area\n"); + return; + } + + //--------------------------------------------------- + // if startTick points into a part, + // record to that part + // else + // create new part + //--------------------------------------------------- + + PartList* pl = mt->parts(); + MidiPart* part = 0; + iPart ip; + for (ip = pl->begin(); ip != pl->end(); ++ip) { + part = (MidiPart*)(ip->second); + unsigned partStart = part->tick(); + unsigned partEnd = part->endTick(); + if (startTick >= partStart && startTick < partEnd) + break; + } + if (ip == pl->end()) { + if (debugMsg) + printf("create new part for recorded events\n"); + // create new part + part = new MidiPart(mt); + + // Changed by Tim. p3.3.8 + + // Honour the Arranger snap settings. (Set to bar by default). + //startTick = roundDownBar(startTick); + //endTick = roundUpBar(endTick); + // Round the start down using the Arranger part snap raster value. + startTick = AL::sigmap.raster1(startTick, arrangerRaster()); + // Round the end up using the Arranger part snap raster value. + endTick = AL::sigmap.raster2(endTick, arrangerRaster()); + + part->setTick(startTick); + part->setLenTick(endTick - startTick); + part->setName(mt->name()); + // copy events + for (iEvent i = s; i != e; ++i) { + Event old = i->second; + Event event = old.clone(); + event.setTick(old.tick() - startTick); + // addEvent also adds port controller values. So does msgAddPart, below. Let msgAddPart handle them. + //addEvent(event, part); + if(part->events()->find(event) == part->events()->end()) + part->events()->add(event); + } + audio->msgAddPart(part); + updateFlags |= SC_PART_INSERTED; + return; + } + + updateFlags |= SC_EVENT_INSERTED; + + unsigned partTick = part->tick(); + if (endTick > part->endTick()) { + // Determine new part length... + endTick = 0; + for (iEvent i = s; i != e; ++i) { + Event event = i->second; + unsigned tick = event.tick() - partTick + event.lenTick(); + if (endTick < tick) + endTick = tick; + } + // Added by Tim. p3.3.8 + + // Round the end up (again) using the Arranger part snap raster value. + endTick = AL::sigmap.raster2(endTick, arrangerRaster()); + + // Remove all of the part's port controller values. Indicate do not do clone parts. + removePortCtrlEvents(part, false); + // Clone the part. This doesn't increment aref count, and doesn't chain clones. + // It also gives the new part a new serial number, but it is + // overwritten with the old one by Song::changePart(), below. + Part* newPart = part->clone(); + // Set the new part's length. + newPart->setLenTick(endTick); + // Change the part. + changePart(part, newPart); + // Manually adjust reference counts. + part->events()->incARef(-1); + newPart->events()->incARef(1); + // Replace the part in the clone chain with the new part. + replaceClone(part, newPart); + // Now add all of the new part's port controller values. Indicate do not do clone parts. + addPortCtrlEvents(newPart, false); + // Create an undo op. Indicate do port controller values but not clone parts. + undoOp(UndoOp::ModifyPart, part, newPart, true, false); + updateFlags |= SC_PART_MODIFIED; + + if (_recMode == REC_REPLACE) + { + iEvent si = newPart->events()->lower_bound(startTick - newPart->tick()); + iEvent ei = newPart->events()->lower_bound(newPart->endTick() - newPart->tick()); + for (iEvent i = si; i != ei; ++i) + { + Event event = i->second; + // Create an undo op. Indicate do port controller values and clone parts. + undoOp(UndoOp::DeleteEvent, event, newPart, true, true); + // Remove the event from the new part's port controller values, and do all clone parts. + removePortCtrlEvents(event, newPart, true); + } + newPart->events()->erase(si, ei); + } + + for (iEvent i = s; i != e; ++i) { + Event event = i->second; + event.setTick(event.tick() - partTick); + Event e; + // Create an undo op. Indicate do port controller values and clone parts. + undoOp(UndoOp::AddEvent, e, event, newPart, true, true); + + if(newPart->events()->find(event) == newPart->events()->end()) + newPart->events()->add(event); + + // Add the event to the new part's port controller values, and do all clone parts. + addPortCtrlEvents(event, newPart, true); + } + + + /* + if (_recMode == REC_REPLACE) + { + iEvent si = part->events()->lower_bound(startTick - part->tick()); + iEvent ei = part->events()->lower_bound(part->endTick() - part->tick()); + + for (iEvent i = si; i != ei; ++i) + { + Event event = i->second; + // Create an undo op. Indicate do port controller values and clone parts. + //undoOp(UndoOp::DeleteEvent, event, part); + undoOp(UndoOp::DeleteEvent, event, part, true, true); + + //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, part); + // } + + // Remove the event from the part's port controller values, and do all clone parts. + //removePortCtrlEvents(event, part, true); + } + part->events()->erase(si, ei); + } + + // Remove all of the part's port controller values, and do all clone parts. + removePortCtrlEvents(part, true); + + // Clone the part. This doesn't increment aref count, and doesn't chain clones. + // It also gives the new part a new serial number, but it is + // overwritten with the old one by Song::changePart(), below. + Part* newPart = part->clone(); + + endTick = 0; + for (iEvent i = s; i != e; ++i) { + Event event = i->second; + unsigned tick = event.tick() - partTick; + event.setTick(tick); + Event e; + // Create an undo op. Indicate do port controller values and clone parts. + //undoOp(UndoOp::AddEvent, e, event, newPart); + undoOp(UndoOp::AddEvent, e, event, newPart, true, true); + + // addEvent also adds port controller values. So does msgChangePart, below. Let msgChangePart handle them. + //addEvent(event, (MidiPart*)newPart); + if(newPart->events()->find(event) == newPart->events()->end()) + newPart->events()->add(event); + + if (endTick < event.tick() + event.lenTick()) + endTick = event.tick() + event.lenTick(); + } + newPart->setLenTick(endTick); // endTick - part->tick() + + //printf("Song::cmdAddRecordedEvents before changePart part:%p events:%p refs:%d Arefs:%d newPart:%p events:%p refs:%d Arefs:%d\n", part, part->events(), part->events()->refCount(), part->events()->arefCount(), newPart, newPart->events(), newPart->events()->refCount(), newPart->events()->arefCount()); + + // Change the part. + changePart(part, newPart); + // Manually adjust reference counts. + part->events()->incARef(-1); + newPart->events()->incARef(1); + // Replace the part in the clone chain with the new part. + replaceClone(part, newPart); + // Now add all of the new part's port controller values, and do all clone parts. + addPortCtrlEvents(newPart, true); + + //printf("Song::cmdAddRecordedEvents after changePart part:%p events:%p refs:%d Arefs:%d newPart:%p events:%p refs:%d Arefs:%d\n", part, part->events(), part->events()->refCount(), part->events()->arefCount(), newPart, newPart->events(), newPart->events()->refCount(), newPart->events()->arefCount()); + + //undoOp(UndoOp::ModifyPart, part, newPart); + // Create an undo op. Indicate do not do port controller values and clone parts. + undoOp(UndoOp::ModifyPart, part, newPart, false, false); + + // Removed by T356. + //part->events()->incARef(-1); + + updateFlags |= SC_PART_MODIFIED; + //printf("Song::cmdAddRecordedEvents final part:%p events:%p refs:%d Arefs:%d newPart:%p events:%p refs:%d Arefs:%d\n", part, part->events(), part->events()->refCount(), part->events()->arefCount(), newPart, newPart->events(), newPart->events()->refCount(), newPart->events()->arefCount()); + */ + + } + else { + if (_recMode == REC_REPLACE) { + iEvent si = part->events()->lower_bound(startTick - part->tick()); + iEvent ei = part->events()->lower_bound(endTick - part->tick()); + + for (iEvent i = si; i != ei; ++i) { + Event event = i->second; + // Create an undo op. Indicate that controller values and clone parts were handled. + //undoOp(UndoOp::DeleteEvent, event, part); + undoOp(UndoOp::DeleteEvent, event, part, true, true); + /* + 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, part); + } + */ + // Remove the event from the part's port controller values, and do all clone parts. + removePortCtrlEvents(event, part, true); + } + part->events()->erase(si, ei); + } + for (iEvent i = s; i != e; ++i) { + Event event = i->second; + int tick = event.tick() - partTick; + event.setTick(tick); + + // Create an undo op. Indicate that controller values and clone parts were handled. + //undoOp(UndoOp::AddEvent, event, part); + undoOp(UndoOp::AddEvent, event, part, true, true); + + //addEvent(event, part); + if(part->events()->find(event) == part->events()->end()) + part->events()->add(event); + + // Add the event to the part's port controller values, and do all clone parts. + addPortCtrlEvents(event, part, true); + } + } + } + +//--------------------------------------------------------- +// findTrack +//--------------------------------------------------------- + +MidiTrack* Song::findTrack(const Part* part) const + { + for (ciTrack t = _tracks.begin(); t != _tracks.end(); ++t) { + MidiTrack* track = dynamic_cast<MidiTrack*>(*t); + if (track == 0) + continue; + PartList* pl = track->parts(); + for (iPart p = pl->begin(); p != pl->end(); ++p) { + if (part == p->second) + return track; + } + } + return 0; + } + +//--------------------------------------------------------- +// findTrack +// find track by name +//--------------------------------------------------------- + +Track* Song::findTrack(const QString& name) const + { + for (ciTrack i = _tracks.begin(); i != _tracks.end(); ++i) { + if ((*i)->name() == name) + return *i; + } + return 0; + } + +//--------------------------------------------------------- +// setLoop +// set transport loop flag +//--------------------------------------------------------- + +void Song::setLoop(bool f) + { + if (loopFlag != f) { + loopFlag = f; + loopAction->setChecked(loopFlag); + emit loopChanged(loopFlag); + } + } + +//--------------------------------------------------------- +// clearTrackRec +//--------------------------------------------------------- +void Song::clearTrackRec() +{ + for (iTrack it = tracks()->begin(); it != tracks()->end(); ++it) + setRecordFlag(*it,false); +} + +//--------------------------------------------------------- +// setRecord +//--------------------------------------------------------- +void Song::setRecord(bool f, bool autoRecEnable) + { + if (f && museProject == museProjectInitPath ) { // check that there is a project stored before commencing + // no project, we need to create one. + if (!muse->saveAs()) + return; // could not store project, won't enable record + } + if (recordFlag != f) { + if (f && autoRecEnable) { + 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) { + setRecordFlag(selectedTrack, true); + } + else if (alreadyRecEnabled) { + // do nothing + } + else { + // if there are no tracks, do not enable record + if (!waves()->size() && !midis()->size()) { + printf("No track to select, won't enable record\n"); + f = false; + } + } + // prepare recording of wave files for all record enabled wave tracks + for (iWaveTrack i = wtl->begin(); i != wtl->end(); ++i) { + if((*i)->recordFlag()) + { + (*i)->prepareRecording(); + } + } + +#if 0 + // check for midi devices suitable for recording + bool portFound = false; + for (int i = 0; i < MIDI_PORTS; ++i) { + MidiDevice* dev = midiPorts[i].device(); + if (dev && (dev->rwFlags() & 0x2)) + portFound = true; + } + if (!portFound) { + QMessageBox::critical(qApp->mainWidget(), "MusE: Record", + "There are no midi devices configured for recording"); + f = false; + } +#endif + } + else { + bounceTrack = 0; + } + if (audio->isPlaying() && f) + f = false; + recordFlag = f; + recordAction->setChecked(recordFlag); + 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 (tempomap.setMasterFlag(cpos(), val)) + { + //audioDevice->setMaster(val); + emit songChanged(SC_MASTER); + } + // Removed. p3.3.26 + //audioDevice->setMaster(val); + } + +//--------------------------------------------------------- +// setPlay +// set transport play flag +//--------------------------------------------------------- + +void Song::setPlay(bool f) + { + if (extSyncFlag.value()) { + if (debugMsg) + printf("not allowed while using external sync"); + return; + } + // only allow the user to set the button "on" + if (!f) + playAction->setChecked(true); + else + audio->msgPlay(true); + } + +void Song::setStop(bool f) + { + if (extSyncFlag.value()) { + if (debugMsg) + printf("not allowed while using external sync"); + return; + } + // only allow the user to set the button "on" + if (!f) + stopAction->setChecked(true); + else + audio->msgPlay(false); + } + +void Song::setStopPlay(bool f) + { + playAction->blockSignals(true); + stopAction->blockSignals(true); + + emit playChanged(f); // signal transport window + + playAction->setChecked(f); + stopAction->setChecked(!f); + + stopAction->blockSignals(false); + playAction->blockSignals(false); + } + +//--------------------------------------------------------- +// swapTracks +//--------------------------------------------------------- + +void Song::swapTracks(int i1, int i2) + { + undoOp(UndoOp::SwapTrack, i1, i2); + Track* track = _tracks[i1]; + _tracks[i1] = _tracks[i2]; + _tracks[i2] = track; + } + +//--------------------------------------------------------- +// setPos +// song->setPos(Song::CPOS, pos, true, true, true); +//--------------------------------------------------------- + +void Song::setPos(int idx, const Pos& val, bool sig, + bool isSeek, bool adjustScrollbar) + { +// printf("setPos %d sig=%d,seek=%d,scroll=%d ", +// idx, sig, isSeek, adjustScrollbar); +// val.dump(0); +// printf("\n"); + + // p3.3.23 + //printf("Song::setPos before audio->msgSeek idx:%d isSeek:%d frame:%d\n", idx, isSeek, val.frame()); + if (pos[idx] == val) + return; + if (idx == CPOS) { + _vcpos = val; + if (isSeek && !extSyncFlag.value()) { + audio->msgSeek(val); + // p3.3.23 + //printf("Song::setPos after audio->msgSeek idx:%d isSeek:%d frame:%d\n", idx, isSeek, val.frame()); + 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].tick(), adjustScrollbar); + emit posChanged(RPOS, pos[RPOS].tick(), adjustScrollbar); + if (idx != LPOS && idx != RPOS) + emit posChanged(idx, pos[idx].tick(), adjustScrollbar); + } + else + emit posChanged(idx, pos[idx].tick(), adjustScrollbar); + } + + if (idx == CPOS) { + iMarker i1 = _markerList->begin(); + iMarker i2 = i1; + bool currentChanged = false; + 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 +//--------------------------------------------------------- + +void Song::forward() + { + unsigned newPos = pos[0].tick() + config.division; + audio->msgSeek(Pos(newPos, true)); + } + +//--------------------------------------------------------- +// rewind +//--------------------------------------------------------- + +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, true)); + } + +//--------------------------------------------------------- +// rewindStart +//--------------------------------------------------------- + +void Song::rewindStart() + { + // Added by T356 + //audio->msgIdle(true); + + audio->msgSeek(Pos(0, true)); + + // Added by T356 + //audio->msgIdle(false); + } + +//--------------------------------------------------------- +// update +//--------------------------------------------------------- + +void Song::update(int flags) + { + static int level = 0; // DEBUG + if (level) { + printf("Song::update %08x, level %d\n", flags, level); + return; + } + ++level; + emit songChanged(flags); + --level; + } + +//--------------------------------------------------------- +// updatePos +//--------------------------------------------------------- + +void Song::updatePos() + { + emit posChanged(0, pos[0].tick(), false); + emit posChanged(1, pos[1].tick(), false); + emit posChanged(2, pos[2].tick(), false); + } + +//--------------------------------------------------------- +// setChannelMute +// mute all midi tracks associated with channel +//--------------------------------------------------------- + +void Song::setChannelMute(int channel, bool val) + { + for (iTrack i = _tracks.begin(); i != _tracks.end(); ++i) { + MidiTrack* track = dynamic_cast<MidiTrack*>(*i); + if (track == 0) + continue; + if (track->outChannel() == channel) + track->setMute(val); + } + emit songChanged(SC_MUTE); + } + +//--------------------------------------------------------- +// len +//--------------------------------------------------------- + +void Song::initLen() + { + _len = AL::sigmap.bar2tick(40, 0, 0); // default song len + for (iTrack t = _tracks.begin(); t != _tracks.end(); ++t) { + MidiTrack* track = dynamic_cast<MidiTrack*>(*t); + if (track == 0) + continue; + PartList* parts = track->parts(); + for (iPart p = parts->begin(); p != parts->end(); ++p) { + unsigned last = p->second->tick() + p->second->lenTick(); + if (last > _len) + _len = last; + } + } + _len = roundUpBar(_len); + } + +//--------------------------------------------------------- +// tempoChanged +//--------------------------------------------------------- + +void Song::tempoChanged() +{ + emit songChanged(SC_TEMPO); +} + +//--------------------------------------------------------- +// 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() + { + 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' + // + // Rough translation: + /* + If a part is selected, edit that. + If a track is selected, edit the first + part of the track, the rest are + 'ghost parts' + When multiple parts are selected, then edit the first, + the rest are 'ghost parts' + */ + + + // 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 (ciTrack t = _tracks.begin(); t != _tracks.end(); ++t) { + if ((*t)->selected()) { + MidiTrack* track = dynamic_cast<MidiTrack*>(*t); + if (track == 0) + continue; + 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 (ciTrack t = _tracks.begin(); t != _tracks.end(); ++t) { + WaveTrack* track = dynamic_cast<WaveTrack*>(*t); + if (track == 0) + continue; + 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 (ciTrack t = _tracks.begin(); t != _tracks.end(); ++t) { + if ((*t)->selected()) { + WaveTrack* track = dynamic_cast<WaveTrack*>(*t); + if (track == 0) + continue; + PartList* pl = track->parts(); + for (iPart p = pl->begin(); p != pl->end(); ++p) + parts->add(p->second); + break; + } + } + } + return parts; + } + +void Song::setMType(MType t) + { +// printf("set MType %d\n", t); + _mtype = t; + song->update(SC_SONG_TYPE); // p4.0.7 Tim. + } + +//--------------------------------------------------------- +// beat +//--------------------------------------------------------- + +void Song::beat() + { + // Keep the sync detectors running... + for(int port = 0; port < MIDI_PORTS; ++port) + { + // Must keep them running even if there's no device... + //if(midiPorts[port].device()) + midiPorts[port].syncInfo().setTime(); + } + + + int tick = audio->tickPos(); + if (audio->isPlaying()) + setPos(0, tick, true, false, true); + + // p3.3.40 Update synth native guis at the heartbeat rate. + for(ciSynthI is = _synthIs.begin(); is != _synthIs.end(); ++is) + (*is)->guiHeartBeat(); + + while (noteFifoSize) { + int pv = recNoteFifo[noteFifoRindex]; + noteFifoRindex = (noteFifoRindex + 1) % REC_NOTE_FIFO_SIZE; + int pitch = (pv >> 8) & 0xff; + int velo = pv & 0xff; + + //--------------------------------------------------- + // filter midi remote control events + //--------------------------------------------------- + + if (rcEnable && velo != 0) { + if (pitch == rcStopNote) + setStop(true); + else if (pitch == rcRecordNote) + setRecord(true); + else if (pitch == rcGotoLeftMarkNote) + setPos(0, pos[LPOS].tick(), true, true, true); + else if (pitch == rcPlayNote) + setPlay(true); + } + emit song->midiNote(pitch, velo); + --noteFifoSize; + } + } + +//--------------------------------------------------------- +// setLen +//--------------------------------------------------------- + +void Song::setLen(unsigned l) + { + _len = l; + update(); + } + +//--------------------------------------------------------- +// addMarker +//--------------------------------------------------------- + +Marker* Song::addMarker(const QString& s, int t, bool lck) + { + Marker* marker = _markerList->add(s, t, lck); + emit markerChanged(MARKER_ADD); + return marker; + } + +//--------------------------------------------------------- +// addMarker +//--------------------------------------------------------- + +Marker* Song::getMarkerAt(int t) + { + iMarker markerI; + for (markerI=_markerList->begin(); markerI != _markerList->end(); ++markerI) { +// if (i1->second.current()) + if (unsigned(t) == markerI->second.tick())//prevent of copmiler warning: comparison signed/unsigned + return &markerI->second; + } + //Marker* marker = _markerList->add(s, t, lck); + return NULL; + } + +//--------------------------------------------------------- +// removeMarker +//--------------------------------------------------------- + +void Song::removeMarker(Marker* marker) + { + _markerList->remove(marker); + emit markerChanged(MARKER_REMOVE); + } + +Marker* Song::setMarkerName(Marker* m, const QString& s) + { + m->setName(s); + emit markerChanged(MARKER_NAME); + return m; + } + +Marker* Song::setMarkerTick(Marker* m, int t) + { + Marker mm(*m); + _markerList->remove(m); + mm.setTick(t); + m = _markerList->add(mm); + emit markerChanged(MARKER_TICK); + return m; + } + +Marker* Song::setMarkerLock(Marker* m, bool f) + { + m->setType(f ? Pos::FRAMES : Pos::TICKS); + emit markerChanged(MARKER_LOCK); + return m; + } + +//--------------------------------------------------------- +// setRecordFlag +//--------------------------------------------------------- + +void Song::setRecordFlag(Track* track, bool val) + { + if (track->type() == Track::WAVE) { + WaveTrack* audioTrack = (WaveTrack*)track; + if(!audioTrack->setRecordFlag1(val)) + return; + audio->msgSetRecord(audioTrack, val); + } + else { + track->setRecordFlag1(val); + track->setRecordFlag2(val); + } +// updateFlags |= SC_RECFLAG; + update(SC_RECFLAG); + + } + +//--------------------------------------------------------- +// rescanAlsaPorts +//--------------------------------------------------------- + +void Song::rescanAlsaPorts() + { + emit midiPortsChanged(); + } + +//--------------------------------------------------------- +// endMsgCmd +//--------------------------------------------------------- + +void Song::endMsgCmd() + { + if (updateFlags) { + redoList->clear(); // TODO: delete elements in list + undoAction->setEnabled(true); + redoAction->setEnabled(false); + emit songChanged(updateFlags); + } + } + +//--------------------------------------------------------- +// undo +//--------------------------------------------------------- + +void Song::undo() + { + updateFlags = 0; + if (doUndo1()) + return; + audio->msgUndo(); + doUndo3(); + redoAction->setEnabled(true); + undoAction->setEnabled(!undoList->empty()); + + if(updateFlags && (SC_TRACK_REMOVED | SC_TRACK_INSERTED)) + audio->msgUpdateSoloStates(); + + emit songChanged(updateFlags); + } + +//--------------------------------------------------------- +// redo +//--------------------------------------------------------- + +void Song::redo() + { + updateFlags = 0; + if (doRedo1()) + return; + audio->msgRedo(); + doRedo3(); + undoAction->setEnabled(true); + redoAction->setEnabled(!redoList->empty()); + + if(updateFlags && (SC_TRACK_REMOVED | SC_TRACK_INSERTED)) + audio->msgUpdateSoloStates(); + + emit songChanged(updateFlags); + } + +//--------------------------------------------------------- +// processMsg +// executed in realtime thread context +//--------------------------------------------------------- + +void Song::processMsg(AudioMsg* msg) + { + switch(msg->id) { + case SEQM_UPDATE_SOLO_STATES: + updateSoloStates(); + break; + case SEQM_UNDO: + doUndo2(); + break; + case SEQM_REDO: + doRedo2(); + break; + case SEQM_MOVE_TRACK: + if (msg->a > msg->b) { + for (int i = msg->a; i > msg->b; --i) { + swapTracks(i, i-1); + } + } + else { + for (int i = msg->a; i < msg->b; ++i) { + swapTracks(i, i+1); + } + } + updateFlags = SC_TRACK_MODIFIED; + break; + case SEQM_ADD_EVENT: + updateFlags = SC_EVENT_INSERTED; + if (addEvent(msg->ev1, (MidiPart*)msg->p2)) { + Event ev; + //undoOp(UndoOp::AddEvent, ev, msg->ev1, (Part*)msg->p2); + undoOp(UndoOp::AddEvent, ev, msg->ev1, (Part*)msg->p2, msg->a, msg->b); + } + else + updateFlags = 0; + if(msg->a) + addPortCtrlEvents(msg->ev1, (Part*)msg->p2, msg->b); + break; + case SEQM_REMOVE_EVENT: + { + Event event = msg->ev1; + MidiPart* part = (MidiPart*)msg->p2; + if(msg->a) + removePortCtrlEvents(event, part, msg->b); + Event e; + //undoOp(UndoOp::DeleteEvent, e, event, (Part*)part); + undoOp(UndoOp::DeleteEvent, e, event, (Part*)part, msg->a, msg->b); + deleteEvent(event, part); + updateFlags = SC_EVENT_REMOVED; + } + break; + case SEQM_CHANGE_EVENT: + if(msg->a) + removePortCtrlEvents(msg->ev1, (MidiPart*)msg->p3, msg->b); + changeEvent(msg->ev1, msg->ev2, (MidiPart*)msg->p3); + if(msg->a) + addPortCtrlEvents(msg->ev2, (Part*)msg->p3, msg->b); + //undoOp(UndoOp::ModifyEvent, msg->ev2, msg->ev1, (Part*)msg->p3); + undoOp(UndoOp::ModifyEvent, msg->ev2, msg->ev1, (Part*)msg->p3, msg->a, msg->b); + updateFlags = SC_EVENT_MODIFIED; + break; + + case SEQM_ADD_TEMPO: + //printf("processMsg (SEQM_ADD_TEMPO) UndoOp::AddTempo. adding tempo at: %d with tempo=%d\n", msg->a, msg->b); + undoOp(UndoOp::AddTempo, msg->a, msg->b); + tempomap.addTempo(msg->a, msg->b); + updateFlags = SC_TEMPO; + break; + + case SEQM_SET_TEMPO: + //printf("processMsg (SEQM_SET_TEMPO) UndoOp::AddTempo. adding tempo at: %d with tempo=%d\n", msg->a, msg->b); + undoOp(UndoOp::AddTempo, msg->a, msg->b); + tempomap.setTempo(msg->a, msg->b); + updateFlags = SC_TEMPO; + break; + + case SEQM_SET_GLOBAL_TEMPO: + tempomap.setGlobalTempo(msg->a); + break; + + case SEQM_REMOVE_TEMPO: + //printf("processMsg (SEQM_REMOVE_TEMPO) UndoOp::DeleteTempo. adding tempo at: %d with tempo=%d\n", msg->a, msg->b); + undoOp(UndoOp::DeleteTempo, msg->a, msg->b); + 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: + undoOp(UndoOp::DeleteSig, msg->a, msg->b, msg->c); + AL::sigmap.del(msg->a); + updateFlags = SC_SIG; + break; + + default: + printf("unknown seq message %d\n", msg->id); + break; + } + } + +//--------------------------------------------------------- +// cmdAddPart +//--------------------------------------------------------- + +void Song::cmdAddPart(Part* part) + { + addPart(part); + undoOp(UndoOp::AddPart, part); + updateFlags = SC_PART_INSERTED; + } + +//--------------------------------------------------------- +// cmdRemovePart +//--------------------------------------------------------- + +void Song::cmdRemovePart(Part* part) + { + removePart(part); + undoOp(UndoOp::DeletePart, part); + part->events()->incARef(-1); + //part->unchainClone(); + unchainClone(part); + updateFlags = SC_PART_REMOVED; + } + +//--------------------------------------------------------- +// cmdChangePart +//--------------------------------------------------------- + +//void Song::cmdChangePart(Part* oldPart, Part* newPart) +void Song::cmdChangePart(Part* oldPart, Part* newPart, bool doCtrls, bool doClones) + { + //printf("Song::cmdChangePart before changePart oldPart:%p events:%p refs:%d Arefs:%d sn:%d newPart:%p events:%p refs:%d Arefs:%d sn:%d\n", oldPart, oldPart->events(), oldPart->events()->refCount(), oldPart->events()->arefCount(), oldPart->sn(), newPart, newPart->events(), newPart->events()->refCount(), newPart->events()->arefCount(), newPart->sn()); + + if(doCtrls) + removePortCtrlEvents(oldPart, doClones); + + changePart(oldPart, newPart); + + //undoOp(UndoOp::ModifyPart, oldPart, newPart); + undoOp(UndoOp::ModifyPart, oldPart, newPart, doCtrls, doClones); + + // Changed by T356. Do not decrement ref count if the new part is a clone of the old part, since the event list + // will still be active. + if(oldPart->cevents() != newPart->cevents()) + oldPart->events()->incARef(-1); + + //oldPart->replaceClone(newPart); + + //printf("Song::cmdChangePart before repl/unchClone oldPart:%p events:%p refs:%d Arefs:%d sn:%d newPart:%p events:%p refs:%d Arefs:%d sn:%d\n", oldPart, oldPart->events(), oldPart->events()->refCount(), oldPart->events()->arefCount(), oldPart->sn(), newPart, newPart->events(), newPart->events()->refCount(), newPart->events()->arefCount(), newPart->sn()); + + replaceClone(oldPart, newPart); + + if(doCtrls) + addPortCtrlEvents(newPart, doClones); + + //printf("Song::cmdChangePart after repl/unchClone oldPart:%p events:%p refs:%d Arefs:%d sn:%d newPart:%p events:%p refs:%d Arefs:%d sn:%d\n", oldPart, oldPart->events(), oldPart->events()->refCount(), oldPart->events()->arefCount(), oldPart->sn(), newPart, newPart->events(), newPart->events()->refCount(), newPart->events()->arefCount(), newPart->sn()); + + updateFlags = SC_PART_MODIFIED; + } + +//--------------------------------------------------------- +// 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) + { + if(debugMsg) + printf("Song::clear\n"); + + bounceTrack = 0; + + _tracks.clear(); + _midis.clearDelete(); + _waves.clearDelete(); + _inputs.clearDelete(); // audio input ports + _outputs.clearDelete(); // audio output ports + _groups.clearDelete(); // mixer groups + _auxs.clearDelete(); // aux sends + + // p3.3.45 Clear all midi port devices. + for(int i = 0; i < MIDI_PORTS; ++i) + { + // p3.3.50 Since midi ports are not deleted, clear all midi port in/out routes. They point to non-existant tracks now. + midiPorts[i].inRoutes()->clear(); + midiPorts[i].outRoutes()->clear(); + + // p3.3.50 Reset this. + midiPorts[i].setFoundInSongFile(false); + + // This will also close the device. + midiPorts[i].setMidiDevice(0); + } + + _synthIs.clearDelete(); + + // p3.3.45 Make sure to delete Jack midi devices, and remove all ALSA midi device routes... + // Otherwise really nasty things happen when loading another song when one is already loaded. + // The loop is a safe way to delete while iterating. + bool loop; + do + { + loop = false; + for(iMidiDevice imd = midiDevices.begin(); imd != midiDevices.end(); ++imd) + { + //if((*imd)->deviceType() == MidiDevice::JACK_MIDI) + if(dynamic_cast< MidiJackDevice* >(*imd)) + { + // Remove the device from the list. + midiDevices.erase(imd); + // Since Jack midi devices are created dynamically, we must delete them. + // The destructor unregisters the device from Jack, which also disconnects all device-to-jack routes. + // This will also delete all midi-track-to-device routes, they point to non-existant midi tracks + // which were all deleted above + delete (*imd); + loop = true; + break; + } + else + //if((*imd)->deviceType() == MidiDevice::ALSA_MIDI) + if(dynamic_cast< MidiAlsaDevice* >(*imd)) + { + // With alsa devices, we must not delete them (they're always in the list). But we must + // clear all routes. They point to non-existant midi tracks, which were all deleted above. + (*imd)->inRoutes()->clear(); + (*imd)->outRoutes()->clear(); + } + } + } + while (loop); + + tempomap.clear(); + AL::sigmap.clear(); + undoList->clearDelete(); + redoList->clear(); + _markerList->clear(); + pos[0].setTick(0); + pos[1].setTick(0); + pos[2].setTick(0); + _vcpos.setTick(0); + + Track::clearSoloRefCounts(); + clearMidiTransforms(); + clearMidiInputTransforms(); + + // Clear all midi port controller values. + for(int i = 0; i < MIDI_PORTS; ++i) + // Don't remove the controllers, just the values. + midiPorts[i].controller()->clearDelete(false); + + _masterFlag = true; + loopFlag = false; + loopFlag = false; + punchinFlag = false; + punchoutFlag = false; + recordFlag = false; + soloFlag = false; + // seq + _mtype = MT_UNKNOWN; + _recMode = REC_OVERDUP; + _cycleMode = CYCLE_NORMAL; + _click = false; + _quantize = false; + _len = 0; // song len in ticks + _follow = JUMP; + // _tempo = 500000; // default tempo 120 + dirty = false; + initDrumMap(); + if (signal) { + emit loopChanged(false); + recordChanged(false); + } + } + +//--------------------------------------------------------- +// cleanupForQuit +// called from Muse::closeEvent +//--------------------------------------------------------- + +void Song::cleanupForQuit() +{ + bounceTrack = 0; + + if(debugMsg) + printf("MusE: Song::cleanupForQuit...\n"); + + _tracks.clear(); + + if(debugMsg) + printf("deleting _midis\n"); + _midis.clearDelete(); + + if(debugMsg) + printf("deleting _waves\n"); + _waves.clearDelete(); + + if(debugMsg) + printf("deleting _inputs\n"); + _inputs.clearDelete(); // audio input ports + + if(debugMsg) + printf("deleting _outputs\n"); + _outputs.clearDelete(); // audio output ports + + if(debugMsg) + printf("deleting _groups\n"); + _groups.clearDelete(); // mixer groups + + if(debugMsg) + printf("deleting _auxs\n"); + _auxs.clearDelete(); // aux sends + + if(debugMsg) + printf("deleting _synthIs\n"); + _synthIs.clearDelete(); // each ~SynthI() -> deactivate3() -> ~SynthIF() + + tempomap.clear(); + AL::sigmap.clear(); + + if(debugMsg) + printf("deleting undoList, clearing redoList\n"); + undoList->clearDelete(); + redoList->clear(); // Check this - Should we do a clearDelete? IIRC it was OK this way - no clearDelete in case of same items in both lists. + + _markerList->clear(); + + _tviews.clear(); + + if(debugMsg) + printf("deleting transforms\n"); + clearMidiTransforms(); // Deletes stuff. + clearMidiInputTransforms(); // Deletes stuff. + + if(debugMsg) + printf("deleting midiport controllers\n"); + // Clear all midi port controllers and values. + for(int i = 0; i < MIDI_PORTS; ++i) + // Remove the controllers and the values. + midiPorts[i].controller()->clearDelete(true); + + // Can't do this here. Jack isn't running. Fixed. Test OK so far. + #if 1 + if(debugMsg) + printf("deleting midi devices except synths\n"); + for(iMidiDevice imd = midiDevices.begin(); imd != midiDevices.end(); ++imd) + { + // Since Syntis are midi devices, there's no need to delete them below. + if((*imd)->isSynti()) + continue; + delete (*imd); + } + midiDevices.clear(); // midi devices + #endif + + if(debugMsg) + printf("deleting global available synths\n"); + // Delete all synths. + std::vector<Synth*>::iterator is; + for(is = synthis.begin(); is != synthis.end(); ++is) + { + Synth* s = *is; + + if(s) + delete s; + } + synthis.clear(); + + if(debugMsg) + printf("deleting midi instruments\n"); + for(iMidiInstrument imi = midiInstruments.begin(); imi != midiInstruments.end(); ++imi) + { + // Since Syntis are midi instruments, there's no need to delete them below. + // Tricky, must cast as SynthI*. + SynthI* s = dynamic_cast <SynthI*> (*imi); + if(s) + continue; + delete (*imi); + } + midiInstruments.clear(); // midi devices + + // Nothing required for ladspa plugin list, and rack instances of them + // are handled by ~AudioTrack. + + if(debugMsg) + printf("...finished cleaning up.\n"); +} + +//--------------------------------------------------------- +// 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; + } + for (int i = 0; i < n; ++i) { +// printf("seqSignal to gui:<%c>\n", buffer[i]); + switch(buffer[i]) { + case '0': // STOP + stopRolling(); + break; + case '1': // PLAY + setStopPlay(true); + break; + case '2': // record + setRecord(true); + break; + case '3': // START_PLAY + jack STOP + abortRolling(); + break; + case 'P': // alsa ports changed + rescanAlsaPorts(); + break; + case 'G': + clearRecAutomation(true); + setPos(0, audio->tickPos(), true, false, true); + break; + case 'S': // shutdown audio + muse->seqStop(); + + { + // 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"); + if (btn == 0) { + printf("restarting!\n"); + muse->seqRestart(); + } + } + + break; + case 'f': // start freewheel + if(debugMsg) + printf("Song: seqSignal: case f: setFreewheel start\n"); + + // Enabled by Tim. p3.3.6 + if(config.freewheelMode) + audioDevice->setFreewheel(true); + + break; + + case 'F': // stop freewheel + if(debugMsg) + printf("Song: seqSignal: case F: setFreewheel stop\n"); + + // Enabled by Tim. p3.3.6 + if(config.freewheelMode) + audioDevice->setFreewheel(false); + + audio->msgPlay(false); +#if 0 + if (record()) + audio->recordStop(); + setStopPlay(false); +#endif + break; + + case 'C': // Graph changed + if (audioDevice) + audioDevice->graphChanged(); + break; + + // p3.3.37 + case 'R': // Registration changed + if (audioDevice) + audioDevice->registrationChanged(); + break; + + default: + printf("unknown Seq Signal <%c>\n", buffer[i]); + break; + } + } + } + +//--------------------------------------------------------- +// recordEvent +//--------------------------------------------------------- + +void Song::recordEvent(MidiTrack* mt, Event& event) + { + //--------------------------------------------------- + // if tick points into a part, + // record to that part + // else + // create new part + //--------------------------------------------------- + + unsigned tick = event.tick(); + PartList* pl = mt->parts(); + MidiPart* part = 0; + iPart ip; + for (ip = pl->begin(); ip != pl->end(); ++ip) { + part = (MidiPart*)(ip->second); + unsigned partStart = part->tick(); + unsigned partEnd = partStart + part->lenTick(); + if (tick >= partStart && tick < partEnd) + break; + } + updateFlags |= SC_EVENT_INSERTED; + if (ip == pl->end()) { + // create new part + part = new MidiPart(mt); + int startTick = roundDownBar(tick); + //int endTick = roundUpBar(tick); + int endTick = roundUpBar(tick + 1); + part->setTick(startTick); + part->setLenTick(endTick - startTick); + part->setName(mt->name()); + event.move(-startTick); + part->events()->add(event); + audio->msgAddPart(part); + return; + } + part = (MidiPart*)(ip->second); + tick -= part->tick(); + event.setTick(tick); + + Event ev; + if(event.type() == Controller) + { + EventRange range = part->events()->equal_range(tick); + for(iEvent i = range.first; i != range.second; ++i) + { + ev = i->second; + // At the moment, Song::recordEvent() is only called by the 'Rec' buttons in the + // midi track info panel. So only controller types are fed to it. If other event types + // are to be passed, we will have to expand on this to check if equal. Instead, maybe add an isEqual() to Event class. + //if((ev.type() == Controller && event.type() == Controller || ev.type() == Controller && event.type() == Controller) + // && ev.dataA() == event.dataA() && ev.dataB() == event.dataB()) + if(ev.type() == Controller && ev.dataA() == event.dataA()) + { + // Don't bother if already set. + if(ev.dataB() == event.dataB()) + return; + // Indicate do undo, and do port controller values and clone parts. + audio->msgChangeEvent(ev, event, part, true, true, true); + return; + } + } + } + + // Indicate do undo, and do port controller values and clone parts. + //audio->msgAddEvent(event, part); + audio->msgAddEvent(event, part, true, true, true); + } + +//--------------------------------------------------------- +// execAutomationCtlPopup +//--------------------------------------------------------- + +int Song::execAutomationCtlPopup(AudioTrack* track, const QPoint& menupos, int acid) +{ + //enum { HEADER, SEP1, PREV_EVENT, NEXT_EVENT, SEP2, ADD_EVENT, CLEAR_EVENT, CLEAR_RANGE, CLEAR_ALL_EVENTS }; + enum { HEADER, PREV_EVENT, NEXT_EVENT, SEP2, ADD_EVENT, CLEAR_EVENT, CLEAR_RANGE, CLEAR_ALL_EVENTS }; + QMenu* menu = new QMenu; + + int count = 0; + bool isEvent = false, canSeekPrev = false, canSeekNext = false, canEraseRange = false; + bool canAdd = false; + double ctlval = 0.0; + if(track) + { + ciCtrlList icl = track->controller()->find(acid); + if(icl != track->controller()->end()) + { + CtrlList *cl = icl->second; + canAdd = true; + ctlval = cl->curVal(); + count = cl->size(); + if(count) + { + int frame = pos[0].frame(); + + iCtrl s = cl->lower_bound(frame); + iCtrl e = cl->upper_bound(frame); + + isEvent = (s != cl->end() && s->second.frame == frame); + + canSeekPrev = s != cl->begin(); + canSeekNext = e != cl->end(); + + s = cl->lower_bound(pos[1].frame()); + + canEraseRange = s != cl->end() + && (int)pos[2].frame() > s->second.frame; + } + } + } + + //menu->insertItem(tr("Automation:"), HEADER, HEADER); + //menu->setItemEnabled(HEADER, false); + //MenuTitleItem* title = new MenuTitleItem(tr("Automation:")); ddskrjo + //menu->insertItem(title, HEADER, HEADER); ddskrjo + menu->addAction(new MenuTitleItem(tr("Automation:"), menu)); + + //menu->insertSeparator(SEP1); + + QAction* prevEvent = menu->addAction(tr("previous event")); + prevEvent->setData(PREV_EVENT); + prevEvent->setEnabled(canSeekPrev); + + QAction* nextEvent = menu->addAction(tr("next event")); + nextEvent->setData(NEXT_EVENT); + nextEvent->setEnabled(canSeekNext); + + //menu->insertSeparator(SEP2); + menu->addSeparator(); + + QAction* addEvent = new QAction(menu); + menu->addAction(addEvent); + if(isEvent) + addEvent->setText(tr("set event")); + else + addEvent->setText(tr("add event")); + addEvent->setData(ADD_EVENT); + addEvent->setEnabled(canAdd); + + QAction* eraseEventAction = menu->addAction(tr("erase event")); + eraseEventAction->setData(CLEAR_EVENT); + eraseEventAction->setEnabled(isEvent); + + QAction* eraseRangeAction = menu->addAction(tr("erase range")); + eraseRangeAction->setData(CLEAR_RANGE); + eraseRangeAction->setEnabled(canEraseRange); + + QAction* clearAction = menu->addAction(tr("clear automation")); + clearAction->setData(CLEAR_ALL_EVENTS); + clearAction->setEnabled((bool)count); + + QAction* act = menu->exec(menupos); + //delete menu; + if (!act || !track) + { + delete menu; + return -1; + } + + //if(!track) + // return -1; + + int sel = act->data().toInt(); + delete menu; + + switch(sel) + { + case ADD_EVENT: + audio->msgAddACEvent(track, acid, pos[0].frame(), ctlval); + break; + case CLEAR_EVENT: + audio->msgEraseACEvent(track, acid, pos[0].frame()); + break; + + case CLEAR_RANGE: + audio->msgEraseRangeACEvents(track, acid, pos[1].frame(), pos[2].frame()); + break; + + case CLEAR_ALL_EVENTS: + if(QMessageBox::question(muse, QString("Muse"), + tr("Clear all controller events?"), tr("&Ok"), tr("&Cancel"), + QString::null, 0, 1 ) == 0) + audio->msgClearControllerEvents(track, acid); + break; + + case PREV_EVENT: + audio->msgSeekPrevACEvent(track, acid); + break; + + case NEXT_EVENT: + audio->msgSeekNextACEvent(track, acid); + break; + + default: + return -1; + break; + } + + return sel; +} + +//--------------------------------------------------------- +// execMidiAutomationCtlPopup +//--------------------------------------------------------- + +int Song::execMidiAutomationCtlPopup(MidiTrack* track, MidiPart* part, const QPoint& menupos, int ctlnum) +{ + if(!track && !part) + return -1; + + //enum { HEADER, SEP1, PREV_EVENT, NEXT_EVENT, SEP2, ADD_EVENT, CLEAR_EVENT, CLEAR_RANGE, CLEAR_ALL_EVENTS }; + enum { HEADER, ADD_EVENT, CLEAR_EVENT }; + QMenu* menu = new QMenu; + + //int count = 0; + bool isEvent = false; + //bool canSeekPrev = false, canSeekNext = false, canEraseRange = false; + //bool canAdd = false; + //double ctlval = 0.0; + + MidiTrack* mt; + if(track) + mt = track; + else + mt = (MidiTrack*)part->track(); + int portno = mt->outPort(); + int channel = mt->outChannel(); + MidiPort* mp = &midiPorts[portno]; + + int dctl = ctlnum; + // Is it a drum controller, according to the track port's instrument? + MidiController *mc = mp->drumController(ctlnum); + if(mc) + { + // Change the controller event's index into the drum map to an instrument note. + int note = ctlnum & 0x7f; + dctl &= ~0xff; + channel = drumMap[note].channel; + mp = &midiPorts[drumMap[note].port]; + dctl |= drumMap[note].anote; + } + + //printf("Song::execMidiAutomationCtlPopup ctlnum:%d dctl:%d anote:%d\n", ctlnum, dctl, drumMap[ctlnum & 0x7f].anote); + + unsigned tick = cpos(); + + if(!part) + { + PartList* pl = mt->parts(); + iPart ip; + for(ip = pl->begin(); ip != pl->end(); ++ip) + { + MidiPart* tpart = (MidiPart*)(ip->second); + unsigned partStart = tpart->tick(); + unsigned partEnd = partStart + tpart->lenTick(); + if(tick >= partStart && tick < partEnd) + { + // Prefer a selected part, otherwise keep looking... + if(tpart->selected()) + { + part = tpart; + break; + } + else + // Remember the first part found... + if(!part) + part = tpart; + } + } + } + + Event ev; + if(part) + { + unsigned partStart = part->tick(); + unsigned partEnd = partStart + part->lenTick(); + if(tick >= partStart && tick < partEnd) + { + EventRange range = part->events()->equal_range(tick - partStart); + for(iEvent i = range.first; i != range.second; ++i) + { + ev = i->second; + if(ev.type() == Controller) + { + //printf("Song::execMidiAutomationCtlPopup ev.dataA:%d\n", ev.dataA()); + + //if(ev.dataA() == dctl) + if(ev.dataA() == ctlnum) + { + isEvent = true; + break; + } + } + } + } + } + + + //menu->insertItem(tr("Automation:"), HEADER, HEADER); + //menu->setItemEnabled(HEADER, false); + //MenuTitleItem* title = new MenuTitleItem(tr("Automation:")); ddskrjo + ///menu->insertItem(title, HEADER, HEADER); ddskrjo + + //menu->insertSeparator(SEP1); + +// menu->insertItem(tr("previous event"), PREV_EVENT, PREV_EVENT); +// menu->setItemEnabled(PREV_EVENT, canSeekPrev); + +// menu->insertItem(tr("next event"), NEXT_EVENT, NEXT_EVENT); +// menu->setItemEnabled(NEXT_EVENT, canSeekNext); + +// menu->insertSeparator(SEP2); + + QAction* addEvent = new QAction(menu); + menu->addAction(addEvent); + if(isEvent) + addEvent->setText(tr("set event")); + else + addEvent->setText(tr("add event")); + addEvent->setData(ADD_EVENT); + //addEvent->setEnabled(canAdd); + addEvent->setEnabled(true); + + QAction* eraseEventAction = menu->addAction(tr("erase event")); + eraseEventAction->setData(CLEAR_EVENT); + eraseEventAction->setEnabled(isEvent); + +// menu->insertItem(tr("erase range"), CLEAR_RANGE, CLEAR_RANGE); +// menu->setItemEnabled(CLEAR_RANGE, canEraseRange); + +// menu->insertItem(tr("clear automation"), CLEAR_ALL_EVENTS, CLEAR_ALL_EVENTS); +// menu->setItemEnabled(CLEAR_ALL_EVENTS, (bool)count); + + + QAction* act = menu->exec(menupos); + //delete menu; + if (!act) + { + delete menu; + return -1; + } + + //if(!part) + // return -1; + + int sel = act->data().toInt(); + delete menu; + + switch(sel) + { + case ADD_EVENT: + { + //int val = mp->hwCtrlState(channel, ctlnum); + int val = mp->hwCtrlState(channel, dctl); + if(val == CTRL_VAL_UNKNOWN) + return -1; + Event e(Controller); + //e.setA(dctl); + e.setA(ctlnum); + e.setB(val); + // Do we replace an old event? + if(isEvent) + { + // Don't bother if already set. + if(ev.dataB() == val) + return -1; + + e.setTick(tick - part->tick()); + // Indicate do undo, and do port controller values and clone parts. + audio->msgChangeEvent(ev, e, part, true, true, true); + } + else + { + // Store a new event... + if(part) + { + e.setTick(tick - part->tick()); + // Indicate do undo, and do port controller values and clone parts. + audio->msgAddEvent(e, part, true, true, true); + } + else + { + // Create a new part... + part = new MidiPart(mt); + int startTick = roundDownBar(tick); + int endTick = roundUpBar(tick + 1); + part->setTick(startTick); + part->setLenTick(endTick - startTick); + part->setName(mt->name()); + e.setTick(tick - startTick); + part->events()->add(e); + // Allow undo. + audio->msgAddPart(part); + } + } + } + break; + case CLEAR_EVENT: + // Indicate do undo, and do port controller values and clone parts. + audio->msgDeleteEvent(ev, part, true, true, true); + break; + + //case CLEAR_RANGE: + //audio->msgEraseRangeACEvents(track, acid, pos[1].frame(), pos[2].frame()); + //break; + + //case CLEAR_ALL_EVENTS: + //if(QMessageBox::question(muse, QString("Muse"), + // tr("Clear all controller events?"), tr("&Ok"), tr("&Cancel"), + // QString::null, 0, 1 ) == 0) + //audio->msgClearControllerEvents(track, acid); + //break; + + //case PREV_EVENT: + //audio->msgSeekPrevACEvent(track, acid); + //break; + + //case NEXT_EVENT: + //audio->msgSeekNextACEvent(track, acid); + //break; + + default: + return -1; + break; + } + + return sel; +} + +//--------------------------------------------------------- +// updateSoloStates +// This will properly set all soloing variables (including other tracks) based entirely +// on the current values of all the tracks' _solo members. +//--------------------------------------------------------- + +void Song::updateSoloStates() +{ + Track::clearSoloRefCounts(); + for(ciTrack i = _tracks.begin(); i != _tracks.end(); ++i) + (*i)->setInternalSolo(0); + for(ciTrack i = _tracks.begin(); i != _tracks.end(); ++i) + (*i)->updateSoloStates(true); +} + +//--------------------------------------------------------- +// clearRecAutomation +//--------------------------------------------------------- + +void Song::clearRecAutomation(bool clearList) +{ + // Clear all pan/vol pressed and touched flags, and all rec event lists, if needed. + for (iTrack it = tracks()->begin(); it != tracks()->end(); ++it) + ((Track*)(*it))->clearRecAutomation(clearList); +} + +//--------------------------------------------------------- +// processAutomationEvents +//--------------------------------------------------------- + +void Song::processAutomationEvents() +{ + // Just clear all pressed and touched flags, not rec event lists. + clearRecAutomation(false); + if (!automation) + return; + for(iTrack i = _tracks.begin(); i != _tracks.end(); ++i) + { + if(!(*i)->isMidiTrack()) + // Process (and clear) rec events. + ((AudioTrack*)(*i))->processAutomationEvents(); + } +} + +//--------------------------------------------------------- +// abortRolling +//--------------------------------------------------------- + +void Song::abortRolling() +{ + if (record()) + audio->recordStop(); + setStopPlay(false); +} + +//--------------------------------------------------------- +// stopRolling +//--------------------------------------------------------- + +void Song::stopRolling() + { + abortRolling(); + processAutomationEvents(); + } + +//--------------------------------------------------------- +// connectJackRoutes +//--------------------------------------------------------- + +void Song::connectJackRoutes(AudioTrack* track, bool disconnect) +{ + switch(track->type()) + { + case Track::AUDIO_OUTPUT: + { + AudioOutput* ao = (AudioOutput*)track; + // This will re-register the track's jack ports. + if(!disconnect) + ao->setName(ao->name()); + // Now reconnect the output routes. + if(checkAudioDevice() && audio->isRunning()) + { + for(int ch = 0; ch < ao->channels(); ++ch) + { + RouteList* ir = ao->outRoutes(); + for (iRoute ii = ir->begin(); ii != ir->end(); ++ii) + { + Route r = *ii; + if ((r.type == Route::JACK_ROUTE) && (r.channel == ch)) + { + if(disconnect) + audioDevice->disconnect(ao->jackPort(ch), r.jackPort); + else + audioDevice->connect(ao->jackPort(ch), r.jackPort); + break; + } + } + if(disconnect) + { + audioDevice->unregisterPort(ao->jackPort(ch)); + ao->setJackPort(ch, 0); + } + } + } + } + break; + case Track::AUDIO_INPUT: + { + AudioInput* ai = (AudioInput*)track; + // This will re-register the track's jack ports. + if(!disconnect) + ai->setName(ai->name()); + // Now reconnect the input routes. + if(checkAudioDevice() && audio->isRunning()) + { + for(int ch = 0; ch < ai->channels(); ++ch) + { + RouteList* ir = ai->inRoutes(); + for (iRoute ii = ir->begin(); ii != ir->end(); ++ii) + { + Route r = *ii; + if ((r.type == Route::JACK_ROUTE) && (r.channel == ch)) + { + if(disconnect) + audioDevice->disconnect(r.jackPort, ai->jackPort(ch)); + else + audioDevice->connect(r.jackPort, ai->jackPort(ch)); + break; + } + } + if(disconnect) + { + audioDevice->unregisterPort(ai->jackPort(ch)); + ai->setJackPort(ch, 0); + } + } + } + } + break; + default: + break; + } +} + +/* +//--------------------------------------------------------- +// chooseMidiRoutes +//--------------------------------------------------------- + +void Song::chooseMidiRoutes(QButton* parent, MidiTrack* track, bool dst) +{ + if(!track) + return; + + //if(!track->isMidiTrack()) + // return; + + QPoint ppt = QCursor::pos(); + //QPoint ppt = parent->rect().bottomLeft(); + + //if(dst) + //{ + // TODO + + //} + //else + //{ + RouteList* rl = dst ? track->outRoutes() : track->inRoutes(); + //Route dst(track, -1); + + QPopupMenu* pup = new QPopupMenu(parent); + pup->setCheckable(true); + + int gid = 0; + int n; + + // FIXME: + // Routes can't be re-read until the message sent from msgAddRoute1() + // has had time to be sent and actually affected the routes. + ///_redisplay: + + pup->clear(); + gid = 0; + + //MidiInPortList* tl = song->midiInPorts(); + //for(iMidiInPort i = tl->begin();i != tl->end(); ++i) + for(int i = 0; i < MIDI_PORTS; ++i) + { + //MidiInPort* track = *i; + // NOTE: Could possibly list all devices, bypassing ports, but no, let's stick wth ports. + MidiPort* mp = &midiPorts[i]; + MidiDevice* md = mp->device(); + if(!md) + continue; + + if(!(md->rwFlags() & (dst ? 1 : 2))) + continue; + + //printf("MidiStrip::iRoutePressed adding submenu portnum:%d\n", i); + + //QMenu* m = menu->addMenu(track->name()); + QPopupMenu* subp = new QPopupMenu(parent); + + for(int ch = 0; ch < MIDI_CHANNELS; ++ch) + { + //QAction* a = m->addAction(QString("Channel %1").arg(ch+1)); + //subp->insertItem(QT_TRANSLATE_NOOP("@default", QString("Channel %1").arg(ch+1)), i * MIDI_CHANNELS + ch); + gid = i * MIDI_CHANNELS + ch; + + //printf("MidiStrip::iRoutePressed inserting gid:%d\n", gid); + + subp->insertItem(QString("Channel %1").arg(ch+1), gid); + //a->setCheckable(true); + //Route src(track, ch, RouteNode::TRACK); + //Route src(md, ch); + //Route r = Route(src, dst); + //a->setData(QVariant::fromValue(r)); + //a->setChecked(rl->indexOf(r) != -1); + Route srcRoute(md, ch); + for(iRoute ir = rl->begin(); ir != rl->end(); ++ir) + { + //if(*ir == dst) + if(*ir == srcRoute) + { + subp->setItemChecked(gid, true); + break; + } + } + } + pup->insertItem(QT_TRANSLATE_NOOP("@default", md->name()), subp); + } + +// QPopupMenu* pup = new QPopupMenu(iR); +// pup->setCheckable(true); + //MidiTrack* t = (MidiTrack*)track; +// RouteList* irl = track->inRoutes(); + +// MidiTrack* t = (MidiTrack*)track; +// int gid = 0; +// for (int i = 0; i < channel; ++i) +// { +// char buffer[128]; +// snprintf(buffer, 128, "%s %d", tr("Channel").toLatin1().constData(), i+1); +// MenuTitleItem* titel = new MenuTitleItem(QString(buffer)); +// pup->insertItem(titel); + +// if (!checkAudioDevice()) return; +// std::list<QString> ol = audioDevice->outputPorts(); +// for (std::list<QString>::iterator ip = ol.begin(); ip != ol.end(); ++ip) { +// int id = pup->insertItem(*ip, (gid * 16) + i); +// Route dst(*ip, true, i); +// ++gid; +// for (iRoute ir = irl->begin(); ir != irl->end(); ++ir) { +// if (*ir == dst) { +// pup->setItemChecked(id, true); +// break; +// } +// } +// } +// if (i+1 != channel) +// pup->insertSeparator(); +// } + + if(pup->count() == 0) + { + delete pup; + return; + } + + //n = pup->exec(QCursor::pos()); + n = pup->exec(ppt); + ///delete pup; + if (n != -1) + { + int mdidx = n / MIDI_CHANNELS; + int ch = n % MIDI_CHANNELS; + + //if(debugMsg) + //printf("Song::chooseMidiRoutes mdidx:%d ch:%d\n", mdidx, ch); + + MidiPort* mp = &midiPorts[mdidx]; + MidiDevice* md = mp->device(); + if(!md) + { + delete pup; + return; + } + + //if(!(md->rwFlags() & 2)) + if(!(md->rwFlags() & (dst ? 1 : 2))) + { + delete pup; + return; + } + + //QString s(pup->text(n)); + //QT_TRANSLATE_NOOP("@default", md->name()) + + //Route srcRoute(s, false, -1); + Route aRoute(md, ch); + //Route srcRoute(md, -1); + //Route dstRoute(track, -1); + Route bRoute(track, ch); + + //if (track->type() == Track::AUDIO_INPUT) + // srcRoute.channel = dstRoute.channel = n & 0xf; + iRoute iir = rl->begin(); + for (; iir != rl->end(); ++iir) + { + //if(*iir == (dst ? bRoute : aRoute)) + if(*iir == aRoute) + break; + } + if (iir != rl->end()) + { + // disconnect + if(dst) + { + //printf("Song::chooseMidiRoutes removing route src track name: %s dst device name: %s\n", track->name().toLatin1().constData(), md->name().toLatin1().constData()); + audio->msgRemoveRoute(bRoute, aRoute); + } + else + { + //printf("Song::chooseMidiRoutes removing route src device name: %s dst track name: %s\n", md->name().toLatin1().constData(), track->name().toLatin1().constData()); + audio->msgRemoveRoute(aRoute, bRoute); + } + } + else + { + // connect + if(dst) + { + //printf("Song::chooseMidiRoutes adding route src track name: %s dst device name: %s\n", track->name().toLatin1().constData(), md->name().toLatin1().constData()); + audio->msgAddRoute(bRoute, aRoute); + } + else + { + //printf("Song::chooseMidiRoutes adding route src device name: %s dst track name: %s\n", md->name().toLatin1().constData(), track->name().toLatin1().constData()); + audio->msgAddRoute(aRoute, bRoute); + } + } + + //printf("Song::chooseMidiRoutes calling msgUpdateSoloStates\n"); + audio->msgUpdateSoloStates(); + //printf("Song::chooseMidiRoutes calling song->update\n"); + song->update(SC_ROUTE); + + // p3.3.46 + ///goto _redisplay; + } + delete pup; + parent->setDown(false); // pup->exec() catches mouse release event + //printf("Song::chooseMidiRoutes end\n"); + + //} +} +*/ + +//--------------------------------------------------------- +// insertTrackView +// add a new trackview for the arranger +//--------------------------------------------------------- + +void Song::insertTrackView(TrackView* tv, int idx) +{ + iTrackView i = _tviews.index2iterator(idx); + _tviews.insert(i, tv); +} + +//--------------------------------------------------------- +// cmdRemoveTrackView +//--------------------------------------------------------- + +void Song::cmdRemoveTrackView(TrackView* tv) +{ + int idx = _tviews.index(tv); + //undoOp(UndoOp::DeleteTrackView, idx, tv); + removeTrackView(tv); + updateFlags |= SC_TRACKVIEW_REMOVED; +} + +//--------------------------------------------------------- +// removeTrackView +// add a new trackview for the arranger +//--------------------------------------------------------- + +void Song::removeTrackView(TrackView* tv) +{ + _tviews.erase(tv); +} + +//--------------------------------------------------------- +// addNewTrackView +// add a new trackview for the arranger +//--------------------------------------------------------- + +TrackView* Song::addNewTrackView(int idx) +{ + TrackView* tv = addTrackView((Track::TrackType)idx); + return tv; +} + +//---------------------------------------------------------/*{{{*/ +// addTrackView +// called from GUI context +//--------------------------------------------------------- + +TrackView* Song::addTrackView(int t) +{ + Track::TrackType type = (Track::TrackType) t; + TrackView* tv = new TrackView(); + switch(type) { + case Track::MIDI: + tv->setType(Track::MIDI); + break; + case Track::DRUM: + tv->setType(Track::MIDI); + break; + case Track::WAVE: + tv->setType(Track::WAVE); + break; + case Track::AUDIO_OUTPUT: + tv->setType(Track::AUDIO_OUTPUT); + break; + case Track::AUDIO_GROUP: + tv->setType(Track::AUDIO_GROUP); + break; + case Track::AUDIO_AUX: + tv->setType(Track::AUDIO_AUX); + break; + case Track::AUDIO_INPUT: + tv->setType(Track::AUDIO_INPUT); + break; + case Track::AUDIO_SOFTSYNTH: + printf("not implemented: Song::addTrackView(SOFTSYNTH)\n"); + break; + default: + printf("Song::addTrackView() illegal type %d\n", type); + abort(); + } + tv->setDefaultName(); + //msgInsertTrackView(tv, -1, true); + + return tv; +}/*}}}*/ + +//--------------------------------------------------------- +// findTrackView +// find track view by name +//--------------------------------------------------------- + +TrackView* Song::findTrackView(const QString& name) const +{ + for (ciTrackView i = _tviews.begin(); i != _tviews.end(); ++i) + { + if ((*i)->viewName() == name) + return *i; + } + return 0; +} + + +//--------------------------------------------------------- +// insertTrack0 +//--------------------------------------------------------- + +void Song::insertTrack0(Track* track, int idx) + { + insertTrack1(track, idx); + insertTrack2(track, idx); // audio->msgInsertTrack(track, idx, false); + insertTrack3(track, idx); + } + +//--------------------------------------------------------- +// insertTrack1 +// non realtime part of insertTrack +//--------------------------------------------------------- + +void Song::insertTrack1(Track* track, int /*idx*/) + { + //printf("Song::insertTrack1 track:%lx\n", track); + + switch(track->type()) { + case Track::AUDIO_SOFTSYNTH: + { + SynthI* s = (SynthI*)track; + Synth* sy = s->synth(); + if (!s->isActivated()) { + s->initInstance(sy, s->name()); + } + } + break; + default: + break; + } + + //printf("Song::insertTrack1 end of function\n"); + + } + +//--------------------------------------------------------- +// insertTrack2 +// realtime part +//--------------------------------------------------------- + +void Song::insertTrack2(Track* track, int idx) +{ + //printf("Song::insertTrack2 track:%lx\n", track); + + int n; + switch(track->type()) { + case Track::MIDI: + case Track::DRUM: + _midis.push_back((MidiTrack*)track); + // Added by T356. + //((MidiTrack*)track)->addPortCtrlEvents(); + addPortCtrlEvents(((MidiTrack*)track)); + + break; + case Track::WAVE: + _waves.push_back((WaveTrack*)track); + break; + case Track::AUDIO_OUTPUT: + _outputs.push_back((AudioOutput*)track); + // set default master & monitor if not defined + if (audio->audioMaster() == 0) + audio->setMaster((AudioOutput*)track); + if (audio->audioMonitor() == 0) + audio->setMonitor((AudioOutput*)track); + break; + case Track::AUDIO_GROUP: + _groups.push_back((AudioGroup*)track); + break; + case Track::AUDIO_AUX: + _auxs.push_back((AudioAux*)track); + break; + case Track::AUDIO_INPUT: + _inputs.push_back((AudioInput*)track); + break; + case Track::AUDIO_SOFTSYNTH: + { + SynthI* s = (SynthI*)track; + midiDevices.add(s); + midiInstruments.push_back(s); + _synthIs.push_back(s); + } + break; + default: + fprintf(stderr, "unknown track type %d\n", track->type()); + // abort(); + return; + } + + // + // initialize missing aux send + // + iTrack i = _tracks.index2iterator(idx); + //printf("Song::insertTrack2 inserting into _tracks...\n"); + + _tracks.insert(i, track); + //printf("Song::insertTrack2 inserted\n"); + + n = _auxs.size(); + for (iTrack i = _tracks.begin(); i != _tracks.end(); ++i) { + if ((*i)->isMidiTrack()) + continue; + WaveTrack* wt = (WaveTrack*)*i; + if (wt->hasAuxSend()) { + wt->addAuxSend(n); + } + } + + /* + // + // add routes + // + + if (track->isMidiTrack()) + return; + AudioTrack* at = (AudioTrack*)track; + Route src(at, -1); + if (at->type() == Track::AUDIO_OUTPUT) { + const RouteList* rl = at->inRoutes(); + for (ciRoute r = rl->begin(); r != rl->end(); ++r) + r->track->outRoutes()->push_back(src); + } + else if (at->type() == Track::AUDIO_INPUT) { + const RouteList* rl = at->outRoutes(); + for (ciRoute r = rl->begin(); r != rl->end(); ++r) + r->track->inRoutes()->push_back(src); + } + else { + const RouteList* rl = at->inRoutes(); + for (ciRoute r = rl->begin(); r != rl->end(); ++r) + r->track->outRoutes()->push_back(src); + rl = at->outRoutes(); + for (ciRoute r = rl->begin(); r != rl->end(); ++r) + r->track->inRoutes()->push_back(src); + } + */ + + // p3.3.38 + + // + // add routes + // + + if (track->type() == Track::AUDIO_OUTPUT) + { + const RouteList* rl = track->inRoutes(); + for (ciRoute r = rl->begin(); r != rl->end(); ++r) + { + //if(r->track == track) + // r->track->outRoutes()->push_back(*r); + // p3.3.50 + Route src(track, r->channel, r->channels); + src.remoteChannel = r->remoteChannel; + r->track->outRoutes()->push_back(src); + } + } + else if (track->type() == Track::AUDIO_INPUT) + { + const RouteList* rl = track->outRoutes(); + for (ciRoute r = rl->begin(); r != rl->end(); ++r) + { + //if(r->track == track) + // r->track->inRoutes()->push_back(*r); + // p3.3.50 + Route src(track, r->channel, r->channels); + src.remoteChannel = r->remoteChannel; + r->track->inRoutes()->push_back(src); + } + } + else if (track->isMidiTrack()) // p3.3.50 + { + const RouteList* rl = track->inRoutes(); + for (ciRoute r = rl->begin(); r != rl->end(); ++r) + { + //printf("Song::insertTrack2 %s in route port:%d\n", track->name().toLatin1().constData(), r->midiPort); // p3.3.50 + Route src(track, r->channel); + midiPorts[r->midiPort].outRoutes()->push_back(src); + } + rl = track->outRoutes(); + for (ciRoute r = rl->begin(); r != rl->end(); ++r) + { + //printf("Song::insertTrack2 %s out route port:%d\n", track->name().toLatin1().constData(), r->midiPort); // p3.3.50 + Route src(track, r->channel); + midiPorts[r->midiPort].inRoutes()->push_back(src); + } + } + else + { + const RouteList* rl = track->inRoutes(); + for (ciRoute r = rl->begin(); r != rl->end(); ++r) + { + //if(r->track == track) + // r->track->outRoutes()->push_back(*r); + // p3.3.50 + Route src(track, r->channel, r->channels); + src.remoteChannel = r->remoteChannel; + r->track->outRoutes()->push_back(src); + } + rl = track->outRoutes(); + for (ciRoute r = rl->begin(); r != rl->end(); ++r) + { + //if(r->track == track) + // r->track->inRoutes()->push_back(*r); + // p3.3.50 + Route src(track, r->channel, r->channels); + src.remoteChannel = r->remoteChannel; + r->track->inRoutes()->push_back(src); + } + } + + //printf("Song::insertTrack2 end of function\n"); + +} + +//--------------------------------------------------------- +// insertTrack3 +// non realtime part of insertTrack +//--------------------------------------------------------- + +void Song::insertTrack3(Track* /*track*/, int /*idx*/)//prevent compiler warning: unused parameter +{ + //printf("Song::insertTrack3\n"); + + /* + switch(track->type()) { + case Track::AUDIO_SOFTSYNTH: + break; + default: + break; + } + */ +} + +//--------------------------------------------------------- +// removeTrack0 +//--------------------------------------------------------- + +void Song::removeTrack0(Track* track) + { + removeTrack1(track); + audio->msgRemoveTrack(track); + removeTrack3(track); + //delete track; + update(SC_TRACK_REMOVED); + } + +//--------------------------------------------------------- +// removeTrack1 +// non realtime part of removeTrack +//--------------------------------------------------------- + +void Song::removeTrack1(Track* track) + { + switch(track->type()) + { + case Track::WAVE: + case Track::AUDIO_OUTPUT: + case Track::AUDIO_INPUT: + case Track::AUDIO_GROUP: + case Track::AUDIO_AUX: + case Track::AUDIO_SOFTSYNTH: + ((AudioTrack*)track)->deleteAllEfxGuis(); + break; + default: + break; + } + + switch(track->type()) + { + case Track::AUDIO_OUTPUT: + case Track::AUDIO_INPUT: + connectJackRoutes((AudioTrack*)track, true); + break; + case Track::AUDIO_SOFTSYNTH: + { + SynthI* si = (SynthI*)track; + if(si->hasGui()) + si->showGui(false); + } + break; + default: + break; + } + } + +//--------------------------------------------------------- +// removeTrack +// called from RT context +//--------------------------------------------------------- + +void Song::removeTrack2(Track* track) +{ + //printf("Song::removeTrack2 track:%s\n", track->name().toLatin1().constData()); // p3.3.50 + + switch(track->type()) { + case Track::MIDI: + case Track::DRUM: + // Added by T356. + //((MidiTrack*)track)->removePortCtrlEvents(); + removePortCtrlEvents(((MidiTrack*)track)); + unchainTrackParts(track, true); + + _midis.erase(track); + break; + case Track::WAVE: + // Added by T356. + unchainTrackParts(track, true); + + _waves.erase(track); + break; + case Track::AUDIO_OUTPUT: + _outputs.erase(track); + break; + case Track::AUDIO_INPUT: + _inputs.erase(track); + break; + case Track::AUDIO_GROUP: + _groups.erase(track); + break; + case Track::AUDIO_AUX: + _auxs.erase(track); + break; + case Track::AUDIO_SOFTSYNTH: + { + SynthI* s = (SynthI*) track; + s->deactivate2(); + _synthIs.erase(track); + } + break; + } + _tracks.erase(track); + + + /* + if (track->isMidiTrack()) + return; + // + // remove routes + // + + AudioTrack* at = (AudioTrack*)track; + Route src(at, -1); + if (at->type() == Track::AUDIO_OUTPUT) { + const RouteList* rl = at->inRoutes(); + for (ciRoute r = rl->begin(); r != rl->end(); ++r) + r->track->outRoutes()->removeRoute(src); + } + else if (at->type() == Track::AUDIO_INPUT) { + const RouteList* rl = at->outRoutes(); + for (ciRoute r = rl->begin(); r != rl->end(); ++r) + r->track->inRoutes()->removeRoute(src); + } + else { + const RouteList* rl = at->inRoutes(); + for (ciRoute r = rl->begin(); r != rl->end(); ++r) + r->track->outRoutes()->removeRoute(src); + rl = at->outRoutes(); + for (ciRoute r = rl->begin(); r != rl->end(); ++r) + r->track->inRoutes()->removeRoute(src); + } + */ + + // p3.3.38 + + // + // remove routes + // + + if (track->type() == Track::AUDIO_OUTPUT) + { + const RouteList* rl = track->inRoutes(); + for (ciRoute r = rl->begin(); r != rl->end(); ++r) + { + //if(r->track == track) + // r->track->outRoutes()->removeRoute(*r); + //printf("Song::removeTrack2 %s audio out track:%s\n", track->name().toLatin1().constData(), r->track->name().toLatin1().constData()); // p3.3.50 + // p3.3.50 + Route src(track, r->channel, r->channels); + src.remoteChannel = r->remoteChannel; + r->track->outRoutes()->removeRoute(src); + } + } + else if (track->type() == Track::AUDIO_INPUT) + { + const RouteList* rl = track->outRoutes(); + for (ciRoute r = rl->begin(); r != rl->end(); ++r) + { + //if(r->track == track) + // r->track->inRoutes()->removeRoute(*r); + //printf("Song::removeTrack2 %s audio in track:%s\n", track->name().toLatin1().constData(), r->track->name().toLatin1().constData()); // p3.3.50 + // p3.3.50 + Route src(track, r->channel, r->channels); + src.remoteChannel = r->remoteChannel; + r->track->inRoutes()->removeRoute(src); + } + } + else if (track->isMidiTrack()) // p3.3.50 + { + const RouteList* rl = track->inRoutes(); + for (ciRoute r = rl->begin(); r != rl->end(); ++r) + { + //printf("Song::removeTrack2 %s in route port:%d\n", track->name().toLatin1().constData(), r->midiPort); // p3.3.50 + Route src(track, r->channel); + midiPorts[r->midiPort].outRoutes()->removeRoute(src); + } + rl = track->outRoutes(); + for (ciRoute r = rl->begin(); r != rl->end(); ++r) + { + //printf("Song::removeTrack2 %s out route port:%d\n", track->name().toLatin1().constData(), r->midiPort); // p3.3.50 + Route src(track, r->channel); + midiPorts[r->midiPort].inRoutes()->removeRoute(src); + } + } + else + { + const RouteList* rl = track->inRoutes(); + for (ciRoute r = rl->begin(); r != rl->end(); ++r) + { + //if(r->track == track) + // r->track->outRoutes()->removeRoute(*r); + //printf("Song::removeTrack2 %s in route track:%s\n", track->name().toLatin1().constData(), r->track->name().toLatin1().constData()); // p3.3.50 + // p3.3.50 + Route src(track, r->channel, r->channels); + src.remoteChannel = r->remoteChannel; + r->track->outRoutes()->removeRoute(src); + } + rl = track->outRoutes(); + for (ciRoute r = rl->begin(); r != rl->end(); ++r) + { + //if(r->track == track) + // r->track->inRoutes()->removeRoute(*r); + //printf("Song::removeTrack2 %s out route track:%s\n", track->name().toLatin1().constData(), r->track->name().toLatin1().constData()); // p3.3.50 + // p3.3.50 + Route src(track, r->channel, r->channels); + src.remoteChannel = r->remoteChannel; + r->track->inRoutes()->removeRoute(src); + } + } + +} + +//--------------------------------------------------------- +// removeTrack3 +// non realtime part of removeTrack +//--------------------------------------------------------- + +void Song::removeTrack3(Track* /*track*/)//prevent of compiler warning: unused parameter + { + /* + switch(track->type()) { + case Track::AUDIO_SOFTSYNTH: + { + SynthI* s = (SynthI*) track; + s->deactivate3(); + } + break; + default: + break; + } + */ + } + +//--------------------------------------------------------- +// executeScript +//--------------------------------------------------------- +void Song::executeScript(const char* scriptfile, PartList* parts, int quant, bool onlyIfSelected) +{ + // a simple format for external processing + // will be extended if there is a need + // + // Semantics: + // PARTLEN <len in ticks> + // BEATLEN <len in ticks> + // QUANTLEN <len in ticks> + // NOTE <tick> <nr> <len in ticks> <velocity> + // CONTROLLER <tick> <a> <b> <c> + // + song->startUndo(); // undo this entire block + for (iPart i = parts->begin(); i != parts->end(); i++) { + //const char* tmp = tmpnam(NULL); + char tmp[16] = "muse-tmp-XXXXXX"; + int fd = mkstemp(tmp); + printf("script input filename=%s\n",tmp); + //FILE *fp = fopen(tmp, "w"); + FILE *fp = fdopen(fd , "w"); + MidiPart *part = (MidiPart*)(i->second); + int partStart = part->endTick()-part->lenTick(); + int z, n; + AL::sigmap.timesig(0, z, n); + fprintf(fp, "TIMESIG %d %d\n", z, n); + fprintf(fp, "PART %d %d\n", partStart, part->lenTick()); + fprintf(fp, "BEATLEN %d\n", AL::sigmap.ticksBeat(0)); + fprintf(fp, "QUANTLEN %d\n", quant); + + //for (iCItem i = items.begin(); i != items.end(); ++i) { + for (iEvent e = part->events()->begin(); e != part->events()->end(); e++) { + Event ev = e->second; + + if (ev.isNote()) + { + if (onlyIfSelected && ev.selected() == false) + continue; + + fprintf(fp,"NOTE %d %d %d %d\n", ev.tick(), ev.dataA(), ev.lenTick(), ev.dataB()); + // Indicate no undo, and do not do port controller values and clone parts. + audio->msgDeleteEvent(ev, part, false, false, false); + } else if (ev.type()==Controller) { + fprintf(fp,"CONTROLLER %d %d %d %d\n", ev.tick(), ev.dataA(), ev.dataB(), ev.dataC()); + // Indicate no undo, and do not do port controller values and clone parts. + audio->msgDeleteEvent(ev, part, false, false, false); + } + } + fclose(fp); + + // Call external program, let it manipulate the file + int pid = fork(); + if (pid == 0) { + if (execlp(scriptfile, scriptfile, tmp, NULL) == -1) { + perror("Failed to launch script!"); + // Get out of here + + // cannot report error through gui, we are in another fork! + //@!TODO: Handle unsuccessful attempts + exit(99); + } + exit(0); + } + else if (pid == -1) { + perror("fork failed"); + } + else { + int status; + waitpid(pid, &status, 0); + if (WEXITSTATUS(status) != 0 ) { + QMessageBox::warning(muse, tr("MusE - external script failed"), + tr("MusE was unable to launch the script\n") + ); + endUndo(SC_EVENT_REMOVED); + return; + } + else { // d0 the fun55or5! + // TODO: Create a new part, update the entire editor from it, hehh.... + + QFile file(tmp); + if ( file.open( QIODevice::ReadOnly ) ) { + QTextStream stream( &file ); + QString line; + while ( !stream.atEnd() ) { + line = stream.readLine(); // line of text excluding '\n' + if (line.startsWith("NOTE")) + { + QStringList sl = line.split(" "); + + Event e(Note); + int tick = sl[1].toInt(); + int pitch = sl[2].toInt(); + int len = sl[3].toInt(); + int velo = sl[4].toInt(); + printf ("tick=%d pitch=%d velo=%d len=%d\n", tick,pitch,velo,len); + e.setTick(tick); + e.setPitch(pitch); + e.setVelo(velo); + e.setLenTick(len); + // Indicate no undo, and do not do port controller values and clone parts. + audio->msgAddEvent(e, part, false, false, false); + } + if (line.startsWith("CONTROLLER")) + { + QStringList sl = line.split(" "); + + Event e(Controller); + int tick = sl[1].toInt(); + int a = sl[2].toInt(); + int b = sl[3].toInt(); + int c = sl[4].toInt(); + printf ("tick=%d a=%d b=%d c=%d\n", tick,a,b,c); + e.setA(a); + e.setB(b); + e.setB(c); + // Indicate no undo, and do not do port controller values and clone parts. + audio->msgAddEvent(e, part, false, false, false); + } + } + file.close(); + } + } + } + remove(tmp); + } + + endUndo(SC_EVENT_REMOVED); +} + + +void Song::populateScriptMenu(QMenu* menuPlugins, QObject* receiver) +{ + // + // List scripts + // + QString distScripts = museGlobalShare + "/scripts"; + + QString userScripts = configPath + "/scripts"; + + QFileInfo distScriptsFi(distScripts); + if (distScriptsFi.isDir()) { + QDir dir = QDir(distScripts); + dir.setFilter(QDir::Executable | QDir::Files); + deliveredScriptNames = dir.entryList(); + } + QFileInfo userScriptsFi(userScripts); + if (userScriptsFi.isDir()) { + QDir dir(userScripts); + dir.setFilter(QDir::Executable | QDir::Files); + userScriptNames = dir.entryList(); + } + + QSignalMapper* distSignalMapper = new QSignalMapper(this); + QSignalMapper* userSignalMapper = new QSignalMapper(this); + + if (deliveredScriptNames.size() > 0 || userScriptNames.size() > 0) { + //menuPlugins = new QPopupMenu(this); + //menuBar()->insertItem(tr("&Plugins"), menuPlugins); + int id = 0; + if (deliveredScriptNames.size() > 0) { + for (QStringList::Iterator it = deliveredScriptNames.begin(); it != deliveredScriptNames.end(); it++, id++) { + //menuPlugins->insertItem(*it, this, SLOT(execDeliveredScript(int)), 0, id); + //menuPlugins->insertItem(*it, this, slot_deliveredscripts, 0, id); + QAction* act = menuPlugins->addAction(*it); + connect(act, SIGNAL(triggered()), distSignalMapper, SLOT(map())); + distSignalMapper->setMapping(act, id); + } + menuPlugins->addSeparator(); + } + if (userScriptNames.size() > 0) { + for (QStringList::Iterator it = userScriptNames.begin(); it != userScriptNames.end(); it++, id++) { + //menuPlugins->insertItem(*it, this, slot_userscripts, 0, id); + QAction* act = menuPlugins->addAction(*it); + connect(act, SIGNAL(triggered()), userSignalMapper, SLOT(map())); + userSignalMapper->setMapping(act, id); + } + menuPlugins->addSeparator(); + } + connect(distSignalMapper, SIGNAL(mapped(int)), receiver, SLOT(execDeliveredScript(int))); + connect(userSignalMapper, SIGNAL(mapped(int)), receiver, SLOT(execUserScript(int))); + } + return; +} + +//--------------------------------------------------------- +// getScriptPath +//--------------------------------------------------------- +QString Song::getScriptPath(int id, bool isdelivered) +{ + if (isdelivered) { + QString path = museGlobalShare + "/scripts/" + deliveredScriptNames[id]; + return path; + } + + QString path = configPath + "/scripts/" + userScriptNames[id - deliveredScriptNames.size()]; + return path; +} + |