void Song::cmdResizePart(Track* track, Part* oPart, unsigned int len)/*{{{*/ { { 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); } }/*}}}*/
void LOS::processTrack(MidiTrack* track) { EventList* tevents = track->events(); if (tevents->empty()) return; //--------------------------------------------------- // Identify Parts // The MIDI tracks are broken up into parts // A new Part will be located based on track. // Events will be aligned on new track //--------------------------------------------------- PartList* pl = track->parts(); int lastTick = 0; for (iEvent i = tevents->begin(); i != tevents->end(); ++i) { Event event = i->second; int epos = event.tick() + event.lenTick(); if (epos > lastTick) lastTick = epos; } QString partname = track->name(); int len = song->roundUpBar(lastTick + 1); // p3.3.27 if (config.importMidiSplitParts) { int bar2, beat; unsigned tick; sigmap.tickValues(len, &bar2, &beat, &tick); int lastOff = 0; int st = -1; // start tick current part int x1 = 0; // start tick current measure int x2 = 0; // end tick current measure for (int bar = 0; bar < bar2; ++bar, x1 = x2) { ///x2 = sigmap.bar2tick(bar+1, 0, 0); x2 = sigmap.bar2tick(bar + 1, 0, 0); if (lastOff > x2) { // this measure is busy! continue; } iEvent i1 = tevents->lower_bound(x1); iEvent i2 = tevents->lower_bound(x2); if (i1 == i2) { // empty? if (st != -1) { MidiPart* part = new MidiPart(track); part->setTick(st); part->setLenTick(x1 - st); // printf("new part %d len: %d\n", st, x1-st); part->setName(partname); pl->add(part); st = -1; } } else { if (st == -1) st = x1; // begin new part //HACK: //lastOff: for (iEvent i = i1; i != i2; ++i) { Event event = i->second; if (event.type() == Note) { int off = event.tick() + event.lenTick(); if (off > lastOff) lastOff = off; } } } } if (st != -1) { MidiPart* part = new MidiPart(track); part->setTick(st); // printf("new part %d len: %d\n", st, x2-st); part->setLenTick(x2 - st); part->setName(partname); pl->add(part); } } else { // Just one long part... MidiPart* part = new MidiPart(track); //part->setTick(st); part->setTick(0); part->setLenTick(len); part->setName(partname); pl->add(part); } //------------------------------------------------------------- // assign events to parts //------------------------------------------------------------- for (iPart p = pl->begin(); p != pl->end(); ++p) { MidiPart* part = (MidiPart*) (p->second); int stick = part->tick(); int etick = part->tick() + part->lenTick(); iEvent r1 = tevents->lower_bound(stick); iEvent r2 = tevents->lower_bound(etick); int startTick = part->tick(); EventList* el = part->events(); for (iEvent i = r1; i != r2; ++i) { Event ev = i->second; int ntick = ev.tick() - startTick; ev.setTick(ntick); el->add(ev); } tevents->erase(r1, r2); } if (tevents->size()) printf("-----------events left: %zd\n", tevents->size()); for (iEvent i = tevents->begin(); i != tevents->end(); ++i) { printf("%d===\n", i->first); i->second.dump(); } // all events should be processed: assert(tevents->empty()); }
void buildMidiEventList(EventList* del, const MPEventList* el, MidiTrack* track, int div, bool addSysexMeta, bool doLoops) { int hbank = 0xff; int lbank = 0xff; int rpnh = -1; int rpnl = -1; int datah = 0; int datal = 0; int dataType = 0; // 0 : disabled, 0x20000 : rpn, 0x30000 : nrpn EventList mel; for (iMPEvent i = el->begin(); i != el->end(); ++i) { MidiPlayEvent ev = *i; if (!addSysexMeta && (ev.type() == ME_SYSEX || ev.type() == ME_META)) continue; if (!(ev.type() == ME_SYSEX || ev.type() == ME_META || ((ev.channel() == track->outChannel()) && (ev.port() == track->outPort())))) continue; unsigned tick = ev.time(); // Added by Tim. p3.3.8 // Added by T356. if (doLoops) { if (tick >= song->lPos().tick() && tick < song->rPos().tick()) { int loopn = ev.loopNum(); int loopc = audio->loopCount(); int cmode = song->cycleMode(); // CYCLE_NORMAL, CYCLE_MIX, CYCLE_REPLACE // If we want REPLACE and the event was recorded in a previous loop, // just ignore it. This will effectively ignore ALL previous loop events inside // the left and right markers, regardless of where recording was started or stopped. // We want to keep any loop 0 note-offs from notes which crossed over the left marker. // To avoid more searching here, just keep ALL note-offs from loop 0, and let code below // sort out and keep which ones had note-ons. if (!(ev.isNoteOff() && loopn == 0)) { if (cmode == Song::CYCLE_REPLACE && loopn < loopc) { // Added by Tim. p3.3.8 //printf("buildMidiEventList: CYCLE_REPLACE t:%d type:%d A:%d B:%d ln:%d lc:%d\n", tick, ev.type(), ev.dataA(), ev.dataB(), loopn, loopc); continue; } // If we want NORMAL, same as REPLACE except keep all events from the previous loop // from rec stop position to right marker (and beyond). if (cmode == Song::CYCLE_NORMAL) { // Not sure of accuracy here. Adjust? Adjusted when used elsewhere? unsigned endRec = audio->getEndRecordPos().tick(); if ((tick < endRec && loopn < loopc) || (tick >= endRec && loopn < (loopc - 1))) { // Added by Tim. p3.3.8 //printf("buildMidiEventList: CYCLE_NORMAL t:%d type:%d A:%d B:%d ln:%d lc:%d\n", tick, ev.type(), ev.dataA(), ev.dataB(), loopn, loopc); continue; } } } } } Event e; switch (ev.type()) { case ME_NOTEON: e.setType(Note); if (track->type() == Track::DRUM) { int instr = drumInmap[ev.dataA()]; e.setPitch(instr); } else { e.setPitch(ev.dataA()); } e.setVelo(ev.dataB()); e.setLenTick(0); break; case ME_NOTEOFF: e.setType(Note); if (track->type() == Track::DRUM) { int instr = drumInmap[ev.dataA()]; e.setPitch(instr); } else e.setPitch(ev.dataA()); e.setVelo(0); e.setVeloOff(ev.dataB()); e.setLenTick(0); break; case ME_POLYAFTER: e.setType(PAfter); e.setA(ev.dataA()); e.setB(ev.dataB()); break; case ME_CONTROLLER: { int val = ev.dataB(); switch (ev.dataA()) { case CTRL_HBANK: hbank = val; break; case CTRL_LBANK: lbank = val; break; case CTRL_HDATA: datah = val; // check if a CTRL_LDATA follows // e.g. wie have a 14 bit controller: { iMPEvent ii = i; ++ii; bool found = false; for (; ii != el->end(); ++ii) { MidiPlayEvent ev = *ii; if (ev.type() == ME_CONTROLLER) { if (ev.dataA() == CTRL_LDATA) { // handle later found = true; } break; } } if (!found) { if (rpnh == -1 || rpnl == -1) { printf("parameter number not defined, data 0x%x\n", datah); } else { int ctrl = dataType | (rpnh << 8) | rpnl; e.setType(Controller); e.setA(ctrl); e.setB(datah); } } } break; case CTRL_LDATA: datal = val; if (rpnh == -1 || rpnl == -1) { printf("parameter number not defined, data 0x%x 0x%x, tick %d, channel %d\n", datah, datal, tick, track->outChannel()); break; } // assume that the sequence is always // CTRL_HDATA - CTRL_LDATA // eg. that LDATA is always send last e.setType(Controller); // 14 Bit RPN/NRPN e.setA((dataType + 0x30000) | (rpnh << 8) | rpnl); e.setB((datah << 7) | datal); break; case CTRL_HNRPN: rpnh = val; dataType = 0x30000; break; case CTRL_LNRPN: rpnl = val; dataType = 0x30000; break; case CTRL_HRPN: rpnh = val; dataType = 0x20000; break; case CTRL_LRPN: rpnl = val; dataType = 0x20000; break; default: e.setType(Controller); int ctl = ev.dataA(); e.setA(ctl); if (track->type() == Track::DRUM) { // Is it a drum controller event, according to the track port's instrument? MidiController *mc = midiPorts[track->outPort()].drumController(ctl); if (mc) // Store an index into the drum map. e.setA((ctl & ~0xff) | drumInmap[ctl & 0x7f]); } e.setB(val); break; } } break; case ME_PROGRAM: e.setType(Controller); e.setA(CTRL_PROGRAM); e.setB((hbank << 16) | (lbank << 8) | ev.dataA()); break; case ME_AFTERTOUCH: e.setType(CAfter); e.setA(ev.dataA()); break; case ME_PITCHBEND: e.setType(Controller); e.setA(CTRL_PITCH); e.setB(ev.dataA()); break; case ME_SYSEX: e.setType(Sysex); e.setData(ev.data(), ev.len()); break; case ME_META: { const unsigned char* data = ev.data(); switch (ev.dataA()) { case 0x01: // Text if (track->comment().isEmpty()) track->setComment(QString((const char*) data)); else track->setComment(track->comment() + "\n" + QString((const char*) data)); break; case 0x03: // Sequence-/TrackName track->setName(QString((char*) data)); break; case 0x6: // Marker { unsigned ltick = CALC_TICK(tick); //(tick * config.division + div/2) / div; song->addMarker(QString((const char*) (data)), ltick, false); } break; case 0x5: // Lyrics case 0x8: // text case 0x9: case 0xa: break; case 0x0f: // Track Comment track->setComment(QString((char*) data)); break; case 0x51: // Tempo { unsigned tempo = data[2] + (data[1] << 8) + (data[0] << 16); unsigned ltick = CALC_TICK(tick); // (unsigned(tick) * unsigned(config.division) + unsigned(div/2)) / unsigned(div); // After ca 10 mins 32 bits will not be enough... This expression has to be changed/factorized or so in some "sane" way... tempomap.addTempo(ltick, tempo); } break; case 0x58: // Time Signature { int timesig_z = data[0]; int n = data[1]; int timesig_n = 1; for (int i = 0; i < n; i++) timesig_n *= 2; int ltick = CALC_TICK(tick); //(tick * config.division + div/2) / div; ///sigmap.add(ltick, timesig_z, timesig_n); AL::sigmap.add(ltick, AL::TimeSignature(timesig_z, timesig_n)); } break; case 0x59: // Key Signature // track->scale.set(data[0]); // track->scale.setMajorMinor(data[1]); break; default: printf("unknown Meta 0x%x %d\n", ev.dataA(), ev.dataA()); } } break; } // switch(ev.type() if (!e.empty()) { e.setTick(tick); // Added by Tim. p3.3.8 //printf("buildMidiEventList: mel adding t:%d type:%d A:%d B:%d C:%d\n", tick, e.type(), e.dataA(), e.dataB(), e.dataC()); mel.add(e); } } // i != el->end() //--------------------------------------------------- // resolve NoteOff events //--------------------------------------------------- // for (iEvent i = mel.begin(); i != mel.end(); ++i) { // Event event = i->second; // if (event.isNote()) // event.setLenTick(0); // } // Added by Tim. p3.3.8 // The loop is a safe way to delete while iterating. bool loop; do { loop = false; for (iEvent i = mel.begin(); i != mel.end(); ++i) { Event ev = i->second; if (ev.isNote()) { if (ev.isNoteOff()) { iEvent k; bool found = false; for (k = i; k != mel.end(); ++k) { Event event = k->second; if (event.tick() > ev.tick()) break; if (event.isNoteOff(ev)) { ev.setLenTick(1); ev.setVelo(event.velo()); ev.setVeloOff(0); // Added by Tim. p3.3.8 //printf("buildMidiEventList: found note off: event t:%d len:%d type:%d A:%d B:%d C:%d ev t:%d len:%d type:%d A:%d B:%d C:%d\n", event.tick(), event.lenTick(), event.type(), event.dataA(), event.dataB(), event.dataC(), ev.tick(), ev.lenTick(), ev.type(), ev.dataA(), ev.dataB(), ev.dataC()); found = true; break; } } if (!found) { printf("NOTE OFF without Note ON tick %d type %d %d %d\n", ev.tick(), ev.type(), ev.pitch(), ev.velo()); } else { mel.erase(k); // Changed by Tim. p3.3.8 //continue; loop = true; break; } } // Added by Tim. p3.3.8 // If the event length is not zero, it means the event and its // note on/off have already been taken care of. So ignore it. if (ev.lenTick() != 0) { continue; } iEvent k; for (k = mel.lower_bound(ev.tick()); k != mel.end(); ++k) { Event event = k->second; if (ev.isNoteOff(event)) { int t = k->first - i->first; if (t <= 0) { if (debugMsg) { printf("Note len is (%d-%d)=%d, set to 1\n", k->first, i->first, k->first - i->first); ev.dump(); event.dump(); } t = 1; } ev.setLenTick(t); ev.setVeloOff(event.veloOff()); // Added by Tim. p3.3.8 //printf("buildMidiEventList: set len and velOff: event t:%d len:%d type:%d A:%d B:%d C:%d ev t:%d len:%d type:%d A:%d B:%d C:%d\n", event.tick(), event.lenTick(), event.type(), event.dataA(), event.dataB(), event.dataC(), ev.tick(), ev.lenTick(), ev.type(), ev.dataA(), ev.dataB(), ev.dataC()); break; } } if (k == mel.end()) { printf("-no note-off! %d pitch %d velo %d\n", ev.tick(), ev.pitch(), ev.velo()); // // switch off at end of measure // int endTick = song->roundUpBar(ev.tick() + 1); ev.setLenTick(endTick - ev.tick()); } else { mel.erase(k); // Added by Tim. p3.3.8 loop = true; break; } } } } while (loop); // DEBUG: any note offs left? // Removed by Tim. p3.3.8 //for (iEvent i = mel.begin(); i != mel.end(); ++i) { // Event ev = i->second; // if (ev.isNoteOff()) { // printf("+extra note-off! %d pitch %d velo %d\n", // i->first, ev.pitch(), ev.velo()); // ev.dump(); // } // } for (iEvent i = mel.begin(); i != mel.end(); ++i) { Event ev = i->second; if (ev.isNoteOff()) { printf("+extra note-off! %d pitch %d velo %d\n", i->first, ev.pitch(), ev.velo()); // ev.dump(); continue; } int tick = CALC_TICK(ev.tick()); //(ev.tick() * config.division + div/2) / div; if (ev.isNote()) { int lenTick = CALC_TICK(ev.lenTick()); //(ev.lenTick() * config.division + div/2) / div; ev.setLenTick(lenTick); } ev.setTick(tick); del->add(ev); } }
void Audio::collectEvents(MidiTrack* track, unsigned int cts, unsigned int nts) { int port = track->outPort(); int channel = track->outChannel(); int defaultPort = port; MidiDevice* md = midiPorts[port].device(); MPEventList* playEvents = md->playEvents(); MPEventList* stuckNotes = md->stuckNotes(); PartList* pl = track->parts(); for (iPart p = pl->begin(); p != pl->end(); ++p) { MidiPart* part = (MidiPart*) (p->second); // dont play muted parts if (part->mute()) continue; EventList* events = part->events(); unsigned partTick = part->tick(); unsigned partLen = part->lenTick(); int delay = track->delay; if (cts > nts) { printf("processMidi: FATAL: cur > next %d > %d\n", cts, nts); return; } unsigned offset = delay + partTick; if (offset > nts) continue; unsigned stick = (offset > cts) ? 0 : cts - offset; unsigned etick = nts - offset; // By T356. Do not play events which are past the end of this part. if (etick > partLen) continue; iEvent ie = events->lower_bound(stick); iEvent iend = events->lower_bound(etick); for (; ie != iend; ++ie) { Event ev = ie->second; port = defaultPort; //Reset each loop // // dont play any meta events // if (ev.type() == Meta) continue; if (track->type() == Track::DRUM) { int instr = ev.pitch(); // ignore muted drums if (ev.isNote() && drumMap[instr].mute) continue; } unsigned tick = ev.tick() + offset; unsigned frame = tempomap.tick2frame(tick) + frameOffset; switch (ev.type()) { case Note: { int len = ev.lenTick(); int pitch = ev.pitch(); int velo = ev.velo(); if (track->type() == Track::DRUM) { // // Map drum-notes to the drum-map values // int instr = ev.pitch(); pitch = drumMap[instr].anote; port = drumMap[instr].port; //This changes to non-default port channel = drumMap[instr].channel; velo = int(double(velo) * (double(drumMap[instr].vol) / 100.0)); } else { // // transpose non drum notes // pitch += (track->transposition + song->globalPitchShift()); } if (pitch > 127) pitch = 127; if (pitch < 0) pitch = 0; velo += track->velocity; velo = (velo * track->compression) / 100; if (velo > 127) velo = 127; if (velo < 1) // no off event velo = 1; len = (len * track->len) / 100; if (len <= 0) // dont allow zero length len = 1; int veloOff = ev.veloOff(); if (port == defaultPort) { //printf("Adding event normally: frame=%d port=%d channel=%d pitch=%d velo=%d\n",frame, port, channel, pitch, velo); // p3.3.25 // If syncing to external midi sync, we cannot use the tempo map. // Therefore we cannot get sub-tick resolution. Just use ticks instead of frames. if (extSyncFlag.value()) playEvents->add(MidiPlayEvent(tick, port, channel, 0x90, pitch, velo)); else playEvents->add(MidiPlayEvent(frame, port, channel, 0x90, pitch, velo)); stuckNotes->add(MidiPlayEvent(tick + len, port, channel, veloOff ? 0x80 : 0x90, pitch, veloOff)); } else { //Handle events to different port than standard. MidiDevice* mdAlt = midiPorts[port].device(); if (mdAlt) { // p3.3.25 if (extSyncFlag.value()) mdAlt->playEvents()->add(MidiPlayEvent(tick, port, channel, 0x90, pitch, velo)); else mdAlt->playEvents()->add(MidiPlayEvent(frame, port, channel, 0x90, pitch, velo)); mdAlt->stuckNotes()->add(MidiPlayEvent(tick + len, port, channel, veloOff ? 0x80 : 0x90, pitch, veloOff)); } } if (velo > track->activity()) track->setActivity(velo); } break; // Added by T356. case Controller: { //int len = ev.lenTick(); //int pitch = ev.pitch(); if (track->type() == Track::DRUM) { int ctl = ev.dataA(); // Is it a drum controller event, according to the track port's instrument? MidiController *mc = midiPorts[defaultPort].drumController(ctl); if (mc) { int instr = ctl & 0x7f; ctl &= ~0xff; int pitch = drumMap[instr].anote & 0x7f; port = drumMap[instr].port; //This changes to non-default port channel = drumMap[instr].channel; MidiDevice* mdAlt = midiPorts[port].device(); if (mdAlt) { // p3.3.25 // If syncing to external midi sync, we cannot use the tempo map. // Therefore we cannot get sub-tick resolution. Just use ticks instead of frames. if (extSyncFlag.value()) mdAlt->playEvents()->add(MidiPlayEvent(tick, port, channel, ME_CONTROLLER, ctl | pitch, ev.dataB())); else //playEvents->add(MidiPlayEvent(frame, port, channel, ev)); mdAlt->playEvents()->add(MidiPlayEvent(frame, port, channel, ME_CONTROLLER, ctl | pitch, ev.dataB())); } break; } } // p3.3.25 if (extSyncFlag.value()) playEvents->add(MidiPlayEvent(tick, port, channel, ev)); else playEvents->add(MidiPlayEvent(frame, port, channel, ev)); } break; default: // p3.3.25 if (extSyncFlag.value()) playEvents->add(MidiPlayEvent(tick, port, channel, ev)); else playEvents->add(MidiPlayEvent(frame, port, channel, ev)); break; } } } }
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; } }