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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1815 - (show annotations) (download)
Mon Dec 22 10:04:59 2008 UTC (15 years, 3 months ago) by capela
File size: 80946 byte(s)
* Tuning options tab layout stretch; bumped to 0.2.1.21

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

  ViewVC Help
Powered by ViewVC