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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC