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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC