1
// qsamplerMainForm.cpp
3
/****************************************************************************
4
Copyright (C) 2004-2007, rncbc aka Rui Nuno Capela. All rights reserved.
5
Copyright (C) 2007, Christian Schoenebeck
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.
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.
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.
21
*****************************************************************************/
23
#include "qsamplerAbout.h"
24
#include "qsamplerMainForm.h"
26
#include "qsamplerOptions.h"
27
#include "qsamplerChannel.h"
28
#include "qsamplerMessages.h"
30
#include "qsamplerChannelStrip.h"
31
#include "qsamplerInstrumentList.h"
33
#include "qsamplerInstrumentListForm.h"
34
#include "qsamplerDeviceForm.h"
35
#include "qsamplerOptionsForm.h"
37
#include <QApplication>
40
#include <QMessageBox>
43
#include <QTextStream>
44
#include <QFileDialog>
49
#include <QDragEnterEvent>
67
// Needed for lroundf()
71
static inline long lroundf ( float x )
74
return long(x + 0.5f);
76
return long(x - 0.5f);
81
// All winsock apps needs this.
83
static WSADATA _wsaData;
89
// Timer constant stuff.
90
#define QSAMPLER_TIMER_MSECS 200
92
// Status bar item indexes
93
#define QSAMPLER_STATUS_CLIENT 0 // Client connection state.
94
#define QSAMPLER_STATUS_SERVER 1 // Currenr server address (host:port)
95
#define QSAMPLER_STATUS_CHANNEL 2 // Active channel caption.
96
#define QSAMPLER_STATUS_SESSION 3 // Current session modification state.
99
//-------------------------------------------------------------------------
100
// CustomEvent -- specialty for callback comunication.
102
#define QSAMPLER_CUSTOM_EVENT QEvent::Type(QEvent::User + 0)
104
class CustomEvent : public QEvent
109
CustomEvent(lscp_event_t event, const char *pchData, int cchData)
110
: QEvent(QSAMPLER_CUSTOM_EVENT)
113
m_data = QString::fromUtf8(pchData, cchData);
117
lscp_event_t event() { return m_event; }
118
QString& data() { return m_data; }
122
// The proper event type.
123
lscp_event_t m_event;
124
// The event data as a string.
129
//-------------------------------------------------------------------------
130
// qsamplerMainForm -- Main window form implementation.
132
// Kind of singleton reference.
133
MainForm* MainForm::g_pMainForm = NULL;
135
MainForm::MainForm ( QWidget *pParent )
136
: QMainWindow(pParent)
140
// Pseudo-singleton reference setup.
143
// Initialize some pointer references.
146
// All child forms are to be created later, not earlier than setup.
148
m_pInstrumentListForm = NULL;
149
m_pDeviceForm = NULL;
151
// We'll start clean.
164
// Set to ignore any fatal "Broken pipe" signals.
165
::signal(SIGPIPE, SIG_IGN);
169
// Make some extras into the toolbar...
170
const QString& sVolumeText = tr("Master volume");
171
m_iVolumeChanging = 0;
173
m_ui.channelsToolbar->addSeparator();
174
m_pVolumeSlider = new QSlider(Qt::Horizontal, m_ui.channelsToolbar);
175
m_pVolumeSlider->setTickPosition(QSlider::TicksBothSides);
176
m_pVolumeSlider->setTickInterval(10);
177
m_pVolumeSlider->setPageStep(10);
178
m_pVolumeSlider->setSingleStep(10);
179
m_pVolumeSlider->setMinimum(0);
180
m_pVolumeSlider->setMaximum(100);
181
m_pVolumeSlider->setMaximumHeight(26);
182
m_pVolumeSlider->setMinimumWidth(160);
183
m_pVolumeSlider->setToolTip(sVolumeText);
184
QObject::connect(m_pVolumeSlider,
185
SIGNAL(valueChanged(int)),
186
SLOT(volumeChanged(int)));
187
//m_ui.channelsToolbar->setHorizontallyStretchable(true);
188
//m_ui.channelsToolbar->setStretchableWidget(m_pVolumeSlider);
189
m_ui.channelsToolbar->addWidget(m_pVolumeSlider);
191
m_ui.channelsToolbar->addSeparator();
192
m_pVolumeSpinBox = new QSpinBox(m_ui.channelsToolbar);
193
m_pVolumeSpinBox->setMaximumHeight(24);
194
m_pVolumeSpinBox->setSuffix(" %");
195
m_pVolumeSpinBox->setMinimum(0);
196
m_pVolumeSpinBox->setMaximum(100);
197
m_pVolumeSpinBox->setToolTip(sVolumeText);
198
QObject::connect(m_pVolumeSpinBox,
199
SIGNAL(valueChanged(int)),
200
SLOT(volumeChanged(int)));
201
m_ui.channelsToolbar->addWidget(m_pVolumeSpinBox);
204
// Make it an MDI workspace.
205
m_pWorkspace = new QWorkspace(this);
206
m_pWorkspace->setScrollBarsEnabled(true);
207
// Set the activation connection.
208
QObject::connect(m_pWorkspace,
209
SIGNAL(windowActivated(QWidget *)),
210
SLOT(activateStrip(QWidget *)));
212
setCentralWidget(m_pWorkspace);
214
// Create some statusbar labels...
217
pLabel = new QLabel(tr("Connected"), this);
218
pLabel->setAlignment(Qt::AlignLeft);
219
pLabel->setMinimumSize(pLabel->sizeHint());
220
m_statusItem[QSAMPLER_STATUS_CLIENT] = pLabel;
221
statusBar()->addWidget(pLabel);
223
pLabel = new QLabel(this);
224
pLabel->setAlignment(Qt::AlignLeft);
225
m_statusItem[QSAMPLER_STATUS_SERVER] = pLabel;
226
statusBar()->addWidget(pLabel, 1);
228
pLabel = new QLabel(this);
229
pLabel->setAlignment(Qt::AlignLeft);
230
m_statusItem[QSAMPLER_STATUS_CHANNEL] = pLabel;
231
statusBar()->addWidget(pLabel, 2);
232
// Session modification status.
233
pLabel = new QLabel(tr("MOD"), this);
234
pLabel->setAlignment(Qt::AlignHCenter);
235
pLabel->setMinimumSize(pLabel->sizeHint());
236
m_statusItem[QSAMPLER_STATUS_SESSION] = pLabel;
237
statusBar()->addWidget(pLabel);
240
WSAStartup(MAKEWORD(1, 1), &_wsaData);
243
// Some actions surely need those
244
// shortcuts firmly attached...
245
addAction(m_ui.viewMenubarAction);
246
addAction(m_ui.viewToolbarAction);
248
QObject::connect(m_ui.fileNewAction,
251
QObject::connect(m_ui.fileOpenAction,
254
QObject::connect(m_ui.fileSaveAction,
257
QObject::connect(m_ui.fileSaveAsAction,
260
QObject::connect(m_ui.fileResetAction,
263
QObject::connect(m_ui.fileRestartAction,
265
SLOT(fileRestart()));
266
QObject::connect(m_ui.fileExitAction,
269
QObject::connect(m_ui.editAddChannelAction,
271
SLOT(editAddChannel()));
272
QObject::connect(m_ui.editRemoveChannelAction,
274
SLOT(editRemoveChannel()));
275
QObject::connect(m_ui.editSetupChannelAction,
277
SLOT(editSetupChannel()));
278
QObject::connect(m_ui.editEditChannelAction,
280
SLOT(editEditChannel()));
281
QObject::connect(m_ui.editResetChannelAction,
283
SLOT(editResetChannel()));
284
QObject::connect(m_ui.editResetAllChannelsAction,
286
SLOT(editResetAllChannels()));
287
QObject::connect(m_ui.viewMenubarAction,
288
SIGNAL(toggled(bool)),
289
SLOT(viewMenubar(bool)));
290
QObject::connect(m_ui.viewToolbarAction,
291
SIGNAL(toggled(bool)),
292
SLOT(viewToolbar(bool)));
293
QObject::connect(m_ui.viewStatusbarAction,
294
SIGNAL(toggled(bool)),
295
SLOT(viewStatusbar(bool)));
296
QObject::connect(m_ui.viewMessagesAction,
297
SIGNAL(toggled(bool)),
298
SLOT(viewMessages(bool)));
299
QObject::connect(m_ui.viewInstrumentsAction,
301
SLOT(viewInstruments()));
302
QObject::connect(m_ui.viewDevicesAction,
304
SLOT(viewDevices()));
305
QObject::connect(m_ui.viewOptionsAction,
307
SLOT(viewOptions()));
308
QObject::connect(m_ui.channelsArrangeAction,
310
SLOT(channelsArrange()));
311
QObject::connect(m_ui.channelsAutoArrangeAction,
312
SIGNAL(toggled(bool)),
313
SLOT(channelsAutoArrange(bool)));
314
QObject::connect(m_ui.helpAboutAction,
317
QObject::connect(m_ui.helpAboutQtAction,
319
SLOT(helpAboutQt()));
321
QObject::connect(m_ui.fileMenu,
322
SIGNAL(aboutToShow()),
323
SLOT(updateRecentFilesMenu()));
324
QObject::connect(m_ui.channelsMenu,
325
SIGNAL(aboutToShow()),
326
SLOT(channelsMenuAboutToShow()));
330
MainForm::~MainForm()
332
// Do final processing anyway.
339
// Finally drop any widgets around...
341
delete m_pDeviceForm;
342
if (m_pInstrumentListForm)
343
delete m_pInstrumentListForm;
349
// Delete status item labels one by one.
350
if (m_statusItem[QSAMPLER_STATUS_CLIENT])
351
delete m_statusItem[QSAMPLER_STATUS_CLIENT];
352
if (m_statusItem[QSAMPLER_STATUS_SERVER])
353
delete m_statusItem[QSAMPLER_STATUS_SERVER];
354
if (m_statusItem[QSAMPLER_STATUS_CHANNEL])
355
delete m_statusItem[QSAMPLER_STATUS_CHANNEL];
356
if (m_statusItem[QSAMPLER_STATUS_SESSION])
357
delete m_statusItem[QSAMPLER_STATUS_SESSION];
360
delete m_pVolumeSpinBox;
361
delete m_pVolumeSlider;
364
// Pseudo-singleton reference shut-down.
369
// Make and set a proper setup options step.
370
void MainForm::setup ( Options *pOptions )
373
m_pOptions = pOptions;
375
// What style do we create these forms?
376
Qt::WindowFlags wflags = Qt::Window
377
#if QT_VERSION >= 0x040200
378
| Qt::CustomizeWindowHint
380
| Qt::WindowTitleHint
381
| Qt::WindowSystemMenuHint
382
| Qt::WindowMinMaxButtonsHint;
383
if (m_pOptions->bKeepOnTop)
385
// Some child forms are to be created right now.
386
m_pMessages = new Messages(this);
387
m_pDeviceForm = new DeviceForm(this, wflags);
388
#ifdef CONFIG_MIDI_INSTRUMENT
389
m_pInstrumentListForm = new InstrumentListForm(this, wflags);
391
viewInstrumentsAction->setEnabled(false);
393
// Set message defaults...
394
updateMessagesFont();
395
updateMessagesLimit();
396
updateMessagesCapture();
397
// Set the visibility signal.
398
QObject::connect(m_pMessages,
399
SIGNAL(visibilityChanged(bool)),
400
SLOT(stabilizeForm()));
402
// Initial decorations toggle state.
403
m_ui.viewMenubarAction->setChecked(m_pOptions->bMenubar);
404
m_ui.viewToolbarAction->setChecked(m_pOptions->bToolbar);
405
m_ui.viewStatusbarAction->setChecked(m_pOptions->bStatusbar);
406
m_ui.channelsAutoArrangeAction->setChecked(m_pOptions->bAutoArrange);
408
// Initial decorations visibility state.
409
viewMenubar(m_pOptions->bMenubar);
410
viewToolbar(m_pOptions->bToolbar);
411
viewStatusbar(m_pOptions->bStatusbar);
413
addDockWidget(Qt::BottomDockWidgetArea, m_pMessages);
415
// Restore whole dock windows state.
416
QByteArray aDockables = m_pOptions->settings().value(
417
"/Layout/DockWindows").toByteArray();
418
if (!aDockables.isEmpty()) {
419
restoreState(aDockables);
422
// Try to restore old window positioning and initial visibility.
423
m_pOptions->loadWidgetGeometry(this);
424
m_pOptions->loadWidgetGeometry(m_pInstrumentListForm);
425
m_pOptions->loadWidgetGeometry(m_pDeviceForm);
427
// Final startup stabilization...
429
updateRecentFilesMenu();
433
statusBar()->showMessage(tr("Ready"), 3000);
435
// We'll try to start immediately...
438
// Register the first timer slot.
439
QTimer::singleShot(QSAMPLER_TIMER_MSECS, this, SLOT(timerSlot()));
443
// Window close event handlers.
444
bool MainForm::queryClose (void)
446
bool bQueryClose = closeSession(false);
448
// Try to save current general state...
450
// Some windows default fonts is here on demand too.
451
if (bQueryClose && m_pMessages)
452
m_pOptions->sMessagesFont = m_pMessages->messagesFont().toString();
453
// Try to save current positioning.
455
// Save decorations state.
456
m_pOptions->bMenubar = m_ui.MenuBar->isVisible();
457
m_pOptions->bToolbar = (m_ui.fileToolbar->isVisible()
458
|| m_ui.editToolbar->isVisible()
459
|| m_ui.channelsToolbar->isVisible());
460
m_pOptions->bStatusbar = statusBar()->isVisible();
461
// Save the dock windows state.
462
const QString sDockables = saveState().toBase64().data();
463
m_pOptions->settings().setValue("/Layout/DockWindows", saveState());
464
// And the children, and the main windows state,.
465
m_pOptions->saveWidgetGeometry(m_pDeviceForm);
466
m_pOptions->saveWidgetGeometry(m_pInstrumentListForm);
467
m_pOptions->saveWidgetGeometry(this);
468
// Close popup widgets.
469
if (m_pInstrumentListForm)
470
m_pInstrumentListForm->close();
472
m_pDeviceForm->close();
473
// Stop client and/or server, gracefully.
482
void MainForm::closeEvent ( QCloseEvent *pCloseEvent )
485
pCloseEvent->accept();
487
pCloseEvent->ignore();
491
// Window drag-n-drop event handlers.
492
void MainForm::dragEnterEvent ( QDragEnterEvent* pDragEnterEvent )
494
// Accept external drags only...
495
if (pDragEnterEvent->source() == NULL
496
&& pDragEnterEvent->mimeData()->hasUrls()) {
497
pDragEnterEvent->accept();
499
pDragEnterEvent->ignore();
504
void MainForm::dropEvent ( QDropEvent* pDropEvent )
506
// Accept externally originated drops only...
507
if (pDropEvent->source())
510
const QMimeData *pMimeData = pDropEvent->mimeData();
511
if (pMimeData->hasUrls()) {
512
QListIterator<QUrl> iter(pMimeData->urls());
513
while (iter.hasNext()) {
514
const QString& sPath = iter.next().toLocalFile();
515
if (Channel::isInstrumentFile(sPath)) {
516
// Try to create a new channel from instrument file...
517
Channel *pChannel = new Channel();
518
if (pChannel == NULL)
520
// Start setting the instrument filename...
521
pChannel->setInstrument(sPath, 0);
522
// Before we show it up, may be we'll
523
// better ask for some initial values?
524
if (!pChannel->channelSetup(this)) {
528
// Finally, give it to a new channel strip...
529
if (!createChannelStrip(pChannel)) {
533
// Make that an overall update.
536
} // Otherwise, load an usual session file (LSCP script)...
537
else if (closeSession(true)) {
538
loadSessionFile(sPath);
542
// Make it look responsive...:)
543
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
548
// Custome event handler.
549
void MainForm::customEvent(QEvent* pCustomEvent)
551
// For the time being, just pump it to messages.
552
if (pCustomEvent->type() == QSAMPLER_CUSTOM_EVENT) {
553
CustomEvent *pEvent = static_cast<CustomEvent *> (pCustomEvent);
554
if (pEvent->event() == LSCP_EVENT_CHANNEL_INFO) {
555
int iChannelID = pEvent->data().toInt();
556
ChannelStrip *pChannelStrip = channelStrip(iChannelID);
558
channelStripChanged(pChannelStrip);
560
appendMessagesColor(tr("Notify event: %1 data: %2")
561
.arg(::lscp_event_to_text(pEvent->event()))
562
.arg(pEvent->data()), "#996699");
567
// Context menu event handler.
568
void MainForm::contextMenuEvent( QContextMenuEvent *pEvent )
572
m_ui.editMenu->exec(pEvent->globalPos());
576
//-------------------------------------------------------------------------
577
// qsamplerMainForm -- Brainless public property accessors.
579
// The global options settings property.
580
Options *MainForm::options (void) const
586
// The LSCP client descriptor property.
587
lscp_client_t *MainForm::client (void) const
593
// The pseudo-singleton instance accessor.
594
MainForm *MainForm::getInstance (void)
600
//-------------------------------------------------------------------------
601
// qsamplerMainForm -- Session file stuff.
603
// Format the displayable session filename.
604
QString MainForm::sessionName ( const QString& sFilename )
606
bool bCompletePath = (m_pOptions && m_pOptions->bCompletePath);
607
QString sSessionName = sFilename;
608
if (sSessionName.isEmpty())
609
sSessionName = tr("Untitled") + QString::number(m_iUntitled);
610
else if (!bCompletePath)
611
sSessionName = QFileInfo(sSessionName).fileName();
616
// Create a new session file from scratch.
617
bool MainForm::newSession (void)
619
// Check if we can do it.
620
if (!closeSession(true))
623
// Give us what the server has, right now...
626
// Ok increment untitled count.
630
m_sFilename = QString::null;
632
appendMessages(tr("New session: \"%1\".").arg(sessionName(m_sFilename)));
639
// Open an existing sampler session.
640
bool MainForm::openSession (void)
642
if (m_pOptions == NULL)
645
// Ask for the filename to open...
646
QString sFilename = QFileDialog::getOpenFileName(this,
647
QSAMPLER_TITLE ": " + tr("Open Session"), // Caption.
648
m_pOptions->sSessionDir, // Start here.
649
tr("LSCP Session files") + " (*.lscp)" // Filter (LSCP files)
652
// Have we cancelled?
653
if (sFilename.isEmpty())
656
// Check if we're going to discard safely the current one...
657
if (!closeSession(true))
660
// Load it right away.
661
return loadSessionFile(sFilename);
665
// Save current sampler session with another name.
666
bool MainForm::saveSession ( bool bPrompt )
668
if (m_pOptions == NULL)
671
QString sFilename = m_sFilename;
673
// Ask for the file to save, if there's none...
674
if (bPrompt || sFilename.isEmpty()) {
675
// If none is given, assume default directory.
676
if (sFilename.isEmpty())
677
sFilename = m_pOptions->sSessionDir;
679
sFilename = QFileDialog::getSaveFileName(this,
680
QSAMPLER_TITLE ": " + tr("Save Session"), // Caption.
681
sFilename, // Start here.
682
tr("LSCP Session files") + " (*.lscp)" // Filter (LSCP files)
684
// Have we cancelled it?
685
if (sFilename.isEmpty())
687
// Enforce .lscp extension...
688
if (QFileInfo(sFilename).suffix().isEmpty())
689
sFilename += ".lscp";
690
// Check if already exists...
691
if (sFilename != m_sFilename && QFileInfo(sFilename).exists()) {
692
if (QMessageBox::warning(this,
693
QSAMPLER_TITLE ": " + tr("Warning"),
694
tr("The file already exists:\n\n"
696
"Do you want to replace it?")
698
tr("Replace"), tr("Cancel")) > 0)
703
// Save it right away.
704
return saveSessionFile(sFilename);
708
// Close current session.
709
bool MainForm::closeSession ( bool bForce )
713
// Are we dirty enough to prompt it?
714
if (m_iDirtyCount > 0) {
715
switch (QMessageBox::warning(this,
716
QSAMPLER_TITLE ": " + tr("Warning"),
717
tr("The current session has been changed:\n\n"
719
"Do you want to save the changes?")
720
.arg(sessionName(m_sFilename)),
721
tr("Save"), tr("Discard"), tr("Cancel"))) {
723
bClose = saveSession(false);
733
// If we may close it, dot it.
735
// Remove all channel strips from sight...
736
m_pWorkspace->setUpdatesEnabled(false);
737
QWidgetList wlist = m_pWorkspace->windowList();
738
for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
739
ChannelStrip *pChannelStrip = (ChannelStrip*) wlist.at(iChannel);
741
Channel *pChannel = pChannelStrip->channel();
742
if (bForce && pChannel)
743
pChannel->removeChannel();
744
delete pChannelStrip;
747
m_pWorkspace->setUpdatesEnabled(true);
748
// We're now clean, for sure.
756
// Load a session from specific file path.
757
bool MainForm::loadSessionFile ( const QString& sFilename )
759
if (m_pClient == NULL)
762
// Open and read from real file.
763
QFile file(sFilename);
764
if (!file.open(QIODevice::ReadOnly)) {
766
tr("Could not open \"%1\" session file.\n\nSorry.")
771
// Tell the world we'll take some time...
772
QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
777
QTextStream ts(&file);
778
while (!ts.atEnd()) {
780
QString sCommand = ts.readLine().trimmed();
782
// If not empty, nor a comment, call the server...
783
if (!sCommand.isEmpty() && sCommand[0] != '#') {
784
// Remember that, no matter what,
785
// all LSCP commands are CR/LF terminated.
787
if (::lscp_client_query(m_pClient, sCommand.toUtf8().constData())
789
appendMessagesColor(QString("%1(%2): %3")
790
.arg(QFileInfo(sFilename).fileName()).arg(iLine)
791
.arg(sCommand.simplified()), "#996633");
792
appendMessagesClient("lscp_client_query");
796
// Try to make it snappy :)
797
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
800
// Ok. we've read it.
803
// Now we'll try to create (update) the whole GUI session.
806
// We're fornerly done.
807
QApplication::restoreOverrideCursor();
809
// Have we any errors?
812
tr("Session loaded with errors\nfrom \"%1\".\n\nSorry.")
816
// Save as default session directory.
818
m_pOptions->sSessionDir = QFileInfo(sFilename).dir().absolutePath();
819
// We're not dirty anymore, if loaded without errors,
820
m_iDirtyCount = iErrors;
822
m_sFilename = sFilename;
823
updateRecentFiles(sFilename);
824
appendMessages(tr("Open session: \"%1\".").arg(sessionName(m_sFilename)));
826
// Make that an overall update.
832
// Save current session to specific file path.
833
bool MainForm::saveSessionFile ( const QString& sFilename )
835
if (m_pClient == NULL)
838
// Check whether server is apparently OK...
839
if (::lscp_get_channels(m_pClient) < 0) {
840
appendMessagesClient("lscp_get_channels");
844
// Open and write into real file.
845
QFile file(sFilename);
846
if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
848
tr("Could not open \"%1\" session file.\n\nSorry.")
853
// Tell the world we'll take some time...
854
QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
858
QTextStream ts(&file);
859
ts << "# " << QSAMPLER_TITLE " - " << tr(QSAMPLER_SUBTITLE) << endl;
860
ts << "# " << tr("Version")
861
<< ": " QSAMPLER_VERSION << endl;
862
ts << "# " << tr("Build")
863
<< ": " __DATE__ " " __TIME__ << endl;
865
ts << "# " << tr("File")
866
<< ": " << QFileInfo(sFilename).fileName() << endl;
867
ts << "# " << tr("Date")
868
<< ": " << QDate::currentDate().toString("MMM dd yyyy")
869
<< " " << QTime::currentTime().toString("hh:mm:ss") << endl;
873
// It is assumed that this new kind of device+session file
874
// will be loaded from a complete initialized server...
877
ts << "RESET" << endl;
879
// Audio device mapping.
880
QMap<int, int> audioDeviceMap;
881
piDeviceIDs = Device::getDevices(m_pClient, Device::Audio);
882
for (iDevice = 0; piDeviceIDs && piDeviceIDs[iDevice] >= 0; iDevice++) {
884
Device device(Device::Audio, piDeviceIDs[iDevice]);
885
// Audio device specification...
886
ts << "# " << device.deviceTypeName() << " " << device.driverName()
887
<< " " << tr("Device") << " " << iDevice << endl;
888
ts << "CREATE AUDIO_OUTPUT_DEVICE " << device.driverName();
889
DeviceParamMap::ConstIterator deviceParam;
890
for (deviceParam = device.params().begin();
891
deviceParam != device.params().end();
893
const DeviceParam& param = deviceParam.value();
894
if (param.value.isEmpty()) ts << "# ";
895
ts << " " << deviceParam.key() << "='" << param.value << "'";
898
// Audio channel parameters...
900
QListIterator<DevicePort *> iter(device.ports());
901
while (iter.hasNext()) {
902
DevicePort *pPort = iter.next();
903
DeviceParamMap::ConstIterator portParam;
904
for (portParam = pPort->params().begin();
905
portParam != pPort->params().end();
907
const DeviceParam& param = portParam.value();
908
if (param.fix || param.value.isEmpty()) ts << "# ";
909
ts << "SET AUDIO_OUTPUT_CHANNEL_PARAMETER " << iDevice
910
<< " " << iPort << " " << portParam.key()
911
<< "='" << param.value << "'" << endl;
915
// Audio device index/id mapping.
916
audioDeviceMap[device.deviceID()] = iDevice;
917
// Try to keep it snappy :)
918
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
921
// MIDI device mapping.
922
QMap<int, int> midiDeviceMap;
923
piDeviceIDs = Device::getDevices(m_pClient, Device::Midi);
924
for (iDevice = 0; piDeviceIDs && piDeviceIDs[iDevice] >= 0; iDevice++) {
926
Device device(Device::Midi, piDeviceIDs[iDevice]);
927
// MIDI device specification...
928
ts << "# " << device.deviceTypeName() << " " << device.driverName()
929
<< " " << tr("Device") << " " << iDevice << endl;
930
ts << "CREATE MIDI_INPUT_DEVICE " << device.driverName();
931
DeviceParamMap::ConstIterator deviceParam;
932
for (deviceParam = device.params().begin();
933
deviceParam != device.params().end();
935
const DeviceParam& param = deviceParam.value();
936
if (param.value.isEmpty()) ts << "# ";
937
ts << " " << deviceParam.key() << "='" << param.value << "'";
940
// MIDI port parameters...
942
QListIterator<DevicePort *> iter(device.ports());
943
while (iter.hasNext()) {
944
DevicePort *pPort = iter.next();
945
DeviceParamMap::ConstIterator portParam;
946
for (portParam = pPort->params().begin();
947
portParam != pPort->params().end();
949
const DeviceParam& param = portParam.value();
950
if (param.fix || param.value.isEmpty()) ts << "# ";
951
ts << "SET MIDI_INPUT_PORT_PARAMETER " << iDevice
952
<< " " << iPort << " " << portParam.key()
953
<< "='" << param.value << "'" << endl;
957
// MIDI device index/id mapping.
958
midiDeviceMap[device.deviceID()] = iDevice;
959
// Try to keep it snappy :)
960
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
964
#ifdef CONFIG_MIDI_INSTRUMENT
965
// MIDI instrument mapping...
966
QMap<int, int> midiInstrumentMap;
967
int *piMaps = ::lscp_list_midi_instrument_maps(m_pClient);
968
for (int iMap = 0; piMaps && piMaps[iMap] >= 0; iMap++) {
969
int iMidiMap = piMaps[iMap];
970
const char *pszMapName
971
= ::lscp_get_midi_instrument_map_name(m_pClient, iMidiMap);
972
ts << "# " << tr("MIDI instrument map") << " " << iMap;
974
ts << " - " << pszMapName;
976
ts << "ADD MIDI_INSTRUMENT_MAP";
978
ts << " '" << pszMapName << "'";
980
// MIDI instrument mapping...
981
lscp_midi_instrument_t *pInstrs
982
= ::lscp_list_midi_instruments(m_pClient, iMidiMap);
983
for (int iInstr = 0; pInstrs && pInstrs[iInstr].map >= 0; iInstr++) {
984
lscp_midi_instrument_info_t *pInstrInfo
985
= ::lscp_get_midi_instrument_info(m_pClient, &pInstrs[iInstr]);
987
ts << "MAP MIDI_INSTRUMENT "
989
<< pInstrs[iInstr].bank << " "
990
<< pInstrs[iInstr].prog << " "
991
<< pInstrInfo->engine_name << " '"
992
<< pInstrInfo->instrument_file << "' "
993
<< pInstrInfo->instrument_nr << " "
994
<< pInstrInfo->volume << " ";
995
switch (pInstrInfo->load_mode) {
996
case LSCP_LOAD_PERSISTENT:
999
case LSCP_LOAD_ON_DEMAND_HOLD:
1000
ts << "ON_DEMAND_HOLD";
1002
case LSCP_LOAD_ON_DEMAND:
1003
case LSCP_LOAD_DEFAULT:
1008
if (pInstrInfo->name)
1009
ts << " '" << pInstrInfo->name << "'";
1011
} // Check for errors...
1012
else if (::lscp_client_get_errno(m_pClient)) {
1013
appendMessagesClient("lscp_get_midi_instrument_info");
1016
// Try to keep it snappy :)
1017
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
1020
// Check for errors...
1021
if (pInstrs == NULL && ::lscp_client_get_errno(m_pClient)) {
1022
appendMessagesClient("lscp_list_midi_instruments");
1025
// MIDI strument index/id mapping.
1026
midiInstrumentMap[iMidiMap] = iMap;
1028
// Check for errors...
1029
if (piMaps == NULL && ::lscp_client_get_errno(m_pClient)) {
1030
appendMessagesClient("lscp_list_midi_instrument_maps");
1033
#endif // CONFIG_MIDI_INSTRUMENT
1035
// Sampler channel mapping.
1036
QWidgetList wlist = m_pWorkspace->windowList();
1037
for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1038
ChannelStrip* pChannelStrip
1039
= static_cast<ChannelStrip *> (wlist.at(iChannel));
1040
if (pChannelStrip) {
1041
Channel *pChannel = pChannelStrip->channel();
1043
ts << "# " << tr("Channel") << " " << iChannel << endl;
1044
ts << "ADD CHANNEL" << endl;
1045
if (audioDeviceMap.isEmpty()) {
1046
ts << "SET CHANNEL AUDIO_OUTPUT_TYPE " << iChannel
1047
<< " " << pChannel->audioDriver() << endl;
1049
ts << "SET CHANNEL AUDIO_OUTPUT_DEVICE " << iChannel
1050
<< " " << audioDeviceMap[pChannel->audioDevice()] << endl;
1052
if (midiDeviceMap.isEmpty()) {
1053
ts << "SET CHANNEL MIDI_INPUT_TYPE " << iChannel
1054
<< " " << pChannel->midiDriver() << endl;
1056
ts << "SET CHANNEL MIDI_INPUT_DEVICE " << iChannel
1057
<< " " << midiDeviceMap[pChannel->midiDevice()] << endl;
1059
ts << "SET CHANNEL MIDI_INPUT_PORT " << iChannel
1060
<< " " << pChannel->midiPort() << endl;
1061
ts << "SET CHANNEL MIDI_INPUT_CHANNEL " << iChannel << " ";
1062
if (pChannel->midiChannel() == LSCP_MIDI_CHANNEL_ALL)
1065
ts << pChannel->midiChannel();
1067
ts << "LOAD ENGINE " << pChannel->engineName()
1068
<< " " << iChannel << endl;
1069
if (pChannel->instrumentStatus() < 100) ts << "# ";
1070
ts << "LOAD INSTRUMENT NON_MODAL '"
1071
<< pChannel->instrumentFile() << "' "
1072
<< pChannel->instrumentNr() << " " << iChannel << endl;
1073
ChannelRoutingMap::ConstIterator audioRoute;
1074
for (audioRoute = pChannel->audioRouting().begin();
1075
audioRoute != pChannel->audioRouting().end();
1077
ts << "SET CHANNEL AUDIO_OUTPUT_CHANNEL " << iChannel
1078
<< " " << audioRoute.key()
1079
<< " " << audioRoute.value() << endl;
1081
ts << "SET CHANNEL VOLUME " << iChannel
1082
<< " " << pChannel->volume() << endl;
1083
if (pChannel->channelMute())
1084
ts << "SET CHANNEL MUTE " << iChannel << " 1" << endl;
1085
if (pChannel->channelSolo())
1086
ts << "SET CHANNEL SOLO " << iChannel << " 1" << endl;
1087
#ifdef CONFIG_MIDI_INSTRUMENT
1088
if (pChannel->midiMap() >= 0) {
1089
ts << "SET CHANNEL MIDI_INSTRUMENT_MAP " << iChannel
1090
<< " " << midiInstrumentMap[pChannel->midiMap()] << endl;
1093
#ifdef CONFIG_FXSEND
1094
int iChannelID = pChannel->channelID();
1095
int *piFxSends = ::lscp_list_fxsends(m_pClient, iChannelID);
1096
for (int iFxSend = 0;
1097
piFxSends && piFxSends[iFxSend] >= 0;
1099
lscp_fxsend_info_t *pFxSendInfo = ::lscp_get_fxsend_info(
1100
m_pClient, iChannelID, piFxSends[iFxSend]);
1102
ts << "CREATE FX_SEND " << iChannel
1103
<< " " << pFxSendInfo->midi_controller;
1104
if (pFxSendInfo->name)
1105
ts << " '" << pFxSendInfo->name << "'";
1107
int *piRouting = pFxSendInfo->audio_routing;
1108
for (int iAudioSrc = 0;
1109
piRouting && piRouting[iAudioSrc] >= 0;
1111
ts << "SET FX_SEND AUDIO_OUTPUT_CHANNEL "
1115
<< " " << piRouting[iAudioSrc] << endl;
1117
#ifdef CONFIG_FXSEND_LEVEL
1118
ts << "SET FX_SEND LEVEL " << iChannel
1120
<< " " << pFxSendInfo->level << endl;
1122
} // Check for errors...
1123
else if (::lscp_client_get_errno(m_pClient)) {
1124
appendMessagesClient("lscp_get_fxsend_info");
1132
// Try to keep it snappy :)
1133
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
1136
#ifdef CONFIG_VOLUME
1137
ts << "# " << tr("Global volume level") << endl;
1138
ts << "SET VOLUME " << ::lscp_get_volume(m_pClient) << endl;
1142
// Ok. we've wrote it.
1145
// We're fornerly done.
1146
QApplication::restoreOverrideCursor();
1148
// Have we any errors?
1150
appendMessagesError(
1151
tr("Some settings could not be saved\n"
1152
"to \"%1\" session file.\n\nSorry.")
1156
// Save as default session directory.
1158
m_pOptions->sSessionDir = QFileInfo(sFilename).dir().absolutePath();
1159
// We're not dirty anymore.
1161
// Stabilize form...
1162
m_sFilename = sFilename;
1163
updateRecentFiles(sFilename);
1164
appendMessages(tr("Save session: \"%1\".").arg(sessionName(m_sFilename)));
1170
// Session change receiver slot.
1171
void MainForm::sessionDirty (void)
1173
// Just mark the dirty form.
1175
// and update the form status...
1180
//-------------------------------------------------------------------------
1181
// qsamplerMainForm -- File Action slots.
1183
// Create a new sampler session.
1184
void MainForm::fileNew (void)
1186
// Of course we'll start clean new.
1191
// Open an existing sampler session.
1192
void MainForm::fileOpen (void)
1194
// Open it right away.
1199
// Open a recent file session.
1200
void MainForm::fileOpenRecent (void)
1202
// Retrive filename index from action data...
1203
QAction *pAction = qobject_cast<QAction *> (sender());
1204
if (pAction && m_pOptions) {
1205
int iIndex = pAction->data().toInt();
1206
if (iIndex >= 0 && iIndex < m_pOptions->recentFiles.count()) {
1207
QString sFilename = m_pOptions->recentFiles[iIndex];
1208
// Check if we can safely close the current session...
1209
if (!sFilename.isEmpty() && closeSession(true))
1210
loadSessionFile(sFilename);
1216
// Save current sampler session.
1217
void MainForm::fileSave (void)
1219
// Save it right away.
1224
// Save current sampler session with another name.
1225
void MainForm::fileSaveAs (void)
1227
// Save it right away, maybe with another name.
1232
// Reset the sampler instance.
1233
void MainForm::fileReset (void)
1235
if (m_pClient == NULL)
1238
// Ask user whether he/she want's an internal sampler reset...
1239
if (QMessageBox::warning(this,
1240
QSAMPLER_TITLE ": " + tr("Warning"),
1241
tr("Resetting the sampler instance will close\n"
1242
"all device and channel configurations.\n\n"
1243
"Please note that this operation may cause\n"
1244
"temporary MIDI and Audio disruption.\n\n"
1245
"Do you want to reset the sampler engine now?"),
1246
tr("Reset"), tr("Cancel")) > 0)
1249
// Trye closing the current session, first...
1250
if (!closeSession(true))
1253
// Just do the reset, after closing down current session...
1254
// Do the actual sampler reset...
1255
if (::lscp_reset_sampler(m_pClient) != LSCP_OK) {
1256
appendMessagesClient("lscp_reset_sampler");
1257
appendMessagesError(tr("Could not reset sampler instance.\n\nSorry."));
1262
appendMessages(tr("Sampler reset."));
1264
// Make it a new session...
1269
// Restart the client/server instance.
1270
void MainForm::fileRestart (void)
1272
if (m_pOptions == NULL)
1275
bool bRestart = true;
1277
// Ask user whether he/she want's a complete restart...
1278
// (if we're currently up and running)
1279
if (bRestart && m_pClient) {
1280
bRestart = (QMessageBox::warning(this,
1281
QSAMPLER_TITLE ": " + tr("Warning"),
1282
tr("New settings will be effective after\n"
1283
"restarting the client/server connection.\n\n"
1284
"Please note that this operation may cause\n"
1285
"temporary MIDI and Audio disruption.\n\n"
1286
"Do you want to restart the connection now?"),
1287
tr("Restart"), tr("Cancel")) == 0);
1290
// Are we still for it?
1291
if (bRestart && closeSession(true)) {
1292
// Stop server, it will force the client too.
1294
// Reschedule a restart...
1295
startSchedule(m_pOptions->iStartDelay);
1300
// Exit application program.
1301
void MainForm::fileExit (void)
1303
// Go for close the whole thing.
1308
//-------------------------------------------------------------------------
1309
// qsamplerMainForm -- Edit Action slots.
1311
// Add a new sampler channel.
1312
void MainForm::editAddChannel (void)
1314
if (m_pClient == NULL)
1317
// Just create the channel instance...
1318
Channel *pChannel = new Channel();
1319
if (pChannel == NULL)
1322
// Before we show it up, may be we'll
1323
// better ask for some initial values?
1324
if (!pChannel->channelSetup(this)) {
1329
// And give it to the strip...
1330
// (will own the channel instance, if successful).
1331
if (!createChannelStrip(pChannel)) {
1336
// Do we auto-arrange?
1337
if (m_pOptions && m_pOptions->bAutoArrange)
1340
// Make that an overall update.
1346
// Remove current sampler channel.
1347
void MainForm::editRemoveChannel (void)
1349
if (m_pClient == NULL)
1352
ChannelStrip* pChannelStrip = activeChannelStrip();
1353
if (pChannelStrip == NULL)
1356
Channel *pChannel = pChannelStrip->channel();
1357
if (pChannel == NULL)
1360
// Prompt user if he/she's sure about this...
1361
if (m_pOptions && m_pOptions->bConfirmRemove) {
1362
if (QMessageBox::warning(this,
1363
QSAMPLER_TITLE ": " + tr("Warning"),
1364
tr("About to remove channel:\n\n"
1367
.arg(pChannelStrip->windowTitle()),
1368
tr("OK"), tr("Cancel")) > 0)
1372
// Remove the existing sampler channel.
1373
if (!pChannel->removeChannel())
1376
// Just delete the channel strip.
1377
delete pChannelStrip;
1379
// Do we auto-arrange?
1380
if (m_pOptions && m_pOptions->bAutoArrange)
1383
// We'll be dirty, for sure...
1389
// Setup current sampler channel.
1390
void MainForm::editSetupChannel (void)
1392
if (m_pClient == NULL)
1395
ChannelStrip* pChannelStrip = activeChannelStrip();
1396
if (pChannelStrip == NULL)
1399
// Just invoque the channel strip procedure.
1400
pChannelStrip->channelSetup();
1404
// Edit current sampler channel.
1405
void MainForm::editEditChannel (void)
1407
if (m_pClient == NULL)
1410
ChannelStrip* pChannelStrip = activeChannelStrip();
1411
if (pChannelStrip == NULL)
1414
// Just invoque the channel strip procedure.
1415
pChannelStrip->channelEdit();
1419
// Reset current sampler channel.
1420
void MainForm::editResetChannel (void)
1422
if (m_pClient == NULL)
1425
ChannelStrip* pChannelStrip = activeChannelStrip();
1426
if (pChannelStrip == NULL)
1429
// Just invoque the channel strip procedure.
1430
pChannelStrip->channelReset();
1434
// Reset all sampler channels.
1435
void MainForm::editResetAllChannels (void)
1437
if (m_pClient == NULL)
1440
// Invoque the channel strip procedure,
1441
// for all channels out there...
1442
m_pWorkspace->setUpdatesEnabled(false);
1443
QWidgetList wlist = m_pWorkspace->windowList();
1444
for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1445
ChannelStrip* pChannelStrip = (ChannelStrip*) wlist.at(iChannel);
1447
pChannelStrip->channelReset();
1449
m_pWorkspace->setUpdatesEnabled(true);
1453
//-------------------------------------------------------------------------
1454
// qsamplerMainForm -- View Action slots.
1456
// Show/hide the main program window menubar.
1457
void MainForm::viewMenubar ( bool bOn )
1460
m_ui.MenuBar->show();
1462
m_ui.MenuBar->hide();
1466
// Show/hide the main program window toolbar.
1467
void MainForm::viewToolbar ( bool bOn )
1470
m_ui.fileToolbar->show();
1471
m_ui.editToolbar->show();
1472
m_ui.channelsToolbar->show();
1474
m_ui.fileToolbar->hide();
1475
m_ui.editToolbar->hide();
1476
m_ui.channelsToolbar->hide();
1481
// Show/hide the main program window statusbar.
1482
void MainForm::viewStatusbar ( bool bOn )
1485
statusBar()->show();
1487
statusBar()->hide();
1491
// Show/hide the messages window logger.
1492
void MainForm::viewMessages ( bool bOn )
1495
m_pMessages->show();
1497
m_pMessages->hide();
1501
// Show/hide the MIDI instrument list-view form.
1502
void MainForm::viewInstruments (void)
1504
if (m_pOptions == NULL)
1507
if (m_pInstrumentListForm) {
1508
m_pOptions->saveWidgetGeometry(m_pInstrumentListForm);
1509
if (m_pInstrumentListForm->isVisible()) {
1510
m_pInstrumentListForm->hide();
1512
m_pInstrumentListForm->show();
1513
m_pInstrumentListForm->raise();
1514
m_pInstrumentListForm->activateWindow();
1520
// Show/hide the device configurator form.
1521
void MainForm::viewDevices (void)
1523
if (m_pOptions == NULL)
1526
if (m_pDeviceForm) {
1527
m_pOptions->saveWidgetGeometry(m_pDeviceForm);
1528
if (m_pDeviceForm->isVisible()) {
1529
m_pDeviceForm->hide();
1531
m_pDeviceForm->show();
1532
m_pDeviceForm->raise();
1533
m_pDeviceForm->activateWindow();
1539
// Show options dialog.
1540
void MainForm::viewOptions (void)
1542
if (m_pOptions == NULL)
1545
OptionsForm* pOptionsForm = new OptionsForm(this);
1547
// Check out some initial nullities(tm)...
1548
ChannelStrip* pChannelStrip = activeChannelStrip();
1549
if (m_pOptions->sDisplayFont.isEmpty() && pChannelStrip)
1550
m_pOptions->sDisplayFont = pChannelStrip->displayFont().toString();
1551
if (m_pOptions->sMessagesFont.isEmpty() && m_pMessages)
1552
m_pOptions->sMessagesFont = m_pMessages->messagesFont().toString();
1553
// To track down deferred or immediate changes.
1554
QString sOldServerHost = m_pOptions->sServerHost;
1555
int iOldServerPort = m_pOptions->iServerPort;
1556
int iOldServerTimeout = m_pOptions->iServerTimeout;
1557
bool bOldServerStart = m_pOptions->bServerStart;
1558
QString sOldServerCmdLine = m_pOptions->sServerCmdLine;
1559
QString sOldDisplayFont = m_pOptions->sDisplayFont;
1560
bool bOldDisplayEffect = m_pOptions->bDisplayEffect;
1561
int iOldMaxVolume = m_pOptions->iMaxVolume;
1562
QString sOldMessagesFont = m_pOptions->sMessagesFont;
1563
bool bOldKeepOnTop = m_pOptions->bKeepOnTop;
1564
bool bOldStdoutCapture = m_pOptions->bStdoutCapture;
1565
int bOldMessagesLimit = m_pOptions->bMessagesLimit;
1566
int iOldMessagesLimitLines = m_pOptions->iMessagesLimitLines;
1567
bool bOldCompletePath = m_pOptions->bCompletePath;
1568
bool bOldInstrumentNames = m_pOptions->bInstrumentNames;
1569
int iOldMaxRecentFiles = m_pOptions->iMaxRecentFiles;
1570
// Load the current setup settings.
1571
pOptionsForm->setup(m_pOptions);
1572
// Show the setup dialog...
1573
if (pOptionsForm->exec()) {
1574
// Warn if something will be only effective on next run.
1575
if (( bOldStdoutCapture && !m_pOptions->bStdoutCapture) ||
1576
(!bOldStdoutCapture && m_pOptions->bStdoutCapture) ||
1577
( bOldKeepOnTop && !m_pOptions->bKeepOnTop) ||
1578
(!bOldKeepOnTop && m_pOptions->bKeepOnTop)) {
1579
QMessageBox::information(this,
1580
QSAMPLER_TITLE ": " + tr("Information"),
1581
tr("Some settings may be only effective\n"
1582
"next time you start this program."), tr("OK"));
1583
updateMessagesCapture();
1585
// Check wheather something immediate has changed.
1586
if (( bOldCompletePath && !m_pOptions->bCompletePath) ||
1587
(!bOldCompletePath && m_pOptions->bCompletePath) ||
1588
(iOldMaxRecentFiles != m_pOptions->iMaxRecentFiles))
1589
updateRecentFilesMenu();
1590
if (( bOldInstrumentNames && !m_pOptions->bInstrumentNames) ||
1591
(!bOldInstrumentNames && m_pOptions->bInstrumentNames))
1592
updateInstrumentNames();
1593
if (( bOldDisplayEffect && !m_pOptions->bDisplayEffect) ||
1594
(!bOldDisplayEffect && m_pOptions->bDisplayEffect))
1595
updateDisplayEffect();
1596
if (sOldDisplayFont != m_pOptions->sDisplayFont)
1597
updateDisplayFont();
1598
if (iOldMaxVolume != m_pOptions->iMaxVolume)
1600
if (sOldMessagesFont != m_pOptions->sMessagesFont)
1601
updateMessagesFont();
1602
if (( bOldMessagesLimit && !m_pOptions->bMessagesLimit) ||
1603
(!bOldMessagesLimit && m_pOptions->bMessagesLimit) ||
1604
(iOldMessagesLimitLines != m_pOptions->iMessagesLimitLines))
1605
updateMessagesLimit();
1606
// And now the main thing, whether we'll do client/server recycling?
1607
if ((sOldServerHost != m_pOptions->sServerHost) ||
1608
(iOldServerPort != m_pOptions->iServerPort) ||
1609
(iOldServerTimeout != m_pOptions->iServerTimeout) ||
1610
( bOldServerStart && !m_pOptions->bServerStart) ||
1611
(!bOldServerStart && m_pOptions->bServerStart) ||
1612
(sOldServerCmdLine != m_pOptions->sServerCmdLine
1613
&& m_pOptions->bServerStart))
1617
delete pOptionsForm;
1625
//-------------------------------------------------------------------------
1626
// qsamplerMainForm -- Channels action slots.
1628
// Arrange channel strips.
1629
void MainForm::channelsArrange (void)
1631
// Full width vertical tiling
1632
QWidgetList wlist = m_pWorkspace->windowList();
1633
if (wlist.isEmpty())
1636
m_pWorkspace->setUpdatesEnabled(false);
1638
for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1639
ChannelStrip* pChannelStrip = (ChannelStrip*) wlist.at(iChannel);
1640
/* if (pChannelStrip->testWState(WState_Maximized | WState_Minimized)) {
1641
// Prevent flicker...
1642
pChannelStrip->hide();
1643
pChannelStrip->showNormal();
1645
pChannelStrip->adjustSize();
1646
int iWidth = m_pWorkspace->width();
1647
if (iWidth < pChannelStrip->width())
1648
iWidth = pChannelStrip->width();
1649
// int iHeight = pChannelStrip->height()
1650
// + pChannelStrip->parentWidget()->baseSize().height();
1651
int iHeight = pChannelStrip->parentWidget()->frameGeometry().height();
1652
pChannelStrip->parentWidget()->setGeometry(0, y, iWidth, iHeight);
1655
m_pWorkspace->setUpdatesEnabled(true);
1661
// Auto-arrange channel strips.
1662
void MainForm::channelsAutoArrange ( bool bOn )
1664
if (m_pOptions == NULL)
1667
// Toggle the auto-arrange flag.
1668
m_pOptions->bAutoArrange = bOn;
1670
// If on, update whole workspace...
1671
if (m_pOptions->bAutoArrange)
1676
//-------------------------------------------------------------------------
1677
// qsamplerMainForm -- Help Action slots.
1679
// Show information about the Qt toolkit.
1680
void MainForm::helpAboutQt (void)
1682
QMessageBox::aboutQt(this);
1686
// Show information about application program.
1687
void MainForm::helpAbout (void)
1689
// Stuff the about box text...
1690
QString sText = "<p>\n";
1691
sText += "<b>" QSAMPLER_TITLE " - " + tr(QSAMPLER_SUBTITLE) + "</b><br />\n";
1692
sText += "<br />\n";
1693
sText += tr("Version") + ": <b>" QSAMPLER_VERSION "</b><br />\n";
1694
sText += "<small>" + tr("Build") + ": " __DATE__ " " __TIME__ "</small><br />\n";
1696
sText += "<small><font color=\"red\">";
1697
sText += tr("Debugging option enabled.");
1698
sText += "</font></small><br />";
1700
#ifndef CONFIG_LIBGIG
1701
sText += "<small><font color=\"red\">";
1702
sText += tr("GIG (libgig) file support disabled.");
1703
sText += "</font></small><br />";
1705
#ifndef CONFIG_INSTRUMENT_NAME
1706
sText += "<small><font color=\"red\">";
1707
sText += tr("LSCP (liblscp) instrument_name support disabled.");
1708
sText += "</font></small><br />";
1710
#ifndef CONFIG_MUTE_SOLO
1711
sText += "<small><font color=\"red\">";
1712
sText += tr("Sampler channel Mute/Solo support disabled.");
1713
sText += "</font></small><br />";
1715
#ifndef CONFIG_AUDIO_ROUTING
1716
sText += "<small><font color=\"red\">";
1717
sText += tr("LSCP (liblscp) audio_routing support disabled.");
1718
sText += "</font></small><br />";
1720
#ifndef CONFIG_FXSEND
1721
sText += "<small><font color=\"red\">";
1722
sText += tr("Sampler channel Effect Sends support disabled.");
1723
sText += "</font></small><br />";
1725
#ifndef CONFIG_VOLUME
1726
sText += "<small><font color=\"red\">";
1727
sText += tr("Global volume support disabled.");
1728
sText += "</font></small><br />";
1730
#ifndef CONFIG_MIDI_INSTRUMENT
1731
sText += "<small><font color=\"red\">";
1732
sText += tr("MIDI instrument mapping support disabled.");
1733
sText += "</font></small><br />";
1735
#ifndef CONFIG_EDIT_INSTRUMENT
1736
sText += "<small><font color=\"red\">";
1737
sText += tr("Instrument editing support disabled.");
1738
sText += "</font></small><br />";
1740
sText += "<br />\n";
1741
sText += tr("Using") + ": ";
1742
sText += ::lscp_client_package();
1744
sText += ::lscp_client_version();
1745
#ifdef CONFIG_LIBGIG
1747
sText += gig::libraryName().c_str();
1749
sText += gig::libraryVersion().c_str();
1751
sText += "<br />\n";
1752
sText += "<br />\n";
1753
sText += tr("Website") + ": <a href=\"" QSAMPLER_WEBSITE "\">" QSAMPLER_WEBSITE "</a><br />\n";
1754
sText += "<br />\n";
1756
sText += QSAMPLER_COPYRIGHT "<br />\n";
1757
sText += QSAMPLER_COPYRIGHT2 "<br />\n";
1758
sText += "<br />\n";
1759
sText += tr("This program is free software; you can redistribute it and/or modify it") + "<br />\n";
1760
sText += tr("under the terms of the GNU General Public License version 2 or later.");
1761
sText += "</small>";
1764
QMessageBox::about(this, tr("About") + " " QSAMPLER_TITLE, sText);
1768
//-------------------------------------------------------------------------
1769
// qsamplerMainForm -- Main window stabilization.
1771
void MainForm::stabilizeForm (void)
1773
// Update the main application caption...
1774
QString sSessionName = sessionName(m_sFilename);
1775
if (m_iDirtyCount > 0)
1776
sSessionName += " *";
1777
setWindowTitle(tr(QSAMPLER_TITLE " - [%1]").arg(sSessionName));
1779
// Update the main menu state...
1780
ChannelStrip* pChannelStrip = activeChannelStrip();
1781
bool bHasClient = (m_pOptions != NULL && m_pClient != NULL);
1782
bool bHasChannel = (bHasClient && pChannelStrip != NULL);
1783
m_ui.fileNewAction->setEnabled(bHasClient);
1784
m_ui.fileOpenAction->setEnabled(bHasClient);
1785
m_ui.fileSaveAction->setEnabled(bHasClient && m_iDirtyCount > 0);
1786
m_ui.fileSaveAsAction->setEnabled(bHasClient);
1787
m_ui.fileResetAction->setEnabled(bHasClient);
1788
m_ui.fileRestartAction->setEnabled(bHasClient || m_pServer == NULL);
1789
m_ui.editAddChannelAction->setEnabled(bHasClient);
1790
m_ui.editRemoveChannelAction->setEnabled(bHasChannel);
1791
m_ui.editSetupChannelAction->setEnabled(bHasChannel);
1792
#ifdef CONFIG_EDIT_INSTRUMENT
1793
m_ui.editEditChannelAction->setEnabled(bHasChannel);
1795
m_ui.editEditChannelAction->setEnabled(false);
1797
m_ui.editResetChannelAction->setEnabled(bHasChannel);
1798
m_ui.editResetAllChannelsAction->setEnabled(bHasChannel);
1799
m_ui.viewMessagesAction->setChecked(m_pMessages && m_pMessages->isVisible());
1800
#ifdef CONFIG_MIDI_INSTRUMENT
1801
m_ui.viewInstrumentsAction->setChecked(m_pInstrumentListForm
1802
&& m_pInstrumentListForm->isVisible());
1803
m_ui.viewInstrumentsAction->setEnabled(bHasClient);
1805
m_ui.viewInstrumentsAction->setEnabled(false);
1807
m_ui.viewDevicesAction->setChecked(m_pDeviceForm
1808
&& m_pDeviceForm->isVisible());
1809
m_ui.viewDevicesAction->setEnabled(bHasClient);
1810
m_ui.channelsArrangeAction->setEnabled(bHasChannel);
1812
#ifdef CONFIG_VOLUME
1813
// Toolbar widgets are also affected...
1814
m_pVolumeSlider->setEnabled(bHasClient);
1815
m_pVolumeSpinBox->setEnabled(bHasClient);
1818
// Client/Server status...
1820
m_statusItem[QSAMPLER_STATUS_CLIENT]->setText(tr("Connected"));
1821
m_statusItem[QSAMPLER_STATUS_SERVER]->setText(m_pOptions->sServerHost
1822
+ ':' + QString::number(m_pOptions->iServerPort));
1824
m_statusItem[QSAMPLER_STATUS_CLIENT]->clear();
1825
m_statusItem[QSAMPLER_STATUS_SERVER]->clear();
1827
// Channel status...
1829
m_statusItem[QSAMPLER_STATUS_CHANNEL]->setText(pChannelStrip->windowTitle());
1831
m_statusItem[QSAMPLER_STATUS_CHANNEL]->clear();
1832
// Session status...
1833
if (m_iDirtyCount > 0)
1834
m_statusItem[QSAMPLER_STATUS_SESSION]->setText(tr("MOD"));
1836
m_statusItem[QSAMPLER_STATUS_SESSION]->clear();
1838
// Recent files menu.
1839
m_ui.fileOpenRecentMenu->setEnabled(m_pOptions->recentFiles.count() > 0);
1843
// Global volume change receiver slot.
1844
void MainForm::volumeChanged ( int iVolume )
1846
#ifdef CONFIG_VOLUME
1848
if (m_iVolumeChanging > 0)
1851
m_iVolumeChanging++;
1853
// Update the toolbar widgets...
1854
if (m_pVolumeSlider->value() != iVolume)
1855
m_pVolumeSlider->setValue(iVolume);
1856
if (m_pVolumeSpinBox->value() != iVolume)
1857
m_pVolumeSpinBox->setValue(iVolume);
1859
// Do it as commanded...
1860
float fVolume = 0.01f * float(iVolume);
1861
if (::lscp_set_volume(m_pClient, fVolume) == LSCP_OK)
1862
appendMessages(QObject::tr("Volume: %1.").arg(fVolume));
1864
appendMessagesClient("lscp_set_volume");
1866
m_iVolumeChanging--;
1875
// Channel change receiver slot.
1876
void MainForm::channelStripChanged(ChannelStrip* pChannelStrip)
1878
// Add this strip to the changed list...
1879
if (!m_changedStrips.contains(pChannelStrip)) {
1880
m_changedStrips.append(pChannelStrip);
1881
pChannelStrip->resetErrorCount();
1884
// Just mark the dirty form.
1886
// and update the form status...
1891
// Grab and restore current sampler channels session.
1892
void MainForm::updateSession (void)
1894
#ifdef CONFIG_VOLUME
1895
int iVolume = ::lroundf(100.0f * ::lscp_get_volume(m_pClient));
1896
m_iVolumeChanging++;
1897
m_pVolumeSlider->setValue(iVolume);
1898
m_pVolumeSpinBox->setValue(iVolume);
1899
m_iVolumeChanging--;
1901
#ifdef CONFIG_MIDI_INSTRUMENT
1902
// FIXME: Make some room for default instrument maps...
1903
int iMaps = ::lscp_get_midi_instrument_maps(m_pClient);
1905
appendMessagesClient("lscp_get_midi_instrument_maps");
1906
else if (iMaps < 1) {
1907
::lscp_add_midi_instrument_map(m_pClient,
1908
tr("Chromatic").toUtf8().constData());
1909
::lscp_add_midi_instrument_map(m_pClient,
1910
tr("Drum Kits").toUtf8().constData());
1914
// Retrieve the current channel list.
1915
int *piChannelIDs = ::lscp_list_channels(m_pClient);
1916
if (piChannelIDs == NULL) {
1917
if (::lscp_client_get_errno(m_pClient)) {
1918
appendMessagesClient("lscp_list_channels");
1919
appendMessagesError(
1920
tr("Could not get current list of channels.\n\nSorry."));
1923
// Try to (re)create each channel.
1924
m_pWorkspace->setUpdatesEnabled(false);
1925
for (int iChannel = 0; piChannelIDs[iChannel] >= 0; iChannel++) {
1926
// Check if theres already a channel strip for this one...
1927
if (!channelStrip(piChannelIDs[iChannel]))
1928
createChannelStrip(new Channel(piChannelIDs[iChannel]));
1930
m_pWorkspace->setUpdatesEnabled(true);
1933
// Do we auto-arrange?
1934
if (m_pOptions && m_pOptions->bAutoArrange)
1937
// Remember to refresh devices and instruments...
1938
if (m_pInstrumentListForm)
1939
m_pInstrumentListForm->refreshInstruments();
1941
m_pDeviceForm->refreshDevices();
1945
// Update the recent files list and menu.
1946
void MainForm::updateRecentFiles ( const QString& sFilename )
1948
if (m_pOptions == NULL)
1951
// Remove from list if already there (avoid duplicates)
1952
int iIndex = m_pOptions->recentFiles.indexOf(sFilename);
1954
m_pOptions->recentFiles.removeAt(iIndex);
1955
// Put it to front...
1956
m_pOptions->recentFiles.push_front(sFilename);
1960
// Update the recent files list and menu.
1961
void MainForm::updateRecentFilesMenu (void)
1963
if (m_pOptions == NULL)
1966
// Time to keep the list under limits.
1967
int iRecentFiles = m_pOptions->recentFiles.count();
1968
while (iRecentFiles > m_pOptions->iMaxRecentFiles) {
1969
m_pOptions->recentFiles.pop_back();
1973
// Rebuild the recent files menu...
1974
m_ui.fileOpenRecentMenu->clear();
1975
for (int i = 0; i < iRecentFiles; i++) {
1976
const QString& sFilename = m_pOptions->recentFiles[i];
1977
if (QFileInfo(sFilename).exists()) {
1978
QAction *pAction = m_ui.fileOpenRecentMenu->addAction(
1979
QString("&%1 %2").arg(i + 1).arg(sessionName(sFilename)),
1980
this, SLOT(fileOpenRecent()));
1981
pAction->setData(i);
1987
// Force update of the channels instrument names mode.
1988
void MainForm::updateInstrumentNames (void)
1990
// Full channel list update...
1991
QWidgetList wlist = m_pWorkspace->windowList();
1992
if (wlist.isEmpty())
1995
m_pWorkspace->setUpdatesEnabled(false);
1996
for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1997
ChannelStrip *pChannelStrip = (ChannelStrip *) wlist.at(iChannel);
1999
pChannelStrip->updateInstrumentName(true);
2001
m_pWorkspace->setUpdatesEnabled(true);
2005
// Force update of the channels display font.
2006
void MainForm::updateDisplayFont (void)
2008
if (m_pOptions == NULL)
2011
// Check if display font is legal.
2012
if (m_pOptions->sDisplayFont.isEmpty())
2016
if (!font.fromString(m_pOptions->sDisplayFont))
2019
// Full channel list update...
2020
QWidgetList wlist = m_pWorkspace->windowList();
2021
if (wlist.isEmpty())
2024
m_pWorkspace->setUpdatesEnabled(false);
2025
for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
2026
ChannelStrip* pChannelStrip = (ChannelStrip*) wlist.at(iChannel);
2028
pChannelStrip->setDisplayFont(font);
2030
m_pWorkspace->setUpdatesEnabled(true);
2034
// Update channel strips background effect.
2035
void MainForm::updateDisplayEffect (void)
2037
// Full channel list update...
2038
QWidgetList wlist = m_pWorkspace->windowList();
2039
if (wlist.isEmpty())
2042
m_pWorkspace->setUpdatesEnabled(false);
2043
for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
2044
ChannelStrip* pChannelStrip = (ChannelStrip*) wlist.at(iChannel);
2046
pChannelStrip->setDisplayEffect(m_pOptions->bDisplayEffect);
2048
m_pWorkspace->setUpdatesEnabled(true);
2052
// Force update of the channels maximum volume setting.
2053
void MainForm::updateMaxVolume (void)
2055
if (m_pOptions == NULL)
2058
#ifdef CONFIG_VOLUME
2059
m_iVolumeChanging++;
2060
m_pVolumeSlider->setMaximum(m_pOptions->iMaxVolume);
2061
m_pVolumeSpinBox->setMaximum(m_pOptions->iMaxVolume);
2062
m_iVolumeChanging--;
2065
// Full channel list update...
2066
QWidgetList wlist = m_pWorkspace->windowList();
2067
if (wlist.isEmpty())
2070
m_pWorkspace->setUpdatesEnabled(false);
2071
for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
2072
ChannelStrip* pChannelStrip = (ChannelStrip*) wlist.at(iChannel);
2074
pChannelStrip->setMaxVolume(m_pOptions->iMaxVolume);
2076
m_pWorkspace->setUpdatesEnabled(true);
2080
//-------------------------------------------------------------------------
2081
// qsamplerMainForm -- Messages window form handlers.
2083
// Messages output methods.
2084
void MainForm::appendMessages( const QString& s )
2087
m_pMessages->appendMessages(s);
2089
statusBar()->showMessage(s, 3000);
2092
void MainForm::appendMessagesColor( const QString& s, const QString& c )
2095
m_pMessages->appendMessagesColor(s, c);
2097
statusBar()->showMessage(s, 3000);
2100
void MainForm::appendMessagesText( const QString& s )
2103
m_pMessages->appendMessagesText(s);
2106
void MainForm::appendMessagesError( const QString& s )
2109
m_pMessages->show();
2111
appendMessagesColor(s.simplified(), "#ff0000");
2113
// Make it look responsive...:)
2114
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
2116
QMessageBox::critical(this,
2117
QSAMPLER_TITLE ": " + tr("Error"), s, tr("Cancel"));
2121
// This is a special message format, just for client results.
2122
void MainForm::appendMessagesClient( const QString& s )
2124
if (m_pClient == NULL)
2127
appendMessagesColor(s + QString(": %1 (errno=%2)")
2128
.arg(::lscp_client_get_result(m_pClient))
2129
.arg(::lscp_client_get_errno(m_pClient)), "#996666");
2131
// Make it look responsive...:)
2132
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
2136
// Force update of the messages font.
2137
void MainForm::updateMessagesFont (void)
2139
if (m_pOptions == NULL)
2142
if (m_pMessages && !m_pOptions->sMessagesFont.isEmpty()) {
2144
if (font.fromString(m_pOptions->sMessagesFont))
2145
m_pMessages->setMessagesFont(font);
2150
// Update messages window line limit.
2151
void MainForm::updateMessagesLimit (void)
2153
if (m_pOptions == NULL)
2157
if (m_pOptions->bMessagesLimit)
2158
m_pMessages->setMessagesLimit(m_pOptions->iMessagesLimitLines);
2160
m_pMessages->setMessagesLimit(-1);
2165
// Enablement of the messages capture feature.
2166
void MainForm::updateMessagesCapture (void)
2168
if (m_pOptions == NULL)
2172
m_pMessages->setCaptureEnabled(m_pOptions->bStdoutCapture);
2176
//-------------------------------------------------------------------------
2177
// qsamplerMainForm -- MDI channel strip management.
2179
// The channel strip creation executive.
2180
ChannelStrip* MainForm::createChannelStrip ( Channel *pChannel )
2182
if (m_pClient == NULL || pChannel == NULL)
2185
// Add a new channel itema...
2186
ChannelStrip *pChannelStrip = new ChannelStrip();
2187
if (pChannelStrip == NULL)
2190
// Set some initial channel strip options...
2192
// Background display effect...
2193
pChannelStrip->setDisplayEffect(m_pOptions->bDisplayEffect);
2194
// We'll need a display font.
2196
if (font.fromString(m_pOptions->sDisplayFont))
2197
pChannelStrip->setDisplayFont(font);
2198
// Maximum allowed volume setting.
2199
pChannelStrip->setMaxVolume(m_pOptions->iMaxVolume);
2202
// Add it to workspace...
2203
m_pWorkspace->addWindow(pChannelStrip, Qt::FramelessWindowHint);
2205
// Actual channel strip setup...
2206
pChannelStrip->setup(pChannel);
2208
QObject::connect(pChannelStrip,
2209
SIGNAL(channelChanged(ChannelStrip*)),
2210
SLOT(channelStripChanged(ChannelStrip*)));
2212
// Now we show up us to the world.
2213
pChannelStrip->show();
2215
// This is pretty new, so we'll watch for it closely.
2216
channelStripChanged(pChannelStrip);
2218
// Return our successful reference...
2219
return pChannelStrip;
2223
// Retrieve the active channel strip.
2224
ChannelStrip* MainForm::activeChannelStrip (void)
2226
return static_cast<ChannelStrip *> (m_pWorkspace->activeWindow());
2230
// Retrieve a channel strip by index.
2231
ChannelStrip* MainForm::channelStripAt ( int iChannel )
2233
QWidgetList wlist = m_pWorkspace->windowList();
2234
if (wlist.isEmpty())
2237
return static_cast<ChannelStrip *> (wlist.at(iChannel));
2241
// Retrieve a channel strip by sampler channel id.
2242
ChannelStrip* MainForm::channelStrip ( int iChannelID )
2244
QWidgetList wlist = m_pWorkspace->windowList();
2245
if (wlist.isEmpty())
2248
for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
2249
ChannelStrip* pChannelStrip
2250
= static_cast<ChannelStrip*> (wlist.at(iChannel));
2251
if (pChannelStrip) {
2252
Channel *pChannel = pChannelStrip->channel();
2253
if (pChannel && pChannel->channelID() == iChannelID)
2254
return pChannelStrip;
2263
// Construct the windows menu.
2264
void MainForm::channelsMenuAboutToShow (void)
2266
m_ui.channelsMenu->clear();
2267
m_ui.channelsMenu->addAction(m_ui.channelsArrangeAction);
2268
m_ui.channelsMenu->addAction(m_ui.channelsAutoArrangeAction);
2270
QWidgetList wlist = m_pWorkspace->windowList();
2271
if (!wlist.isEmpty()) {
2272
m_ui.channelsMenu->addSeparator();
2273
for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
2274
ChannelStrip* pChannelStrip
2275
= static_cast<ChannelStrip*> (wlist.at(iChannel));
2276
if (pChannelStrip) {
2277
QAction *pAction = m_ui.channelsMenu->addAction(
2278
pChannelStrip->windowTitle(),
2279
this, SLOT(channelsMenuActivated()));
2280
pAction->setCheckable(true);
2281
pAction->setChecked(activeChannelStrip() == pChannelStrip);
2282
pAction->setData(iChannel);
2289
// Windows menu activation slot
2290
void MainForm::channelsMenuActivated (void)
2292
// Retrive channel index from action data...
2293
QAction *pAction = qobject_cast<QAction *> (sender());
2294
if (pAction == NULL)
2297
ChannelStrip* pChannelStrip = channelStripAt(pAction->data().toInt());
2298
if (pChannelStrip) {
2299
pChannelStrip->showNormal();
2300
pChannelStrip->setFocus();
2305
//-------------------------------------------------------------------------
2306
// qsamplerMainForm -- Timer stuff.
2308
// Set the pseudo-timer delay schedule.
2309
void MainForm::startSchedule ( int iStartDelay )
2311
m_iStartDelay = 1 + (iStartDelay * 1000);
2315
// Suspend the pseudo-timer delay schedule.
2316
void MainForm::stopSchedule (void)
2322
// Timer slot funtion.
2323
void MainForm::timerSlot (void)
2325
if (m_pOptions == NULL)
2328
// Is it the first shot on server start after a few delay?
2329
if (m_iTimerDelay < m_iStartDelay) {
2330
m_iTimerDelay += QSAMPLER_TIMER_MSECS;
2331
if (m_iTimerDelay >= m_iStartDelay) {
2332
// If we cannot start it now, maybe a lil'mo'later ;)
2333
if (!startClient()) {
2334
m_iStartDelay += m_iTimerDelay;
2341
// Update the channel information for each pending strip...
2342
QListIterator<ChannelStrip *> iter(m_changedStrips);
2343
while (iter.hasNext()) {
2344
ChannelStrip *pChannelStrip = iter.next();
2345
// If successfull, remove from pending list...
2346
if (pChannelStrip->updateChannelInfo()) {
2347
int iChannelStrip = m_changedStrips.indexOf(pChannelStrip);
2348
if (iChannelStrip >= 0)
2349
m_changedStrips.removeAt(iChannelStrip);
2352
// Refresh each channel usage, on each period...
2353
if (m_pOptions->bAutoRefresh) {
2354
m_iTimerSlot += QSAMPLER_TIMER_MSECS;
2355
if (m_iTimerSlot >= m_pOptions->iAutoRefreshTime) {
2357
// Update the channel stream usage for each strip...
2358
QWidgetList wlist = m_pWorkspace->windowList();
2359
for (int iChannel = 0;
2360
iChannel < (int) wlist.count(); iChannel++) {
2361
ChannelStrip* pChannelStrip
2362
= (ChannelStrip*) wlist.at(iChannel);
2363
if (pChannelStrip && pChannelStrip->isVisible())
2364
pChannelStrip->updateChannelUsage();
2370
// Register the next timer slot.
2371
QTimer::singleShot(QSAMPLER_TIMER_MSECS, this, SLOT(timerSlot()));
2375
//-------------------------------------------------------------------------
2376
// qsamplerMainForm -- Server stuff.
2378
// Start linuxsampler server...
2379
void MainForm::startServer (void)
2381
if (m_pOptions == NULL)
2384
// Aren't already a client, are we?
2385
if (!m_pOptions->bServerStart || m_pClient)
2388
// Is the server process instance still here?
2390
switch (QMessageBox::warning(this,
2391
QSAMPLER_TITLE ": " + tr("Warning"),
2392
tr("Could not start the LinuxSampler server.\n\n"
2393
"Maybe it ss already started."),
2394
tr("Stop"), tr("Kill"), tr("Cancel"))) {
2396
m_pServer->terminate();
2405
// Reset our timer counters...
2408
// Verify we have something to start with...
2409
if (m_pOptions->sServerCmdLine.isEmpty())
2412
// OK. Let's build the startup process...
2413
m_pServer = new QProcess(this);
2415
// Setup stdout/stderr capture...
2416
// if (m_pOptions->bStdoutCapture) {
2417
#if QT_VERSION >= 0x040200
2418
m_pServer->setProcessChannelMode(QProcess::ForwardedChannels);
2420
QObject::connect(m_pServer,
2421
SIGNAL(readyReadStandardOutput()),
2422
SLOT(readServerStdout()));
2423
QObject::connect(m_pServer,
2424
SIGNAL(readyReadStandardError()),
2425
SLOT(readServerStdout()));
2428
// The unforgiveable signal communication...
2429
QObject::connect(m_pServer,
2430
SIGNAL(finished(int, QProcess::ExitStatus)),
2431
SLOT(processServerExit()));
2433
// Build process arguments...
2434
QStringList args = m_pOptions->sServerCmdLine.split(' ');
2435
QString sCommand = args[0];
2438
appendMessages(tr("Server is starting..."));
2439
appendMessagesColor(m_pOptions->sServerCmdLine, "#990099");
2441
// Go linuxsampler, go...
2442
m_pServer->start(sCommand, args);
2443
if (!m_pServer->waitForStarted()) {
2444
appendMessagesError(tr("Could not start server.\n\nSorry."));
2445
processServerExit();
2449
// Show startup results...
2451
tr("Server was started with PID=%1.").arg((long) m_pServer->pid()));
2453
// Reset (yet again) the timer counters,
2454
// but this time is deferred as the user opted.
2455
startSchedule(m_pOptions->iStartDelay);
2460
// Stop linuxsampler server...
2461
void MainForm::stopServer (void)
2463
// Stop client code.
2466
// And try to stop server.
2468
appendMessages(tr("Server is stopping..."));
2469
if (m_pServer->state() == QProcess::Running) {
2475
m_pServer->terminate();
2478
} // Do final processing anyway.
2479
else processServerExit();
2481
// Give it some time to terminate gracefully and stabilize...
2484
while (t.elapsed() < QSAMPLER_TIMER_MSECS)
2485
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
2489
// Stdout handler...
2490
void MainForm::readServerStdout (void)
2493
m_pMessages->appendStdoutBuffer(m_pServer->readAllStandardOutput());
2497
// Linuxsampler server cleanup.
2498
void MainForm::processServerExit (void)
2500
// Force client code cleanup.
2503
// Flush anything that maybe pending...
2505
m_pMessages->flushStdoutBuffer();
2508
if (m_pServer->state() != QProcess::NotRunning) {
2509
appendMessages(tr("Server is being forced..."));
2510
// Force final server shutdown...
2512
// Give it some time to terminate gracefully and stabilize...
2515
while (t.elapsed() < QSAMPLER_TIMER_MSECS)
2516
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
2518
// Force final server shutdown...
2520
tr("Server was stopped with exit status %1.")
2521
.arg(m_pServer->exitStatus()));
2526
// Again, make status visible stable.
2531
//-------------------------------------------------------------------------
2532
// qsamplerMainForm -- Client stuff.
2534
// The LSCP client callback procedure.
2535
lscp_status_t qsampler_client_callback ( lscp_client_t */*pClient*/,
2536
lscp_event_t event, const char *pchData, int cchData, void *pvData )
2538
MainForm* pMainForm = (MainForm *) pvData;
2539
if (pMainForm == NULL)
2542
// ATTN: DO NOT EVER call any GUI code here,
2543
// as this is run under some other thread context.
2544
// A custom event must be posted here...
2545
QApplication::postEvent(pMainForm,
2546
new CustomEvent(event, pchData, cchData));
2552
// Start our almighty client...
2553
bool MainForm::startClient (void)
2556
if (m_pOptions == NULL)
2559
// Aren't we already started, are we?
2563
// Log prepare here.
2564
appendMessages(tr("Client connecting..."));
2566
// Create the client handle...
2567
m_pClient = ::lscp_client_create(
2568
m_pOptions->sServerHost.toUtf8().constData(),
2569
m_pOptions->iServerPort, qsampler_client_callback, this);
2570
if (m_pClient == NULL) {
2571
// Is this the first try?
2572
// maybe we need to start a local server...
2573
if ((m_pServer && m_pServer->state() == QProcess::Running)
2574
|| !m_pOptions->bServerStart) {
2575
appendMessagesError(
2576
tr("Could not connect to server as client.\n\nSorry."));
2580
// This is always a failure.
2584
// Just set receive timeout value, blindly.
2585
::lscp_client_set_timeout(m_pClient, m_pOptions->iServerTimeout);
2587
tr("Client receive timeout is set to %1 msec.")
2588
.arg(::lscp_client_get_timeout(m_pClient)));
2590
// Subscribe to channel info change notifications...
2591
if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_CHANNEL_INFO) != LSCP_OK)
2592
appendMessagesClient("lscp_client_subscribe");
2594
// We may stop scheduling around.
2597
// We'll accept drops from now on...
2598
setAcceptDrops(true);
2600
// Log success here.
2601
appendMessages(tr("Client connected."));
2603
// Hard-notify instrumnet and device configuration forms,
2604
// if visible, that we're ready...
2605
if (m_pInstrumentListForm)
2606
m_pInstrumentListForm->refreshInstruments();
2608
m_pDeviceForm->refreshDevices();
2610
// Is any session pending to be loaded?
2611
if (!m_pOptions->sSessionFile.isEmpty()) {
2612
// Just load the prabably startup session...
2613
if (loadSessionFile(m_pOptions->sSessionFile)) {
2614
m_pOptions->sSessionFile = QString::null;
2619
// Make a new session
2620
return newSession();
2625
void MainForm::stopClient (void)
2627
if (m_pClient == NULL)
2630
// Log prepare here.
2631
appendMessages(tr("Client disconnecting..."));
2633
// Clear timer counters...
2636
// We'll reject drops from now on...
2637
setAcceptDrops(false);
2639
// Force any channel strips around, but
2640
// but avoid removing the corresponding
2641
// channels from the back-end server.
2643
closeSession(false);
2645
// Close us as a client...
2646
::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_CHANNEL_INFO);
2647
::lscp_client_destroy(m_pClient);
2650
// Hard-notify instrumnet and device configuration forms,
2651
// if visible, that we're running out...
2652
if (m_pInstrumentListForm)
2653
m_pInstrumentListForm->refreshInstruments();
2655
m_pDeviceForm->refreshDevices();
2658
appendMessages(tr("Client disconnected."));
2660
// Make visible status.
2665
// Channel strip activation/selection.
2666
void MainForm::activateStrip ( QWidget *pWidget )
2668
ChannelStrip *pChannelStrip
2669
= static_cast<ChannelStrip *> (pWidget);
2671
pChannelStrip->setSelected(true);
2677
} // namespace QSampler
2680
// end of qsamplerMainForm.cpp