diff options
Diffstat (limited to 'muse2/muse/part.cpp')
-rw-r--r-- | muse2/muse/part.cpp | 1432 |
1 files changed, 1432 insertions, 0 deletions
diff --git a/muse2/muse/part.cpp b/muse2/muse/part.cpp new file mode 100644 index 00000000..250cf3ea --- /dev/null +++ b/muse2/muse/part.cpp @@ -0,0 +1,1432 @@ +//========================================================= +// 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) +//========================================================= + +#include <stdio.h> +#include <assert.h> +#include <cmath> + +#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" + +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) + { + // Added by Tim. p3.3.6 + //printf("chainCloneInternal track %p %s part %p %s evlist %p\n", (*imt), (*imt)->name().latin1(), ip->second, ip->second->name().latin1(), ip->second->cevents()); + + 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); + + // Link the replacement... + //p2->setPrevClone(p1->prevClone()); + //p2->setNextClone(p1->nextClone()); + + // Isolate the replaced part. + p1->setNextClone(p1); + p1->setPrevClone(p1); + // Added by Tim. p3.3.6 + //printf("replaceClone p1: %s %p arefs:%d p2: %s %p arefs:%d\n", p1->name().latin1(), 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); + + // Added by Tim. p3.3.6 + //printf("chainTrackParts track %p %s part %p %s evlist %p\n", t, t->name().latin1(), p, p->name().latin1(), p->cevents()); + + 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) + { + // Added by Tim. p3.3.6 + //printf("chainTrackParts track %p %s part %p %s evlist %p\n", mt, mt->name().latin1(), ip->second, ip->second->name().latin1(), ip->second->cevents()); + + 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().latin1(), p->nextClone(), p->nextClone()->prevClone()->name().latin1(), p->nextClone()->prevClone(), p->name().latin1(), p); + if(p->prevClone()->nextClone() != p) + printf("chainCheckErr: Prev clone:%s %p next clone:%s %p != %s %p\n", p->prevClone()->name().latin1(), p->prevClone(), p->prevClone()->nextClone()->name().latin1(), p->prevClone()->nextClone(), p->name().latin1(), 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) + { + // Added by Tim. p3.3.6 + //printf("addPortCtrlEvents i:%d %s %p events %p refs:%d arefs:%d\n", i, p->name().latin1(), p, part->cevents(), part->cevents()->refCount(), j); + + 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 Tim. p3.3.6 + //printf("addPortCtrlEvents %s len:%d end:%d etick:%d\n", p->name().latin1(), p->lenTick(), p->endTick(), event.tick()); + + // 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) + { + // Added by Tim. p3.3.6 + //printf("addPortCtrlEvents i:%d %s %p events %p refs:%d arefs:%d\n", i, p->name().latin1(), p, part->cevents(), part->cevents()->refCount(), j); + + 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(); + //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(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) + { + _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) + { + _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; + //setSn(newSn()); + //_sn = p._sn; + //_name = p._name; + //_selected = p._selected; + //_mute = p._mute; + //_colorIndex = p._colorIndex; + //_track = p._track; + //_events = p._events; +} + +//--------------------------------------------------------- +// 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; + //setSn(newSn()); + //_sn = p._sn; + //_name = p._name; + //_selected = p._selected; + //_mute = p._mute; + //_colorIndex = p._colorIndex; + //_track = p._track; + //_events = p._events; +} + +//--------------------------------------------------------- +// Part +//--------------------------------------------------------- + +Part::~Part() + { + _events->incRef(-1); + if (_events->refCount() <= 0) + delete _events; + } + +/* +//--------------------------------------------------------- +// unchainClone +//--------------------------------------------------------- + +void Part::unchainClone() +{ + chainCheckErr(); + + _prevClone->setNextClone(_nextClone); + _nextClone->setPrevClone(_prevClone); + + _prevClone = this; + _nextClone = this; +} + +//--------------------------------------------------------- +// chainClone +// The quick way - if part to chain to is known... +//--------------------------------------------------------- + +void Part::chainClone(const Part* p) +{ + chainCheckErr(); + + // Make sure the part is unchained first. + p->prevClone()->setNextClone(p->nextClone()); + p->nextClone()->setPrevClone(p->prevClone()); + + p->setPrevClone(this); + p->setNextClone(_nextClone->prevClone()); + + _nextClone->setPrevClone(p); + _nextClone = (Part*)p; +} + +//--------------------------------------------------------- +// chainClone +// The slow way - if part to chain to is not known... +//--------------------------------------------------------- + +void Part::chainClone() +{ + chainCheckErr(); + + // Look for a part with the same event list, that we can chain to... + Part* p = 0; + if(!_track || (_track && _track->isMidiTrack())) + { + MidiTrackList* mtl = song->midis(); + for(ciMidiTrack imt = mtl->begin(); imt != mtl->end(); ++imt) + { + const PartList* pl = (*imt)->cparts(); + for(ciPart ip = pl->begin(); ip != pl->end(); ++ip) + { + if(ip->second != this && ip->second->events() == _events) + { + p = ip->second; + break; + } + } + } + } + + if((!p && !_track) || (_track && _track->type() == Track::WAVE)) + { + WaveTrackList* wtl = song->waves(); + for(ciWaveTrack iwt = wtl->begin(); iwt != wtl->end(); ++iwt) + { + const PartList* pl = (*iwt)->cparts(); + for(ciPart ip = pl->begin(); ip != pl->end(); ++ip) + { + if(ip->second != this && ip->second->events() == _events) + { + p = ip->second; + break; + } + } + } + } + + // No part found with same event list? Done. + if(!p) + return; + + // Make sure this part is unchained first. + _prevClone->setNextClone(_nextClone); + _nextClone->setPrevClone(_prevClone); + + _prevClone = p; + _nextClone = p->nextClone(); + + p->nextClone()->setPrevClone(this); + p->setNextClone(this); +} + +//--------------------------------------------------------- +// replaceClone +//--------------------------------------------------------- + +void Part::replaceClone(const Part* p) +{ + chainCheckErr(); + + // Make sure the part is unchained first. + p->prevClone()->setNextClone(p->nextClone()); + p->nextClone()->setPrevClone(p->prevClone()); + + // If this part is a clone, not a single lone part... + if(_prevClone != this) + _prevClone->setNextClone(p); + if(_nextClone != this) + _nextClone->setPrevClone(p); + + p->setPrevClone(_prevClone); + p->setNextClone(_nextClone); + + _nextClone = this; + _prevClone = this; +} + +//--------------------------------------------------------- +// chainCheckErr +//--------------------------------------------------------- + +void Part::chainCheckErr() +{ + if(_nextClone->prevClone() != this) + printf("Part::chainCheckErr Error! Next clone:%s %x prev clone:%s %x != this:%s %x\n", _nextClone->name().latin1(), _nextClone, _nextClone->prevClone()->name().latin1(), _nextClone->prevClone(), name().latin1(), this); + if(_prevClone->nextClone() != this) + printf("Part::chainCheckErr Error! Prev clone:%s %x next clone:%s %x != this:%s %x\n", _prevClone->name().latin1(), _prevClone, _prevClone->nextClone()->name().latin1(), _prevClone->nextClone(), name().latin1(), this); +} +*/ + +//--------------------------------------------------------- +// 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<const int, Part*> (part->frame(), part)); + else + return insert(std::pair<const int, Part*> (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); + + //part->addPortCtrlEvents(); + // Indicate do not do clones. + addPortCtrlEvents(part, false); + } + +//--------------------------------------------------------- +// removePart +//--------------------------------------------------------- + +void Song::removePart(Part* part) + { + //part->removePortCtrlEvents(); + // Indicate do not do clones. + //removePortCtrlEvents(part); + removePortCtrlEvents(part, false); + Track* track = part->track(); + track->parts()->remove(part); + } + +//--------------------------------------------------------- +// cmdResizePart +//--------------------------------------------------------- + +void Song::cmdResizePart(Track* track, Part* oPart, unsigned int len) + { + 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); + //printf("new partlength in frames: %d\n", new_partlength); + + // 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()) { + startUndo(); + + 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(); + //printf("Event frame=%d, length=%d\n", event_startframe, event_length); + if (event_endframe < new_partlength) + continue; + if (event_startframe > new_partlength) { // If event start was after the new length, remove it from part + // Indicate no undo, and do not do port controller values and clone parts. + //audio->msgDeleteEvent(e, nPart, false); + audio->msgDeleteEvent(e, nPart, false, 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); + // Indicate no undo, and do not do port controller values and clone parts. + //audio->msgChangeEvent(e, newEvent, nPart, false); + audio->msgChangeEvent(e, newEvent, nPart, false, false, false); + } + } + nPart->setLenFrame(new_partlength); + // Indicate no undo, and do not do port controller values and clone parts. + //audio->msgChangePart(oPart, nPart, false); + audio->msgChangePart(oPart, nPart, false, false, false); + + endUndo(SC_PART_MODIFIED); + } + // 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 { + 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(); + //printf("SndFileR samples=%d channels=%d event samplepos=%d clipframes=%d\n", file.samples(), file.channels(), last.spos(), clipframes); + + 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); + startUndo(); + // Indicate no undo, and do not do port controller values and clone parts. + //audio->msgChangeEvent(last, newEvent, nPart, false); + audio->msgChangeEvent(last, newEvent, nPart, false, false, false); + } + else + { + startUndo(); + } + + nPart->setLenFrame(new_partlength); + // Indicate no undo, and do not do port controller values and clone parts. + //audio->msgChangePart(oPart, nPart, false); + audio->msgChangePart(oPart, nPart, false, false, false); + endUndo(SC_PART_MODIFIED); + } + } + break; + case Track::MIDI: + case Track::DRUM: + { + startUndo(); + + MidiPart* nPart = new MidiPart(*(MidiPart*)oPart); + nPart->setLenTick(len); + // Indicate no undo, and do port controller values but not clone parts. + audio->msgChangePart(oPart, nPart, false, true, false); + + // cut Events in nPart + // Changed by T356. Don't delete events if this is a clone part. + // The other clones might be longer than this one and need these events. + if(nPart->cevents()->arefCount() <= 1) + { + if(oPart->lenTick() > len) { + EventList* el = nPart->events(); + iEvent ie = el->lower_bound(len); + for (; ie != el->end();) { + iEvent i = ie; + ++ie; + // Indicate no undo, and do port controller values and clone parts. + audio->msgDeleteEvent(i->second, nPart, false, true, true); + } + } + } + + /* + // cut Events in nPart + // Changed by T356. Don't delete events if this is a clone part. + // The other clones might be longer than this one and need these events. + if(oPart->cevents()->arefCount() <= 1) + { + if (oPart->lenTick() > len) { + EventList* el = nPart->events(); + iEvent ie = el->lower_bound(len); + for (; ie != el->end();) { + iEvent i = ie; + ++ie; + // Indicate no undo, and do not do port controller values and clone parts. + //audio->msgDeleteEvent(i->second, nPart, false); + audio->msgDeleteEvent(i->second, nPart, false, false, false); + } + } + } + // Indicate no undo, and do port controller values but not clone parts. + //audio->msgChangePart(oPart, nPart, false); + audio->msgChangePart(oPart, nPart, false, true, false); + */ + + endUndo(SC_PART_MODIFIED); + 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 + //printf("Track::splitPart part ev %p sz:%d ref:%d p1 %p sz:%d ref:%d p2 %p sz:%d ref:%d\n", part->events(), part->events()->size(), part->events()->arefCount(), p1->events(), p1->events()->size(), p1->events()->arefCount(), p2->events(), p2->events()->size(), p2->events()->arefCount()); + + 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(); + + // Added by Tim. p3.3.6 + //printf("Song::changePart before oPart->removePortCtrlEvents oldPart refs:%d Arefs:%d newPart refs:%d Arefs:%d\n", oPart->events()->refCount(), oPart->events()->arefCount(), nPart->events()->refCount(), nPart->events()->arefCount()); + + // Removed. Port controller events will have to be add/removed separately from this routine. + //oPart->removePortCtrlEvents(); + //removePortCtrlEvents(oPart); + + // Added by Tim. p3.3.6 + //printf("Song::changePart after oPart->removePortCtrlEvents oldPart refs:%d Arefs:%d newPart refs:%d Arefs:%d\n", oPart->events()->refCount(), oPart->events()->arefCount(), nPart->events()->refCount(), nPart->events()->arefCount()); + + oTrack->parts()->remove(oPart); + nTrack->parts()->add(nPart); + + // Added by Tim. p3.3.6 + //printf("Song::changePart after add(nPart) oldPart refs:%d Arefs:%d newPart refs:%d Arefs:%d\n", oPart->events()->refCount(), oPart->events()->arefCount(), nPart->events()->refCount(), nPart->events()->arefCount()); + + //nPart->addPortCtrlEvents(); + //addPortCtrlEvents(nPart); + + // Added by Tim. p3.3.6 + //printf("Song::changePart after nPart->addPortCtrlEvents() oldPart refs:%d Arefs:%d newPart refs:%d Arefs:%d\n", oPart->events()->refCount(), oPart->events()->arefCount(), nPart->events()->refCount(), nPart->events()->arefCount()); + + // Added by T356. + // adjust song len: + unsigned epos = nPart->tick() + nPart->lenTick(); + if (epos > len()) + _len = epos; + + // Added by Tim. p3.3.6 + //printf("Song::changePart after len adjust oldPart refs:%d Arefs:%d newPart refs:%d Arefs:%d\n", oPart->events()->refCount(), oPart->events()->arefCount(), nPart->events()->refCount(), nPart->events()->arefCount()); + + } + +//--------------------------------------------------------- +// 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(); + + //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); + // } + // p3.3.54 Changed. + 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); + 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.latin1()); + 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); + } + |