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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1509 - (show annotations) (download)
Thu Nov 22 11:10:44 2007 UTC (16 years, 4 months ago) by capela
File size: 74325 byte(s)
* Audio routing table is initially hidden in the dialog, when
  creating a new sampler channel.

* README requirements and configuration notes update.

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

  ViewVC Help
Powered by ViewVC