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); } }/*}}}*/
PyObject* modifyPart(PyObject*, PyObject* part) { int id = getPythonPartId(part); Part* opart = NULL; // Verify a part with that id actually exists, then get it TrackList* tracks = song->tracks(); for (ciTrack t = tracks->begin(); t != tracks->end(); ++t) { Track* track = *t; for (ciPart p = track->parts()->begin(); p != track->parts()->end(); p++) { if (p->second->sn() == id) { opart = p->second; break; } } } if (opart == NULL) { printf("Part doesn't exist!\n"); return NULL; } // Remove all note and controller events from current part eventlist std::list< std::pair<const unsigned, Event> > elist; MidiPart* npart = new MidiPart((MidiTrack*) opart->track()); npart->setTick(opart->tick()); npart->setLenTick(opart->lenTick()); npart->setSn(opart->sn()); for (iEvent e = opart->events()->begin(); e != opart->events()->end(); e++) { Event& event = e->second; if (event.type() == Note || event.type() == Controller) continue; npart->events()->add(event); } addPyPartEventsToOOMidiPart(npart, part); //song->startUndo(); song->changePart(opart, npart); //song->endUndo(SC_TRACK_MODIFIED | SC_PART_MODIFIED | SC_PART_INSERTED); // Crash! Probably since the call ends up in Qt GUI thread from this thread QPybridgeEvent* pyevent = new QPybridgeEvent(QPybridgeEvent::SONG_UPDATE, SC_TRACK_MODIFIED | SC_PART_MODIFIED | SC_PART_INSERTED); QApplication::postEvent(song, pyevent); Py_INCREF(Py_None); return Py_None; }
void EventCanvas::songChanged(int flags)/*{{{*/ { // Is it simply a midi controller value adjustment? Forget it. if (flags == SC_MIDI_CONTROLLER) return; if (flags & ~SC_SELECTION) { _items.clear(); start_tick = MAXINT; end_tick = 0; _curPart = 0; for (iPart p = editor->parts()->begin(); p != editor->parts()->end(); ++p) { MidiPart* part = (MidiPart*) (p->second); if (part->sn() == _curPartId) _curPart = part; unsigned stick = part->tick(); unsigned len = part->lenTick(); unsigned etick = stick + len; if (stick < start_tick) start_tick = stick; if (etick > end_tick) end_tick = etick; EventList* el = part->events(); for (iEvent i = el->begin(); i != el->end(); ++i) { Event e = i->second; if (e.isNote()) { addItem(part, e); } } } } Event event; MidiPart* part = 0; int x = 0; CItem* nevent = 0; int n = 0; // count selections for (iCItem k = _items.begin(); k != _items.end(); ++k) { Event ev = k->second->event(); bool selected = ev.selected(); if (selected) { k->second->setSelected(true); ++n; if (!nevent) { nevent = k->second; Event mi = nevent->event(); curVelo = mi.velo(); } } } start_tick = song->roundDownBar(start_tick); end_tick = song->roundUpBar(end_tick); if (n == 1) { x = nevent->x(); event = nevent->event(); part = (MidiPart*) nevent->part(); if (_curPart != part) { _curPart = part; _curPartId = _curPart->sn(); curPartChanged(); } } emit selectionChanged(x, event, part); if (_curPart == 0) { _curPart = (MidiPart*) (editor->parts()->begin()->second); if(_curPart) { editor->setCurCanvasPart(_curPart); } } updateCItemsZValues(); redraw(); }/*}}}*/
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 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 Audio::preloadControllers()/*{{{*/ { midiBusy = true; MidiTrackList* tracks = song->midis(); for (iMidiTrack it = tracks->begin(); it != tracks->end(); ++it) { MidiTrack* track = *it; //activePorts[track->outPort()] = true; QList<ProcessList*> pcevents; int port = track->outPort(); int channel = track->outChannel(); int defaultPort = port; MidiDevice* md = midiPorts[port].device(); if (!md) { continue; } MPEventList* playEvents = md->playEvents(); playEvents->erase(playEvents->begin(), playEvents->end()); PartList* pl = track->parts(); for (iPart p = pl->begin(); p != pl->end(); ++p) { MidiPart* part = (MidiPart*) (p->second); EventList* events = part->events(); unsigned partTick = part->tick(); //unsigned partLen = part->lenTick(); int delay = track->delay; unsigned offset = delay + partTick; for (iEvent ie = events->begin(); ie != events->end(); ++ie) { Event ev = ie->second; port = defaultPort; unsigned tick = ev.tick() + offset; //unsigned frame = tempomap.tick2frame(tick) + frameOffset; switch (ev.dataA()) { case CTRL_PROGRAM: { ProcessList *pl = new ProcessList; pl->port = port; pl->channel = channel; pl->dataB = ev.dataB(); bool addEvent = true; for(int i = 0; i < pcevents.size(); ++i) { ProcessList* ipl = pcevents.at(i); if(ipl->port == pl->port && ipl->channel == pl->channel && ipl->dataB == pl->dataB) { addEvent = false; break; } } if(addEvent) { printf("Audio::preloadControllers() Loading event @ tick: %d - on channel: %d - on port: %d - dataA: %d - dataB: %d\n", tick, channel, port, ev.dataA(), ev.dataB()); pcevents.append(pl); playEvents->add(MidiPlayEvent(tick, port, channel, ev)); } } break; default: break; } } } md->setNextPlayEvent(playEvents->begin()); } midiBusy = false; }/*}}}*/
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; } }
PyObject* getParts(PyObject*, PyObject* args) { TrackList* tracks = song->tracks(); const char* trackname; if (!PyArg_ParseTuple(args, "s", &trackname)) { return NULL; } PyObject* pyparts = Py_BuildValue("[]"); for (ciTrack t = tracks->begin(); t != tracks->end(); ++t) { Track* track = *t; if (track->name() != trackname) continue; PartList* parts = track->parts(); for (ciPart p = parts->begin(); p != parts->end(); p++) { Part* part = p->second; MidiPart* mpart = (MidiPart*) part; PyObject* pypart = PyDict_New(); int tick = mpart->tick(); int lentick = mpart->lenTick(); int serialnr = mpart->sn(); PyObject* pstrtick = Py_BuildValue("s", "tick"); PyObject* pitick = Py_BuildValue("i", tick); PyObject* pstrid = Py_BuildValue("s", "id"); PyObject* pstrserial = Py_BuildValue("i", serialnr); PyObject* pstrlen = Py_BuildValue("s", "len"); PyObject* pstrtick2 = Py_BuildValue("i", lentick); PyDict_SetItem(pypart, pstrtick, pitick); PyDict_SetItem(pypart, pstrid, pstrserial); PyDict_SetItem(pypart, pstrlen, pstrtick2); Py_DECREF(pstrtick); Py_DECREF(pitick); Py_DECREF(pstrid); Py_DECREF(pstrserial); Py_DECREF(pstrlen); Py_DECREF(pstrtick2); // Pack midi events into list before wrapping it all up EventList* events = mpart->events(); PyObject* pyevents = Py_BuildValue("[]"); for (ciEvent e = events->begin(); e != events->end(); e++) { PyObject* pyevent = PyDict_New(); // The event structure - a dictionary with keys 'type','tick','data' const Event& event = e->second; unsigned tick = e->first; PyObject* eventdata = Py_BuildValue("[i,i,i]", event.dataA(), event.dataB(), event.dataC()); PyObject* pstrdata = Py_BuildValue("s", "data"); pstrtick = Py_BuildValue("s", "tick"); PyObject* pitickval = Py_BuildValue("i", tick); PyDict_SetItem(pyevent, pstrdata, eventdata); PyDict_SetItem(pyevent, pstrtick, pitickval); Py_DECREF(eventdata); Py_DECREF(pstrdata); Py_DECREF(pstrtick); Py_DECREF(pitickval); switch (event.type()) { case Note: { PyObject* pstrtype = Py_BuildValue("s", "type"); PyObject* pstrnote = Py_BuildValue("s", "note"); PyObject* pstrlen = Py_BuildValue("s", "len"); PyObject* pilentick = Py_BuildValue("i", event.lenTick()); PyDict_SetItem(pyevent, pstrtype, pstrnote); PyDict_SetItem(pyevent, pstrlen, pilentick); Py_DECREF(pstrtype); Py_DECREF(pstrnote); Py_DECREF(pstrlen); Py_DECREF(pilentick); break; } case Controller: { PyObject* pstrtype = Py_BuildValue("s", "type"); PyObject* pstrctrl = Py_BuildValue("s", "ctrl"); PyDict_SetItem(pyevent, pstrtype, pstrctrl); Py_DECREF(pstrtype); Py_DECREF(pstrctrl); break; } default: printf("Event type not supported yet: %d\n", event.type()); break; } PyList_Append(pyevents, pyevent); Py_DECREF(pyevent); } Py_DECREF(pyevents); // Add the event list to the pypart dictionary PyObject* pystrevents = Py_BuildValue("s", "events"); PyDict_SetItem(pypart, pystrevents, pyevents); Py_DECREF(pystrevents); PyList_Append(pyparts, pypart); Py_DECREF(pypart); } return pyparts; } return NULL; }
void ListEdit::songChanged(int type) { if (type == 0) return; if (type & (SC_PART_REMOVED | SC_PART_MODIFIED | SC_PART_INSERTED | SC_EVENT_REMOVED | SC_EVENT_MODIFIED | SC_EVENT_INSERTED | SC_SELECTION)) { if (type & (SC_PART_REMOVED | SC_PART_INSERTED)) genPartlist(); // close window if editor has no parts anymore if (parts()->empty()) { close(); return; } liste->setSortingEnabled(false); if (type == SC_SELECTION) { bool update = false; QTreeWidgetItem* ci = 0; liste->blockSignals(true); for (int row = 0; row < liste->topLevelItemCount(); ++row) { QTreeWidgetItem* i = liste->topLevelItem(row); if (i->isSelected() ^ ((EventListItem*) i)->event.selected()) { i->setSelected(((EventListItem*) i)->event.selected()); if (i->isSelected()) ci = i; update = true; } } if (update) { if (ci) { liste->setCurrentItem(ci); liste->scrollToItem(ci, QAbstractItemView::EnsureVisible); } //liste->update(); } liste->blockSignals(false); } else { curPart = 0; curTrack = 0; liste->clear(); for (iPart p = parts()->begin(); p != parts()->end(); ++p) { MidiPart* part = (MidiPart*) (p->second); if (part->sn() == curPartId) curPart = part; EventList* el = part->events(); for (iEvent i = el->begin(); i != el->end(); ++i) { EventListItem* item = new EventListItem(liste, i->second, part); for (int col = 0; col < liste->columnCount(); ++col) item->setText(col, item->text(col)); item->setSelected(i->second.selected()); if (item->event.tick() == (unsigned) selectedTick) { //prevent compiler warning: comparison of signed/unsigned) liste->setCurrentItem(item); item->setSelected(true); liste->scrollToItem(item, QAbstractItemView::EnsureVisible); } } } } // p3.3.34 //if (curPart == 0) // curPart = (MidiPart*)(parts()->begin()->second); //curTrack = curPart->track(); if (!curPart) { if (!parts()->empty()) { curPart = (MidiPart*) (parts()->begin()->second); if (curPart) curTrack = curPart->track(); else curPart = 0; } } } liste->setSortingEnabled(true); }