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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2077 - (show annotations) (download)
Wed Mar 31 09:07:30 2010 UTC (14 years ago) by capela
File size: 82016 byte(s)
* Initial widget geometry and visibility persistence logic has
  been slightly revised as much to avoid crash failures due to
  wrong main widget hidden state.

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

  ViewVC Help
Powered by ViewVC