--- qsampler/trunk/src/qsamplerChannelStrip.cpp 2007/11/20 16:48:04 1499 +++ qsampler/trunk/src/qsamplerChannelStrip.cpp 2010/01/07 18:42:26 2038 @@ -1,8 +1,8 @@ // qsamplerChannelStrip.cpp // /**************************************************************************** - Copyright (C) 2004-2007, rncbc aka Rui Nuno Capela. All rights reserved. - Copyright (C) 2007, Christian Schoenebeck + 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 @@ -25,52 +25,120 @@ #include "qsamplerMainForm.h" +#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(":/icons/ledon1.png"); + g_pMidiActivityLedOff = new QPixmap(":/icons/ledoff1.png"); + } + + m_ui.MidiActivityLabel->setPixmap(*g_pMidiActivityLedOff); + +#ifndef CONFIG_EVENT_CHANNEL_MIDI + m_ui.MidiActivityLabel->setToolTip("MIDI activity (disabled)"); +#endif + + m_pMidiActivityTimer = new QTimer(this); + m_pMidiActivityTimer->setSingleShot(true); - // Initialize locals. - m_pChannel = NULL; - m_iDirtyChange = 0; - m_iErrorCount = 0; + QObject::connect(m_pMidiActivityTimer, + SIGNAL(timeout()), + SLOT(midiActivityLedOff()) + ); - // Try to restore normal window positioning. - adjustSize(); + // Try to restore normal window positioning. + adjustSize(); - QObject::connect(ui.ChannelSetupPushButton, + QObject::connect(m_ui.ChannelSetupPushButton, SIGNAL(clicked()), SLOT(channelSetup())); - QObject::connect(ui.ChannelMutePushButton, + QObject::connect(m_ui.ChannelMutePushButton, SIGNAL(toggled(bool)), SLOT(channelMute(bool))); - QObject::connect(ui.ChannelSoloPushButton, + QObject::connect(m_ui.ChannelSoloPushButton, SIGNAL(toggled(bool)), SLOT(channelSolo(bool))); - QObject::connect(ui.VolumeSlider, + QObject::connect(m_ui.VolumeSlider, SIGNAL(valueChanged(int)), SLOT(volumeChanged(int))); - QObject::connect(ui.VolumeSpinBox, + QObject::connect(m_ui.VolumeSpinBox, SIGNAL(valueChanged(int)), SLOT(volumeChanged(int))); - QObject::connect(ui.ChannelEditPushButton, + QObject::connect(m_ui.ChannelEditPushButton, SIGNAL(clicked()), SLOT(channelEdit())); + QObject::connect(m_ui.FxPushButton, + SIGNAL(clicked()), + SLOT(channelFxEdit())); + + setSelected(false); } -ChannelStrip::~ChannelStrip() { - // Destroy existing channel descriptor. - if (m_pChannel) - delete m_pChannel; - m_pChannel = NULL; + +ChannelStrip::~ChannelStrip (void) +{ + 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; + } } @@ -89,7 +157,7 @@ while (iter.hasNext()) { const QString& sFilename = iter.next().toLocalFile(); if (!sFilename.isEmpty()) { - bAccept = qsamplerChannel::isInstrumentFile(sFilename); + bAccept = Channel::isInstrumentFile(sFilename); break; } } @@ -130,43 +198,44 @@ // 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); } @@ -174,6 +243,9 @@ void ChannelStrip::setDisplayEffect ( bool bDisplayEffect ) { 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(":/icons/displaybg1.png"); @@ -181,18 +253,19 @@ } else { pal.setColor(QPalette::Background, Qt::black); } - ui.ChannelInfoFrame->setPalette(pal); - ui.StreamVoiceCountTextLabel->setPalette(pal); + 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--; } @@ -253,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) @@ -282,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; } @@ -296,125 +401,118 @@ // 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(); - setWindowTitle(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); // Common palette... QPalette pal; const QColor& rgbFore = pal.color(QPalette::Foreground); - // Instrument status... - int iInstrumentStatus = m_pChannel->instrumentStatus(); - if (iInstrumentStatus < 0) { + // Instrument status... + int iInstrumentStatus = m_pChannel->instrumentStatus(); + if (iInstrumentStatus < 0) { pal.setColor(QPalette::Foreground, Qt::red); - ui.InstrumentStatusTextLabel->setPalette(pal); - ui.InstrumentStatusTextLabel->setText(tr("ERR%1").arg(iInstrumentStatus)); - m_iErrorCount++; - return false; - } - // All seems normal... + 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); - ui.InstrumentStatusTextLabel->setPalette(pal); - ui.InstrumentStatusTextLabel->setText(QString::number(iInstrumentStatus) + '%'); - m_iErrorCount = 0; + m_ui.InstrumentStatusTextLabel->setPalette(pal); + m_ui.InstrumentStatusTextLabel->setText( + QString::number(iInstrumentStatus) + '%'); + m_iErrorCount = 0; #ifdef CONFIG_MUTE_SOLO - // Mute/Solo button state coloring... - bool bMute = m_pChannel->channelMute(); + // Mute/Solo button state coloring... + bool bMute = m_pChannel->channelMute(); const QColor& rgbButton = pal.color(QPalette::Button); pal.setColor(QPalette::Foreground, rgbFore); pal.setColor(QPalette::Button, bMute ? Qt::yellow : rgbButton); - ui.ChannelMutePushButton->setPalette(pal); - ui.ChannelMutePushButton->setDown(bMute); - bool bSolo = m_pChannel->channelSolo(); + m_ui.ChannelMutePushButton->setPalette(pal); + m_ui.ChannelMutePushButton->setDown(bMute); + bool bSolo = m_pChannel->channelSolo(); pal.setColor(QPalette::Button, bSolo ? Qt::cyan : rgbButton); - ui.ChannelSoloPushButton->setPalette(pal); - ui.ChannelSoloPushButton->setDown(bSolo); + 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) @@ -422,57 +520,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); } @@ -482,6 +597,37 @@ 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