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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC