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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC