--- qsampler/trunk/src/qsamplerChannelStrip.cpp 2007/10/28 23:30:36 1461 +++ qsampler/trunk/src/qsamplerChannelStrip.cpp 2010/03/29 17:00:30 2074 @@ -1,157 +1,271 @@ +// qsamplerChannelStrip.cpp +// +/**************************************************************************** + Copyright (C) 2004-2010, rncbc aka Rui Nuno Capela. All rights reserved. + Copyright (C) 2007, 2008 Christian Schoenebeck + + 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; either 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 "qsamplerAbout.h" #include "qsamplerChannelStrip.h" #include "qsamplerMainForm.h" -#include +#include "qsamplerChannelFxForm.h" +#include +#include +#include #include -#include // Channel status/usage usage limit control. #define QSAMPLER_ERROR_LIMIT 3 +// Needed for lroundf() +#include + +#ifndef CONFIG_ROUND +static inline long lroundf ( float x ) +{ + if (x >= 0.0f) + return long(x + 0.5f); + else + return long(x - 0.5f); +} +#endif + + namespace QSampler { -ChannelStrip::ChannelStrip(QWidget* parent, Qt::WFlags f) : QWidget(parent, f) { - ui.setupUi(this); +//------------------------------------------------------------------------- +// QSampler::ChannelStrip -- Channel strip form implementation. +// + +// MIDI activity pixmap common resources. +int ChannelStrip::g_iMidiActivityRefCount = 0; +QPixmap *ChannelStrip::g_pMidiActivityLedOn = NULL; +QPixmap *ChannelStrip::g_pMidiActivityLedOff = NULL; + +// Channel strip activation/selection. +ChannelStrip *ChannelStrip::g_pSelectedStrip = NULL; + +ChannelStrip::ChannelStrip ( QWidget* pParent, Qt::WindowFlags wflags ) + : QWidget(pParent, wflags) +{ + m_ui.setupUi(this); + + // Initialize locals. + m_pChannel = NULL; + m_iDirtyChange = 0; + m_iErrorCount = 0; + + if (++g_iMidiActivityRefCount == 1) { + g_pMidiActivityLedOn = new QPixmap(":/images/ledon1.png"); + g_pMidiActivityLedOff = new QPixmap(":/images/ledoff1.png"); + } - // Initialize locals. - m_pChannel = NULL; - m_iDirtyChange = 0; - m_iErrorCount = 0; + m_ui.MidiActivityLabel->setPixmap(*g_pMidiActivityLedOff); - // Try to restore normal window positioning. - adjustSize(); -} +#ifndef CONFIG_EVENT_CHANNEL_MIDI + m_ui.MidiActivityLabel->setToolTip("MIDI activity (disabled)"); +#endif -ChannelStrip::~ChannelStrip() { - // Destroy existing channel descriptor. - if (m_pChannel) - delete m_pChannel; - m_pChannel = NULL; + m_pMidiActivityTimer = new QTimer(this); + m_pMidiActivityTimer->setSingleShot(true); + + QObject::connect(m_pMidiActivityTimer, + SIGNAL(timeout()), + SLOT(midiActivityLedOff()) + ); + + // Try to restore normal window positioning. + adjustSize(); + + QObject::connect(m_ui.ChannelSetupPushButton, + SIGNAL(clicked()), + SLOT(channelSetup())); + QObject::connect(m_ui.ChannelMutePushButton, + SIGNAL(toggled(bool)), + SLOT(channelMute(bool))); + QObject::connect(m_ui.ChannelSoloPushButton, + SIGNAL(toggled(bool)), + SLOT(channelSolo(bool))); + QObject::connect(m_ui.VolumeSlider, + SIGNAL(valueChanged(int)), + SLOT(volumeChanged(int))); + QObject::connect(m_ui.VolumeSpinBox, + SIGNAL(valueChanged(int)), + SLOT(volumeChanged(int))); + QObject::connect(m_ui.ChannelEditPushButton, + SIGNAL(clicked()), + SLOT(channelEdit())); + QObject::connect(m_ui.FxPushButton, + SIGNAL(clicked()), + SLOT(channelFxEdit())); + + setSelected(false); } -// Drag'n'drop file handler. -bool ChannelStrip::decodeDragFile ( const QMimeSource *pEvent, QString& sInstrumentFile ) +ChannelStrip::~ChannelStrip (void) { - if (m_pChannel == NULL) - return false; - if (Q3TextDrag::canDecode(pEvent)) { - QString sText; - if (Q3TextDrag::decode(pEvent, sText)) { - QStringList files = QStringList::split('\n', sText); - for (QStringList::Iterator iter = files.begin(); iter != files.end(); iter++) { - *iter = QUrl((*iter).stripWhiteSpace().replace(QRegExp("^file:"), QString::null)).path(); - if (qsamplerChannel::isInstrumentFile(*iter)) { - sInstrumentFile = *iter; - return true; - } - } - } + setSelected(false); + + // Destroy existing channel descriptor. + if (m_pChannel) + delete m_pChannel; + m_pChannel = NULL; + + if (--g_iMidiActivityRefCount == 0) { + if (g_pMidiActivityLedOn) + delete g_pMidiActivityLedOn; + g_pMidiActivityLedOn = NULL; + if (g_pMidiActivityLedOff) + delete g_pMidiActivityLedOff; + g_pMidiActivityLedOff = NULL; } - // Fail. - return false; } // Window drag-n-drop event handlers. void ChannelStrip::dragEnterEvent ( QDragEnterEvent* pDragEnterEvent ) { - QString sInstrumentFile; - pDragEnterEvent->accept(decodeDragFile(pDragEnterEvent, sInstrumentFile)); + if (m_pChannel == NULL) + return; + + bool bAccept = false; + + if (pDragEnterEvent->source() == NULL) { + const QMimeData *pMimeData = pDragEnterEvent->mimeData(); + if (pMimeData && pMimeData->hasUrls()) { + QListIterator iter(pMimeData->urls()); + while (iter.hasNext()) { + const QString& sFilename = iter.next().toLocalFile(); + if (!sFilename.isEmpty()) { + bAccept = Channel::isInstrumentFile(sFilename); + break; + } + } + } + } + + if (bAccept) + pDragEnterEvent->accept(); + else + pDragEnterEvent->ignore(); } void ChannelStrip::dropEvent ( QDropEvent* pDropEvent ) { - QString sInstrumentFile; + if (m_pChannel == NULL) + return; + + if (pDropEvent->source()) + return; - if (decodeDragFile(pDropEvent, sInstrumentFile)) { - // Go and set the dropped instrument filename... - m_pChannel->setInstrument(sInstrumentFile, 0); - // Open up the channel dialog. - channelSetup(); + const QMimeData *pMimeData = pDropEvent->mimeData(); + if (pMimeData && pMimeData->hasUrls()) { + QStringList files; + QListIterator iter(pMimeData->urls()); + while (iter.hasNext()) { + const QString& sFilename = iter.next().toLocalFile(); + if (!sFilename.isEmpty()) { + // Go and set the dropped instrument filename... + m_pChannel->setInstrument(sFilename, 0); + // Open up the channel dialog. + channelSetup(); + break; + } + } } } // Channel strip setup formal initializer. -void ChannelStrip::setup ( qsamplerChannel *pChannel ) +void ChannelStrip::setup ( Channel *pChannel ) { - // Destroy any previous channel descriptor; - // (remember that once setup we own it!) - if (m_pChannel) - delete m_pChannel; + // Destroy any previous channel descriptor; + // (remember that once setup we own it!) + if (m_pChannel) + delete m_pChannel; - // Set the new one... - m_pChannel = pChannel; + // Set the new one... + m_pChannel = pChannel; - // Stabilize this around. - updateChannelInfo(); + // Stabilize this around. + updateChannelInfo(); // We'll accept drops from now on... if (m_pChannel) setAcceptDrops(true); } + // Channel secriptor accessor. -qsamplerChannel *ChannelStrip::channel (void) +Channel *ChannelStrip::channel (void) const { - return m_pChannel; + return m_pChannel; } // Messages view font accessors. -QFont ChannelStrip::displayFont (void) +QFont ChannelStrip::displayFont (void) const { - return ui.EngineNameTextLabel->font(); + return m_ui.EngineNameTextLabel->font(); } void ChannelStrip::setDisplayFont ( const QFont & font ) { - ui.EngineNameTextLabel->setFont(font); - ui.MidiPortChannelTextLabel->setFont(font); - ui.InstrumentNameTextLabel->setFont(font); - ui.InstrumentStatusTextLabel->setFont(font); + m_ui.EngineNameTextLabel->setFont(font); + m_ui.MidiPortChannelTextLabel->setFont(font); + m_ui.InstrumentNameTextLabel->setFont(font); + m_ui.InstrumentStatusTextLabel->setFont(font); } // Channel display background effect. void ChannelStrip::setDisplayEffect ( bool bDisplayEffect ) { - QPixmap pm = - (bDisplayEffect) ? - QPixmap(":/qsampler/pixmaps/displaybg1.png") : QPixmap(); - setDisplayBackground(pm); -} - - -// Update main display background pixmap. -void ChannelStrip::setDisplayBackground ( const QPixmap& pm ) -{ - // Set the main origin... - ui.ChannelInfoFrame->setPaletteBackgroundPixmap(pm); - - // Iterate for every child text label... - QList list = ui.ChannelInfoFrame->queryList("QLabel"); - for (QList::iterator iter = list.begin(); iter != list.end(); iter++) { - static_cast(*iter)->setPaletteBackgroundPixmap(pm); - } - - // And this standalone too. - ui.StreamVoiceCountTextLabel->setPaletteBackgroundPixmap(pm); + QPalette pal; + pal.setColor(QPalette::Foreground, Qt::yellow); + m_ui.EngineNameTextLabel->setPalette(pal); + m_ui.MidiPortChannelTextLabel->setPalette(pal); + pal.setColor(QPalette::Foreground, Qt::green); + if (bDisplayEffect) { + QPixmap pm(":/images/displaybg1.png"); + pal.setBrush(QPalette::Background, QBrush(pm)); + } else { + pal.setColor(QPalette::Background, Qt::black); + } + m_ui.ChannelInfoFrame->setPalette(pal); + m_ui.InstrumentNameTextLabel->setPalette(pal); + m_ui.StreamVoiceCountTextLabel->setPalette(pal); } // Maximum volume slider accessors. void ChannelStrip::setMaxVolume ( int iMaxVolume ) { - m_iDirtyChange++; - ui.VolumeSlider->setRange(0, iMaxVolume); - ui.VolumeSpinBox->setRange(0, iMaxVolume); - m_iDirtyChange--; + m_iDirtyChange++; + m_ui.VolumeSlider->setRange(0, iMaxVolume); + m_ui.VolumeSpinBox->setRange(0, iMaxVolume); + m_iDirtyChange--; } @@ -212,6 +326,33 @@ m_pChannel->editChannel(); } +bool ChannelStrip::channelFxEdit (void) +{ + MainForm *pMainForm = MainForm::getInstance(); + if (!pMainForm || !channel()) + return false; + + pMainForm->appendMessages(QObject::tr("channel fx sends...")); + + bool bResult = false; + +#if CONFIG_FXSEND + ChannelFxForm *pChannelFxForm = + new ChannelFxForm(channel(), parentWidget()); + if (pChannelFxForm) { + //pChannelForm->setup(this); + bResult = pChannelFxForm->exec(); + delete pChannelFxForm; + } +#else // CONFIG_FXSEND + QMessageBox::critical(this, + QSAMPLER_TITLE ": " + tr("Unavailable"), + tr("Sorry, QSampler was built without FX send support!\n\n" + "(Make sure you have a recent liblscp when recompiling QSampler)")); +#endif // CONFIG_FXSEND + + return bResult; +} // Channel reset slot. bool ChannelStrip::channelReset (void) @@ -241,12 +382,17 @@ // Instrument name... if (m_pChannel->instrumentName().isEmpty()) { - if (m_pChannel->instrumentStatus() >= 0) - ui.InstrumentNameTextLabel->setText(' ' + qsamplerChannel::loadingInstrument()); - else - ui.InstrumentNameTextLabel->setText(' ' + qsamplerChannel::noInstrumentName()); - } else - ui.InstrumentNameTextLabel->setText(' ' + m_pChannel->instrumentName()); + if (m_pChannel->instrumentStatus() >= 0) { + m_ui.InstrumentNameTextLabel->setText( + ' ' + Channel::loadingInstrument()); + } else { + m_ui.InstrumentNameTextLabel->setText( + ' ' + Channel::noInstrumentName()); + } + } else { + m_ui.InstrumentNameTextLabel->setText( + ' ' + m_pChannel->instrumentName()); + } return true; } @@ -255,115 +401,121 @@ // Do the dirty volume change. bool ChannelStrip::updateChannelVolume (void) { - if (m_pChannel == NULL) - return false; - - // Convert... -#ifdef CONFIG_ROUND - int iVolume = (int) ::round(100.0 * m_pChannel->volume()); -#else - double fIPart = 0.0; - double fFPart = ::modf(100.0 * m_pChannel->volume(), &fIPart); - int iVolume = (int) fIPart; - if (fFPart >= +0.5) - iVolume++; - else - if (fFPart <= -0.5) - iVolume--; -#endif + if (m_pChannel == NULL) + return false; - // And clip... - if (iVolume < 0) - iVolume = 0; - - // Flag it here, to avoid infinite recursion. - m_iDirtyChange++; - ui.VolumeSlider->setValue(iVolume); - ui.VolumeSpinBox->setValue(iVolume); - m_iDirtyChange--; + // Convert... + int iVolume = ::lroundf(100.0f * m_pChannel->volume()); + // And clip... + if (iVolume < 0) + iVolume = 0; + + // Flag it here, to avoid infinite recursion. + m_iDirtyChange++; + m_ui.VolumeSlider->setValue(iVolume); + m_ui.VolumeSpinBox->setValue(iVolume); + m_iDirtyChange--; - return true; + return true; } // Update whole channel info state. bool ChannelStrip::updateChannelInfo (void) { - if (m_pChannel == NULL) - return false; + if (m_pChannel == NULL) + return false; // Check for error limit/recycle... if (m_iErrorCount > QSAMPLER_ERROR_LIMIT) return true; - // Update strip caption. - QString sText = m_pChannel->channelName(); - setCaption(sText); - ui.ChannelSetupPushButton->setText(sText); + // Update strip caption. + QString sText = m_pChannel->channelName(); + setWindowTitle(sText); + m_ui.ChannelSetupPushButton->setText('&' + sText); - // Check if we're up and connected. + // Check if we're up and connected. MainForm* pMainForm = MainForm::getInstance(); if (pMainForm->client() == NULL) return false; - // Read actual channel information. - m_pChannel->updateChannelInfo(); + // Read actual channel information. + m_pChannel->updateChannelInfo(); - // Engine name... - if (m_pChannel->engineName().isEmpty()) - ui.EngineNameTextLabel->setText(' ' + qsamplerChannel::noEngineName()); - else - ui.EngineNameTextLabel->setText(' ' + m_pChannel->engineName()); + // Engine name... + if (m_pChannel->engineName().isEmpty()) { + m_ui.EngineNameTextLabel->setText( + ' ' + Channel::noEngineName()); + } else { + m_ui.EngineNameTextLabel->setText( + ' ' + m_pChannel->engineName()); + } // Instrument name... updateInstrumentName(false); - // MIDI Port/Channel... + // MIDI Port/Channel... QString sMidiPortChannel = QString::number(m_pChannel->midiPort()) + " / "; if (m_pChannel->midiChannel() == LSCP_MIDI_CHANNEL_ALL) sMidiPortChannel += tr("All"); else sMidiPortChannel += QString::number(m_pChannel->midiChannel() + 1); - ui.MidiPortChannelTextLabel->setText(sMidiPortChannel); + m_ui.MidiPortChannelTextLabel->setText(sMidiPortChannel); - // Instrument status... - int iInstrumentStatus = m_pChannel->instrumentStatus(); - if (iInstrumentStatus < 0) { - ui.InstrumentStatusTextLabel->setPaletteForegroundColor(Qt::red); - ui.InstrumentStatusTextLabel->setText(tr("ERR%1").arg(iInstrumentStatus)); - m_iErrorCount++; - return false; - } - // All seems normal... - ui.InstrumentStatusTextLabel->setPaletteForegroundColor(iInstrumentStatus < 100 ? Qt::yellow : Qt::green); - ui.InstrumentStatusTextLabel->setText(QString::number(iInstrumentStatus) + '%'); - m_iErrorCount = 0; + // Common palette... + QPalette pal; + const QColor& rgbFore = pal.color(QPalette::Foreground); + + // Instrument status... + int iInstrumentStatus = m_pChannel->instrumentStatus(); + if (iInstrumentStatus < 0) { + pal.setColor(QPalette::Foreground, Qt::red); + m_ui.InstrumentStatusTextLabel->setPalette(pal); + m_ui.InstrumentStatusTextLabel->setText( + tr("ERR%1").arg(iInstrumentStatus)); + m_iErrorCount++; + return false; + } + // All seems normal... + pal.setColor(QPalette::Foreground, + iInstrumentStatus < 100 ? Qt::yellow : Qt::green); + m_ui.InstrumentStatusTextLabel->setPalette(pal); + m_ui.InstrumentStatusTextLabel->setText( + QString::number(iInstrumentStatus) + '%'); + m_iErrorCount = 0; #ifdef CONFIG_MUTE_SOLO - // Mute/Solo button state coloring... - const QColor& rgbNormal = ChannelSetupPushButton->paletteBackgroundColor(); - bool bMute = m_pChannel->channelMute(); - ChannelMutePushButton->setPaletteBackgroundColor(bMute ? Qt::yellow : rgbNormal); - ChannelMutePushButton->setDown(bMute); - bool bSolo = m_pChannel->channelSolo(); - ChannelSoloPushButton->setPaletteBackgroundColor(bSolo ? Qt::cyan : rgbNormal); - ChannelSoloPushButton->setDown(bSolo); + // Mute/Solo button state coloring... + bool bMute = m_pChannel->channelMute(); + const QColor& rgbButton = pal.color(QPalette::Button); + const QColor& rgbButtonText = pal.color(QPalette::ButtonText); + pal.setColor(QPalette::Foreground, rgbFore); + pal.setColor(QPalette::Button, bMute ? Qt::yellow : rgbButton); + pal.setColor(QPalette::ButtonText, bMute ? Qt::darkYellow : rgbButtonText); + m_ui.ChannelMutePushButton->setPalette(pal); + m_ui.ChannelMutePushButton->setDown(bMute); + bool bSolo = m_pChannel->channelSolo(); + pal.setColor(QPalette::Button, bSolo ? Qt::cyan : rgbButton); + pal.setColor(QPalette::ButtonText, bSolo ? Qt::darkCyan : rgbButtonText); + m_ui.ChannelSoloPushButton->setPalette(pal); + m_ui.ChannelSoloPushButton->setDown(bSolo); #else - ui.ChannelMutePushButton->setEnabled(false); - ui.ChannelSoloPushButton->setEnabled(false); + m_ui.ChannelMutePushButton->setEnabled(false); + m_ui.ChannelSoloPushButton->setEnabled(false); #endif - // And update the both GUI volume elements; - // return success if, and only if, intrument is fully loaded... - return updateChannelVolume() && (iInstrumentStatus == 100); + // And update the both GUI volume elements; + // return success if, and only if, intrument is fully loaded... + return updateChannelVolume() && (iInstrumentStatus == 100); } // Update whole channel usage state. bool ChannelStrip::updateChannelUsage (void) { - if (m_pChannel == NULL) - return false; + if (m_pChannel == NULL) + return false; MainForm *pMainForm = MainForm::getInstance(); if (pMainForm->client() == NULL) @@ -371,57 +523,74 @@ // This only makes sense on fully loaded channels... if (m_pChannel->instrumentStatus() < 100) - return false; + return false; - // Get current channel voice count. - int iVoiceCount = ::lscp_get_channel_voice_count(pMainForm->client(), m_pChannel->channelID()); - // Get current stream count. - int iStreamCount = ::lscp_get_channel_stream_count(pMainForm->client(), m_pChannel->channelID()); - // Get current channel buffer fill usage. - // As benno has suggested this is the percentage usage - // of the least filled buffer stream... - int iStreamUsage = ::lscp_get_channel_stream_usage(pMainForm->client(), m_pChannel->channelID());; - - // Update the GUI elements... - ui.StreamUsageProgressBar->setValue(iStreamUsage); - ui.StreamVoiceCountTextLabel->setText(QString("%1 / %2").arg(iStreamCount).arg(iVoiceCount)); + // Get current channel voice count. + int iVoiceCount = ::lscp_get_channel_voice_count( + pMainForm->client(), m_pChannel->channelID()); +// Get current stream count. + int iStreamCount = ::lscp_get_channel_stream_count( + pMainForm->client(), m_pChannel->channelID()); + // Get current channel buffer fill usage. + // As benno has suggested this is the percentage usage + // of the least filled buffer stream... + int iStreamUsage = ::lscp_get_channel_stream_usage( + pMainForm->client(), m_pChannel->channelID());; + + // Update the GUI elements... + m_ui.StreamUsageProgressBar->setValue(iStreamUsage); + m_ui.StreamVoiceCountTextLabel->setText( + QString("%1 / %2").arg(iStreamCount).arg(iVoiceCount)); - // We're clean. - return true; + // We're clean. + return true; } // Volume change slot. void ChannelStrip::volumeChanged ( int iVolume ) { - if (m_pChannel == NULL) - return; + if (m_pChannel == NULL) + return; + + // Avoid recursion. + if (m_iDirtyChange > 0) + return; - // Avoid recursion. - if (m_iDirtyChange > 0) - return; - - // Convert and clip. - float fVolume = (float) iVolume / 100.0; - if (fVolume < 0.001) - fVolume = 0.0; - - // Update the GUI elements. - if (m_pChannel->setVolume(fVolume)) { - updateChannelVolume(); - emit channelChanged(this); - } + // Convert and clip. + float fVolume = (float) iVolume / 100.0f; + if (fVolume < 0.001f) + fVolume = 0.0f; + + // Update the GUI elements. + if (m_pChannel->setVolume(fVolume)) { + updateChannelVolume(); + emit channelChanged(this); + } } // Context menu event handler. void ChannelStrip::contextMenuEvent( QContextMenuEvent *pEvent ) { - if (m_pChannel == NULL) - return; + if (m_pChannel == NULL) + return; - // We'll just show up the main form's edit menu (thru qsamplerChannel). - m_pChannel->contextMenuEvent(pEvent); + // We'll just show up the main form's edit menu (thru qsamplerChannel). + m_pChannel->contextMenuEvent(pEvent); +} + + +void ChannelStrip::midiActivityLedOn (void) +{ + m_ui.MidiActivityLabel->setPixmap(*g_pMidiActivityLedOn); + m_pMidiActivityTimer->start(100); +} + + +void ChannelStrip::midiActivityLedOff (void) +{ + m_ui.MidiActivityLabel->setPixmap(*g_pMidiActivityLedOff); } @@ -431,4 +600,38 @@ m_iErrorCount = 0; } + +// Channel strip activation/selection. +void ChannelStrip::setSelected ( bool bSelected ) +{ + if (bSelected) { + if (g_pSelectedStrip == this) + return; + if (g_pSelectedStrip) + g_pSelectedStrip->setSelected(false); + g_pSelectedStrip = this; + } else { + if (g_pSelectedStrip == this) + g_pSelectedStrip = NULL; + } + + QPalette pal; + if (bSelected) { + const QColor& color = pal.midlight().color(); + pal.setColor(QPalette::Background, color.dark(150)); + pal.setColor(QPalette::Foreground, color.light(150)); + } + QWidget::setPalette(pal); +} + + +bool ChannelStrip::isSelected (void) const +{ + return (this == g_pSelectedStrip); +} + + } // namespace QSampler + + +// end of qsamplerChannelStrip.cpp