1 /*
2 SMF GUI Player test using the MIDI Sequencer C++ library
3 Copyright (C) 2006-2021, Pedro Lopez-Cabanillas <plcl@users.sf.net>
4
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 3 of the License, or
8 (at your option) any later version.
9
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.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <QApplication>
20 #include <QCloseEvent>
21 #include <QDebug>
22 #include <QDragEnterEvent>
23 #include <QDropEvent>
24 #include <QFileDialog>
25 #include <QFileInfo>
26 #include <QInputDialog>
27 #include <QMessageBox>
28 #include <QMimeData>
29 #include <QSettings>
30 #include <QStatusBar>
31 #include <QTextCodec>
32 #include <QToolTip>
33 #include <QUrl>
34 #include <qmath.h>
35
36 #include "guiplayer.h"
37 #include "iconutils.h"
38 #include "player.h"
39 #include "playerabout.h"
40 #include "song.h"
41 #include "ui_guiplayer.h"
42 #include <drumstick/alsaclient.h>
43 #include <drumstick/alsaevent.h>
44 #include <drumstick/alsaport.h>
45 #include <drumstick/alsaqueue.h>
46 #include <drumstick/qsmf.h>
47 #include <drumstick/qwrk.h>
48 #include <drumstick/rmid.h>
49 #include <drumstick/sequencererror.h>
50
51 DISABLE_WARNING_PUSH
52 DISABLE_WARNING_DEPRECATED_DECLARATIONS
53
54 using namespace drumstick;
55 using namespace ALSA;
56 using namespace File;
57
58 const QString GUIPlayer::QSTR_DOMAIN = QStringLiteral("drumstick.sourceforge.net");
59 const QString GUIPlayer::QSTR_APPNAME = QStringLiteral("GUIPlayer");
60
GUIPlayer(QWidget * parent,Qt::WindowFlags flags)61 GUIPlayer::GUIPlayer(QWidget *parent, Qt::WindowFlags flags)
62 : QMainWindow(parent, flags),
63 m_portId(-1),
64 m_queueId(-1),
65 m_initialTempo(0),
66 m_currentTrack(0),
67 m_tempoFactor(1.0),
68 m_tick(0),
69 m_state(InvalidState),
70 m_smf(nullptr),
71 m_wrk(nullptr),
72 m_Client(nullptr),
73 m_Port(nullptr),
74 m_Queue(nullptr),
75 m_player(nullptr),
76 m_ui(new Ui::GUIPlayerClass),
77 m_pd(nullptr),
78 m_song(new Song)
79 {
80 m_ui->setupUi(this);
81 setAcceptDrops(true);
82 connect(m_ui->actionAbout, &QAction::triggered, this, &GUIPlayer::about);
83 connect(m_ui->actionAboutQt, &QAction::triggered, qApp, QApplication::aboutQt);
84 connect(m_ui->actionPlay, &QAction::triggered, this, &GUIPlayer::play);
85 connect(m_ui->actionPause, &QAction::triggered, this, &GUIPlayer::pause);
86 connect(m_ui->actionStop, &QAction::triggered, this, &GUIPlayer::stop);
87 connect(m_ui->actionOpen, &QAction::triggered, this, &GUIPlayer::open);
88 connect(m_ui->actionMIDISetup, &QAction::triggered, this, &GUIPlayer::setup);
89 connect(m_ui->actionQuit, &QAction::triggered, this, &GUIPlayer::close);
90 connect(m_ui->btnTempo, &QPushButton::clicked, this, &GUIPlayer::tempoReset);
91 connect(m_ui->btnVolume, &QPushButton::clicked, this, &GUIPlayer::volumeReset);
92 connect(m_ui->sliderTempo, &QSlider::valueChanged, this, &GUIPlayer::tempoSlider);
93 connect(m_ui->volumeSlider, &QSlider::valueChanged, this, &GUIPlayer::volumeSlider);
94 connect(m_ui->spinPitch, QOverload<int>::of(&QSpinBox::valueChanged),
95 this, &GUIPlayer::pitchShift);
96 connect(m_ui->toolBar->toggleViewAction(), &QAction::toggled,
97 m_ui->actionShowToolbar, &QAction::setChecked);
98
99 m_ui->actionPlay->setIcon(QIcon(IconUtils::GetPixmap(this, ":/resources/play.png")));
100 m_ui->actionPlay->setShortcut( Qt::Key_MediaPlay );
101 m_ui->actionStop->setIcon(QIcon(IconUtils::GetPixmap(this, ":/resources/stop.png")));
102 m_ui->actionStop->setShortcut( Qt::Key_MediaStop );
103 m_ui->actionPause->setIcon(QIcon(IconUtils::GetPixmap(this, ":/resources/pause.png")));
104 m_ui->actionMIDISetup->setIcon(QIcon(IconUtils::GetPixmap(this, ":/resources/setup.png")));
105
106 m_Client = new MidiClient(this);
107 m_Client->open();
108 m_Client->setPoolOutput(50); // small size, for near real-time pitchShift
109 m_Client->setClientName("MIDI Player");
110 connect( m_Client, &MidiClient::eventReceived, this, &GUIPlayer::sequencerEvent, Qt::QueuedConnection );
111
112 m_Port = new MidiPort(this);
113 m_Port->attach( m_Client );
114 m_Port->setPortName("MIDI Player Output Port");
115 m_Port->setCapability( SND_SEQ_PORT_CAP_READ |
116 SND_SEQ_PORT_CAP_SUBS_READ |
117 SND_SEQ_PORT_CAP_WRITE );
118 m_Port->setPortType( SND_SEQ_PORT_TYPE_APPLICATION |
119 SND_SEQ_PORT_TYPE_MIDI_GENERIC );
120
121 m_Queue = m_Client->createQueue(QSTR_APPNAME);
122 m_queueId = m_Queue->getId();
123 m_portId = m_Port->getPortId();
124
125 m_rmi = new Rmidi(this);
126 connect(m_rmi, &Rmidi::signalRiffData, this, &GUIPlayer::dataHandler);
127
128 m_smf = new QSmf(this);
129 connect(m_smf, &QSmf::signalSMFHeader, this, &GUIPlayer::smfHeaderEvent);
130 connect(m_smf, &QSmf::signalSMFNoteOn, this, &GUIPlayer::smfNoteOnEvent);
131 connect(m_smf, &QSmf::signalSMFNoteOff, this, &GUIPlayer::smfNoteOffEvent);
132 connect(m_smf, &QSmf::signalSMFKeyPress, this, &GUIPlayer::smfKeyPressEvent);
133 connect(m_smf, &QSmf::signalSMFCtlChange, this, &GUIPlayer::smfCtlChangeEvent);
134 connect(m_smf, &QSmf::signalSMFPitchBend, this, &GUIPlayer::smfPitchBendEvent);
135 connect(m_smf, &QSmf::signalSMFProgram, this, &GUIPlayer::smfProgramEvent);
136 connect(m_smf, &QSmf::signalSMFChanPress, this, &GUIPlayer::smfChanPressEvent);
137 connect(m_smf, &QSmf::signalSMFSysex, this, &GUIPlayer::smfSysexEvent);
138 connect(m_smf, &QSmf::signalSMFText, this, &GUIPlayer::smfUpdateLoadProgress);
139 connect(m_smf, &QSmf::signalSMFTempo, this, &GUIPlayer::smfTempoEvent);
140 connect(m_smf, &QSmf::signalSMFTrackStart, this, &GUIPlayer::smfUpdateLoadProgress);
141 connect(m_smf, &QSmf::signalSMFTrackStart, this, &GUIPlayer::smfTrackStarted);
142 connect(m_smf, &QSmf::signalSMFTrackEnd, this, &GUIPlayer::smfTrackEnded);
143 connect(m_smf, &QSmf::signalSMFendOfTrack, this, &GUIPlayer::smfUpdateLoadProgress);
144 connect(m_smf, &QSmf::signalSMFError, this, &GUIPlayer::smfErrorHandler);
145
146 m_wrk = new QWrk(this);
147 m_wrk->setTextCodec(QTextCodec::codecForLocale());
148 connect(m_wrk, &QWrk::signalWRKError, this, &GUIPlayer::wrkErrorHandler);
149 connect(m_wrk, &QWrk::signalWRKUnknownChunk, this, &GUIPlayer::wrkUpdateLoadProgress);
150 connect(m_wrk, &QWrk::signalWRKHeader, this, &GUIPlayer::wrkFileHeader);
151 connect(m_wrk, &QWrk::signalWRKEnd, this, &GUIPlayer::wrkEndOfFile);
152 connect(m_wrk, &QWrk::signalWRKStreamEnd, this, &GUIPlayer::wrkStreamEndEvent);
153 connect(m_wrk, &QWrk::signalWRKGlobalVars, this, &GUIPlayer::wrkUpdateLoadProgress);
154 connect(m_wrk, &QWrk::signalWRKTrack, this, &GUIPlayer::wrkTrackHeader);
155 connect(m_wrk, &QWrk::signalWRKTimeBase, this, &GUIPlayer::wrkTimeBase);
156 connect(m_wrk, &QWrk::signalWRKNote, this, &GUIPlayer::wrkNoteEvent);
157 connect(m_wrk, &QWrk::signalWRKKeyPress, this, &GUIPlayer::wrkKeyPressEvent);
158 connect(m_wrk, &QWrk::signalWRKCtlChange, this, &GUIPlayer::wrkCtlChangeEvent);
159 connect(m_wrk, &QWrk::signalWRKPitchBend, this, &GUIPlayer::wrkPitchBendEvent);
160 connect(m_wrk, &QWrk::signalWRKProgram, this, &GUIPlayer::wrkProgramEvent);
161 connect(m_wrk, &QWrk::signalWRKChanPress, this, &GUIPlayer::wrkChanPressEvent);
162 connect(m_wrk, &QWrk::signalWRKSysexEvent, this, &GUIPlayer::wrkSysexEvent);
163 connect(m_wrk, &QWrk::signalWRKSysex, this, &GUIPlayer::wrkSysexEventBank);
164 connect(m_wrk, &QWrk::signalWRKText, this, &GUIPlayer::wrkUpdateLoadProgress);
165 connect(m_wrk, &QWrk::signalWRKTimeSig, this, &GUIPlayer::wrkUpdateLoadProgress);
166 connect(m_wrk, &QWrk::signalWRKKeySig, this, &GUIPlayer::wrkUpdateLoadProgress);
167 connect(m_wrk, &QWrk::signalWRKTempo, this, &GUIPlayer::wrkTempoEvent);
168 connect(m_wrk, &QWrk::signalWRKTrackPatch, this, &GUIPlayer::wrkTrackPatch);
169 connect(m_wrk, &QWrk::signalWRKComments, this, &GUIPlayer::wrkUpdateLoadProgress);
170 connect(m_wrk, &QWrk::signalWRKVariableRecord, this, &GUIPlayer::wrkUpdateLoadProgress);
171 connect(m_wrk, &QWrk::signalWRKNewTrack, this, &GUIPlayer::wrkNewTrackHeader);
172 connect(m_wrk, &QWrk::signalWRKTrackName, this, &GUIPlayer::wrkUpdateLoadProgress);
173 connect(m_wrk, &QWrk::signalWRKTrackVol, this, &GUIPlayer::wrkTrackVol);
174 connect(m_wrk, &QWrk::signalWRKTrackBank, this, &GUIPlayer::wrkTrackBank);
175 connect(m_wrk, &QWrk::signalWRKSegment, this, &GUIPlayer::wrkUpdateLoadProgress);
176 connect(m_wrk, &QWrk::signalWRKChord, this, &GUIPlayer::wrkUpdateLoadProgress);
177 connect(m_wrk, &QWrk::signalWRKExpression, this, &GUIPlayer::wrkUpdateLoadProgress);
178
179 m_player = new Player(m_Client, m_portId);
180 connect(m_player, &Player::playbackStopped, this, &GUIPlayer::playerStopped, Qt::QueuedConnection);
181
182 m_Client->setRealTimeInput(false);
183 m_Client->startSequencerInput();
184 tempoReset();
185 volumeReset();
186 updateState(EmptyState);
187 readSettings();
188 }
189
~GUIPlayer()190 GUIPlayer::~GUIPlayer()
191 {
192 m_Client->stopSequencerInput();
193 m_Port->unsubscribeAll();
194 m_Port->detach();
195 m_Client->close();
196 delete m_player;
197 delete m_ui;
198 delete m_song;
199 }
200
subscribe(const QString & portName)201 void GUIPlayer::subscribe(const QString& portName)
202 {
203 try {
204 if (!m_subscription.isEmpty()) {
205 m_Port->unsubscribeTo(m_subscription);
206 }
207 m_subscription = portName;
208 m_Port->subscribeTo(m_subscription);
209 } catch (const SequencerError& err) {
210 qWarning() << "SequencerError exception. Error code: " << err.code()
211 << " (" << err.qstrError() << ")";
212 qWarning() << "Location: " << err.location();
213 }
214 }
215
updateTimeLabel(int mins,int secs,int cnts)216 void GUIPlayer::updateTimeLabel(int mins, int secs, int cnts)
217 {
218 static QChar fill('0');
219 QString stime = QString("%1:%2.%3").arg(mins,2,10,fill)
220 .arg(secs,2,10,fill)
221 .arg(cnts,2,10,fill);
222 m_ui->lblTime->setText(stime);
223 }
224
updateState(PlayerState newState)225 void GUIPlayer::updateState(PlayerState newState)
226 {
227 if (m_state == newState)
228 return;
229 switch (newState) {
230 case EmptyState:
231 m_ui->actionPlay->setEnabled(false);
232 m_ui->actionPause->setEnabled(false);
233 m_ui->actionStop->setEnabled(false);
234 statusBar()->showMessage("Please, load a song");
235 break;
236 case PlayingState:
237 m_ui->actionPlay->setEnabled(false);
238 m_ui->actionPause->setEnabled(true);
239 m_ui->actionStop->setEnabled(true);
240 statusBar()->showMessage("Playing");
241 break;
242 case PausedState:
243 m_ui->actionPlay->setEnabled(false);
244 m_ui->actionStop->setEnabled(true);
245 statusBar()->showMessage("Paused");
246 break;
247 case StoppedState:
248 m_ui->actionPause->setChecked(false);
249 m_ui->actionPause->setEnabled(false);
250 m_ui->actionStop->setEnabled(false);
251 m_ui->actionPlay->setEnabled(true);
252 statusBar()->showMessage("Stopped");
253 break;
254 default:
255 statusBar()->showMessage("Not initialized");
256 break;
257 }
258 m_state = newState;
259 }
260
play()261 void GUIPlayer::play()
262 {
263 if (!m_song->isEmpty()) {
264 if (m_player->getInitialPosition() == 0) {
265 if (m_initialTempo == 0)
266 return;
267 QueueTempo firstTempo = m_Queue->getTempo();
268 firstTempo.setPPQ(m_song->getDivision());
269 firstTempo.setTempo(m_initialTempo);
270 firstTempo.setTempoFactor(m_tempoFactor);
271 m_Queue->setTempo(firstTempo);
272 m_Client->drainOutput();
273 m_player->sendVolumeEvents();
274 }
275 m_player->start();
276 updateState(PlayingState);
277 }
278 }
279
pause()280 void GUIPlayer::pause()
281 {
282 if (m_state == PlayingState || m_player->isRunning()) {
283 m_player->stop();
284 m_player->setPosition(m_Queue->getStatus().getTickTime());
285 updateState(PausedState);
286 } else if (!m_song->isEmpty()) {
287 m_player->start();
288 updateState(PlayingState);
289 }
290 }
291
stop()292 void GUIPlayer::stop()
293 {
294 if (m_state == PlayingState || m_state == PausedState ||
295 m_player->isRunning()) {
296 m_Queue->stop();
297 m_Queue->clear();
298 m_player->stop();
299 }
300 if (m_initialTempo != 0)
301 songFinished();
302 else
303 updateState(StoppedState);
304 }
305
progressDialogInit(const QString & type,int max)306 void GUIPlayer::progressDialogInit(const QString& type, int max)
307 {
308 m_pd = new QProgressDialog("", "", 0, max, this);
309 m_pd->setWindowTitle(QString("Loading %1 file...").arg(type));
310 m_pd->setMinimumDuration(1000);
311 m_pd->setValue(0);
312 }
313
progressDialogUpdate(int pos)314 void GUIPlayer::progressDialogUpdate(int pos)
315 {
316 if (m_pd != nullptr) {
317 m_pd->setValue(pos);
318 qApp->processEvents();
319 }
320 }
321
progressDialogClose()322 void GUIPlayer::progressDialogClose()
323 {
324 delete m_pd; // set to 0 by QPointer<>
325 }
326
openFile(const QString & fileName)327 void GUIPlayer::openFile(const QString& fileName)
328 {
329 QFileInfo finfo(fileName);
330 if (finfo.exists()) {
331 m_song->clear();
332 m_loadingMessages.clear();
333 m_tick = 0;
334 m_initialTempo = 0;
335 m_currentTrack = 0;
336 try {
337 QString ext = finfo.suffix().toLower();
338 if (ext == "wrk") {
339 progressDialogInit("Cakewalk", finfo.size());
340 m_wrk->readFromFile(fileName);
341 }
342 else if (ext == "mid" || ext == "midi" || ext == "kar") {
343 progressDialogInit("MIDI", finfo.size());
344 m_smf->readFromFile(fileName);
345 }
346 else if (ext == "rmi") {
347 progressDialogInit("RIFF MIDI", finfo.size());
348 m_rmi->readFromFile(fileName);
349 }
350 progressDialogUpdate(finfo.size());
351 if (m_song->isEmpty()) {
352 m_ui->lblName->clear();
353 } else {
354 m_song->sort();
355 m_player->setSong(m_song);
356 m_ui->lblName->setText(finfo.fileName());
357 m_lastDirectory = finfo.absolutePath();
358 }
359 } catch (...) {
360 m_song->clear();
361 m_ui->lblName->clear();
362 }
363 progressDialogClose();
364 if (m_initialTempo == 0) {
365 m_initialTempo = 500000;
366 }
367 updateTimeLabel(0,0,0);
368 updateTempoLabel(6.0e7f / m_initialTempo);
369 m_ui->progressBar->setValue(0);
370 if (!m_loadingMessages.isEmpty()) {
371 m_loadingMessages.insert(0,
372 "Warning, this file may be non-standard or damaged.<br>");
373 QMessageBox::warning(this, QSTR_APPNAME, m_loadingMessages);
374 }
375 if (m_song->isEmpty())
376 updateState(EmptyState);
377 else
378 updateState(StoppedState);
379 }
380 }
381
open()382 void GUIPlayer::open()
383 {
384 QString fileName = QFileDialog::getOpenFileName(this,
385 "Open MIDI File", m_lastDirectory,
386 "All files (*.kar *.mid *.midi *rmi *.wrk);;"
387 "Karaoke files (*.kar);;"
388 "MIDI Files (*.mid *.midi);;"
389 "RIFF MIDI Files (*.rmi);;"
390 "Cakewalk files (*.wrk)" );
391 if (! fileName.isEmpty() ) {
392 stop();
393 openFile(fileName);
394 }
395 }
396
setup()397 void GUIPlayer::setup()
398 {
399 bool ok;
400 int current;
401 QStringList items;
402 QListIterator<PortInfo> it(m_Client->getAvailableOutputs());
403 while(it.hasNext()) {
404 PortInfo p = it.next();
405 items << QString("%1:%2").arg(p.getClientName()).arg(p.getPort());
406 }
407 current = items.indexOf(m_subscription);
408 QString item = QInputDialog::getItem(this, "Player subscription",
409 "Output port:", items,
410 current, false, &ok);
411 if (ok && !item.isEmpty())
412 subscribe(item);
413 }
414
songFinished()415 void GUIPlayer::songFinished()
416 {
417 m_player->resetPosition();
418 updateState(StoppedState);
419 }
420
playerStopped()421 void GUIPlayer::playerStopped()
422 {
423 int portId = m_Port->getPortId();
424 for (int channel = 0; channel < 16; ++channel) {
425 ControllerEvent ev1(channel, MIDI_CTL_ALL_NOTES_OFF, 0);
426 ev1.setSource(portId);
427 ev1.setSubscribers();
428 ev1.setDirect();
429 m_Client->outputDirect(&ev1);
430 ControllerEvent ev2(channel, MIDI_CTL_ALL_SOUNDS_OFF, 0);
431 ev2.setSource(portId);
432 ev2.setSubscribers();
433 ev2.setDirect();
434 m_Client->outputDirect(&ev2);
435 }
436 m_Client->drainOutput();
437 }
438
updateTempoLabel(float ftempo)439 void GUIPlayer::updateTempoLabel(float ftempo)
440 {
441 QString stempo = QString("%1 bpm").arg(ftempo, 0, 'f', 2);
442 m_ui->lblOther->setText(stempo);
443 }
444
sequencerEvent(SequencerEvent * ev)445 void GUIPlayer::sequencerEvent(SequencerEvent *ev)
446 {
447 if ((ev->getSequencerType() == SND_SEQ_EVENT_ECHO) && (m_tick != 0)){
448 auto t = ev->getTick();
449 int pos = 100 * t / m_tick;
450 const snd_seq_real_time_t* rt = m_Queue->getStatus().getRealtime();
451 int mins = rt->tv_sec / 60;
452 int secs = rt->tv_sec % 60;
453 int cnts = qFloor( rt->tv_nsec / 1.0e7 );
454 updateTempoLabel(m_Queue->getTempo().getRealBPM());
455 updateTimeLabel(mins, secs, cnts);
456 m_ui->progressBar->setValue(pos);
457 if (t >= m_tick) {
458 songFinished();
459 }
460 }
461 delete ev;
462 }
463
dataHandler(const QString & dataType,const QByteArray & data)464 void GUIPlayer::dataHandler(const QString &dataType, const QByteArray &data)
465 {
466 if (dataType == "RMID") {
467 QDataStream ds(data);
468 m_smf->readFromStream(&ds);
469 }
470 }
471
pitchShift(int value)472 void GUIPlayer::pitchShift(int value)
473 {
474 m_player->setPitchShift(value);
475 }
476
tempoReset()477 void GUIPlayer::tempoReset()
478 {
479 m_ui->sliderTempo->setValue(100);
480 tempoSlider(100);
481 }
482
volumeReset()483 void GUIPlayer::volumeReset()
484 {
485 m_ui->volumeSlider->setValue(100);
486 volumeSlider(100);
487 }
488
tempoSlider(int value)489 void GUIPlayer::tempoSlider(int value)
490 {
491 m_tempoFactor = (value*value + 100.0*value + 20000.0) / 40000.0;
492 QueueTempo qtempo = m_Queue->getTempo();
493 qtempo.setTempoFactor(m_tempoFactor);
494 m_Queue->setTempo(qtempo);
495 m_Client->drainOutput();
496 if (!m_player->isRunning())
497 updateTempoLabel(qtempo.getRealBPM());
498 // Slider tooltip
499 QString tip = QString("%1 %").arg(m_tempoFactor*100.0, 0, 'f', 0);
500 m_ui->sliderTempo->setToolTip(tip);
501 QToolTip::showText(QCursor::pos(), tip, this);
502 }
503
volumeSlider(int value)504 void GUIPlayer::volumeSlider(int value)
505 {
506 QString tip = QString::number(value)+'%';
507 m_ui->lblVolume->setText(tip);
508 m_ui->volumeSlider->setToolTip(tip);
509 m_player->setVolumeFactor(value);
510 QToolTip::showText(QCursor::pos(), tip, this);
511 }
512
dragEnterEvent(QDragEnterEvent * event)513 void GUIPlayer::dragEnterEvent( QDragEnterEvent * event )
514 {
515 if (event->mimeData()->hasUrls()) {
516 event->acceptProposedAction();
517 }
518 }
519
dropEvent(QDropEvent * event)520 void GUIPlayer::dropEvent( QDropEvent * event )
521 {
522 if ( event->mimeData()->hasUrls() ) {
523 QList<QUrl> urls = event->mimeData()->urls();
524 if (!urls.empty()) {
525 QString fileName = urls.first().toLocalFile();
526 if ( fileName.endsWith(".mid", Qt::CaseInsensitive) ||
527 fileName.endsWith(".midi", Qt::CaseInsensitive) ||
528 fileName.endsWith(".kar", Qt::CaseInsensitive) ||
529 fileName.endsWith(".rmi", Qt::CaseInsensitive) ||
530 fileName.endsWith(".wrk", Qt::CaseInsensitive) ) {
531 stop();
532 event->accept();
533 openFile(fileName);
534 } else {
535 QMessageBox::warning(this, QSTR_APPNAME,
536 QString("Dropped file %1 is not supported").arg(fileName));
537 }
538 }
539 }
540 }
541
readSettings()542 void GUIPlayer::readSettings()
543 {
544 QSettings settings;
545
546 settings.beginGroup("Window");
547 restoreGeometry(settings.value("Geometry").toByteArray());
548 restoreState(settings.value("State").toByteArray());
549 settings.endGroup();
550
551 settings.beginGroup("Preferences");
552 m_lastDirectory = settings.value("LastDirectory").toString();
553 QString midiConn = settings.value("MIDIConnection").toString();
554 settings.endGroup();
555
556 if (midiConn.length() > 0)
557 subscribe(midiConn);
558 }
559
writeSettings()560 void GUIPlayer::writeSettings()
561 {
562 QSettings settings;
563
564 settings.beginGroup("Window");
565 settings.setValue("Geometry", saveGeometry());
566 settings.setValue("State", saveState());
567 settings.endGroup();
568
569 settings.beginGroup("Preferences");
570 settings.setValue("LastDirectory", m_lastDirectory);
571 settings.setValue("MIDIConnection", m_subscription);
572 settings.endGroup();
573 }
574
closeEvent(QCloseEvent * event)575 void GUIPlayer::closeEvent( QCloseEvent *event )
576 {
577 stop();
578 m_player->wait();
579 writeSettings();
580 event->accept();
581 }
582
about()583 void GUIPlayer::about()
584 {
585 About aboutDlg(this);
586 aboutDlg.exec();
587 }
588
589 /* **************************************** *
590 * SMF (Standard MIDI file) format handling
591 * **************************************** */
592
smfUpdateLoadProgress()593 void GUIPlayer::smfUpdateLoadProgress()
594 {
595 progressDialogUpdate(m_smf->getFilePos());
596 }
597
appendSMFEvent(SequencerEvent * ev)598 void GUIPlayer::appendSMFEvent(SequencerEvent* ev)
599 {
600 unsigned long tick = m_smf->getCurrentTime();
601 ev->setSource(m_portId);
602 if (ev->getSequencerType() != SND_SEQ_EVENT_TEMPO) {
603 ev->setSubscribers();
604 }
605 ev->scheduleTick(m_queueId, tick, false);
606 m_song->append(ev);
607 if (tick > m_tick)
608 m_tick = tick;
609 smfUpdateLoadProgress();
610 }
611
smfHeaderEvent(int format,int ntrks,int division)612 void GUIPlayer::smfHeaderEvent(int format, int ntrks, int division)
613 {
614 m_song->setHeader(format, ntrks, division);
615 smfUpdateLoadProgress();
616 }
617
smfNoteOnEvent(int chan,int pitch,int vol)618 void GUIPlayer::smfNoteOnEvent(int chan, int pitch, int vol)
619 {
620 SequencerEvent* ev = new NoteOnEvent (chan, pitch, vol);
621 appendSMFEvent(ev);
622 }
623
smfNoteOffEvent(int chan,int pitch,int vol)624 void GUIPlayer::smfNoteOffEvent(int chan, int pitch, int vol)
625 {
626 SequencerEvent* ev = new NoteOffEvent (chan, pitch, vol);
627 appendSMFEvent(ev);
628 }
629
smfKeyPressEvent(int chan,int pitch,int press)630 void GUIPlayer::smfKeyPressEvent(int chan, int pitch, int press)
631 {
632 SequencerEvent* ev = new KeyPressEvent (chan, pitch, press);
633 appendSMFEvent(ev);
634 }
635
smfCtlChangeEvent(int chan,int ctl,int value)636 void GUIPlayer::smfCtlChangeEvent(int chan, int ctl, int value)
637 {
638 SequencerEvent* ev = new ControllerEvent (chan, ctl, value);
639 appendSMFEvent(ev);
640 }
641
smfPitchBendEvent(int chan,int value)642 void GUIPlayer::smfPitchBendEvent(int chan, int value)
643 {
644 SequencerEvent* ev = new PitchBendEvent (chan, value);
645 appendSMFEvent(ev);
646 }
647
smfProgramEvent(int chan,int patch)648 void GUIPlayer::smfProgramEvent(int chan, int patch)
649 {
650 SequencerEvent* ev = new ProgramChangeEvent (chan, patch);
651 appendSMFEvent(ev);
652 }
653
smfChanPressEvent(int chan,int press)654 void GUIPlayer::smfChanPressEvent(int chan, int press)
655 {
656 SequencerEvent* ev = new ChanPressEvent (chan, press);
657 appendSMFEvent(ev);
658 }
659
smfSysexEvent(const QByteArray & data)660 void GUIPlayer::smfSysexEvent(const QByteArray& data)
661 {
662 SequencerEvent* ev = new SysExEvent (data);
663 appendSMFEvent(ev);
664 }
665
smfTempoEvent(int tempo)666 void GUIPlayer::smfTempoEvent(int tempo)
667 {
668 if ( m_initialTempo == 0 ) {
669 m_initialTempo = tempo;
670 }
671 SequencerEvent* ev = new TempoEvent (m_queueId, tempo);
672 appendSMFEvent(ev);
673 }
674
smfErrorHandler(const QString & errorStr)675 void GUIPlayer::smfErrorHandler(const QString& errorStr)
676 {
677 if (m_loadingMessages.length() < 1024)
678 m_loadingMessages.append(QString("%1 at file offset %2<br>")
679 .arg(errorStr).arg(m_smf->getFilePos()));
680 }
681
smfTrackStarted()682 void GUIPlayer::smfTrackStarted()
683 {
684 m_currentTrack++;
685 }
686
smfTrackEnded()687 void GUIPlayer::smfTrackEnded()
688 {
689 if (m_currentTrack == m_smf->getTracks()) {
690 SequencerEvent* ev = new SystemEvent(SND_SEQ_EVENT_ECHO);
691 appendSMFEvent(ev);
692 }
693 }
694
695 /* ********************************* *
696 * Cakewalk WRK file format handling
697 * ********************************* */
698
wrkUpdateLoadProgress()699 void GUIPlayer::wrkUpdateLoadProgress()
700 {
701 if (m_pd != nullptr) {
702 progressDialogUpdate(m_wrk->getFilePos());
703 }
704 }
705
706 void
appendWRKEvent(unsigned long ticks,SequencerEvent * ev)707 GUIPlayer::appendWRKEvent(unsigned long ticks, SequencerEvent* ev)
708 {
709 ev->setSource(m_portId);
710 if (ev->getSequencerType() != SND_SEQ_EVENT_TEMPO) {
711 ev->setSubscribers();
712 }
713 ev->scheduleTick(m_queueId, ticks, false);
714 m_song->append(ev);
715 if (ticks > m_tick)
716 m_tick = ticks;
717 wrkUpdateLoadProgress();
718 }
719
wrkErrorHandler(const QString & errorStr)720 void GUIPlayer::wrkErrorHandler(const QString& errorStr)
721 {
722 if (m_loadingMessages.length() < 1024) {
723 m_loadingMessages.append(QString("%1 at file offset %2<br>")
724 .arg(errorStr).arg(m_wrk->getFilePos()));
725 }
726 }
727
wrkFileHeader(int,int)728 void GUIPlayer::wrkFileHeader(int /*verh*/, int /*verl*/)
729 {
730 m_song->setHeader(1, 0, 120);
731 wrkUpdateLoadProgress();
732 // qDebug() << Q_FUNC_INFO;
733 }
734
wrkTimeBase(int timebase)735 void GUIPlayer::wrkTimeBase(int timebase)
736 {
737 m_song->setDivision(timebase);
738 wrkUpdateLoadProgress();
739 // qDebug() << Q_FUNC_INFO << timebase;
740 }
741
wrkStreamEndEvent(long time)742 void GUIPlayer::wrkStreamEndEvent(long time)
743 {
744 unsigned long ticks = time;
745 if (ticks > m_tick)
746 m_tick = ticks;
747 wrkUpdateLoadProgress();
748 // qDebug() << Q_FUNC_INFO << time;
749 }
750
wrkTrackHeader(const QString &,const QString &,int trackno,int channel,int pitch,int velocity,int,bool,bool,bool)751 void GUIPlayer::wrkTrackHeader( const QString& /*name1*/,
752 const QString& /*name2*/,
753 int trackno, int channel,
754 int pitch, int velocity, int /*port*/,
755 bool /*selected*/, bool /*muted*/, bool /*loop*/ )
756 {
757 TrackMapRec rec;
758 rec.channel = channel;
759 rec.pitch = pitch;
760 rec.velocity = velocity;
761 m_trackMap[trackno] = rec;
762 wrkUpdateLoadProgress();
763 // qDebug() << Q_FUNC_INFO << trackno << channel << pitch << velocity;
764 }
765
wrkNoteEvent(int track,long time,int chan,int pitch,int vol,int dur)766 void GUIPlayer::wrkNoteEvent(int track, long time, int chan, int pitch, int vol, int dur)
767 {
768 TrackMapRec rec = m_trackMap[track];
769 int channel = (rec.channel > -1) ? rec.channel : chan;
770 int key = qBound(0, pitch + rec.pitch, 127);
771 int velocity = qBound(0, vol + rec.velocity, 127);
772 SequencerEvent* ev = new NoteEvent(channel, key, velocity, dur);
773 appendWRKEvent(time, ev);
774 // qDebug() << Q_FUNC_INFO << channel << key << velocity << dur;
775 }
776
wrkKeyPressEvent(int track,long time,int chan,int pitch,int press)777 void GUIPlayer::wrkKeyPressEvent(int track, long time, int chan, int pitch, int press)
778 {
779 TrackMapRec rec = m_trackMap[track];
780 int key = pitch + rec.pitch;
781 int channel = (rec.channel > -1) ? rec.channel : chan;
782 SequencerEvent* ev = new KeyPressEvent(channel, key, press);
783 appendWRKEvent(time, ev);
784 // qDebug() << Q_FUNC_INFO;
785 }
786
wrkCtlChangeEvent(int track,long time,int chan,int ctl,int value)787 void GUIPlayer::wrkCtlChangeEvent(int track, long time, int chan, int ctl, int value)
788 {
789 TrackMapRec rec = m_trackMap[track];
790 int channel = (rec.channel > -1) ? rec.channel : chan;
791 SequencerEvent* ev = new ControllerEvent(channel, ctl, value);
792 appendWRKEvent(time, ev);
793 // qDebug() << Q_FUNC_INFO;
794 }
795
wrkPitchBendEvent(int track,long time,int chan,int value)796 void GUIPlayer::wrkPitchBendEvent(int track, long time, int chan, int value)
797 {
798 TrackMapRec rec = m_trackMap[track];
799 int channel = (rec.channel > -1) ? rec.channel : chan;
800 SequencerEvent* ev = new PitchBendEvent(channel, value);
801 appendWRKEvent(time, ev);
802 // qDebug() << Q_FUNC_INFO;
803 }
804
wrkProgramEvent(int track,long time,int chan,int patch)805 void GUIPlayer::wrkProgramEvent(int track, long time, int chan, int patch)
806 {
807 TrackMapRec rec = m_trackMap[track];
808 int channel = (rec.channel > -1) ? rec.channel : chan;
809 SequencerEvent* ev = new ProgramChangeEvent(channel, patch);
810 appendWRKEvent(time, ev);
811 // qDebug() << Q_FUNC_INFO;
812 }
813
wrkChanPressEvent(int track,long time,int chan,int press)814 void GUIPlayer::wrkChanPressEvent(int track, long time, int chan, int press)
815 {
816 TrackMapRec rec = m_trackMap[track];
817 int channel = (rec.channel > -1) ? rec.channel : chan;
818 SequencerEvent* ev = new ChanPressEvent(channel, press);
819 appendWRKEvent(time, ev);
820 // qDebug() << Q_FUNC_INFO;
821 }
822
wrkSysexEvent(int track,long time,int bank)823 void GUIPlayer::wrkSysexEvent(int track, long time, int bank)
824 {
825 Q_UNUSED(track)
826 qDebug() << Q_FUNC_INFO;
827 if (m_savedSysexEvents.contains(bank)) {
828 SysExEvent* ev = m_savedSysexEvents[bank].clone();
829 appendWRKEvent(time, ev);
830 wrkUpdateLoadProgress();
831 }
832 }
833
wrkSysexEventBank(int bank,const QString &,bool autosend,int,const QByteArray & data)834 void GUIPlayer::wrkSysexEventBank(int bank, const QString& /*name*/,
835 bool autosend, int /*port*/, const QByteArray& data)
836 {
837 //qDebug() << Q_FUNC_INFO;
838 SysExEvent* ev = new SysExEvent(data);
839 if (autosend) {
840 appendWRKEvent(0, ev);
841 } else {
842 m_savedSysexEvents[bank] = *ev;
843 delete ev;
844 }
845 wrkUpdateLoadProgress();
846 }
847
wrkTempoEvent(long time,int tempo)848 void GUIPlayer::wrkTempoEvent(long time, int tempo)
849 {
850 double bpm = tempo / 100.0;
851 if ( m_initialTempo < 0 )
852 m_initialTempo = qRound( bpm );
853 SequencerEvent* ev = new TempoEvent(m_queueId, qRound ( 6e7 / bpm ) );
854 appendWRKEvent(time, ev);
855 // qDebug() << Q_FUNC_INFO;
856 }
857
wrkTrackPatch(int track,int patch)858 void GUIPlayer::wrkTrackPatch(int track, int patch)
859 {
860 TrackMapRec rec = m_trackMap[track];
861 int channel = (rec.channel > -1) ? rec.channel : 0;
862 wrkProgramEvent(track, 0, channel, patch);
863 // qDebug() << Q_FUNC_INFO;
864 }
865
wrkNewTrackHeader(const QString &,int trackno,int channel,int pitch,int velocity,int,bool,bool,bool)866 void GUIPlayer::wrkNewTrackHeader( const QString& /*name*/,
867 int trackno, int channel,
868 int pitch, int velocity, int /*port*/,
869 bool /*selected*/, bool /*muted*/, bool /*loop*/ )
870 {
871 TrackMapRec rec;
872 rec.channel = channel;
873 rec.pitch = pitch;
874 rec.velocity = velocity;
875 m_trackMap[trackno] = rec;
876 wrkUpdateLoadProgress();
877 // qDebug() << Q_FUNC_INFO << trackno << channel << pitch << velocity;
878 }
879
wrkTrackVol(int track,int vol)880 void GUIPlayer::wrkTrackVol(int track, int vol)
881 {
882 int lsb, msb;
883 TrackMapRec rec = m_trackMap[track];
884 int channel = (rec.channel > -1) ? rec.channel : 0;
885 if (vol < 128)
886 wrkCtlChangeEvent(track, 0, channel, MIDI_CTL_MSB_MAIN_VOLUME, vol);
887 else {
888 lsb = vol % 0x80;
889 msb = vol / 0x80;
890 wrkCtlChangeEvent(track, 0, channel, MIDI_CTL_LSB_MAIN_VOLUME, lsb);
891 wrkCtlChangeEvent(track, 0, channel, MIDI_CTL_MSB_MAIN_VOLUME, msb);
892 }
893 // qDebug() << Q_FUNC_INFO;
894 }
895
wrkTrackBank(int track,int bank)896 void GUIPlayer::wrkTrackBank(int track, int bank)
897 {
898 // assume GM/GS bank method
899 int lsb, msb;
900 TrackMapRec rec = m_trackMap[track];
901 int channel = (rec.channel > -1) ? rec.channel : 0;
902 lsb = bank % 0x80;
903 msb = bank / 0x80;
904 wrkCtlChangeEvent(track, 0, channel, MIDI_CTL_MSB_BANK, msb);
905 wrkCtlChangeEvent(track, 0, channel, MIDI_CTL_LSB_BANK, lsb);
906 // qDebug() << Q_FUNC_INFO;
907 }
908
wrkEndOfFile()909 void GUIPlayer::wrkEndOfFile()
910 {
911 if (m_initialTempo < 0)
912 m_initialTempo = 120;
913 SequencerEvent* ev = new SystemEvent(SND_SEQ_EVENT_ECHO);
914 appendWRKEvent(m_tick, ev);
915 // qDebug() << Q_FUNC_INFO;
916 }
917
918 DISABLE_WARNING_POP
919