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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1704 - (show annotations) (download)
Tue Feb 19 09:18:45 2008 UTC (16 years, 1 month ago) by capela
File size: 79686 byte(s)
- Update for the lowling win32 project build.

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

  ViewVC Help
Powered by ViewVC