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 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 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()); }
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; }