//========================================================= // MusE // Linux Music Editor // $Id: editevent.cpp,v 1.12.2.6 2009/02/02 21:38:00 terminator356 Exp $ // (C) Copyright 1999-2004 Werner Schweer (ws@seh.de) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; version 2 of // the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "awl/posedit.h" #include "song.h" #include "event.h" #include "midictrl.h" #include "editevent.h" #include "pitchedit.h" #include "intlabel.h" #include "globals.h" #include "gconfig.h" #include "midiport.h" #include "midiedit/drummap.h" #include "instruments/minstrument.h" #include "midi.h" #include "popupmenu.h" namespace MusEGui { //--------------------------------------------------------- // string2qhex //--------------------------------------------------------- QString string2hex(const unsigned char* data, int len) { QString d; QString s; for (int i = 0; i < len; ++i) { if ((i > 0) && ((i % 8)==0)) { d += "\n"; } else if (i) d += " "; d += s.sprintf("%02x", data[i]); } return d; } //--------------------------------------------------------- // hex2string //--------------------------------------------------------- char* hex2string(QWidget* parent, const char* src, int& len) { char buffer[2048]; char* dst = buffer; while (*src) { while (*src == ' ' || *src == '\n') ++src; char* ep; long val = strtol(src, &ep, 16); if (ep == src) { QMessageBox::information(parent, QString("MusE"), QWidget::tr("Cannot convert sysex string")); return 0; } src = ep; *dst++ = val; if (dst - buffer >= 2048) { QMessageBox::information(parent, QString("MusE"), QWidget::tr("Hex String too long (2048 bytes limit)")); return 0; } } len = dst - buffer; if(len == 0) return 0; char* b = new char[len+1]; memcpy(b, buffer, len); b[len] = 0; return b; } //--------------------------------------------------------- // getEvent //--------------------------------------------------------- MusECore::Event EditNoteDialog::getEvent(int tick, const MusECore::Event& event, QWidget* parent) { EditNoteDialog* dlg = new EditNoteDialog(tick, event, parent); MusECore::Event nevent; if (dlg->exec() == QDialog::Accepted) { nevent = dlg->event(); } delete dlg; return nevent; } MusECore::Event EditSysexDialog::getEvent(int tick, const MusECore::Event& event, QWidget* parent) { EditSysexDialog* dlg = new EditSysexDialog(tick, event, parent); MusECore::Event nevent; if (dlg->exec() == QDialog::Accepted) { nevent = dlg->event(); } delete dlg; return nevent; } MusECore::Event EditMetaDialog::getEvent(int tick, const MusECore::Event& event, QWidget* parent) { EditEventDialog* dlg = new EditMetaDialog(tick, event, parent); MusECore::Event nevent; if (dlg->exec() == QDialog::Accepted) { nevent = dlg->event(); } delete dlg; return nevent; } //--------------------------------------------------------- // EditEventDialog //--------------------------------------------------------- EditEventDialog::EditEventDialog(QWidget* parent) : QDialog(parent) { QVBoxLayout* xlayout = new QVBoxLayout; layout1 = new QGridLayout; // ddskrjo this xlayout->addLayout(layout1); //--------------------------------------------------- // Ok, Cancel //--------------------------------------------------- QBoxLayout* w5 = new QHBoxLayout; // ddskrjo this QPushButton* okB = new QPushButton(tr("Ok")); okB->setDefault(true); QPushButton* cancelB = new QPushButton(tr("Cancel")); okB->setFixedWidth(80); cancelB->setFixedWidth(80); w5->addWidget(okB); w5->addSpacing(12); w5->addWidget(cancelB); w5->addStretch(1); xlayout->addLayout(w5); setLayout(xlayout); connect(cancelB, SIGNAL(clicked()), SLOT(reject())); connect(okB, SIGNAL(clicked()), SLOT(accept())); } //--------------------------------------------------------- // EditNoteDialog //--------------------------------------------------------- EditNoteDialog::EditNoteDialog(int tick, const MusECore::Event& event, QWidget* parent) : QDialog(parent) { setupUi(this); if (!event.empty()) { epos->setValue(tick); il1->setValue(event.lenTick()); pl->setValue(event.pitch()); il2->setValue(event.velo()); il3->setValue(event.veloOff()); } else { epos->setValue(tick); il1->setValue(96); pl->setValue(64); il2->setValue(100); il3->setValue(0); } } //--------------------------------------------------------- // EditNoteDialog::event //--------------------------------------------------------- MusECore::Event EditNoteDialog::event() { MusECore::Event event(MusECore::Note); event.setTick(epos->pos().tick()); event.setA(pl->value()); event.setB(il2->value()); event.setC(il3->value()); event.setLenTick(il1->value()); return event; } //--------------------------------------------------------- // EditSysExDialog //--------------------------------------------------------- EditSysexDialog::EditSysexDialog(int tick, const MusECore::Event& event, QWidget* parent) : QDialog(parent) { setupUi(this); sysex = 0; if (!event.empty()) { epos->setValue(tick); edit->setText(string2hex(event.data(), event.dataLen())); } else { epos->setValue(tick); } } //--------------------------------------------------------- // ~EditSysexDialog //--------------------------------------------------------- EditSysexDialog::~EditSysexDialog() { if (sysex) delete sysex; } //--------------------------------------------------------- // EditSysExDialog::event //--------------------------------------------------------- MusECore::Event EditSysexDialog::event() { MusECore::Event event(MusECore::Sysex); event.setTick(epos->pos().tick()); event.setData(sysex, len); return event; } //--------------------------------------------------------- // accept //--------------------------------------------------------- void EditSysexDialog::accept() { QString qsrc = edit->toPlainText(); QByteArray ba = qsrc.toLatin1(); const char* src = ba.constData(); sysex = (unsigned char*)hex2string(this, src, len); if (sysex) QDialog::accept(); } //--------------------------------------------------------- // EditMetaDialog //--------------------------------------------------------- EditMetaDialog::EditMetaDialog(int tick, const MusECore::Event& ev, QWidget* parent) : EditEventDialog(parent) { meta = 0; setWindowTitle(tr("MusE: Enter Meta Event")); QLabel* l1 = new QLabel(tr("Time Position")); ///epos = new PosEdit; epos = new Awl::PosEdit; QLabel* l2 = new QLabel(tr("Meta Type")); il2 = new MusEGui::IntLabel(-1, 0, 127, this, -1); il2->setFixedWidth(100); il2->setFrame(true); il2->setDark(); typeLabel = new QLabel; typeLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); QHBoxLayout* typeLayout = new QHBoxLayout; typeLayout->addWidget(il2); typeLayout->addWidget(typeLabel); typeLayout->addStretch(); hexButton = new QRadioButton(tr("Enter Hex")); hexButton->setChecked(true); connect(hexButton, SIGNAL(toggled(bool)), SLOT(toggled(bool))); edit = new QTextEdit; edit->setFont(MusEGlobal::config.fonts[0]); if (!ev.empty()) { epos->setValue(tick); il2->setValue(ev.dataA()); toggled(true); edit->setText(string2hex(ev.data(), ev.dataLen())); } else { epos->setValue(tick); il2->setValue(0); } typeChanged(il2->value()); connect(il2, SIGNAL(valueChanged(int)), SLOT(typeChanged(int))); layout1->addWidget(l1, 0, 0); layout1->addWidget(epos, 0, 1, Qt::AlignLeft); layout1->addWidget(l2, 1, 0); //layout1->addWidget(il2, 1, 1, AlignLeft); layout1->addLayout(typeLayout, 1, 1); //layout1->addMultiCellWidget(hexButton, 2, 2, 0, 1); //layout1->addMultiCellWidget(edit, 3, 3, 0, 1); layout1->addWidget(hexButton, 2, 0, 1, 2); layout1->addWidget(edit, 3, 0, 1, 2); } //--------------------------------------------------------- // typeChanged //--------------------------------------------------------- void EditMetaDialog::typeChanged(int val) { typeLabel->setText(MusECore::midiMetaName(val)); } //--------------------------------------------------------- // toggled //--------------------------------------------------------- void EditMetaDialog::toggled(bool flag) { QString qsrc = edit->toPlainText(); QByteArray ba = qsrc.toLatin1(); const char* src = ba.constData(); edit->clear(); QString dst; if (flag) { // convert to hex dst = string2hex((unsigned char*)src, ba.length()); } else { // convert to string int len; dst = hex2string(this, src, len); } edit->setText(dst); } //--------------------------------------------------------- // ~EditMetaDialog //--------------------------------------------------------- EditMetaDialog::~EditMetaDialog() { if (meta) delete meta; } //--------------------------------------------------------- // EditMetaDialog::event //--------------------------------------------------------- MusECore::Event EditMetaDialog::event() { MusECore::Event event(MusECore::Meta); event.setTick(epos->pos().tick()); event.setA(il2->value()); event.setData(meta, len); // TODO ?? return event; } //--------------------------------------------------------- // accept //--------------------------------------------------------- void EditMetaDialog::accept() { QString qsrc = edit->toPlainText(); QByteArray ba = qsrc.toLatin1(); const char* src = ba.constData(); if (!hexButton->isChecked()) { meta = (unsigned char*)strdup(src); len = ba.length(); QDialog::accept(); return; } meta = (unsigned char*)hex2string(this, src, len); if (meta) QDialog::accept(); } //--------------------------------------------------------- // getEvent //--------------------------------------------------------- MusECore::Event EditCtrlDialog::getEvent(int tick, const MusECore::Event& event, const MusECore::MidiPart* part, QWidget* parent) { EditCtrlDialog* dlg = new EditCtrlDialog(tick, event, part, parent); MusECore::Event nevent; if (dlg->exec() == QDialog::Accepted) { nevent = dlg->event(); } delete dlg; return nevent; } //--------------------------------------------------------- // EditCtrlDialog::event //--------------------------------------------------------- MusECore::Event EditCtrlDialog::event() { MusECore::Event event(MusECore::Controller); event.setTick(timePos->pos().tick()); int cnum = 0; QListWidgetItem* item = ctrlList->currentItem(); if(item != 0) cnum = item->data(Qt::UserRole).toInt(); MusECore::MidiTrack* track = part->track(); bool isDrum = track->type() == MusECore::Track::DRUM; MusECore::MidiPort* port = &MusEGlobal::midiPorts[track->outPort()]; int channel = track->outChannel(); int evnum = cnum; int num = cnum; if((cnum & 0xff) == 0xff) { evnum = (cnum & ~0xff) | (noteSpinBox->value() & 0x7f); num = evnum; if(isDrum) { MusECore::DrumMap* dm = &MusEGlobal::drumMap[noteSpinBox->value() & 0x7f]; num = (cnum & ~0xff) | dm->anote; // Default to track port if -1 and track channel if -1. if(dm->port != -1) port = &MusEGlobal::midiPorts[dm->port]; if(dm->channel != -1) channel = dm->channel; } } MusECore::MidiController* c = port->midiController(cnum); MusECore::MidiCtrlValListList* cll = port->controller(); if(cll->find(channel, num) == cll->end()) { MusECore::MidiCtrlValList* vl = new MusECore::MidiCtrlValList(num); cll->add(channel, vl); } event.setA(evnum); if(cnum == MusECore::CTRL_PROGRAM) { int hb = hbank->value(); int lb = lbank->value(); int prog = program->value(); if (hb > 0 && hb < 129) hb -= 1; else hb = 0xff; if (lb > 0 && lb < 129) lb -= 1; else lb = 0xff; if (prog > 0 && prog < 129) prog -= 1; else prog = 0xff; int val = (hb << 16) + (lb << 8) + prog; event.setB(val); } else event.setB(valSlider->value() + c->bias()); return event; } //--------------------------------------------------------- // EditCtrlDialog // PosEdit* timePos; // QSlider* valSlider; // QSpinBox* valSpinBox; // QLabel* controllerName; // QListWidget* ctrlList; // QPushButton* buttonNewController; //--------------------------------------------------------- struct CI { int num; QString s; bool used; bool off; bool instrument; CI(int n, const QString& ss, bool u, bool o, bool i) : num(n), s(ss), used(u), off(o), instrument(i) {} }; EditCtrlDialog::EditCtrlDialog(int tick, const MusECore::Event& event, const MusECore::MidiPart* p, QWidget* parent) : QDialog(parent), part(p) { setupUi(this); widgetStack->setAutoFillBackground(true); MusECore::MidiTrack* track = part->track(); MusECore::MidiPort* port = &MusEGlobal::midiPorts[track->outPort()]; bool isDrum = track->type() == MusECore::Track::DRUM; bool isNewDrum = track->type() == MusECore::Track::NEW_DRUM; bool isMidi = track->type() == MusECore::Track::MIDI; MusECore::MidiCtrlValListList* cll = port->controller(); int channel = track->outChannel(); MusECore::MidiInstrument* instr = port->instrument(); MusECore::MidiControllerList* mcl = instr->controller(); int val = 0; int ev_num = 0; int num = 0; int ev_cnum = 0; int ev_note = -1; if (!event.empty()) { ev_num = event.dataA(); num = ev_num; ev_cnum = ev_num; val = event.dataB(); if(port->drumController(ev_num)) { ev_cnum |= 0xff; if(isDrum) num = (ev_num & ~0xff) | MusEGlobal::drumMap[ev_num & 0xff].anote; ev_note = ev_num & 0xff; } } MusECore::MidiController* mc = port->midiController(ev_num); ctrlList->clear(); ctrlList->setSelectionMode(QAbstractItemView::SingleSelection); //--------------------------------------------------- // build list of midi controllers for current // MusECore::MidiPort/channel //--------------------------------------------------- std::list sList; typedef std::list::iterator isList; std::set already_added_nums; for (MusECore::iMidiCtrlValList it = cll->begin(); it != cll->end(); ++it) { MusECore::MidiCtrlValList* cl = it->second; int ch = it->first >> 24; if(ch != channel) continue; MusECore::MidiController* c = port->midiController(cl->num()); bool isDrumCtrl = (c->isPerNoteController()); int show = c->showInTracks(); int cnum = c->num(); int clnum = cl->num(); isList i = sList.begin(); for (; i != sList.end(); ++i) { if (i->num == cnum) break; } if (i == sList.end()) { bool used = (clnum == num); bool off = cl->hwVal() == MusECore::CTRL_VAL_UNKNOWN; // Does it have a value or is it 'off'? // Filter if not used and off. But if there's something there, we must show it. //if(!used && off && if(!used && //off && (((isDrumCtrl || isNewDrum) && !(show & MusECore::MidiController::ShowInDrum)) || (isMidi && !(show & MusECore::MidiController::ShowInMidi)))) continue; bool isinstr = mcl->find(cnum) != mcl->end(); // Need to distinguish between global default controllers and // instrument defined controllers. Instrument takes priority over global // ie they 'overtake' definition of a global controller such that the // global def is no longer available. //sList.push_back(CI(num, sList.push_back(CI(cnum, isinstr ? MusECore::midiCtrlNumString(cnum, true) + c->name() : MusECore::midiCtrlName(cnum, true), used, off, isinstr)); already_added_nums.insert(num); } } // Add instrument-defined controllers: QListWidgetItem* sel_item = 0; for (isList i = sList.begin(); i != sList.end(); ++i) { // Filter if not used and off. But if there's something there, we must show it. if(!i->instrument && !i->used && i->off) continue; QListWidgetItem* item = new QListWidgetItem(i->s, ctrlList); item->setData(Qt::UserRole, i->num); if(i->num == ev_cnum) sel_item = item; } if(sel_item) ctrlList->setCurrentItem(sel_item); valSlider->setRange(mc->minVal(), mc->maxVal()); valSpinBox->setRange(mc->minVal(), mc->maxVal()); controllerName->setText(mc->name()); if(!event.empty()) { if(ev_num == MusECore::CTRL_PROGRAM) { widgetStack->setCurrentIndex(1); updatePatch(val); } else { widgetStack->setCurrentIndex(0); valSlider->setValue(val - mc->bias()); if(mc->isPerNoteController()) { noteSpinBox->setVisible(true); noteSpinBox->setEnabled(true); noteLabel->setVisible(true); noteLabel->setEnabled(true); if(ev_note != -1) noteSpinBox->setValue(ev_note); } else { noteSpinBox->setEnabled(false); noteSpinBox->setVisible(false); noteLabel->setEnabled(false); noteLabel->setVisible(false); } } } else { noteSpinBox->setEnabled(false); noteSpinBox->setVisible(false); noteLabel->setEnabled(false); noteLabel->setVisible(false); if(sel_item) ctrlListClicked(sel_item); } connect(ctrlList, SIGNAL(itemClicked(QListWidgetItem*)), SLOT(ctrlListClicked(QListWidgetItem*))); connect(buttonNewController, SIGNAL(clicked()), SLOT(newController())); connect(hbank, SIGNAL(valueChanged(int)), SLOT(programChanged())); connect(lbank, SIGNAL(valueChanged(int)), SLOT(programChanged())); connect(program, SIGNAL(valueChanged(int)), SLOT(programChanged())); connect(patchName, SIGNAL(released()), SLOT(instrPopup())); connect(buttonCancel, SIGNAL(clicked()), SLOT(reject())); connect(buttonOk, SIGNAL(clicked()), SLOT(accept())); timePos->setValue(tick); } //--------------------------------------------------------- // newController //--------------------------------------------------------- void EditCtrlDialog::newController() { MusEGui::PopupMenu* pup = new MusEGui::PopupMenu(this); // populate popup with all controllers available for // current instrument MusECore::MidiTrack* track = part->track(); int portn = track->outPort(); MusECore::MidiPort* port = &MusEGlobal::midiPorts[portn]; bool isDrum = track->type() == MusECore::Track::DRUM; bool isNewDrum = track->type() == MusECore::Track::NEW_DRUM; bool isMidi = track->type() == MusECore::Track::MIDI; MusECore::MidiInstrument* instr = port->instrument(); MusECore::MidiControllerList* mcl = instr->controller(); MusECore::MidiCtrlValListList* cll = port->controller(); int channel = track->outChannel(); for (MusECore::iMidiController ci = mcl->begin(); ci != mcl->end(); ++ci) { MusECore::MidiController* c = ci->second; int cnum = c->num(); int show = c->showInTracks(); if(((isDrum || isNewDrum) && !(show & MusECore::MidiController::ShowInDrum)) || (isMidi && !(show & MusECore::MidiController::ShowInMidi))) continue; // If it's not already in the parent menu... int idx = 0; for(; idx < ctrlList->count(); ++idx) { if(ctrlList->item(idx)->data(Qt::UserRole).toInt() == cnum) break; } if(idx >= ctrlList->count()) { QAction* act = pup->addAction(MusECore::midiCtrlNumString(cnum, true) + c->name()); act->setData(cnum); } } QAction* act = pup->exec(buttonNewController->mapToGlobal(QPoint(0,0))); if (act && act->data().toInt() != -1) { int rv = act->data().toInt(); int cnum = rv; for (MusECore::iMidiController ci = mcl->begin(); ci != mcl->end(); ++ci) { MusECore::MidiController* mc = ci->second; if (mc->num() == cnum) { // Create a new controller list if it does not exist. // FIXME: Sorry no per-pitch controller lists created here // (meaning you should only create one 'new' one at a time) // because the user has not had a chance to choose a pitch yet. // They are handled in accept(), where there are more checks and creations. if(!mc->isPerNoteController() && cll->find(channel, rv) == cll->end()) { MusECore::MidiCtrlValList* vl = new MusECore::MidiCtrlValList(rv); cll->add(channel, vl); } int idx = 0; for (; idx < ctrlList->count() ;++idx) { QListWidgetItem* item = ctrlList->item(idx); int item_data = item->data(Qt::UserRole).toInt(); if(item_data == cnum) { ctrlList->setCurrentItem(item); ctrlListClicked(item); break; } } if (idx >= ctrlList->count()) { QListWidgetItem* new_item = new QListWidgetItem(act->text(), ctrlList); new_item->setData(Qt::UserRole, cnum); ctrlList->setCurrentItem(new_item); ctrlListClicked(new_item); break; } break; } } } delete pup; } //--------------------------------------------------------- // ctrlListClicked //--------------------------------------------------------- void EditCtrlDialog::ctrlListClicked(QListWidgetItem* item) { if(item == 0) return; int cnum = item->data(Qt::UserRole).toInt(); MusECore::MidiTrack* track = part->track(); int portn = track->outPort(); MusECore::MidiPort* port = &MusEGlobal::midiPorts[portn]; MusECore::MidiController* c = port->midiController(cnum); int val; if (cnum == MusECore::CTRL_PROGRAM) { widgetStack->setCurrentIndex(1); val = c->initVal(); if(val == MusECore::CTRL_VAL_UNKNOWN) val = 0; updatePatch(val); } else { widgetStack->setCurrentIndex(0); if(c->isPerNoteController()) { noteSpinBox->setEnabled(true); noteSpinBox->setVisible(true); noteLabel->setEnabled(true); noteLabel->setVisible(true); } else { noteSpinBox->setEnabled(false); noteSpinBox->setVisible(false); noteLabel->setEnabled(false); noteLabel->setVisible(false); } valSlider->setRange(c->minVal(), c->maxVal()); valSpinBox->setRange(c->minVal(), c->maxVal()); controllerName->setText(c->name()); val = c->initVal(); if(val == MusECore::CTRL_VAL_UNKNOWN || val == 0) { switch(cnum) { case MusECore::CTRL_PANPOT: val = 64 - c->bias(); break; case MusECore::CTRL_VOLUME: val = 100; break; default: val = 0; break; } } valSlider->setValue(val); } } //--------------------------------------------------------- // updatePatch //--------------------------------------------------------- void EditCtrlDialog::updatePatch(int val) { MusECore::MidiTrack* track = part->track(); int port = track->outPort(); int channel = track->outChannel(); MusECore::MidiInstrument* instr = MusEGlobal::midiPorts[port].instrument(); patchName->setText(instr->getPatchName(channel, val, track->isDrumTrack())); int hb = ((val >> 16) & 0xff) + 1; if (hb == 0x100) hb = 0; int lb = ((val >> 8) & 0xff) + 1; if (lb == 0x100) lb = 0; int pr = (val & 0xff) + 1; if (pr == 0x100) pr = 0; hbank->blockSignals(true); lbank->blockSignals(true); program->blockSignals(true); hbank->setValue(hb); lbank->setValue(lb); program->setValue(pr); hbank->blockSignals(false); lbank->blockSignals(false); program->blockSignals(false); } //--------------------------------------------------------- // instrPopup //--------------------------------------------------------- void EditCtrlDialog::instrPopup() { MusECore::MidiTrack* track = part->track(); int channel = track->outChannel(); int port = track->outPort(); MusECore::MidiInstrument* instr = MusEGlobal::midiPorts[port].instrument(); MusEGui::PopupMenu* pup = new MusEGui::PopupMenu(this); instr->populatePatchPopup(pup, channel, track->isDrumTrack()); if(pup->actions().count() == 0) { delete pup; return; } QAction* rv = pup->exec(patchName->mapToGlobal(QPoint(10,5))); if (rv) { updatePatch(rv->data().toInt()); } delete pup; } //--------------------------------------------------------- // programChanged //--------------------------------------------------------- void EditCtrlDialog::programChanged() { int hb = hbank->value(); int lb = lbank->value(); int prog = program->value(); if (hb > 0 && hb < 129) hb -= 1; else hb = 0xff; if (lb > 0 && lb < 129) lb -= 1; else lb = 0xff; if (prog > 0 && prog < 129) prog -= 1; else prog = 0xff; int val = (hb << 16) + (lb << 8) + prog; updatePatch(val); } } // namespace MusEGui