--- qsampler/trunk/src/qsamplerChannelFxForm.cpp 2008/02/04 23:24:19 1667 +++ qsampler/trunk/src/qsamplerChannelFxForm.cpp 2008/02/05 15:42:33 1668 @@ -23,22 +23,113 @@ #include "qsamplerChannelFxForm.h" #include "qsamplerFxSendsModel.h" +// let's not reinvent the wheel for audio routing +#include "qsamplerChannel.h" + +#include + #include +#include +#include +#include + +namespace { // private namespace + +static const char* _midiControllerName(int iMidiCtrl) { + switch (iMidiCtrl) { + case 0: return "Bank select MSB"; + case 1: return "Modulation MSB"; + case 2: return "Breath Controller"; + case 4: return "Foot Controller MSB"; + case 5: return "Portamento Time MSB"; + case 6: return "(N)RPN Data Byte"; + case 7: return "Main Volume"; + case 8: return "Balance"; + case 10: return "Panorama"; + case 11: return "Expression"; + case 12: return "Effect Control 1"; + case 13: return "Effect Control 2"; + case 16: return "General Purpose Controller 1"; + case 17: return "General Purpose Controller 2"; + case 18: return "General Purpose Controller 3"; + case 19: return "General Purpose Controller 4"; + case 32: return "Bank select LSB"; + case 63: return "LSB for Controllers 0?31"; + case 64: return "Hold 1"; + case 65: return "Portamento"; + case 66: return "Sostenuto"; + case 67: return "Soft Pedal"; + case 68: return "Legato Footswitch"; + case 69: return "Hold 2"; + case 70: return "Sound Controller 1 (Sound Variation)"; + case 71: return "Sound Controller 2 (Harmonic Content)"; + case 72: return "Sound Controller 3 (Release Time)"; + case 73: return "Sound Controller 4 (Attack Time)"; + case 74: return "Sound Controller 5 (Brightness)"; + case 75: return "Sound Controller 6"; + case 76: return "Sound Controller 7"; + case 77: return "Sound Controller 8"; + case 78: return "Sound Controller 9"; + case 79: return "Sound Controller 10"; + case 80: return "General Purpose 5"; + case 81: return "General Purpose 6"; + case 82: return "General Purpose 7"; + case 83: return "General Purpose 8"; + case 84: return "Portamento Control"; + case 91: return "Effects 1 Depth"; + case 92: return "Effects 2 Depth"; + case 93: return "Effects 3 Depth"; + case 94: return "Effects 4 Depth"; + case 95: return "Effects 5 Depth"; + case 96: return "Data Increment (N)RPN"; + case 97: return "Data Decrement (N)RPN"; + case 98: return "NRPN LSB"; + case 99: return "NRPN MSB"; + case 100: return "RPN LSB"; + case 101: return "RPN MSB"; + case 120: return "All Sounds Off"; + case 121: return "Controller Reset"; + case 122: return "Local Control on/off"; + case 123: return "All Notes Off"; + case 124: return "Omni Off"; + case 125: return "Omni On"; + case 126: return "Mono On / Poly Off"; + case 127: return "Poly On / Mono Off"; + default: return ""; + } +} + +} // private namespace + namespace QSampler { ChannelFxForm::ChannelFxForm ( - int SamplerChannelID, QWidget* pParent, Qt::WindowFlags wflags ) + Channel* pSamplerChannel, QWidget* pParent, Qt::WindowFlags wflags ) : QDialog(pParent, wflags) { m_ui.setupUi(this); - m_SamplerChannelID = SamplerChannelID; + m_pSamplerChannel = pSamplerChannel; + + m_pAudioDevice = NULL; FxSendsModel* pModel = - new FxSendsModel(SamplerChannelID, m_ui.SendsListView); + new FxSendsModel(m_pSamplerChannel->channelID(), m_ui.SendsListView); m_ui.SendsListView->setModel(pModel); + const int iRowHeight = m_ui.audioRoutingTable->fontMetrics().height() + 4; + m_ui.audioRoutingTable->verticalHeader()->setDefaultSectionSize(iRowHeight); + m_ui.audioRoutingTable->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft); + ChannelRoutingModel* pRoutingModel = + new ChannelRoutingModel(m_ui.audioRoutingTable); + m_ui.audioRoutingTable->setModel(pRoutingModel); + ChannelRoutingDelegate* pRoutingDelegate = + new ChannelRoutingDelegate(m_ui.audioRoutingTable); + m_ui.audioRoutingTable->setItemDelegate(pRoutingDelegate); + m_ui.audioRoutingTable->horizontalHeader()->setResizeMode(0, QHeaderView::Stretch); +// m_ui.audioRoutingTable->verticalHeader()->hide(); + QAbstractButton* pApplyButton = m_ui.buttonBox->button(QDialogButtonBox::Apply); pApplyButton->setEnabled(false); @@ -55,9 +146,19 @@ QAbstractButton* pResetButton = m_ui.buttonBox->button(QDialogButtonBox::Reset); pResetButton->setEnabled(false); + pResetButton->setToolTip("Revert all changes."); m_ui.destroyPushButton->setEnabled(false); + m_ui.mainParametersGroupBox->setEnabled(false); + m_ui.audioRoutingGroupBox->setEnabled(false); + + for (int i = 0; i < 128; ++i) { + m_ui.depthCtrlComboBox->addItem( + QString("[") + QString::number(i) + "] " + _midiControllerName(i) + ); + } + connect( m_ui.buttonBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT(onButtonClicked(QAbstractButton*)) @@ -82,9 +183,30 @@ m_ui.SendsListView, SIGNAL(clicked(const QModelIndex&)), this, SLOT(onFxSendSelection(const QModelIndex&)) ); + connect( + m_ui.depthCtrlComboBox, SIGNAL(activated(int)), + this, SLOT(onDepthCtrlChanged(int)) + ); + connect( + m_ui.depthSpinBox, SIGNAL(valueChanged(int)), + this, SLOT(onCurrentSendDepthChanged(int)) + ); + connect( + pRoutingModel, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(updateTableCellRenderers(const QModelIndex&, const QModelIndex&)) + ); + connect( + pRoutingModel, SIGNAL(modelReset()), + this, SLOT(updateTableCellRenderers()) + ); + connect( + pRoutingModel, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(onRoutingTableChanged()) + ); } ChannelFxForm::~ChannelFxForm() { + if (m_pAudioDevice) delete m_pAudioDevice; } void ChannelFxForm::onButtonClicked(QAbstractButton* button) { @@ -93,9 +215,13 @@ case QDialogButtonBox::AcceptRole: case QDialogButtonBox::ApplyRole: pModel->applyToSampler(); + // force a refresh of the parameter control elements + onFxSendSelection(m_ui.SendsListView->currentIndex()); break; case QDialogButtonBox::ResetRole: pModel->cleanRefresh(); + // force a refresh of the parameter control elements + onFxSendSelection(m_ui.SendsListView->currentIndex()); break; default: // to avoid gcc warnings break; @@ -103,7 +229,37 @@ } void ChannelFxForm::onFxSendSelection(const QModelIndex& index) { - m_ui.destroyPushButton->setEnabled(index.isValid()); + const bool bValid = index.isValid(); + m_ui.destroyPushButton->setEnabled(bValid); + m_ui.mainParametersGroupBox->setEnabled(bValid); + m_ui.audioRoutingGroupBox->setEnabled(bValid); + + FxSendsModel* pModel = (FxSendsModel*) m_ui.SendsListView->model(); + FxSend* pFxSend = pModel->fxSend(index); + + // clear routing model + ChannelRoutingModel* pRoutingModel = + (ChannelRoutingModel*) m_ui.audioRoutingTable->model(); + pRoutingModel->refresh(NULL, ChannelRoutingMap()); + pRoutingModel->routingMap().clear(); // Reset routing change map. + if (m_pAudioDevice) { + delete m_pAudioDevice; + m_pAudioDevice = NULL; + } + + if (!pFxSend) return; + + // update routing model + if (m_pSamplerChannel->audioDevice() >= 0) { + m_pAudioDevice = + new Device(Device::Audio, m_pSamplerChannel->audioDevice()); + pRoutingModel->refresh(m_pAudioDevice, pFxSend->audioRouting()); + } + + m_ui.depthCtrlComboBox->setCurrentIndex(pFxSend->sendDepthMidiCtrl()); + m_ui.depthSpinBox->setValue( + ::round(pFxSend->currentDepth() * 100.0) + ); } void ChannelFxForm::onCreateFxSend() { @@ -116,6 +272,78 @@ pModel->removeFxSend(m_ui.SendsListView->currentIndex()); } +void ChannelFxForm::onDepthCtrlChanged(int iMidiCtrl) { + FxSendsModel* pModel = (FxSendsModel*) m_ui.SendsListView->model(); + const QModelIndex index = m_ui.SendsListView->currentIndex(); + FxSend* pFxSend = pModel->fxSend(index); + if (!pFxSend) return; + + pFxSend->setSendDepthMidiCtrl(iMidiCtrl); + pModel->onExternalModifiication(index); +} + +void ChannelFxForm::onCurrentSendDepthChanged(int depthPercent) { + FxSendsModel* pModel = (FxSendsModel*) m_ui.SendsListView->model(); + const QModelIndex index = m_ui.SendsListView->currentIndex(); + FxSend* pFxSend = pModel->fxSend(index); + if (!pFxSend) return; + + if (depthPercent == int( ::round(pFxSend->currentDepth() * 100.0) )) + return; // nothing changed actually + + pFxSend->setCurrentDepth(double(depthPercent) / 100.0); + pModel->onExternalModifiication(index); +} + +void ChannelFxForm::onRoutingTableChanged() { + ChannelRoutingModel* pRoutingModel = + (ChannelRoutingModel*) m_ui.audioRoutingTable->model(); + if (pRoutingModel->routingMap().size() <= 0) + return; // no changes + + FxSendsModel* pModel = (FxSendsModel*) m_ui.SendsListView->model(); + const QModelIndex index = m_ui.SendsListView->currentIndex(); + FxSend* pFxSend = pModel->fxSend(index); + if (!pFxSend) { + pRoutingModel->routingMap().clear(); // reset routing change map + return; + } + + ChannelRoutingMap routingMap = pRoutingModel->routingMap(); + for ( + ChannelRoutingMap::iterator iter = routingMap.begin(); + iter != routingMap.end(); ++iter + ) pFxSend->setAudioChannel(iter.key(), iter.value()); + + pRoutingModel->routingMap().clear(); // reset routing change map + + pModel->onExternalModifiication(index); +} + +void ChannelFxForm::updateTableCellRenderers() { + ChannelRoutingModel* pRoutingModel = + (ChannelRoutingModel*) m_ui.audioRoutingTable->model(); + const int rows = pRoutingModel->rowCount(); + const int cols = pRoutingModel->columnCount(); + updateTableCellRenderers( + pRoutingModel->index(0, 0), + pRoutingModel->index(rows - 1, cols - 1) + ); +} + +void ChannelFxForm::updateTableCellRenderers ( + const QModelIndex& topLeft, const QModelIndex& bottomRight ) +{ + ChannelRoutingModel* pRoutingModel = + (ChannelRoutingModel*) m_ui.audioRoutingTable->model(); + for (int r = topLeft.row(); r <= bottomRight.row(); r++) { + for (int c = topLeft.column(); c <= bottomRight.column(); c++) { + const QModelIndex index = pRoutingModel->index(r, c); + m_ui.audioRoutingTable->openPersistentEditor(index); + } + } +} + } // namespace QSampler // end of qsamplerChannelFxForm.cpp