/[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 758 - (show annotations) (download) (as text)
Sun Aug 28 00:31:34 2005 UTC (18 years, 7 months ago) by capela
File MIME type: text/x-c++hdr
File size: 66735 byte(s)
* Audio output channel routing configuration finally hits
  the sampler channel dialog, at last! (EXPERIMENTAL).

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

  ViewVC Help
Powered by ViewVC