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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1698 - (show annotations) (download)
Sat Feb 16 19:41:05 2008 UTC (16 years, 1 month ago) by schoenebeck
File size: 77112 byte(s)
* implemented MIDI device activity windows, selectable from the "View"
  main menu, still quite ugly I admit ;-)
* bumped version to 0.2.1.7

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

  ViewVC Help
Powered by ViewVC