/[svn]/qsampler/trunk/src/qsamplerMainForm.ui.h
ViewVC logotype

Contents of /qsampler/trunk/src/qsamplerMainForm.ui.h

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1036 - (show annotations) (download) (as text)
Mon Jan 15 19:11:59 2007 UTC (15 years ago) by capela
File MIME type: text/x-c++hdr
File size: 77085 byte(s)
* Master volume slider has tick marks and value is now
  rounded properly on session startup.
* Channel button colors have changed: yellow for mute
  and cyan for solo is now the rule, but note that this color
  highlighting is only rendered on some widget styles.

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

  ViewVC Help
Powered by ViewVC