--- qsampler/trunk/src/qsamplerMainForm.cpp 2007/11/23 10:51:37 1514 +++ qsampler/trunk/src/qsamplerMainForm.cpp 2008/02/17 13:53:00 1702 @@ -2,7 +2,7 @@ // /**************************************************************************** Copyright (C) 2004-2007, rncbc aka Rui Nuno Capela. All rights reserved. - Copyright (C) 2007, Christian Schoenebeck + 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 @@ -33,6 +33,7 @@ #include "qsamplerInstrumentListForm.h" #include "qsamplerDeviceForm.h" #include "qsamplerOptionsForm.h" +#include "qsamplerDeviceStatusForm.h" #include #include @@ -77,6 +78,15 @@ } #endif + +// All winsock apps needs this. +#if defined(WIN32) +static WSADATA _wsaData; +#endif + + +namespace QSampler { + // Timer constant stuff. #define QSAMPLER_TIMER_MSECS 200 @@ -87,23 +97,17 @@ #define QSAMPLER_STATUS_SESSION 3 // Current session modification state. -// All winsock apps needs this. -#if defined(WIN32) -static WSADATA _wsaData; -#endif - - //------------------------------------------------------------------------- -// qsamplerCustomEvent -- specialty for callback comunication. +// CustomEvent -- specialty for callback comunication. #define QSAMPLER_CUSTOM_EVENT QEvent::Type(QEvent::User + 0) -class qsamplerCustomEvent : public QEvent +class CustomEvent : public QEvent { public: // Constructor. - qsamplerCustomEvent(lscp_event_t event, const char *pchData, int cchData) + CustomEvent(lscp_event_t event, const char *pchData, int cchData) : QEvent(QSAMPLER_CUSTOM_EVENT) { m_event = event; @@ -126,8 +130,6 @@ //------------------------------------------------------------------------- // qsamplerMainForm -- Main window form implementation. -namespace QSampler { - // Kind of singleton reference. MainForm* MainForm::g_pMainForm = NULL; @@ -171,7 +173,7 @@ // Volume slider... m_ui.channelsToolbar->addSeparator(); m_pVolumeSlider = new QSlider(Qt::Horizontal, m_ui.channelsToolbar); - m_pVolumeSlider->setTickPosition(QSlider::TicksBelow); + m_pVolumeSlider->setTickPosition(QSlider::TicksBothSides); m_pVolumeSlider->setTickInterval(10); m_pVolumeSlider->setPageStep(10); m_pVolumeSlider->setSingleStep(10); @@ -189,6 +191,7 @@ // Volume spin-box m_ui.channelsToolbar->addSeparator(); m_pVolumeSpinBox = new QSpinBox(m_ui.channelsToolbar); + m_pVolumeSpinBox->setMaximumHeight(24); m_pVolumeSpinBox->setSuffix(" %"); m_pVolumeSpinBox->setMinimum(0); m_pVolumeSpinBox->setMaximum(100); @@ -365,7 +368,7 @@ // Make and set a proper setup options step. -void MainForm::setup ( qsamplerOptions *pOptions ) +void MainForm::setup ( Options *pOptions ) { // We got options? m_pOptions = pOptions; @@ -381,7 +384,7 @@ if (m_pOptions->bKeepOnTop) wflags |= Qt::Tool; // Some child forms are to be created right now. - m_pMessages = new qsamplerMessages(this); + m_pMessages = new Messages(this); m_pDeviceForm = new DeviceForm(this, wflags); #ifdef CONFIG_MIDI_INSTRUMENT m_pInstrumentListForm = new InstrumentListForm(this, wflags); @@ -469,7 +472,7 @@ if (m_pDeviceForm) m_pDeviceForm->close(); // Stop client and/or server, gracefully. - stopServer(); + stopServer(true /*interactive*/); } } @@ -479,9 +482,10 @@ void MainForm::closeEvent ( QCloseEvent *pCloseEvent ) { - if (queryClose()) + if (queryClose()) { + DeviceStatusForm::deleteAllInstances(); pCloseEvent->accept(); - else + } else pCloseEvent->ignore(); } @@ -510,9 +514,9 @@ QListIterator iter(pMimeData->urls()); while (iter.hasNext()) { const QString& sPath = iter.next().toLocalFile(); - if (qsamplerChannel::isInstrumentFile(sPath)) { + if (Channel::isInstrumentFile(sPath)) { // Try to create a new channel from instrument file... - qsamplerChannel *pChannel = new qsamplerChannel(); + Channel *pChannel = new Channel(); if (pChannel == NULL) return; // Start setting the instrument filename... @@ -548,20 +552,78 @@ { // For the time being, just pump it to messages. if (pCustomEvent->type() == QSAMPLER_CUSTOM_EVENT) { - qsamplerCustomEvent *pEvent = (qsamplerCustomEvent *) pCustomEvent; - if (pEvent->event() == LSCP_EVENT_CHANNEL_INFO) { - int iChannelID = pEvent->data().toInt(); - ChannelStrip *pChannelStrip = channelStrip(iChannelID); - if (pChannelStrip) - channelStripChanged(pChannelStrip); - } else { - appendMessagesColor(tr("Notify event: %1 data: %2") - .arg(::lscp_event_to_text(pEvent->event())) - .arg(pEvent->data()), "#996699"); + CustomEvent *pEvent = static_cast (pCustomEvent); + switch (pEvent->event()) { + case LSCP_EVENT_CHANNEL_COUNT: + updateAllChannelStrips(true); + break; + case LSCP_EVENT_CHANNEL_INFO: { + int iChannelID = pEvent->data().toInt(); + ChannelStrip *pChannelStrip = channelStrip(iChannelID); + if (pChannelStrip) + channelStripChanged(pChannelStrip); + break; + } + case LSCP_EVENT_MIDI_INPUT_DEVICE_COUNT: + if (m_pDeviceForm) m_pDeviceForm->refreshDevices(); + DeviceStatusForm::onDevicesChanged(); + updateViewMidiDeviceStatusMenu(); + break; + case LSCP_EVENT_MIDI_INPUT_DEVICE_INFO: { + if (m_pDeviceForm) m_pDeviceForm->refreshDevices(); + const int iDeviceID = pEvent->data().section(' ', 0, 0).toInt(); + DeviceStatusForm::onDeviceChanged(iDeviceID); + break; + } + case LSCP_EVENT_AUDIO_OUTPUT_DEVICE_COUNT: + if (m_pDeviceForm) m_pDeviceForm->refreshDevices(); + break; + case LSCP_EVENT_AUDIO_OUTPUT_DEVICE_INFO: + if (m_pDeviceForm) m_pDeviceForm->refreshDevices(); + break; +#if CONFIG_LSCP_CHANNEL_MIDI + case LSCP_EVENT_CHANNEL_MIDI: { + const int iChannelID = pEvent->data().section(' ', 0, 0).toInt(); + ChannelStrip *pChannelStrip = channelStrip(iChannelID); + if (pChannelStrip) + pChannelStrip->midiArrived(); + break; + } +#endif +#if CONFIG_LSCP_DEVICE_MIDI + case LSCP_EVENT_DEVICE_MIDI: { + const int iDeviceID = pEvent->data().section(' ', 0, 0).toInt(); + const int iPortID = pEvent->data().section(' ', 1, 1).toInt(); + DeviceStatusForm* pDeviceStatusForm = + DeviceStatusForm::getInstance(iDeviceID); + if (pDeviceStatusForm) + pDeviceStatusForm->midiArrived(iPortID); + break; + } +#endif + default: + appendMessagesColor(tr("Notify event: %1 data: %2") + .arg(::lscp_event_to_text(pEvent->event())) + .arg(pEvent->data()), "#996699"); } } } +void MainForm::updateViewMidiDeviceStatusMenu() { + m_ui.viewMidiDeviceStatusMenu->clear(); + const std::map statusForms = + DeviceStatusForm::getInstances(); + for ( + std::map::const_iterator iter = statusForms.begin(); + iter != statusForms.end(); ++iter + ) { + DeviceStatusForm* pForm = iter->second; + m_ui.viewMidiDeviceStatusMenu->addAction( + pForm->visibleAction() + ); + } +} + // Context menu event handler. void MainForm::contextMenuEvent( QContextMenuEvent *pEvent ) { @@ -575,7 +637,7 @@ // qsamplerMainForm -- Brainless public property accessors. // The global options settings property. -qsamplerOptions *MainForm::options (void) const +Options *MainForm::options (void) const { return m_pOptions; } @@ -736,7 +798,7 @@ for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) { ChannelStrip *pChannelStrip = (ChannelStrip*) wlist.at(iChannel); if (pChannelStrip) { - qsamplerChannel *pChannel = pChannelStrip->channel(); + Channel *pChannel = pChannelStrip->channel(); if (bForce && pChannel) pChannel->removeChannel(); delete pChannelStrip; @@ -876,33 +938,33 @@ // Audio device mapping. QMap audioDeviceMap; - piDeviceIDs = qsamplerDevice::getDevices(m_pClient, qsamplerDevice::Audio); + piDeviceIDs = Device::getDevices(m_pClient, Device::Audio); for (iDevice = 0; piDeviceIDs && piDeviceIDs[iDevice] >= 0; iDevice++) { ts << endl; - qsamplerDevice device(qsamplerDevice::Audio, piDeviceIDs[iDevice]); + Device device(Device::Audio, piDeviceIDs[iDevice]); // Audio device specification... ts << "# " << device.deviceTypeName() << " " << device.driverName() << " " << tr("Device") << " " << iDevice << endl; ts << "CREATE AUDIO_OUTPUT_DEVICE " << device.driverName(); - qsamplerDeviceParamMap::ConstIterator deviceParam; + DeviceParamMap::ConstIterator deviceParam; for (deviceParam = device.params().begin(); deviceParam != device.params().end(); ++deviceParam) { - const qsamplerDeviceParam& param = deviceParam.value(); + const DeviceParam& param = deviceParam.value(); if (param.value.isEmpty()) ts << "# "; ts << " " << deviceParam.key() << "='" << param.value << "'"; } ts << endl; // Audio channel parameters... int iPort = 0; - QListIterator iter(device.ports()); + QListIterator iter(device.ports()); while (iter.hasNext()) { - qsamplerDevicePort *pPort = iter.next(); - qsamplerDeviceParamMap::ConstIterator portParam; + DevicePort *pPort = iter.next(); + DeviceParamMap::ConstIterator portParam; for (portParam = pPort->params().begin(); portParam != pPort->params().end(); ++portParam) { - const qsamplerDeviceParam& param = portParam.value(); + const DeviceParam& param = portParam.value(); if (param.fix || param.value.isEmpty()) ts << "# "; ts << "SET AUDIO_OUTPUT_CHANNEL_PARAMETER " << iDevice << " " << iPort << " " << portParam.key() @@ -918,33 +980,33 @@ // MIDI device mapping. QMap midiDeviceMap; - piDeviceIDs = qsamplerDevice::getDevices(m_pClient, qsamplerDevice::Midi); + piDeviceIDs = Device::getDevices(m_pClient, Device::Midi); for (iDevice = 0; piDeviceIDs && piDeviceIDs[iDevice] >= 0; iDevice++) { ts << endl; - qsamplerDevice device(qsamplerDevice::Midi, piDeviceIDs[iDevice]); + Device device(Device::Midi, piDeviceIDs[iDevice]); // MIDI device specification... ts << "# " << device.deviceTypeName() << " " << device.driverName() << " " << tr("Device") << " " << iDevice << endl; ts << "CREATE MIDI_INPUT_DEVICE " << device.driverName(); - qsamplerDeviceParamMap::ConstIterator deviceParam; + DeviceParamMap::ConstIterator deviceParam; for (deviceParam = device.params().begin(); deviceParam != device.params().end(); ++deviceParam) { - const qsamplerDeviceParam& param = deviceParam.value(); + const DeviceParam& param = deviceParam.value(); if (param.value.isEmpty()) ts << "# "; ts << " " << deviceParam.key() << "='" << param.value << "'"; } ts << endl; // MIDI port parameters... int iPort = 0; - QListIterator iter(device.ports()); + QListIterator iter(device.ports()); while (iter.hasNext()) { - qsamplerDevicePort *pPort = iter.next(); - qsamplerDeviceParamMap::ConstIterator portParam; + DevicePort *pPort = iter.next(); + DeviceParamMap::ConstIterator portParam; for (portParam = pPort->params().begin(); portParam != pPort->params().end(); ++portParam) { - const qsamplerDeviceParam& param = portParam.value(); + const DeviceParam& param = portParam.value(); if (param.fix || param.value.isEmpty()) ts << "# "; ts << "SET MIDI_INPUT_PORT_PARAMETER " << iDevice << " " << iPort << " " << portParam.key() @@ -1036,7 +1098,7 @@ ChannelStrip* pChannelStrip = static_cast (wlist.at(iChannel)); if (pChannelStrip) { - qsamplerChannel *pChannel = pChannelStrip->channel(); + Channel *pChannel = pChannelStrip->channel(); if (pChannel) { ts << "# " << tr("Channel") << " " << iChannel << endl; ts << "ADD CHANNEL" << endl; @@ -1068,7 +1130,7 @@ ts << "LOAD INSTRUMENT NON_MODAL '" << pChannel->instrumentFile() << "' " << pChannel->instrumentNr() << " " << iChannel << endl; - qsamplerChannelRoutingMap::ConstIterator audioRoute; + ChannelRoutingMap::ConstIterator audioRoute; for (audioRoute = pChannel->audioRouting().begin(); audioRoute != pChannel->audioRouting().end(); ++audioRoute) { @@ -1313,7 +1375,7 @@ return; // Just create the channel instance... - qsamplerChannel *pChannel = new qsamplerChannel(); + Channel *pChannel = new Channel(); if (pChannel == NULL) return; @@ -1331,6 +1393,10 @@ return; } + // Do we auto-arrange? + if (m_pOptions && m_pOptions->bAutoArrange) + channelsArrange(); + // Make that an overall update. m_iDirtyCount++; stabilizeForm(); @@ -1347,7 +1413,7 @@ if (pChannelStrip == NULL) return; - qsamplerChannel *pChannel = pChannelStrip->channel(); + Channel *pChannel = pChannelStrip->channel(); if (pChannel == NULL) return; @@ -1905,6 +1971,20 @@ } #endif + updateAllChannelStrips(false); + + // Do we auto-arrange? + if (m_pOptions && m_pOptions->bAutoArrange) + channelsArrange(); + + // Remember to refresh devices and instruments... + if (m_pInstrumentListForm) + m_pInstrumentListForm->refreshInstruments(); + if (m_pDeviceForm) + m_pDeviceForm->refreshDevices(); +} + +void MainForm::updateAllChannelStrips(bool bRemoveDeadStrips) { // Retrieve the current channel list. int *piChannelIDs = ::lscp_list_channels(m_pClient); if (piChannelIDs == NULL) { @@ -1919,22 +1999,34 @@ for (int iChannel = 0; piChannelIDs[iChannel] >= 0; iChannel++) { // Check if theres already a channel strip for this one... if (!channelStrip(piChannelIDs[iChannel])) - createChannelStrip(new qsamplerChannel(piChannelIDs[iChannel])); + createChannelStrip(new Channel(piChannelIDs[iChannel])); } - m_pWorkspace->setUpdatesEnabled(true); - } - // Do we auto-arrange? - if (m_pOptions && m_pOptions->bAutoArrange) - channelsArrange(); + // Do we auto-arrange? + if (m_pOptions && m_pOptions->bAutoArrange) + channelsArrange(); - // Remember to refresh devices and instruments... - if (m_pInstrumentListForm) - m_pInstrumentListForm->refreshInstruments(); - if (m_pDeviceForm) - m_pDeviceForm->refreshDevices(); -} + stabilizeForm(); + // remove dead channel strips + if (bRemoveDeadStrips) { + for (int i = 0; channelStripAt(i); ++i) { + ChannelStrip* pChannelStrip = channelStripAt(i); + bool bExists = false; + for (int j = 0; piChannelIDs[j] >= 0; ++j) { + if (!pChannelStrip->channel()) break; + if (piChannelIDs[j] == pChannelStrip->channel()->channelID()) { + // strip exists, don't touch it + bExists = true; + break; + } + } + if (!bExists) destroyChannelStrip(pChannelStrip); + } + } + m_pWorkspace->setUpdatesEnabled(true); + } +} // Update the recent files list and menu. void MainForm::updateRecentFiles ( const QString& sFilename ) @@ -2171,39 +2263,17 @@ // qsamplerMainForm -- MDI channel strip management. // The channel strip creation executive. -ChannelStrip* MainForm::createChannelStrip(qsamplerChannel* pChannel) +ChannelStrip* MainForm::createChannelStrip ( Channel *pChannel ) { if (m_pClient == NULL || pChannel == NULL) return NULL; - // Prepare for auto-arrange? - ChannelStrip* pChannelStrip = NULL; - int y = 0; - if (m_pOptions && m_pOptions->bAutoArrange) { - QWidgetList wlist = m_pWorkspace->windowList(); - for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) { - pChannelStrip = static_cast (wlist.at(iChannel)); - if (pChannelStrip) { - // y += pChannelStrip->height() - // + pChannelStrip->parentWidget()->baseSize().height(); - y += pChannelStrip->parentWidget()->frameGeometry().height(); - } - } - } - // Add a new channel itema... - pChannelStrip = new ChannelStrip(); + ChannelStrip *pChannelStrip = new ChannelStrip(); if (pChannelStrip == NULL) return NULL; - m_pWorkspace->addWindow(pChannelStrip, Qt::FramelessWindowHint); - - // Actual channel strip setup... - pChannelStrip->setup(pChannel); - QObject::connect(pChannelStrip, - SIGNAL(channelChanged(ChannelStrip*)), - SLOT(channelStripChanged(ChannelStrip*))); - // Set some initial aesthetic options... + // Set some initial channel strip options... if (m_pOptions) { // Background display effect... pChannelStrip->setDisplayEffect(m_pOptions->bDisplayEffect); @@ -2215,16 +2285,18 @@ pChannelStrip->setMaxVolume(m_pOptions->iMaxVolume); } + // Add it to workspace... + m_pWorkspace->addWindow(pChannelStrip, Qt::FramelessWindowHint); + + // Actual channel strip setup... + pChannelStrip->setup(pChannel); + + QObject::connect(pChannelStrip, + SIGNAL(channelChanged(ChannelStrip*)), + SLOT(channelStripChanged(ChannelStrip*))); + // Now we show up us to the world. pChannelStrip->show(); - // Only then, we'll auto-arrange... - if (m_pOptions && m_pOptions->bAutoArrange) { - int iWidth = m_pWorkspace->width(); - // int iHeight = pChannel->height() - // + pChannel->parentWidget()->baseSize().height(); - int iHeight = pChannelStrip->parentWidget()->frameGeometry().height(); - pChannelStrip->parentWidget()->setGeometry(0, y, iWidth, iHeight); - } // This is pretty new, so we'll watch for it closely. channelStripChanged(pChannelStrip); @@ -2233,6 +2305,16 @@ return pChannelStrip; } +void MainForm::destroyChannelStrip(ChannelStrip* pChannelStrip) { + // Just delete the channel strip. + delete pChannelStrip; + + // Do we auto-arrange? + if (m_pOptions && m_pOptions->bAutoArrange) + channelsArrange(); + + stabilizeForm(); +} // Retrieve the active channel strip. ChannelStrip* MainForm::activeChannelStrip (void) @@ -2244,11 +2326,16 @@ // Retrieve a channel strip by index. ChannelStrip* MainForm::channelStripAt ( int iChannel ) { + if (!m_pWorkspace) return NULL; + QWidgetList wlist = m_pWorkspace->windowList(); if (wlist.isEmpty()) return NULL; - return static_cast (wlist.at(iChannel)); + if (iChannel < 0 || iChannel >= wlist.size()) + return NULL; + + return dynamic_cast (wlist.at(iChannel)); } @@ -2263,7 +2350,7 @@ ChannelStrip* pChannelStrip = static_cast (wlist.at(iChannel)); if (pChannelStrip) { - qsamplerChannel *pChannel = pChannelStrip->channel(); + Channel *pChannel = pChannelStrip->channel(); if (pChannel && pChannel->channelID() == iChannelID) return pChannelStrip; } @@ -2404,7 +2491,7 @@ switch (QMessageBox::warning(this, QSAMPLER_TITLE ": " + tr("Warning"), tr("Could not start the LinuxSampler server.\n\n" - "Maybe it ss already started."), + "Maybe it is already started."), tr("Stop"), tr("Kill"), tr("Cancel"))) { case 0: m_pServer->terminate(); @@ -2424,12 +2511,14 @@ return; // OK. Let's build the startup process... - m_pServer = new QProcess(this); + m_pServer = new QProcess(); + bForceServerStop = true; // Setup stdout/stderr capture... // if (m_pOptions->bStdoutCapture) { - //m_pServer->setProcessChannelMode( - // QProcess::StandardOutput); +#if QT_VERSION >= 0x040200 + m_pServer->setProcessChannelMode(QProcess::ForwardedChannels); +#endif QObject::connect(m_pServer, SIGNAL(readyReadStandardOutput()), SLOT(readServerStdout())); @@ -2440,7 +2529,7 @@ // The unforgiveable signal communication... QObject::connect(m_pServer, - SIGNAL(finished(int,QProcess::ExitStatus)), + SIGNAL(finished(int, QProcess::ExitStatus)), SLOT(processServerExit())); // Build process arguments... @@ -2471,26 +2560,46 @@ // Stop linuxsampler server... -void MainForm::stopServer (void) +void MainForm::stopServer (bool bInteractive) { // Stop client code. stopClient(); + if (m_pServer && bInteractive) { + if (QMessageBox::question(this, + QSAMPLER_TITLE ": " + tr("The backend's fate ..."), + tr("You have the option to keep the sampler backend (LinuxSampler)\n" + "running in the background. The sampler would continue to work\n" + "according to your current sampler session and you could alter the\n" + "sampler session at any time by relaunching QSampler.\n\n" + "Do you want LinuxSampler to stop or to keep running in\n" + "the background?"), + tr("Stop"), tr("Keep Running")) == 1) + { + bForceServerStop = false; + } + } + // And try to stop server. - if (m_pServer) { + if (m_pServer && bForceServerStop) { appendMessages(tr("Server is stopping...")); - if (m_pServer->state() == QProcess::Running) + if (m_pServer->state() == QProcess::Running) { +#if defined(WIN32) + // Try harder... + m_pServer->kill(); +#else + // Try softly... m_pServer->terminate(); - } +#endif + } + } // Do final processing anyway. + else processServerExit(); // Give it some time to terminate gracefully and stabilize... QTime t; t.start(); while (t.elapsed() < QSAMPLER_TIMER_MSECS) QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); - - // Do final processing anyway. - processServerExit(); } @@ -2512,15 +2621,21 @@ if (m_pMessages) m_pMessages->flushStdoutBuffer(); - if (m_pServer) { + if (m_pServer && bForceServerStop) { + if (m_pServer->state() != QProcess::NotRunning) { + appendMessages(tr("Server is being forced...")); + // Force final server shutdown... + m_pServer->kill(); + // Give it some time to terminate gracefully and stabilize... + QTime t; + t.start(); + while (t.elapsed() < QSAMPLER_TIMER_MSECS) + QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); + } // Force final server shutdown... appendMessages( tr("Server was stopped with exit status %1.") .arg(m_pServer->exitStatus())); - m_pServer->terminate(); - if (!m_pServer->waitForFinished(2000)) - m_pServer->kill(); - // Destroy it. delete m_pServer; m_pServer = NULL; } @@ -2545,7 +2660,7 @@ // as this is run under some other thread context. // A custom event must be posted here... QApplication::postEvent(pMainForm, - new qsamplerCustomEvent(event, pchData, cchData)); + new CustomEvent(event, pchData, cchData)); return LSCP_OK; } @@ -2590,8 +2705,33 @@ .arg(::lscp_client_get_timeout(m_pClient))); // Subscribe to channel info change notifications... + if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_CHANNEL_COUNT) != LSCP_OK) + appendMessagesClient("lscp_client_subscribe(CHANNEL_COUNT)"); if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_CHANNEL_INFO) != LSCP_OK) - appendMessagesClient("lscp_client_subscribe"); + appendMessagesClient("lscp_client_subscribe(CHANNEL_INFO)"); + + DeviceStatusForm::onDevicesChanged(); // initialize + updateViewMidiDeviceStatusMenu(); + if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_MIDI_INPUT_DEVICE_COUNT) != LSCP_OK) + appendMessagesClient("lscp_client_subscribe(MIDI_INPUT_DEVICE_COUNT)"); + if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_MIDI_INPUT_DEVICE_INFO) != LSCP_OK) + appendMessagesClient("lscp_client_subscribe(MIDI_INPUT_DEVICE_INFO)"); + if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_AUDIO_OUTPUT_DEVICE_COUNT) != LSCP_OK) + appendMessagesClient("lscp_client_subscribe(AUDIO_OUTPUT_DEVICE_COUNT)"); + if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_AUDIO_OUTPUT_DEVICE_INFO) != LSCP_OK) + appendMessagesClient("lscp_client_subscribe(AUDIO_OUTPUT_DEVICE_INFO)"); + +#if CONFIG_LSCP_CHANNEL_MIDI + // Subscribe to channel MIDI data notifications... + if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_CHANNEL_MIDI) != LSCP_OK) + appendMessagesClient("lscp_client_subscribe(CHANNEL_MIDI)"); +#endif + +#if CONFIG_LSCP_DEVICE_MIDI + // Subscribe to channel MIDI data notifications... + if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_DEVICE_MIDI) != LSCP_OK) + appendMessagesClient("lscp_client_subscribe(DEVICE_MIDI)"); +#endif // We may stop scheduling around. stopSchedule(); @@ -2645,7 +2785,18 @@ closeSession(false); // Close us as a client... +#if CONFIG_LSCP_DEVICE_MIDI + ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_DEVICE_MIDI); +#endif +#if CONFIG_LSCP_CHANNEL_MIDI + ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_CHANNEL_MIDI); +#endif + ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_AUDIO_OUTPUT_DEVICE_INFO); + ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_AUDIO_OUTPUT_DEVICE_COUNT); + ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_MIDI_INPUT_DEVICE_INFO); + ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_MIDI_INPUT_DEVICE_COUNT); ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_CHANNEL_INFO); + ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_CHANNEL_COUNT); ::lscp_client_destroy(m_pClient); m_pClient = NULL;