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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2112 - (show annotations) (download)
Wed Jul 21 18:33:25 2010 UTC (13 years, 9 months ago) by capela
File size: 83024 byte(s)
- Moving from old deprecated Qt3'ish custom event post handling
  into regular asynchronous signal/slot strategy. (EXPERIMENTAL)

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

  ViewVC Help
Powered by ViewVC