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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC