diff options
author | Tim E. Real <termtech@rogers.com> | 2010-12-24 01:08:57 +0000 |
---|---|---|
committer | Tim E. Real <termtech@rogers.com> | 2010-12-24 01:08:57 +0000 |
commit | 877ccd787443eb6a944c98487583f242add5a944 (patch) | |
tree | c7bd278a684017698a1a2f14e6f5ca743f30f8fa /muse2/muse/widgets/mtrackinfo.cpp | |
parent | 31c7e7e3594b80c880f905a8bd2948e2bb75c7d8 (diff) |
MidiTrackInfo is now self-contained.
Diffstat (limited to 'muse2/muse/widgets/mtrackinfo.cpp')
-rw-r--r-- | muse2/muse/widgets/mtrackinfo.cpp | 1491 |
1 files changed, 1491 insertions, 0 deletions
diff --git a/muse2/muse/widgets/mtrackinfo.cpp b/muse2/muse/widgets/mtrackinfo.cpp new file mode 100644 index 00000000..f88f60ce --- /dev/null +++ b/muse2/muse/widgets/mtrackinfo.cpp @@ -0,0 +1,1491 @@ +//========================================================= +// MusE +// Linux Music Editor +// (C) Copyright 2010 Werner Schweer and others (ws@seh.de) +//========================================================= + +#include <QTimer> +#include <QMessageBox> + +#include <math.h> +#include <string.h> + +#include "mtrackinfo.h" +#include "song.h" +#include "globals.h" +#include "config.h" +#include "gconfig.h" +#include "midiport.h" +#include "minstrument.h" +#include "mididev.h" +#include "utils.h" +#include "audio.h" +#include "midi.h" +#include "midictrl.h" +#include "icons.h" +#include "app.h" +#include "route.h" +#include "popupmenu.h" + +//--------------------------------------------------------- +// setTrack +//--------------------------------------------------------- + +void MidiTrackInfo::setTrack(Track* t) +{ + selected = t; + //updateTrackInfo(-1); +} + +//--------------------------------------------------------- +// midiTrackInfo +//--------------------------------------------------------- + +MidiTrackInfo::MidiTrackInfo(QWidget* parent, Track* sel_track) : QWidget(parent) +{ + setupUi(this); + _midiDetect = false; + + selected = sel_track; + + // Since program covers 3 controls at once, it is in 'midi controller' units rather than 'gui control' units. + //program = -1; + program = CTRL_VAL_UNKNOWN; + pan = -65; + volume = -1; + + //iChanDetectLabel->setPixmap(*darkgreendotIcon); + iChanDetectLabel->setPixmap(*darkRedLedIcon); + + QIcon recEchoIconSet; + //recEchoIconSet.addPixmap(*recEchoIconOn, QIcon::Normal, QIcon::On); + //recEchoIconSet.addPixmap(*recEchoIconOff, QIcon::Normal, QIcon::Off); + recEchoIconSet.addPixmap(*midiConnectorRedBorderIcon, QIcon::Normal, QIcon::On); + recEchoIconSet.addPixmap(*edit_midiIcon, QIcon::Normal, QIcon::Off); + recEchoButton->setIcon(recEchoIconSet); + //recEchoButton->setIcon(QIcon(*edit_midiIcon)); + //recEchoButton->setIconSize(edit_midiIcon->size()); + + + // MusE-2: AlignCenter and WordBreak are set in the ui(3) file, but not supported by QLabel. Turn them on here. + trackNameLabel->setAlignment(Qt::AlignCenter); + //Qt::TextWordWrap is not available for alignment in Qt4 - Orcan + // MusE-2 Tested: TextWrapAnywhere actually works, but in fact it takes precedence + // over word wrap, so I found it is not really desirable. Maybe with a user setting... + //trackNameLabel->setAlignment(Qt::AlignCenter | Qt::TextWordWrap | Qt::TextWrapAnywhere); + //trackNameLabel->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Minimum)); + + if(selected) + trackNameLabel->setObjectName(selected->cname()); + QPalette pal; + pal.setColor(trackNameLabel->backgroundRole(), QColor(0, 160, 255)); // Med blue + trackNameLabel->setPalette(pal); + trackNameLabel->setWordWrap(true); + trackNameLabel->setAutoFillBackground(true); + trackNameLabel->setTextFormat(Qt::PlainText); + trackNameLabel->setLineWidth(2); + trackNameLabel->setFrameStyle(QFrame::Sunken | QFrame::StyledPanel); + trackNameLabel->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Minimum)); + + // Added by Tim. p3.3.9 + setLabelText(); + setLabelFont(); + + connect(iPatch, SIGNAL(released()), SLOT(instrPopup())); + + ///pop = new QMenu(iPatch); + //pop->setCheckable(false); // not needed in Qt4 + + // Removed by Tim. p3.3.9 + //connect(iName, SIGNAL(returnPressed()), SLOT(iNameChanged())); + + connect(iOutputChannel, SIGNAL(valueChanged(int)), SLOT(iOutputChannelChanged(int))); + ///connect(iInputChannel, SIGNAL(textChanged(const QString&)), SLOT(iInputChannelChanged(const QString&))); + connect(iHBank, SIGNAL(valueChanged(int)), SLOT(iProgHBankChanged())); + connect(iLBank, SIGNAL(valueChanged(int)), SLOT(iProgLBankChanged())); + connect(iProgram, SIGNAL(valueChanged(int)), SLOT(iProgramChanged())); + connect(iHBank, SIGNAL(doubleClicked()), SLOT(iProgramDoubleClicked())); + connect(iLBank, SIGNAL(doubleClicked()), SLOT(iProgramDoubleClicked())); + connect(iProgram, SIGNAL(doubleClicked()), SLOT(iProgramDoubleClicked())); + connect(iLautst, SIGNAL(valueChanged(int)), SLOT(iLautstChanged(int))); + connect(iLautst, SIGNAL(doubleClicked()), SLOT(iLautstDoubleClicked())); + connect(iTransp, SIGNAL(valueChanged(int)), SLOT(iTranspChanged(int))); + connect(iAnschl, SIGNAL(valueChanged(int)), SLOT(iAnschlChanged(int))); + connect(iVerz, SIGNAL(valueChanged(int)), SLOT(iVerzChanged(int))); + connect(iLen, SIGNAL(valueChanged(int)), SLOT(iLenChanged(int))); + connect(iKompr, SIGNAL(valueChanged(int)), SLOT(iKomprChanged(int))); + connect(iPan, SIGNAL(valueChanged(int)), SLOT(iPanChanged(int))); + connect(iPan, SIGNAL(doubleClicked()), SLOT(iPanDoubleClicked())); + connect(iOutput, SIGNAL(activated(int)), SLOT(iOutputPortChanged(int))); + ///connect(iInput, SIGNAL(textChanged(const QString&)), SLOT(iInputPortChanged(const QString&))); + connect(recordButton, SIGNAL(clicked()), SLOT(recordClicked())); + connect(progRecButton, SIGNAL(clicked()), SLOT(progRecClicked())); + connect(volRecButton, SIGNAL(clicked()), SLOT(volRecClicked())); + connect(panRecButton, SIGNAL(clicked()), SLOT(panRecClicked())); + connect(recEchoButton, SIGNAL(toggled(bool)), SLOT(recEchoToggled(bool))); + connect(iRButton, SIGNAL(pressed()), SLOT(inRoutesPressed())); + + // TODO: Works OK, but disabled for now, until we figure out what to do about multiple out routes and display values... + oRButton->setEnabled(false); + oRButton->setVisible(false); + connect(oRButton, SIGNAL(pressed()), SLOT(outRoutesPressed())); + + connect(heartBeatTimer, SIGNAL(timeout()), SLOT(heartBeat())); +} + +//--------------------------------------------------------- +// heartBeat +//--------------------------------------------------------- + +void MidiTrackInfo::heartBeat() +{ + ///if(!showTrackinfoFlag || !selected) + if(!isVisible() || !isEnabled() || !selected) + return; + switch(selected->type()) + { + case Track::MIDI: + case Track::DRUM: + { + MidiTrack* track = (MidiTrack*)selected; + + int outChannel = track->outChannel(); + int outPort = track->outPort(); + ///int ichMask = track->inChannelMask(); + //int iptMask = track->inPortMask(); + ///unsigned int iptMask = track->inPortMask(); + + MidiPort* mp = &midiPorts[outPort]; + + // Set record echo. + if(recEchoButton->isChecked() != track->recEcho()) + { + recEchoButton->blockSignals(true); + recEchoButton->setChecked(track->recEcho()); + recEchoButton->blockSignals(false); + } + + // Check for detection of midi general activity on chosen channels... + int mpt = 0; + //int mch = 0; + RouteList* rl = track->inRoutes(); + + ciRoute r = rl->begin(); + //for( ; mpt < MIDI_PORTS; ++mpt) + for( ; r != rl->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; + + // NOTE: TODO: Code for channelless events like sysex, ** IF we end up using the 'special channel 17' method. + //if(r->channel == -1) + if(r->channel == -1 || r->channel == 0) // p3.3.50 + continue; + + // No port assigned to the device? + //mpt = r->device->midiPort(); + mpt = r->midiPort; // p3.3.49 + if(mpt < 0 || mpt >= MIDI_PORTS) + continue; + + //for(; mch < MIDI_CHANNELS; ++mch) + //{ + //if(midiPorts[mpt].syncInfo().actDetect(mch) && (iptMask & (1 << mpt)) && (ichMask & (1 << mch)) ) + //if((iptMask & bitShiftLU[mpt]) && (midiPorts[mpt].syncInfo().actDetectBits() & ichMask) ) + //if(midiPorts[mpt].syncInfo().actDetectBits() & bitShiftLU[r->channel]) + if(midiPorts[mpt].syncInfo().actDetectBits() & r->channel) // p3.3.50 Use new channel mask. + { + //if(iChanTextLabel->paletteBackgroundColor() != green) + // iChanTextLabel->setPaletteBackgroundColor(green); + //if(iChanDetectLabel->pixmap() != greendotIcon) + if(!_midiDetect) + { + //printf("Arranger::midiTrackInfoHeartBeat setting green icon\n"); + + _midiDetect = true; + //iChanDetectLabel->setPixmap(*greendotIcon); + iChanDetectLabel->setPixmap(*redLedIcon); + } + break; + } + //} + } + // No activity detected? + //if(mch == MIDI_CHANNELS) + //if(mpt == MIDI_PORTS) + if(r == rl->end()) + { + //if(iChanTextLabel->paletteBackgroundColor() != darkGreen) + // iChanTextLabel->setPaletteBackgroundColor(darkGreen); + //if(iChanDetectLabel->pixmap() != darkgreendotIcon) + if(_midiDetect) + { + //printf("Arranger::midiTrackInfoHeartBeat setting darkgreen icon\n"); + + _midiDetect = false; + //iChanDetectLabel->setPixmap(*darkgreendotIcon); + iChanDetectLabel->setPixmap(*darkRedLedIcon); + } + } + + int nprogram = mp->hwCtrlState(outChannel, CTRL_PROGRAM); + if(nprogram == CTRL_VAL_UNKNOWN) + { + if(program != CTRL_VAL_UNKNOWN) + { + //printf("Arranger::midiTrackInfoHeartBeat setting program to unknown\n"); + + program = CTRL_VAL_UNKNOWN; + if(iHBank->value() != 0) + { + iHBank->blockSignals(true); + iHBank->setValue(0); + iHBank->blockSignals(false); + } + if(iLBank->value() != 0) + { + iLBank->blockSignals(true); + iLBank->setValue(0); + iLBank->blockSignals(false); + } + if(iProgram->value() != 0) + { + iProgram->blockSignals(true); + iProgram->setValue(0); + iProgram->blockSignals(false); + } + } + + nprogram = mp->lastValidHWCtrlState(outChannel, CTRL_PROGRAM); + if(nprogram == CTRL_VAL_UNKNOWN) + { + //const char* n = "<unknown>"; + const QString n(tr("<unknown>")); + //if(strcmp(iPatch->text().toLatin1().constData(), n) != 0) + if(iPatch->text() != n) + { + //printf("Arranger::midiTrackInfoHeartBeat setting patch <unknown>\n"); + + iPatch->setText(n); + } + } + else + { + MidiInstrument* instr = mp->instrument(); + QString name = instr->getPatchName(outChannel, nprogram, song->mtype(), track->type() == Track::DRUM); + if(name.isEmpty()) + { + const QString n("???"); + if(iPatch->text() != n) + iPatch->setText(n); + } + else + if(iPatch->text() != name) + { + //printf("Arranger::midiTrackInfoHeartBeat setting patch name\n"); + + iPatch->setText(name); + } + } + } + else + if(program != nprogram) + { + program = nprogram; + + //int hb, lb, pr; + //if (program == CTRL_VAL_UNKNOWN) { + // hb = lb = pr = 0; + // iPatch->setText("---"); + // } + //else + //{ + MidiInstrument* instr = mp->instrument(); + QString name = instr->getPatchName(outChannel, program, song->mtype(), track->type() == Track::DRUM); + if(iPatch->text() != name) + iPatch->setText(name); + + int hb = ((program >> 16) & 0xff) + 1; + if (hb == 0x100) + hb = 0; + int lb = ((program >> 8) & 0xff) + 1; + if (lb == 0x100) + lb = 0; + int pr = (program & 0xff) + 1; + if (pr == 0x100) + pr = 0; + //} + + //printf("Arranger::midiTrackInfoHeartBeat setting program\n"); + + if(iHBank->value() != hb) + { + iHBank->blockSignals(true); + iHBank->setValue(hb); + iHBank->blockSignals(false); + } + if(iLBank->value() != lb) + { + iLBank->blockSignals(true); + iLBank->setValue(lb); + iLBank->blockSignals(false); + } + if(iProgram->value() != pr) + { + iProgram->blockSignals(true); + iProgram->setValue(pr); + iProgram->blockSignals(false); + } + + } + + MidiController* mc = mp->midiController(CTRL_VOLUME); + int mn = mc->minVal(); + int v = mp->hwCtrlState(outChannel, CTRL_VOLUME); + if(v == CTRL_VAL_UNKNOWN) + //{ + //v = mc->initVal(); + //if(v == CTRL_VAL_UNKNOWN) + // v = 0; + v = mn - 1; + //} + else + // Auto bias... + v -= mc->bias(); + if(volume != v) + { + volume = v; + if(iLautst->value() != v) + { + //printf("Arranger::midiTrackInfoHeartBeat setting volume\n"); + + iLautst->blockSignals(true); + //iLautst->setRange(mn - 1, mc->maxVal()); + iLautst->setValue(v); + iLautst->blockSignals(false); + } + } + + mc = mp->midiController(CTRL_PANPOT); + mn = mc->minVal(); + v = mp->hwCtrlState(outChannel, CTRL_PANPOT); + if(v == CTRL_VAL_UNKNOWN) + //{ + //v = mc->initVal(); + //if(v == CTRL_VAL_UNKNOWN) + // v = 0; + v = mn - 1; + //} + else + // Auto bias... + v -= mc->bias(); + if(pan != v) + { + pan = v; + if(iPan->value() != v) + { + //printf("Arranger::midiTrackInfoHeartBeat setting pan\n"); + + iPan->blockSignals(true); + //iPan->setRange(mn - 1, mc->maxVal()); + iPan->setValue(v); + iPan->blockSignals(false); + } + } + + // Does it include a midi controller value adjustment? Then handle it... + //if(flags & SC_MIDI_CONTROLLER) + // seek(); + + /* + if(iTransp->value() != track->transposition) + iTransp->setValue(track->transposition); + if(iAnschl->value() != track->velocity) + iAnschl->setValue(track->velocity); + if(iVerz->value() != track->delay) + iVerz->setValue(track->delay); + if(iLen->value() != track->len) + iLen->setValue(track->len); + if(iKompr->value() != track->compression) + iKompr->setValue(track->compression); + */ + } + break; + + case Track::WAVE: + case Track::AUDIO_OUTPUT: + case Track::AUDIO_INPUT: + case Track::AUDIO_GROUP: + case Track::AUDIO_AUX: + case Track::AUDIO_SOFTSYNTH: + break; + } +} + +//--------------------------------------------------------- +// songChanged +//--------------------------------------------------------- + +void MidiTrackInfo::songChanged(int type) + { + // Is it simply a midi controller value adjustment? Forget it. + if(type != SC_MIDI_CONTROLLER) + { +/* + unsigned endTick = song->len(); + int offset = AL::sigmap.ticksMeasure(endTick); + hscroll->setRange(-offset, endTick + offset); //DEBUG + canvas->setOrigin(-offset, 0); + time->setOrigin(-offset, 0); + + int bar, beat; + unsigned tick; + AL::sigmap.tickValues(endTick, &bar, &beat, &tick); + if (tick || beat) + ++bar; + lenEntry->blockSignals(true); + lenEntry->setValue(bar); + lenEntry->blockSignals(false); + + trackSelectionChanged(); + canvas->partsChanged(); + typeBox->setCurrentIndex(int(song->mtype())); + if (type & SC_SIG) + time->redraw(); + if (type & SC_TEMPO) + setGlobalTempo(tempomap.globalTempo()); + + if(type & SC_TRACK_REMOVED) + { + AudioStrip* w = (AudioStrip*)(trackInfo->getWidget(2)); + if(w) + { + Track* t = w->getTrack(); + if(t) + { + TrackList* tl = song->tracks(); + iTrack it = tl->find(t); + if(it == tl->end()) + { + delete w; + trackInfo->addWidget(0, 2); + selected = 0; + } + } + } + } +*/ + + } + + updateTrackInfo(type); + } + +//--------------------------------------------------------- +// setLabelText +//--------------------------------------------------------- + +void MidiTrackInfo::setLabelText() +{ + MidiTrack* track = (MidiTrack*)selected; + if(track) + trackNameLabel->setText(track->name()); + else + trackNameLabel->setText(QString()); +} + +//--------------------------------------------------------- +// setLabelFont +//--------------------------------------------------------- + +void MidiTrackInfo::setLabelFont() +{ + MidiTrack* track = (MidiTrack*)selected; + if(!track) + return; + + // Use the new font #6 I created just for these labels (so far). + // Set the label's font. + trackNameLabel->setFont(config.fonts[6]); + // Dealing with a horizontally constrained label. Ignore vertical. Use a minimum readable point size. + autoAdjustFontSize(trackNameLabel, trackNameLabel->text(), false, true, config.fonts[6].pointSize(), 5); +} + +//--------------------------------------------------------- +// iOutputChannelChanged +//--------------------------------------------------------- + +void MidiTrackInfo::iOutputChannelChanged(int channel) + { + --channel; + MidiTrack* track = (MidiTrack*)selected; + if (channel != track->outChannel()) { + // Changed by T356. + //track->setOutChannel(channel); + audio->msgIdle(true); + //audio->msgSetTrackOutChannel(track, channel); + track->setOutChanAndUpdate(channel); + audio->msgIdle(false); + + // may result in adding/removing mixer strip: + //song->update(-1); + song->update(SC_MIDI_CHANNEL); + } + } + +//--------------------------------------------------------- +// iOutputPortChanged +//--------------------------------------------------------- + +void MidiTrackInfo::iOutputPortChanged(int index) + { + MidiTrack* track = (MidiTrack*)selected; + if (index == track->outPort()) + return; + // Changed by T356. + //track->setOutPort(index); + audio->msgIdle(true); + //audio->msgSetTrackOutPort(track, index); + track->setOutPortAndUpdate(index); + audio->msgIdle(false); + + ///list->redraw(); + emit outputPortChanged(index); + } + +//--------------------------------------------------------- +// routingPopupMenuActivated +//--------------------------------------------------------- + +//void MidiTrackInfo::routingPopupMenuActivated(int n) +void MidiTrackInfo::routingPopupMenuActivated(QAction* act) +{ + ///if(!midiTrackInfo || gRoutingPopupMenuMaster != midiTrackInfo || !selected || !selected->isMidiTrack()) + if((gRoutingPopupMenuMaster != this) || !selected || !selected->isMidiTrack()) + return; + muse->routingPopupMenuActivated(selected, act->data().toInt()); +} + +#if 0 +//--------------------------------------------------------- +// routingPopupViewActivated +//--------------------------------------------------------- + +void MidiTrackInfo::routingPopupViewActivated(const QModelIndex& mdi) +{ + ///if(!midiTrackInfo || gRoutingPopupMenuMaster != midiTrackInfo || !selected || !selected->isMidiTrack()) + if(gRoutingPopupMenuMaster != this || !selected || !selected->isMidiTrack()) + return; + muse->routingPopupMenuActivated(selected, mdi.data().toInt()); +} +#endif + +//--------------------------------------------------------- +// inRoutesPressed +//--------------------------------------------------------- + +void MidiTrackInfo::inRoutesPressed() +{ + if(!selected) + return; + if(!selected->isMidiTrack()) + return; + + PopupMenu* pup = muse->prepareRoutingPopupMenu(selected, false); + //PopupView* pup = muse->prepareRoutingPopupView(selected, false); + + if(!pup) { + int ret = QMessageBox::warning(this, tr("No inputs"), + tr("There are no midi inputs.\n" + "Do you want to open the midi configuration dialog?"), + QMessageBox::Ok | QMessageBox::Cancel, + QMessageBox::Ok); + if (ret == QMessageBox::Ok) { + printf("open config midi ports\n"); + muse->configMidiPorts(); + } + return; + } + + ///gRoutingPopupMenuMaster = midiTrackInfo; + gRoutingPopupMenuMaster = this; + connect(pup, SIGNAL(triggered(QAction*)), SLOT(routingPopupMenuActivated(QAction*))); + //connect(pup, SIGNAL(activated(const QModelIndex&)), SLOT(routingPopupViewActivated(const QModelIndex&))); + connect(pup, SIGNAL(aboutToHide()), muse, SLOT(routingPopupMenuAboutToHide())); + //connect(pup, SIGNAL(aboutToHide()), muse, SLOT(routingPopupViewAboutToHide())); + pup->popup(QCursor::pos()); + //pup->setVisible(true); + iRButton->setDown(false); + return; +} + +//--------------------------------------------------------- +// outRoutesPressed +//--------------------------------------------------------- + +void MidiTrackInfo::outRoutesPressed() +{ + if(!selected) + return; + if(!selected->isMidiTrack()) + return; + + PopupMenu* pup = muse->prepareRoutingPopupMenu(selected, true); + if(!pup) + return; + + ///gRoutingPopupMenuMaster = midiTrackInfo; + gRoutingPopupMenuMaster = this; + connect(pup, SIGNAL(triggered(QAction*)), SLOT(routingPopupMenuActivated(QAction*))); + connect(pup, SIGNAL(aboutToHide()), muse, SLOT(routingPopupMenuAboutToHide())); + pup->popup(QCursor::pos()); + oRButton->setDown(false); + return; +} + +//--------------------------------------------------------- +// iProgHBankChanged +//--------------------------------------------------------- + +void MidiTrackInfo::iProgHBankChanged() + { + MidiTrack* track = (MidiTrack*)selected; + int channel = track->outChannel(); + int port = track->outPort(); + int hbank = iHBank->value(); + int lbank = iLBank->value(); + int prog = iProgram->value(); + + if (hbank > 0 && hbank < 129) + hbank -= 1; + else + hbank = 0xff; + if (lbank > 0 && lbank < 129) + lbank -= 1; + else + lbank = 0xff; + if (prog > 0 && prog < 129) + prog -= 1; + else + prog = 0xff; + + MidiPort* mp = &midiPorts[port]; + if(prog == 0xff && hbank == 0xff && lbank == 0xff) + { + program = CTRL_VAL_UNKNOWN; + if(mp->hwCtrlState(channel, CTRL_PROGRAM) != CTRL_VAL_UNKNOWN) + audio->msgSetHwCtrlState(mp, channel, CTRL_PROGRAM, CTRL_VAL_UNKNOWN); + return; + } + + int np = mp->hwCtrlState(channel, CTRL_PROGRAM); + if(np == CTRL_VAL_UNKNOWN) + { + np = mp->lastValidHWCtrlState(channel, CTRL_PROGRAM); + if(np != CTRL_VAL_UNKNOWN) + { + lbank = (np & 0xff00) >> 8; + prog = np & 0xff; + if(prog == 0xff) + prog = 0; + int ilbnk = lbank; + int iprog = prog; + if(ilbnk == 0xff) + ilbnk = -1; + ++ilbnk; + ++iprog; + iLBank->blockSignals(true); + iProgram->blockSignals(true); + iLBank->setValue(ilbnk); + iProgram->setValue(iprog); + iLBank->blockSignals(false); + iProgram->blockSignals(false); + } + } + + if(prog == 0xff && (hbank != 0xff || lbank != 0xff)) + { + prog = 0; + iProgram->blockSignals(true); + iProgram->setValue(1); + iProgram->blockSignals(false); + } + program = (hbank << 16) + (lbank << 8) + prog; + MidiPlayEvent ev(0, port, channel, ME_CONTROLLER, CTRL_PROGRAM, program); + audio->msgPlayMidiEvent(&ev); + + MidiInstrument* instr = mp->instrument(); + iPatch->setText(instr->getPatchName(channel, program, song->mtype(), track->type() == Track::DRUM)); +// updateTrackInfo(); + } + +//--------------------------------------------------------- +// iProgLBankChanged +//--------------------------------------------------------- + +void MidiTrackInfo::iProgLBankChanged() + { + MidiTrack* track = (MidiTrack*)selected; + int channel = track->outChannel(); + int port = track->outPort(); + int hbank = iHBank->value(); + int lbank = iLBank->value(); + int prog = iProgram->value(); + + if (hbank > 0 && hbank < 129) + hbank -= 1; + else + hbank = 0xff; + if (lbank > 0 && lbank < 129) + lbank -= 1; + else + lbank = 0xff; + if (prog > 0 && prog < 129) + prog -= 1; + else + prog = 0xff; + + MidiPort* mp = &midiPorts[port]; + if(prog == 0xff && hbank == 0xff && lbank == 0xff) + { + program = CTRL_VAL_UNKNOWN; + if(mp->hwCtrlState(channel, CTRL_PROGRAM) != CTRL_VAL_UNKNOWN) + audio->msgSetHwCtrlState(mp, channel, CTRL_PROGRAM, CTRL_VAL_UNKNOWN); + return; + } + + int np = mp->hwCtrlState(channel, CTRL_PROGRAM); + if(np == CTRL_VAL_UNKNOWN) + { + np = mp->lastValidHWCtrlState(channel, CTRL_PROGRAM); + if(np != CTRL_VAL_UNKNOWN) + { + hbank = (np & 0xff0000) >> 16; + prog = np & 0xff; + if(prog == 0xff) + prog = 0; + int ihbnk = hbank; + int iprog = prog; + if(ihbnk == 0xff) + ihbnk = -1; + ++ihbnk; + ++iprog; + iHBank->blockSignals(true); + iProgram->blockSignals(true); + iHBank->setValue(ihbnk); + iProgram->setValue(iprog); + iHBank->blockSignals(false); + iProgram->blockSignals(false); + } + } + + if(prog == 0xff && (hbank != 0xff || lbank != 0xff)) + { + prog = 0; + iProgram->blockSignals(true); + iProgram->setValue(1); + iProgram->blockSignals(false); + } + program = (hbank << 16) + (lbank << 8) + prog; + MidiPlayEvent ev(0, port, channel, ME_CONTROLLER, CTRL_PROGRAM, program); + audio->msgPlayMidiEvent(&ev); + + MidiInstrument* instr = mp->instrument(); + iPatch->setText(instr->getPatchName(channel, program, song->mtype(), track->type() == Track::DRUM)); +// updateTrackInfo(); + } + +//--------------------------------------------------------- +// iProgramChanged +//--------------------------------------------------------- + +void MidiTrackInfo::iProgramChanged() + { + MidiTrack* track = (MidiTrack*)selected; + int channel = track->outChannel(); + int port = track->outPort(); + int hbank = iHBank->value(); + int lbank = iLBank->value(); + int prog = iProgram->value(); + + if (hbank > 0 && hbank < 129) + hbank -= 1; + else + hbank = 0xff; + if (lbank > 0 && lbank < 129) + lbank -= 1; + else + lbank = 0xff; + if (prog > 0 && prog < 129) + prog -= 1; + else + prog = 0xff; + + MidiPort *mp = &midiPorts[port]; + if(prog == 0xff) + { + program = CTRL_VAL_UNKNOWN; + iHBank->blockSignals(true); + iLBank->blockSignals(true); + iHBank->setValue(0); + iLBank->setValue(0); + iHBank->blockSignals(false); + iLBank->blockSignals(false); + + if(mp->hwCtrlState(channel, CTRL_PROGRAM) != CTRL_VAL_UNKNOWN) + audio->msgSetHwCtrlState(mp, channel, CTRL_PROGRAM, CTRL_VAL_UNKNOWN); + return; + } + else + { + int np = mp->hwCtrlState(channel, CTRL_PROGRAM); + if(np == CTRL_VAL_UNKNOWN) + { + np = mp->lastValidHWCtrlState(channel, CTRL_PROGRAM); + if(np != CTRL_VAL_UNKNOWN) + { + hbank = (np & 0xff0000) >> 16; + lbank = (np & 0xff00) >> 8; + int ihbnk = hbank; + int ilbnk = lbank; + if(ihbnk == 0xff) + ihbnk = -1; + if(ilbnk == 0xff) + ilbnk = -1; + ++ihbnk; + ++ilbnk; + iHBank->blockSignals(true); + iLBank->blockSignals(true); + iHBank->setValue(ihbnk); + iLBank->setValue(ilbnk); + iHBank->blockSignals(false); + iLBank->blockSignals(false); + } + } + program = (hbank << 16) + (lbank << 8) + prog; + MidiPlayEvent ev(0, port, channel, ME_CONTROLLER, CTRL_PROGRAM, program); + audio->msgPlayMidiEvent(&ev); + + MidiInstrument* instr = mp->instrument(); + iPatch->setText(instr->getPatchName(channel, program, song->mtype(), track->type() == Track::DRUM)); + } + +// updateTrackInfo(); + } + +//--------------------------------------------------------- +// iLautstChanged +//--------------------------------------------------------- + +void MidiTrackInfo::iLautstChanged(int val) + { + MidiTrack* track = (MidiTrack*)selected; + int outPort = track->outPort(); + int chan = track->outChannel(); + MidiPort* mp = &midiPorts[outPort]; + MidiController* mctl = mp->midiController(CTRL_VOLUME); + if((val < mctl->minVal()) || (val > mctl->maxVal())) + { + if(mp->hwCtrlState(chan, CTRL_VOLUME) != CTRL_VAL_UNKNOWN) + audio->msgSetHwCtrlState(mp, chan, CTRL_VOLUME, CTRL_VAL_UNKNOWN); + } + else + { + val += mctl->bias(); + + MidiPlayEvent ev(0, outPort, chan, + ME_CONTROLLER, CTRL_VOLUME, val); + audio->msgPlayMidiEvent(&ev); + } + song->update(SC_MIDI_CONTROLLER); + } + +//--------------------------------------------------------- +// iTranspChanged +//--------------------------------------------------------- + +void MidiTrackInfo::iTranspChanged(int val) + { + MidiTrack* track = (MidiTrack*)selected; + track->transposition = val; + } + +//--------------------------------------------------------- +// iAnschlChanged +//--------------------------------------------------------- + +void MidiTrackInfo::iAnschlChanged(int val) + { + MidiTrack* track = (MidiTrack*)selected; + track->velocity = val; + } + +//--------------------------------------------------------- +// iVerzChanged +//--------------------------------------------------------- + +void MidiTrackInfo::iVerzChanged(int val) + { + MidiTrack* track = (MidiTrack*)selected; + track->delay = val; + } + +//--------------------------------------------------------- +// iLenChanged +//--------------------------------------------------------- + +void MidiTrackInfo::iLenChanged(int val) + { + MidiTrack* track = (MidiTrack*)selected; + track->len = val; + } + +//--------------------------------------------------------- +// iKomprChanged +//--------------------------------------------------------- + +void MidiTrackInfo::iKomprChanged(int val) + { + MidiTrack* track = (MidiTrack*)selected; + track->compression = val; + } + +//--------------------------------------------------------- +// iPanChanged +//--------------------------------------------------------- + +void MidiTrackInfo::iPanChanged(int val) + { + MidiTrack* track = (MidiTrack*)selected; + int port = track->outPort(); + int chan = track->outChannel(); + MidiPort* mp = &midiPorts[port]; + MidiController* mctl = mp->midiController(CTRL_PANPOT); + if((val < mctl->minVal()) || (val > mctl->maxVal())) + { + if(mp->hwCtrlState(chan, CTRL_PANPOT) != CTRL_VAL_UNKNOWN) + audio->msgSetHwCtrlState(mp, chan, CTRL_PANPOT, CTRL_VAL_UNKNOWN); + } + else + { + val += mctl->bias(); + + // Realtime Change: + MidiPlayEvent ev(0, port, chan, + ME_CONTROLLER, CTRL_PANPOT, val); + audio->msgPlayMidiEvent(&ev); + } + song->update(SC_MIDI_CONTROLLER); + } + +//--------------------------------------------------------- +// instrPopup +//--------------------------------------------------------- + +void MidiTrackInfo::instrPopup() + { + MidiTrack* track = (MidiTrack*)selected; + int channel = track->outChannel(); + int port = track->outPort(); + MidiInstrument* instr = midiPorts[port].instrument(); + QMenu* pup = new QMenu; + ///instr->populatePatchPopup(pop, channel, song->mtype(), track->type() == Track::DRUM); + instr->populatePatchPopup(pup, channel, song->mtype(), track->type() == Track::DRUM); + + ///if(pop->actions().count() == 0) + /// return; + if(pup->actions().count() == 0) + { + delete pup; + return; + } + + ///QAction *act = pop->exec(iPatch->mapToGlobal(QPoint(10,5))); + QAction *act = pup->exec(iPatch->mapToGlobal(QPoint(10,5))); + if (act) { + int rv = act->data().toInt(); + MidiPlayEvent ev(0, port, channel, ME_CONTROLLER, CTRL_PROGRAM, rv); + audio->msgPlayMidiEvent(&ev); + updateTrackInfo(-1); + } + + delete pup; + } + +//--------------------------------------------------------- +// recEchoToggled +//--------------------------------------------------------- + +void MidiTrackInfo::recEchoToggled(bool v) +{ + MidiTrack* track = (MidiTrack*)selected; + track->setRecEcho(v); + + //song->update(SC_???); +} + +//--------------------------------------------------------- +// iProgramDoubleClicked +//--------------------------------------------------------- + +void MidiTrackInfo::iProgramDoubleClicked() +{ + MidiTrack* track = (MidiTrack*)selected; + int port = track->outPort(); + int chan = track->outChannel(); + MidiPort* mp = &midiPorts[port]; + MidiController* mctl = mp->midiController(CTRL_PROGRAM); + + if(!track || !mctl) + return; + + int lastv = mp->lastValidHWCtrlState(chan, CTRL_PROGRAM); + int curv = mp->hwCtrlState(chan, CTRL_PROGRAM); + + if(curv == CTRL_VAL_UNKNOWN) + { + // If no value has ever been set yet, use the current knob value + // (or the controller's initial value?) to 'turn on' the controller. + if(lastv == CTRL_VAL_UNKNOWN) + { + int kiv = mctl->initVal(); + //int kiv = lrint(_knob->value()); + if(kiv == CTRL_VAL_UNKNOWN) + kiv = 0; + //else + //{ + //if(kiv < mctrl->minVal()) + // kiv = mctrl->minVal(); + //if(kiv > mctrl->maxVal()) + // kiv = mctrl->maxVal(); + //kiv += mctrl->bias(); + //} + + //MidiPlayEvent ev(0, port, chan, ME_CONTROLLER, num, kiv); + MidiPlayEvent ev(0, port, chan, ME_CONTROLLER, CTRL_PROGRAM, kiv); + audio->msgPlayMidiEvent(&ev); + } + else + { + MidiPlayEvent ev(0, port, chan, ME_CONTROLLER, CTRL_PROGRAM, lastv); + audio->msgPlayMidiEvent(&ev); + } + } + else + { + if(mp->hwCtrlState(chan, CTRL_PROGRAM) != CTRL_VAL_UNKNOWN) + audio->msgSetHwCtrlState(mp, chan, CTRL_PROGRAM, CTRL_VAL_UNKNOWN); + } + + song->update(SC_MIDI_CONTROLLER); +} + +//--------------------------------------------------------- +// iLautstDoubleClicked +//--------------------------------------------------------- + +void MidiTrackInfo::iLautstDoubleClicked() +{ + MidiTrack* track = (MidiTrack*)selected; + int port = track->outPort(); + int chan = track->outChannel(); + MidiPort* mp = &midiPorts[port]; + MidiController* mctl = mp->midiController(CTRL_VOLUME); + + if(!track || !mctl) + return; + + int lastv = mp->lastValidHWCtrlState(chan, CTRL_VOLUME); + int curv = mp->hwCtrlState(chan, CTRL_VOLUME); + + if(curv == CTRL_VAL_UNKNOWN) + { + // If no value has ever been set yet, use the current knob value + // (or the controller's initial value?) to 'turn on' the controller. + if(lastv == CTRL_VAL_UNKNOWN) + { + int kiv = mctl->initVal(); + //int kiv = lrint(_knob->value()); + if(kiv == CTRL_VAL_UNKNOWN) + // Set volume to 78% of range, so that if range is 0 - 127, then value is 100. + kiv = lround(double(mctl->maxVal() - mctl->minVal()) * 0.7874); + else + { + if(kiv < mctl->minVal()) + kiv = mctl->minVal(); + if(kiv > mctl->maxVal()) + kiv = mctl->maxVal(); + kiv += mctl->bias(); + } + + MidiPlayEvent ev(0, port, chan, ME_CONTROLLER, CTRL_VOLUME, kiv); + audio->msgPlayMidiEvent(&ev); + } + else + { + MidiPlayEvent ev(0, port, chan, ME_CONTROLLER, CTRL_VOLUME, lastv); + audio->msgPlayMidiEvent(&ev); + } + } + else + { + if(mp->hwCtrlState(chan, CTRL_VOLUME) != CTRL_VAL_UNKNOWN) + audio->msgSetHwCtrlState(mp, chan, CTRL_VOLUME, CTRL_VAL_UNKNOWN); + } + + song->update(SC_MIDI_CONTROLLER); +} + +//--------------------------------------------------------- +// iPanDoubleClicked +//--------------------------------------------------------- + +void MidiTrackInfo::iPanDoubleClicked() +{ + MidiTrack* track = (MidiTrack*)selected; + int port = track->outPort(); + int chan = track->outChannel(); + MidiPort* mp = &midiPorts[port]; + MidiController* mctl = mp->midiController(CTRL_PANPOT); + + if(!track || !mctl) + return; + + int lastv = mp->lastValidHWCtrlState(chan, CTRL_PANPOT); + int curv = mp->hwCtrlState(chan, CTRL_PANPOT); + + if(curv == CTRL_VAL_UNKNOWN) + { + // If no value has ever been set yet, use the current knob value + // (or the controller's initial value?) to 'turn on' the controller. + if(lastv == CTRL_VAL_UNKNOWN) + { + int kiv = mctl->initVal(); + //int kiv = lrint(_knob->value()); + if(kiv == CTRL_VAL_UNKNOWN) + // Set volume to 50% of range, so that if range is 0 - 127, then value is 64. + kiv = lround(double(mctl->maxVal() - mctl->minVal()) * 0.5); + else + { + if(kiv < mctl->minVal()) + kiv = mctl->minVal(); + if(kiv > mctl->maxVal()) + kiv = mctl->maxVal(); + kiv += mctl->bias(); + } + + MidiPlayEvent ev(0, port, chan, ME_CONTROLLER, CTRL_PANPOT, kiv); + audio->msgPlayMidiEvent(&ev); + } + else + { + MidiPlayEvent ev(0, port, chan, ME_CONTROLLER, CTRL_PANPOT, lastv); + audio->msgPlayMidiEvent(&ev); + } + } + else + { + if(mp->hwCtrlState(chan, CTRL_PANPOT) != CTRL_VAL_UNKNOWN) + audio->msgSetHwCtrlState(mp, chan, CTRL_PANPOT, CTRL_VAL_UNKNOWN); + } + + song->update(SC_MIDI_CONTROLLER); +} + + +//--------------------------------------------------------- +// updateTrackInfo +//--------------------------------------------------------- + +void MidiTrackInfo::updateTrackInfo(int flags) +{ + if(!selected) + return; + + MidiTrack* track = (MidiTrack*)selected; + + // Is it simply a midi controller value adjustment? Forget it. + if(flags == SC_MIDI_CONTROLLER) + return; + + // p3.3.47 Update the routing popup menu if anything relevant changes. + //if(gRoutingPopupMenuMaster == midiTrackInfo && selected && (flags & (SC_ROUTE | SC_CHANNELS | SC_CONFIG))) + if(flags & (SC_ROUTE | SC_CHANNELS | SC_CONFIG)) // p3.3.50 + // Use this handy shared routine. + //muse->updateRouteMenus(selected); + ///muse->updateRouteMenus(selected, midiTrackInfo); // p3.3.50 + muse->updateRouteMenus(selected, this); + + // Added by Tim. p3.3.9 + setLabelText(); + setLabelFont(); + + //{ + int outChannel = track->outChannel(); + ///int inChannel = track->inChannelMask(); + int outPort = track->outPort(); + //int inPort = track->inPortMask(); + ///unsigned int inPort = track->inPortMask(); + + //iInput->clear(); + iOutput->clear(); + + for (int i = 0; i < MIDI_PORTS; ++i) { + QString name; + name.sprintf("%d:%s", i+1, midiPorts[i].portname().toLatin1().constData()); + iOutput->insertItem(i, name); + if (i == outPort) + iOutput->setCurrentIndex(i); + } + //iInput->setText(bitmap2String(inPort)); + ///iInput->setText(u32bitmap2String(inPort)); + + //iInputChannel->setText(bitmap2String(inChannel)); + + // Removed by Tim. p3.3.9 + //if (iName->text() != selected->name()) { + // iName->setText(selected->name()); + // iName->home(false); + // } + + iOutputChannel->setValue(outChannel+1); + ///iInputChannel->setText(bitmap2String(inChannel)); + + // Set record echo. + if(recEchoButton->isChecked() != track->recEcho()) + { + recEchoButton->blockSignals(true); + recEchoButton->setChecked(track->recEcho()); + recEchoButton->blockSignals(false); + } + + MidiPort* mp = &midiPorts[outPort]; + int nprogram = mp->hwCtrlState(outChannel, CTRL_PROGRAM); + if(nprogram == CTRL_VAL_UNKNOWN) + { + iHBank->blockSignals(true); + iLBank->blockSignals(true); + iProgram->blockSignals(true); + iHBank->setValue(0); + iLBank->setValue(0); + iProgram->setValue(0); + iHBank->blockSignals(false); + iLBank->blockSignals(false); + iProgram->blockSignals(false); + + program = CTRL_VAL_UNKNOWN; + nprogram = mp->lastValidHWCtrlState(outChannel, CTRL_PROGRAM); + if(nprogram == CTRL_VAL_UNKNOWN) + //iPatch->setText(QString("<unknown>")); + iPatch->setText(tr("<unknown>")); + else + { + MidiInstrument* instr = mp->instrument(); + iPatch->setText(instr->getPatchName(outChannel, nprogram, song->mtype(), track->type() == Track::DRUM)); + } + } + else + //if (program != nprogram) + { + program = nprogram; + + //int hb, lb, pr; + //if (program == CTRL_VAL_UNKNOWN) { + // hb = lb = pr = 0; + // iPatch->setText("---"); + // } + //else + //{ + MidiInstrument* instr = mp->instrument(); + iPatch->setText(instr->getPatchName(outChannel, program, song->mtype(), track->type() == Track::DRUM)); + + int hb = ((program >> 16) & 0xff) + 1; + if (hb == 0x100) + hb = 0; + int lb = ((program >> 8) & 0xff) + 1; + if (lb == 0x100) + lb = 0; + int pr = (program & 0xff) + 1; + if (pr == 0x100) + pr = 0; + //} + iHBank->blockSignals(true); + iLBank->blockSignals(true); + iProgram->blockSignals(true); + + iHBank->setValue(hb); + iLBank->setValue(lb); + iProgram->setValue(pr); + + iHBank->blockSignals(false); + iLBank->blockSignals(false); + iProgram->blockSignals(false); + } + + MidiController* mc = mp->midiController(CTRL_VOLUME); + int mn = mc->minVal(); + int v = mp->hwCtrlState(outChannel, CTRL_VOLUME); + volume = v; + if(v == CTRL_VAL_UNKNOWN) + //{ + //v = mc->initVal(); + //if(v == CTRL_VAL_UNKNOWN) + // v = 0; + v = mn - 1; + //} + else + // Auto bias... + v -= mc->bias(); + iLautst->blockSignals(true); + iLautst->setRange(mn - 1, mc->maxVal()); + iLautst->setValue(v); + iLautst->blockSignals(false); + + mc = mp->midiController(CTRL_PANPOT); + mn = mc->minVal(); + v = mp->hwCtrlState(outChannel, CTRL_PANPOT); + pan = v; + if(v == CTRL_VAL_UNKNOWN) + //{ + //v = mc->initVal(); + //if(v == CTRL_VAL_UNKNOWN) + // v = 0; + v = mn - 1; + //} + else + // Auto bias... + v -= mc->bias(); + iPan->blockSignals(true); + iPan->setRange(mn - 1, mc->maxVal()); + iPan->setValue(v); + iPan->blockSignals(false); + //} + + + // Does it include a midi controller value adjustment? Then handle it... + //if(flags & SC_MIDI_CONTROLLER) + // seek(); + + // Is it simply a midi controller value adjustment? Forget it. + //if(flags != SC_MIDI_CONTROLLER) + //{ + iTransp->setValue(track->transposition); + iAnschl->setValue(track->velocity); + iVerz->setValue(track->delay); + iLen->setValue(track->len); + iKompr->setValue(track->compression); + //} +} + +//--------------------------------------------------------- +// progRecClicked +//--------------------------------------------------------- + +void MidiTrackInfo::progRecClicked() + { + MidiTrack* track = (MidiTrack*)selected; + int portno = track->outPort(); + int channel = track->outChannel(); + MidiPort* port = &midiPorts[portno]; + int program = port->hwCtrlState(channel, CTRL_PROGRAM); + if(program == CTRL_VAL_UNKNOWN || program == 0xffffff) + return; + + unsigned tick = song->cpos(); + Event a(Controller); + a.setTick(tick); + a.setA(CTRL_PROGRAM); + a.setB(program); + + song->recordEvent(track, a); + } + +//--------------------------------------------------------- +// volRecClicked +//--------------------------------------------------------- + +void MidiTrackInfo::volRecClicked() + { + MidiTrack* track = (MidiTrack*)selected; + int portno = track->outPort(); + int channel = track->outChannel(); + MidiPort* port = &midiPorts[portno]; + int volume = port->hwCtrlState(channel, CTRL_VOLUME); + if(volume == CTRL_VAL_UNKNOWN) + return; + + unsigned tick = song->cpos(); + Event a(Controller); + a.setTick(tick); + a.setA(CTRL_VOLUME); + a.setB(volume); + + song->recordEvent(track, a); + } + +//--------------------------------------------------------- +// panRecClicked +//--------------------------------------------------------- + +void MidiTrackInfo::panRecClicked() + { + MidiTrack* track = (MidiTrack*)selected; + int portno = track->outPort(); + int channel = track->outChannel(); + MidiPort* port = &midiPorts[portno]; + int pan = port->hwCtrlState(channel, CTRL_PANPOT); + if(pan == CTRL_VAL_UNKNOWN) + return; + + unsigned tick = song->cpos(); + Event a(Controller); + a.setTick(tick); + a.setA(CTRL_PANPOT); + a.setB(pan); + + song->recordEvent(track, a); + } + +//--------------------------------------------------------- +// recordClicked +//--------------------------------------------------------- + +void MidiTrackInfo::recordClicked() + { + MidiTrack* track = (MidiTrack*)selected; + int portno = track->outPort(); + int channel = track->outChannel(); + MidiPort* port = &midiPorts[portno]; + unsigned tick = song->cpos(); + + int program = port->hwCtrlState(channel, CTRL_PROGRAM); + if(program != CTRL_VAL_UNKNOWN && program != 0xffffff) + { + Event a(Controller); + a.setTick(tick); + a.setA(CTRL_PROGRAM); + a.setB(program); + song->recordEvent(track, a); + } + int volume = port->hwCtrlState(channel, CTRL_VOLUME); + if(volume != CTRL_VAL_UNKNOWN) + { + Event a(Controller); + a.setTick(tick); + a.setA(CTRL_VOLUME); + a.setB(volume); + song->recordEvent(track, a); + } + int pan = port->hwCtrlState(channel, CTRL_PANPOT); + if(pan != CTRL_VAL_UNKNOWN) + { + Event a(Controller); + a.setTick(tick); + a.setA(CTRL_PANPOT); + a.setB(pan); + song->recordEvent(track, a); + } + } + |