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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC