/[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 957 - (show annotations) (download) (as text)
Wed Nov 29 11:48:26 2006 UTC (17 years, 4 months ago) by capela
File MIME type: text/x-c++hdr
File size: 68032 byte(s)
* Added preliminary MIDI instrument mapping support.

1 // qsamplerMainForm.ui.h
2 //
3 // ui.h extension file, included from the uic-generated form implementation.
4 /****************************************************************************
5 Copyright (C) 2004-2006, 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 <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().stripWhiteSpace();
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 #ifdef CONFIG_MIDI_INSTRUMENT
811 // MIDI instrument mapping...
812 lscp_midi_instrument_t *pInstrs = ::lscp_list_midi_instruments(m_pClient);
813 if (pInstrs)
814 ts << "# " << tr("MIDI instrument mapping") << endl;
815 for (int iInstr = 0; pInstrs && pInstrs[iInstr].program >= 0; iInstr++) {
816 lscp_midi_instrument_info_t *pInstrInfo
817 = ::lscp_get_midi_instrument_info(m_pClient, &pInstrs[iInstr]);
818 if (pInstrInfo) {
819 ts << "MAP MIDI_INSTRUMENT "
820 << pInstrs[iInstr].bank_msb << " "
821 << pInstrs[iInstr].bank_lsb << " "
822 << pInstrs[iInstr].program << " "
823 << pInstrInfo->engine_name << " '"
824 << pInstrInfo->instrument_file << "' "
825 << pInstrInfo->instrument_nr << " "
826 << pInstrInfo->volume << " ";
827 switch (pInstrInfo->load_mode) {
828 case LSCP_LOAD_PERSISTENT:
829 ts << "PERSISTENT";
830 break;
831 case LSCP_LOAD_ON_DEMAND_HOLD:
832 ts << "ON_DEMAND_HOLD";
833 break;
834 case LSCP_LOAD_ON_DEMAND:
835 case LSCP_LOAD_DEFAULT:
836 default:
837 ts << "ON_DEMAND";
838 break;
839 }
840 if (pInstrInfo->name)
841 ts << " '" << pInstrInfo->name << "'";
842 ts << endl;
843 }
844 ts << endl;
845 // Try to keep it snappy :)
846 QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
847 }
848 #endif // CONFIG_MIDI_INSTRUMENT
849
850 // Ok. we've wrote it.
851 file.close();
852
853 // Have we any errors?
854 if (iErrors > 0)
855 appendMessagesError(tr("Some settings could not be saved\nto \"%1\" session file.\n\nSorry.").arg(sFilename));
856
857 // Save as default session directory.
858 if (m_pOptions)
859 m_pOptions->sSessionDir = QFileInfo(sFilename).dirPath(true);
860 // We're not dirty anymore.
861 m_iDirtyCount = 0;
862 // Stabilize form...
863 m_sFilename = sFilename;
864 updateRecentFiles(sFilename);
865 appendMessages(tr("Save session: \"%1\".").arg(sessionName(m_sFilename)));
866 stabilizeForm();
867 return true;
868 }
869
870
871 // Session change receiver slot.
872 void qsamplerMainForm::sessionDirty (void)
873 {
874 // Just mark the dirty form.
875 m_iDirtyCount++;
876 // and update the form status...
877 stabilizeForm();
878 }
879
880
881 //-------------------------------------------------------------------------
882 // qsamplerMainForm -- File Action slots.
883
884 // Create a new sampler session.
885 void qsamplerMainForm::fileNew (void)
886 {
887 // Of course we'll start clean new.
888 newSession();
889 }
890
891
892 // Open an existing sampler session.
893 void qsamplerMainForm::fileOpen (void)
894 {
895 // Open it right away.
896 openSession();
897 }
898
899
900 // Open a recent file session.
901 void qsamplerMainForm::fileOpenRecent ( int iIndex )
902 {
903 // Check if we can safely close the current session...
904 if (m_pOptions && closeSession(true)) {
905 QString sFilename = m_pOptions->recentFiles[iIndex];
906 loadSessionFile(sFilename);
907 }
908 }
909
910
911 // Save current sampler session.
912 void qsamplerMainForm::fileSave (void)
913 {
914 // Save it right away.
915 saveSession(false);
916 }
917
918
919 // Save current sampler session with another name.
920 void qsamplerMainForm::fileSaveAs (void)
921 {
922 // Save it right away, maybe with another name.
923 saveSession(true);
924 }
925
926
927 // Reset the sampler instance.
928 void qsamplerMainForm::fileReset (void)
929 {
930 if (m_pClient == NULL)
931 return;
932
933 // Ask user whether he/she want's an internal sampler reset...
934 if (QMessageBox::warning(this,
935 QSAMPLER_TITLE ": " + tr("Warning"),
936 tr("Resetting the sampler instance will close\n"
937 "all device and channel configurations.\n\n"
938 "Please note that this operation may cause\n"
939 "temporary MIDI and Audio disruption.\n\n"
940 "Do you want to reset the sampler engine now?"),
941 tr("Reset"), tr("Cancel")) > 0)
942 return;
943
944 // Just do the reset, after closing down current session...
945 if (closeSession(true) && ::lscp_reset_sampler(m_pClient) != LSCP_OK) {
946 appendMessagesClient("lscp_reset_sampler");
947 appendMessagesError(tr("Could not reset sampler instance.\n\nSorry."));
948 return;
949 }
950
951 // Log this.
952 appendMessages(tr("Sampler reset."));
953
954 // Make it a new session...
955 newSession();
956 }
957
958
959 // Restart the client/server instance.
960 void qsamplerMainForm::fileRestart (void)
961 {
962 if (m_pOptions == NULL)
963 return;
964
965 bool bRestart = true;
966
967 // Ask user whether he/she want's a complete restart...
968 // (if we're currently up and running)
969 if (bRestart && m_pClient) {
970 bRestart = (QMessageBox::warning(this,
971 QSAMPLER_TITLE ": " + tr("Warning"),
972 tr("New settings will be effective after\n"
973 "restarting the client/server connection.\n\n"
974 "Please note that this operation may cause\n"
975 "temporary MIDI and Audio disruption.\n\n"
976 "Do you want to restart the connection now?"),
977 tr("Restart"), tr("Cancel")) == 0);
978 }
979
980 // Are we still for it?
981 if (bRestart && closeSession(true)) {
982 // Stop server, it will force the client too.
983 stopServer();
984 // Reschedule a restart...
985 startSchedule(m_pOptions->iStartDelay);
986 }
987 }
988
989
990 // Exit application program.
991 void qsamplerMainForm::fileExit (void)
992 {
993 // Go for close the whole thing.
994 close();
995 }
996
997
998 //-------------------------------------------------------------------------
999 // qsamplerMainForm -- Edit Action slots.
1000
1001 // Add a new sampler channel.
1002 void qsamplerMainForm::editAddChannel (void)
1003 {
1004 if (m_pClient == NULL)
1005 return;
1006
1007 // Just create the channel instance...
1008 qsamplerChannel *pChannel = new qsamplerChannel(this);
1009 if (pChannel == NULL)
1010 return;
1011
1012 // Before we show it up, may be we'll
1013 // better ask for some initial values?
1014 if (!pChannel->channelSetup(this)) {
1015 delete pChannel;
1016 return;
1017 }
1018
1019 // And give it to the strip (will own the channel instance, if successful).
1020 if (!createChannelStrip(pChannel)) {
1021 delete pChannel;
1022 return;
1023 }
1024
1025 // Make that an overall update.
1026 m_iDirtyCount++;
1027 stabilizeForm();
1028 }
1029
1030
1031 // Remove current sampler channel.
1032 void qsamplerMainForm::editRemoveChannel (void)
1033 {
1034 if (m_pClient == NULL)
1035 return;
1036
1037 qsamplerChannelStrip *pChannelStrip = activeChannelStrip();
1038 if (pChannelStrip == NULL)
1039 return;
1040
1041 qsamplerChannel *pChannel = pChannelStrip->channel();
1042 if (pChannel == NULL)
1043 return;
1044
1045 // Prompt user if he/she's sure about this...
1046 if (m_pOptions && m_pOptions->bConfirmRemove) {
1047 if (QMessageBox::warning(this,
1048 QSAMPLER_TITLE ": " + tr("Warning"),
1049 tr("About to remove channel:\n\n"
1050 "%1\n\n"
1051 "Are you sure?")
1052 .arg(pChannelStrip->caption()),
1053 tr("OK"), tr("Cancel")) > 0)
1054 return;
1055 }
1056
1057 // Remove the existing sampler channel.
1058 if (!pChannel->removeChannel())
1059 return;
1060
1061 // Just delete the channel strip.
1062 delete pChannelStrip;
1063
1064 // Do we auto-arrange?
1065 if (m_pOptions && m_pOptions->bAutoArrange)
1066 channelsArrange();
1067
1068 // We'll be dirty, for sure...
1069 m_iDirtyCount++;
1070 stabilizeForm();
1071 }
1072
1073
1074 // Setup current sampler channel.
1075 void qsamplerMainForm::editSetupChannel (void)
1076 {
1077 if (m_pClient == NULL)
1078 return;
1079
1080 qsamplerChannelStrip *pChannelStrip = activeChannelStrip();
1081 if (pChannelStrip == NULL)
1082 return;
1083
1084 // Just invoque the channel strip procedure.
1085 pChannelStrip->channelSetup();
1086 }
1087
1088
1089 // Reset current sampler channel.
1090 void qsamplerMainForm::editResetChannel (void)
1091 {
1092 if (m_pClient == NULL)
1093 return;
1094
1095 qsamplerChannelStrip *pChannelStrip = activeChannelStrip();
1096 if (pChannelStrip == NULL)
1097 return;
1098
1099 // Just invoque the channel strip procedure.
1100 pChannelStrip->channelReset();
1101 }
1102
1103
1104 // Reset all sampler channels.
1105 void qsamplerMainForm::editResetAllChannels (void)
1106 {
1107 if (m_pClient == NULL)
1108 return;
1109
1110 // Invoque the channel strip procedure,
1111 // for all channels out there...
1112 m_pWorkspace->setUpdatesEnabled(false);
1113 QWidgetList wlist = m_pWorkspace->windowList();
1114 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1115 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1116 if (pChannelStrip)
1117 pChannelStrip->channelReset();
1118 }
1119 m_pWorkspace->setUpdatesEnabled(true);
1120 }
1121
1122
1123 //-------------------------------------------------------------------------
1124 // qsamplerMainForm -- View Action slots.
1125
1126 // Show/hide the main program window menubar.
1127 void qsamplerMainForm::viewMenubar ( bool bOn )
1128 {
1129 if (bOn)
1130 MenuBar->show();
1131 else
1132 MenuBar->hide();
1133 }
1134
1135
1136 // Show/hide the main program window toolbar.
1137 void qsamplerMainForm::viewToolbar ( bool bOn )
1138 {
1139 if (bOn) {
1140 fileToolbar->show();
1141 editToolbar->show();
1142 channelsToolbar->show();
1143 } else {
1144 fileToolbar->hide();
1145 editToolbar->hide();
1146 channelsToolbar->hide();
1147 }
1148 }
1149
1150
1151 // Show/hide the main program window statusbar.
1152 void qsamplerMainForm::viewStatusbar ( bool bOn )
1153 {
1154 if (bOn)
1155 statusBar()->show();
1156 else
1157 statusBar()->hide();
1158 }
1159
1160
1161 // Show/hide the messages window logger.
1162 void qsamplerMainForm::viewMessages ( bool bOn )
1163 {
1164 if (bOn)
1165 m_pMessages->show();
1166 else
1167 m_pMessages->hide();
1168 }
1169
1170
1171 // Show/hide the device configurator form.
1172 void qsamplerMainForm::viewDevices (void)
1173 {
1174 if (m_pOptions == NULL)
1175 return;
1176
1177 if (m_pDeviceForm) {
1178 m_pOptions->saveWidgetGeometry(m_pDeviceForm);
1179 m_pDeviceForm->setClient(m_pClient);
1180 if (m_pDeviceForm->isVisible()) {
1181 m_pDeviceForm->hide();
1182 } else {
1183 m_pDeviceForm->show();
1184 m_pDeviceForm->raise();
1185 m_pDeviceForm->setActiveWindow();
1186 }
1187 }
1188 }
1189
1190
1191 // Show options dialog.
1192 void qsamplerMainForm::viewOptions (void)
1193 {
1194 if (m_pOptions == NULL)
1195 return;
1196
1197 qsamplerOptionsForm *pOptionsForm = new qsamplerOptionsForm(this);
1198 if (pOptionsForm) {
1199 // Check out some initial nullities(tm)...
1200 qsamplerChannelStrip *pChannelStrip = activeChannelStrip();
1201 if (m_pOptions->sDisplayFont.isEmpty() && pChannelStrip)
1202 m_pOptions->sDisplayFont = pChannelStrip->displayFont().toString();
1203 if (m_pOptions->sMessagesFont.isEmpty() && m_pMessages)
1204 m_pOptions->sMessagesFont = m_pMessages->messagesFont().toString();
1205 // To track down deferred or immediate changes.
1206 QString sOldServerHost = m_pOptions->sServerHost;
1207 int iOldServerPort = m_pOptions->iServerPort;
1208 int iOldServerTimeout = m_pOptions->iServerTimeout;
1209 bool bOldServerStart = m_pOptions->bServerStart;
1210 QString sOldServerCmdLine = m_pOptions->sServerCmdLine;
1211 QString sOldDisplayFont = m_pOptions->sDisplayFont;
1212 bool bOldDisplayEffect = m_pOptions->bDisplayEffect;
1213 int iOldMaxVolume = m_pOptions->iMaxVolume;
1214 QString sOldMessagesFont = m_pOptions->sMessagesFont;
1215 bool bOldKeepOnTop = m_pOptions->bKeepOnTop;
1216 bool bOldStdoutCapture = m_pOptions->bStdoutCapture;
1217 int bOldMessagesLimit = m_pOptions->bMessagesLimit;
1218 int iOldMessagesLimitLines = m_pOptions->iMessagesLimitLines;
1219 bool bOldCompletePath = m_pOptions->bCompletePath;
1220 bool bOldInstrumentNames = m_pOptions->bInstrumentNames;
1221 int iOldMaxRecentFiles = m_pOptions->iMaxRecentFiles;
1222 // Load the current setup settings.
1223 pOptionsForm->setup(m_pOptions);
1224 // Show the setup dialog...
1225 if (pOptionsForm->exec()) {
1226 // Warn if something will be only effective on next run.
1227 if (( bOldStdoutCapture && !m_pOptions->bStdoutCapture) ||
1228 (!bOldStdoutCapture && m_pOptions->bStdoutCapture) ||
1229 ( bOldKeepOnTop && !m_pOptions->bKeepOnTop) ||
1230 (!bOldKeepOnTop && m_pOptions->bKeepOnTop)) {
1231 QMessageBox::information(this,
1232 QSAMPLER_TITLE ": " + tr("Information"),
1233 tr("Some settings may be only effective\n"
1234 "next time you start this program."), tr("OK"));
1235 updateMessagesCapture();
1236 }
1237 // Check wheather something immediate has changed.
1238 if (( bOldCompletePath && !m_pOptions->bCompletePath) ||
1239 (!bOldCompletePath && m_pOptions->bCompletePath) ||
1240 (iOldMaxRecentFiles != m_pOptions->iMaxRecentFiles))
1241 updateRecentFilesMenu();
1242 if (( bOldInstrumentNames && !m_pOptions->bInstrumentNames) ||
1243 (!bOldInstrumentNames && m_pOptions->bInstrumentNames))
1244 updateInstrumentNames();
1245 if (( bOldDisplayEffect && !m_pOptions->bDisplayEffect) ||
1246 (!bOldDisplayEffect && m_pOptions->bDisplayEffect))
1247 updateDisplayEffect();
1248 if (sOldDisplayFont != m_pOptions->sDisplayFont)
1249 updateDisplayFont();
1250 if (iOldMaxVolume != m_pOptions->iMaxVolume)
1251 updateMaxVolume();
1252 if (sOldMessagesFont != m_pOptions->sMessagesFont)
1253 updateMessagesFont();
1254 if (( bOldMessagesLimit && !m_pOptions->bMessagesLimit) ||
1255 (!bOldMessagesLimit && m_pOptions->bMessagesLimit) ||
1256 (iOldMessagesLimitLines != m_pOptions->iMessagesLimitLines))
1257 updateMessagesLimit();
1258 // And now the main thing, whether we'll do client/server recycling?
1259 if ((sOldServerHost != m_pOptions->sServerHost) ||
1260 (iOldServerPort != m_pOptions->iServerPort) ||
1261 (iOldServerTimeout != m_pOptions->iServerTimeout) ||
1262 ( bOldServerStart && !m_pOptions->bServerStart) ||
1263 (!bOldServerStart && m_pOptions->bServerStart) ||
1264 (sOldServerCmdLine != m_pOptions->sServerCmdLine && m_pOptions->bServerStart))
1265 fileRestart();
1266 }
1267 // Done.
1268 delete pOptionsForm;
1269 }
1270
1271 // This makes it.
1272 stabilizeForm();
1273 }
1274
1275
1276 //-------------------------------------------------------------------------
1277 // qsamplerMainForm -- Channels action slots.
1278
1279 // Arrange channel strips.
1280 void qsamplerMainForm::channelsArrange (void)
1281 {
1282 // Full width vertical tiling
1283 QWidgetList wlist = m_pWorkspace->windowList();
1284 if (wlist.isEmpty())
1285 return;
1286
1287 m_pWorkspace->setUpdatesEnabled(false);
1288 int y = 0;
1289 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1290 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1291 /* if (pChannelStrip->testWState(WState_Maximized | WState_Minimized)) {
1292 // Prevent flicker...
1293 pChannelStrip->hide();
1294 pChannelStrip->showNormal();
1295 } */
1296 pChannelStrip->adjustSize();
1297 int iWidth = m_pWorkspace->width();
1298 if (iWidth < pChannelStrip->width())
1299 iWidth = pChannelStrip->width();
1300 // int iHeight = pChannelStrip->height() + pChannelStrip->parentWidget()->baseSize().height();
1301 int iHeight = pChannelStrip->parentWidget()->frameGeometry().height();
1302 pChannelStrip->parentWidget()->setGeometry(0, y, iWidth, iHeight);
1303 y += iHeight;
1304 }
1305 m_pWorkspace->setUpdatesEnabled(true);
1306
1307 stabilizeForm();
1308 }
1309
1310
1311 // Auto-arrange channel strips.
1312 void qsamplerMainForm::channelsAutoArrange ( bool bOn )
1313 {
1314 if (m_pOptions == NULL)
1315 return;
1316
1317 // Toggle the auto-arrange flag.
1318 m_pOptions->bAutoArrange = bOn;
1319
1320 // If on, update whole workspace...
1321 if (m_pOptions->bAutoArrange)
1322 channelsArrange();
1323 }
1324
1325
1326 //-------------------------------------------------------------------------
1327 // qsamplerMainForm -- Help Action slots.
1328
1329 // Show information about the Qt toolkit.
1330 void qsamplerMainForm::helpAboutQt (void)
1331 {
1332 QMessageBox::aboutQt(this);
1333 }
1334
1335
1336 // Show information about application program.
1337 void qsamplerMainForm::helpAbout (void)
1338 {
1339 // Stuff the about box text...
1340 QString sText = "<p>\n";
1341 sText += "<b>" QSAMPLER_TITLE " - " + tr(QSAMPLER_SUBTITLE) + "</b><br />\n";
1342 sText += "<br />\n";
1343 sText += tr("Version") + ": <b>" QSAMPLER_VERSION "</b><br />\n";
1344 sText += "<small>" + tr("Build") + ": " __DATE__ " " __TIME__ "</small><br />\n";
1345 #ifdef CONFIG_DEBUG
1346 sText += "<small><font color=\"red\">";
1347 sText += tr("Debugging option enabled.");
1348 sText += "</font></small><br />";
1349 #endif
1350 #ifndef CONFIG_LIBGIG
1351 sText += "<small><font color=\"red\">";
1352 sText += tr("GIG (libgig) file support disabled.");
1353 sText += "</font></small><br />";
1354 #endif
1355 #ifndef CONFIG_INSTRUMENT_NAME
1356 sText += "<small><font color=\"red\">";
1357 sText += tr("LSCP (liblscp) instrument_name support disabled.");
1358 sText += "</font></small><br />";
1359 #endif
1360 #ifndef CONFIG_MUTE_SOLO
1361 sText += "<small><font color=\"red\">";
1362 sText += tr("Sampler channel Mute/Solo support disabled.");
1363 sText += "</font></small><br />";
1364 #endif
1365 sText += "<br />\n";
1366 sText += tr("Using") + ": ";
1367 sText += ::lscp_client_package();
1368 sText += " ";
1369 sText += ::lscp_client_version();
1370 #ifdef CONFIG_LIBGIG
1371 sText += ", ";
1372 sText += gig::libraryName().c_str();
1373 sText += " ";
1374 sText += gig::libraryVersion().c_str();
1375 #endif
1376 sText += "<br />\n";
1377 sText += "<br />\n";
1378 sText += tr("Website") + ": <a href=\"" QSAMPLER_WEBSITE "\">" QSAMPLER_WEBSITE "</a><br />\n";
1379 sText += "<br />\n";
1380 sText += "<small>";
1381 sText += QSAMPLER_COPYRIGHT "<br />\n";
1382 sText += "<br />\n";
1383 sText += tr("This program is free software; you can redistribute it and/or modify it") + "<br />\n";
1384 sText += tr("under the terms of the GNU General Public License version 2 or later.");
1385 sText += "</small>";
1386 sText += "</p>\n";
1387
1388 QMessageBox::about(this, tr("About") + " " QSAMPLER_TITLE, sText);
1389 }
1390
1391
1392 //-------------------------------------------------------------------------
1393 // qsamplerMainForm -- Main window stabilization.
1394
1395 void qsamplerMainForm::stabilizeForm (void)
1396 {
1397 // Update the main application caption...
1398 QString sSessionName = sessionName(m_sFilename);
1399 if (m_iDirtyCount > 0)
1400 sSessionName += " *";
1401 setCaption(tr(QSAMPLER_TITLE " - [%1]").arg(sSessionName));
1402
1403 // Update the main menu state...
1404 qsamplerChannelStrip *pChannelStrip = activeChannelStrip();
1405 bool bHasClient = (m_pOptions != NULL && m_pClient != NULL);
1406 bool bHasChannel = (bHasClient && pChannelStrip != NULL);
1407 fileNewAction->setEnabled(bHasClient);
1408 fileOpenAction->setEnabled(bHasClient);
1409 fileSaveAction->setEnabled(bHasClient && m_iDirtyCount > 0);
1410 fileSaveAsAction->setEnabled(bHasClient);
1411 fileResetAction->setEnabled(bHasClient);
1412 fileRestartAction->setEnabled(bHasClient || m_pServer == NULL);
1413 editAddChannelAction->setEnabled(bHasClient);
1414 editRemoveChannelAction->setEnabled(bHasChannel);
1415 editSetupChannelAction->setEnabled(bHasChannel);
1416 editResetChannelAction->setEnabled(bHasChannel);
1417 editResetAllChannelsAction->setEnabled(bHasChannel);
1418 viewMessagesAction->setOn(m_pMessages && m_pMessages->isVisible());
1419 viewDevicesAction->setOn(m_pDeviceForm && m_pDeviceForm->isVisible());
1420 viewDevicesAction->setEnabled(bHasClient);
1421 channelsArrangeAction->setEnabled(bHasChannel);
1422
1423 // Client/Server status...
1424 if (bHasClient) {
1425 m_statusItem[QSAMPLER_STATUS_CLIENT]->setText(tr("Connected"));
1426 m_statusItem[QSAMPLER_STATUS_SERVER]->setText(m_pOptions->sServerHost + ":" + QString::number(m_pOptions->iServerPort));
1427 } else {
1428 m_statusItem[QSAMPLER_STATUS_CLIENT]->clear();
1429 m_statusItem[QSAMPLER_STATUS_SERVER]->clear();
1430 }
1431 // Channel status...
1432 if (bHasChannel)
1433 m_statusItem[QSAMPLER_STATUS_CHANNEL]->setText(pChannelStrip->caption());
1434 else
1435 m_statusItem[QSAMPLER_STATUS_CHANNEL]->clear();
1436 // Session status...
1437 if (m_iDirtyCount > 0)
1438 m_statusItem[QSAMPLER_STATUS_SESSION]->setText(tr("MOD"));
1439 else
1440 m_statusItem[QSAMPLER_STATUS_SESSION]->clear();
1441
1442 // Recent files menu.
1443 m_pRecentFilesMenu->setEnabled(bHasClient && m_pOptions->recentFiles.count() > 0);
1444
1445 // Always make the latest message visible.
1446 if (m_pMessages)
1447 m_pMessages->scrollToBottom();
1448 }
1449
1450
1451 // Channel change receiver slot.
1452 void qsamplerMainForm::channelStripChanged( qsamplerChannelStrip *pChannelStrip )
1453 {
1454 // Add this strip to the changed list...
1455 if (m_changedStrips.containsRef(pChannelStrip) == 0) {
1456 m_changedStrips.append(pChannelStrip);
1457 pChannelStrip->resetErrorCount();
1458 }
1459
1460 // Just mark the dirty form.
1461 m_iDirtyCount++;
1462 // and update the form status...
1463 stabilizeForm();
1464 }
1465
1466
1467 // Grab and restore current sampler channels session.
1468 void qsamplerMainForm::updateSession (void)
1469 {
1470 // Retrieve the current channel list.
1471 int *piChannelIDs = ::lscp_list_channels(m_pClient);
1472 if (piChannelIDs == NULL) {
1473 if (::lscp_client_get_errno(m_pClient)) {
1474 appendMessagesClient("lscp_list_channels");
1475 appendMessagesError(tr("Could not get current list of channels.\n\nSorry."));
1476 }
1477 return;
1478 }
1479
1480 // Try to (re)create each channel.
1481 m_pWorkspace->setUpdatesEnabled(false);
1482 for (int iChannel = 0; piChannelIDs[iChannel] >= 0; iChannel++) {
1483 // Check if theres already a channel strip for this one...
1484 if (!channelStrip(piChannelIDs[iChannel]))
1485 createChannelStrip(new qsamplerChannel(this, piChannelIDs[iChannel]));
1486 // Make it visibly responsive...
1487 QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
1488 }
1489 m_pWorkspace->setUpdatesEnabled(true);
1490
1491 // Remember to refresh devices
1492 if (m_pDeviceForm)
1493 m_pDeviceForm->refreshDevices();
1494 }
1495
1496
1497 // Update the recent files list and menu.
1498 void qsamplerMainForm::updateRecentFiles ( const QString& sFilename )
1499 {
1500 if (m_pOptions == NULL)
1501 return;
1502
1503 // Remove from list if already there (avoid duplicates)
1504 QStringList::Iterator iter = m_pOptions->recentFiles.find(sFilename);
1505 if (iter != m_pOptions->recentFiles.end())
1506 m_pOptions->recentFiles.remove(iter);
1507 // Put it to front...
1508 m_pOptions->recentFiles.push_front(sFilename);
1509
1510 // May update the menu.
1511 updateRecentFilesMenu();
1512 }
1513
1514
1515 // Update the recent files list and menu.
1516 void qsamplerMainForm::updateRecentFilesMenu (void)
1517 {
1518 if (m_pOptions == NULL)
1519 return;
1520
1521 // Time to keep the list under limits.
1522 int iRecentFiles = m_pOptions->recentFiles.count();
1523 while (iRecentFiles > m_pOptions->iMaxRecentFiles) {
1524 m_pOptions->recentFiles.pop_back();
1525 iRecentFiles--;
1526 }
1527
1528 // rebuild the recent files menu...
1529 m_pRecentFilesMenu->clear();
1530 for (int i = 0; i < iRecentFiles; i++) {
1531 const QString& sFilename = m_pOptions->recentFiles[i];
1532 if (QFileInfo(sFilename).exists()) {
1533 m_pRecentFilesMenu->insertItem(QString("&%1 %2")
1534 .arg(i + 1).arg(sessionName(sFilename)),
1535 this, SLOT(fileOpenRecent(int)), 0, i);
1536 }
1537 }
1538 }
1539
1540
1541 // Force update of the channels instrument names mode.
1542 void qsamplerMainForm::updateInstrumentNames (void)
1543 {
1544 // Full channel list update...
1545 QWidgetList wlist = m_pWorkspace->windowList();
1546 if (wlist.isEmpty())
1547 return;
1548
1549 m_pWorkspace->setUpdatesEnabled(false);
1550 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1551 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1552 if (pChannelStrip)
1553 pChannelStrip->updateInstrumentName(true);
1554 }
1555 m_pWorkspace->setUpdatesEnabled(true);
1556 }
1557
1558
1559 // Force update of the channels display font.
1560 void qsamplerMainForm::updateDisplayFont (void)
1561 {
1562 if (m_pOptions == NULL)
1563 return;
1564
1565 // Check if display font is legal.
1566 if (m_pOptions->sDisplayFont.isEmpty())
1567 return;
1568 // Realize it.
1569 QFont font;
1570 if (!font.fromString(m_pOptions->sDisplayFont))
1571 return;
1572
1573 // Full channel list update...
1574 QWidgetList wlist = m_pWorkspace->windowList();
1575 if (wlist.isEmpty())
1576 return;
1577
1578 m_pWorkspace->setUpdatesEnabled(false);
1579 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1580 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1581 if (pChannelStrip)
1582 pChannelStrip->setDisplayFont(font);
1583 }
1584 m_pWorkspace->setUpdatesEnabled(true);
1585 }
1586
1587
1588 // Update channel strips background effect.
1589 void qsamplerMainForm::updateDisplayEffect (void)
1590 {
1591 QPixmap pm;
1592 if (m_pOptions->bDisplayEffect)
1593 pm = QPixmap::fromMimeSource("displaybg1.png");
1594
1595 // Full channel list update...
1596 QWidgetList wlist = m_pWorkspace->windowList();
1597 if (wlist.isEmpty())
1598 return;
1599
1600 m_pWorkspace->setUpdatesEnabled(false);
1601 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1602 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1603 if (pChannelStrip)
1604 pChannelStrip->setDisplayBackground(pm);
1605 }
1606 m_pWorkspace->setUpdatesEnabled(true);
1607 }
1608
1609
1610 // Force update of the channels maximum volume setting.
1611 void qsamplerMainForm::updateMaxVolume (void)
1612 {
1613 if (m_pOptions == NULL)
1614 return;
1615
1616 // Full channel list update...
1617 QWidgetList wlist = m_pWorkspace->windowList();
1618 if (wlist.isEmpty())
1619 return;
1620
1621 m_pWorkspace->setUpdatesEnabled(false);
1622 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1623 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1624 if (pChannelStrip)
1625 pChannelStrip->setMaxVolume(m_pOptions->iMaxVolume);
1626 }
1627 m_pWorkspace->setUpdatesEnabled(true);
1628 }
1629
1630
1631 //-------------------------------------------------------------------------
1632 // qsamplerMainForm -- Messages window form handlers.
1633
1634 // Messages output methods.
1635 void qsamplerMainForm::appendMessages( const QString& s )
1636 {
1637 if (m_pMessages)
1638 m_pMessages->appendMessages(s);
1639
1640 statusBar()->message(s, 3000);
1641 }
1642
1643 void qsamplerMainForm::appendMessagesColor( const QString& s, const QString& c )
1644 {
1645 if (m_pMessages)
1646 m_pMessages->appendMessagesColor(s, c);
1647
1648 statusBar()->message(s, 3000);
1649 }
1650
1651 void qsamplerMainForm::appendMessagesText( const QString& s )
1652 {
1653 if (m_pMessages)
1654 m_pMessages->appendMessagesText(s);
1655 }
1656
1657 void qsamplerMainForm::appendMessagesError( const QString& s )
1658 {
1659 if (m_pMessages)
1660 m_pMessages->show();
1661
1662 appendMessagesColor(s.simplifyWhiteSpace(), "#ff0000");
1663
1664 QMessageBox::critical(this,
1665 QSAMPLER_TITLE ": " + tr("Error"), s, tr("Cancel"));
1666 }
1667
1668
1669 // This is a special message format, just for client results.
1670 void qsamplerMainForm::appendMessagesClient( const QString& s )
1671 {
1672 if (m_pClient == NULL)
1673 return;
1674
1675 appendMessagesColor(s + QString(": %1 (errno=%2)")
1676 .arg(::lscp_client_get_result(m_pClient))
1677 .arg(::lscp_client_get_errno(m_pClient)), "#996666");
1678 }
1679
1680
1681 // Force update of the messages font.
1682 void qsamplerMainForm::updateMessagesFont (void)
1683 {
1684 if (m_pOptions == NULL)
1685 return;
1686
1687 if (m_pMessages && !m_pOptions->sMessagesFont.isEmpty()) {
1688 QFont font;
1689 if (font.fromString(m_pOptions->sMessagesFont))
1690 m_pMessages->setMessagesFont(font);
1691 }
1692 }
1693
1694
1695 // Update messages window line limit.
1696 void qsamplerMainForm::updateMessagesLimit (void)
1697 {
1698 if (m_pOptions == NULL)
1699 return;
1700
1701 if (m_pMessages) {
1702 if (m_pOptions->bMessagesLimit)
1703 m_pMessages->setMessagesLimit(m_pOptions->iMessagesLimitLines);
1704 else
1705 m_pMessages->setMessagesLimit(-1);
1706 }
1707 }
1708
1709
1710 // Enablement of the messages capture feature.
1711 void qsamplerMainForm::updateMessagesCapture (void)
1712 {
1713 if (m_pOptions == NULL)
1714 return;
1715
1716 if (m_pMessages)
1717 m_pMessages->setCaptureEnabled(m_pOptions->bStdoutCapture);
1718 }
1719
1720
1721 //-------------------------------------------------------------------------
1722 // qsamplerMainForm -- MDI channel strip management.
1723
1724 // The channel strip creation executive.
1725 qsamplerChannelStrip *qsamplerMainForm::createChannelStrip ( qsamplerChannel *pChannel )
1726 {
1727 if (m_pClient == NULL || pChannel == NULL)
1728 return NULL;
1729
1730 // Prepare for auto-arrange?
1731 qsamplerChannelStrip *pChannelStrip = NULL;
1732 int y = 0;
1733 if (m_pOptions && m_pOptions->bAutoArrange) {
1734 QWidgetList wlist = m_pWorkspace->windowList();
1735 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1736 pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1737 if (pChannelStrip) {
1738 // y += pChannelStrip->height() + pChannelStrip->parentWidget()->baseSize().height();
1739 y += pChannelStrip->parentWidget()->frameGeometry().height();
1740 }
1741 }
1742 }
1743
1744 // Add a new channel itema...
1745 WFlags wflags = Qt::WStyle_Customize | Qt::WStyle_Tool | Qt::WStyle_Title | Qt::WStyle_NoBorder;
1746 pChannelStrip = new qsamplerChannelStrip(m_pWorkspace, 0, wflags);
1747 if (pChannelStrip == NULL)
1748 return NULL;
1749
1750 // Actual channel strip setup...
1751 pChannelStrip->setup(pChannel);
1752 QObject::connect(pChannelStrip, SIGNAL(channelChanged(qsamplerChannelStrip *)), this, SLOT(channelStripChanged(qsamplerChannelStrip *)));
1753 // Set some initial aesthetic options...
1754 if (m_pOptions) {
1755 // Background display effect...
1756 pChannelStrip->setDisplayEffect(m_pOptions->bDisplayEffect);
1757 // We'll need a display font.
1758 QFont font;
1759 if (font.fromString(m_pOptions->sDisplayFont))
1760 pChannelStrip->setDisplayFont(font);
1761 // Maximum allowed volume setting.
1762 pChannelStrip->setMaxVolume(m_pOptions->iMaxVolume);
1763 }
1764
1765 // Now we show up us to the world.
1766 pChannelStrip->show();
1767 // Only then, we'll auto-arrange...
1768 if (m_pOptions && m_pOptions->bAutoArrange) {
1769 int iWidth = m_pWorkspace->width();
1770 // int iHeight = pChannel->height() + pChannel->parentWidget()->baseSize().height();
1771 int iHeight = pChannelStrip->parentWidget()->frameGeometry().height();
1772 pChannelStrip->parentWidget()->setGeometry(0, y, iWidth, iHeight);
1773 }
1774
1775 // This is pretty new, so we'll watch for it closely.
1776 channelStripChanged(pChannelStrip);
1777
1778 // Return our successful reference...
1779 return pChannelStrip;
1780 }
1781
1782
1783 // Retrieve the active channel strip.
1784 qsamplerChannelStrip *qsamplerMainForm::activeChannelStrip (void)
1785 {
1786 return (qsamplerChannelStrip *) m_pWorkspace->activeWindow();
1787 }
1788
1789
1790 // Retrieve a channel strip by index.
1791 qsamplerChannelStrip *qsamplerMainForm::channelStripAt ( int iChannel )
1792 {
1793 QWidgetList wlist = m_pWorkspace->windowList();
1794 if (wlist.isEmpty())
1795 return NULL;
1796
1797 return (qsamplerChannelStrip *) wlist.at(iChannel);
1798 }
1799
1800
1801 // Retrieve a channel strip by sampler channel id.
1802 qsamplerChannelStrip *qsamplerMainForm::channelStrip ( int iChannelID )
1803 {
1804 QWidgetList wlist = m_pWorkspace->windowList();
1805 if (wlist.isEmpty())
1806 return NULL;
1807
1808 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1809 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1810 if (pChannelStrip) {
1811 qsamplerChannel *pChannel = pChannelStrip->channel();
1812 if (pChannel && pChannel->channelID() == iChannelID)
1813 return pChannelStrip;
1814 }
1815 }
1816
1817 // Not found.
1818 return NULL;
1819 }
1820
1821
1822 // Construct the windows menu.
1823 void qsamplerMainForm::channelsMenuAboutToShow (void)
1824 {
1825 channelsMenu->clear();
1826 channelsArrangeAction->addTo(channelsMenu);
1827 channelsAutoArrangeAction->addTo(channelsMenu);
1828
1829 QWidgetList wlist = m_pWorkspace->windowList();
1830 if (!wlist.isEmpty()) {
1831 channelsMenu->insertSeparator();
1832 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1833 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1834 if (pChannelStrip) {
1835 int iItemID = channelsMenu->insertItem(pChannelStrip->caption(), this, SLOT(channelsMenuActivated(int)));
1836 channelsMenu->setItemParameter(iItemID, iChannel);
1837 channelsMenu->setItemChecked(iItemID, activeChannelStrip() == pChannelStrip);
1838 }
1839 }
1840 }
1841 }
1842
1843
1844 // Windows menu activation slot
1845 void qsamplerMainForm::channelsMenuActivated ( int iChannel )
1846 {
1847 qsamplerChannelStrip *pChannelStrip = channelStripAt(iChannel);
1848 if (pChannelStrip)
1849 pChannelStrip->showNormal();
1850 pChannelStrip->setFocus();
1851 }
1852
1853
1854 //-------------------------------------------------------------------------
1855 // qsamplerMainForm -- Timer stuff.
1856
1857 // Set the pseudo-timer delay schedule.
1858 void qsamplerMainForm::startSchedule ( int iStartDelay )
1859 {
1860 m_iStartDelay = 1 + (iStartDelay * 1000);
1861 m_iTimerDelay = 0;
1862 }
1863
1864 // Suspend the pseudo-timer delay schedule.
1865 void qsamplerMainForm::stopSchedule (void)
1866 {
1867 m_iStartDelay = 0;
1868 m_iTimerDelay = 0;
1869 }
1870
1871 // Timer slot funtion.
1872 void qsamplerMainForm::timerSlot (void)
1873 {
1874 if (m_pOptions == NULL)
1875 return;
1876
1877 // Is it the first shot on server start after a few delay?
1878 if (m_iTimerDelay < m_iStartDelay) {
1879 m_iTimerDelay += QSAMPLER_TIMER_MSECS;
1880 if (m_iTimerDelay >= m_iStartDelay) {
1881 // If we cannot start it now, maybe a lil'mo'later ;)
1882 if (!startClient()) {
1883 m_iStartDelay += m_iTimerDelay;
1884 m_iTimerDelay = 0;
1885 }
1886 }
1887 }
1888
1889 if (m_pClient) {
1890 // Update the channel information for each pending strip...
1891 if (m_changedStrips.count() > 0) {
1892 for (qsamplerChannelStrip *pChannelStrip = m_changedStrips.first();
1893 pChannelStrip; pChannelStrip = m_changedStrips.next()) {
1894 // If successfull, remove from pending list...
1895 if (pChannelStrip->updateChannelInfo())
1896 m_changedStrips.remove(pChannelStrip);
1897 }
1898 }
1899 // Refresh each channel usage, on each period...
1900 if (m_pOptions->bAutoRefresh) {
1901 m_iTimerSlot += QSAMPLER_TIMER_MSECS;
1902 if (m_iTimerSlot >= m_pOptions->iAutoRefreshTime) {
1903 m_iTimerSlot = 0;
1904 // Update the channel stream usage for each strip...
1905 QWidgetList wlist = m_pWorkspace->windowList();
1906 for (int iChannel = 0;
1907 iChannel < (int) wlist.count(); iChannel++) {
1908 qsamplerChannelStrip *pChannelStrip
1909 = (qsamplerChannelStrip *) wlist.at(iChannel);
1910 if (pChannelStrip && pChannelStrip->isVisible())
1911 pChannelStrip->updateChannelUsage();
1912 }
1913 }
1914 }
1915 }
1916
1917 // Register the next timer slot.
1918 QTimer::singleShot(QSAMPLER_TIMER_MSECS, this, SLOT(timerSlot()));
1919 }
1920
1921
1922 //-------------------------------------------------------------------------
1923 // qsamplerMainForm -- Server stuff.
1924
1925 // Start linuxsampler server...
1926 void qsamplerMainForm::startServer (void)
1927 {
1928 if (m_pOptions == NULL)
1929 return;
1930
1931 // Aren't already a client, are we?
1932 if (!m_pOptions->bServerStart || m_pClient)
1933 return;
1934
1935 // Is the server process instance still here?
1936 if (m_pServer) {
1937 switch (QMessageBox::warning(this,
1938 QSAMPLER_TITLE ": " + tr("Warning"),
1939 tr("Could not start the LinuxSampler server.\n\n"
1940 "Maybe it ss already started."),
1941 tr("Stop"), tr("Kill"), tr("Cancel"))) {
1942 case 0:
1943 m_pServer->tryTerminate();
1944 break;
1945 case 1:
1946 m_pServer->kill();
1947 break;
1948 }
1949 return;
1950 }
1951
1952 // Reset our timer counters...
1953 stopSchedule();
1954
1955 // OK. Let's build the startup process...
1956 m_pServer = new QProcess(this);
1957
1958 // Setup stdout/stderr capture...
1959 //if (m_pOptions->bStdoutCapture) {
1960 m_pServer->setCommunication(QProcess::Stdout | QProcess::Stderr | QProcess::DupStderr);
1961 QObject::connect(m_pServer, SIGNAL(readyReadStdout()), this, SLOT(readServerStdout()));
1962 QObject::connect(m_pServer, SIGNAL(readyReadStderr()), this, SLOT(readServerStdout()));
1963 //}
1964 // The unforgiveable signal communication...
1965 QObject::connect(m_pServer, SIGNAL(processExited()), this, SLOT(processServerExit()));
1966
1967 // Build process arguments...
1968 m_pServer->setArguments(QStringList::split(' ', m_pOptions->sServerCmdLine));
1969
1970 appendMessages(tr("Server is starting..."));
1971 appendMessagesColor(m_pOptions->sServerCmdLine, "#990099");
1972
1973 // Go jack, go...
1974 if (!m_pServer->start()) {
1975 appendMessagesError(tr("Could not start server.\n\nSorry."));
1976 processServerExit();
1977 return;
1978 }
1979
1980 // Show startup results...
1981 appendMessages(tr("Server was started with PID=%1.").arg((long) m_pServer->processIdentifier()));
1982
1983 // Reset (yet again) the timer counters,
1984 // but this time is deferred as the user opted.
1985 startSchedule(m_pOptions->iStartDelay);
1986 stabilizeForm();
1987 }
1988
1989
1990 // Stop linuxsampler server...
1991 void qsamplerMainForm::stopServer (void)
1992 {
1993 // Stop client code.
1994 stopClient();
1995
1996 // And try to stop server.
1997 if (m_pServer) {
1998 appendMessages(tr("Server is stopping..."));
1999 if (m_pServer->isRunning())
2000 m_pServer->tryTerminate();
2001 }
2002
2003 // Give it some time to terminate gracefully and stabilize...
2004 QTime t;
2005 t.start();
2006 while (t.elapsed() < QSAMPLER_TIMER_MSECS)
2007 QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
2008
2009 // Do final processing anyway.
2010 processServerExit();
2011 }
2012
2013
2014 // Stdout handler...
2015 void qsamplerMainForm::readServerStdout (void)
2016 {
2017 if (m_pMessages)
2018 m_pMessages->appendStdoutBuffer(m_pServer->readStdout());
2019 }
2020
2021
2022 // Linuxsampler server cleanup.
2023 void qsamplerMainForm::processServerExit (void)
2024 {
2025 // Force client code cleanup.
2026 stopClient();
2027
2028 // Flush anything that maybe pending...
2029 if (m_pMessages)
2030 m_pMessages->flushStdoutBuffer();
2031
2032 if (m_pServer) {
2033 // Force final server shutdown...
2034 appendMessages(tr("Server was stopped with exit status %1.").arg(m_pServer->exitStatus()));
2035 if (!m_pServer->normalExit())
2036 m_pServer->kill();
2037 // Destroy it.
2038 delete m_pServer;
2039 m_pServer = NULL;
2040 }
2041
2042 // Again, make status visible stable.
2043 stabilizeForm();
2044 }
2045
2046
2047 //-------------------------------------------------------------------------
2048 // qsamplerMainForm -- Client stuff.
2049
2050 // The LSCP client callback procedure.
2051 lscp_status_t qsampler_client_callback ( lscp_client_t */*pClient*/, lscp_event_t event, const char *pchData, int cchData, void *pvData )
2052 {
2053 qsamplerMainForm *pMainForm = (qsamplerMainForm *) pvData;
2054 if (pMainForm == NULL)
2055 return LSCP_FAILED;
2056
2057 // ATTN: DO NOT EVER call any GUI code here,
2058 // as this is run under some other thread context.
2059 // A custom event must be posted here...
2060 QApplication::postEvent(pMainForm, new qsamplerCustomEvent(event, pchData, cchData));
2061
2062 return LSCP_OK;
2063 }
2064
2065
2066 // Start our almighty client...
2067 bool qsamplerMainForm::startClient (void)
2068 {
2069 // Have it a setup?
2070 if (m_pOptions == NULL)
2071 return false;
2072
2073 // Aren't we already started, are we?
2074 if (m_pClient)
2075 return true;
2076
2077 // Log prepare here.
2078 appendMessages(tr("Client connecting..."));
2079
2080 // Create the client handle...
2081 m_pClient = ::lscp_client_create(m_pOptions->sServerHost.latin1(), m_pOptions->iServerPort, qsampler_client_callback, this);
2082 if (m_pClient == NULL) {
2083 // Is this the first try?
2084 // maybe we need to start a local server...
2085 if ((m_pServer && m_pServer->isRunning()) || !m_pOptions->bServerStart)
2086 appendMessagesError(tr("Could not connect to server as client.\n\nSorry."));
2087 else
2088 startServer();
2089 // This is always a failure.
2090 stabilizeForm();
2091 return false;
2092 }
2093 // Just set receive timeout value, blindly.
2094 ::lscp_client_set_timeout(m_pClient, m_pOptions->iServerTimeout);
2095 appendMessages(tr("Client receive timeout is set to %1 msec.").arg(::lscp_client_get_timeout(m_pClient)));
2096
2097 // Subscribe to channel info change notifications...
2098 if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_CHANNEL_INFO) != LSCP_OK)
2099 appendMessagesClient("lscp_client_subscribe");
2100
2101 // We may stop scheduling around.
2102 stopSchedule();
2103
2104 // We'll accept drops from now on...
2105 setAcceptDrops(true);
2106
2107 // Log success here.
2108 appendMessages(tr("Client connected."));
2109
2110 // Hard-notify device configuration form,
2111 // if visible, that we're ready...
2112 if (m_pDeviceForm && m_pDeviceForm->isVisible())
2113 m_pDeviceForm->setClient(m_pClient);
2114
2115 // Is any session pending to be loaded?
2116 if (!m_pOptions->sSessionFile.isEmpty()) {
2117 // Just load the prabably startup session...
2118 if (loadSessionFile(m_pOptions->sSessionFile)) {
2119 m_pOptions->sSessionFile = QString::null;
2120 return true;
2121 }
2122 }
2123
2124 // Make a new session
2125 return newSession();
2126 }
2127
2128
2129 // Stop client...
2130 void qsamplerMainForm::stopClient (void)
2131 {
2132 if (m_pClient == NULL)
2133 return;
2134
2135 // Hard-notify device configuration form,
2136 // if visible, that we're running out...
2137 if (m_pDeviceForm && m_pDeviceForm->isVisible())
2138 m_pDeviceForm->setClient(NULL);
2139
2140 // Log prepare here.
2141 appendMessages(tr("Client disconnecting..."));
2142
2143 // Clear timer counters...
2144 stopSchedule();
2145
2146 // We'll reject drops from now on...
2147 setAcceptDrops(false);
2148
2149 // Force any channel strips around, but
2150 // but avoid removing the corresponding
2151 // channels from the back-end server.
2152 m_iDirtyCount = 0;
2153 closeSession(false);
2154
2155 // Close us as a client...
2156 ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_CHANNEL_INFO);
2157 ::lscp_client_destroy(m_pClient);
2158 m_pClient = NULL;
2159
2160 // Log final here.
2161 appendMessages(tr("Client disconnected."));
2162
2163 // Make visible status.
2164 stabilizeForm();
2165 }
2166
2167
2168 // end of qsamplerMainForm.ui.h

  ViewVC Help
Powered by ViewVC