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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1739 - (show annotations) (download)
Wed May 14 17:37:45 2008 UTC (15 years, 11 months ago) by capela
File size: 80201 byte(s)
- Messages file logging makes its first long overdue appearance,
  with user configurable settings in View/Options.../Server/Logging.

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

  ViewVC Help
Powered by ViewVC