bool MidiFile::writeTrack(const MidiTrack &t) { write("MTrk", 4); qint64 lenpos = fp->pos(); writeLong(0); // dummy len status = -1; int tick = 0; for (auto i : t.events()) { int ntick = i.first; putvl(ntick - tick); // write tick delta // // if track channel != -1, then use this // channel for all events in this track // if (t.outChannel() != -1) writeEvent(i.second); tick = ntick; } //--------------------------------------------------- // write "End Of Track" Meta // write Track Len // putvl(1); put(0xff); // Meta put(0x2f); // EOT putvl(0); // len 0 qint64 endpos = fp->pos(); fp->seek(lenpos); writeLong(endpos-lenpos-4); // tracklen fp->seek(endpos); return false; }
PyObject* getMidiControllerValue(PyObject*, PyObject* args) { const char* trackname; int ctrltype; if (!PyArg_ParseTuple(args, "si", &trackname, &ctrltype)) { return NULL; } Track* t = song->findTrack(QString(trackname)); if (t == NULL) return NULL; if (t->isMidiTrack() == false) { Py_INCREF(Py_None); return Py_None; } MidiTrack* track = (MidiTrack*) t; int channel = track->outChannel(); int outport = track->outPort(); MidiPort* mp = &midiPorts[outport]; if (mp == NULL) return Py_BuildValue("i", -1); int value = mp->hwCtrlState(channel, ctrltype); return Py_BuildValue("i", value); }
MidiTrack* MidiTrack::createTempoTrack() { MidiTrack* T = new MidiTrack(); T->insertEvent(new TimeSignature()); T->insertEvent(new Tempo()); return T; }
void TrackListView::updatePartSelection(Part* part)/*{{{*/ { if(part) { MidiTrack* track = part->track(); Part* curPart = m_editor->curCanvasPart(); if (curPart) { song->setRecordFlag(curPart->track(), false); } m_editor->setCurCanvasPart(part); Song::movePlaybackToPart(part); // and turn it on for the new parts track song->setRecordFlag(track, true); song->deselectTracks(); song->deselectAllParts(); track->setSelected(true); part->setSelected(true); song->update(SC_SELECTION); int psn = part->sn(); for(int i = 0; i < m_model->rowCount(); ++i) { QStandardItem* item = m_model->item(i, 0); if(item) { int type = item->data(TrackRole).toInt(); if(type == 1) {//TrackMode continue; } else {//PartMode int sn = item->data(PartRole).toInt(); if(psn == sn) { m_selectedIndex = item->row(); m_tempColor = item->foreground(); m_model->blockSignals(true); item->setForeground(QColor(99, 36, 36)); m_model->blockSignals(false); update(); m_table->selectRow(m_selectedIndex); m_table->scrollTo(m_model->index(m_selectedIndex, 0)); m_colorRows.append(m_selectedIndex); QTimer::singleShot(350, this, SLOT(updateColor())); break; } } } } } }/*}}}*/
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().toLatin1().constData(), p, part->cevents(), part->cevents()->refCount(), j); Track* t = p->track(); if (t) { 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().toLatin1().constData(), 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]; 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; } } }/*}}}*/
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) { MidiTrack* t = p->track(); if (t) { 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]; 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; } } }/*}}}*/
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... { 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().toLatin1().constData(), ip->second, ip->second->name().toLatin1().constData(), 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; } } // 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); }/*}}}*/
void AbstractMidiEditor::writePartList(int level, Xml& xml) const/*{{{*/ { for (ciPart p = _pl->begin(); p != _pl->end(); ++p) { Part* part = p->second; MidiTrack* track = part->track(); int trkIdx = song->artracks()->index(track); int partIdx = track->parts()->index(part); if ((trkIdx == -1) || (partIdx == -1)) printf("AbstractMidiEditor::writePartList error: trkIdx:%d partIdx:%d\n", trkIdx, partIdx); xml.put(level, "<part>%d:%d</part>", trkIdx, partIdx); } }/*}}}*/
void LOS::importPart() { unsigned curPos = song->cpos(); MidiTrackList* tracks = song->tracks(); MidiTrack* track = 0; // Get first selected track: for (iMidiTrack i = tracks->begin(); i != tracks->end(); i++) { MidiTrack* t = *i; if (t->selected()) { // Changed by T356. Support mixed .mpt files. { track = t; break; } } } if (track) { bool loadAll; QString filename = getOpenFileName(QString(""), part_file_pattern, this, tr("LOS: load part"), &loadAll); if (!filename.isEmpty()) { // Make a backup of the current clone list, to retain any 'copy' items, // so that pasting works properly after. CloneList copyCloneList = cloneList; // Clear the clone list to prevent any dangerous associations with // current non-original parts. cloneList.clear(); importPartToTrack(filename, curPos, track); // Restore backup of the clone list, to retain any 'copy' items, // so that pasting works properly after. cloneList.clear(); cloneList = copyCloneList; } } else { QMessageBox::warning(this, QString("LOS"), tr("No track selected for import")); } }
void EventCanvas::populateMultiSelect(CItem* baseItem)/*{{{*/ { if(editor->isGlobalEdit() && baseItem) { PartList* pl = editor->parts(); int curTranspose = ((MidiTrack*)baseItem->part()->track())->getTransposition(); Event curEvent = baseItem->event(); int curPitch = curEvent.pitch(); int curRawPitch = curPitch - curTranspose; //int curLen = curEvent.lenTick(); m_multiSelect.clear(); for(iPart p = pl->begin(); p != pl->end(); ++p) { if(p->second == _curPart) continue; CItemList pitems = getItemlistForPart(p->second); for (iCItem i = pitems.begin(); i != pitems.end(); ++i) { MidiTrack* mtrack = (MidiTrack*)i->second->part()->track(); int transp = mtrack->getTransposition(); Event e = i->second->event(); if(e.empty()) continue; int pitch = e.pitch(); int rpitch = pitch - transp; //int len = e.lenTick(); //printf("Current pitch: %d, rawpitch: %d - note pitch: %d, raw: %d\n", curPitch, curRawPitch, pitch, rpitch); if(e.tick() == curEvent.tick() && rpitch == curRawPitch/*, len == curLen*/) { m_multiSelect.add(i->second); break; } } } //printf("MultiSelect list size: %d \n", (int)m_multiSelect.size()); } }/*}}}*/
void TrackListView::selectionChanged(const QModelIndex current, const QModelIndex)/*{{{*/ { if(!current.isValid()) return; int row = current.row(); m_selectedIndex = row; QStandardItem* item = m_model->item(row); if(item) { int type = item->data(TrackRole).toInt(); qint64 tid = item->data(TrackIdRole).toLongLong(); bool checked = (item->checkState() == Qt::Checked); //QString trackName = item->data(TrackNameRole).toString(); MidiTrack* track = song->findTrackById(tid); if(!track || !m_editor || type == 1 || !checked) return; PartList* list = track->parts(); int sn = item->data(PartRole).toInt(); unsigned tick = item->data(TickRole).toInt(); Part* part = list->find(tick, sn); updatePartSelection(part); } }/*}}}*/
QWidget* MenuList::createWidget(QWidget* parent) { if(!_track) return 0; //if(!_track->isMidiTrack() || _track->type() != Track::AUDIO_SOFTSYNTH) // return 0; MidiTrack* track = (MidiTrack*) _track; MidiDevice* md = 0; int port = -1; if (track->type() == Track::AUDIO_SOFTSYNTH) { md = dynamic_cast<MidiDevice*> (track); if (md) port = md->midiPort(); } else port = track->outPort(); list = new QListWidget(parent); list->setSelectionMode(QAbstractItemView::SingleSelection); list->setAlternatingRowColors(true); list->setEditTriggers(QAbstractItemView::NoEditTriggers); list->setFixedHeight(300); for (int i = 0; i < MIDI_PORTS; ++i) { QString name; name.sprintf("%d:%s", i + 1, midiPorts[i].portname().toLatin1().constData()); list->insertItem(i, name); if (i == port) list->setCurrentRow(i); } connect(list, SIGNAL(itemPressed(QListWidgetItem*)), this, SLOT(updateData(QListWidgetItem*))); //connect(list, SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(updateData(QListWidgetItem*))); //connect(list, SIGNAL(itemActivated(QListWidgetItem*)), this, SLOT(updateData(QListWidgetItem*))); return list; }
std::multimap<ReducedFraction, std::string> extractLyricsFromTrack(const MidiTrack &lyricsTrack, int division) { std::multimap<ReducedFraction, std::string> lyrics; for (const auto &i: lyricsTrack.events()) { const auto& e = i.second; if (isLyricEvent(e)) { const uchar* data = (uchar*)e.edata(); std::string text = MidiCharset::fromUchar(data); if (isLyricText(text)) { const auto tick = toMuseScoreTicks(i.first, division); // no charset handling here lyrics.insert({tick, text}); } } } return lyrics; }
bool isLyricsTrack(const MidiTrack &lyricsTrack) { bool lyricsFlag = false; for (const auto &i: lyricsTrack.events()) { const auto& e = i.second; if (isLyricEvent(e)) { const uchar* data = (uchar*)e.edata(); // no charset handling here std::string text = MidiCharset::fromUchar(data); if (isLyricText(text)) { if (!lyricsFlag) lyricsFlag = true; } } else if (e.type() == ME_NOTE) return false; } return lyricsFlag; }
MidiTrack MidiTrack::ReadFromStream(std::istream &stream) { // Verify the track header const static string MidiTrackHeader = "MTrk"; // I could use (MidiTrackHeader.length() + 1), but then this has to be // dynamically allocated. More hassle than it's worth. MIDI is well // defined and will always have a 4-byte header. We use 5 so we get // free null termination. char header_id[5] = { 0, 0, 0, 0, 0 }; unsigned long track_length; stream.read(header_id, static_cast<streamsize>(MidiTrackHeader.length())); stream.read(reinterpret_cast<char*>(&track_length), sizeof(unsigned long)); if (stream.fail()) throw MidiError(MidiError_TrackHeaderTooShort); string header(header_id); if (header != MidiTrackHeader) throw MidiError_BadTrackHeaderType; // Pull the full track out of the file all at once -- there is an // End-Of-Track event, but this allows us handle malformed MIDI a // little more gracefully. track_length = BigToSystem32(track_length); char *buffer = new char[track_length + 1]; buffer[track_length] = 0; stream.read(buffer, track_length); if (stream.fail()) { delete[] buffer; throw MidiError(MidiError_TrackTooShort); } // We have to jump through a couple hoops because istringstream // can't handle binary data unless constructed through an std::string. string buffer_string(buffer, track_length); istringstream event_stream(buffer_string, ios::binary); delete[] buffer; MidiTrack t; // Read events until we run out of track char last_status = 0; unsigned long current_pulse_count = 0; while (event_stream.peek() != char_traits<char>::eof()) { MidiEvent ev = MidiEvent::ReadFromStream(event_stream, last_status); last_status = ev.StatusCode(); t.m_events.push_back(ev); current_pulse_count += ev.GetDeltaPulses(); t.m_event_pulses.push_back(current_pulse_count); } t.BuildNoteSet(); t.DiscoverInstrument(); return t; }
void EventCanvas::viewMousePressEvent(QMouseEvent* event)/*{{{*/ { ///keyState = event->state(); _keyState = ((QInputEvent*) event)->modifiers(); _button = event->button(); //printf("viewMousePressEvent buttons:%x mods:%x button:%x\n", (int)event->buttons(), (int)keyState, event->button()); // special events if right button is clicked while operations // like moving or drawing lasso is performed. if (event->buttons() & Qt::RightButton & ~(event->button())) { //printf("viewMousePressEvent special buttons:%x mods:%x button:%x\n", (int)event->buttons(), (int)keyState, event->button()); switch (_drag) { case DRAG_LASSO: _drag = DRAG_OFF; redraw(); return; case DRAG_MOVE: _drag = DRAG_OFF; endMoveItems(_start, MOVE_MOVE, 0); return; default: break; } } // ignore event if (another) button is already active: if (event->buttons() & (Qt::LeftButton | Qt::RightButton | Qt::MidButton) & ~(event->button())) { //printf("viewMousePressEvent ignoring buttons:%x mods:%x button:%x\n", (int)event->buttons(), (int)keyState, event->button()); return; } bool shift = _keyState & Qt::ShiftModifier; bool alt = _keyState & Qt::AltModifier; bool ctrl = _keyState & Qt::ControlModifier; _start = event->pos(); //--------------------------------------------------- // set curItem to item mouse is pointing // (if any) //--------------------------------------------------- CItemList list = _items; if(multiPartSelectionAction && !multiPartSelectionAction->isChecked()) list = getItemlistForCurrentPart(); if (virt()) { _curItem = list.find(_start);//_items.find(_start); } else { _curItem = 0; //selectAtTick(_start.x()); iCItem ius; bool usfound = false; for (iCItem i = list.begin(); i != list.end(); ++i) { MidiTrack* mtrack = (MidiTrack*)i->second->part()->track(); int sy = _start.y(); int p = y2pitch(sy); if(editor->isGlobalEdit()) p += mtrack->getTransposition(); int p2 = pitch2y(p); QPoint lpos(_start.x(), p2); QRect box = i->second->bbox(); int x = rmapxDev(box.x()); int y = rmapyDev(box.y()); int w = rmapxDev(box.width()); int h = rmapyDev(box.height()); QRect r(x, y, w, h); r.translate(i->second->pos().x(), i->second->pos().y()); if(r.contains(lpos)) { if (i->second->isSelected()) { _curItem = i->second; break; } else if (!usfound) { ius = i; usfound = true; } } } if (!_curItem && usfound) _curItem = ius->second; } if(editor->isGlobalEdit() && _curItem) { populateMultiSelect(_curItem); } if (_curItem && (event->button() == Qt::MidButton)) { if (!_curItem->isSelected()) { selectItem(_curItem, true); updateSelection(); redraw(); } startDrag(_curItem, shift); } else if (event->button() == Qt::RightButton) { if (_curItem) { if (shift) { _drag = DRAG_RESIZE; setCursor(); int dx = _start.x() - _curItem->x(); _curItem->setWidth(dx); _start.setX(_curItem->x()); deselectAll(); selectItem(_curItem, true); updateSelection(); redraw(); } else { _itemPopupMenu = genItemPopup(_curItem); if (_itemPopupMenu) { QAction *act = _itemPopupMenu->exec(QCursor::pos()); if (act) itemPopup(_curItem, act->data().toInt(), _start); delete _itemPopupMenu; } } } else { _canvasPopupMenu = genCanvasPopup(true); if (_canvasPopupMenu) { QAction *act = _canvasPopupMenu->exec(QCursor::pos(), 0); if (act) { int actnum = act->data().toInt(); canvasPopup(actnum); if(actnum >= 20) //Nome of the tools have a higher number than 9 { editor->updateCanvas(); los->arranger->updateCanvas(); } } delete _canvasPopupMenu; } } } else if (event->button() == Qt::LeftButton) { switch (_tool) { case PointerTool: if (_curItem) { /*if (_curItem->part() != _curPart) { _curPart = _curItem->part(); _curPartId = _curPart->sn(); curPartChanged(); }*/ itemPressed(_curItem); if (shift) _drag = DRAG_COPY_START; else if (alt) { _drag = DRAG_CLONE_START; } else if (ctrl) { //Select all on the same pitch (e.g. same y-value) deselectAll(); //printf("Yes, ctrl and press\n"); for (iCItem i = _items.begin(); i != _items.end(); ++i) { if (i->second->y() == _curItem->y()) selectItem(i->second, true); } updateSelection(); redraw(); } else _drag = DRAG_MOVE_START; } else _drag = DRAG_LASSO_START; setCursor(); break; case RubberTool: deleteItem(_start); _drag = DRAG_DELETE; setCursor(); break; case PencilTool: if (_curItem) { _drag = DRAG_RESIZE; setCursor(); int dx = _start.x() - _curItem->x(); _curItem->setWidth(dx); _start.setX(_curItem->x()); } else { _drag = DRAG_NEW; setCursor(); _curItem = newItem(_start, event->modifiers()); if (_curItem) _items.add(_curItem); else { _drag = DRAG_OFF; setCursor(); } } deselectAll(); if (_curItem) { selectItem(_curItem, true); // Play the note itemPressed(_curItem); } updateSelection(); redraw(); break; default: break; } } mousePress(event); }/*}}}*/
void Audio::processMidi() { midiBusy = true; // // TODO: syntis should directly write into recordEventList // for (iMidiDevice id = midiDevices.begin(); id != midiDevices.end(); ++id) { MidiDevice* md = *id; MPEventList* playEvents = md->playEvents(); // // erase already played events: // iMPEvent nextPlayEvent = md->nextPlayEvent(); playEvents->erase(playEvents->begin(), nextPlayEvent); // klumsy hack for synti devices: if (md->isSynti()) { SynthI* s = (SynthI*) md; while (s->eventsPending()) { MidiRecordEvent ev = s->receiveEvent(); md->recordEvent(ev); } } // Is it a Jack midi device? //MidiJackDevice* mjd = dynamic_cast<MidiJackDevice*>(md); //if(mjd) // mjd->collectMidiEvents(); md->collectMidiEvents(); // Take snapshots of the current sizes of the recording fifos, // because they may change while here in process, asynchronously. md->beforeProcess(); } MPEventList* playEvents = metronome->playEvents(); iMPEvent nextPlayEvent = metronome->nextPlayEvent(); playEvents->erase(playEvents->begin(), nextPlayEvent); // p3.3.25 bool extsync = extSyncFlag.value(); for (iMidiTrack t = song->midis()->begin(); t != song->midis()->end(); ++t) { MidiTrack* track = *t; int port = track->outPort(); MidiDevice* md = midiPorts[port].device(); // Changed by Tim. p3.3.8 //if(md == 0) // continue; //MPEventList* playEvents = md->playEvents(); //if (playEvents == 0) // continue; //if (!track->isMute()) MPEventList* playEvents = 0; if (md) { playEvents = md->playEvents(); // only add track events if the track is unmuted if (!track->isMute()) { if (isPlaying() && (curTickPos < nextTickPos)) collectEvents(track, curTickPos, nextTickPos); } } // //----------midi recording // if (track->recordFlag()) { //int portMask = track->inPortMask(); // p3.3.38 Removed //unsigned int portMask = track->inPortMask(); //int channelMask = track->inChannelMask(); MPEventList* rl = track->mpevents(); MidiPort* tport = &midiPorts[port]; // p3.3.38 //for (iMidiDevice id = midiDevices.begin(); id != midiDevices.end(); ++id) //{ RouteList* irl = track->inRoutes(); for (ciRoute r = irl->begin(); r != irl->end(); ++r) { //if(!r->isValid() || (r->type != Route::ALSA_MIDI_ROUTE && r->type != Route::JACK_MIDI_ROUTE)) //if(!r->isValid() || (r->type != Route::MIDI_DEVICE_ROUTE)) if (!r->isValid() || (r->type != Route::MIDI_PORT_ROUTE)) // p3.3.49 continue; int devport = r->midiPort; // p3.3.49 if (devport == -1) continue; //MidiDevice* dev = *id; //MidiDevice* dev = r->device; MidiDevice* dev = midiPorts[devport].device(); // p3.3.49 if (!dev) continue; // p3.3.50 Removed //int channel = r->channel; // NOTE: TODO: Special for input device sysex 'channel' marked as -1, ** IF we end up going with that method **. // This would mean having a separate 'System' channel listed in the routing popups. // The other alternative is to accept sysex from a device as long as ANY regular channel is routed from it, // this does not require a 'System' channel listed in the routing popups. // But that requires more code below... Done. //if(channel == -1) //channel = MIDI_CHANNELS; // Special channel '17' // continue; //int devport = dev->midiPort(); // record only from ports marked in portMask: //if (devport == -1 || !(portMask & (1 << devport))) //if (devport == -1) // continue; //MREventList* el = dev->recordEvents(); //MidiFifo& rf = dev->recordEvents(); int channelMask = r->channel; // p3.3.50 if (channelMask == -1 || channelMask == 0) continue; for (int channel = 0; channel < MIDI_CHANNELS; ++channel) // p3.3.50 { if (!(channelMask & (1 << channel))) continue; if (!dev->sysexFIFOProcessed()) { // Set to the sysex fifo at first. MidiFifo& rf = dev->recordEvents(MIDI_CHANNELS); // Get the frozen snapshot of the size. int count = dev->tmpRecordCount(MIDI_CHANNELS); for (int i = 0; i < count; ++i) { MidiPlayEvent event(rf.peek(i)); //unsigned time = event.time() + segmentSize*(segmentCount-1); //unsigned time = event.time() + (extsync ? config.division/24 : segmentSize*(segmentCount-1)); //unsigned time = extsync ? curTickPos : (event.time() + segmentSize*(segmentCount-1)); //event.setTime(time); //if(!extsync) // event.setTime(event.time() + segmentSize*(segmentCount-1)); event.setPort(port); // dont't echo controller changes back to software // synthesizer: if (!dev->isSynti() && md && track->recEcho()) playEvents->add(event); // If syncing externally the event time is already in units of ticks, set above. if (!extsync) { //time = tempomap.frame2tick(event.time()); //event.setTime(time); // set tick time event.setTime(tempomap.frame2tick(event.time())); // set tick time } if (recording) rl->add(event); } dev->setSysexFIFOProcessed(true); } // Set to the sysex fifo at first. ///MidiFifo& rf = dev->recordEvents(MIDI_CHANNELS); // Get the frozen snapshot of the size. ///int count = dev->tmpRecordCount(MIDI_CHANNELS); // Iterate once for sysex fifo (if needed), once for channel fifos. ///for(int sei = 0; sei < 2; ++sei) { // If on first pass, do sysex fifo. /* if(sei == 0) { // Ignore any further channel routes on this device if already done here. if(dev->sysexFIFOProcessed()) continue; // Go ahead and set this now. dev->setSysexFIFOProcessed(true); // Allow it to fall through with the sysex fifo and count... } else { // We're on the second pass, do channel fifos. rf = dev->recordEvents(channel); // Get the frozen snapshot of the size. count = dev->tmpRecordCount(channel); } */ MidiFifo& rf = dev->recordEvents(channel); int count = dev->tmpRecordCount(channel); //for (iMREvent ie = el->begin(); ie != el->end(); ++ie) for (int i = 0; i < count; ++i) { MidiPlayEvent event(rf.peek(i)); //int channel = ie->channel(); ///int channel = event.channel(); int defaultPort = devport; ///if (!(channelMask & (1 << channel))) ///{ /// continue; ///} //MidiPlayEvent event(*ie); int drumRecPitch = 0; //prevent compiler warning: variable used without initialization MidiController *mc = 0; int ctl = 0; //Hmmm, hehhh... // TODO: Clean up a bit around here when it comes to separate events for rec & for playback. // But not before 0.7 (ml) int prePitch = 0, preVelo = 0; event.setChannel(track->outChannel()); if (event.isNote() || event.isNoteOff()) { // // apply track values // //Apply drum inkey: if (track->type() == Track::DRUM) { int pitch = event.dataA(); //Map note that is played according to drumInmap drumRecPitch = drumMap[(unsigned int) drumInmap[pitch]].enote; devport = drumMap[(unsigned int) drumInmap[pitch]].port; event.setPort(devport); channel = drumMap[(unsigned int) drumInmap[pitch]].channel; event.setA(drumMap[(unsigned int) drumInmap[pitch]].anote); event.setChannel(channel); } else { //Track transpose if non-drum prePitch = event.dataA(); int pitch = prePitch + track->transposition; if (pitch > 127) pitch = 127; if (pitch < 0) pitch = 0; event.setA(pitch); } if (!event.isNoteOff()) { preVelo = event.dataB(); int velo = preVelo + track->velocity; velo = (velo * track->compression) / 100; if (velo > 127) velo = 127; if (velo < 1) velo = 1; event.setB(velo); } } // Added by T356. else if (event.type() == ME_CONTROLLER) { //printf("11111111111111111111111111111111111111111111111111111\n"); if (track->type() == Track::DRUM) { //printf("2222222222222222222222222222222222222222222222222222222222\n"); ctl = event.dataA(); // Regardless of what port the event came from, is it a drum controller event // according to the track port's instrument? mc = tport->drumController(ctl); if (mc) { //printf("333333333333333333333333333333333333333333333333\n"); int pitch = ctl & 0x7f; ctl &= ~0xff; int dmindex = drumInmap[pitch] & 0x7f; //Map note that is played according to drumInmap drumRecPitch = drumMap[dmindex].enote; devport = drumMap[dmindex].port; event.setPort(devport); channel = drumMap[dmindex].channel; event.setA(ctl | drumMap[dmindex].anote); event.setChannel(channel); } } } // p3.3.25 // OOMidi uses a fixed clocks per quarternote of 24. // At standard 384 ticks per quarternote for example, // 384/24=16 for a division of 16 sub-frames (16 OOMidi 'ticks'). // That is what we'll use if syncing externally. //unsigned time = event.time() + segmentSize*(segmentCount-1); //unsigned time = event.time() + (extsync ? config.division/24 : segmentSize*(segmentCount-1)); // p3.3.34 // Oops, use the current tick. //unsigned time = extsync ? curTickPos : (event.time() + segmentSize*(segmentCount-1)); //event.setTime(time); // p3.3.35 // If ext sync, events are now time-stamped with last tick in MidiDevice::recordEvent(). // TODO: Tested, but record resolution not so good. Switch to wall clock based separate list in MidiDevice. // p3.3.36 //if(!extsync) // event.setTime(event.time() + segmentSize*(segmentCount-1)); // dont't echo controller changes back to software // synthesizer: if (!dev->isSynti()) { //printf("444444444444444444444444444444444444444444444444444444\n"); //Check if we're outputting to another port than default: if (devport == defaultPort) { //printf("5555555555555555555555555555555555555555555\n"); event.setPort(port); if (md && track->recEcho()) playEvents->add(event); } else { //printf("66666666666666666666666666666666666666\n"); // Hmm, this appears to work, but... Will this induce trouble with md->setNextPlayEvent?? MidiDevice* mdAlt = midiPorts[devport].device(); if (mdAlt && track->recEcho()) mdAlt->playEvents()->add(event); } // Shall we activate meters even while rec echo is off? Sure, why not... if (event.isNote() && event.dataB() > track->activity()) track->setActivity(event.dataB()); } // p3.3.25 // If syncing externally the event time is already in units of ticks, set above. if (!extsync) { //printf("7777777777777777777777777777777777777777777\n"); // p3.3.35 //time = tempomap.frame2tick(event.time()); //event.setTime(time); // set tick time event.setTime(tempomap.frame2tick(event.time())); // set tick time } // Special handling of events stored in rec-lists. a bit hACKish. TODO: Clean up (after 0.7)! :-/ (ml) if (recording) { //printf("888888888888888888888888888888888888888888888888\n"); // In these next steps, it is essential to set the recorded event's port // to the track port so buildMidiEventList will accept it. Even though // the port may have no device "<none>". // if (track->type() == Track::DRUM) { //printf("99999999999999999999999999999999999999999999999999\n"); // Is it a drum controller event? if (mc) { //printf("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n"); MidiPlayEvent drumRecEvent = event; drumRecEvent.setA(ctl | drumRecPitch); // In this case, preVelo is simply the controller value. drumRecEvent.setB(preVelo); drumRecEvent.setPort(port); //rec-event to current port drumRecEvent.setChannel(track->outChannel()); //rec-event to current channel rl->add(drumRecEvent); } else { //printf("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n"); MidiPlayEvent drumRecEvent = event; drumRecEvent.setA(drumRecPitch); drumRecEvent.setB(preVelo); // Changed by T356. // Tested: Events were not being recorded for a drum map entry pointing to a // different port. This must have been wrong - buildMidiEventList would ignore this. //drumRecEvent.setPort(devport); drumRecEvent.setPort(port); //rec-event to current port drumRecEvent.setChannel(track->outChannel()); //rec-event to current channel rl->add(drumRecEvent); } } else { //printf("ccccccccccccccccccccccccccccccccccccccccccccc\n"); // Restore record-pitch to non-transposed value since we don't want the note transposed twice next MidiPlayEvent recEvent = event; if (prePitch) recEvent.setA(prePitch); if (preVelo) recEvent.setB(preVelo); recEvent.setPort(port); recEvent.setChannel(track->outChannel()); rl->add(recEvent); } } } } } } } // Added by Tim. p3.3.8 if (md) { //printf("zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\n"); md->setNextPlayEvent(playEvents->begin()); } } // // clear all recorded events in midiDevices // process stuck notes // for (iMidiDevice id = midiDevices.begin(); id != midiDevices.end(); ++id) { //printf("--------------------------aaaaaaaaaaaaaaaaaaaaaaaaaaaa\n"); MidiDevice* md = *id; ///md->recordEvents()->clear(); // By T356. Done processing this rec buffer, now flip to the other one. ///md->flipRecBuffer(); // We are done with the 'frozen' recording fifos, remove the events. md->afterProcess(); MPEventList* stuckNotes = md->stuckNotes(); MPEventList* playEvents = md->playEvents(); iMPEvent k; for (k = stuckNotes->begin(); k != stuckNotes->end(); ++k) { if (k->time() >= nextTickPos) break; MidiPlayEvent ev(*k); // p3.3.25 //int frame = tempomap.tick2frame(k->time()) + frameOffset; if (extsync) { ev.setTime(k->time()); } else { int frame = tempomap.tick2frame(k->time()) + frameOffset; ev.setTime(frame); } // p3.3.25 //ev.setTime(frame); playEvents->add(ev); } stuckNotes->erase(stuckNotes->begin(), k); md->setNextPlayEvent(playEvents->begin()); } //--------------------------------------------------- // insert metronome clicks //--------------------------------------------------- MidiDevice* md = 0; if (midiClickFlag) md = midiPorts[clickPort].device(); if (song->click() && (isPlaying() || state == PRECOUNT)) { MPEventList* playEvents = 0; MPEventList* stuckNotes = 0; if (md) { playEvents = md->playEvents(); stuckNotes = md->stuckNotes(); } int bar, beat; unsigned tick; bool isMeasure = false; while (midiClick < nextTickPos) { if (isPlaying()) { ///sigmap.tickValues(midiClick, &bar, &beat, &tick); AL::sigmap.tickValues(midiClick, &bar, &beat, &tick); isMeasure = beat == 0; } else if (state == PRECOUNT) { isMeasure = (clickno % clicksMeasure) == 0; } // p3.3.25 //int frame = tempomap.tick2frame(midiClick) + frameOffset; int evtime = extsync ? midiClick : tempomap.tick2frame(midiClick) + frameOffset; // p3.3.25 //MidiPlayEvent ev(frame, clickPort, clickChan, ME_NOTEON, MidiPlayEvent ev(evtime, clickPort, clickChan, ME_NOTEON, beatClickNote, beatClickVelo); if (md) { // p3.3.25 //MidiPlayEvent ev(frame, clickPort, clickChan, ME_NOTEON, MidiPlayEvent ev(evtime, clickPort, clickChan, ME_NOTEON, beatClickNote, beatClickVelo); if (isMeasure) { ev.setA(measureClickNote); ev.setB(measureClickVelo); } playEvents->add(ev); } if (audioClickFlag) { // p3.3.25 //MidiPlayEvent ev1(frame, 0, 0, ME_NOTEON, 0, 0); MidiPlayEvent ev1(evtime, 0, 0, ME_NOTEON, 0, 0); ev1.setA(isMeasure ? 0 : 1); metronome->playEvents()->add(ev1); } if (md) { ev.setB(0); // p3.3.25 // Removed. Why was this here? //frame = tempomap.tick2frame(midiClick+20) + frameOffset; // // Does it mean this should be changed too? // No, stuck notes are in units of ticks, not frames like (normal, non-external) play events... ev.setTime(midiClick + 10); if (md) stuckNotes->add(ev); } if (isPlaying()) ///midiClick = sigmap.bar2tick(bar, beat+1, 0); midiClick = AL::sigmap.bar2tick(bar, beat + 1, 0); else if (state == PRECOUNT) { midiClick += ticksBeat; if (clickno) --clickno; else state = START_PLAY; } } if (md) md->setNextPlayEvent(playEvents->begin()); if (audioClickFlag) metronome->setNextPlayEvent(metronome->playEvents()->begin()); } if (state == STOP) { //--------------------------------------------------- // end all notes //--------------------------------------------------- for (iMidiDevice imd = midiDevices.begin(); imd != midiDevices.end(); ++imd) { MidiDevice* md = *imd; MPEventList* playEvents = md->playEvents(); MPEventList* stuckNotes = md->stuckNotes(); for (iMPEvent k = stuckNotes->begin(); k != stuckNotes->end(); ++k) { MidiPlayEvent ev(*k); ev.setTime(0); // play now playEvents->add(ev); } stuckNotes->clear(); } } // p3.3.36 //int tickpos = audio->tickPos(); //bool extsync = extSyncFlag.value(); // // Special for Jack midi devices: Play all Jack midi events up to curFrame. // for (iMidiDevice id = midiDevices.begin(); id != midiDevices.end(); ++id) { (*id)->processMidi(); /* int port = md->midiPort(); MidiPort* mp = port != -1 ? &midiPorts[port] : 0; MPEventList* el = md->playEvents(); if (el->empty()) continue; iMPEvent i = md->nextPlayEvent(); for(; i != el->end(); ++i) { // 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(i->time() > curFrame) if(i->time() > (extsync ? tickpos : curFrame)) { //printf(" curT %d frame %d\n", i->time(), curFrame); break; // skip this event } if(mp) { if(mp->sendEvent(*i)) break; } else { if(md->putEvent(*i)) break; } } md->setNextPlayEvent(i); */ } midiBusy = false; }
bool QSoundSeq::PostLoad() { if (readMode != READMODE_CONVERT_TO_MIDI) return true; // We need to add pitch bend events for vibrato, which is controlled by a software LFO // This is actually a bit tricky because the LFO is running independent of the sequence // tempo. It gets updated (251/4) times a second, always. We will have to convert // ticks in our sequence into absolute elapsed time, which means we also need to keep // track of any tempo events that change the absolute time per tick. vector<MidiEvent*> tempoEvents; vector<MidiTrack*>& miditracks = midi->aTracks; // First get all tempo events, we assume they occur on track 1 for (unsigned int i = 0; i < miditracks[0]->aEvents.size(); i++) { MidiEvent* event = miditracks[0]->aEvents[i]; if (event->GetEventType() == MIDIEVENT_TEMPO) tempoEvents.push_back(event); } // Now for each track, gather all vibrato events, lfo events, pitch bend events and track end events for (unsigned int i = 0; i < miditracks.size(); i++) { vector<MidiEvent*> events(tempoEvents); MidiTrack* track = miditracks[i]; int channel = this->aTracks[i]->channel; for (unsigned int j = 0; j < track->aEvents.size(); j++) { MidiEvent* event = miditracks[i]->aEvents[j]; MidiEventType type = event->GetEventType(); if (type == MIDIEVENT_MARKER || type == MIDIEVENT_PITCHBEND || type == MIDIEVENT_ENDOFTRACK) events.push_back(event); } // We need to sort by priority so that vibrato events get ordered correctly. Since they cause the // pitch bend range to be set, they need to occur before pitchbend events. stable_sort(events.begin(), events.end(), PriorityCmp()); //Sort all the events by priority stable_sort(events.begin(), events.end(), AbsTimeCmp()); //Sort all the events by absolute time, so that delta times can be recorded correctly // And now we actually add vibrato and pitch bend events const uint32_t ppqn = GetPPQN(); // pulses (ticks) per quarter note const uint32_t mpLFOt = (uint32_t)((1/(251/4.0)) * 1000000); // microseconds per LFO tick uint32_t mpqn = 500000; // microseconds per quarter note - 120 bpm default uint32_t mpt = mpqn / ppqn; // microseconds per MIDI tick short pitchbend = 0; // pitch bend in cents int pitchbendRange = 200; // pitch bend range in cents default 2 semitones double vibrato = 0; // vibrato depth in cents uint16_t tremelo = 0; // tremelo depth. we divide this value by 0x10000 to get percent amplitude attenuation uint16_t lfoRate = 0; // value added to lfo env every lfo tick uint32_t lfoVal = 0; // LFO envelope value. 0 - 0xFFFFFF . Effective envelope range is -0x1000000 to +0x1000000 int lfoStage = 0; // 0 = rising from mid, 1 = falling from peak, 2 = falling from mid 3 = rising from bottom short lfoCents = 0; // cents adjustment from most recent LFO val excluding pitchbend. long effectiveLfoVal = 0; //bool bLfoRising = true;; // is LFO rising or falling? uint32_t startAbsTicks = 0; // The MIDI tick time to start from for a given vibrato segment size_t numEvents = events.size(); for (size_t j = 0; j < numEvents; j++) { MidiEvent* event = events[j]; uint32_t curTicks = event->AbsTime; //current absolute ticks if (curTicks > 0 /*&& (vibrato > 0 || tremelo > 0)*/ && startAbsTicks < curTicks) { // if we're starting a fresh vibrato segment, let's start the LFO env at 0 and set it to rise /*if (prevVibrato == 0 && prevTremelo == 0) { lfoVal = 0; lfoStage = 0; }*/ long segmentDurTicks = curTicks - startAbsTicks; double segmentDur = segmentDurTicks * mpt; // duration of this segment in micros double lfoTicks = segmentDur / (double)mpLFOt; double numLfoPhases = (lfoTicks * (double)lfoRate) / (double)0x20000; //double midiTicksPerPhase = (curTicks-startAbsTicks) / numLfoPhases; //double centsPerMidiTick = vibrato / midiTicksPerPhase; double lfoRatePerMidiTick = (numLfoPhases * 0x20000) / (double)segmentDurTicks; const uint8_t tickRes = 16; uint32_t lfoRatePerLoop = (uint32_t)((tickRes * lfoRatePerMidiTick) * 256); for (int t = 0; t < segmentDurTicks; t += tickRes) { lfoVal += lfoRatePerLoop; if (lfoVal > 0xFFFFFF) { lfoVal -= 0x1000000; lfoStage = (lfoStage+1) % 4; } effectiveLfoVal = lfoVal; if (lfoStage == 1) effectiveLfoVal = 0x1000000 - lfoVal; else if (lfoStage == 2) effectiveLfoVal = -((long)lfoVal); else if (lfoStage == 3) effectiveLfoVal = -0x1000000 + lfoVal; double lfoPercent = (effectiveLfoVal / (double)0x1000000); if (vibrato > 0) { lfoCents = (short)(lfoPercent * vibrato); track->InsertPitchBend(channel, (short)(((lfoCents + pitchbend) / (double)pitchbendRange) * 8192), startAbsTicks + t); } if (tremelo > 0) { uint8_t expression = ConvertPercentAmpToStdMidiVal((0x10000 - (tremelo*abs(lfoPercent))) / (double)0x10000); track->InsertExpression(channel, expression, startAbsTicks + t); } } // TODO add adjustment for segmentDurTicks % tickRes } switch(event->GetEventType()) { case MIDIEVENT_TEMPO: { TempoEvent* tempoevent = (TempoEvent*)event; mpqn = tempoevent->microSecs; mpt = mpqn / ppqn; } break; case MIDIEVENT_ENDOFTRACK: break; case MIDIEVENT_MARKER: { MarkerEvent* marker = (MarkerEvent*)event; if (marker->name == "vibrato") { vibrato = vibrato_depth_table[marker->databyte1] * (100/256.0); //pitchbendRange = max(200, (vibrato + 50)); //50 cents to allow for pitchbend values, which range -50/+50 pitchbendRange = (int)max(200, ceil((vibrato+50)/100.0)*100); //+50 cents to allow for pitchbend values, which range -50/+50 track->InsertPitchBendRange(channel, pitchbendRange/100, pitchbendRange%100, curTicks); lfoCents = (short)((effectiveLfoVal / (double)0x1000000) * vibrato); if (curTicks > 0) track->InsertPitchBend(channel, (short)(((lfoCents + pitchbend) / (double)pitchbendRange) * 8192), curTicks); } else if (marker->name == "tremelo") { tremelo = tremelo_depth_table[marker->databyte1]; if (tremelo == 0) track->InsertExpression(channel, 127, curTicks); } else if (marker->name == "lfo") { lfoRate = lfo_rate_table[marker->databyte1]; } else if (marker->name == "resetlfo") { if (marker->databyte1 != 1) break; lfoVal = 0; effectiveLfoVal = 0; lfoStage = 0; lfoCents = 0; if (vibrato > 0) track->InsertPitchBend(channel, (short)(((0 + pitchbend) / (double)pitchbendRange) * 8192), curTicks); if (tremelo > 0) track->InsertExpression(channel, 127, curTicks); } else if (marker->name == "pitchbend") { pitchbend = (short)(((char)marker->databyte1 / 256.0) * 100); //stringstream stream; //stream << "cents: " << cents << " finalval: " << (cents / (double)pitchbendRange) * 8192 << "\n"; //ATLTRACE(stream.str().c_str()); track->InsertPitchBend(channel, (short)(((lfoCents + pitchbend) / (double)pitchbendRange) * 8192), curTicks); } } break; } startAbsTicks = curTicks; } } return VGMSeq::PostLoad(); }
bool LOS::importMidi(const QString name, bool merge)/*{{{*/ { bool popenFlag; FILE* fp = fileOpen(this, name, QString(".mid"), "r", popenFlag); if (fp == 0) return true; MidiFile mf(fp); bool rv = mf.read(); popenFlag ? pclose(fp) : fclose(fp); if (rv) { QString s(tr("reading midifile\n ")); s += name; s += tr("\nfailed: "); s += mf.error(); QMessageBox::critical(this, QString("LOS"), s); return rv; } // // evaluate song Type (GM, XG, GS, unknown) // MType t = song->midiType(); if (!merge) { t = mf.mtype(); song->setMType(t); } MidiInstrument* instr = 0; for (iMidiInstrument i = midiInstruments.begin(); i != midiInstruments.end(); ++i) { MidiInstrument* mi = *i; if ((mi->iname() == "GM" && ((t == MT_UNKNOWN) || (t == MIDI_TYPE_GM))) || ((mi->iname() == "GS") && (t == MT_GS)) || ((mi->iname() == "XG") && (t == MT_XG))) { instr = mi; break; } } if (instr == 0) { // the standard instrument files (GM, GS, XG) must be present printf("no instrument, type %d\n", t); return true; //abort(); } MidiFileTrackList* etl = mf.trackList(); int division = mf.division(); // // create MidiTrack and copy events to ->events() // - combine note on/off events // - calculate tick value for internal resolution // for (iMidiFileTrack t = etl->begin(); t != etl->end(); ++t) { MPEventList* el = &((*t)->events); if (el->empty()) continue; // // if we split the track, SYSEX and META events go into // the first target track bool first = true; // somewhat silly and slooow: for (int port = 0; port < kMaxMidiPorts; ++port) { for (int channel = 0; channel < kMaxMidiChannels; ++channel) { // // check if there are any events for port/channel in track: // iMPEvent i; for (i = el->begin(); i != el->end(); ++i) { MidiPlayEvent ev = *i; if (ev.type() != ME_SYSEX && ev.type() != ME_META && ev.channel() == channel && ev.port() == port) break; } if (i == el->end()) continue; MidiTrack* track = new MidiTrack(); if ((*t)->isDrumTrack) { track->setType(Track::DRUM); } track->setOutChannel(channel); track->setOutPort(port); MidiPort* mport = &midiPorts[track->outPort()]; // this overwrites any instrument set for this port: mport->setInstrument(instr); EventList* mel = track->events(); //buildMidiEventList(mel, el, track, division, first); // Don't do loops. buildMidiEventList(mel, el, track, division, first, false); first = false; // Hmm. buildMidiEventList already takes care of this. // But it seems to work. How? Must test. if (channel == 9 && song->midiType() != MT_UNKNOWN) { track->setType(Track::DRUM); // // remap drum pitch with drumInmap // EventList* tevents = track->events(); for (iEvent i = tevents->begin(); i != tevents->end(); ++i) { Event ev = i->second; if (ev.isNote()) { int pitch = drumInmap[ev.pitch()]; ev.setPitch(pitch); } else if (ev.type() == Controller) { int ctl = ev.dataA(); MidiController *mc = mport->drumController(ctl); if (mc) ev.setA((ctl & ~0xff) | drumInmap[ctl & 0x7f]); } } } processTrack(track); song->insertTrack(track, -1); } } if (first) { // // track does only contain non-channel messages // (SYSEX or META) // MidiTrack* track = new MidiTrack(); track->setOutChannel(0); track->setOutPort(0); EventList* mel = track->events(); //buildMidiEventList(mel, el, track, division, true); // Do SysexMeta. Don't do loops. buildMidiEventList(mel, el, track, division, true, false); processTrack(track); song->insertTrack(track, -1); } } if (!merge) { TrackList* tl = song->tracks(); if (!tl->empty()) { Track* track = tl->front(); track->setSelected(true); } song->initLen(); int z, n; ///sigmap.timesig(0, z, n); sigmap.timesig(0, z, n); int tempo = tempomap.tempo(0); transport->setTimesig(z, n); transport->setTempo(tempo); bool masterF = !tempomap.empty(); song->setMasterFlag(masterF); transport->setMasterFlag(masterF); song->updatePos(); composer->reset(); ///composer->setMode(int(song->midiType())); // p4.0.7 Tim } else { song->initLen(); } return false; }/*}}}*/
bool LOS::importMidi(const QString name, bool merge)/*{{{*/ { bool popenFlag; FILE* fp = fileOpen(this, name, QString(".mid"), "r", popenFlag); if (fp == 0) return true; MidiFile mf(fp); bool rv = mf.read(); popenFlag ? pclose(fp) : fclose(fp); if (rv) { QString s(tr("reading midifile\n ")); s += name; s += tr("\nfailed: "); s += mf.error(); QMessageBox::critical(this, QString("LOS"), s); return rv; } // // evaluate song Type (GM, XG, GS, unknown) // MidiType t = song->midiType(); if (!merge) { t = mf.midiType(); song->setMidiType(t); } MidiInstrument* instr = 0; for (iMidiInstrument i = midiInstruments.begin(); i != midiInstruments.end(); ++i) { MidiInstrument* mi = *i; if ((mi->iname() == "GM" && ((t == MIDI_TYPE_NULL) || (t == MIDI_TYPE_GM))) || (mi->iname() == "GS" && t == MIDI_TYPE_GS) || (mi->iname() == "XG" && t == MIDI_TYPE_XG)) { instr = mi; break; } } if (instr == 0) { // the standard instrument files (GM, GS, XG) must be present printf("no instrument, type %d\n", t); return true; //abort(); } MidiFileTrackList* etl = mf.trackList(); int division = mf.division(); // // create MidiTrack and copy events to ->events() // - combine note on/off events // - calculate tick value for internal resolution // int mPort = getFreeMidiPort(); for (iMidiFileTrack t = etl->begin(); t != etl->end(); ++t) { MPEventList* el = &((*t)->events); if (el->empty()) continue; // // if we split the track, SYSEX and META events go into // the first target track bool first = true; QList<QPair<int, int> > portChannelList; iMPEvent i; for(i = el->begin(); i != el->end(); i++) { if (i->type() != ME_SYSEX && i->type() != ME_META) { int chan = i->channel(); int port = i->port(); if(portChannelList.isEmpty() || !portChannelList.contains(qMakePair(chan, port))) { portChannelList.append(qMakePair(chan, port)); MidiTrack* track = new MidiTrack(); track->setDefaultName(); track->setMasterFlag(true); if(config.partColorNames[lastTrackPartColorIndex].contains("menu:", Qt::CaseSensitive)) lastTrackPartColorIndex ++; track->setDefaultPartColor(lastTrackPartColorIndex); lastTrackPartColorIndex ++; if(lastTrackPartColorIndex == NUM_PARTCOLORS) lastTrackPartColorIndex = 1; //Set track channel so buildMidiEventList can match the event to a channel track->setOutChannel(chan); track->setOutPort(mPort); MidiPort* mport = &midiPorts[track->outPort()]; // this overwrites any instrument set for this port: mport->setInstrument(instr); EventList* mel = track->events(); buildMidiEventList(mel, el, track, division, first, false, false); first = false; processTrack(track); //Update track to channel 1 //99% of all midi we import will be alien to our setup anyway, //so I'm making it easy for the user to just set the Instrument and go track->setOutChannel(0); song->insertTrack(track, -1); mPort++; //FIXME: Provice a non-iterative way to do this using the new losMidiPorts hash //Or maintain a list of configured or inuse ports while((&midiPorts[mPort])->device() && mPort < kMaxMidiPorts) mPort++;//Just incase we have a configured port after an empty one } } } if (first) { // // track does only contain non-channel messages // (SYSEX or META) // MidiTrack* track = new MidiTrack(); track->setDefaultName(); track->setMasterFlag(true); track->setOutChannel(0); track->setOutPort(mPort); if(config.partColorNames[lastTrackPartColorIndex].contains("menu:", Qt::CaseSensitive)) lastTrackPartColorIndex ++; track->setDefaultPartColor(lastTrackPartColorIndex); lastTrackPartColorIndex ++; if(lastTrackPartColorIndex == NUM_PARTCOLORS) lastTrackPartColorIndex = 1; EventList* mel = track->events(); // Do SysexMeta. Don't do loops. // TODO: Properly support sysex dumps buildMidiEventList(mel, el, track, division, true, false, false); processTrack(track); song->insertTrack(track, -1); mPort++; while((&midiPorts[mPort])->device() && mPort < kMaxMidiPorts) mPort++; } } if (!merge) { MidiTrackList* tl = song->tracks(); if (!tl->empty()) { MidiTrack* track = tl->front(); track->setSelected(true); } song->initLen(); int z, n; sigmap.timesig(0, z, n); int tempo = tempomap.tempo(0); transport->setTimesig(z, n); transport->setTempo(tempo); bool masterF = !tempomap.empty(); song->setMasterFlag(masterF); transport->setMasterFlag(masterF); song->updatePos(); composer->reset(); } else { song->initLen(); } return false; }/*}}}*/
bool LOS::importMidi(const QString name, bool merge)/*{{{*/ { bool popenFlag; FILE* fp = fileOpen(this, name, QString(".mid"), "r", popenFlag); if (fp == 0) return true; MidiFile mf(fp); bool rv = mf.read(); popenFlag ? pclose(fp) : fclose(fp); if (rv) { QString s(tr("reading midifile\n ")); s += name; s += tr("\nfailed: "); s += mf.error(); QMessageBox::critical(this, QString("LOS"), s); return rv; } // // evaluate song Type (GM, XG, GS, unknown) // MType t = song->midiType(); if (!merge) { t = mf.mtype(); song->setMType(t); } MidiInstrument* instr = 0; for (iMidiInstrument i = midiInstruments.begin(); i != midiInstruments.end(); ++i) { MidiInstrument* mi = *i; if ((mi->iname() == "GM" && ((t == MT_UNKNOWN) || (t == MIDI_TYPE_GM))) || ((mi->iname() == "GS") && (t == MT_GS)) || ((mi->iname() == "XG") && (t == MT_XG))) { instr = mi; break; } } if (instr == 0) { // the standard instrument files (GM, GS, XG) must be present printf("no instrument, type %d\n", t); return true; //abort(); } MidiFileTrackList* etl = mf.trackList(); int division = mf.division(); // // create MidiTrack and copy events to ->events() // - combine note on/off events // - calculate tick value for internal resolution // int mPort = getFreeMidiPort(); for (iMidiFileTrack t = etl->begin(); t != etl->end(); ++t) { MPEventList* el = &((*t)->events); if (el->empty()) continue; // // if we split the track, SYSEX and META events go into // the first target track bool first = true; // somewhat silly and slooow: QList<QPair<int, int> > eventChannelList; if(mPort >= 0 && mPort < kMaxMidiPorts) { for (int channel = 0; channel < kMaxMidiChannels; ++channel) { // // check if there are any events for port/channel in track: // iMPEvent i; for (i = el->begin(); i != el->end(); ++i) { MidiPlayEvent ev = *i; if (ev.type() != ME_SYSEX && ev.type() != ME_META && ev.channel() == channel) break; } if (i == el->end()) continue; MidiTrack* track = new MidiTrack(); track->setDefaultName(); track->setMasterFlag(true); track->setOutChannel(channel); track->setOutPort(mPort); MidiPort* mport = &midiPorts[track->outPort()]; // this overwrites any instrument set for this port: mport->setInstrument(instr); EventList* mel = track->events(); buildMidiEventList(mel, el, track, division, first, false, false); first = false; processTrack(track); song->insertTrack(track, -1); //Create the Audio input side of the track Track* input = song->addTrackByName(QString("i").append(track->name()), Track::AUDIO_INPUT, -1, false, false); if(input) { input->setMasterFlag(false); input->setChainMaster(track->id()); track->addManagedTrack(input->id()); } } } if (first) { // // track does only contain non-channel messages // (SYSEX or META) // MidiTrack* track = new MidiTrack(); track->setDefaultName(); track->setMasterFlag(true); track->setOutChannel(0); track->setOutPort(mPort); EventList* mel = track->events(); //buildMidiEventList(mel, el, track, division, true); // Do SysexMeta. Don't do loops. buildMidiEventList(mel, el, track, division, true, false, false); processTrack(track); song->insertTrack(track, -1); //Create the Audio input side of the track Track* input = song->addTrackByName(QString("i").append(track->name()), Track::AUDIO_INPUT, -1, false, false); if(input) { input->setMasterFlag(false); input->setChainMaster(track->id()); track->addManagedTrack(input->id()); } } mPort++; //FIXME: Provice a non-iterative way to do this using the new losMidiPorts hash //Or maintain a list of configured or inuse ports while((&midiPorts[mPort])->device() && mPort < kMaxMidiPorts) mPort++;//Just incase we have a configured port after an empty one } if (!merge) { TrackList* tl = song->tracks(); if (!tl->empty()) { Track* track = tl->front(); track->setSelected(true); } song->initLen(); int z, n; sigmap.timesig(0, z, n); int tempo = tempomap.tempo(0); transport->setTimesig(z, n); transport->setTempo(tempo); bool masterF = !tempomap.empty(); song->setMasterFlag(masterF); transport->setMasterFlag(masterF); song->updatePos(); composer->reset(); } else { song->initLen(); } return false; }/*}}}*/
void Audio::initDevices() { // // mark all used ports // bool activePorts[MIDI_PORTS]; for (int i = 0; i < MIDI_PORTS; ++i) activePorts[i] = false; MidiTrackList* tracks = song->midis(); for (iMidiTrack it = tracks->begin(); it != tracks->end(); ++it) { MidiTrack* track = *it; activePorts[track->outPort()] = true; } if (song->click()) activePorts[clickPort] = true; // // test for explicit instrument initialization // for (int i = 0; i < MIDI_PORTS; ++i) { if (!activePorts[i]) continue; MidiPort* port = &midiPorts[i]; MidiInstrument* instr = port->instrument(); MidiDevice* md = port->device(); if (instr && md) { EventList* events = instr->midiInit(); if (events->empty()) continue; for (iEvent ie = events->begin(); ie != events->end(); ++ie) { MidiPlayEvent ev(0, i, 0, ie->second); md->putEvent(ev); } activePorts[i] = false; // no standard initialization } } // // damit Midi-Devices, die mehrere Ports besitzen, wie z.B. // das Korg NS5R, nicht mehrmals zwischen GM und XG/GS hin und // hergeschaltet werden, wird zunïÿýhst auf allen Ports GM // initialisiert, und dann erst XG/GS // // Standard initialization... for (int i = 0; i < MIDI_PORTS; ++i) { if (!activePorts[i]) continue; MidiPort* port = &midiPorts[i]; switch (song->mtype()) { case MT_GS: case MT_UNKNOWN: break; case MT_GM: case MT_XG: port->sendGmOn(); break; } } for (int i = 0; i < MIDI_PORTS; ++i) { if (!activePorts[i]) continue; MidiPort* port = &midiPorts[i]; switch (song->mtype()) { case MT_UNKNOWN: break; case MT_GM: port->sendGmInitValues(); break; case MT_GS: port->sendGsOn(); port->sendGsInitValues(); break; case MT_XG: port->sendXgOn(); port->sendXgInitValues(); 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; }/*}}}*/
int main(int argc, char* argv[]) { int c; while ((c = getopt(argc, argv, "vdD:r")) != EOF) { switch (c) { case 'v': printVersion(); return 0; case 'd': debugMode = true; break; default: usage(); return -1; } } QIODevice* in = 0; QIODevice* out = 0; switch (argc - optind) { case 2: out = new QFile(argv[1 + optind]); if (!out->open(QIODevice::WriteOnly)) { printf("cannot open output file <%s>: %s\n", argv[optind+1], strerror(errno)); return -3; } case 1: in = new QFile(argv[optind]); if (!in->open(QIODevice::ReadOnly)) { printf("cannot open input file <%s>: %s\n", argv[optind], strerror(errno)); return -4; } break; case 0: break; default: usage(); return -2; break; } if (in == 0) { in = new QFile; ((QFile*)in)->open(stdin, QIODevice::ReadOnly); } if (out == 0) { out = new QFile; ((QFile*)out)->open(stdout, QIODevice::WriteOnly); } MidiFile mf; mf.setFormat(1); XmlReader e(in); while (e.readNextStartElement()) { const QStringRef& tag(e.name()); if (tag == "SMF") { while (e.readNextStartElement()) { const QStringRef& tag(e.name()); if (tag == "Track") { MidiTrack* track = new MidiTrack(&mf); while (e.readNextStartElement()) { const QStringRef& tag(e.name()); if (tag == "NoteOff") { MidiEventType t = MidiEventType::NOTEOFF; int tick = e.intAttribute("tick"); uchar c = e.intAttribute("c"); uchar a = e.intAttribute("a", 0, 16); uchar b = e.intAttribute("b", 0, 16); track->events().insert(std::pair<int,MidiEvent>(tick, MidiEvent(t, c, a, b))); e.skipCurrentElement(); } else if (tag == "NoteOn") { MidiEventType t = MidiEventType::NOTEON; int tick = e.intAttribute("tick"); uchar c = e.intAttribute("c"); uchar a = e.intAttribute("a", 0, 16); uchar b = e.intAttribute("b", 0, 16); track->events().insert(std::pair<int,MidiEvent>(tick, MidiEvent(t, c, a, b))); e.skipCurrentElement(); } else if (tag == "Ctrl") { MidiEventType t = MidiEventType::CONTROLLER; int tick = e.intAttribute("tick"); uchar c = e.intAttribute("c"); uchar a = e.intAttribute("a", 0, 16); uchar b = e.intAttribute("b", 0, 16); track->events().insert(std::pair<int,MidiEvent>(tick, MidiEvent(t, c, a, b))); e.skipCurrentElement(); } else if (tag == "Program") { MidiEventType t = MidiEventType::PROGRAM; int tick = e.intAttribute("tick"); uchar c = e.intAttribute("c"); uchar a = e.intAttribute("a", 0, 16); track->events().insert(std::pair<int,MidiEvent>(tick, MidiEvent(t, c, a, 0))); e.skipCurrentElement(); } else if (tag == "Event") { uchar t = e.intAttribute("t"); int tick = e.intAttribute("tick"); uchar c = e.intAttribute("c"); uchar a = e.intAttribute("a", 0, 16); uchar b = e.intAttribute("b", 0, 16); track->events().insert(std::pair<int,MidiEvent>(tick, MidiEvent(MidiEventType(t), c, a, b))); e.skipCurrentElement(); } else e.unknown(); } mf.tracks().push_back(track); } else if (tag == "division") mf.setDivision(e.readInt()); else if (tag == "format") mf.setFormat(e.readInt()); else e.unknown(); } } else e.unknown(); } mf.write(out); delete out; delete in; return 0; }
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().toLatin1().constData(), 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; } } }
void TrackListView::contextPopupMenu(QPoint pos)/*{{{*/ { QModelIndex mindex = m_table->indexAt(pos); if(!mindex.isValid()) return; //int row = mindex.row(); QStandardItem* item = m_model->itemFromIndex(mindex); if(item) { m_selectedIndex = item->row(); //Make it works even if you rightclick on the checkbox QString trackName = item->data(TrackNameRole).toString(); int type = item->data(TrackRole).toInt(); MidiTrack* track = song->findTrack(trackName); if(!track || !m_editor) return; QMenu* p = new QMenu(this); QString title(tr("Part Color")); int index = track->getDefaultPartColor(); Part* npart = 0; if(type == 1) title = QString(tr("Default Part Color")); else { PartList* list = track->parts(); int sn = item->data(PartRole).toInt(); unsigned tick = item->data(TickRole).toInt(); npart = list->find(tick, sn); if(npart) index = npart->colorIndex(); } QMenu* colorPopup = p->addMenu(title); QMenu* colorSub; for (int i = 0; i < NUM_PARTCOLORS; ++i) { QString colorname(config.partColorNames[i]); if(colorname.contains("menu:", Qt::CaseSensitive)) { colorSub = colorPopup->addMenu(colorname.replace("menu:", "")); } else { if(index == i) { colorname = QString(config.partColorNames[i]); colorPopup->setIcon(partColorIconsSelected.at(i)); colorPopup->setTitle(colorSub->title()+": "+colorname); colorname = QString("* "+config.partColorNames[i]); QAction *act_color = colorSub->addAction(partColorIconsSelected.at(i), colorname); act_color->setData(20 + i); } else { colorname = QString(" "+config.partColorNames[i]); QAction *act_color = colorSub->addAction(partColorIcons.at(i), colorname); act_color->setData(20 + i); } } } p->addAction(tr("Add Part"))->setData(1); p->addAction(tr("Add Part and Select"))->setData(2); if(type == 2) p->addAction(tr("Delete Part"))->setData(3); QAction* act = p->exec(QCursor::pos()); if (act) { int selection = act->data().toInt(); switch(selection) { case 1: { CItem *citem = los->composer->addCanvasPart(track); if(citem) { Part* mp = citem->part(); populateTable();//update check state //Select and scroll to the added part if(mp) { int psn = mp->sn(); for(int i = 0; i < m_model->rowCount(); ++i) { QStandardItem* item = m_model->item(i, 0); if(item) { int type = item->data(TrackRole).toInt(); if(type == 1) {//TrackMode continue; } else {//PartMode int sn = item->data(PartRole).toInt(); if(psn == sn) { //m_tempColor = item->foreground(); m_model->blockSignals(true); item->setForeground(QColor(99, 36, 36)); m_model->blockSignals(false); update(); m_selectedIndex = item->row(); m_table->selectRow(m_selectedIndex); m_table->scrollTo(m_model->index(m_selectedIndex, 0)); m_colorRows.append(m_selectedIndex); QTimer::singleShot(350, this, SLOT(updateColor())); break; } } } } } } } break; case 2: { CItem* citem = los->composer->addCanvasPart(track); if(citem) { Part* mp = citem->part(); if(mp) { m_editor->addPart(mp); populateTable();//update check state updatePartSelection(mp); } } break; } case 3: { if(npart) { audio->msgRemovePart(npart); populateTable();//update check state scrollPos = pos; /*if(row < m_model->rowCount()) { QModelIndex rowIndex = m_table->indexAt(pos); m_table->scrollTo(rowIndex, QAbstractItemView::PositionAtTop); }*/ } break; } case 20 ... NUM_PARTCOLORS + 20: { int curColorIndex = selection - 20; if(npart) { npart->setColorIndex(curColorIndex); song->update(SC_PART_COLOR_MODIFIED); } else { track->setDefaultPartColor(curColorIndex); } populateTable();//update check state break; } } } delete p; } }/*}}}*/
Route name2route(const QString& rn, bool /*dst*/, int rtype)/*{{{*/ { // printf("name2route %s\n", rn.toLatin1().constData()); int channel = -1; QString s(rn); // Support old route style in oom files. Obsolete. if (rn.size() >= 2 && rn[0].isNumber() && rn[1] == ':') { channel = rn[0].toAscii() - int('1'); s = rn.mid(2); } if (rtype == -1) { if (checkAudioDevice()) { void* p = audioDevice->findPort(s.toLatin1().constData()); if (p) return Route(p, channel); } TrackList* tl = song->tracks(); for (iTrack i = tl->begin(); i != tl->end(); ++i) { if ((*i)->isMidiTrack()) { MidiTrack* track = (MidiTrack*) * i; if (track->name() == s) return Route(track, channel); } else { AudioTrack* track = (AudioTrack*) * i; if (track->name() == s) return Route(track, channel); } } for (iMidiDevice i = midiDevices.begin(); i != midiDevices.end(); ++i) { if ((*i)->name() == s) return Route(*i, channel); } // p3.3.49 if (s.left(ROUTE_MIDIPORT_NAME_PREFIX.length()) == ROUTE_MIDIPORT_NAME_PREFIX) { bool ok = false; int port = s.mid(ROUTE_MIDIPORT_NAME_PREFIX.length()).toInt(&ok); if (ok) return Route(port, channel); } } else { if (rtype == Route::TRACK_ROUTE) { TrackList* tl = song->tracks(); for (iTrack i = tl->begin(); i != tl->end(); ++i) { if ((*i)->isMidiTrack()) { MidiTrack* track = (MidiTrack*) * i; if (track->name() == s) return Route(track, channel); } else { AudioTrack* track = (AudioTrack*) * i; if (track->name() == s) return Route(track, channel); } } }// TODO Distinguish the device types else if (rtype == Route::MIDI_DEVICE_ROUTE) { for (iMidiDevice i = midiDevices.begin(); i != midiDevices.end(); ++i) { if ((*i)->name() == s) return Route(*i, channel); } } else if (rtype == Route::JACK_ROUTE) { if (checkAudioDevice()) { void* p = audioDevice->findPort(s.toLatin1().constData()); if (p) return Route(p, channel); } } else if (rtype == Route::MIDI_PORT_ROUTE) // p3.3.49 { if (s.left(ROUTE_MIDIPORT_NAME_PREFIX.length()) == ROUTE_MIDIPORT_NAME_PREFIX) { bool ok = false; int port = s.mid(ROUTE_MIDIPORT_NAME_PREFIX.length()).toInt(&ok); if (ok) return Route(port, channel); } } } printf(" name2route: <%s> not found\n", rn.toLatin1().constData()); return Route((Track*) 0, channel); }/*}}}*/
void TrackListView::populateTable()/*{{{*/ { if(debugMsg) printf("TrackListView::populateTable\n"); QScrollBar *bar = m_table->verticalScrollBar(); int barPos = 0; if(bar) barPos = bar->sliderPosition(); m_model->clear(); for(iMidiTrack i = song->artracks()->begin(); i != song->artracks()->end(); ++i) { MidiTrack* track = (MidiTrack*)(*i); PartList* pl = track->parts(); if(m_displayRole == PartRole && pl->empty()) { continue; } QStandardItem* trackName = new QStandardItem(); trackName->setForeground(QBrush(QColor(205,209,205))); trackName->setBackground(QBrush(QColor(20,20,20))); trackName->setFont(QFont("fixed-width", 10, QFont::Bold)); trackName->setText(track->name()); trackName->setCheckable(true); trackName->setCheckState(m_selectedTracks.contains(track->id()) ? Qt::Checked : Qt::Unchecked); trackName->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter); trackName->setData(1, TrackRole); trackName->setData(track->name(), TrackNameRole); trackName->setData(track->id(), TrackIdRole); trackName->setEditable(true); m_model->appendRow(trackName); for(iPart ip = pl->begin(); ip != pl->end(); ++ip) { Part* part = ip->second; QStandardItem* partName = new QStandardItem(); partName->setFont(QFont("fixed-width", 9, QFont::Bold)); partName->setText(part->name()); partName->setData(part->sn(), PartRole); partName->setData(2, TrackRole); partName->setData(track->name(), TrackNameRole); partName->setData(part->name(), PartNameRole); partName->setData(part->tick(), TickRole); partName->setData(track->id(), TrackIdRole); partName->setEditable(true); partName->setCheckable(true); partName->setCheckState(m_editor->hasPart(part->sn()) ? Qt::Checked : Qt::Unchecked); if(!partColorIcons.isEmpty() && part->colorIndex() < partColorIcons.size()) partName->setIcon(partColorIcons.at(part->colorIndex())); m_model->appendRow(partName); } } m_model->setHorizontalHeaderLabels(m_headers); if(m_selectedIndex < m_model->rowCount()) { m_table->selectRow(m_selectedIndex); m_table->scrollTo(m_model->index(m_selectedIndex, 0)); } if(bar) bar->setSliderPosition(barPos); }/*}}}*/
static void writeSeqConfiguration(int level, Xml& xml, bool writePortInfo)/*{{{*/ { xml.tag(level++, "sequencer"); if (writePortInfo) { // // write information about all midi ports, their assigned // instruments and all managed midi controllers // for (int i = 0; i < MIDI_PORTS; ++i) { bool used = false; MidiPort* mport = &midiPorts[i]; // Route check by Tim. Port can now be used for routing even if no device. // Also, check for other non-defaults and save port, to preserve settings even if no device. // Dont write the config for the global inputs list they will be auto created with each startup if (mport->defaultInChannels() || mport->defaultOutChannels() || (mport->instrument() && !mport->instrument()->iname().isEmpty() && mport->instrument()->iname() != "GM") /*|| !mport->syncInfo().isDefault()*/ ) { used = true; } else {//Put the ID of this track into a list MidiTrackList* tl = song->midis(); for (iMidiTrack it = tl->begin(); it != tl->end(); ++it) { MidiTrack* t = *it; if (t->outPort() == i) { used = true; break; } } } MidiDevice* dev = mport->device(); if (!used && !dev) continue; bool isGlobal = gInputListPorts.contains(mport->portno()); xml.tag(level++, "midiport portId=\"%lld\" isGlobalInput=\"%d\"", mport->id(), isGlobal); if (mport->defaultInChannels()) xml.intTag(level, "defaultInChans", mport->defaultInChannels()); if (mport->defaultOutChannels()) xml.intTag(level, "defaultOutChans", mport->defaultOutChannels()); if (mport->instrument() && !mport->instrument()->iname().isEmpty() && (mport->instrument()->iname() != "GM")) // FIXME: TODO: Make this user configurable. { xml.strTag(level, "instrument", mport->instrument()->iname()); } if (dev) { xml.strTag(level, "name", dev->name()); xml.intTag(level, "cacheNRPN", (int)dev->cacheNRPN()); if (dev->deviceType() != MidiDevice::ALSA_MIDI) xml.intTag(level, "type", dev->deviceType()); xml.intTag(level, "openFlags", dev->openFlags()); } // write out registered controller for all channels MidiCtrlValListList* vll = mport->controller(); for (int k = 0; k < MIDI_CHANNELS; ++k) { int min = k << 24; int max = min + 0x100000; xml.tag(level++, "channel idx=\"%d\"", k); iMidiCtrlValList s = vll->lower_bound(min); iMidiCtrlValList e = vll->lower_bound(max); if (s != e) { for (iMidiCtrlValList i = s; i != e; ++i) { if(i->second->num() != 262145) { xml.tag(level++, "controller id=\"%d\"", i->second->num()); if (i->second->hwVal() != CTRL_VAL_UNKNOWN) xml.intTag(level, "val", i->second->hwVal()); xml.etag(--level, "controller"); } } } xml.etag(--level, "channel"); } QList<PatchSequence*> *patchSequences = mport->patchSequences(); if (patchSequences && !patchSequences->isEmpty()) { for (int p = 0; p < patchSequences->size(); ++p) { PatchSequence* ps = patchSequences->at(p); QString pm = ps->name.replace('\n', " "); xml.put(level, "<patchSequence id=\"%d\" name=\"%s\" checked=\"%d\" />", ps->id, pm.toLatin1().constData(), ps->selected); } } if(!mport->presets()->isEmpty()) { QHashIterator<int, QString> iter(*mport->presets()); while(iter.hasNext()) { iter.next(); xml.put(level, "<midiPreset id=\"%d\" sysex=\"%s\"/>", iter.key(), iter.value().toLatin1().constData()); } } xml.etag(--level, "midiport"); } } xml.tag(--level, "/sequencer"); }/*}}}*/
void TrackListView::toggleTrackPart(QStandardItem* item)/*{{{*/ { if(!item) return; m_editing = true; int type = item->data(TrackRole).toInt(); int column = item->column(); bool checked = (item->checkState() == Qt::Checked); qint64 tid = item->data(TrackIdRole).toLongLong(); QString trackName = item->data(TrackNameRole).toString(); QString partName; m_selectedIndex = item->row(); QString newName = item->text(); if(type == 1) { if(trackName == newName) column = 0; else column = 1; } else { partName = item->data(PartNameRole).toString(); if(partName == newName) column = 0; else column = 1; } MidiTrack* track = song->findTrackById(tid); if(!track || !m_editor) { m_editing = false; return; } PartList* list = track->parts(); if(list->empty()) { m_editing = false; updateCheck(); return; } switch(type) { case 1: //Track { if(!column) { if(checked) { m_editor->addParts(list); m_selectedTracks.append(track->id()); if(!list->empty()) { updatePartSelection(list->begin()->second); updateCheck(list, checked); } } else { m_editor->removeParts(list); m_editor->updateCanvas(); m_selectedTracks.removeAll(track->id()); updateCheck(); song->update(SC_SELECTION); } } else { bool valid = true; if(newName.isEmpty()) { valid = false; } if(valid) { for (iMidiTrack i = song->tracks()->begin(); i != song->tracks()->end(); ++i) { if ((*i)->name() == newName) { valid = false; break; } } } if(!valid) { QMessageBox::critical(this, tr("LOS: bad trackname"), tr("please choose a unique track name"), QMessageBox::Ok, Qt::NoButton, Qt::NoButton); m_model->blockSignals(true); item->setText(item->data(TrackNameRole).toString()); m_model->blockSignals(false); update(); m_editing = false; return; } m_model->blockSignals(true); item->setData(newName, TrackNameRole); m_model->blockSignals(false); Track* newTrack = track->clone(false); newTrack->setName(newName); track->setName(newName); audio->msgChangeTrack((MidiTrack*)newTrack, track); } } break; case 2: //Part { int sn = item->data(PartRole).toInt(); unsigned tick = item->data(TickRole).toInt(); Part* part = list->find(tick, sn); if(part) { if(!column) { if(checked) { m_editor->addPart(part); updatePartSelection(part); } else { m_editor->removePart(sn); m_editor->updateCanvas(); updateCheck(); song->update(SC_SELECTION); } } else { if(partName.isEmpty()) { QMessageBox::critical(this, tr("LOS: Invalid part name"), tr("Please choose a name with at least one charactor"), QMessageBox::Ok, Qt::NoButton, Qt::NoButton); m_model->blockSignals(true); item->setText(item->data(PartNameRole).toString()); m_model->blockSignals(false); update(); m_editing = false; return; } m_model->blockSignals(true); item->setData(newName, PartNameRole); m_model->blockSignals(false); Part* newPart = part->clone(); newPart->setName(newName); // Indicate do undo, and do port controller values but not clone parts. audio->msgChangePart(part, newPart, true, true, false); song->update(SC_PART_MODIFIED); } } } break; } update(); if(m_selectedIndex < m_model->rowCount()) { m_table->selectRow(m_selectedIndex); m_table->scrollTo(m_model->index(m_selectedIndex, 0)); } m_editing = false; }/*}}}*/