/[svn]/qsampler/trunk/src/qsamplerMainForm.cpp
ViewVC logotype

Contents of /qsampler/trunk/src/qsamplerMainForm.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2108 - (show annotations) (download)
Thu Jul 15 08:03:32 2010 UTC (13 years, 8 months ago) by capela
File size: 82057 byte(s)
* Sampler channel and instrument file requester support for
  other than GIG instrument files (*gig *.dls) has been added,
  now also allowing for SFZ instrument files (*.sfz) loading.

1 // qsamplerMainForm.cpp
2 //
3 /****************************************************************************
4 Copyright (C) 2004-2010, rncbc aka Rui Nuno Capela. All rights reserved.
5 Copyright (C) 2007, 2008 Christian Schoenebeck
6
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License
9 as published by the Free Software Foundation; either version 2
10 of the License, or (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License along
18 with this program; if not, write to the Free Software Foundation, Inc.,
19 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
21 *****************************************************************************/
22
23 #include "qsamplerAbout.h"
24 #include "qsamplerMainForm.h"
25
26 #include "qsamplerOptions.h"
27 #include "qsamplerChannel.h"
28 #include "qsamplerMessages.h"
29
30 #include "qsamplerChannelStrip.h"
31 #include "qsamplerInstrumentList.h"
32
33 #include "qsamplerInstrumentListForm.h"
34 #include "qsamplerDeviceForm.h"
35 #include "qsamplerOptionsForm.h"
36 #include "qsamplerDeviceStatusForm.h"
37
38 #include <QApplication>
39 #include <QWorkspace>
40 #include <QProcess>
41 #include <QMessageBox>
42
43 #include <QRegExp>
44 #include <QTextStream>
45 #include <QFileDialog>
46 #include <QFileInfo>
47 #include <QFile>
48 #include <QUrl>
49
50 #include <QDragEnterEvent>
51
52 #include <QStatusBar>
53 #include <QSpinBox>
54 #include <QSlider>
55 #include <QLabel>
56 #include <QTimer>
57 #include <QDateTime>
58
59 #if QT_VERSION < 0x040500
60 namespace Qt {
61 const WindowFlags WindowCloseButtonHint = WindowFlags(0x08000000);
62 #if QT_VERSION < 0x040200
63 const WindowFlags CustomizeWindowHint = WindowFlags(0x02000000);
64 #endif
65 }
66 #endif
67
68 #ifdef HAVE_SIGNAL_H
69 #include <signal.h>
70 #endif
71
72 #ifdef CONFIG_LIBGIG
73 #include <gig.h>
74 #endif
75
76 // Needed for lroundf()
77 #include <math.h>
78
79 #ifndef CONFIG_ROUND
80 static inline long lroundf ( float x )
81 {
82 if (x >= 0.0f)
83 return long(x + 0.5f);
84 else
85 return long(x - 0.5f);
86 }
87 #endif
88
89
90 // All winsock apps needs this.
91 #if defined(WIN32)
92 static WSADATA _wsaData;
93 #endif
94
95
96 namespace QSampler {
97
98 // Timer constant stuff.
99 #define QSAMPLER_TIMER_MSECS 200
100
101 // Status bar item indexes
102 #define QSAMPLER_STATUS_CLIENT 0 // Client connection state.
103 #define QSAMPLER_STATUS_SERVER 1 // Currenr server address (host:port)
104 #define QSAMPLER_STATUS_CHANNEL 2 // Active channel caption.
105 #define QSAMPLER_STATUS_SESSION 3 // Current session modification state.
106
107
108 // Specialties for thread-callback comunication.
109 #define QSAMPLER_LSCP_EVENT QEvent::Type(QEvent::User + 1)
110 #define QSAMPLER_SAVE_EVENT QEvent::Type(QEvent::User + 2)
111
112
113 //-------------------------------------------------------------------------
114 // LscpEvent -- specialty for LSCP callback comunication.
115
116
117 class LscpEvent : public QEvent
118 {
119 public:
120
121 // Constructor.
122 LscpEvent(lscp_event_t event, const char *pchData, int cchData)
123 : QEvent(QSAMPLER_LSCP_EVENT)
124 {
125 m_event = event;
126 m_data = QString::fromUtf8(pchData, cchData);
127 }
128
129 // Accessors.
130 lscp_event_t event() { return m_event; }
131 QString& data() { return m_data; }
132
133 private:
134
135 // The proper event type.
136 lscp_event_t m_event;
137 // The event data as a string.
138 QString m_data;
139 };
140
141
142 //-------------------------------------------------------------------------
143 // LADISH Level 1 support stuff.
144
145 void qsampler_on_sigusr1 ( int /*signo*/ )
146 {
147 QApplication::postEvent(
148 MainForm::getInstance(),
149 new QEvent(QSAMPLER_SAVE_EVENT));
150 }
151
152
153 //-------------------------------------------------------------------------
154 // qsamplerMainForm -- Main window form implementation.
155
156 // Kind of singleton reference.
157 MainForm* MainForm::g_pMainForm = NULL;
158
159 MainForm::MainForm ( QWidget *pParent )
160 : QMainWindow(pParent)
161 {
162 m_ui.setupUi(this);
163
164 // Pseudo-singleton reference setup.
165 g_pMainForm = this;
166
167 // Initialize some pointer references.
168 m_pOptions = NULL;
169
170 // All child forms are to be created later, not earlier than setup.
171 m_pMessages = NULL;
172 m_pInstrumentListForm = NULL;
173 m_pDeviceForm = NULL;
174
175 // We'll start clean.
176 m_iUntitled = 0;
177 m_iDirtyCount = 0;
178
179 m_pServer = NULL;
180 m_pClient = NULL;
181
182 m_iStartDelay = 0;
183 m_iTimerDelay = 0;
184
185 m_iTimerSlot = 0;
186
187 #ifdef HAVE_SIGNAL_H
188 // Set to ignore any fatal "Broken pipe" signals.
189 ::signal(SIGPIPE, SIG_IGN);
190 // LADISH Level 1 suport.
191 ::signal(SIGUSR1, qsampler_on_sigusr1);
192 #endif
193
194 #ifdef CONFIG_VOLUME
195 // Make some extras into the toolbar...
196 const QString& sVolumeText = tr("Master volume");
197 m_iVolumeChanging = 0;
198 // Volume slider...
199 m_ui.channelsToolbar->addSeparator();
200 m_pVolumeSlider = new QSlider(Qt::Horizontal, m_ui.channelsToolbar);
201 m_pVolumeSlider->setTickPosition(QSlider::TicksBothSides);
202 m_pVolumeSlider->setTickInterval(10);
203 m_pVolumeSlider->setPageStep(10);
204 m_pVolumeSlider->setSingleStep(10);
205 m_pVolumeSlider->setMinimum(0);
206 m_pVolumeSlider->setMaximum(100);
207 m_pVolumeSlider->setMaximumHeight(26);
208 m_pVolumeSlider->setMinimumWidth(160);
209 m_pVolumeSlider->setToolTip(sVolumeText);
210 QObject::connect(m_pVolumeSlider,
211 SIGNAL(valueChanged(int)),
212 SLOT(volumeChanged(int)));
213 //m_ui.channelsToolbar->setHorizontallyStretchable(true);
214 //m_ui.channelsToolbar->setStretchableWidget(m_pVolumeSlider);
215 m_ui.channelsToolbar->addWidget(m_pVolumeSlider);
216 // Volume spin-box
217 m_ui.channelsToolbar->addSeparator();
218 m_pVolumeSpinBox = new QSpinBox(m_ui.channelsToolbar);
219 m_pVolumeSpinBox->setMaximumHeight(24);
220 m_pVolumeSpinBox->setSuffix(" %");
221 m_pVolumeSpinBox->setMinimum(0);
222 m_pVolumeSpinBox->setMaximum(100);
223 m_pVolumeSpinBox->setToolTip(sVolumeText);
224 QObject::connect(m_pVolumeSpinBox,
225 SIGNAL(valueChanged(int)),
226 SLOT(volumeChanged(int)));
227 m_ui.channelsToolbar->addWidget(m_pVolumeSpinBox);
228 #endif
229
230 // Make it an MDI workspace.
231 m_pWorkspace = new QWorkspace(this);
232 m_pWorkspace->setScrollBarsEnabled(true);
233 // Set the activation connection.
234 QObject::connect(m_pWorkspace,
235 SIGNAL(windowActivated(QWidget *)),
236 SLOT(activateStrip(QWidget *)));
237 // Make it shine :-)
238 setCentralWidget(m_pWorkspace);
239
240 // Create some statusbar labels...
241 QLabel *pLabel;
242 // Client status.
243 pLabel = new QLabel(tr("Connected"), this);
244 pLabel->setAlignment(Qt::AlignLeft);
245 pLabel->setMinimumSize(pLabel->sizeHint());
246 m_statusItem[QSAMPLER_STATUS_CLIENT] = pLabel;
247 statusBar()->addWidget(pLabel);
248 // Server address.
249 pLabel = new QLabel(this);
250 pLabel->setAlignment(Qt::AlignLeft);
251 m_statusItem[QSAMPLER_STATUS_SERVER] = pLabel;
252 statusBar()->addWidget(pLabel, 1);
253 // Channel title.
254 pLabel = new QLabel(this);
255 pLabel->setAlignment(Qt::AlignLeft);
256 m_statusItem[QSAMPLER_STATUS_CHANNEL] = pLabel;
257 statusBar()->addWidget(pLabel, 2);
258 // Session modification status.
259 pLabel = new QLabel(tr("MOD"), this);
260 pLabel->setAlignment(Qt::AlignHCenter);
261 pLabel->setMinimumSize(pLabel->sizeHint());
262 m_statusItem[QSAMPLER_STATUS_SESSION] = pLabel;
263 statusBar()->addWidget(pLabel);
264
265 #if defined(WIN32)
266 WSAStartup(MAKEWORD(1, 1), &_wsaData);
267 #endif
268
269 // Some actions surely need those
270 // shortcuts firmly attached...
271 addAction(m_ui.viewMenubarAction);
272 addAction(m_ui.viewToolbarAction);
273
274 QObject::connect(m_ui.fileNewAction,
275 SIGNAL(triggered()),
276 SLOT(fileNew()));
277 QObject::connect(m_ui.fileOpenAction,
278 SIGNAL(triggered()),
279 SLOT(fileOpen()));
280 QObject::connect(m_ui.fileSaveAction,
281 SIGNAL(triggered()),
282 SLOT(fileSave()));
283 QObject::connect(m_ui.fileSaveAsAction,
284 SIGNAL(triggered()),
285 SLOT(fileSaveAs()));
286 QObject::connect(m_ui.fileResetAction,
287 SIGNAL(triggered()),
288 SLOT(fileReset()));
289 QObject::connect(m_ui.fileRestartAction,
290 SIGNAL(triggered()),
291 SLOT(fileRestart()));
292 QObject::connect(m_ui.fileExitAction,
293 SIGNAL(triggered()),
294 SLOT(fileExit()));
295 QObject::connect(m_ui.editAddChannelAction,
296 SIGNAL(triggered()),
297 SLOT(editAddChannel()));
298 QObject::connect(m_ui.editRemoveChannelAction,
299 SIGNAL(triggered()),
300 SLOT(editRemoveChannel()));
301 QObject::connect(m_ui.editSetupChannelAction,
302 SIGNAL(triggered()),
303 SLOT(editSetupChannel()));
304 QObject::connect(m_ui.editEditChannelAction,
305 SIGNAL(triggered()),
306 SLOT(editEditChannel()));
307 QObject::connect(m_ui.editResetChannelAction,
308 SIGNAL(triggered()),
309 SLOT(editResetChannel()));
310 QObject::connect(m_ui.editResetAllChannelsAction,
311 SIGNAL(triggered()),
312 SLOT(editResetAllChannels()));
313 QObject::connect(m_ui.viewMenubarAction,
314 SIGNAL(toggled(bool)),
315 SLOT(viewMenubar(bool)));
316 QObject::connect(m_ui.viewToolbarAction,
317 SIGNAL(toggled(bool)),
318 SLOT(viewToolbar(bool)));
319 QObject::connect(m_ui.viewStatusbarAction,
320 SIGNAL(toggled(bool)),
321 SLOT(viewStatusbar(bool)));
322 QObject::connect(m_ui.viewMessagesAction,
323 SIGNAL(toggled(bool)),
324 SLOT(viewMessages(bool)));
325 QObject::connect(m_ui.viewInstrumentsAction,
326 SIGNAL(triggered()),
327 SLOT(viewInstruments()));
328 QObject::connect(m_ui.viewDevicesAction,
329 SIGNAL(triggered()),
330 SLOT(viewDevices()));
331 QObject::connect(m_ui.viewOptionsAction,
332 SIGNAL(triggered()),
333 SLOT(viewOptions()));
334 QObject::connect(m_ui.channelsArrangeAction,
335 SIGNAL(triggered()),
336 SLOT(channelsArrange()));
337 QObject::connect(m_ui.channelsAutoArrangeAction,
338 SIGNAL(toggled(bool)),
339 SLOT(channelsAutoArrange(bool)));
340 QObject::connect(m_ui.helpAboutAction,
341 SIGNAL(triggered()),
342 SLOT(helpAbout()));
343 QObject::connect(m_ui.helpAboutQtAction,
344 SIGNAL(triggered()),
345 SLOT(helpAboutQt()));
346
347 QObject::connect(m_ui.fileMenu,
348 SIGNAL(aboutToShow()),
349 SLOT(updateRecentFilesMenu()));
350 QObject::connect(m_ui.channelsMenu,
351 SIGNAL(aboutToShow()),
352 SLOT(channelsMenuAboutToShow()));
353 }
354
355 // Destructor.
356 MainForm::~MainForm()
357 {
358 // Do final processing anyway.
359 processServerExit();
360
361 #if defined(WIN32)
362 WSACleanup();
363 #endif
364
365 // Finally drop any widgets around...
366 if (m_pDeviceForm)
367 delete m_pDeviceForm;
368 if (m_pInstrumentListForm)
369 delete m_pInstrumentListForm;
370 if (m_pMessages)
371 delete m_pMessages;
372 if (m_pWorkspace)
373 delete m_pWorkspace;
374
375 // Delete status item labels one by one.
376 if (m_statusItem[QSAMPLER_STATUS_CLIENT])
377 delete m_statusItem[QSAMPLER_STATUS_CLIENT];
378 if (m_statusItem[QSAMPLER_STATUS_SERVER])
379 delete m_statusItem[QSAMPLER_STATUS_SERVER];
380 if (m_statusItem[QSAMPLER_STATUS_CHANNEL])
381 delete m_statusItem[QSAMPLER_STATUS_CHANNEL];
382 if (m_statusItem[QSAMPLER_STATUS_SESSION])
383 delete m_statusItem[QSAMPLER_STATUS_SESSION];
384
385 #ifdef CONFIG_VOLUME
386 delete m_pVolumeSpinBox;
387 delete m_pVolumeSlider;
388 #endif
389
390 // Pseudo-singleton reference shut-down.
391 g_pMainForm = NULL;
392 }
393
394
395 // Make and set a proper setup options step.
396 void MainForm::setup ( Options *pOptions )
397 {
398 // We got options?
399 m_pOptions = pOptions;
400
401 // What style do we create these forms?
402 Qt::WindowFlags wflags = Qt::Window
403 | Qt::CustomizeWindowHint
404 | Qt::WindowTitleHint
405 | Qt::WindowSystemMenuHint
406 | Qt::WindowMinMaxButtonsHint
407 | Qt::WindowCloseButtonHint;
408 if (m_pOptions->bKeepOnTop)
409 wflags |= Qt::Tool;
410
411 // Some child forms are to be created right now.
412 m_pMessages = new Messages(this);
413 m_pDeviceForm = new DeviceForm(this, wflags);
414 #ifdef CONFIG_MIDI_INSTRUMENT
415 m_pInstrumentListForm = new InstrumentListForm(this, wflags);
416 #else
417 m_ui.viewInstrumentsAction->setEnabled(false);
418 #endif
419
420 // Setup messages logging appropriately...
421 m_pMessages->setLogging(
422 m_pOptions->bMessagesLog,
423 m_pOptions->sMessagesLogPath);
424
425 // Set message defaults...
426 updateMessagesFont();
427 updateMessagesLimit();
428 updateMessagesCapture();
429 // Set the visibility signal.
430 QObject::connect(m_pMessages,
431 SIGNAL(visibilityChanged(bool)),
432 SLOT(stabilizeForm()));
433
434 // Initial decorations toggle state.
435 m_ui.viewMenubarAction->setChecked(m_pOptions->bMenubar);
436 m_ui.viewToolbarAction->setChecked(m_pOptions->bToolbar);
437 m_ui.viewStatusbarAction->setChecked(m_pOptions->bStatusbar);
438 m_ui.channelsAutoArrangeAction->setChecked(m_pOptions->bAutoArrange);
439
440 // Initial decorations visibility state.
441 viewMenubar(m_pOptions->bMenubar);
442 viewToolbar(m_pOptions->bToolbar);
443 viewStatusbar(m_pOptions->bStatusbar);
444
445 addDockWidget(Qt::BottomDockWidgetArea, m_pMessages);
446
447 // Restore whole dock windows state.
448 QByteArray aDockables = m_pOptions->settings().value(
449 "/Layout/DockWindows").toByteArray();
450 if (!aDockables.isEmpty()) {
451 restoreState(aDockables);
452 }
453
454 // Try to restore old window positioning and initial visibility.
455 m_pOptions->loadWidgetGeometry(this, true);
456 m_pOptions->loadWidgetGeometry(m_pInstrumentListForm);
457 m_pOptions->loadWidgetGeometry(m_pDeviceForm);
458
459 // Final startup stabilization...
460 updateMaxVolume();
461 updateRecentFilesMenu();
462 stabilizeForm();
463
464 // Make it ready :-)
465 statusBar()->showMessage(tr("Ready"), 3000);
466
467 // We'll try to start immediately...
468 startSchedule(0);
469
470 // Register the first timer slot.
471 QTimer::singleShot(QSAMPLER_TIMER_MSECS, this, SLOT(timerSlot()));
472 }
473
474
475 // Window close event handlers.
476 bool MainForm::queryClose (void)
477 {
478 bool bQueryClose = closeSession(false);
479
480 // Try to save current general state...
481 if (m_pOptions) {
482 // Some windows default fonts is here on demand too.
483 if (bQueryClose && m_pMessages)
484 m_pOptions->sMessagesFont = m_pMessages->messagesFont().toString();
485 // Try to save current positioning.
486 if (bQueryClose) {
487 // Save decorations state.
488 m_pOptions->bMenubar = m_ui.MenuBar->isVisible();
489 m_pOptions->bToolbar = (m_ui.fileToolbar->isVisible()
490 || m_ui.editToolbar->isVisible()
491 || m_ui.channelsToolbar->isVisible());
492 m_pOptions->bStatusbar = statusBar()->isVisible();
493 // Save the dock windows state.
494 const QString sDockables = saveState().toBase64().data();
495 m_pOptions->settings().setValue("/Layout/DockWindows", saveState());
496 // And the children, and the main windows state,.
497 m_pOptions->saveWidgetGeometry(m_pDeviceForm);
498 m_pOptions->saveWidgetGeometry(m_pInstrumentListForm);
499 m_pOptions->saveWidgetGeometry(this, true);
500 // Close popup widgets.
501 if (m_pInstrumentListForm)
502 m_pInstrumentListForm->close();
503 if (m_pDeviceForm)
504 m_pDeviceForm->close();
505 // Stop client and/or server, gracefully.
506 stopServer(true /*interactive*/);
507 }
508 }
509
510 return bQueryClose;
511 }
512
513
514 void MainForm::closeEvent ( QCloseEvent *pCloseEvent )
515 {
516 if (queryClose()) {
517 DeviceStatusForm::deleteAllInstances();
518 pCloseEvent->accept();
519 } else
520 pCloseEvent->ignore();
521 }
522
523
524 // Window drag-n-drop event handlers.
525 void MainForm::dragEnterEvent ( QDragEnterEvent* pDragEnterEvent )
526 {
527 // Accept external drags only...
528 if (pDragEnterEvent->source() == NULL
529 && pDragEnterEvent->mimeData()->hasUrls()) {
530 pDragEnterEvent->accept();
531 } else {
532 pDragEnterEvent->ignore();
533 }
534 }
535
536
537 void MainForm::dropEvent ( QDropEvent* pDropEvent )
538 {
539 // Accept externally originated drops only...
540 if (pDropEvent->source())
541 return;
542
543 const QMimeData *pMimeData = pDropEvent->mimeData();
544 if (pMimeData->hasUrls()) {
545 QListIterator<QUrl> iter(pMimeData->urls());
546 while (iter.hasNext()) {
547 const QString& sPath = iter.next().toLocalFile();
548 // if (Channel::isDlsInstrumentFile(sPath)) {
549 if (QFileInfo(sPath).exists()) {
550 // Try to create a new channel from instrument file...
551 Channel *pChannel = new Channel();
552 if (pChannel == NULL)
553 return;
554 // Start setting the instrument filename...
555 pChannel->setInstrument(sPath, 0);
556 // Before we show it up, may be we'll
557 // better ask for some initial values?
558 if (!pChannel->channelSetup(this)) {
559 delete pChannel;
560 return;
561 }
562 // Finally, give it to a new channel strip...
563 if (!createChannelStrip(pChannel)) {
564 delete pChannel;
565 return;
566 }
567 // Make that an overall update.
568 m_iDirtyCount++;
569 stabilizeForm();
570 } // Otherwise, load an usual session file (LSCP script)...
571 else if (closeSession(true)) {
572 loadSessionFile(sPath);
573 break;
574 }
575 }
576 // Make it look responsive...:)
577 QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
578 }
579 }
580
581
582 // Custome event handler.
583 void MainForm::customEvent ( QEvent* pEvent )
584 {
585 // For the time being, just pump it to messages.
586 if (pEvent->type() == QSAMPLER_LSCP_EVENT) {
587 LscpEvent *pLscpEvent = static_cast<LscpEvent *> (pEvent);
588 switch (pLscpEvent->event()) {
589 case LSCP_EVENT_CHANNEL_COUNT:
590 updateAllChannelStrips(true);
591 break;
592 case LSCP_EVENT_CHANNEL_INFO: {
593 int iChannelID = pLscpEvent->data().toInt();
594 ChannelStrip *pChannelStrip = channelStrip(iChannelID);
595 if (pChannelStrip)
596 channelStripChanged(pChannelStrip);
597 break;
598 }
599 case LSCP_EVENT_MIDI_INPUT_DEVICE_COUNT:
600 if (m_pDeviceForm) m_pDeviceForm->refreshDevices();
601 DeviceStatusForm::onDevicesChanged();
602 updateViewMidiDeviceStatusMenu();
603 break;
604 case LSCP_EVENT_MIDI_INPUT_DEVICE_INFO: {
605 if (m_pDeviceForm) m_pDeviceForm->refreshDevices();
606 const int iDeviceID = pLscpEvent->data().section(' ', 0, 0).toInt();
607 DeviceStatusForm::onDeviceChanged(iDeviceID);
608 break;
609 }
610 case LSCP_EVENT_AUDIO_OUTPUT_DEVICE_COUNT:
611 if (m_pDeviceForm) m_pDeviceForm->refreshDevices();
612 break;
613 case LSCP_EVENT_AUDIO_OUTPUT_DEVICE_INFO:
614 if (m_pDeviceForm) m_pDeviceForm->refreshDevices();
615 break;
616 #if CONFIG_EVENT_CHANNEL_MIDI
617 case LSCP_EVENT_CHANNEL_MIDI: {
618 const int iChannelID = pLscpEvent->data().section(' ', 0, 0).toInt();
619 ChannelStrip *pChannelStrip = channelStrip(iChannelID);
620 if (pChannelStrip)
621 pChannelStrip->midiActivityLedOn();
622 break;
623 }
624 #endif
625 #if CONFIG_EVENT_DEVICE_MIDI
626 case LSCP_EVENT_DEVICE_MIDI: {
627 const int iDeviceID = pLscpEvent->data().section(' ', 0, 0).toInt();
628 const int iPortID = pLscpEvent->data().section(' ', 1, 1).toInt();
629 DeviceStatusForm *pDeviceStatusForm
630 = DeviceStatusForm::getInstance(iDeviceID);
631 if (pDeviceStatusForm)
632 pDeviceStatusForm->midiArrived(iPortID);
633 break;
634 }
635 #endif
636 default:
637 appendMessagesColor(tr("LSCP Event: %1 data: %2")
638 .arg(::lscp_event_to_text(pLscpEvent->event()))
639 .arg(pLscpEvent->data()), "#996699");
640 }
641 } // LADISH1 Level 1 support...
642 else if (pEvent->type() == QSAMPLER_SAVE_EVENT)
643 saveSession(false);
644 }
645
646
647 void MainForm::updateViewMidiDeviceStatusMenu (void)
648 {
649 m_ui.viewMidiDeviceStatusMenu->clear();
650 const std::map<int, DeviceStatusForm *> statusForms
651 = DeviceStatusForm::getInstances();
652 std::map<int, DeviceStatusForm *>::const_iterator iter
653 = statusForms.begin();
654 for ( ; iter != statusForms.end(); ++iter) {
655 DeviceStatusForm *pStatusForm = iter->second;
656 m_ui.viewMidiDeviceStatusMenu->addAction(
657 pStatusForm->visibleAction());
658 }
659 }
660
661
662 // Context menu event handler.
663 void MainForm::contextMenuEvent( QContextMenuEvent *pEvent )
664 {
665 stabilizeForm();
666
667 m_ui.editMenu->exec(pEvent->globalPos());
668 }
669
670
671 //-------------------------------------------------------------------------
672 // qsamplerMainForm -- Brainless public property accessors.
673
674 // The global options settings property.
675 Options *MainForm::options (void) const
676 {
677 return m_pOptions;
678 }
679
680
681 // The LSCP client descriptor property.
682 lscp_client_t *MainForm::client (void) const
683 {
684 return m_pClient;
685 }
686
687
688 // The pseudo-singleton instance accessor.
689 MainForm *MainForm::getInstance (void)
690 {
691 return g_pMainForm;
692 }
693
694
695 //-------------------------------------------------------------------------
696 // qsamplerMainForm -- Session file stuff.
697
698 // Format the displayable session filename.
699 QString MainForm::sessionName ( const QString& sFilename )
700 {
701 bool bCompletePath = (m_pOptions && m_pOptions->bCompletePath);
702 QString sSessionName = sFilename;
703 if (sSessionName.isEmpty())
704 sSessionName = tr("Untitled") + QString::number(m_iUntitled);
705 else if (!bCompletePath)
706 sSessionName = QFileInfo(sSessionName).fileName();
707 return sSessionName;
708 }
709
710
711 // Create a new session file from scratch.
712 bool MainForm::newSession (void)
713 {
714 // Check if we can do it.
715 if (!closeSession(true))
716 return false;
717
718 // Give us what the server has, right now...
719 updateSession();
720
721 // Ok increment untitled count.
722 m_iUntitled++;
723
724 // Stabilize form.
725 m_sFilename = QString::null;
726 m_iDirtyCount = 0;
727 appendMessages(tr("New session: \"%1\".").arg(sessionName(m_sFilename)));
728 stabilizeForm();
729
730 return true;
731 }
732
733
734 // Open an existing sampler session.
735 bool MainForm::openSession (void)
736 {
737 if (m_pOptions == NULL)
738 return false;
739
740 // Ask for the filename to open...
741 QString sFilename = QFileDialog::getOpenFileName(this,
742 QSAMPLER_TITLE ": " + tr("Open Session"), // Caption.
743 m_pOptions->sSessionDir, // Start here.
744 tr("LSCP Session files") + " (*.lscp)" // Filter (LSCP files)
745 );
746
747 // Have we cancelled?
748 if (sFilename.isEmpty())
749 return false;
750
751 // Check if we're going to discard safely the current one...
752 if (!closeSession(true))
753 return false;
754
755 // Load it right away.
756 return loadSessionFile(sFilename);
757 }
758
759
760 // Save current sampler session with another name.
761 bool MainForm::saveSession ( bool bPrompt )
762 {
763 if (m_pOptions == NULL)
764 return false;
765
766 QString sFilename = m_sFilename;
767
768 // Ask for the file to save, if there's none...
769 if (bPrompt || sFilename.isEmpty()) {
770 // If none is given, assume default directory.
771 if (sFilename.isEmpty())
772 sFilename = m_pOptions->sSessionDir;
773 // Prompt the guy...
774 sFilename = QFileDialog::getSaveFileName(this,
775 QSAMPLER_TITLE ": " + tr("Save Session"), // Caption.
776 sFilename, // Start here.
777 tr("LSCP Session files") + " (*.lscp)" // Filter (LSCP files)
778 );
779 // Have we cancelled it?
780 if (sFilename.isEmpty())
781 return false;
782 // Enforce .lscp extension...
783 if (QFileInfo(sFilename).suffix().isEmpty())
784 sFilename += ".lscp";
785 // Check if already exists...
786 if (sFilename != m_sFilename && QFileInfo(sFilename).exists()) {
787 if (QMessageBox::warning(this,
788 QSAMPLER_TITLE ": " + tr("Warning"),
789 tr("The file already exists:\n\n"
790 "\"%1\"\n\n"
791 "Do you want to replace it?")
792 .arg(sFilename),
793 QMessageBox::Yes | QMessageBox::No)
794 == QMessageBox::No)
795 return false;
796 }
797 }
798
799 // Save it right away.
800 return saveSessionFile(sFilename);
801 }
802
803
804 // Close current session.
805 bool MainForm::closeSession ( bool bForce )
806 {
807 bool bClose = true;
808
809 // Are we dirty enough to prompt it?
810 if (m_iDirtyCount > 0) {
811 switch (QMessageBox::warning(this,
812 QSAMPLER_TITLE ": " + tr("Warning"),
813 tr("The current session has been changed:\n\n"
814 "\"%1\"\n\n"
815 "Do you want to save the changes?")
816 .arg(sessionName(m_sFilename)),
817 QMessageBox::Save |
818 QMessageBox::Discard |
819 QMessageBox::Cancel)) {
820 case QMessageBox::Save:
821 bClose = saveSession(false);
822 // Fall thru....
823 case QMessageBox::Discard:
824 break;
825 default: // Cancel.
826 bClose = false;
827 break;
828 }
829 }
830
831 // If we may close it, dot it.
832 if (bClose) {
833 // Remove all channel strips from sight...
834 m_pWorkspace->setUpdatesEnabled(false);
835 QWidgetList wlist = m_pWorkspace->windowList();
836 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
837 ChannelStrip *pChannelStrip = (ChannelStrip*) wlist.at(iChannel);
838 if (pChannelStrip) {
839 Channel *pChannel = pChannelStrip->channel();
840 if (bForce && pChannel)
841 pChannel->removeChannel();
842 delete pChannelStrip;
843 }
844 }
845 m_pWorkspace->setUpdatesEnabled(true);
846 // We're now clean, for sure.
847 m_iDirtyCount = 0;
848 }
849
850 return bClose;
851 }
852
853
854 // Load a session from specific file path.
855 bool MainForm::loadSessionFile ( const QString& sFilename )
856 {
857 if (m_pClient == NULL)
858 return false;
859
860 // Open and read from real file.
861 QFile file(sFilename);
862 if (!file.open(QIODevice::ReadOnly)) {
863 appendMessagesError(
864 tr("Could not open \"%1\" session file.\n\nSorry.")
865 .arg(sFilename));
866 return false;
867 }
868
869 // Tell the world we'll take some time...
870 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
871
872 // Read the file.
873 int iLine = 0;
874 int iErrors = 0;
875 QTextStream ts(&file);
876 while (!ts.atEnd()) {
877 // Read the line.
878 QString sCommand = ts.readLine().trimmed();
879 iLine++;
880 // If not empty, nor a comment, call the server...
881 if (!sCommand.isEmpty() && sCommand[0] != '#') {
882 // Remember that, no matter what,
883 // all LSCP commands are CR/LF terminated.
884 sCommand += "\r\n";
885 if (::lscp_client_query(m_pClient, sCommand.toUtf8().constData())
886 != LSCP_OK) {
887 appendMessagesColor(QString("%1(%2): %3")
888 .arg(QFileInfo(sFilename).fileName()).arg(iLine)
889 .arg(sCommand.simplified()), "#996633");
890 appendMessagesClient("lscp_client_query");
891 iErrors++;
892 }
893 }
894 // Try to make it snappy :)
895 QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
896 }
897
898 // Ok. we've read it.
899 file.close();
900
901 // Now we'll try to create (update) the whole GUI session.
902 updateSession();
903
904 // We're fornerly done.
905 QApplication::restoreOverrideCursor();
906
907 // Have we any errors?
908 if (iErrors > 0) {
909 appendMessagesError(
910 tr("Session loaded with errors\nfrom \"%1\".\n\nSorry.")
911 .arg(sFilename));
912 }
913
914 // Save as default session directory.
915 if (m_pOptions)
916 m_pOptions->sSessionDir = QFileInfo(sFilename).dir().absolutePath();
917 // We're not dirty anymore, if loaded without errors,
918 m_iDirtyCount = iErrors;
919 // Stabilize form...
920 m_sFilename = sFilename;
921 updateRecentFiles(sFilename);
922 appendMessages(tr("Open session: \"%1\".").arg(sessionName(m_sFilename)));
923
924 // Make that an overall update.
925 stabilizeForm();
926 return true;
927 }
928
929
930 // Save current session to specific file path.
931 bool MainForm::saveSessionFile ( const QString& sFilename )
932 {
933 if (m_pClient == NULL)
934 return false;
935
936 // Check whether server is apparently OK...
937 if (::lscp_get_channels(m_pClient) < 0) {
938 appendMessagesClient("lscp_get_channels");
939 return false;
940 }
941
942 // Open and write into real file.
943 QFile file(sFilename);
944 if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
945 appendMessagesError(
946 tr("Could not open \"%1\" session file.\n\nSorry.")
947 .arg(sFilename));
948 return false;
949 }
950
951 // Tell the world we'll take some time...
952 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
953
954 // Write the file.
955 int iErrors = 0;
956 QTextStream ts(&file);
957 ts << "# " << QSAMPLER_TITLE " - " << tr(QSAMPLER_SUBTITLE) << endl;
958 ts << "# " << tr("Version")
959 << ": " QSAMPLER_VERSION << endl;
960 ts << "# " << tr("Build")
961 << ": " __DATE__ " " __TIME__ << endl;
962 ts << "#" << endl;
963 ts << "# " << tr("File")
964 << ": " << QFileInfo(sFilename).fileName() << endl;
965 ts << "# " << tr("Date")
966 << ": " << QDate::currentDate().toString("MMM dd yyyy")
967 << " " << QTime::currentTime().toString("hh:mm:ss") << endl;
968 ts << "#" << endl;
969 ts << endl;
970
971 // It is assumed that this new kind of device+session file
972 // will be loaded from a complete initialized server...
973 int *piDeviceIDs;
974 int iDevice;
975 ts << "RESET" << endl;
976
977 // Audio device mapping.
978 QMap<int, int> audioDeviceMap;
979 piDeviceIDs = Device::getDevices(m_pClient, Device::Audio);
980 for (iDevice = 0; piDeviceIDs && piDeviceIDs[iDevice] >= 0; iDevice++) {
981 ts << endl;
982 Device device(Device::Audio, piDeviceIDs[iDevice]);
983 // Audio device specification...
984 ts << "# " << device.deviceTypeName() << " " << device.driverName()
985 << " " << tr("Device") << " " << iDevice << endl;
986 ts << "CREATE AUDIO_OUTPUT_DEVICE " << device.driverName();
987 DeviceParamMap::ConstIterator deviceParam;
988 for (deviceParam = device.params().begin();
989 deviceParam != device.params().end();
990 ++deviceParam) {
991 const DeviceParam& param = deviceParam.value();
992 if (param.value.isEmpty()) ts << "# ";
993 ts << " " << deviceParam.key() << "='" << param.value << "'";
994 }
995 ts << endl;
996 // Audio channel parameters...
997 int iPort = 0;
998 QListIterator<DevicePort *> iter(device.ports());
999 while (iter.hasNext()) {
1000 DevicePort *pPort = iter.next();
1001 DeviceParamMap::ConstIterator portParam;
1002 for (portParam = pPort->params().begin();
1003 portParam != pPort->params().end();
1004 ++portParam) {
1005 const DeviceParam& param = portParam.value();
1006 if (param.fix || param.value.isEmpty()) ts << "# ";
1007 ts << "SET AUDIO_OUTPUT_CHANNEL_PARAMETER " << iDevice
1008 << " " << iPort << " " << portParam.key()
1009 << "='" << param.value << "'" << endl;
1010 }
1011 iPort++;
1012 }
1013 // Audio device index/id mapping.
1014 audioDeviceMap[device.deviceID()] = iDevice;
1015 // Try to keep it snappy :)
1016 QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
1017 }
1018
1019 // MIDI device mapping.
1020 QMap<int, int> midiDeviceMap;
1021 piDeviceIDs = Device::getDevices(m_pClient, Device::Midi);
1022 for (iDevice = 0; piDeviceIDs && piDeviceIDs[iDevice] >= 0; iDevice++) {
1023 ts << endl;
1024 Device device(Device::Midi, piDeviceIDs[iDevice]);
1025 // MIDI device specification...
1026 ts << "# " << device.deviceTypeName() << " " << device.driverName()
1027 << " " << tr("Device") << " " << iDevice << endl;
1028 ts << "CREATE MIDI_INPUT_DEVICE " << device.driverName();
1029 DeviceParamMap::ConstIterator deviceParam;
1030 for (deviceParam = device.params().begin();
1031 deviceParam != device.params().end();
1032 ++deviceParam) {
1033 const DeviceParam& param = deviceParam.value();
1034 if (param.value.isEmpty()) ts << "# ";
1035 ts << " " << deviceParam.key() << "='" << param.value << "'";
1036 }
1037 ts << endl;
1038 // MIDI port parameters...
1039 int iPort = 0;
1040 QListIterator<DevicePort *> iter(device.ports());
1041 while (iter.hasNext()) {
1042 DevicePort *pPort = iter.next();
1043 DeviceParamMap::ConstIterator portParam;
1044 for (portParam = pPort->params().begin();
1045 portParam != pPort->params().end();
1046 ++portParam) {
1047 const DeviceParam& param = portParam.value();
1048 if (param.fix || param.value.isEmpty()) ts << "# ";
1049 ts << "SET MIDI_INPUT_PORT_PARAMETER " << iDevice
1050 << " " << iPort << " " << portParam.key()
1051 << "='" << param.value << "'" << endl;
1052 }
1053 iPort++;
1054 }
1055 // MIDI device index/id mapping.
1056 midiDeviceMap[device.deviceID()] = iDevice;
1057 // Try to keep it snappy :)
1058 QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
1059 }
1060 ts << endl;
1061
1062 #ifdef CONFIG_MIDI_INSTRUMENT
1063 // MIDI instrument mapping...
1064 QMap<int, int> midiInstrumentMap;
1065 int *piMaps = ::lscp_list_midi_instrument_maps(m_pClient);
1066 for (int iMap = 0; piMaps && piMaps[iMap] >= 0; iMap++) {
1067 int iMidiMap = piMaps[iMap];
1068 const char *pszMapName
1069 = ::lscp_get_midi_instrument_map_name(m_pClient, iMidiMap);
1070 ts << "# " << tr("MIDI instrument map") << " " << iMap;
1071 if (pszMapName)
1072 ts << " - " << pszMapName;
1073 ts << endl;
1074 ts << "ADD MIDI_INSTRUMENT_MAP";
1075 if (pszMapName)
1076 ts << " '" << pszMapName << "'";
1077 ts << endl;
1078 // MIDI instrument mapping...
1079 lscp_midi_instrument_t *pInstrs
1080 = ::lscp_list_midi_instruments(m_pClient, iMidiMap);
1081 for (int iInstr = 0; pInstrs && pInstrs[iInstr].map >= 0; iInstr++) {
1082 lscp_midi_instrument_info_t *pInstrInfo
1083 = ::lscp_get_midi_instrument_info(m_pClient, &pInstrs[iInstr]);
1084 if (pInstrInfo) {
1085 ts << "MAP MIDI_INSTRUMENT "
1086 << iMap << " "
1087 << pInstrs[iInstr].bank << " "
1088 << pInstrs[iInstr].prog << " "
1089 << pInstrInfo->engine_name << " '"
1090 << pInstrInfo->instrument_file << "' "
1091 << pInstrInfo->instrument_nr << " "
1092 << pInstrInfo->volume << " ";
1093 switch (pInstrInfo->load_mode) {
1094 case LSCP_LOAD_PERSISTENT:
1095 ts << "PERSISTENT";
1096 break;
1097 case LSCP_LOAD_ON_DEMAND_HOLD:
1098 ts << "ON_DEMAND_HOLD";
1099 break;
1100 case LSCP_LOAD_ON_DEMAND:
1101 case LSCP_LOAD_DEFAULT:
1102 default:
1103 ts << "ON_DEMAND";
1104 break;
1105 }
1106 if (pInstrInfo->name)
1107 ts << " '" << pInstrInfo->name << "'";
1108 ts << endl;
1109 } // Check for errors...
1110 else if (::lscp_client_get_errno(m_pClient)) {
1111 appendMessagesClient("lscp_get_midi_instrument_info");
1112 iErrors++;
1113 }
1114 // Try to keep it snappy :)
1115 QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
1116 }
1117 ts << endl;
1118 // Check for errors...
1119 if (pInstrs == NULL && ::lscp_client_get_errno(m_pClient)) {
1120 appendMessagesClient("lscp_list_midi_instruments");
1121 iErrors++;
1122 }
1123 // MIDI strument index/id mapping.
1124 midiInstrumentMap[iMidiMap] = iMap;
1125 }
1126 // Check for errors...
1127 if (piMaps == NULL && ::lscp_client_get_errno(m_pClient)) {
1128 appendMessagesClient("lscp_list_midi_instrument_maps");
1129 iErrors++;
1130 }
1131 #endif // CONFIG_MIDI_INSTRUMENT
1132
1133 // Sampler channel mapping.
1134 QWidgetList wlist = m_pWorkspace->windowList();
1135 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1136 ChannelStrip* pChannelStrip
1137 = static_cast<ChannelStrip *> (wlist.at(iChannel));
1138 if (pChannelStrip) {
1139 Channel *pChannel = pChannelStrip->channel();
1140 if (pChannel) {
1141 ts << "# " << tr("Channel") << " " << iChannel << endl;
1142 ts << "ADD CHANNEL" << endl;
1143 if (audioDeviceMap.isEmpty()) {
1144 ts << "SET CHANNEL AUDIO_OUTPUT_TYPE " << iChannel
1145 << " " << pChannel->audioDriver() << endl;
1146 } else {
1147 ts << "SET CHANNEL AUDIO_OUTPUT_DEVICE " << iChannel
1148 << " " << audioDeviceMap[pChannel->audioDevice()] << endl;
1149 }
1150 if (midiDeviceMap.isEmpty()) {
1151 ts << "SET CHANNEL MIDI_INPUT_TYPE " << iChannel
1152 << " " << pChannel->midiDriver() << endl;
1153 } else {
1154 ts << "SET CHANNEL MIDI_INPUT_DEVICE " << iChannel
1155 << " " << midiDeviceMap[pChannel->midiDevice()] << endl;
1156 }
1157 ts << "SET CHANNEL MIDI_INPUT_PORT " << iChannel
1158 << " " << pChannel->midiPort() << endl;
1159 ts << "SET CHANNEL MIDI_INPUT_CHANNEL " << iChannel << " ";
1160 if (pChannel->midiChannel() == LSCP_MIDI_CHANNEL_ALL)
1161 ts << "ALL";
1162 else
1163 ts << pChannel->midiChannel();
1164 ts << endl;
1165 ts << "LOAD ENGINE " << pChannel->engineName()
1166 << " " << iChannel << endl;
1167 if (pChannel->instrumentStatus() < 100) ts << "# ";
1168 ts << "LOAD INSTRUMENT NON_MODAL '"
1169 << pChannel->instrumentFile() << "' "
1170 << pChannel->instrumentNr() << " " << iChannel << endl;
1171 ChannelRoutingMap::ConstIterator audioRoute;
1172 for (audioRoute = pChannel->audioRouting().begin();
1173 audioRoute != pChannel->audioRouting().end();
1174 ++audioRoute) {
1175 ts << "SET CHANNEL AUDIO_OUTPUT_CHANNEL " << iChannel
1176 << " " << audioRoute.key()
1177 << " " << audioRoute.value() << endl;
1178 }
1179 ts << "SET CHANNEL VOLUME " << iChannel
1180 << " " << pChannel->volume() << endl;
1181 if (pChannel->channelMute())
1182 ts << "SET CHANNEL MUTE " << iChannel << " 1" << endl;
1183 if (pChannel->channelSolo())
1184 ts << "SET CHANNEL SOLO " << iChannel << " 1" << endl;
1185 #ifdef CONFIG_MIDI_INSTRUMENT
1186 if (pChannel->midiMap() >= 0) {
1187 ts << "SET CHANNEL MIDI_INSTRUMENT_MAP " << iChannel
1188 << " " << midiInstrumentMap[pChannel->midiMap()] << endl;
1189 }
1190 #endif
1191 #ifdef CONFIG_FXSEND
1192 int iChannelID = pChannel->channelID();
1193 int *piFxSends = ::lscp_list_fxsends(m_pClient, iChannelID);
1194 for (int iFxSend = 0;
1195 piFxSends && piFxSends[iFxSend] >= 0;
1196 iFxSend++) {
1197 lscp_fxsend_info_t *pFxSendInfo = ::lscp_get_fxsend_info(
1198 m_pClient, iChannelID, piFxSends[iFxSend]);
1199 if (pFxSendInfo) {
1200 ts << "CREATE FX_SEND " << iChannel
1201 << " " << pFxSendInfo->midi_controller;
1202 if (pFxSendInfo->name)
1203 ts << " '" << pFxSendInfo->name << "'";
1204 ts << endl;
1205 int *piRouting = pFxSendInfo->audio_routing;
1206 for (int iAudioSrc = 0;
1207 piRouting && piRouting[iAudioSrc] >= 0;
1208 iAudioSrc++) {
1209 ts << "SET FX_SEND AUDIO_OUTPUT_CHANNEL "
1210 << iChannel
1211 << " " << iFxSend
1212 << " " << iAudioSrc
1213 << " " << piRouting[iAudioSrc] << endl;
1214 }
1215 #ifdef CONFIG_FXSEND_LEVEL
1216 ts << "SET FX_SEND LEVEL " << iChannel
1217 << " " << iFxSend
1218 << " " << pFxSendInfo->level << endl;
1219 #endif
1220 } // Check for errors...
1221 else if (::lscp_client_get_errno(m_pClient)) {
1222 appendMessagesClient("lscp_get_fxsend_info");
1223 iErrors++;
1224 }
1225 }
1226 #endif
1227 ts << endl;
1228 }
1229 }
1230 // Try to keep it snappy :)
1231 QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
1232 }
1233
1234 #ifdef CONFIG_VOLUME
1235 ts << "# " << tr("Global volume level") << endl;
1236 ts << "SET VOLUME " << ::lscp_get_volume(m_pClient) << endl;
1237 ts << endl;
1238 #endif
1239
1240 // Ok. we've wrote it.
1241 file.close();
1242
1243 // We're fornerly done.
1244 QApplication::restoreOverrideCursor();
1245
1246 // Have we any errors?
1247 if (iErrors > 0) {
1248 appendMessagesError(
1249 tr("Some settings could not be saved\n"
1250 "to \"%1\" session file.\n\nSorry.")
1251 .arg(sFilename));
1252 }
1253
1254 // Save as default session directory.
1255 if (m_pOptions)
1256 m_pOptions->sSessionDir = QFileInfo(sFilename).dir().absolutePath();
1257 // We're not dirty anymore.
1258 m_iDirtyCount = 0;
1259 // Stabilize form...
1260 m_sFilename = sFilename;
1261 updateRecentFiles(sFilename);
1262 appendMessages(tr("Save session: \"%1\".").arg(sessionName(m_sFilename)));
1263 stabilizeForm();
1264 return true;
1265 }
1266
1267
1268 // Session change receiver slot.
1269 void MainForm::sessionDirty (void)
1270 {
1271 // Just mark the dirty form.
1272 m_iDirtyCount++;
1273 // and update the form status...
1274 stabilizeForm();
1275 }
1276
1277
1278 //-------------------------------------------------------------------------
1279 // qsamplerMainForm -- File Action slots.
1280
1281 // Create a new sampler session.
1282 void MainForm::fileNew (void)
1283 {
1284 // Of course we'll start clean new.
1285 newSession();
1286 }
1287
1288
1289 // Open an existing sampler session.
1290 void MainForm::fileOpen (void)
1291 {
1292 // Open it right away.
1293 openSession();
1294 }
1295
1296
1297 // Open a recent file session.
1298 void MainForm::fileOpenRecent (void)
1299 {
1300 // Retrive filename index from action data...
1301 QAction *pAction = qobject_cast<QAction *> (sender());
1302 if (pAction && m_pOptions) {
1303 int iIndex = pAction->data().toInt();
1304 if (iIndex >= 0 && iIndex < m_pOptions->recentFiles.count()) {
1305 QString sFilename = m_pOptions->recentFiles[iIndex];
1306 // Check if we can safely close the current session...
1307 if (!sFilename.isEmpty() && closeSession(true))
1308 loadSessionFile(sFilename);
1309 }
1310 }
1311 }
1312
1313
1314 // Save current sampler session.
1315 void MainForm::fileSave (void)
1316 {
1317 // Save it right away.
1318 saveSession(false);
1319 }
1320
1321
1322 // Save current sampler session with another name.
1323 void MainForm::fileSaveAs (void)
1324 {
1325 // Save it right away, maybe with another name.
1326 saveSession(true);
1327 }
1328
1329
1330 // Reset the sampler instance.
1331 void MainForm::fileReset (void)
1332 {
1333 if (m_pClient == NULL)
1334 return;
1335
1336 // Ask user whether he/she want's an internal sampler reset...
1337 if (QMessageBox::warning(this,
1338 QSAMPLER_TITLE ": " + tr("Warning"),
1339 tr("Resetting the sampler instance will close\n"
1340 "all device and channel configurations.\n\n"
1341 "Please note that this operation may cause\n"
1342 "temporary MIDI and Audio disruption.\n\n"
1343 "Do you want to reset the sampler engine now?"),
1344 QMessageBox::Ok | QMessageBox::Cancel)
1345 == QMessageBox::Cancel)
1346 return;
1347
1348 // Trye closing the current session, first...
1349 if (!closeSession(true))
1350 return;
1351
1352 // Just do the reset, after closing down current session...
1353 // Do the actual sampler reset...
1354 if (::lscp_reset_sampler(m_pClient) != LSCP_OK) {
1355 appendMessagesClient("lscp_reset_sampler");
1356 appendMessagesError(tr("Could not reset sampler instance.\n\nSorry."));
1357 return;
1358 }
1359
1360 // Log this.
1361 appendMessages(tr("Sampler reset."));
1362
1363 // Make it a new session...
1364 newSession();
1365 }
1366
1367
1368 // Restart the client/server instance.
1369 void MainForm::fileRestart (void)
1370 {
1371 if (m_pOptions == NULL)
1372 return;
1373
1374 bool bRestart = true;
1375
1376 // Ask user whether he/she want's a complete restart...
1377 // (if we're currently up and running)
1378 if (bRestart && m_pClient) {
1379 bRestart = (QMessageBox::warning(this,
1380 QSAMPLER_TITLE ": " + tr("Warning"),
1381 tr("New settings will be effective after\n"
1382 "restarting the client/server connection.\n\n"
1383 "Please note that this operation may cause\n"
1384 "temporary MIDI and Audio disruption.\n\n"
1385 "Do you want to restart the connection now?"),
1386 QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok);
1387 }
1388
1389 // Are we still for it?
1390 if (bRestart && closeSession(true)) {
1391 // Stop server, it will force the client too.
1392 stopServer();
1393 // Reschedule a restart...
1394 startSchedule(m_pOptions->iStartDelay);
1395 }
1396 }
1397
1398
1399 // Exit application program.
1400 void MainForm::fileExit (void)
1401 {
1402 // Go for close the whole thing.
1403 close();
1404 }
1405
1406
1407 //-------------------------------------------------------------------------
1408 // qsamplerMainForm -- Edit Action slots.
1409
1410 // Add a new sampler channel.
1411 void MainForm::editAddChannel (void)
1412 {
1413 if (m_pClient == NULL)
1414 return;
1415
1416 // Just create the channel instance...
1417 Channel *pChannel = new Channel();
1418 if (pChannel == NULL)
1419 return;
1420
1421 // Before we show it up, may be we'll
1422 // better ask for some initial values?
1423 if (!pChannel->channelSetup(this)) {
1424 delete pChannel;
1425 return;
1426 }
1427
1428 // And give it to the strip...
1429 // (will own the channel instance, if successful).
1430 if (!createChannelStrip(pChannel)) {
1431 delete pChannel;
1432 return;
1433 }
1434
1435 // Do we auto-arrange?
1436 if (m_pOptions && m_pOptions->bAutoArrange)
1437 channelsArrange();
1438
1439 // Make that an overall update.
1440 m_iDirtyCount++;
1441 stabilizeForm();
1442 }
1443
1444
1445 // Remove current sampler channel.
1446 void MainForm::editRemoveChannel (void)
1447 {
1448 if (m_pClient == NULL)
1449 return;
1450
1451 ChannelStrip* pChannelStrip = activeChannelStrip();
1452 if (pChannelStrip == NULL)
1453 return;
1454
1455 Channel *pChannel = pChannelStrip->channel();
1456 if (pChannel == NULL)
1457 return;
1458
1459 // Prompt user if he/she's sure about this...
1460 if (m_pOptions && m_pOptions->bConfirmRemove) {
1461 if (QMessageBox::warning(this,
1462 QSAMPLER_TITLE ": " + tr("Warning"),
1463 tr("About to remove channel:\n\n"
1464 "%1\n\n"
1465 "Are you sure?")
1466 .arg(pChannelStrip->windowTitle()),
1467 QMessageBox::Ok | QMessageBox::Cancel)
1468 == QMessageBox::Cancel)
1469 return;
1470 }
1471
1472 // Remove the existing sampler channel.
1473 if (!pChannel->removeChannel())
1474 return;
1475
1476 // Just delete the channel strip.
1477 delete pChannelStrip;
1478
1479 // Do we auto-arrange?
1480 if (m_pOptions && m_pOptions->bAutoArrange)
1481 channelsArrange();
1482
1483 // We'll be dirty, for sure...
1484 m_iDirtyCount++;
1485 stabilizeForm();
1486 }
1487
1488
1489 // Setup current sampler channel.
1490 void MainForm::editSetupChannel (void)
1491 {
1492 if (m_pClient == NULL)
1493 return;
1494
1495 ChannelStrip* pChannelStrip = activeChannelStrip();
1496 if (pChannelStrip == NULL)
1497 return;
1498
1499 // Just invoque the channel strip procedure.
1500 pChannelStrip->channelSetup();
1501 }
1502
1503
1504 // Edit current sampler channel.
1505 void MainForm::editEditChannel (void)
1506 {
1507 if (m_pClient == NULL)
1508 return;
1509
1510 ChannelStrip* pChannelStrip = activeChannelStrip();
1511 if (pChannelStrip == NULL)
1512 return;
1513
1514 // Just invoque the channel strip procedure.
1515 pChannelStrip->channelEdit();
1516 }
1517
1518
1519 // Reset current sampler channel.
1520 void MainForm::editResetChannel (void)
1521 {
1522 if (m_pClient == NULL)
1523 return;
1524
1525 ChannelStrip* pChannelStrip = activeChannelStrip();
1526 if (pChannelStrip == NULL)
1527 return;
1528
1529 // Just invoque the channel strip procedure.
1530 pChannelStrip->channelReset();
1531 }
1532
1533
1534 // Reset all sampler channels.
1535 void MainForm::editResetAllChannels (void)
1536 {
1537 if (m_pClient == NULL)
1538 return;
1539
1540 // Invoque the channel strip procedure,
1541 // for all channels out there...
1542 m_pWorkspace->setUpdatesEnabled(false);
1543 QWidgetList wlist = m_pWorkspace->windowList();
1544 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1545 ChannelStrip* pChannelStrip = (ChannelStrip*) wlist.at(iChannel);
1546 if (pChannelStrip)
1547 pChannelStrip->channelReset();
1548 }
1549 m_pWorkspace->setUpdatesEnabled(true);
1550 }
1551
1552
1553 //-------------------------------------------------------------------------
1554 // qsamplerMainForm -- View Action slots.
1555
1556 // Show/hide the main program window menubar.
1557 void MainForm::viewMenubar ( bool bOn )
1558 {
1559 if (bOn)
1560 m_ui.MenuBar->show();
1561 else
1562 m_ui.MenuBar->hide();
1563 }
1564
1565
1566 // Show/hide the main program window toolbar.
1567 void MainForm::viewToolbar ( bool bOn )
1568 {
1569 if (bOn) {
1570 m_ui.fileToolbar->show();
1571 m_ui.editToolbar->show();
1572 m_ui.channelsToolbar->show();
1573 } else {
1574 m_ui.fileToolbar->hide();
1575 m_ui.editToolbar->hide();
1576 m_ui.channelsToolbar->hide();
1577 }
1578 }
1579
1580
1581 // Show/hide the main program window statusbar.
1582 void MainForm::viewStatusbar ( bool bOn )
1583 {
1584 if (bOn)
1585 statusBar()->show();
1586 else
1587 statusBar()->hide();
1588 }
1589
1590
1591 // Show/hide the messages window logger.
1592 void MainForm::viewMessages ( bool bOn )
1593 {
1594 if (bOn)
1595 m_pMessages->show();
1596 else
1597 m_pMessages->hide();
1598 }
1599
1600
1601 // Show/hide the MIDI instrument list-view form.
1602 void MainForm::viewInstruments (void)
1603 {
1604 if (m_pOptions == NULL)
1605 return;
1606
1607 if (m_pInstrumentListForm) {
1608 m_pOptions->saveWidgetGeometry(m_pInstrumentListForm);
1609 if (m_pInstrumentListForm->isVisible()) {
1610 m_pInstrumentListForm->hide();
1611 } else {
1612 m_pInstrumentListForm->show();
1613 m_pInstrumentListForm->raise();
1614 m_pInstrumentListForm->activateWindow();
1615 }
1616 }
1617 }
1618
1619
1620 // Show/hide the device configurator form.
1621 void MainForm::viewDevices (void)
1622 {
1623 if (m_pOptions == NULL)
1624 return;
1625
1626 if (m_pDeviceForm) {
1627 m_pOptions->saveWidgetGeometry(m_pDeviceForm);
1628 if (m_pDeviceForm->isVisible()) {
1629 m_pDeviceForm->hide();
1630 } else {
1631 m_pDeviceForm->show();
1632 m_pDeviceForm->raise();
1633 m_pDeviceForm->activateWindow();
1634 }
1635 }
1636 }
1637
1638
1639 // Show options dialog.
1640 void MainForm::viewOptions (void)
1641 {
1642 if (m_pOptions == NULL)
1643 return;
1644
1645 OptionsForm* pOptionsForm = new OptionsForm(this);
1646 if (pOptionsForm) {
1647 // Check out some initial nullities(tm)...
1648 ChannelStrip* pChannelStrip = activeChannelStrip();
1649 if (m_pOptions->sDisplayFont.isEmpty() && pChannelStrip)
1650 m_pOptions->sDisplayFont = pChannelStrip->displayFont().toString();
1651 if (m_pOptions->sMessagesFont.isEmpty() && m_pMessages)
1652 m_pOptions->sMessagesFont = m_pMessages->messagesFont().toString();
1653 // To track down deferred or immediate changes.
1654 QString sOldServerHost = m_pOptions->sServerHost;
1655 int iOldServerPort = m_pOptions->iServerPort;
1656 int iOldServerTimeout = m_pOptions->iServerTimeout;
1657 bool bOldServerStart = m_pOptions->bServerStart;
1658 QString sOldServerCmdLine = m_pOptions->sServerCmdLine;
1659 bool bOldMessagesLog = m_pOptions->bMessagesLog;
1660 QString sOldMessagesLogPath = m_pOptions->sMessagesLogPath;
1661 QString sOldDisplayFont = m_pOptions->sDisplayFont;
1662 bool bOldDisplayEffect = m_pOptions->bDisplayEffect;
1663 int iOldMaxVolume = m_pOptions->iMaxVolume;
1664 QString sOldMessagesFont = m_pOptions->sMessagesFont;
1665 bool bOldKeepOnTop = m_pOptions->bKeepOnTop;
1666 bool bOldStdoutCapture = m_pOptions->bStdoutCapture;
1667 int bOldMessagesLimit = m_pOptions->bMessagesLimit;
1668 int iOldMessagesLimitLines = m_pOptions->iMessagesLimitLines;
1669 bool bOldCompletePath = m_pOptions->bCompletePath;
1670 bool bOldInstrumentNames = m_pOptions->bInstrumentNames;
1671 int iOldMaxRecentFiles = m_pOptions->iMaxRecentFiles;
1672 int iOldBaseFontSize = m_pOptions->iBaseFontSize;
1673 // Load the current setup settings.
1674 pOptionsForm->setup(m_pOptions);
1675 // Show the setup dialog...
1676 if (pOptionsForm->exec()) {
1677 // Warn if something will be only effective on next run.
1678 if (( bOldStdoutCapture && !m_pOptions->bStdoutCapture) ||
1679 (!bOldStdoutCapture && m_pOptions->bStdoutCapture) ||
1680 ( bOldKeepOnTop && !m_pOptions->bKeepOnTop) ||
1681 (!bOldKeepOnTop && m_pOptions->bKeepOnTop) ||
1682 (iOldBaseFontSize != m_pOptions->iBaseFontSize)) {
1683 QMessageBox::information(this,
1684 QSAMPLER_TITLE ": " + tr("Information"),
1685 tr("Some settings may be only effective\n"
1686 "next time you start this program."));
1687 updateMessagesCapture();
1688 }
1689 // Check wheather something immediate has changed.
1690 if (( bOldMessagesLog && !m_pOptions->bMessagesLog) ||
1691 (!bOldMessagesLog && m_pOptions->bMessagesLog) ||
1692 (sOldMessagesLogPath != m_pOptions->sMessagesLogPath))
1693 m_pMessages->setLogging(
1694 m_pOptions->bMessagesLog, m_pOptions->sMessagesLogPath);
1695 if (( bOldCompletePath && !m_pOptions->bCompletePath) ||
1696 (!bOldCompletePath && m_pOptions->bCompletePath) ||
1697 (iOldMaxRecentFiles != m_pOptions->iMaxRecentFiles))
1698 updateRecentFilesMenu();
1699 if (( bOldInstrumentNames && !m_pOptions->bInstrumentNames) ||
1700 (!bOldInstrumentNames && m_pOptions->bInstrumentNames))
1701 updateInstrumentNames();
1702 if (( bOldDisplayEffect && !m_pOptions->bDisplayEffect) ||
1703 (!bOldDisplayEffect && m_pOptions->bDisplayEffect))
1704 updateDisplayEffect();
1705 if (sOldDisplayFont != m_pOptions->sDisplayFont)
1706 updateDisplayFont();
1707 if (iOldMaxVolume != m_pOptions->iMaxVolume)
1708 updateMaxVolume();
1709 if (sOldMessagesFont != m_pOptions->sMessagesFont)
1710 updateMessagesFont();
1711 if (( bOldMessagesLimit && !m_pOptions->bMessagesLimit) ||
1712 (!bOldMessagesLimit && m_pOptions->bMessagesLimit) ||
1713 (iOldMessagesLimitLines != m_pOptions->iMessagesLimitLines))
1714 updateMessagesLimit();
1715 // And now the main thing, whether we'll do client/server recycling?
1716 if ((sOldServerHost != m_pOptions->sServerHost) ||
1717 (iOldServerPort != m_pOptions->iServerPort) ||
1718 (iOldServerTimeout != m_pOptions->iServerTimeout) ||
1719 ( bOldServerStart && !m_pOptions->bServerStart) ||
1720 (!bOldServerStart && m_pOptions->bServerStart) ||
1721 (sOldServerCmdLine != m_pOptions->sServerCmdLine
1722 && m_pOptions->bServerStart))
1723 fileRestart();
1724 }
1725 // Done.
1726 delete pOptionsForm;
1727 }
1728
1729 // This makes it.
1730 stabilizeForm();
1731 }
1732
1733
1734 //-------------------------------------------------------------------------
1735 // qsamplerMainForm -- Channels action slots.
1736
1737 // Arrange channel strips.
1738 void MainForm::channelsArrange (void)
1739 {
1740 // Full width vertical tiling
1741 QWidgetList wlist = m_pWorkspace->windowList();
1742 if (wlist.isEmpty())
1743 return;
1744
1745 m_pWorkspace->setUpdatesEnabled(false);
1746 int y = 0;
1747 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1748 ChannelStrip* pChannelStrip = (ChannelStrip*) wlist.at(iChannel);
1749 /* if (pChannelStrip->testWState(WState_Maximized | WState_Minimized)) {
1750 // Prevent flicker...
1751 pChannelStrip->hide();
1752 pChannelStrip->showNormal();
1753 } */
1754 pChannelStrip->adjustSize();
1755 int iWidth = m_pWorkspace->width();
1756 if (iWidth < pChannelStrip->width())
1757 iWidth = pChannelStrip->width();
1758 // int iHeight = pChannelStrip->height()
1759 // + pChannelStrip->parentWidget()->baseSize().height();
1760 int iHeight = pChannelStrip->parentWidget()->frameGeometry().height();
1761 pChannelStrip->parentWidget()->setGeometry(0, y, iWidth, iHeight);
1762 y += iHeight;
1763 }
1764 m_pWorkspace->setUpdatesEnabled(true);
1765
1766 stabilizeForm();
1767 }
1768
1769
1770 // Auto-arrange channel strips.
1771 void MainForm::channelsAutoArrange ( bool bOn )
1772 {
1773 if (m_pOptions == NULL)
1774 return;
1775
1776 // Toggle the auto-arrange flag.
1777 m_pOptions->bAutoArrange = bOn;
1778
1779 // If on, update whole workspace...
1780 if (m_pOptions->bAutoArrange)
1781 channelsArrange();
1782 }
1783
1784
1785 //-------------------------------------------------------------------------
1786 // qsamplerMainForm -- Help Action slots.
1787
1788 // Show information about the Qt toolkit.
1789 void MainForm::helpAboutQt (void)
1790 {
1791 QMessageBox::aboutQt(this);
1792 }
1793
1794
1795 // Show information about application program.
1796 void MainForm::helpAbout (void)
1797 {
1798 // Stuff the about box text...
1799 QString sText = "<p>\n";
1800 sText += "<b>" QSAMPLER_TITLE " - " + tr(QSAMPLER_SUBTITLE) + "</b><br />\n";
1801 sText += "<br />\n";
1802 sText += tr("Version") + ": <b>" QSAMPLER_VERSION "</b><br />\n";
1803 sText += "<small>" + tr("Build") + ": " __DATE__ " " __TIME__ "</small><br />\n";
1804 #ifdef CONFIG_DEBUG
1805 sText += "<small><font color=\"red\">";
1806 sText += tr("Debugging option enabled.");
1807 sText += "</font></small><br />";
1808 #endif
1809 #ifndef CONFIG_LIBGIG
1810 sText += "<small><font color=\"red\">";
1811 sText += tr("GIG (libgig) file support disabled.");
1812 sText += "</font></small><br />";
1813 #endif
1814 #ifndef CONFIG_INSTRUMENT_NAME
1815 sText += "<small><font color=\"red\">";
1816 sText += tr("LSCP (liblscp) instrument_name support disabled.");
1817 sText += "</font></small><br />";
1818 #endif
1819 #ifndef CONFIG_MUTE_SOLO
1820 sText += "<small><font color=\"red\">";
1821 sText += tr("Sampler channel Mute/Solo support disabled.");
1822 sText += "</font></small><br />";
1823 #endif
1824 #ifndef CONFIG_AUDIO_ROUTING
1825 sText += "<small><font color=\"red\">";
1826 sText += tr("LSCP (liblscp) audio_routing support disabled.");
1827 sText += "</font></small><br />";
1828 #endif
1829 #ifndef CONFIG_FXSEND
1830 sText += "<small><font color=\"red\">";
1831 sText += tr("Sampler channel Effect Sends support disabled.");
1832 sText += "</font></small><br />";
1833 #endif
1834 #ifndef CONFIG_VOLUME
1835 sText += "<small><font color=\"red\">";
1836 sText += tr("Global volume support disabled.");
1837 sText += "</font></small><br />";
1838 #endif
1839 #ifndef CONFIG_MIDI_INSTRUMENT
1840 sText += "<small><font color=\"red\">";
1841 sText += tr("MIDI instrument mapping support disabled.");
1842 sText += "</font></small><br />";
1843 #endif
1844 #ifndef CONFIG_EDIT_INSTRUMENT
1845 sText += "<small><font color=\"red\">";
1846 sText += tr("Instrument editing support disabled.");
1847 sText += "</font></small><br />";
1848 #endif
1849 #ifndef CONFIG_EVENT_CHANNEL_MIDI
1850 sText += "<small><font color=\"red\">";
1851 sText += tr("Channel MIDI event support disabled.");
1852 sText += "</font></small><br />";
1853 #endif
1854 #ifndef CONFIG_EVENT_DEVICE_MIDI
1855 sText += "<small><font color=\"red\">";
1856 sText += tr("Device MIDI event support disabled.");
1857 sText += "</font></small><br />";
1858 #endif
1859 #ifndef CONFIG_MAX_VOICES
1860 sText += "<small><font color=\"red\">";
1861 sText += tr("Runtime max. voices / disk streams support disabled.");
1862 sText += "</font></small><br />";
1863 #endif
1864 sText += "<br />\n";
1865 sText += tr("Using") + ": ";
1866 sText += ::lscp_client_package();
1867 sText += " ";
1868 sText += ::lscp_client_version();
1869 #ifdef CONFIG_LIBGIG
1870 sText += ", ";
1871 sText += gig::libraryName().c_str();
1872 sText += " ";
1873 sText += gig::libraryVersion().c_str();
1874 #endif
1875 sText += "<br />\n";
1876 sText += "<br />\n";
1877 sText += tr("Website") + ": <a href=\"" QSAMPLER_WEBSITE "\">" QSAMPLER_WEBSITE "</a><br />\n";
1878 sText += "<br />\n";
1879 sText += "<small>";
1880 sText += QSAMPLER_COPYRIGHT "<br />\n";
1881 sText += QSAMPLER_COPYRIGHT2 "<br />\n";
1882 sText += "<br />\n";
1883 sText += tr("This program is free software; you can redistribute it and/or modify it") + "<br />\n";
1884 sText += tr("under the terms of the GNU General Public License version 2 or later.");
1885 sText += "</small>";
1886 sText += "</p>\n";
1887
1888 QMessageBox::about(this, tr("About") + " " QSAMPLER_TITLE, sText);
1889 }
1890
1891
1892 //-------------------------------------------------------------------------
1893 // qsamplerMainForm -- Main window stabilization.
1894
1895 void MainForm::stabilizeForm (void)
1896 {
1897 // Update the main application caption...
1898 QString sSessionName = sessionName(m_sFilename);
1899 if (m_iDirtyCount > 0)
1900 sSessionName += " *";
1901 setWindowTitle(tr(QSAMPLER_TITLE " - [%1]").arg(sSessionName));
1902
1903 // Update the main menu state...
1904 ChannelStrip *pChannelStrip = activeChannelStrip();
1905 bool bHasClient = (m_pOptions != NULL && m_pClient != NULL);
1906 bool bHasChannel = (bHasClient && pChannelStrip != NULL);
1907 bool bHasChannels = (bHasClient && m_pWorkspace->windowList().count() > 0);
1908 m_ui.fileNewAction->setEnabled(bHasClient);
1909 m_ui.fileOpenAction->setEnabled(bHasClient);
1910 m_ui.fileSaveAction->setEnabled(bHasClient && m_iDirtyCount > 0);
1911 m_ui.fileSaveAsAction->setEnabled(bHasClient);
1912 m_ui.fileResetAction->setEnabled(bHasClient);
1913 m_ui.fileRestartAction->setEnabled(bHasClient || m_pServer == NULL);
1914 m_ui.editAddChannelAction->setEnabled(bHasClient);
1915 m_ui.editRemoveChannelAction->setEnabled(bHasChannel);
1916 m_ui.editSetupChannelAction->setEnabled(bHasChannel);
1917 #ifdef CONFIG_EDIT_INSTRUMENT
1918 m_ui.editEditChannelAction->setEnabled(bHasChannel);
1919 #else
1920 m_ui.editEditChannelAction->setEnabled(false);
1921 #endif
1922 m_ui.editResetChannelAction->setEnabled(bHasChannel);
1923 m_ui.editResetAllChannelsAction->setEnabled(bHasChannels);
1924 m_ui.viewMessagesAction->setChecked(m_pMessages && m_pMessages->isVisible());
1925 #ifdef CONFIG_MIDI_INSTRUMENT
1926 m_ui.viewInstrumentsAction->setChecked(m_pInstrumentListForm
1927 && m_pInstrumentListForm->isVisible());
1928 m_ui.viewInstrumentsAction->setEnabled(bHasClient);
1929 #else
1930 m_ui.viewInstrumentsAction->setEnabled(false);
1931 #endif
1932 m_ui.viewDevicesAction->setChecked(m_pDeviceForm
1933 && m_pDeviceForm->isVisible());
1934 m_ui.viewDevicesAction->setEnabled(bHasClient);
1935 m_ui.viewMidiDeviceStatusMenu->setEnabled(
1936 DeviceStatusForm::getInstances().size() > 0);
1937 m_ui.channelsArrangeAction->setEnabled(bHasChannels);
1938
1939 #ifdef CONFIG_VOLUME
1940 // Toolbar widgets are also affected...
1941 m_pVolumeSlider->setEnabled(bHasClient);
1942 m_pVolumeSpinBox->setEnabled(bHasClient);
1943 #endif
1944
1945 // Client/Server status...
1946 if (bHasClient) {
1947 m_statusItem[QSAMPLER_STATUS_CLIENT]->setText(tr("Connected"));
1948 m_statusItem[QSAMPLER_STATUS_SERVER]->setText(m_pOptions->sServerHost
1949 + ':' + QString::number(m_pOptions->iServerPort));
1950 } else {
1951 m_statusItem[QSAMPLER_STATUS_CLIENT]->clear();
1952 m_statusItem[QSAMPLER_STATUS_SERVER]->clear();
1953 }
1954 // Channel status...
1955 if (bHasChannel)
1956 m_statusItem[QSAMPLER_STATUS_CHANNEL]->setText(pChannelStrip->windowTitle());
1957 else
1958 m_statusItem[QSAMPLER_STATUS_CHANNEL]->clear();
1959 // Session status...
1960 if (m_iDirtyCount > 0)
1961 m_statusItem[QSAMPLER_STATUS_SESSION]->setText(tr("MOD"));
1962 else
1963 m_statusItem[QSAMPLER_STATUS_SESSION]->clear();
1964
1965 // Recent files menu.
1966 m_ui.fileOpenRecentMenu->setEnabled(m_pOptions->recentFiles.count() > 0);
1967 }
1968
1969
1970 // Global volume change receiver slot.
1971 void MainForm::volumeChanged ( int iVolume )
1972 {
1973 #ifdef CONFIG_VOLUME
1974
1975 if (m_iVolumeChanging > 0)
1976 return;
1977
1978 m_iVolumeChanging++;
1979
1980 // Update the toolbar widgets...
1981 if (m_pVolumeSlider->value() != iVolume)
1982 m_pVolumeSlider->setValue(iVolume);
1983 if (m_pVolumeSpinBox->value() != iVolume)
1984 m_pVolumeSpinBox->setValue(iVolume);
1985
1986 // Do it as commanded...
1987 float fVolume = 0.01f * float(iVolume);
1988 if (::lscp_set_volume(m_pClient, fVolume) == LSCP_OK)
1989 appendMessages(QObject::tr("Volume: %1.").arg(fVolume));
1990 else
1991 appendMessagesClient("lscp_set_volume");
1992
1993 m_iVolumeChanging--;
1994
1995 m_iDirtyCount++;
1996 stabilizeForm();
1997
1998 #endif
1999 }
2000
2001
2002 // Channel change receiver slot.
2003 void MainForm::channelStripChanged(ChannelStrip* pChannelStrip)
2004 {
2005 // Add this strip to the changed list...
2006 if (!m_changedStrips.contains(pChannelStrip)) {
2007 m_changedStrips.append(pChannelStrip);
2008 pChannelStrip->resetErrorCount();
2009 }
2010
2011 // Just mark the dirty form.
2012 m_iDirtyCount++;
2013 // and update the form status...
2014 stabilizeForm();
2015 }
2016
2017
2018 // Grab and restore current sampler channels session.
2019 void MainForm::updateSession (void)
2020 {
2021 #ifdef CONFIG_VOLUME
2022 int iVolume = ::lroundf(100.0f * ::lscp_get_volume(m_pClient));
2023 m_iVolumeChanging++;
2024 m_pVolumeSlider->setValue(iVolume);
2025 m_pVolumeSpinBox->setValue(iVolume);
2026 m_iVolumeChanging--;
2027 #endif
2028 #ifdef CONFIG_MIDI_INSTRUMENT
2029 // FIXME: Make some room for default instrument maps...
2030 int iMaps = ::lscp_get_midi_instrument_maps(m_pClient);
2031 if (iMaps < 0)
2032 appendMessagesClient("lscp_get_midi_instrument_maps");
2033 else if (iMaps < 1) {
2034 ::lscp_add_midi_instrument_map(m_pClient,
2035 tr("Chromatic").toUtf8().constData());
2036 ::lscp_add_midi_instrument_map(m_pClient,
2037 tr("Drum Kits").toUtf8().constData());
2038 }
2039 #endif
2040
2041 updateAllChannelStrips(false);
2042
2043 // Do we auto-arrange?
2044 if (m_pOptions && m_pOptions->bAutoArrange)
2045 channelsArrange();
2046
2047 // Remember to refresh devices and instruments...
2048 if (m_pInstrumentListForm)
2049 m_pInstrumentListForm->refreshInstruments();
2050 if (m_pDeviceForm)
2051 m_pDeviceForm->refreshDevices();
2052 }
2053
2054 void MainForm::updateAllChannelStrips(bool bRemoveDeadStrips) {
2055 // Retrieve the current channel list.
2056 int *piChannelIDs = ::lscp_list_channels(m_pClient);
2057 if (piChannelIDs == NULL) {
2058 if (::lscp_client_get_errno(m_pClient)) {
2059 appendMessagesClient("lscp_list_channels");
2060 appendMessagesError(
2061 tr("Could not get current list of channels.\n\nSorry."));
2062 }
2063 } else {
2064 // Try to (re)create each channel.
2065 m_pWorkspace->setUpdatesEnabled(false);
2066 for (int iChannel = 0; piChannelIDs[iChannel] >= 0; iChannel++) {
2067 // Check if theres already a channel strip for this one...
2068 if (!channelStrip(piChannelIDs[iChannel]))
2069 createChannelStrip(new Channel(piChannelIDs[iChannel]));
2070 }
2071
2072 // Do we auto-arrange?
2073 if (m_pOptions && m_pOptions->bAutoArrange)
2074 channelsArrange();
2075
2076 stabilizeForm();
2077
2078 // remove dead channel strips
2079 if (bRemoveDeadStrips) {
2080 for (int i = 0; channelStripAt(i); ++i) {
2081 ChannelStrip* pChannelStrip = channelStripAt(i);
2082 bool bExists = false;
2083 for (int j = 0; piChannelIDs[j] >= 0; ++j) {
2084 if (!pChannelStrip->channel()) break;
2085 if (piChannelIDs[j] == pChannelStrip->channel()->channelID()) {
2086 // strip exists, don't touch it
2087 bExists = true;
2088 break;
2089 }
2090 }
2091 if (!bExists) destroyChannelStrip(pChannelStrip);
2092 }
2093 }
2094 m_pWorkspace->setUpdatesEnabled(true);
2095 }
2096 }
2097
2098 // Update the recent files list and menu.
2099 void MainForm::updateRecentFiles ( const QString& sFilename )
2100 {
2101 if (m_pOptions == NULL)
2102 return;
2103
2104 // Remove from list if already there (avoid duplicates)
2105 int iIndex = m_pOptions->recentFiles.indexOf(sFilename);
2106 if (iIndex >= 0)
2107 m_pOptions->recentFiles.removeAt(iIndex);
2108 // Put it to front...
2109 m_pOptions->recentFiles.push_front(sFilename);
2110 }
2111
2112
2113 // Update the recent files list and menu.
2114 void MainForm::updateRecentFilesMenu (void)
2115 {
2116 if (m_pOptions == NULL)
2117 return;
2118
2119 // Time to keep the list under limits.
2120 int iRecentFiles = m_pOptions->recentFiles.count();
2121 while (iRecentFiles > m_pOptions->iMaxRecentFiles) {
2122 m_pOptions->recentFiles.pop_back();
2123 iRecentFiles--;
2124 }
2125
2126 // Rebuild the recent files menu...
2127 m_ui.fileOpenRecentMenu->clear();
2128 for (int i = 0; i < iRecentFiles; i++) {
2129 const QString& sFilename = m_pOptions->recentFiles[i];
2130 if (QFileInfo(sFilename).exists()) {
2131 QAction *pAction = m_ui.fileOpenRecentMenu->addAction(
2132 QString("&%1 %2").arg(i + 1).arg(sessionName(sFilename)),
2133 this, SLOT(fileOpenRecent()));
2134 pAction->setData(i);
2135 }
2136 }
2137 }
2138
2139
2140 // Force update of the channels instrument names mode.
2141 void MainForm::updateInstrumentNames (void)
2142 {
2143 // Full channel list update...
2144 QWidgetList wlist = m_pWorkspace->windowList();
2145 if (wlist.isEmpty())
2146 return;
2147
2148 m_pWorkspace->setUpdatesEnabled(false);
2149 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
2150 ChannelStrip *pChannelStrip = (ChannelStrip *) wlist.at(iChannel);
2151 if (pChannelStrip)
2152 pChannelStrip->updateInstrumentName(true);
2153 }
2154 m_pWorkspace->setUpdatesEnabled(true);
2155 }
2156
2157
2158 // Force update of the channels display font.
2159 void MainForm::updateDisplayFont (void)
2160 {
2161 if (m_pOptions == NULL)
2162 return;
2163
2164 // Check if display font is legal.
2165 if (m_pOptions->sDisplayFont.isEmpty())
2166 return;
2167 // Realize it.
2168 QFont font;
2169 if (!font.fromString(m_pOptions->sDisplayFont))
2170 return;
2171
2172 // Full channel list update...
2173 QWidgetList wlist = m_pWorkspace->windowList();
2174 if (wlist.isEmpty())
2175 return;
2176
2177 m_pWorkspace->setUpdatesEnabled(false);
2178 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
2179 ChannelStrip* pChannelStrip = (ChannelStrip*) wlist.at(iChannel);
2180 if (pChannelStrip)
2181 pChannelStrip->setDisplayFont(font);
2182 }
2183 m_pWorkspace->setUpdatesEnabled(true);
2184 }
2185
2186
2187 // Update channel strips background effect.
2188 void MainForm::updateDisplayEffect (void)
2189 {
2190 // Full channel list update...
2191 QWidgetList wlist = m_pWorkspace->windowList();
2192 if (wlist.isEmpty())
2193 return;
2194
2195 m_pWorkspace->setUpdatesEnabled(false);
2196 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
2197 ChannelStrip* pChannelStrip = (ChannelStrip*) wlist.at(iChannel);
2198 if (pChannelStrip)
2199 pChannelStrip->setDisplayEffect(m_pOptions->bDisplayEffect);
2200 }
2201 m_pWorkspace->setUpdatesEnabled(true);
2202 }
2203
2204
2205 // Force update of the channels maximum volume setting.
2206 void MainForm::updateMaxVolume (void)
2207 {
2208 if (m_pOptions == NULL)
2209 return;
2210
2211 #ifdef CONFIG_VOLUME
2212 m_iVolumeChanging++;
2213 m_pVolumeSlider->setMaximum(m_pOptions->iMaxVolume);
2214 m_pVolumeSpinBox->setMaximum(m_pOptions->iMaxVolume);
2215 m_iVolumeChanging--;
2216 #endif
2217
2218 // Full channel list update...
2219 QWidgetList wlist = m_pWorkspace->windowList();
2220 if (wlist.isEmpty())
2221 return;
2222
2223 m_pWorkspace->setUpdatesEnabled(false);
2224 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
2225 ChannelStrip* pChannelStrip = (ChannelStrip*) wlist.at(iChannel);
2226 if (pChannelStrip)
2227 pChannelStrip->setMaxVolume(m_pOptions->iMaxVolume);
2228 }
2229 m_pWorkspace->setUpdatesEnabled(true);
2230 }
2231
2232
2233 //-------------------------------------------------------------------------
2234 // qsamplerMainForm -- Messages window form handlers.
2235
2236 // Messages output methods.
2237 void MainForm::appendMessages( const QString& s )
2238 {
2239 if (m_pMessages)
2240 m_pMessages->appendMessages(s);
2241
2242 statusBar()->showMessage(s, 3000);
2243 }
2244
2245 void MainForm::appendMessagesColor( const QString& s, const QString& c )
2246 {
2247 if (m_pMessages)
2248 m_pMessages->appendMessagesColor(s, c);
2249
2250 statusBar()->showMessage(s, 3000);
2251 }
2252
2253 void MainForm::appendMessagesText( const QString& s )
2254 {
2255 if (m_pMessages)
2256 m_pMessages->appendMessagesText(s);
2257 }
2258
2259 void MainForm::appendMessagesError( const QString& s )
2260 {
2261 if (m_pMessages)
2262 m_pMessages->show();
2263
2264 appendMessagesColor(s.simplified(), "#ff0000");
2265
2266 // Make it look responsive...:)
2267 QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
2268
2269 QMessageBox::critical(this,
2270 QSAMPLER_TITLE ": " + tr("Error"), s, QMessageBox::Cancel);
2271 }
2272
2273
2274 // This is a special message format, just for client results.
2275 void MainForm::appendMessagesClient( const QString& s )
2276 {
2277 if (m_pClient == NULL)
2278 return;
2279
2280 appendMessagesColor(s + QString(": %1 (errno=%2)")
2281 .arg(::lscp_client_get_result(m_pClient))
2282 .arg(::lscp_client_get_errno(m_pClient)), "#996666");
2283
2284 // Make it look responsive...:)
2285 QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
2286 }
2287
2288
2289 // Force update of the messages font.
2290 void MainForm::updateMessagesFont (void)
2291 {
2292 if (m_pOptions == NULL)
2293 return;
2294
2295 if (m_pMessages && !m_pOptions->sMessagesFont.isEmpty()) {
2296 QFont font;
2297 if (font.fromString(m_pOptions->sMessagesFont))
2298 m_pMessages->setMessagesFont(font);
2299 }
2300 }
2301
2302
2303 // Update messages window line limit.
2304 void MainForm::updateMessagesLimit (void)
2305 {
2306 if (m_pOptions == NULL)
2307 return;
2308
2309 if (m_pMessages) {
2310 if (m_pOptions->bMessagesLimit)
2311 m_pMessages->setMessagesLimit(m_pOptions->iMessagesLimitLines);
2312 else
2313 m_pMessages->setMessagesLimit(-1);
2314 }
2315 }
2316
2317
2318 // Enablement of the messages capture feature.
2319 void MainForm::updateMessagesCapture (void)
2320 {
2321 if (m_pOptions == NULL)
2322 return;
2323
2324 if (m_pMessages)
2325 m_pMessages->setCaptureEnabled(m_pOptions->bStdoutCapture);
2326 }
2327
2328
2329 //-------------------------------------------------------------------------
2330 // qsamplerMainForm -- MDI channel strip management.
2331
2332 // The channel strip creation executive.
2333 ChannelStrip* MainForm::createChannelStrip ( Channel *pChannel )
2334 {
2335 if (m_pClient == NULL || pChannel == NULL)
2336 return NULL;
2337
2338 // Add a new channel itema...
2339 ChannelStrip *pChannelStrip = new ChannelStrip();
2340 if (pChannelStrip == NULL)
2341 return NULL;
2342
2343 // Set some initial channel strip options...
2344 if (m_pOptions) {
2345 // Background display effect...
2346 pChannelStrip->setDisplayEffect(m_pOptions->bDisplayEffect);
2347 // We'll need a display font.
2348 QFont font;
2349 if (font.fromString(m_pOptions->sDisplayFont))
2350 pChannelStrip->setDisplayFont(font);
2351 // Maximum allowed volume setting.
2352 pChannelStrip->setMaxVolume(m_pOptions->iMaxVolume);
2353 }
2354
2355 // Add it to workspace...
2356 m_pWorkspace->addWindow(pChannelStrip, Qt::FramelessWindowHint);
2357
2358 // Actual channel strip setup...
2359 pChannelStrip->setup(pChannel);
2360
2361 QObject::connect(pChannelStrip,
2362 SIGNAL(channelChanged(ChannelStrip*)),
2363 SLOT(channelStripChanged(ChannelStrip*)));
2364
2365 // Now we show up us to the world.
2366 pChannelStrip->show();
2367
2368 // This is pretty new, so we'll watch for it closely.
2369 channelStripChanged(pChannelStrip);
2370
2371 // Return our successful reference...
2372 return pChannelStrip;
2373 }
2374
2375 void MainForm::destroyChannelStrip(ChannelStrip* pChannelStrip) {
2376 // Just delete the channel strip.
2377 delete pChannelStrip;
2378
2379 // Do we auto-arrange?
2380 if (m_pOptions && m_pOptions->bAutoArrange)
2381 channelsArrange();
2382
2383 stabilizeForm();
2384 }
2385
2386 // Retrieve the active channel strip.
2387 ChannelStrip* MainForm::activeChannelStrip (void)
2388 {
2389 return static_cast<ChannelStrip *> (m_pWorkspace->activeWindow());
2390 }
2391
2392
2393 // Retrieve a channel strip by index.
2394 ChannelStrip* MainForm::channelStripAt ( int iChannel )
2395 {
2396 if (!m_pWorkspace) return NULL;
2397
2398 QWidgetList wlist = m_pWorkspace->windowList();
2399 if (wlist.isEmpty())
2400 return NULL;
2401
2402 if (iChannel < 0 || iChannel >= wlist.size())
2403 return NULL;
2404
2405 return dynamic_cast<ChannelStrip *> (wlist.at(iChannel));
2406 }
2407
2408
2409 // Retrieve a channel strip by sampler channel id.
2410 ChannelStrip* MainForm::channelStrip ( int iChannelID )
2411 {
2412 QWidgetList wlist = m_pWorkspace->windowList();
2413 if (wlist.isEmpty())
2414 return NULL;
2415
2416 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
2417 ChannelStrip* pChannelStrip
2418 = static_cast<ChannelStrip*> (wlist.at(iChannel));
2419 if (pChannelStrip) {
2420 Channel *pChannel = pChannelStrip->channel();
2421 if (pChannel && pChannel->channelID() == iChannelID)
2422 return pChannelStrip;
2423 }
2424 }
2425
2426 // Not found.
2427 return NULL;
2428 }
2429
2430
2431 // Construct the windows menu.
2432 void MainForm::channelsMenuAboutToShow (void)
2433 {
2434 m_ui.channelsMenu->clear();
2435 m_ui.channelsMenu->addAction(m_ui.channelsArrangeAction);
2436 m_ui.channelsMenu->addAction(m_ui.channelsAutoArrangeAction);
2437
2438 QWidgetList wlist = m_pWorkspace->windowList();
2439 if (!wlist.isEmpty()) {
2440 m_ui.channelsMenu->addSeparator();
2441 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
2442 ChannelStrip* pChannelStrip
2443 = static_cast<ChannelStrip*> (wlist.at(iChannel));
2444 if (pChannelStrip) {
2445 QAction *pAction = m_ui.channelsMenu->addAction(
2446 pChannelStrip->windowTitle(),
2447 this, SLOT(channelsMenuActivated()));
2448 pAction->setCheckable(true);
2449 pAction->setChecked(activeChannelStrip() == pChannelStrip);
2450 pAction->setData(iChannel);
2451 }
2452 }
2453 }
2454 }
2455
2456
2457 // Windows menu activation slot
2458 void MainForm::channelsMenuActivated (void)
2459 {
2460 // Retrive channel index from action data...
2461 QAction *pAction = qobject_cast<QAction *> (sender());
2462 if (pAction == NULL)
2463 return;
2464
2465 ChannelStrip* pChannelStrip = channelStripAt(pAction->data().toInt());
2466 if (pChannelStrip) {
2467 pChannelStrip->showNormal();
2468 pChannelStrip->setFocus();
2469 }
2470 }
2471
2472
2473 //-------------------------------------------------------------------------
2474 // qsamplerMainForm -- Timer stuff.
2475
2476 // Set the pseudo-timer delay schedule.
2477 void MainForm::startSchedule ( int iStartDelay )
2478 {
2479 m_iStartDelay = 1 + (iStartDelay * 1000);
2480 m_iTimerDelay = 0;
2481 }
2482
2483 // Suspend the pseudo-timer delay schedule.
2484 void MainForm::stopSchedule (void)
2485 {
2486 m_iStartDelay = 0;
2487 m_iTimerDelay = 0;
2488 }
2489
2490 // Timer slot funtion.
2491 void MainForm::timerSlot (void)
2492 {
2493 if (m_pOptions == NULL)
2494 return;
2495
2496 // Is it the first shot on server start after a few delay?
2497 if (m_iTimerDelay < m_iStartDelay) {
2498 m_iTimerDelay += QSAMPLER_TIMER_MSECS;
2499 if (m_iTimerDelay >= m_iStartDelay) {
2500 // If we cannot start it now, maybe a lil'mo'later ;)
2501 if (!startClient()) {
2502 m_iStartDelay += m_iTimerDelay;
2503 m_iTimerDelay = 0;
2504 }
2505 }
2506 }
2507
2508 if (m_pClient) {
2509 // Update the channel information for each pending strip...
2510 QListIterator<ChannelStrip *> iter(m_changedStrips);
2511 while (iter.hasNext()) {
2512 ChannelStrip *pChannelStrip = iter.next();
2513 // If successfull, remove from pending list...
2514 if (pChannelStrip->updateChannelInfo()) {
2515 int iChannelStrip = m_changedStrips.indexOf(pChannelStrip);
2516 if (iChannelStrip >= 0)
2517 m_changedStrips.removeAt(iChannelStrip);
2518 }
2519 }
2520 // Refresh each channel usage, on each period...
2521 if (m_pOptions->bAutoRefresh) {
2522 m_iTimerSlot += QSAMPLER_TIMER_MSECS;
2523 if (m_iTimerSlot >= m_pOptions->iAutoRefreshTime) {
2524 m_iTimerSlot = 0;
2525 // Update the channel stream usage for each strip...
2526 QWidgetList wlist = m_pWorkspace->windowList();
2527 for (int iChannel = 0;
2528 iChannel < (int) wlist.count(); iChannel++) {
2529 ChannelStrip* pChannelStrip
2530 = (ChannelStrip*) wlist.at(iChannel);
2531 if (pChannelStrip && pChannelStrip->isVisible())
2532 pChannelStrip->updateChannelUsage();
2533 }
2534 }
2535 }
2536 }
2537
2538 // Register the next timer slot.
2539 QTimer::singleShot(QSAMPLER_TIMER_MSECS, this, SLOT(timerSlot()));
2540 }
2541
2542
2543 //-------------------------------------------------------------------------
2544 // qsamplerMainForm -- Server stuff.
2545
2546 // Start linuxsampler server...
2547 void MainForm::startServer (void)
2548 {
2549 if (m_pOptions == NULL)
2550 return;
2551
2552 // Aren't already a client, are we?
2553 if (!m_pOptions->bServerStart || m_pClient)
2554 return;
2555
2556 // Is the server process instance still here?
2557 if (m_pServer) {
2558 if (QMessageBox::warning(this,
2559 QSAMPLER_TITLE ": " + tr("Warning"),
2560 tr("Could not start the LinuxSampler server.\n\n"
2561 "Maybe it is already started."),
2562 QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) {
2563 m_pServer->terminate();
2564 m_pServer->kill();
2565 }
2566 return;
2567 }
2568
2569 // Reset our timer counters...
2570 stopSchedule();
2571
2572 // Verify we have something to start with...
2573 if (m_pOptions->sServerCmdLine.isEmpty())
2574 return;
2575
2576 // OK. Let's build the startup process...
2577 m_pServer = new QProcess();
2578 bForceServerStop = true;
2579
2580 // Setup stdout/stderr capture...
2581 // if (m_pOptions->bStdoutCapture) {
2582 #if QT_VERSION >= 0x040200
2583 m_pServer->setProcessChannelMode(QProcess::ForwardedChannels);
2584 #endif
2585 QObject::connect(m_pServer,
2586 SIGNAL(readyReadStandardOutput()),
2587 SLOT(readServerStdout()));
2588 QObject::connect(m_pServer,
2589 SIGNAL(readyReadStandardError()),
2590 SLOT(readServerStdout()));
2591 // }
2592
2593 // The unforgiveable signal communication...
2594 QObject::connect(m_pServer,
2595 SIGNAL(finished(int, QProcess::ExitStatus)),
2596 SLOT(processServerExit()));
2597
2598 // Build process arguments...
2599 QStringList args = m_pOptions->sServerCmdLine.split(' ');
2600 QString sCommand = args[0];
2601 args.removeAt(0);
2602
2603 appendMessages(tr("Server is starting..."));
2604 appendMessagesColor(m_pOptions->sServerCmdLine, "#990099");
2605
2606 // Go linuxsampler, go...
2607 m_pServer->start(sCommand, args);
2608 if (!m_pServer->waitForStarted()) {
2609 appendMessagesError(tr("Could not start server.\n\nSorry."));
2610 processServerExit();
2611 return;
2612 }
2613
2614 // Show startup results...
2615 appendMessages(
2616 tr("Server was started with PID=%1.").arg((long) m_pServer->pid()));
2617
2618 // Reset (yet again) the timer counters,
2619 // but this time is deferred as the user opted.
2620 startSchedule(m_pOptions->iStartDelay);
2621 stabilizeForm();
2622 }
2623
2624
2625 // Stop linuxsampler server...
2626 void MainForm::stopServer (bool bInteractive)
2627 {
2628 // Stop client code.
2629 stopClient();
2630
2631 if (m_pServer && bInteractive) {
2632 if (QMessageBox::question(this,
2633 QSAMPLER_TITLE ": " + tr("The backend's fate ..."),
2634 tr("You have the option to keep the sampler backend (LinuxSampler)\n"
2635 "running in the background. The sampler would continue to work\n"
2636 "according to your current sampler session and you could alter the\n"
2637 "sampler session at any time by relaunching QSampler.\n\n"
2638 "Do you want LinuxSampler to stop?"),
2639 QMessageBox::Yes | QMessageBox::No) == QMessageBox::No)
2640 {
2641 bForceServerStop = false;
2642 }
2643 }
2644
2645 // And try to stop server.
2646 if (m_pServer && bForceServerStop) {
2647 appendMessages(tr("Server is stopping..."));
2648 if (m_pServer->state() == QProcess::Running) {
2649 #if defined(WIN32)
2650 // Try harder...
2651 m_pServer->kill();
2652 #else
2653 // Try softly...
2654 m_pServer->terminate();
2655 #endif
2656 }
2657 } // Do final processing anyway.
2658 else processServerExit();
2659
2660 // Give it some time to terminate gracefully and stabilize...
2661 QTime t;
2662 t.start();
2663 while (t.elapsed() < QSAMPLER_TIMER_MSECS)
2664 QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
2665 }
2666
2667
2668 // Stdout handler...
2669 void MainForm::readServerStdout (void)
2670 {
2671 if (m_pMessages)
2672 m_pMessages->appendStdoutBuffer(m_pServer->readAllStandardOutput());
2673 }
2674
2675
2676 // Linuxsampler server cleanup.
2677 void MainForm::processServerExit (void)
2678 {
2679 // Force client code cleanup.
2680 stopClient();
2681
2682 // Flush anything that maybe pending...
2683 if (m_pMessages)
2684 m_pMessages->flushStdoutBuffer();
2685
2686 if (m_pServer && bForceServerStop) {
2687 if (m_pServer->state() != QProcess::NotRunning) {
2688 appendMessages(tr("Server is being forced..."));
2689 // Force final server shutdown...
2690 m_pServer->kill();
2691 // Give it some time to terminate gracefully and stabilize...
2692 QTime t;
2693 t.start();
2694 while (t.elapsed() < QSAMPLER_TIMER_MSECS)
2695 QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
2696 }
2697 // Force final server shutdown...
2698 appendMessages(
2699 tr("Server was stopped with exit status %1.")
2700 .arg(m_pServer->exitStatus()));
2701 delete m_pServer;
2702 m_pServer = NULL;
2703 }
2704
2705 // Again, make status visible stable.
2706 stabilizeForm();
2707 }
2708
2709
2710 //-------------------------------------------------------------------------
2711 // qsamplerMainForm -- Client stuff.
2712
2713 // The LSCP client callback procedure.
2714 lscp_status_t qsampler_client_callback ( lscp_client_t */*pClient*/,
2715 lscp_event_t event, const char *pchData, int cchData, void *pvData )
2716 {
2717 MainForm* pMainForm = (MainForm *) pvData;
2718 if (pMainForm == NULL)
2719 return LSCP_FAILED;
2720
2721 // ATTN: DO NOT EVER call any GUI code here,
2722 // as this is run under some other thread context.
2723 // A custom event must be posted here...
2724 QApplication::postEvent(pMainForm,
2725 new LscpEvent(event, pchData, cchData));
2726
2727 return LSCP_OK;
2728 }
2729
2730
2731 // Start our almighty client...
2732 bool MainForm::startClient (void)
2733 {
2734 // Have it a setup?
2735 if (m_pOptions == NULL)
2736 return false;
2737
2738 // Aren't we already started, are we?
2739 if (m_pClient)
2740 return true;
2741
2742 // Log prepare here.
2743 appendMessages(tr("Client connecting..."));
2744
2745 // Create the client handle...
2746 m_pClient = ::lscp_client_create(
2747 m_pOptions->sServerHost.toUtf8().constData(),
2748 m_pOptions->iServerPort, qsampler_client_callback, this);
2749 if (m_pClient == NULL) {
2750 // Is this the first try?
2751 // maybe we need to start a local server...
2752 if ((m_pServer && m_pServer->state() == QProcess::Running)
2753 || !m_pOptions->bServerStart) {
2754 appendMessagesError(
2755 tr("Could not connect to server as client.\n\nSorry."));
2756 } else {
2757 startServer();
2758 }
2759 // This is always a failure.
2760 stabilizeForm();
2761 return false;
2762 }
2763 // Just set receive timeout value, blindly.
2764 ::lscp_client_set_timeout(m_pClient, m_pOptions->iServerTimeout);
2765 appendMessages(
2766 tr("Client receive timeout is set to %1 msec.")
2767 .arg(::lscp_client_get_timeout(m_pClient)));
2768
2769 // Subscribe to channel info change notifications...
2770 if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_CHANNEL_COUNT) != LSCP_OK)
2771 appendMessagesClient("lscp_client_subscribe(CHANNEL_COUNT)");
2772 if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_CHANNEL_INFO) != LSCP_OK)
2773 appendMessagesClient("lscp_client_subscribe(CHANNEL_INFO)");
2774
2775 DeviceStatusForm::onDevicesChanged(); // initialize
2776 updateViewMidiDeviceStatusMenu();
2777 if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_MIDI_INPUT_DEVICE_COUNT) != LSCP_OK)
2778 appendMessagesClient("lscp_client_subscribe(MIDI_INPUT_DEVICE_COUNT)");
2779 if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_MIDI_INPUT_DEVICE_INFO) != LSCP_OK)
2780 appendMessagesClient("lscp_client_subscribe(MIDI_INPUT_DEVICE_INFO)");
2781 if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_AUDIO_OUTPUT_DEVICE_COUNT) != LSCP_OK)
2782 appendMessagesClient("lscp_client_subscribe(AUDIO_OUTPUT_DEVICE_COUNT)");
2783 if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_AUDIO_OUTPUT_DEVICE_INFO) != LSCP_OK)
2784 appendMessagesClient("lscp_client_subscribe(AUDIO_OUTPUT_DEVICE_INFO)");
2785
2786 #if CONFIG_EVENT_CHANNEL_MIDI
2787 // Subscribe to channel MIDI data notifications...
2788 if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_CHANNEL_MIDI) != LSCP_OK)
2789 appendMessagesClient("lscp_client_subscribe(CHANNEL_MIDI)");
2790 #endif
2791
2792 #if CONFIG_EVENT_DEVICE_MIDI
2793 // Subscribe to channel MIDI data notifications...
2794 if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_DEVICE_MIDI) != LSCP_OK)
2795 appendMessagesClient("lscp_client_subscribe(DEVICE_MIDI)");
2796 #endif
2797
2798 // We may stop scheduling around.
2799 stopSchedule();
2800
2801 // We'll accept drops from now on...
2802 setAcceptDrops(true);
2803
2804 // Log success here.
2805 appendMessages(tr("Client connected."));
2806
2807 // Hard-notify instrumnet and device configuration forms,
2808 // if visible, that we're ready...
2809 if (m_pInstrumentListForm)
2810 m_pInstrumentListForm->refreshInstruments();
2811 if (m_pDeviceForm)
2812 m_pDeviceForm->refreshDevices();
2813
2814 // Is any session pending to be loaded?
2815 if (!m_pOptions->sSessionFile.isEmpty()) {
2816 // Just load the prabably startup session...
2817 if (loadSessionFile(m_pOptions->sSessionFile)) {
2818 m_pOptions->sSessionFile = QString::null;
2819 return true;
2820 }
2821 }
2822
2823 // send the current / loaded fine tuning settings to the sampler
2824 m_pOptions->sendFineTuningSettings();
2825
2826 // Make a new session
2827 return newSession();
2828 }
2829
2830
2831 // Stop client...
2832 void MainForm::stopClient (void)
2833 {
2834 if (m_pClient == NULL)
2835 return;
2836
2837 // Log prepare here.
2838 appendMessages(tr("Client disconnecting..."));
2839
2840 // Clear timer counters...
2841 stopSchedule();
2842
2843 // We'll reject drops from now on...
2844 setAcceptDrops(false);
2845
2846 // Force any channel strips around, but
2847 // but avoid removing the corresponding
2848 // channels from the back-end server.
2849 m_iDirtyCount = 0;
2850 closeSession(false);
2851
2852 // Close us as a client...
2853 #if CONFIG_EVENT_DEVICE_MIDI
2854 ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_DEVICE_MIDI);
2855 #endif
2856 #if CONFIG_EVENT_CHANNEL_MIDI
2857 ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_CHANNEL_MIDI);
2858 #endif
2859 ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_AUDIO_OUTPUT_DEVICE_INFO);
2860 ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_AUDIO_OUTPUT_DEVICE_COUNT);
2861 ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_MIDI_INPUT_DEVICE_INFO);
2862 ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_MIDI_INPUT_DEVICE_COUNT);
2863 ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_CHANNEL_INFO);
2864 ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_CHANNEL_COUNT);
2865 ::lscp_client_destroy(m_pClient);
2866 m_pClient = NULL;
2867
2868 // Hard-notify instrumnet and device configuration forms,
2869 // if visible, that we're running out...
2870 if (m_pInstrumentListForm)
2871 m_pInstrumentListForm->refreshInstruments();
2872 if (m_pDeviceForm)
2873 m_pDeviceForm->refreshDevices();
2874
2875 // Log final here.
2876 appendMessages(tr("Client disconnected."));
2877
2878 // Make visible status.
2879 stabilizeForm();
2880 }
2881
2882
2883 // Channel strip activation/selection.
2884 void MainForm::activateStrip ( QWidget *pWidget )
2885 {
2886 ChannelStrip *pChannelStrip
2887 = static_cast<ChannelStrip *> (pWidget);
2888 if (pChannelStrip)
2889 pChannelStrip->setSelected(true);
2890
2891 stabilizeForm();
2892 }
2893
2894
2895 } // namespace QSampler
2896
2897
2898 // end of qsamplerMainForm.cpp

  ViewVC Help
Powered by ViewVC