//========================================================= // MusE // Linux Music Editor // $Id: part.cpp,v 1.12.2.17 2009/06/25 05:13:02 terminator356 Exp $ // // (C) Copyright 1999/2000 Werner Schweer (ws@seh.de) // Additions, modifications (C) Copyright 2011 Tim E. Real (terminator356 on users DOT sourceforge DOT net) //========================================================= #include #include #include #include "song.h" #include "part.h" #include "track.h" #include "globals.h" #include "event.h" #include "audio.h" #include "wave.h" #include "midiport.h" #include "drummap.h" //#include "midiedit/drummap.h" // p4.0.2 int Part::snGen; //--------------------------------------------------------- // unchainClone //--------------------------------------------------------- void unchainClone(Part* p) { chainCheckErr(p); // Unchain the part. p->prevClone()->setNextClone(p->nextClone()); p->nextClone()->setPrevClone(p->prevClone()); // Isolate the part. p->setPrevClone(p); p->setNextClone(p); } //--------------------------------------------------------- // chainClone // The quick way - if part to chain to is known... //--------------------------------------------------------- void chainClone(Part* p1, Part* p2) { chainCheckErr(p1); // Make sure the part to be chained is unchained first. p2->prevClone()->setNextClone(p2->nextClone()); p2->nextClone()->setPrevClone(p2->prevClone()); // Link the part to be chained. p2->setPrevClone(p1); p2->setNextClone(p1->nextClone()); // Re-link the existing part. p1->nextClone()->setPrevClone(p2); p1->setNextClone(p2); } //--------------------------------------------------------- // chainCloneInternal // No error check, so it can be called by replaceClone() //--------------------------------------------------------- void chainCloneInternal(Part* p) { Track* t = p->track(); Part* p1 = 0; // Look for a part with the same event list, that we can chain to. // It's faster if track type is known... if(!t || (t && t->isMidiTrack())) { MidiTrack* mt = 0; MidiTrackList* mtl = song->midis(); for(ciMidiTrack imt = mtl->begin(); imt != mtl->end(); ++imt) { mt = *imt; const PartList* pl = mt->cparts(); for(ciPart ip = pl->begin(); ip != pl->end(); ++ip) { if(ip->second != p && ip->second->cevents() == p->cevents()) { p1 = ip->second; break; } } // If a suitable part was found on a different track, we're done. We will chain to it. // Otherwise keep looking for parts on another track. If no others found, then we // chain to any suitable part which was found on the same given track t. if(p1 && mt != t) break; } } if((!p1 && !t) || (t && t->type() == Track::WAVE)) { WaveTrack* wt = 0; WaveTrackList* wtl = song->waves(); for(ciWaveTrack iwt = wtl->begin(); iwt != wtl->end(); ++iwt) { wt = *iwt; const PartList* pl = wt->cparts(); for(ciPart ip = pl->begin(); ip != pl->end(); ++ip) { if(ip->second != p && ip->second->cevents() == p->cevents()) { p1 = ip->second; break; } } if(p1 && wt != t) break; } } // No part found with same event list? Done. if(!p1) return; // Make sure the part to be chained is unchained first. p->prevClone()->setNextClone(p->nextClone()); p->nextClone()->setPrevClone(p->prevClone()); // Link the part to be chained. p->setPrevClone(p1); p->setNextClone(p1->nextClone()); // Re-link the existing part. p1->nextClone()->setPrevClone(p); p1->setNextClone(p); } //--------------------------------------------------------- // chainClone // The slow way - if part to chain to is not known... //--------------------------------------------------------- void chainClone(Part* p) { chainCheckErr(p); chainCloneInternal(p); } //--------------------------------------------------------- // replaceClone //--------------------------------------------------------- void replaceClone(Part* p1, Part* p2) { chainCheckErr(p1); // Make sure the replacement part is unchained first. p2->prevClone()->setNextClone(p2->nextClone()); p2->nextClone()->setPrevClone(p2->prevClone()); // If the two parts share the same event list, then this MUST // be a straight forward replacement operation. Continue on. // If not, and either part has more than one ref count, then do this... if(p1->cevents() != p2->cevents()) { bool ret = false; // If the part to be replaced is a single uncloned part, // and the replacement part is not, then this operation // MUST be an undo of a de-cloning of a cloned part. //if(p1->cevents()->refCount() <= 1 && p2->cevents()->refCount() > 1) if(p2->cevents()->refCount() > 1) { // Chain the replacement part. We don't know the chain it came from, // so we use the slow method. chainCloneInternal(p2); //return; ret = true; } // If the replacement part is a single uncloned part, // and the part to be replaced is not, then this operation // MUST be a de-cloning of a cloned part. //if(p1->cevents()->refCount() > 1 && p2->cevents()->refCount() <= 1) if(p1->cevents()->refCount() > 1) { // Unchain the part to be replaced. p1->prevClone()->setNextClone(p1->nextClone()); p1->nextClone()->setPrevClone(p1->prevClone()); // Isolate the part. p1->setPrevClone(p1); p1->setNextClone(p1); //return; ret = true; } // Was the operation handled? if(ret) return; // Note that two parts here with different event lists, each with more than one // reference count, would be an error. It's not done anywhere in muse. But just // to be sure, four lines above were changed to allow that condition. // If each of the two different event lists, has only one ref count, we // handle it like a regular replacement, below... } // If the part to be replaced is a clone not a single lone part, re-link its neighbours to the replacement part... if(p1->prevClone() != p1) { p1->prevClone()->setNextClone(p2); p2->setPrevClone(p1->prevClone()); } else p2->setPrevClone(p2); if(p1->nextClone() != p1) { p1->nextClone()->setPrevClone(p2); p2->setNextClone(p1->nextClone()); } else p2->setNextClone(p2); // Isolate the replaced part. p1->setNextClone(p1); p1->setPrevClone(p1); } //--------------------------------------------------------- // unchainTrackParts //--------------------------------------------------------- void unchainTrackParts(Track* t, bool decRefCount) { PartList* pl = t->parts(); for(iPart ip = pl->begin(); ip != pl->end(); ++ip) { Part* p = ip->second; chainCheckErr(p); // Do we want to decrease the reference count? if(decRefCount) p->events()->incARef(-1); // Unchain the part. p->prevClone()->setNextClone(p->nextClone()); p->nextClone()->setPrevClone(p->prevClone()); // Isolate the part. p->setPrevClone(p); p->setNextClone(p); } } //--------------------------------------------------------- // chainTrackParts //--------------------------------------------------------- void chainTrackParts(Track* t, bool incRefCount) { PartList* pl = t->parts(); for(iPart ip = pl->begin(); ip != pl->end(); ++ip) { Part* p = ip->second; chainCheckErr(p); // Do we want to increase the reference count? if(incRefCount) p->events()->incARef(1); Part* p1 = 0; // Look for a part with the same event list, that we can chain to. // It's faster if track type is known... if(!t || (t && t->isMidiTrack())) { MidiTrack* mt = 0; MidiTrackList* mtl = song->midis(); for(ciMidiTrack imt = mtl->begin(); imt != mtl->end(); ++imt) { mt = *imt; const PartList* pl = mt->cparts(); for(ciPart ip = pl->begin(); ip != pl->end(); ++ip) { if(ip->second != p && ip->second->cevents() == p->cevents()) { p1 = ip->second; break; } } // If a suitable part was found on a different track, we're done. We will chain to it. // Otherwise keep looking for parts on another track. If no others found, then we // chain to any suitable part which was found on the same given track t. if(p1 && mt != t) break; } } if((!p1 && !t) || (t && t->type() == Track::WAVE)) { WaveTrack* wt = 0; WaveTrackList* wtl = song->waves(); for(ciWaveTrack iwt = wtl->begin(); iwt != wtl->end(); ++iwt) { wt = *iwt; const PartList* pl = wt->cparts(); for(ciPart ip = pl->begin(); ip != pl->end(); ++ip) { if(ip->second != p && ip->second->cevents() == p->cevents()) { p1 = ip->second; break; } } if(p1 && wt != t) break; } } // No part found with same event list? Done. if(!p1) continue; // Make sure the part to be chained is unchained first. p->prevClone()->setNextClone(p->nextClone()); p->nextClone()->setPrevClone(p->prevClone()); // Link the part to be chained. p->setPrevClone(p1); p->setNextClone(p1->nextClone()); // Re-link the existing part. p1->nextClone()->setPrevClone(p); p1->setNextClone(p); } } //--------------------------------------------------------- // chainCheckErr //--------------------------------------------------------- void chainCheckErr(Part* p) { // At all times these must be true... if(p->nextClone()->prevClone() != p) printf("chainCheckErr: Next clone:%s %p prev clone:%s %p != %s %p\n", p->nextClone()->name().toLatin1().constData(), p->nextClone(), p->nextClone()->prevClone()->name().toLatin1().constData(), p->nextClone()->prevClone(), p->name().toLatin1().constData(), p); if(p->prevClone()->nextClone() != p) printf("chainCheckErr: Prev clone:%s %p next clone:%s %p != %s %p\n", p->prevClone()->name().toLatin1().constData(), p->prevClone(), p->prevClone()->nextClone()->name().toLatin1().constData(), p->prevClone()->nextClone(), p->name().toLatin1().constData(), p); } //--------------------------------------------------------- // addPortCtrlEvents //--------------------------------------------------------- void addPortCtrlEvents(Event& event, Part* part, bool doClones) { // Traverse and process the clone chain ring until we arrive at the same part again. // The loop is a safety net. // Update: Due to the varying calls, and order of, incARefcount, (msg)ChangePart, replaceClone, and remove/addPortCtrlEvents, // we can not rely on the reference count as a safety net in these routines. We will just have to trust the clone chain. Part* p = part; //int j = doClones ? p->cevents()->arefCount() : 1; //if(j > 0) { //for(int i = 0; i < j; ++i) while(1) { Track* t = p->track(); if(t && t->isMidiTrack()) { MidiTrack* mt = (MidiTrack*)t; int port = mt->outPort(); unsigned len = p->lenTick(); // Do not add events which are past the end of the part. if(event.tick() >= len) break; if(event.type() == Controller) { int ch = mt->outChannel(); int tck = event.tick() + p->tick(); int cntrl = event.dataA(); int val = event.dataB(); MidiPort* mp = &midiPorts[port]; // Is it a drum controller event, according to the track port's instrument? if(mt->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, tck, cntrl, val, p); } } if(!doClones) break; // Get the next clone in the chain ring. p = p->nextClone(); // Same as original part? Finished. if(p == part) break; } } } //--------------------------------------------------------- // addPortCtrlEvents //--------------------------------------------------------- void addPortCtrlEvents(Part* part, bool doClones) { // Traverse and process the clone chain ring until we arrive at the same part again. // The loop is a safety net. // Update: Due to the varying calls, and order of, incARefcount, (msg)ChangePart, replaceClone, and remove/addPortCtrlEvents, // we can not rely on the reference count as a safety net in these routines. We will just have to trust the clone chain. Part* p = part; //int j = doClones ? p->cevents()->arefCount() : 1; //if(j > 0) { //for(int i = 0; i < j; ++i) while(1) { Track* t = p->track(); if(t && t->isMidiTrack()) { MidiTrack* mt = (MidiTrack*)t; int port = mt->outPort(); const EventList* el = p->cevents(); unsigned len = p->lenTick(); for(ciEvent ie = el->begin(); ie != el->end(); ++ie) { const Event& ev = ie->second; // Added by T356. Do not add events which are past the end of the part. if(ev.tick() >= len) break; if(ev.type() == Controller) { int ch = mt->outChannel(); int tck = ev.tick() + p->tick(); int cntrl = ev.dataA(); int val = ev.dataB(); MidiPort* mp = &midiPorts[port]; // Is it a drum controller event, according to the track port's instrument? if(mt->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, tck, cntrl, val, p); } } } if(!doClones) break; // Get the next clone in the chain ring. p = p->nextClone(); // Same as original part? Finished. if(p == part) break; } } } //--------------------------------------------------------- // removePortCtrlEvents //--------------------------------------------------------- void removePortCtrlEvents(Event& event, Part* part, bool doClones) { // Traverse and process the clone chain ring until we arrive at the same part again. // The loop is a safety net. // Update: Due to the varying calls, and order of, incARefcount, (msg)ChangePart, replaceClone, and remove/addPortCtrlEvents, // we can not rely on the reference count as a safety net in these routines. We will just have to trust the clone chain. Part* p = part; //int j = doClones ? p->cevents()->arefCount() : 1; //if(j > 0) { //for(int i = 0; i < j; ++i) while(1) { Track* t = p->track(); if(t && t->isMidiTrack()) { MidiTrack* mt = (MidiTrack*)t; int port = mt->outPort(); if(event.type() == Controller) { int ch = mt->outChannel(); int tck = event.tick() + p->tick(); int cntrl = event.dataA(); MidiPort* mp = &midiPorts[port]; // Is it a drum controller event, according to the track port's instrument? if(mt->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, tck, cntrl, p); } } if(!doClones) break; // Get the next clone in the chain ring. p = p->nextClone(); // Same as original part? Finished. if(p == part) break; } } } //--------------------------------------------------------- // removePortCtrlEvents //--------------------------------------------------------- void removePortCtrlEvents(Part* part, bool doClones) { // Traverse and process the clone chain ring until we arrive at the same part again. // The loop is a safety net. // Update: Due to the varying calls, and order of, incARefcount, (msg)ChangePart, replaceClone, and remove/addPortCtrlEvents, // we can not rely on the reference count as a safety net in these routines. We will just have to trust the clone chain. Part* p = part; //int j = doClones ? p->cevents()->arefCount() : 1; //if(j > 0) { //for(int i = 0; i < j; ++i) while(1) { Track* t = p->track(); if(t && t->isMidiTrack()) { MidiTrack* mt = (MidiTrack*)t; int port = mt->outPort(); const EventList* el = p->cevents(); //unsigned len = p->lenTick(); for(ciEvent ie = el->begin(); ie != el->end(); ++ie) { const Event& ev = ie->second; // Added by T356. Do not remove events which are past the end of the part. // No, actually, do remove ALL of them belonging to the part. // Just in case there are stray values left after the part end. //if(ev.tick() >= len) // break; if(ev.type() == Controller) { int ch = mt->outChannel(); int tck = ev.tick() + p->tick(); int cntrl = ev.dataA(); MidiPort* mp = &midiPorts[port]; // Is it a drum controller event, according to the track port's instrument? if(mt->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, tck, cntrl, p); } } } if(!doClones) break; // Get the next clone in the chain ring. p = p->nextClone(); // Same as original part? Finished. if(p == part) break; } } } //--------------------------------------------------------- // addEvent //--------------------------------------------------------- iEvent Part::addEvent(Event& p) { return _events->add(p); } //--------------------------------------------------------- // index //--------------------------------------------------------- int PartList::index(Part* part) { int index = 0; for (iPart i = begin(); i != end(); ++i, ++index) if (i->second == part) { return index; } if(debugMsg) printf("PartList::index(): not found!\n"); //return 0; return -1; } //--------------------------------------------------------- // find //--------------------------------------------------------- Part* PartList::find(int idx) { int index = 0; for (iPart i = begin(); i != end(); ++i, ++index) if (index == idx) return i->second; return 0; } //--------------------------------------------------------- // Part //--------------------------------------------------------- Part::Part(Track* t) { _hiddenEvents = NoEventsHidden; _prevClone = this; _nextClone = this; setSn(newSn()); _track = t; _selected = false; _mute = false; _colorIndex = 0; _events = new EventList; _events->incRef(1); _events->incARef(1); } //--------------------------------------------------------- // Part //--------------------------------------------------------- Part::Part(Track* t, EventList* ev) { _hiddenEvents = NoEventsHidden; _prevClone = this; _nextClone = this; setSn(newSn()); _track = t; _selected = false; _mute = false; _colorIndex = 0; _events = ev; _events->incRef(1); _events->incARef(1); } //--------------------------------------------------------- // MidiPart // copy constructor //--------------------------------------------------------- MidiPart::MidiPart(const MidiPart& p) : Part(p) { _prevClone = this; _nextClone = this; } //--------------------------------------------------------- // WavePart //--------------------------------------------------------- WavePart::WavePart(WaveTrack* t) : Part(t) { setType(FRAMES); } WavePart::WavePart(WaveTrack* t, EventList* ev) : Part(t, ev) { setType(FRAMES); } //--------------------------------------------------------- // WavePart // copy constructor //--------------------------------------------------------- WavePart::WavePart(const WavePart& p) : Part(p) { _prevClone = this; _nextClone = this; } //--------------------------------------------------------- // Part //--------------------------------------------------------- Part::~Part() { _events->incRef(-1); if (_events->refCount() <= 0) delete _events; } //--------------------------------------------------------- // findPart //--------------------------------------------------------- iPart PartList::findPart(unsigned tick) { iPart i; for (i = begin(); i != end(); ++i) if (i->second->tick() == tick) break; return i; } //--------------------------------------------------------- // add //--------------------------------------------------------- iPart PartList::add(Part* part) { // Added by T356. A part list containing wave parts should be sorted by // frames. WaveTrack::fetchData() relies on the sorting order, and // there was a bug that waveparts were sometimes muted because of // incorrect sorting order (by ticks). // Also, when the tempo map is changed, every wavepart would have to be // re-added to the part list so that the proper sorting order (by ticks) // could be achieved. // Note that in a med file, the tempo list is loaded AFTER all the tracks. // There was a bug that all the wave parts' tick values were not correct, // since they were computed BEFORE the tempo map was loaded. if(part->type() == Pos::FRAMES) return insert(std::pair (part->frame(), part)); else return insert(std::pair (part->tick(), part)); } //--------------------------------------------------------- // remove //--------------------------------------------------------- void PartList::remove(Part* part) { iPart i; for (i = begin(); i != end(); ++i) { if (i->second == part) { erase(i); break; } } assert(i != end()); } //--------------------------------------------------------- // addPart //--------------------------------------------------------- void Song::addPart(Part* part) { // adjust song len: unsigned epos = part->tick() + part->lenTick(); if (epos > len()) _len = epos; part->track()->addPart(part); // Indicate do not do clones. addPortCtrlEvents(part, false); } //--------------------------------------------------------- // removePart //--------------------------------------------------------- void Song::removePart(Part* part) { // Indicate do not do clones. removePortCtrlEvents(part, false); Track* track = part->track(); track->parts()->remove(part); } //--------------------------------------------------------- // cmdResizePart //--------------------------------------------------------- void Song::cmdResizePart(Track* track, Part* oPart, unsigned int len, bool doClones) { switch(track->type()) { case Track::WAVE: { WavePart* nPart = new WavePart(*(WavePart*)oPart); EventList* el = nPart->events(); unsigned new_partlength = tempomap.deltaTick2frame(oPart->tick(), oPart->tick() + len); // If new nr of frames is less than previous what can happen is: // - 0 or more events are beginning after the new final position. Those are removed from the part // - The last event begins before new final position and ends after it. If so, it will be resized to end at new part length if (new_partlength < oPart->lenFrame()) { Undo operations; for (iEvent i = el->begin(); i != el->end(); i++) { Event e = i->second; unsigned event_startframe = e.frame(); unsigned event_endframe = event_startframe + e.lenFrame(); if (event_endframe < new_partlength) continue; if (event_startframe > new_partlength) { // If event start was after the new length, remove it from part // Do not do port controller values and clone parts. operations.push_back(UndoOp(UndoOp::DeleteEvent, e, nPart, false, false)); continue; } if (event_endframe > new_partlength) { // If this event starts before new length and ends after, shrink it Event newEvent = e.clone(); newEvent.setLenFrame(new_partlength - event_startframe); // Do not do port controller values and clone parts. operations.push_back(UndoOp(UndoOp::ModifyEvent, newEvent, e, nPart, false,false)); } } nPart->setLenFrame(new_partlength); // Do not do port controller values and clone parts. operations.push_back(UndoOp(UndoOp::ModifyPart, oPart, nPart, false, false)); song->applyOperationGroup(operations); } // If the part is expanded there can be no additional events beginning after the previous final position // since those are removed if the part has been shrunk at some time (see above) // The only thing we need to check is the final event: If it has data after the previous final position, // we'll expand that event else { Undo operations; if(!el->empty()) { iEvent i = el->end(); i--; Event last = i->second; unsigned last_start = last.frame(); SndFileR file = last.sndFile(); if (file.isNull()) return; unsigned clipframes = (file.samples() - last.spos());// / file.channels(); Event newEvent = last.clone(); unsigned new_eventlength = new_partlength - last_start; if (new_eventlength > clipframes) // Shrink event length if new partlength exceeds last clip new_eventlength = clipframes; newEvent.setLenFrame(new_eventlength); // Do not do port controller values and clone parts. operations.push_back(UndoOp(UndoOp::ModifyEvent, newEvent, last, nPart, false, false)); } nPart->setLenFrame(new_partlength); // Do not do port controller values and clone parts. operations.push_back(UndoOp(UndoOp::ModifyPart, oPart, nPart, false, false)); song->applyOperationGroup(operations); } } break; case Track::MIDI: case Track::DRUM: { Undo operations; unsigned orig_len=oPart->lenTick(); MidiPart* part_it=(MidiPart*)oPart; do { if (part_it->lenTick()==orig_len) { MidiPart* newPart = new MidiPart(*part_it); newPart->setLenTick(len); // Do port controller values but not clone parts. operations.push_back(UndoOp(UndoOp::ModifyPart, part_it, newPart, true, false)); } part_it=(MidiPart*)part_it->nextClone(); } while (doClones && (part_it != (MidiPart*)oPart)); song->applyOperationGroup(operations); break; } default: break; } } //--------------------------------------------------------- // splitPart // split part "part" at "tick" position // create two new parts p1 and p2 //--------------------------------------------------------- void Track::splitPart(Part* part, int tickpos, Part*& p1, Part*& p2) { int l1 = 0; // len of first new part (ticks or samples) int l2 = 0; // len of second new part int samplepos = tempomap.tick2frame(tickpos); switch (type()) { case WAVE: l1 = samplepos - part->frame(); l2 = part->lenFrame() - l1; break; case MIDI: case DRUM: l1 = tickpos - part->tick(); l2 = part->lenTick() - l1; break; default: return; } if (l1 <= 0 || l2 <= 0) return; p1 = newPart(part); // new left part p2 = newPart(part); // new right part // Added by Tim. p3.3.6 switch (type()) { case WAVE: p1->setLenFrame(l1); p2->setFrame(samplepos); p2->setLenFrame(l2); break; case MIDI: case DRUM: p1->setLenTick(l1); p2->setTick(tickpos); p2->setLenTick(l2); break; default: break; } p2->setSn(p2->newSn()); EventList* se = part->events(); EventList* de1 = p1->events(); EventList* de2 = p2->events(); if (type() == WAVE) { int ps = part->frame(); int d1p1 = p1->frame(); int d2p1 = p1->endFrame(); int d1p2 = p2->frame(); int d2p2 = p2->endFrame(); for (iEvent ie = se->begin(); ie != se->end(); ++ie) { Event event = ie->second; int s1 = event.frame() + ps; int s2 = event.endFrame() + ps; if ((s2 > d1p1) && (s1 < d2p1)) { Event si = event.mid(d1p1 - ps, d2p1 - ps); de1->add(si); } if ((s2 > d1p2) && (s1 < d2p2)) { Event si = event.mid(d1p2 - ps, d2p2 - ps); de2->add(si); } } } else { for (iEvent ie = se->begin(); ie != se->end(); ++ie) { Event event = ie->second.clone(); int t = event.tick(); if (t >= l1) { event.move(-l1); de2->add(event); } else de1->add(event); } } } //--------------------------------------------------------- // cmdSplitPart //--------------------------------------------------------- void Song::cmdSplitPart(Track* track, Part* part, int tick) { int l1 = tick - part->tick(); int l2 = part->lenTick() - l1; if (l1 <= 0 || l2 <= 0) return; Part* p1; Part* p2; track->splitPart(part, tick, p1, p2); startUndo(); // Indicate no undo, and do port controller values but not clone parts. //audio->msgChangePart(part, p1, false); audio->msgChangePart(part, p1, false, true, false); audio->msgAddPart(p2, false); endUndo(SC_TRACK_MODIFIED | SC_PART_MODIFIED | SC_PART_INSERTED); } //--------------------------------------------------------- // changePart //--------------------------------------------------------- void Song::changePart(Part* oPart, Part* nPart) { nPart->setSn(oPart->sn()); Track* oTrack = oPart->track(); Track* nTrack = nPart->track(); oTrack->parts()->remove(oPart); nTrack->parts()->add(nPart); // Added by T356. // adjust song len: unsigned epos = nPart->tick() + nPart->lenTick(); if (epos > len()) _len = epos; } //--------------------------------------------------------- // cmdGluePart //--------------------------------------------------------- void Song::cmdGluePart(Track* track, Part* oPart) { // p3.3.54 if(track->type() != Track::WAVE && !track->isMidiTrack()) return; PartList* pl = track->parts(); Part* nextPart = 0; for (iPart ip = pl->begin(); ip != pl->end(); ++ip) { if (ip->second == oPart) { ++ip; if (ip == pl->end()) return; nextPart = ip->second; break; } } Part* nPart = track->newPart(oPart); nPart->setLenTick(nextPart->tick() + nextPart->lenTick() - oPart->tick()); // populate nPart with Events from oPart and nextPart EventList* sl1 = oPart->events(); EventList* dl = nPart->events(); for (iEvent ie = sl1->begin(); ie != sl1->end(); ++ie) dl->add(ie->second); EventList* sl2 = nextPart->events(); if(track->type() == Track::WAVE) { int frameOffset = nextPart->frame() - oPart->frame(); for (iEvent ie = sl2->begin(); ie != sl2->end(); ++ie) { Event event = ie->second.clone(); event.setFrame(event.frame() + frameOffset); dl->add(event); } } else if(track->isMidiTrack()) { int tickOffset = nextPart->tick() - oPart->tick(); for (iEvent ie = sl2->begin(); ie != sl2->end(); ++ie) { Event event = ie->second.clone(); event.setTick(event.tick() + tickOffset); dl->add(event); } } startUndo(); audio->msgRemovePart(nextPart, false); // Indicate no undo, and do port controller values but not clone parts. audio->msgChangePart(oPart, nPart, false, true, false); endUndo(SC_PART_MODIFIED | SC_PART_REMOVED); } //--------------------------------------------------------- // dump //--------------------------------------------------------- void Part::dump(int n) const { for (int i = 0; i < n; ++i) putchar(' '); printf("Part: <%s> ", _name.toLatin1().constData()); for (int i = 0; i < n; ++i) putchar(' '); PosLen::dump(); } void WavePart::dump(int n) const { Part::dump(n); for (int i = 0; i < n; ++i) putchar(' '); printf("WavePart\n"); } void MidiPart::dump(int n) const { Part::dump(n); for (int i = 0; i < n; ++i) putchar(' '); printf("MidiPart\n"); } //--------------------------------------------------------- // clone //--------------------------------------------------------- MidiPart* MidiPart::clone() const { return new MidiPart(*this); } WavePart* WavePart::clone() const { return new WavePart(*this); } /* bool Part::hasHiddenNotes() { unsigned lastNote=0; for (iEvent ev=events()->begin(); ev!=events()->end(); ev++) if (ev->second.endTick() > lastNote) lastNote=ev->second.endTick(); return lastNote > lenTick(); } */ //--------------------------------------------------------- // hasHiddenEvents // Returns combination of HiddenEventsType enum. //--------------------------------------------------------- int Part::hasHiddenEvents() { unsigned len = lenTick(); // TODO: For now, we don't support events before the left border, only events past the right border. for(iEvent ev=events()->begin(); ev!=events()->end(); ev++) { if(ev->second.endTick() > len) { _hiddenEvents = RightEventsHidden; // Cache the result for later. return _hiddenEvents; } } _hiddenEvents = NoEventsHidden; // Cache the result for later. return _hiddenEvents; }