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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2144 - (show annotations) (download)
Wed Oct 6 18:49:54 2010 UTC (9 years ago) by persson
File size: 83196 byte(s)
* Fixes for cross compiling and building for Windows with configure
  and make.
* Made lookup of translation files more robust on Windows

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

  ViewVC Help
Powered by ViewVC