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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1803 - (show annotations) (download)
Sun Dec 7 13:58:16 2008 UTC (14 years, 2 months ago) by schoenebeck
File size: 80427 byte(s)
* Added support for runtime max. voices / disk streams setting (accessible
  from the "Options..." dialog). Those fine tuning settins will be saved
  in case the user modified them and automatically restored to the sampler
  when reconnecting to a sampler the next time.
* bumped version to 0.2.1.20

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

  ViewVC Help
Powered by ViewVC