1 /***************************************************************************
2 * Copyright (C) 2009 by Alex Montgomery and Nedko Arnaudov *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
21 #include <QMessageBox>
23 #include <QTextStream>
27 #include "mainWindow.h"
28 #include "validator.h"
29 #include "sequencerThread.h"
32 #include "../../common.h"
35 MainWindow::MainWindow() : m_sequencerThread(0), m_settingsMutex(QMutex::Recursive
)
37 setupUi(this); // use the UI layout generated by qjackmmc.ui
39 // make sure the fps and jitter edit boxes only take positive whole numbers
40 QValidator
* validator
= new Validator(this);
41 fpsEdit
->setValidator(validator
);
42 jitterEdit
->setValidator(validator
);
44 // make sure the device edit box only take positive hexadecimal values
45 validator
= new HexValidator(this);
46 deviceEdit
->setValidator(validator
);
48 // connect all UI editable elements
49 connect(fpsEdit
, SIGNAL(editingFinished()), this, SLOT(onValidateFps()));
50 connect(jitterEdit
, SIGNAL(editingFinished()), this, SLOT(onValidateJitter()));
51 connect(deviceEdit
, SIGNAL(editingFinished()), this, SLOT(onValidateDeviceID()));
52 connect(rtBox
, SIGNAL(clicked(bool)), this, SLOT(onRealtimeChanged(bool)));
53 connect(verboseBox
, SIGNAL(clicked(bool)), this, SLOT(onVerboseChanged(bool)));
54 connect(whatsThisButton
, SIGNAL(clicked()), this, SLOT(on_actionWhat_triggered()));
56 // set the program's icon
57 const QString
iconPath(QString(ICON_DIR
) + "/qjackmmc.png");
58 if (QFile::exists(iconPath
))
59 setWindowIcon(QPixmap(iconPath
));
62 bool MainWindow::init(int argc
, char *argv
[])
64 bool succeeded
= initSound(argc
, argv
); // setup Jack, ALSA, and Lash so the user can connect MIDI to the program
70 // load the default configuration file if it exists
71 QFile
loadFile(QDir::homePath() + QJACKMMC_CONFIG
);
72 if (loadFile
.exists())
77 // start the MMC listener thread
78 Q_ASSERT(!m_sequencerThread
);
79 m_sequencerThread
= new SequencerThread(this, &m_settings
, &m_settingsMutex
);
80 m_sequencerThread
->listen(rtBox
->isChecked());
82 printMMCMessage("QJackMMC is actively listening for MMC messages. If you want to see information about them as they come in, make sure \"Verbose output\" is checked.");
88 MainWindow::~MainWindow()
93 void MainWindow::on_actionQuit_triggered()
98 void MainWindow::on_actionAbout_triggered()
102 #include "../../VERSION"
105 QMessageBox
* aboutBox
= new QMessageBox(this);
106 aboutBox
->setText(message
);
110 void MainWindow::on_actionWhat_triggered()
112 QWhatsThis::enterWhatsThisMode();
115 void MainWindow::on_loadButton_clicked()
117 QFile
loadFile(QDir::homePath() + QJACKMMC_CONFIG
);
118 if (loadFile
.exists())
119 loadConfig(loadFile
);
122 QMessageBox
* fileError
= new QMessageBox(this);
123 fileError
->setText(QDir::homePath() + QString(QJACKMMC_CONFIG
) + " does not exist. You must save a default configuration before you can load one.");
128 void MainWindow::on_saveButton_clicked()
130 QFile
saveFile(QDir::homePath() + QJACKMMC_CONFIG
);
131 if (saveFile
.open(QIODevice::WriteOnly
| QIODevice::Text
))
133 QTextStream
out(&saveFile
);
135 // write out check boxes
137 boolVal
= (verboseBox
->isChecked() ? 1 : 0);
138 out
<< boolVal
<< endl
;
139 boolVal
= (rtBox
->isChecked() ? 1 : 0);
140 out
<< boolVal
<< endl
;
142 // note: this value is ignored but written for backwards compatibility.
143 // It used to signify "listen on startup" but now QJackMMC is always listening.
145 out
<< boolVal
<< endl
;
147 // write out text boxes
148 out
<< fpsEdit
->text() << endl
;
149 out
<< jitterEdit
->text() << endl
;
150 out
<< deviceEdit
->text() << endl
;
152 if (saveFile
.error() != QFile::NoError
)
154 QMessageBox
* jackError
= new QMessageBox(this);
155 jackError
->setText("An error occurred while writing to " + QDir::homePath() + QString(QJACKMMC_CONFIG
));
161 QMessageBox
* fileError
= new QMessageBox(this);
162 fileError
->setText(QDir::homePath() + QString(QJACKMMC_CONFIG
) + " cannot be opened for writing. Make sure that you have permission to write to that directory and file.");
168 void MainWindow::onValidateFps()
170 // validate setting, revert to prior value if not valid
172 int fps
= fpsEdit
->text().toInt(&ok
);
174 // we go to great lengths to make sure that the input boxes only accept positive integers, but check the fields just in case
177 QMessageBox
* inputError
= new QMessageBox(this);
178 inputError
->setText("the frames / sec parameter needs to be a positive integer.");
182 QMutexLocker
settingsLock(&m_settingsMutex
);
183 fpsEdit
->setText(QString::number(m_settings
.frameRate
));
187 QMutexLocker
settingsLock(&m_settingsMutex
);
188 m_settings
.frameRate
= fps
;
192 void MainWindow::onValidateJitter()
194 // validate setting, revert to prior value if not valid
196 int jitterTolerance
= jitterEdit
->text().toInt(&ok
);
197 if (!ok
|| jitterTolerance
< 0)
199 QMessageBox
* inputError
= new QMessageBox(this);
200 inputError
->setText("the jitter tolerance needs to be a positive integer.");
204 QMutexLocker
settingsLock(&m_settingsMutex
);
205 jitterEdit
->setText(QString::number(m_settings
.jitterTolerance
));
209 QMutexLocker
settingsLock(&m_settingsMutex
);
210 m_settings
.jitterTolerance
= jitterTolerance
;
214 void MainWindow::onValidateDeviceID()
216 // validate setting, revert to prior value if not valid
218 int deviceID
= deviceEdit
->text().toInt(&ok
, 16);
219 if (!ok
|| deviceID
> 255 || deviceID
< 0)
221 QMessageBox
* inputError
= new QMessageBox(this);
222 inputError
->setText("the deviceID needs to be a hexadecimal number between 0 and ff.");
226 QMutexLocker
settingsLock(&m_settingsMutex
);
227 deviceEdit
->setText(QString::number(m_settings
.deviceID
));
231 QMutexLocker
settingsLock(&m_settingsMutex
);
232 m_settings
.deviceID
= (uint8_t) deviceID
;
237 void MainWindow::onVerboseChanged(bool checked
)
239 QMutexLocker
settingsLock(&m_settingsMutex
);
240 m_settings
.verbose
= checked
;
243 void MainWindow::onRealtimeChanged(bool checked
)
245 if (m_sequencerThread
)
246 m_sequencerThread
->die(); // die() calls deleteLater and ensures the thread is cleaned up once the listen loop ends
248 m_sequencerThread
= new SequencerThread(this, &m_settings
, &m_settingsMutex
);
249 m_sequencerThread
->listen(checked
);
253 void MainWindow::onMessageReceived(QString message
)
255 messageArea
->append(message
);
258 bool MainWindow::initSound(int argc
, char *argv
[])
260 bool succeeded
= true, alsaPortCreated
= false, jackPortCreated
= false;
261 int ret
= init_alsa_sequencer("QJjackMMC");
264 QMessageBox
* alsaError
= new QMessageBox(this);
265 alsaError
->setText("Can't create alsa sequencer. You will not be able to connect MIDI devices to this program using ALSA. Jack Midi might still function.");
269 alsaPortCreated
= true;
271 if (succeeded
&& init_jack("QJjackMMC") < 0)
273 QMessageBox
* jackError
= new QMessageBox(this);
274 jackError
->setText("couldn't connect to the JACK server. Would you like to start one with default parameters? (Answering \"No\" will close this program.)");
275 jackError
->addButton(QMessageBox::Yes
);
276 jackError
->addButton(QMessageBox::No
);
277 if (jackError
->exec() == QMessageBox::No
)
281 init_lash(argc
, argv
);
285 #endif // LASH_SUPPORT
287 #if JACK_MIDI_SUPPORT
290 if (!init_jack_midi(&m_settings
))
292 QMessageBox
* activateError
= new QMessageBox(this);
293 activateError
->setText("couldn't activate JACK midi, you will not be able to connect MIDI devices to this program using JACK midi.");
294 activateError
->exec();
297 jackPortCreated
= true;
299 #endif // JACK_MIDI_SUPPORT
301 if (succeeded
&& activate_jack() != 0)
303 QMessageBox
* activateError
= new QMessageBox(this);
304 activateError
->setText("couldn't activate JACK, Please check your JACK installation and rerun this program.");
305 activateError
->exec();
309 if (jackPortCreated
== false && alsaPortCreated
== false)
311 QMessageBox
* activateError
= new QMessageBox(this);
312 activateError
->setText("Neither JACK midi nor ALSA midi could be initialized, bailing out.");
313 activateError
->exec();
319 void MainWindow::loadConfig(QFile
& loadFile
)
321 if (loadFile
.open(QIODevice::ReadOnly
| QIODevice::Text
))
323 QTextStream
in(&loadFile
);
325 // read in check boxes
328 verboseBox
->setChecked(boolVal
== 1);
329 m_settings
.verbose
= boolVal
== 1;
331 rtBox
->setChecked(boolVal
== 1);
333 // note: this value is ignored but read for backwards compatibility.
334 // It used to signify "listen on startup" but now QJackMMC is always listening.
337 // read in edit boxes
339 in
>> value
; fpsEdit
->setText(value
); onValidateFps();
340 in
>> value
; jitterEdit
->setText(value
); onValidateJitter();
341 in
>> value
; deviceEdit
->setText(value
); onValidateDeviceID();
343 if (loadFile
.error() != QFile::NoError
)
345 QMessageBox
* jackError
= new QMessageBox(this);
346 jackError
->setText("An error occurred while reading " + QDir::homePath() + QString(QJACKMMC_CONFIG
) +
347 ". The file is either nonexistent, corrupt, or from an older version of QJackMMC. Please set the QJackMMC parameters \
348 how you like them and click \"Save as Default settings\".");
354 QMessageBox
* fileError
= new QMessageBox(this);
355 fileError
->setText(QDir::homePath() + QString(QJACKMMC_CONFIG
) + " cannot be opened for reading. It's either corrupt, or you don't have permission to read it.");
360 void MainWindow::setDefaultSettings()
362 QMutexLocker
settingsLocker(&m_settingsMutex
);
363 m_settings
.deviceID
= 0x7f;
364 m_settings
.frameRate
= 30;
365 m_settings
.jitterTolerance
= 50;
366 m_settings
.verbose
= false;