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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1699 - (show annotations) (download)
Sun Feb 17 10:46:17 2008 UTC (16 years, 1 month ago) by schoenebeck
File size: 77646 byte(s)
* refresh device management dialog when device informations changed,
  e.g. on changes caused by other frontends
  (fixes segfault on device changes)
* rebuild device status window on device info changes (including ones
  caused by other frontends)
* bugfix: close all device status windows when main window was closed

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

  ViewVC Help
Powered by ViewVC