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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2036 - (show annotations) (download)
Wed Jan 6 09:52:32 2010 UTC (14 years, 2 months ago) by capela
File size: 81061 byte(s)
* MIDI activity indicator on channel strips is now implemented as
  a flashing green LED, instead of a lame solid background :).

- New year's bumping.

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

  ViewVC Help
Powered by ViewVC