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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2681 - (show annotations) (download)
Wed Dec 24 14:23:06 2014 UTC (9 years, 3 months ago) by capela
File size: 86099 byte(s)
* Master volume slider now getting proper layout when changing its
  main toolbar orientation.
1 // qsamplerMainForm.cpp
2 //
3 /****************************************************************************
4 Copyright (C) 2004-2014, rncbc aka Rui Nuno Capela. All rights reserved.
5 Copyright (C) 2007, 2008 Christian Schoenebeck
6
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License
9 as published by the Free Software Foundation; either version 2
10 of the License, or (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License along
18 with this program; if not, write to the Free Software Foundation, Inc.,
19 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
21 *****************************************************************************/
22
23 #include "qsamplerAbout.h"
24 #include "qsamplerMainForm.h"
25
26 #include "qsamplerOptions.h"
27 #include "qsamplerChannel.h"
28 #include "qsamplerMessages.h"
29
30 #include "qsamplerChannelStrip.h"
31 #include "qsamplerInstrumentList.h"
32
33 #include "qsamplerInstrumentListForm.h"
34 #include "qsamplerDeviceForm.h"
35 #include "qsamplerOptionsForm.h"
36 #include "qsamplerDeviceStatusForm.h"
37
38 #include <QMdiArea>
39 #include <QMdiSubWindow>
40
41 #include <QApplication>
42 #include <QProcess>
43 #include <QMessageBox>
44
45 #include <QRegExp>
46 #include <QTextStream>
47 #include <QFileDialog>
48 #include <QFileInfo>
49 #include <QFile>
50 #include <QUrl>
51
52 #include <QDragEnterEvent>
53
54 #include <QStatusBar>
55 #include <QSpinBox>
56 #include <QSlider>
57 #include <QLabel>
58 #include <QTimer>
59 #include <QDateTime>
60
61 #if QT_VERSION >= 0x050000
62 #include <QMimeData>
63 #endif
64
65 #if QT_VERSION < 0x040500
66 namespace Qt {
67 const WindowFlags WindowCloseButtonHint = WindowFlags(0x08000000);
68 }
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 (QMessageBox::warning(this,
1409 QSAMPLER_TITLE ": " + tr("Warning"),
1410 tr("Resetting the sampler instance will close\n"
1411 "all device and channel configurations.\n\n"
1412 "Please note that this operation may cause\n"
1413 "temporary MIDI and Audio disruption.\n\n"
1414 "Do you want to reset the sampler engine now?"),
1415 QMessageBox::Ok | QMessageBox::Cancel)
1416 == QMessageBox::Cancel)
1417 return;
1418
1419 // Trye closing the current session, first...
1420 if (!closeSession(true))
1421 return;
1422
1423 // Just do the reset, after closing down current session...
1424 // Do the actual sampler reset...
1425 if (::lscp_reset_sampler(m_pClient) != LSCP_OK) {
1426 appendMessagesClient("lscp_reset_sampler");
1427 appendMessagesError(tr("Could not reset sampler instance.\n\nSorry."));
1428 return;
1429 }
1430
1431 // Log this.
1432 appendMessages(tr("Sampler reset."));
1433
1434 // Make it a new session...
1435 newSession();
1436 }
1437
1438
1439 // Restart the client/server instance.
1440 void MainForm::fileRestart (void)
1441 {
1442 if (m_pOptions == NULL)
1443 return;
1444
1445 bool bRestart = true;
1446
1447 // Ask user whether he/she want's a complete restart...
1448 // (if we're currently up and running)
1449 if (bRestart && m_pClient) {
1450 bRestart = (QMessageBox::warning(this,
1451 QSAMPLER_TITLE ": " + tr("Warning"),
1452 tr("New settings will be effective after\n"
1453 "restarting the client/server connection.\n\n"
1454 "Please note that this operation may cause\n"
1455 "temporary MIDI and Audio disruption.\n\n"
1456 "Do you want to restart the connection now?"),
1457 QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok);
1458 }
1459
1460 // Are we still for it?
1461 if (bRestart && closeSession(true)) {
1462 // Stop server, it will force the client too.
1463 stopServer();
1464 // Reschedule a restart...
1465 startSchedule(m_pOptions->iStartDelay);
1466 }
1467 }
1468
1469
1470 // Exit application program.
1471 void MainForm::fileExit (void)
1472 {
1473 // Go for close the whole thing.
1474 close();
1475 }
1476
1477
1478 //-------------------------------------------------------------------------
1479 // qsamplerMainForm -- Edit Action slots.
1480
1481 // Add a new sampler channel.
1482 void MainForm::editAddChannel (void)
1483 {
1484 if (m_pClient == NULL)
1485 return;
1486
1487 // Just create the channel instance...
1488 Channel *pChannel = new Channel();
1489 if (pChannel == NULL)
1490 return;
1491
1492 // Before we show it up, may be we'll
1493 // better ask for some initial values?
1494 if (!pChannel->channelSetup(this)) {
1495 delete pChannel;
1496 return;
1497 }
1498
1499 // And give it to the strip...
1500 // (will own the channel instance, if successful).
1501 if (!createChannelStrip(pChannel)) {
1502 delete pChannel;
1503 return;
1504 }
1505
1506 // Do we auto-arrange?
1507 if (m_pOptions && m_pOptions->bAutoArrange)
1508 channelsArrange();
1509
1510 // Make that an overall update.
1511 m_iDirtyCount++;
1512 stabilizeForm();
1513 }
1514
1515
1516 // Remove current sampler channel.
1517 void MainForm::editRemoveChannel (void)
1518 {
1519 if (m_pClient == NULL)
1520 return;
1521
1522 ChannelStrip *pChannelStrip = activeChannelStrip();
1523 if (pChannelStrip == NULL)
1524 return;
1525
1526 Channel *pChannel = pChannelStrip->channel();
1527 if (pChannel == NULL)
1528 return;
1529
1530 // Prompt user if he/she's sure about this...
1531 if (m_pOptions && m_pOptions->bConfirmRemove) {
1532 if (QMessageBox::warning(this,
1533 QSAMPLER_TITLE ": " + tr("Warning"),
1534 tr("About to remove channel:\n\n"
1535 "%1\n\n"
1536 "Are you sure?")
1537 .arg(pChannelStrip->windowTitle()),
1538 QMessageBox::Ok | QMessageBox::Cancel)
1539 == QMessageBox::Cancel)
1540 return;
1541 }
1542
1543 // Remove the existing sampler channel.
1544 if (!pChannel->removeChannel())
1545 return;
1546
1547 // We'll be dirty, for sure...
1548 m_iDirtyCount++;
1549
1550 // Just delete the channel strip.
1551 destroyChannelStrip(pChannelStrip);
1552 }
1553
1554
1555 // Setup current sampler channel.
1556 void MainForm::editSetupChannel (void)
1557 {
1558 if (m_pClient == NULL)
1559 return;
1560
1561 ChannelStrip *pChannelStrip = activeChannelStrip();
1562 if (pChannelStrip == NULL)
1563 return;
1564
1565 // Just invoque the channel strip procedure.
1566 pChannelStrip->channelSetup();
1567 }
1568
1569
1570 // Edit current sampler channel.
1571 void MainForm::editEditChannel (void)
1572 {
1573 if (m_pClient == NULL)
1574 return;
1575
1576 ChannelStrip *pChannelStrip = activeChannelStrip();
1577 if (pChannelStrip == NULL)
1578 return;
1579
1580 // Just invoque the channel strip procedure.
1581 pChannelStrip->channelEdit();
1582 }
1583
1584
1585 // Reset current sampler channel.
1586 void MainForm::editResetChannel (void)
1587 {
1588 if (m_pClient == NULL)
1589 return;
1590
1591 ChannelStrip *pChannelStrip = activeChannelStrip();
1592 if (pChannelStrip == NULL)
1593 return;
1594
1595 // Just invoque the channel strip procedure.
1596 pChannelStrip->channelReset();
1597 }
1598
1599
1600 // Reset all sampler channels.
1601 void MainForm::editResetAllChannels (void)
1602 {
1603 if (m_pClient == NULL)
1604 return;
1605
1606 // Invoque the channel strip procedure,
1607 // for all channels out there...
1608 m_pWorkspace->setUpdatesEnabled(false);
1609 QList<QMdiSubWindow *> wlist = m_pWorkspace->subWindowList();
1610 for (int iChannel = 0; iChannel < (int) wlist.count(); ++iChannel) {
1611 ChannelStrip *pChannelStrip = NULL;
1612 QMdiSubWindow *pMdiSubWindow = wlist.at(iChannel);
1613 if (pMdiSubWindow)
1614 pChannelStrip = static_cast<ChannelStrip *> (pMdiSubWindow->widget());
1615 if (pChannelStrip)
1616 pChannelStrip->channelReset();
1617 }
1618 m_pWorkspace->setUpdatesEnabled(true);
1619 }
1620
1621
1622 //-------------------------------------------------------------------------
1623 // qsamplerMainForm -- View Action slots.
1624
1625 // Show/hide the main program window menubar.
1626 void MainForm::viewMenubar ( bool bOn )
1627 {
1628 if (bOn)
1629 m_ui.MenuBar->show();
1630 else
1631 m_ui.MenuBar->hide();
1632 }
1633
1634
1635 // Show/hide the main program window toolbar.
1636 void MainForm::viewToolbar ( bool bOn )
1637 {
1638 if (bOn) {
1639 m_ui.fileToolbar->show();
1640 m_ui.editToolbar->show();
1641 m_ui.channelsToolbar->show();
1642 } else {
1643 m_ui.fileToolbar->hide();
1644 m_ui.editToolbar->hide();
1645 m_ui.channelsToolbar->hide();
1646 }
1647 }
1648
1649
1650 // Show/hide the main program window statusbar.
1651 void MainForm::viewStatusbar ( bool bOn )
1652 {
1653 if (bOn)
1654 statusBar()->show();
1655 else
1656 statusBar()->hide();
1657 }
1658
1659
1660 // Show/hide the messages window logger.
1661 void MainForm::viewMessages ( bool bOn )
1662 {
1663 if (bOn)
1664 m_pMessages->show();
1665 else
1666 m_pMessages->hide();
1667 }
1668
1669
1670 // Show/hide the MIDI instrument list-view form.
1671 void MainForm::viewInstruments (void)
1672 {
1673 if (m_pOptions == NULL)
1674 return;
1675
1676 if (m_pInstrumentListForm) {
1677 m_pOptions->saveWidgetGeometry(m_pInstrumentListForm);
1678 if (m_pInstrumentListForm->isVisible()) {
1679 m_pInstrumentListForm->hide();
1680 } else {
1681 m_pInstrumentListForm->show();
1682 m_pInstrumentListForm->raise();
1683 m_pInstrumentListForm->activateWindow();
1684 }
1685 }
1686 }
1687
1688
1689 // Show/hide the device configurator form.
1690 void MainForm::viewDevices (void)
1691 {
1692 if (m_pOptions == NULL)
1693 return;
1694
1695 if (m_pDeviceForm) {
1696 m_pOptions->saveWidgetGeometry(m_pDeviceForm);
1697 if (m_pDeviceForm->isVisible()) {
1698 m_pDeviceForm->hide();
1699 } else {
1700 m_pDeviceForm->show();
1701 m_pDeviceForm->raise();
1702 m_pDeviceForm->activateWindow();
1703 }
1704 }
1705 }
1706
1707
1708 // Show options dialog.
1709 void MainForm::viewOptions (void)
1710 {
1711 if (m_pOptions == NULL)
1712 return;
1713
1714 OptionsForm* pOptionsForm = new OptionsForm(this);
1715 if (pOptionsForm) {
1716 // Check out some initial nullities(tm)...
1717 ChannelStrip *pChannelStrip = activeChannelStrip();
1718 if (m_pOptions->sDisplayFont.isEmpty() && pChannelStrip)
1719 m_pOptions->sDisplayFont = pChannelStrip->displayFont().toString();
1720 if (m_pOptions->sMessagesFont.isEmpty() && m_pMessages)
1721 m_pOptions->sMessagesFont = m_pMessages->messagesFont().toString();
1722 // To track down deferred or immediate changes.
1723 QString sOldServerHost = m_pOptions->sServerHost;
1724 int iOldServerPort = m_pOptions->iServerPort;
1725 int iOldServerTimeout = m_pOptions->iServerTimeout;
1726 bool bOldServerStart = m_pOptions->bServerStart;
1727 QString sOldServerCmdLine = m_pOptions->sServerCmdLine;
1728 bool bOldMessagesLog = m_pOptions->bMessagesLog;
1729 QString sOldMessagesLogPath = m_pOptions->sMessagesLogPath;
1730 QString sOldDisplayFont = m_pOptions->sDisplayFont;
1731 bool bOldDisplayEffect = m_pOptions->bDisplayEffect;
1732 int iOldMaxVolume = m_pOptions->iMaxVolume;
1733 QString sOldMessagesFont = m_pOptions->sMessagesFont;
1734 bool bOldKeepOnTop = m_pOptions->bKeepOnTop;
1735 bool bOldStdoutCapture = m_pOptions->bStdoutCapture;
1736 int bOldMessagesLimit = m_pOptions->bMessagesLimit;
1737 int iOldMessagesLimitLines = m_pOptions->iMessagesLimitLines;
1738 bool bOldCompletePath = m_pOptions->bCompletePath;
1739 bool bOldInstrumentNames = m_pOptions->bInstrumentNames;
1740 int iOldMaxRecentFiles = m_pOptions->iMaxRecentFiles;
1741 int iOldBaseFontSize = m_pOptions->iBaseFontSize;
1742 // Load the current setup settings.
1743 pOptionsForm->setup(m_pOptions);
1744 // Show the setup dialog...
1745 if (pOptionsForm->exec()) {
1746 // Warn if something will be only effective on next run.
1747 if (( bOldStdoutCapture && !m_pOptions->bStdoutCapture) ||
1748 (!bOldStdoutCapture && m_pOptions->bStdoutCapture) ||
1749 ( bOldKeepOnTop && !m_pOptions->bKeepOnTop) ||
1750 (!bOldKeepOnTop && m_pOptions->bKeepOnTop) ||
1751 (iOldBaseFontSize != m_pOptions->iBaseFontSize)) {
1752 QMessageBox::information(this,
1753 QSAMPLER_TITLE ": " + tr("Information"),
1754 tr("Some settings may be only effective\n"
1755 "next time you start this program."));
1756 updateMessagesCapture();
1757 }
1758 // Check wheather something immediate has changed.
1759 if (( bOldMessagesLog && !m_pOptions->bMessagesLog) ||
1760 (!bOldMessagesLog && m_pOptions->bMessagesLog) ||
1761 (sOldMessagesLogPath != m_pOptions->sMessagesLogPath))
1762 m_pMessages->setLogging(
1763 m_pOptions->bMessagesLog, m_pOptions->sMessagesLogPath);
1764 if (( bOldCompletePath && !m_pOptions->bCompletePath) ||
1765 (!bOldCompletePath && m_pOptions->bCompletePath) ||
1766 (iOldMaxRecentFiles != m_pOptions->iMaxRecentFiles))
1767 updateRecentFilesMenu();
1768 if (( bOldInstrumentNames && !m_pOptions->bInstrumentNames) ||
1769 (!bOldInstrumentNames && m_pOptions->bInstrumentNames))
1770 updateInstrumentNames();
1771 if (( bOldDisplayEffect && !m_pOptions->bDisplayEffect) ||
1772 (!bOldDisplayEffect && m_pOptions->bDisplayEffect))
1773 updateDisplayEffect();
1774 if (sOldDisplayFont != m_pOptions->sDisplayFont)
1775 updateDisplayFont();
1776 if (iOldMaxVolume != m_pOptions->iMaxVolume)
1777 updateMaxVolume();
1778 if (sOldMessagesFont != m_pOptions->sMessagesFont)
1779 updateMessagesFont();
1780 if (( bOldMessagesLimit && !m_pOptions->bMessagesLimit) ||
1781 (!bOldMessagesLimit && m_pOptions->bMessagesLimit) ||
1782 (iOldMessagesLimitLines != m_pOptions->iMessagesLimitLines))
1783 updateMessagesLimit();
1784 // And now the main thing, whether we'll do client/server recycling?
1785 if ((sOldServerHost != m_pOptions->sServerHost) ||
1786 (iOldServerPort != m_pOptions->iServerPort) ||
1787 (iOldServerTimeout != m_pOptions->iServerTimeout) ||
1788 ( bOldServerStart && !m_pOptions->bServerStart) ||
1789 (!bOldServerStart && m_pOptions->bServerStart) ||
1790 (sOldServerCmdLine != m_pOptions->sServerCmdLine
1791 && m_pOptions->bServerStart))
1792 fileRestart();
1793 }
1794 // Done.
1795 delete pOptionsForm;
1796 }
1797
1798 // This makes it.
1799 stabilizeForm();
1800 }
1801
1802
1803 //-------------------------------------------------------------------------
1804 // qsamplerMainForm -- Channels action slots.
1805
1806 // Arrange channel strips.
1807 void MainForm::channelsArrange (void)
1808 {
1809 // Full width vertical tiling
1810 QList<QMdiSubWindow *> wlist = m_pWorkspace->subWindowList();
1811 if (wlist.isEmpty())
1812 return;
1813
1814 m_pWorkspace->setUpdatesEnabled(false);
1815 int y = 0;
1816 for (int iChannel = 0; iChannel < (int) wlist.count(); ++iChannel) {
1817 ChannelStrip *pChannelStrip = NULL;
1818 QMdiSubWindow *pMdiSubWindow = wlist.at(iChannel);
1819 if (pMdiSubWindow)
1820 pChannelStrip = static_cast<ChannelStrip *> (pMdiSubWindow->widget());
1821 if (pChannelStrip) {
1822 /* if (pChannelStrip->testWState(WState_Maximized | WState_Minimized)) {
1823 // Prevent flicker...
1824 pChannelStrip->hide();
1825 pChannelStrip->showNormal();
1826 } */
1827 pChannelStrip->adjustSize();
1828 int iWidth = m_pWorkspace->width();
1829 if (iWidth < pChannelStrip->width())
1830 iWidth = pChannelStrip->width();
1831 // int iHeight = pChannelStrip->height()
1832 // + pChannelStrip->parentWidget()->baseSize().height();
1833 int iHeight = pChannelStrip->parentWidget()->frameGeometry().height();
1834 pChannelStrip->parentWidget()->setGeometry(0, y, iWidth, iHeight);
1835 y += iHeight;
1836 }
1837 }
1838 m_pWorkspace->setUpdatesEnabled(true);
1839
1840 stabilizeForm();
1841 }
1842
1843
1844 // Auto-arrange channel strips.
1845 void MainForm::channelsAutoArrange ( bool bOn )
1846 {
1847 if (m_pOptions == NULL)
1848 return;
1849
1850 // Toggle the auto-arrange flag.
1851 m_pOptions->bAutoArrange = bOn;
1852
1853 // If on, update whole workspace...
1854 if (m_pOptions->bAutoArrange)
1855 channelsArrange();
1856 }
1857
1858
1859 //-------------------------------------------------------------------------
1860 // qsamplerMainForm -- Help Action slots.
1861
1862 // Show information about the Qt toolkit.
1863 void MainForm::helpAboutQt (void)
1864 {
1865 QMessageBox::aboutQt(this);
1866 }
1867
1868
1869 // Show information about application program.
1870 void MainForm::helpAbout (void)
1871 {
1872 // Stuff the about box text...
1873 QString sText = "<p>\n";
1874 sText += "<b>" QSAMPLER_TITLE " - " + tr(QSAMPLER_SUBTITLE) + "</b><br />\n";
1875 sText += "<br />\n";
1876 sText += tr("Version") + ": <b>" QSAMPLER_VERSION "</b><br />\n";
1877 sText += "<small>" + tr("Build") + ": " __DATE__ " " __TIME__ "</small><br />\n";
1878 #ifdef CONFIG_DEBUG
1879 sText += "<small><font color=\"red\">";
1880 sText += tr("Debugging option enabled.");
1881 sText += "</font></small><br />";
1882 #endif
1883 #ifndef CONFIG_LIBGIG
1884 sText += "<small><font color=\"red\">";
1885 sText += tr("GIG (libgig) file support disabled.");
1886 sText += "</font></small><br />";
1887 #endif
1888 #ifndef CONFIG_INSTRUMENT_NAME
1889 sText += "<small><font color=\"red\">";
1890 sText += tr("LSCP (liblscp) instrument_name support disabled.");
1891 sText += "</font></small><br />";
1892 #endif
1893 #ifndef CONFIG_MUTE_SOLO
1894 sText += "<small><font color=\"red\">";
1895 sText += tr("Sampler channel Mute/Solo support disabled.");
1896 sText += "</font></small><br />";
1897 #endif
1898 #ifndef CONFIG_AUDIO_ROUTING
1899 sText += "<small><font color=\"red\">";
1900 sText += tr("LSCP (liblscp) audio_routing support disabled.");
1901 sText += "</font></small><br />";
1902 #endif
1903 #ifndef CONFIG_FXSEND
1904 sText += "<small><font color=\"red\">";
1905 sText += tr("Sampler channel Effect Sends support disabled.");
1906 sText += "</font></small><br />";
1907 #endif
1908 #ifndef CONFIG_VOLUME
1909 sText += "<small><font color=\"red\">";
1910 sText += tr("Global volume support disabled.");
1911 sText += "</font></small><br />";
1912 #endif
1913 #ifndef CONFIG_MIDI_INSTRUMENT
1914 sText += "<small><font color=\"red\">";
1915 sText += tr("MIDI instrument mapping support disabled.");
1916 sText += "</font></small><br />";
1917 #endif
1918 #ifndef CONFIG_EDIT_INSTRUMENT
1919 sText += "<small><font color=\"red\">";
1920 sText += tr("Instrument editing support disabled.");
1921 sText += "</font></small><br />";
1922 #endif
1923 #ifndef CONFIG_EVENT_CHANNEL_MIDI
1924 sText += "<small><font color=\"red\">";
1925 sText += tr("Channel MIDI event support disabled.");
1926 sText += "</font></small><br />";
1927 #endif
1928 #ifndef CONFIG_EVENT_DEVICE_MIDI
1929 sText += "<small><font color=\"red\">";
1930 sText += tr("Device MIDI event support disabled.");
1931 sText += "</font></small><br />";
1932 #endif
1933 #ifndef CONFIG_MAX_VOICES
1934 sText += "<small><font color=\"red\">";
1935 sText += tr("Runtime max. voices / disk streams support disabled.");
1936 sText += "</font></small><br />";
1937 #endif
1938 sText += "<br />\n";
1939 sText += tr("Using") + ": ";
1940 sText += ::lscp_client_package();
1941 sText += " ";
1942 sText += ::lscp_client_version();
1943 #ifdef CONFIG_LIBGIG
1944 sText += ", ";
1945 sText += gig::libraryName().c_str();
1946 sText += " ";
1947 sText += gig::libraryVersion().c_str();
1948 #endif
1949 sText += "<br />\n";
1950 sText += "<br />\n";
1951 sText += tr("Website") + ": <a href=\"" QSAMPLER_WEBSITE "\">" QSAMPLER_WEBSITE "</a><br />\n";
1952 sText += "<br />\n";
1953 sText += "<small>";
1954 sText += QSAMPLER_COPYRIGHT "<br />\n";
1955 sText += QSAMPLER_COPYRIGHT2 "<br />\n";
1956 sText += "<br />\n";
1957 sText += tr("This program is free software; you can redistribute it and/or modify it") + "<br />\n";
1958 sText += tr("under the terms of the GNU General Public License version 2 or later.");
1959 sText += "</small>";
1960 sText += "</p>\n";
1961
1962 QMessageBox::about(this, tr("About") + " " QSAMPLER_TITLE, sText);
1963 }
1964
1965
1966 //-------------------------------------------------------------------------
1967 // qsamplerMainForm -- Main window stabilization.
1968
1969 void MainForm::stabilizeForm (void)
1970 {
1971 // Update the main application caption...
1972 QString sSessionName = sessionName(m_sFilename);
1973 if (m_iDirtyCount > 0)
1974 sSessionName += " *";
1975 setWindowTitle(tr(QSAMPLER_TITLE " - [%1]").arg(sSessionName));
1976
1977 // Update the main menu state...
1978 ChannelStrip *pChannelStrip = activeChannelStrip();
1979 bool bHasClient = (m_pOptions != NULL && m_pClient != NULL);
1980 bool bHasChannel = (bHasClient && pChannelStrip != NULL);
1981 bool bHasChannels = (bHasClient && m_pWorkspace->subWindowList().count() > 0);
1982 m_ui.fileNewAction->setEnabled(bHasClient);
1983 m_ui.fileOpenAction->setEnabled(bHasClient);
1984 m_ui.fileSaveAction->setEnabled(bHasClient && m_iDirtyCount > 0);
1985 m_ui.fileSaveAsAction->setEnabled(bHasClient);
1986 m_ui.fileResetAction->setEnabled(bHasClient);
1987 m_ui.fileRestartAction->setEnabled(bHasClient || m_pServer == NULL);
1988 m_ui.editAddChannelAction->setEnabled(bHasClient);
1989 m_ui.editRemoveChannelAction->setEnabled(bHasChannel);
1990 m_ui.editSetupChannelAction->setEnabled(bHasChannel);
1991 #ifdef CONFIG_EDIT_INSTRUMENT
1992 m_ui.editEditChannelAction->setEnabled(bHasChannel);
1993 #else
1994 m_ui.editEditChannelAction->setEnabled(false);
1995 #endif
1996 m_ui.editResetChannelAction->setEnabled(bHasChannel);
1997 m_ui.editResetAllChannelsAction->setEnabled(bHasChannels);
1998 m_ui.viewMessagesAction->setChecked(m_pMessages && m_pMessages->isVisible());
1999 #ifdef CONFIG_MIDI_INSTRUMENT
2000 m_ui.viewInstrumentsAction->setChecked(m_pInstrumentListForm
2001 && m_pInstrumentListForm->isVisible());
2002 m_ui.viewInstrumentsAction->setEnabled(bHasClient);
2003 #else
2004 m_ui.viewInstrumentsAction->setEnabled(false);
2005 #endif
2006 m_ui.viewDevicesAction->setChecked(m_pDeviceForm
2007 && m_pDeviceForm->isVisible());
2008 m_ui.viewDevicesAction->setEnabled(bHasClient);
2009 m_ui.viewMidiDeviceStatusMenu->setEnabled(
2010 DeviceStatusForm::getInstances().size() > 0);
2011 m_ui.channelsArrangeAction->setEnabled(bHasChannels);
2012
2013 #ifdef CONFIG_VOLUME
2014 // Toolbar widgets are also affected...
2015 m_pVolumeSlider->setEnabled(bHasClient);
2016 m_pVolumeSpinBox->setEnabled(bHasClient);
2017 #endif
2018
2019 // Client/Server status...
2020 if (bHasClient) {
2021 m_statusItem[QSAMPLER_STATUS_CLIENT]->setText(tr("Connected"));
2022 m_statusItem[QSAMPLER_STATUS_SERVER]->setText(m_pOptions->sServerHost
2023 + ':' + QString::number(m_pOptions->iServerPort));
2024 } else {
2025 m_statusItem[QSAMPLER_STATUS_CLIENT]->clear();
2026 m_statusItem[QSAMPLER_STATUS_SERVER]->clear();
2027 }
2028 // Channel status...
2029 if (bHasChannel)
2030 m_statusItem[QSAMPLER_STATUS_CHANNEL]->setText(pChannelStrip->windowTitle());
2031 else
2032 m_statusItem[QSAMPLER_STATUS_CHANNEL]->clear();
2033 // Session status...
2034 if (m_iDirtyCount > 0)
2035 m_statusItem[QSAMPLER_STATUS_SESSION]->setText(tr("MOD"));
2036 else
2037 m_statusItem[QSAMPLER_STATUS_SESSION]->clear();
2038
2039 // Recent files menu.
2040 m_ui.fileOpenRecentMenu->setEnabled(m_pOptions->recentFiles.count() > 0);
2041 }
2042
2043
2044 // Global volume change receiver slot.
2045 void MainForm::volumeChanged ( int iVolume )
2046 {
2047 #ifdef CONFIG_VOLUME
2048
2049 if (m_iVolumeChanging > 0)
2050 return;
2051
2052 m_iVolumeChanging++;
2053
2054 // Update the toolbar widgets...
2055 if (m_pVolumeSlider->value() != iVolume)
2056 m_pVolumeSlider->setValue(iVolume);
2057 if (m_pVolumeSpinBox->value() != iVolume)
2058 m_pVolumeSpinBox->setValue(iVolume);
2059
2060 // Do it as commanded...
2061 float fVolume = 0.01f * float(iVolume);
2062 if (::lscp_set_volume(m_pClient, fVolume) == LSCP_OK)
2063 appendMessages(QObject::tr("Volume: %1.").arg(fVolume));
2064 else
2065 appendMessagesClient("lscp_set_volume");
2066
2067 m_iVolumeChanging--;
2068
2069 m_iDirtyCount++;
2070 stabilizeForm();
2071
2072 #endif
2073 }
2074
2075
2076 // Channel change receiver slot.
2077 void MainForm::channelStripChanged ( ChannelStrip *pChannelStrip )
2078 {
2079 // Add this strip to the changed list...
2080 if (!m_changedStrips.contains(pChannelStrip)) {
2081 m_changedStrips.append(pChannelStrip);
2082 pChannelStrip->resetErrorCount();
2083 }
2084
2085 // Just mark the dirty form.
2086 m_iDirtyCount++;
2087 // and update the form status...
2088 stabilizeForm();
2089 }
2090
2091
2092 // Grab and restore current sampler channels session.
2093 void MainForm::updateSession (void)
2094 {
2095 #ifdef CONFIG_VOLUME
2096 int iVolume = ::lroundf(100.0f * ::lscp_get_volume(m_pClient));
2097 m_iVolumeChanging++;
2098 m_pVolumeSlider->setValue(iVolume);
2099 m_pVolumeSpinBox->setValue(iVolume);
2100 m_iVolumeChanging--;
2101 #endif
2102 #ifdef CONFIG_MIDI_INSTRUMENT
2103 // FIXME: Make some room for default instrument maps...
2104 int iMaps = ::lscp_get_midi_instrument_maps(m_pClient);
2105 if (iMaps < 0)
2106 appendMessagesClient("lscp_get_midi_instrument_maps");
2107 else if (iMaps < 1) {
2108 ::lscp_add_midi_instrument_map(m_pClient,
2109 tr("Chromatic").toUtf8().constData());
2110 ::lscp_add_midi_instrument_map(m_pClient,
2111 tr("Drum Kits").toUtf8().constData());
2112 }
2113 #endif
2114
2115 updateAllChannelStrips(false);
2116
2117 // Do we auto-arrange?
2118 if (m_pOptions && m_pOptions->bAutoArrange)
2119 channelsArrange();
2120
2121 // Remember to refresh devices and instruments...
2122 if (m_pInstrumentListForm)
2123 m_pInstrumentListForm->refreshInstruments();
2124 if (m_pDeviceForm)
2125 m_pDeviceForm->refreshDevices();
2126 }
2127
2128
2129 void MainForm::updateAllChannelStrips ( bool bRemoveDeadStrips )
2130 {
2131 // Retrieve the current channel list.
2132 int *piChannelIDs = ::lscp_list_channels(m_pClient);
2133 if (piChannelIDs == NULL) {
2134 if (::lscp_client_get_errno(m_pClient)) {
2135 appendMessagesClient("lscp_list_channels");
2136 appendMessagesError(
2137 tr("Could not get current list of channels.\n\nSorry."));
2138 }
2139 } else {
2140 // Try to (re)create each channel.
2141 m_pWorkspace->setUpdatesEnabled(false);
2142 for (int iChannel = 0; piChannelIDs[iChannel] >= 0; ++iChannel) {
2143 // Check if theres already a channel strip for this one...
2144 if (!channelStrip(piChannelIDs[iChannel]))
2145 createChannelStrip(new Channel(piChannelIDs[iChannel]));
2146 }
2147 // Do we auto-arrange?
2148 if (m_pOptions && m_pOptions->bAutoArrange)
2149 channelsArrange();
2150 // remove dead channel strips
2151 if (bRemoveDeadStrips) {
2152 QList<QMdiSubWindow *> wlist = m_pWorkspace->subWindowList();
2153 for (int iChannel = 0; iChannel < (int) wlist.count(); ++iChannel) {
2154 ChannelStrip *pChannelStrip = NULL;
2155 QMdiSubWindow *pMdiSubWindow = wlist.at(iChannel);
2156 if (pMdiSubWindow)
2157 pChannelStrip = static_cast<ChannelStrip *> (pMdiSubWindow->widget());
2158 if (pChannelStrip) {
2159 bool bExists = false;
2160 for (int j = 0; piChannelIDs[j] >= 0; ++j) {
2161 if (!pChannelStrip->channel())
2162 break;
2163 if (piChannelIDs[j] == pChannelStrip->channel()->channelID()) {
2164 // strip exists, don't touch it
2165 bExists = true;
2166 break;
2167 }
2168 }
2169 if (!bExists)
2170 destroyChannelStrip(pChannelStrip);
2171 }
2172 }
2173 }
2174 m_pWorkspace->setUpdatesEnabled(true);
2175 }
2176
2177 stabilizeForm();
2178 }
2179
2180
2181 // Update the recent files list and menu.
2182 void MainForm::updateRecentFiles ( const QString& sFilename )
2183 {
2184 if (m_pOptions == NULL)
2185 return;
2186
2187 // Remove from list if already there (avoid duplicates)
2188 int iIndex = m_pOptions->recentFiles.indexOf(sFilename);
2189 if (iIndex >= 0)
2190 m_pOptions->recentFiles.removeAt(iIndex);
2191 // Put it to front...
2192 m_pOptions->recentFiles.push_front(sFilename);
2193 }
2194
2195
2196 // Update the recent files list and menu.
2197 void MainForm::updateRecentFilesMenu (void)
2198 {
2199 if (m_pOptions == NULL)
2200 return;
2201
2202 // Time to keep the list under limits.
2203 int iRecentFiles = m_pOptions->recentFiles.count();
2204 while (iRecentFiles > m_pOptions->iMaxRecentFiles) {
2205 m_pOptions->recentFiles.pop_back();
2206 iRecentFiles--;
2207 }
2208
2209 // Rebuild the recent files menu...
2210 m_ui.fileOpenRecentMenu->clear();
2211 for (int i = 0; i < iRecentFiles; i++) {
2212 const QString& sFilename = m_pOptions->recentFiles[i];
2213 if (QFileInfo(sFilename).exists()) {
2214 QAction *pAction = m_ui.fileOpenRecentMenu->addAction(
2215 QString("&%1 %2").arg(i + 1).arg(sessionName(sFilename)),
2216 this, SLOT(fileOpenRecent()));
2217 pAction->setData(i);
2218 }
2219 }
2220 }
2221
2222
2223 // Force update of the channels instrument names mode.
2224 void MainForm::updateInstrumentNames (void)
2225 {
2226 // Full channel list update...
2227 QList<QMdiSubWindow *> wlist = m_pWorkspace->subWindowList();
2228 if (wlist.isEmpty())
2229 return;
2230
2231 m_pWorkspace->setUpdatesEnabled(false);
2232 for (int iChannel = 0; iChannel < (int) wlist.count(); ++iChannel) {
2233 ChannelStrip *pChannelStrip = (ChannelStrip *) wlist.at(iChannel);
2234 if (pChannelStrip)
2235 pChannelStrip->updateInstrumentName(true);
2236 }
2237 m_pWorkspace->setUpdatesEnabled(true);
2238 }
2239
2240
2241 // Force update of the channels display font.
2242 void MainForm::updateDisplayFont (void)
2243 {
2244 if (m_pOptions == NULL)
2245 return;
2246
2247 // Check if display font is legal.
2248 if (m_pOptions->sDisplayFont.isEmpty())
2249 return;
2250 // Realize it.
2251 QFont font;
2252 if (!font.fromString(m_pOptions->sDisplayFont))
2253 return;
2254
2255 // Full channel list update...
2256 QList<QMdiSubWindow *> wlist = m_pWorkspace->subWindowList();
2257 if (wlist.isEmpty())
2258 return;
2259
2260 m_pWorkspace->setUpdatesEnabled(false);
2261 for (int iChannel = 0; iChannel < (int) wlist.count(); ++iChannel) {
2262 ChannelStrip *pChannelStrip = NULL;
2263 QMdiSubWindow *pMdiSubWindow = wlist.at(iChannel);
2264 if (pMdiSubWindow)
2265 pChannelStrip = static_cast<ChannelStrip *> (pMdiSubWindow->widget());
2266 if (pChannelStrip)
2267 pChannelStrip->setDisplayFont(font);
2268 }
2269 m_pWorkspace->setUpdatesEnabled(true);
2270 }
2271
2272
2273 // Update channel strips background effect.
2274 void MainForm::updateDisplayEffect (void)
2275 {
2276 // Full channel list update...
2277 QList<QMdiSubWindow *> wlist = m_pWorkspace->subWindowList();
2278 if (wlist.isEmpty())
2279 return;
2280
2281 m_pWorkspace->setUpdatesEnabled(false);
2282 for (int iChannel = 0; iChannel < (int) wlist.count(); ++iChannel) {
2283 ChannelStrip *pChannelStrip = NULL;
2284 QMdiSubWindow *pMdiSubWindow = wlist.at(iChannel);
2285 if (pMdiSubWindow)
2286 pChannelStrip = static_cast<ChannelStrip *> (pMdiSubWindow->widget());
2287 if (pChannelStrip)
2288 pChannelStrip->setDisplayEffect(m_pOptions->bDisplayEffect);
2289 }
2290 m_pWorkspace->setUpdatesEnabled(true);
2291 }
2292
2293
2294 // Force update of the channels maximum volume setting.
2295 void MainForm::updateMaxVolume (void)
2296 {
2297 if (m_pOptions == NULL)
2298 return;
2299
2300 #ifdef CONFIG_VOLUME
2301 m_iVolumeChanging++;
2302 m_pVolumeSlider->setMaximum(m_pOptions->iMaxVolume);
2303 m_pVolumeSpinBox->setMaximum(m_pOptions->iMaxVolume);
2304 m_iVolumeChanging--;
2305 #endif
2306
2307 // Full channel list update...
2308 QList<QMdiSubWindow *> wlist = m_pWorkspace->subWindowList();
2309 if (wlist.isEmpty())
2310 return;
2311
2312 m_pWorkspace->setUpdatesEnabled(false);
2313 for (int iChannel = 0; iChannel < (int) wlist.count(); ++iChannel) {
2314 ChannelStrip *pChannelStrip = NULL;
2315 QMdiSubWindow *pMdiSubWindow = wlist.at(iChannel);
2316 if (pMdiSubWindow)
2317 pChannelStrip = static_cast<ChannelStrip *> (pMdiSubWindow->widget());
2318 if (pChannelStrip)
2319 pChannelStrip->setMaxVolume(m_pOptions->iMaxVolume);
2320 }
2321 m_pWorkspace->setUpdatesEnabled(true);
2322 }
2323
2324
2325 //-------------------------------------------------------------------------
2326 // qsamplerMainForm -- Messages window form handlers.
2327
2328 // Messages output methods.
2329 void MainForm::appendMessages( const QString& s )
2330 {
2331 if (m_pMessages)
2332 m_pMessages->appendMessages(s);
2333
2334 statusBar()->showMessage(s, 3000);
2335 }
2336
2337 void MainForm::appendMessagesColor( const QString& s, const QString& c )
2338 {
2339 if (m_pMessages)
2340 m_pMessages->appendMessagesColor(s, c);
2341
2342 statusBar()->showMessage(s, 3000);
2343 }
2344
2345 void MainForm::appendMessagesText( const QString& s )
2346 {
2347 if (m_pMessages)
2348 m_pMessages->appendMessagesText(s);
2349 }
2350
2351 void MainForm::appendMessagesError( const QString& s )
2352 {
2353 if (m_pMessages)
2354 m_pMessages->show();
2355
2356 appendMessagesColor(s.simplified(), "#ff0000");
2357
2358 // Make it look responsive...:)
2359 QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
2360
2361 QMessageBox::critical(this,
2362 QSAMPLER_TITLE ": " + tr("Error"), s, QMessageBox::Cancel);
2363 }
2364
2365
2366 // This is a special message format, just for client results.
2367 void MainForm::appendMessagesClient( const QString& s )
2368 {
2369 if (m_pClient == NULL)
2370 return;
2371
2372 appendMessagesColor(s + QString(": %1 (errno=%2)")
2373 .arg(::lscp_client_get_result(m_pClient))
2374 .arg(::lscp_client_get_errno(m_pClient)), "#996666");
2375
2376 // Make it look responsive...:)
2377 QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
2378 }
2379
2380
2381 // Force update of the messages font.
2382 void MainForm::updateMessagesFont (void)
2383 {
2384 if (m_pOptions == NULL)
2385 return;
2386
2387 if (m_pMessages && !m_pOptions->sMessagesFont.isEmpty()) {
2388 QFont font;
2389 if (font.fromString(m_pOptions->sMessagesFont))
2390 m_pMessages->setMessagesFont(font);
2391 }
2392 }
2393
2394
2395 // Update messages window line limit.
2396 void MainForm::updateMessagesLimit (void)
2397 {
2398 if (m_pOptions == NULL)
2399 return;
2400
2401 if (m_pMessages) {
2402 if (m_pOptions->bMessagesLimit)
2403 m_pMessages->setMessagesLimit(m_pOptions->iMessagesLimitLines);
2404 else
2405 m_pMessages->setMessagesLimit(-1);
2406 }
2407 }
2408
2409
2410 // Enablement of the messages capture feature.
2411 void MainForm::updateMessagesCapture (void)
2412 {
2413 if (m_pOptions == NULL)
2414 return;
2415
2416 if (m_pMessages)
2417 m_pMessages->setCaptureEnabled(m_pOptions->bStdoutCapture);
2418 }
2419
2420
2421 //-------------------------------------------------------------------------
2422 // qsamplerMainForm -- MDI channel strip management.
2423
2424 // The channel strip creation executive.
2425 ChannelStrip *MainForm::createChannelStrip ( Channel *pChannel )
2426 {
2427 if (m_pClient == NULL || pChannel == NULL)
2428 return NULL;
2429
2430 // Add a new channel itema...
2431 ChannelStrip *pChannelStrip = new ChannelStrip();
2432 if (pChannelStrip == NULL)
2433 return NULL;
2434
2435 // Set some initial channel strip options...
2436 if (m_pOptions) {
2437 // Background display effect...
2438 pChannelStrip->setDisplayEffect(m_pOptions->bDisplayEffect);
2439 // We'll need a display font.
2440 QFont font;
2441 if (font.fromString(m_pOptions->sDisplayFont))
2442 pChannelStrip->setDisplayFont(font);
2443 // Maximum allowed volume setting.
2444 pChannelStrip->setMaxVolume(m_pOptions->iMaxVolume);
2445 }
2446
2447 // Add it to workspace...
2448 m_pWorkspace->addSubWindow(pChannelStrip,
2449 Qt::SubWindow | Qt::FramelessWindowHint);
2450
2451 // Actual channel strip setup...
2452 pChannelStrip->setup(pChannel);
2453
2454 QObject::connect(pChannelStrip,
2455 SIGNAL(channelChanged(ChannelStrip *)),
2456 SLOT(channelStripChanged(ChannelStrip *)));
2457
2458 // Now we show up us to the world.
2459 pChannelStrip->show();
2460
2461 // This is pretty new, so we'll watch for it closely.
2462 channelStripChanged(pChannelStrip);
2463
2464 // Return our successful reference...
2465 return pChannelStrip;
2466 }
2467
2468
2469 void MainForm::destroyChannelStrip ( ChannelStrip *pChannelStrip )
2470 {
2471 QMdiSubWindow *pMdiSubWindow
2472 = static_cast<QMdiSubWindow *> (pChannelStrip->parentWidget());
2473 if (pMdiSubWindow == NULL)
2474 return;
2475
2476 // Just delete the channel strip.
2477 delete pChannelStrip;
2478 delete pMdiSubWindow;
2479
2480 // Do we auto-arrange?
2481 if (m_pOptions && m_pOptions->bAutoArrange)
2482 channelsArrange();
2483
2484 stabilizeForm();
2485 }
2486
2487
2488 // Retrieve the active channel strip.
2489 ChannelStrip *MainForm::activeChannelStrip (void)
2490 {
2491 QMdiSubWindow *pMdiSubWindow = m_pWorkspace->activeSubWindow();
2492 if (pMdiSubWindow)
2493 return static_cast<ChannelStrip *> (pMdiSubWindow->widget());
2494 else
2495 return NULL;
2496 }
2497
2498
2499 // Retrieve a channel strip by index.
2500 ChannelStrip *MainForm::channelStripAt ( int iChannel )
2501 {
2502 if (!m_pWorkspace) return NULL;
2503
2504 QList<QMdiSubWindow *> wlist = m_pWorkspace->subWindowList();
2505 if (wlist.isEmpty())
2506 return NULL;
2507
2508 if (iChannel < 0 || iChannel >= wlist.size())
2509 return NULL;
2510
2511 QMdiSubWindow *pMdiSubWindow = wlist.at(iChannel);
2512 if (pMdiSubWindow)
2513 return static_cast<ChannelStrip *> (pMdiSubWindow->widget());
2514 else
2515 return NULL;
2516 }
2517
2518
2519 // Retrieve a channel strip by sampler channel id.
2520 ChannelStrip *MainForm::channelStrip ( int iChannelID )
2521 {
2522 QList<QMdiSubWindow *> wlist = m_pWorkspace->subWindowList();
2523 if (wlist.isEmpty())
2524 return NULL;
2525
2526 for (int iChannel = 0; iChannel < (int) wlist.count(); ++iChannel) {
2527 ChannelStrip *pChannelStrip = NULL;
2528 QMdiSubWindow *pMdiSubWindow = wlist.at(iChannel);
2529 if (pMdiSubWindow)
2530 pChannelStrip = static_cast<ChannelStrip *> (pMdiSubWindow->widget());
2531 if (pChannelStrip) {
2532 Channel *pChannel = pChannelStrip->channel();
2533 if (pChannel && pChannel->channelID() == iChannelID)
2534 return pChannelStrip;
2535 }
2536 }
2537
2538 // Not found.
2539 return NULL;
2540 }
2541
2542
2543 // Construct the windows menu.
2544 void MainForm::channelsMenuAboutToShow (void)
2545 {
2546 m_ui.channelsMenu->clear();
2547 m_ui.channelsMenu->addAction(m_ui.channelsArrangeAction);
2548 m_ui.channelsMenu->addAction(m_ui.channelsAutoArrangeAction);
2549
2550 QList<QMdiSubWindow *> wlist = m_pWorkspace->subWindowList();
2551 if (!wlist.isEmpty()) {
2552 m_ui.channelsMenu->addSeparator();
2553 for (int iChannel = 0; iChannel < (int) wlist.count(); ++iChannel) {
2554 ChannelStrip *pChannelStrip = NULL;
2555 QMdiSubWindow *pMdiSubWindow = wlist.at(iChannel);
2556 if (pMdiSubWindow)
2557 pChannelStrip = static_cast<ChannelStrip *> (pMdiSubWindow->widget());
2558 if (pChannelStrip) {
2559 QAction *pAction = m_ui.channelsMenu->addAction(
2560 pChannelStrip->windowTitle(),
2561 this, SLOT(channelsMenuActivated()));
2562 pAction->setCheckable(true);
2563 pAction->setChecked(activeChannelStrip() == pChannelStrip);
2564 pAction->setData(iChannel);
2565 }
2566 }
2567 }
2568 }
2569
2570
2571 // Windows menu activation slot
2572 void MainForm::channelsMenuActivated (void)
2573 {
2574 // Retrive channel index from action data...
2575 QAction *pAction = qobject_cast<QAction *> (sender());
2576 if (pAction == NULL)
2577 return;
2578
2579 ChannelStrip *pChannelStrip = channelStripAt(pAction->data().toInt());
2580 if (pChannelStrip) {
2581 pChannelStrip->showNormal();
2582 pChannelStrip->setFocus();
2583 }
2584 }
2585
2586
2587 //-------------------------------------------------------------------------
2588 // qsamplerMainForm -- Timer stuff.
2589
2590 // Set the pseudo-timer delay schedule.
2591 void MainForm::startSchedule ( int iStartDelay )
2592 {
2593 m_iStartDelay = 1 + (iStartDelay * 1000);
2594 m_iTimerDelay = 0;
2595 }
2596
2597 // Suspend the pseudo-timer delay schedule.
2598 void MainForm::stopSchedule (void)
2599 {
2600 m_iStartDelay = 0;
2601 m_iTimerDelay = 0;
2602 }
2603
2604 // Timer slot funtion.
2605 void MainForm::timerSlot (void)
2606 {
2607 if (m_pOptions == NULL)
2608 return;
2609
2610 // Is it the first shot on server start after a few delay?
2611 if (m_iTimerDelay < m_iStartDelay) {
2612 m_iTimerDelay += QSAMPLER_TIMER_MSECS;
2613 if (m_iTimerDelay >= m_iStartDelay) {
2614 // If we cannot start it now, maybe a lil'mo'later ;)
2615 if (!startClient()) {
2616 m_iStartDelay += m_iTimerDelay;
2617 m_iTimerDelay = 0;
2618 }
2619 }
2620 }
2621
2622 if (m_pClient) {
2623 // Update the channel information for each pending strip...
2624 QListIterator<ChannelStrip *> iter(m_changedStrips);
2625 while (iter.hasNext()) {
2626 ChannelStrip *pChannelStrip = iter.next();
2627 // If successfull, remove from pending list...
2628 if (pChannelStrip->updateChannelInfo()) {
2629 int iChannelStrip = m_changedStrips.indexOf(pChannelStrip);
2630 if (iChannelStrip >= 0)
2631 m_changedStrips.removeAt(iChannelStrip);
2632 }
2633 }
2634 // Refresh each channel usage, on each period...
2635 if (m_pOptions->bAutoRefresh) {
2636 m_iTimerSlot += QSAMPLER_TIMER_MSECS;
2637 if (m_iTimerSlot >= m_pOptions->iAutoRefreshTime) {
2638 m_iTimerSlot = 0;
2639 // Update the channel stream usage for each strip...
2640 QList<QMdiSubWindow *> wlist = m_pWorkspace->subWindowList();
2641 for (int iChannel = 0; iChannel < (int) wlist.count(); ++iChannel) {
2642 ChannelStrip *pChannelStrip = NULL;
2643 QMdiSubWindow *pMdiSubWindow = wlist.at(iChannel);
2644 if (pMdiSubWindow)
2645 pChannelStrip = static_cast<ChannelStrip *> (pMdiSubWindow->widget());
2646 if (pChannelStrip && pChannelStrip->isVisible())
2647 pChannelStrip->updateChannelUsage();
2648 }
2649 }
2650 }
2651 }
2652
2653 // Register the next timer slot.
2654 QTimer::singleShot(QSAMPLER_TIMER_MSECS, this, SLOT(timerSlot()));
2655 }
2656
2657
2658 //-------------------------------------------------------------------------
2659 // qsamplerMainForm -- Server stuff.
2660
2661 // Start linuxsampler server...
2662 void MainForm::startServer (void)
2663 {
2664 if (m_pOptions == NULL)
2665 return;
2666
2667 // Aren't already a client, are we?
2668 if (!m_pOptions->bServerStart || m_pClient)
2669 return;
2670
2671 // Is the server process instance still here?
2672 if (m_pServer) {
2673 if (QMessageBox::warning(this,
2674 QSAMPLER_TITLE ": " + tr("Warning"),
2675 tr("Could not start the LinuxSampler server.\n\n"
2676 "Maybe it is already started."),
2677 QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) {
2678 m_pServer->terminate();
2679 m_pServer->kill();
2680 }
2681 return;
2682 }
2683
2684 // Reset our timer counters...
2685 stopSchedule();
2686
2687 // Verify we have something to start with...
2688 if (m_pOptions->sServerCmdLine.isEmpty())
2689 return;
2690
2691 // OK. Let's build the startup process...
2692 m_pServer = new QProcess();
2693 bForceServerStop = true;
2694
2695 // Setup stdout/stderr capture...
2696 // if (m_pOptions->bStdoutCapture) {
2697 m_pServer->setProcessChannelMode(QProcess::ForwardedChannels);
2698 QObject::connect(m_pServer,
2699 SIGNAL(readyReadStandardOutput()),
2700 SLOT(readServerStdout()));
2701 QObject::connect(m_pServer,
2702 SIGNAL(readyReadStandardError()),
2703 SLOT(readServerStdout()));
2704 // }
2705
2706 // The unforgiveable signal communication...
2707 QObject::connect(m_pServer,
2708 SIGNAL(finished(int, QProcess::ExitStatus)),
2709 SLOT(processServerExit()));
2710
2711 // Build process arguments...
2712 QStringList args = m_pOptions->sServerCmdLine.split(' ');
2713 QString sCommand = args[0];
2714 args.removeAt(0);
2715
2716 appendMessages(tr("Server is starting..."));
2717 appendMessagesColor(m_pOptions->sServerCmdLine, "#990099");
2718
2719 // Go linuxsampler, go...
2720 m_pServer->start(sCommand, args);
2721 if (!m_pServer->waitForStarted()) {
2722 appendMessagesError(tr("Could not start server.\n\nSorry."));
2723 processServerExit();
2724 return;
2725 }
2726
2727 // Show startup results...
2728 appendMessages(
2729 tr("Server was started with PID=%1.").arg((long) m_pServer->pid()));
2730
2731 // Reset (yet again) the timer counters,
2732 // but this time is deferred as the user opted.
2733 startSchedule(m_pOptions->iStartDelay);
2734 stabilizeForm();
2735 }
2736
2737
2738 // Stop linuxsampler server...
2739 void MainForm::stopServer (bool bInteractive)
2740 {
2741 // Stop client code.
2742 stopClient();
2743
2744 if (m_pServer && bInteractive) {
2745 if (QMessageBox::question(this,
2746 QSAMPLER_TITLE ": " + tr("The backend's fate ..."),
2747 tr("You have the option to keep the sampler backend (LinuxSampler)\n"
2748 "running in the background. The sampler would continue to work\n"
2749 "according to your current sampler session and you could alter the\n"
2750 "sampler session at any time by relaunching QSampler.\n\n"
2751 "Do you want LinuxSampler to stop?"),
2752 QMessageBox::Yes | QMessageBox::No) == QMessageBox::No)
2753 {
2754 bForceServerStop = false;
2755 }
2756 }
2757
2758 // And try to stop server.
2759 if (m_pServer && bForceServerStop) {
2760 appendMessages(tr("Server is stopping..."));
2761 if (m_pServer->state() == QProcess::Running) {
2762 #if defined(WIN32)
2763 // Try harder...
2764 m_pServer->kill();
2765 #else
2766 // Try softly...
2767 m_pServer->terminate();
2768 #endif
2769 }
2770 } // Do final processing anyway.
2771 else processServerExit();
2772
2773 // Give it some time to terminate gracefully and stabilize...
2774 QTime t;
2775 t.start();
2776 while (t.elapsed() < QSAMPLER_TIMER_MSECS)
2777 QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
2778 }
2779
2780
2781 // Stdout handler...
2782 void MainForm::readServerStdout (void)
2783 {
2784 if (m_pMessages)
2785 m_pMessages->appendStdoutBuffer(m_pServer->readAllStandardOutput());
2786 }
2787
2788
2789 // Linuxsampler server cleanup.
2790 void MainForm::processServerExit (void)
2791 {
2792 // Force client code cleanup.
2793 stopClient();
2794
2795 // Flush anything that maybe pending...
2796 if (m_pMessages)
2797 m_pMessages->flushStdoutBuffer();
2798
2799 if (m_pServer && bForceServerStop) {
2800 if (m_pServer->state() != QProcess::NotRunning) {
2801 appendMessages(tr("Server is being forced..."));
2802 // Force final server shutdown...
2803 m_pServer->kill();
2804 // Give it some time to terminate gracefully and stabilize...
2805 QTime t;
2806 t.start();
2807 while (t.elapsed() < QSAMPLER_TIMER_MSECS)
2808 QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
2809 }
2810 // Force final server shutdown...
2811 appendMessages(
2812 tr("Server was stopped with exit status %1.")
2813 .arg(m_pServer->exitStatus()));
2814 delete m_pServer;
2815 m_pServer = NULL;
2816 }
2817
2818 // Again, make status visible stable.
2819 stabilizeForm();
2820 }
2821
2822
2823 //-------------------------------------------------------------------------
2824 // qsamplerMainForm -- Client stuff.
2825
2826 // The LSCP client callback procedure.
2827 lscp_status_t qsampler_client_callback ( lscp_client_t */*pClient*/,
2828 lscp_event_t event, const char *pchData, int cchData, void *pvData )
2829 {
2830 MainForm* pMainForm = (MainForm *) pvData;
2831 if (pMainForm == NULL)
2832 return LSCP_FAILED;
2833
2834 // ATTN: DO NOT EVER call any GUI code here,
2835 // as this is run under some other thread context.
2836 // A custom event must be posted here...
2837 QApplication::postEvent(pMainForm,
2838 new LscpEvent(event, pchData, cchData));
2839
2840 return LSCP_OK;
2841 }
2842
2843
2844 // Start our almighty client...
2845 bool MainForm::startClient (void)
2846 {
2847 // Have it a setup?
2848 if (m_pOptions == NULL)
2849 return false;
2850
2851 // Aren't we already started, are we?
2852 if (m_pClient)
2853 return true;
2854
2855 // Log prepare here.
2856 appendMessages(tr("Client connecting..."));
2857
2858 // Create the client handle...
2859 m_pClient = ::lscp_client_create(
2860 m_pOptions->sServerHost.toUtf8().constData(),
2861 m_pOptions->iServerPort, qsampler_client_callback, this);
2862 if (m_pClient == NULL) {
2863 // Is this the first try?
2864 // maybe we need to start a local server...
2865 if ((m_pServer && m_pServer->state() == QProcess::Running)
2866 || !m_pOptions->bServerStart) {
2867 appendMessagesError(
2868 tr("Could not connect to server as client.\n\nSorry."));
2869 } else {
2870 startServer();
2871 }
2872 // This is always a failure.
2873 stabilizeForm();
2874 return false;
2875 }
2876 // Just set receive timeout value, blindly.
2877 ::lscp_client_set_timeout(m_pClient, m_pOptions->iServerTimeout);
2878 appendMessages(
2879 tr("Client receive timeout is set to %1 msec.")
2880 .arg(::lscp_client_get_timeout(m_pClient)));
2881
2882 // Subscribe to channel info change notifications...
2883 if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_CHANNEL_COUNT) != LSCP_OK)
2884 appendMessagesClient("lscp_client_subscribe(CHANNEL_COUNT)");
2885 if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_CHANNEL_INFO) != LSCP_OK)
2886 appendMessagesClient("lscp_client_subscribe(CHANNEL_INFO)");
2887
2888 DeviceStatusForm::onDevicesChanged(); // initialize
2889 updateViewMidiDeviceStatusMenu();
2890 if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_MIDI_INPUT_DEVICE_COUNT) != LSCP_OK)
2891 appendMessagesClient("lscp_client_subscribe(MIDI_INPUT_DEVICE_COUNT)");
2892 if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_MIDI_INPUT_DEVICE_INFO) != LSCP_OK)
2893 appendMessagesClient("lscp_client_subscribe(MIDI_INPUT_DEVICE_INFO)");
2894 if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_AUDIO_OUTPUT_DEVICE_COUNT) != LSCP_OK)
2895 appendMessagesClient("lscp_client_subscribe(AUDIO_OUTPUT_DEVICE_COUNT)");
2896 if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_AUDIO_OUTPUT_DEVICE_INFO) != LSCP_OK)
2897 appendMessagesClient("lscp_client_subscribe(AUDIO_OUTPUT_DEVICE_INFO)");
2898
2899 #if CONFIG_EVENT_CHANNEL_MIDI
2900 // Subscribe to channel MIDI data notifications...
2901 if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_CHANNEL_MIDI) != LSCP_OK)
2902 appendMessagesClient("lscp_client_subscribe(CHANNEL_MIDI)");
2903 #endif
2904
2905 #if CONFIG_EVENT_DEVICE_MIDI
2906 // Subscribe to channel MIDI data notifications...
2907 if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_DEVICE_MIDI) != LSCP_OK)
2908 appendMessagesClient("lscp_client_subscribe(DEVICE_MIDI)");
2909 #endif
2910
2911 // We may stop scheduling around.
2912 stopSchedule();
2913
2914 // We'll accept drops from now on...
2915 setAcceptDrops(true);
2916
2917 // Log success here.
2918 appendMessages(tr("Client connected."));
2919
2920 // Hard-notify instrumnet and device configuration forms,
2921 // if visible, that we're ready...
2922 if (m_pInstrumentListForm)
2923 m_pInstrumentListForm->refreshInstruments();
2924 if (m_pDeviceForm)
2925 m_pDeviceForm->refreshDevices();
2926
2927 // Is any session pending to be loaded?
2928 if (!m_pOptions->sSessionFile.isEmpty()) {
2929 // Just load the prabably startup session...
2930 if (loadSessionFile(m_pOptions->sSessionFile)) {
2931 m_pOptions->sSessionFile = QString::null;
2932 return true;
2933 }
2934 }
2935
2936 // send the current / loaded fine tuning settings to the sampler
2937 m_pOptions->sendFineTuningSettings();
2938
2939 // Make a new session
2940 return newSession();
2941 }
2942
2943
2944 // Stop client...
2945 void MainForm::stopClient (void)
2946 {
2947 if (m_pClient == NULL)
2948 return;
2949
2950 // Log prepare here.
2951 appendMessages(tr("Client disconnecting..."));
2952
2953 // Clear timer counters...
2954 stopSchedule();
2955
2956 // We'll reject drops from now on...
2957 setAcceptDrops(false);
2958
2959 // Force any channel strips around, but
2960 // but avoid removing the corresponding
2961 // channels from the back-end server.
2962 m_iDirtyCount = 0;
2963 closeSession(false);
2964
2965 // Close us as a client...
2966 #if CONFIG_EVENT_DEVICE_MIDI
2967 ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_DEVICE_MIDI);
2968 #endif
2969 #if CONFIG_EVENT_CHANNEL_MIDI
2970 ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_CHANNEL_MIDI);
2971 #endif
2972 ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_AUDIO_OUTPUT_DEVICE_INFO);
2973 ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_AUDIO_OUTPUT_DEVICE_COUNT);
2974 ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_MIDI_INPUT_DEVICE_INFO);
2975 ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_MIDI_INPUT_DEVICE_COUNT);
2976 ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_CHANNEL_INFO);
2977 ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_CHANNEL_COUNT);
2978 ::lscp_client_destroy(m_pClient);
2979 m_pClient = NULL;
2980
2981 // Hard-notify instrumnet and device configuration forms,
2982 // if visible, that we're running out...
2983 if (m_pInstrumentListForm)
2984 m_pInstrumentListForm->refreshInstruments();
2985 if (m_pDeviceForm)
2986 m_pDeviceForm->refreshDevices();
2987
2988 // Log final here.
2989 appendMessages(tr("Client disconnected."));
2990
2991 // Make visible status.
2992 stabilizeForm();
2993 }
2994
2995
2996 // Channel strip activation/selection.
2997 void MainForm::activateStrip ( QMdiSubWindow *pMdiSubWindow )
2998 {
2999 ChannelStrip *pChannelStrip = NULL;
3000 if (pMdiSubWindow)
3001 pChannelStrip = static_cast<ChannelStrip *> (pMdiSubWindow->widget());
3002 if (pChannelStrip)
3003 pChannelStrip->setSelected(true);
3004
3005 stabilizeForm();
3006 }
3007
3008
3009 // Channel toolbar orientation change.
3010 void MainForm::channelsToolbarOrientation ( Qt::Orientation orientation )
3011 {
3012 #ifdef CONFIG_VOLUME
3013 m_pVolumeSlider->setOrientation(orientation);
3014 if (orientation == Qt::Horizontal) {
3015 m_pVolumeSlider->setMinimumHeight(24);
3016 m_pVolumeSlider->setMaximumHeight(32);
3017 m_pVolumeSlider->setMinimumWidth(120);
3018 m_pVolumeSlider->setMaximumWidth(640);
3019 m_pVolumeSpinBox->setMaximumWidth(64);
3020 m_pVolumeSpinBox->setButtonSymbols(QSpinBox::UpDownArrows);
3021 } else {
3022 m_pVolumeSlider->setMinimumHeight(120);
3023 m_pVolumeSlider->setMaximumHeight(480);
3024 m_pVolumeSlider->setMinimumWidth(24);
3025 m_pVolumeSlider->setMaximumWidth(32);
3026 m_pVolumeSpinBox->setMaximumWidth(32);
3027 m_pVolumeSpinBox->setButtonSymbols(QSpinBox::NoButtons);
3028 }
3029 #endif
3030 }
3031
3032
3033 } // namespace QSampler
3034
3035
3036 // end of qsamplerMainForm.cpp

  ViewVC Help
Powered by ViewVC