drumstick
0.5.0
|
SMF playback, graphic user interface program
/* SMF GUI Player test using the MIDI Sequencer C++ library Copyright (C) 2006-2010, Pedro Lopez-Cabanillas <plcl@users.sf.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef INCLUDED_GUIPLAYER_H #define INCLUDED_GUIPLAYER_H #include <QtGui/QMainWindow> #include <QtGui/QProgressDialog> #include <QtCore/QObject> #include <QtCore/QString> #include <QtCore/QList> #include <QtCore/QHash> #include <QtCore/QPointer> namespace drumstick { class QSmf; class QWrk; class QOve; class MidiClient; class MidiPort; class MidiQueue; class SequencerEvent; } namespace Ui { class GUIPlayerClass; } class Player; class About; class Song; using namespace drumstick; const QString QSTR_DOMAIN("drumstick.sourceforge.net"); const QString QSTR_APPNAME("GUIPlayer"); enum PlayerState { InvalidState, EmptyState, PlayingState, PausedState, StoppedState }; class GUIPlayer : public QMainWindow { Q_OBJECT public: GUIPlayer(QWidget *parent = 0, Qt::WindowFlags flags = 0); ~GUIPlayer(); void appendSMFEvent(SequencerEvent* ev); void appendWRKEvent(unsigned long ticks, SequencerEvent* ev); void appendOVEEvent(unsigned long ticks, SequencerEvent* ev); void subscribe(const QString& portName); void updateTimeLabel(int mins, int secs, int cnts); void updateTempoLabel(float ftempo); void dragEnterEvent(QDragEnterEvent* event); void dropEvent(QDropEvent* event); void closeEvent(QCloseEvent* event); bool event(QEvent* event); void openFile(const QString& fileName); void readSettings(); void writeSettings(); void updateState(PlayerState newState); void progressDialogInit(const QString& type, int max); void progressDialogUpdate(int pos); void progressDialogClose(); public slots: void about(); void aboutQt(); void play(); void pause(); void stop(); void open(); void setup(); void tempoReset(); void volumeReset(); void tempoSlider(int value); void quit(); void volumeSlider(int value); void pitchShift(int value); void songFinished(); void playerStopped(); void sequencerEvent(SequencerEvent* ev); /* SMF slots */ void smfHeaderEvent(int format, int ntrks, int division); void smfNoteOnEvent(int chan, int pitch, int vol); void smfNoteOffEvent(int chan, int pitch, int vol); void smfKeyPressEvent(int chan, int pitch, int press); void smfCtlChangeEvent(int chan, int ctl, int value); void smfPitchBendEvent(int chan, int value); void smfProgramEvent(int chan, int patch); void smfChanPressEvent(int chan, int press); void smfSysexEvent(const QByteArray& data); void smfTempoEvent(int tempo); void smfErrorHandler(const QString& errorStr); void smfUpdateLoadProgress(); /* WRK slots */ void wrkUpdateLoadProgress(); void wrkErrorHandler(const QString& errorStr); void wrkFileHeader(int verh, int verl); void wrkEndOfFile(); void wrkStreamEndEvent(long time); void wrkTrackHeader(const QString& name1, const QString& name2, int trackno, int channel, int pitch, int velocity, int port, bool selected, bool muted, bool loop); void wrkTimeBase(int timebase); void wrkNoteEvent(int track, long time, int chan, int pitch, int vol, int dur); void wrkKeyPressEvent(int track, long time, int chan, int pitch, int press); void wrkCtlChangeEvent(int track, long time, int chan, int ctl, int value); void wrkPitchBendEvent(int track, long time, int chan, int value); void wrkProgramEvent(int track, long time, int chan, int patch); void wrkChanPressEvent(int track, long time, int chan, int press); void wrkSysexEvent(int track, long time, int bank); void wrkSysexEventBank(int bank, const QString& name, bool autosend, int port, const QByteArray& data); void wrkTempoEvent(long time, int tempo); void wrkTrackPatch(int track, int patch); void wrkNewTrackHeader(const QString& name, int trackno, int channel, int pitch, int velocity, int port, bool selected, bool muted, bool loop); void wrkTrackVol(int track, int vol); void wrkTrackBank(int track, int bank); /* OVE slots */ void oveErrorHandler(const QString& errorStr); void oveFileHeader(int quarter, int trackCount); void oveNoteOnEvent(int track, long tick, int channel, int pitch, int vol); void oveNoteOffEvent(int track, long tick, int channel, int pitch, int vol); void oveTrackPatch(int track, int channel, int patch); void oveTrackVol(int track, int channel, int vol); void oveTrackBank(int track, int channel, int bank); private: int m_portId; int m_queueId; int m_initialTempo; float m_tempoFactor; unsigned long m_tick; PlayerState m_state; QSmf* m_smf; QWrk* m_wrk; QOve* m_ove; MidiClient* m_Client; MidiPort* m_Port; MidiQueue* m_Queue; Player* m_player; Ui::GUIPlayerClass* m_ui; QPointer<QProgressDialog> m_pd; QPointer<About> m_aboutDlg; Song* m_song; QString m_subscription; QString m_lastDirectory; QString m_loadingMessages; struct SysexEventRec { int track; long time; int bank; }; QList<SysexEventRec> m_savedSysexEvents; struct TrackMapRec { int channel; int pitch; int velocity; }; QHash<int,TrackMapRec> m_trackMap; }; #endif // INCLUDED_GUIPLAYER_H
/* SMF GUI Player test using the MIDI Sequencer C++ library Copyright (C) 2006-2010, Pedro Lopez-Cabanillas <plcl@users.sf.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "guiplayer.h" #include "ui_guiplayer.h" #include "playerabout.h" #include "player.h" #include "song.h" #include "qsmf.h" #include "qwrk.h" #include "qove.h" #include "alsaevent.h" #include "alsaclient.h" #include "alsaqueue.h" #include "alsaport.h" #include <QtGui/QApplication> #include <QtGui/QFileDialog> #include <QtGui/QInputDialog> #include <QtGui/QDragEnterEvent> #include <QtGui/QDropEvent> #include <QtGui/QCloseEvent> #include <QtGui/QToolTip> #include <QtGui/QMessageBox> #include <QtGui/QStatusBar> #include <QtCore/QSettings> #include <QtCore/QUrl> #include <QtCore/QFileInfo> #include <QtCore/QTextCodec> #include <qmath.h> GUIPlayer::GUIPlayer(QWidget *parent, Qt::WindowFlags flags) : QMainWindow(parent, flags), m_portId(-1), m_queueId(-1), m_initialTempo(0), m_tempoFactor(1.0), m_tick(0), m_state(InvalidState), m_smf(0), m_wrk(0), m_ove(0), m_Client(0), m_Port(0), m_Queue(0), m_player(0), m_ui(new Ui::GUIPlayerClass), m_pd(0), m_aboutDlg(0), m_song(new Song) { m_ui->setupUi(this); setAcceptDrops(true); connect(m_ui->actionAbout, SIGNAL(triggered()), SLOT(about())); connect(m_ui->actionAboutQt, SIGNAL(triggered()), SLOT(aboutQt())); connect(m_ui->actionPlay, SIGNAL(triggered()), SLOT(play())); connect(m_ui->actionPause, SIGNAL(triggered()), SLOT(pause())); connect(m_ui->actionStop, SIGNAL(triggered()), SLOT(stop())); connect(m_ui->actionOpen, SIGNAL(triggered()), SLOT(open())); connect(m_ui->actionMIDISetup, SIGNAL(triggered()), SLOT(setup())); connect(m_ui->actionQuit, SIGNAL(triggered()), SLOT(quit())); connect(m_ui->btnTempo, SIGNAL(clicked()), SLOT(tempoReset())); connect(m_ui->btnVolume, SIGNAL(clicked()), SLOT(volumeReset())); connect(m_ui->sliderTempo, SIGNAL(valueChanged(int)), SLOT(tempoSlider(int))); connect(m_ui->volumeSlider, SIGNAL(valueChanged(int)), SLOT(volumeSlider(int))); connect(m_ui->spinPitch, SIGNAL(valueChanged(int)), SLOT(pitchShift(int))); connect(m_ui->toolBar->toggleViewAction(), SIGNAL(toggled(bool)), m_ui->actionShowToolbar, SLOT(setChecked(bool))); m_ui->actionPlay->setShortcut( Qt::Key_MediaPlay ); m_ui->actionStop->setShortcut( Qt::Key_MediaStop ); m_Client = new MidiClient(this); m_Client->open(); m_Client->setPoolOutput(50); // small size, for near real-time pitchShift m_Client->setClientName("MIDI Player"); connect( m_Client, SIGNAL(eventReceived(SequencerEvent*)), SLOT(sequencerEvent(SequencerEvent*)), Qt::QueuedConnection ); m_Port = new MidiPort(this); m_Port->attach( m_Client ); m_Port->setPortName("MIDI Player Output Port"); m_Port->setCapability( SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ | SND_SEQ_PORT_CAP_WRITE ); m_Port->setPortType( SND_SEQ_PORT_TYPE_APPLICATION | SND_SEQ_PORT_TYPE_MIDI_GENERIC ); m_Queue = m_Client->createQueue(QSTR_APPNAME); m_queueId = m_Queue->getId(); m_portId = m_Port->getPortId(); m_smf = new QSmf(this); connect(m_smf, SIGNAL(signalSMFHeader(int,int,int)), SLOT(smfHeaderEvent(int,int,int))); connect(m_smf, SIGNAL(signalSMFNoteOn(int,int,int)), SLOT(smfNoteOnEvent(int,int,int))); connect(m_smf, SIGNAL(signalSMFNoteOff(int,int,int)), SLOT(smfNoteOffEvent(int,int,int))); connect(m_smf, SIGNAL(signalSMFKeyPress(int,int,int)), SLOT(smfKeyPressEvent(int,int,int))); connect(m_smf, SIGNAL(signalSMFCtlChange(int,int,int)), SLOT(smfCtlChangeEvent(int,int,int))); connect(m_smf, SIGNAL(signalSMFPitchBend(int,int)), SLOT(smfPitchBendEvent(int,int))); connect(m_smf, SIGNAL(signalSMFProgram(int,int)), SLOT(smfProgramEvent(int,int))); connect(m_smf, SIGNAL(signalSMFChanPress(int,int)), SLOT(smfChanPressEvent(int,int))); connect(m_smf, SIGNAL(signalSMFSysex(const QByteArray&)), SLOT(smfSysexEvent(const QByteArray&))); connect(m_smf, SIGNAL(signalSMFText(int,const QString&)), SLOT(smfUpdateLoadProgress())); connect(m_smf, SIGNAL(signalSMFTempo(int)), SLOT(smfTempoEvent(int))); connect(m_smf, SIGNAL(signalSMFTrackStart()), SLOT(smfUpdateLoadProgress())); connect(m_smf, SIGNAL(signalSMFTrackEnd()), SLOT(smfUpdateLoadProgress())); connect(m_smf, SIGNAL(signalSMFendOfTrack()), SLOT(smfUpdateLoadProgress())); connect(m_smf, SIGNAL(signalSMFError(const QString&)), SLOT(smfErrorHandler(const QString&))); m_wrk = new QWrk(this); connect(m_wrk, SIGNAL(signalWRKError(const QString&)), SLOT(wrkErrorHandler(const QString&))); connect(m_wrk, SIGNAL(signalWRKUnknownChunk(int,const QByteArray&)), SLOT(wrkUpdateLoadProgress())); connect(m_wrk, SIGNAL(signalWRKHeader(int,int)), SLOT(wrkFileHeader(int,int))); connect(m_wrk, SIGNAL(signalWRKEnd()), SLOT(wrkEndOfFile())); connect(m_wrk, SIGNAL(signalWRKStreamEnd(long)), SLOT(wrkStreamEndEvent(long))); connect(m_wrk, SIGNAL(signalWRKGlobalVars()), SLOT(wrkUpdateLoadProgress())); connect(m_wrk, SIGNAL(signalWRKTrack(const QString&, const QString&, int,int,int,int,int,bool,bool,bool)), SLOT(wrkTrackHeader(const QString&, const QString&, int,int,int,int,int,bool,bool,bool))); connect(m_wrk, SIGNAL(signalWRKTimeBase(int)), SLOT(wrkTimeBase(int))); connect(m_wrk, SIGNAL(signalWRKNote(int,long,int,int,int,int)), SLOT(wrkNoteEvent(int,long,int,int,int,int))); connect(m_wrk, SIGNAL(signalWRKKeyPress(int,long,int,int,int)), SLOT(wrkKeyPressEvent(int,long,int,int,int))); connect(m_wrk, SIGNAL(signalWRKCtlChange(int,long,int,int,int)), SLOT(wrkCtlChangeEvent(int,long,int,int,int))); connect(m_wrk, SIGNAL(signalWRKPitchBend(int,long,int,int)), SLOT(wrkPitchBendEvent(int,long,int,int))); connect(m_wrk, SIGNAL(signalWRKProgram(int,long,int,int)), SLOT(wrkProgramEvent(int,long,int,int))); connect(m_wrk, SIGNAL(signalWRKChanPress(int,long,int,int)), SLOT(wrkChanPressEvent(int,long,int,int))); connect(m_wrk, SIGNAL(signalWRKSysexEvent(int,long,int)), SLOT(wrkSysexEvent(int,long,int))); connect(m_wrk, SIGNAL(signalWRKSysex(int,const QString&,bool,int,const QByteArray&)), SLOT(wrkSysexEventBank(int,const QString&,bool,int,const QByteArray&))); connect(m_wrk, SIGNAL(signalWRKText(int,long,int,const QString&)), SLOT(wrkUpdateLoadProgress())); connect(m_wrk, SIGNAL(signalWRKTimeSig(int,int,int)), SLOT(wrkUpdateLoadProgress())); connect(m_wrk, SIGNAL(signalWRKKeySig(int,int)), SLOT(wrkUpdateLoadProgress())); connect(m_wrk, SIGNAL(signalWRKTempo(long,int)), SLOT(wrkTempoEvent(long,int))); connect(m_wrk, SIGNAL(signalWRKTrackPatch(int,int)), SLOT(wrkTrackPatch(int,int))); connect(m_wrk, SIGNAL(signalWRKComments(const QString&)), SLOT(wrkUpdateLoadProgress())); connect(m_wrk, SIGNAL(signalWRKVariableRecord(const QString&,const QByteArray&)), SLOT(wrkUpdateLoadProgress())); connect(m_wrk, SIGNAL(signalWRKNewTrack(const QString&,int,int,int,int,int,bool,bool,bool)), SLOT(wrkNewTrackHeader(const QString&,int,int,int,int,int,bool,bool,bool))); connect(m_wrk, SIGNAL(signalWRKTrackName(int,const QString&)), SLOT(wrkUpdateLoadProgress())); connect(m_wrk, SIGNAL(signalWRKTrackVol(int,int)), SLOT(wrkTrackVol(int,int))); connect(m_wrk, SIGNAL(signalWRKTrackBank(int,int)), SLOT(wrkTrackBank(int,int))); connect(m_wrk, SIGNAL(signalWRKSegment(int,long,const QString&)), SLOT(wrkUpdateLoadProgress())); connect(m_wrk, SIGNAL(signalWRKChord(int,long,const QString&,const QByteArray&)), SLOT(wrkUpdateLoadProgress())); connect(m_wrk, SIGNAL(signalWRKExpression(int,long,int,const QString&)), SLOT(wrkUpdateLoadProgress())); m_ove = new QOve(this); connect(m_ove, SIGNAL(signalOVEError(const QString&)), SLOT(oveErrorHandler(const QString&))); connect(m_ove, SIGNAL(signalOVEHeader(int,int)), SLOT(oveFileHeader(int,int))); connect(m_ove, SIGNAL(signalOVEEnd()), SLOT(wrkEndOfFile())); connect(m_ove, SIGNAL(signalOVENoteOn(int, long, int, int, int)), SLOT(oveNoteOnEvent(int, long, int, int, int))); connect(m_ove, SIGNAL(signalOVENoteOff(int, long, int, int, int)), SLOT(oveNoteOffEvent(int, long, int, int, int))); connect(m_ove, SIGNAL(signalOVEKeyPress(int,long,int,int,int)), SLOT(wrkKeyPressEvent(int,long,int,int,int))); connect(m_ove, SIGNAL(signalOVECtlChange(int,long,int,int,int)), SLOT(wrkCtlChangeEvent(int,long,int,int,int))); connect(m_ove, SIGNAL(signalOVEPitchBend(int,long,int,int)), SLOT(wrkPitchBendEvent(int,long,int,int))); connect(m_ove, SIGNAL(signalOVEProgram(int,long,int,int)), SLOT(wrkProgramEvent(int,long,int,int))); connect(m_ove, SIGNAL(signalOVEChanPress(int,long,int,int)), SLOT(wrkChanPressEvent(int,long,int,int))); connect(m_ove, SIGNAL(signalOVESysexEvent(int,long,int)), SLOT(wrkSysexEvent(int,long,int))); connect(m_ove, SIGNAL(signalOVESysex(int,const QString&,bool,int,const QByteArray&)), SLOT(wrkSysexEventBank(int,const QString&,bool,int,const QByteArray&))); connect(m_ove, SIGNAL(signalOVETempo(long,int)), SLOT(wrkTempoEvent(long,int))); connect(m_ove, SIGNAL(signalOVETrackPatch(int,int,int)), SLOT(oveTrackPatch(int,int,int))); connect(m_ove, SIGNAL(signalOVENewTrack(const QString&,int,int,int,int,int,bool,bool,bool)), SLOT(wrkNewTrackHeader(const QString&,int,int,int,int,int,bool,bool,bool))); connect(m_ove, SIGNAL(signalOVETrackVol(int,int,int)), SLOT(wrkTrackVol(int,int))); connect(m_ove, SIGNAL(signalOVETrackBank(int,int,int)), SLOT(oveTrackBank(int,int,int))); m_player = new Player(m_Client, m_portId); connect(m_player, SIGNAL(finished()), SLOT(songFinished())); connect(m_player, SIGNAL(stopped()), SLOT(playerStopped())); m_Client->setRealTimeInput(false); m_Client->startSequencerInput(); tempoReset(); volumeReset(); updateState(EmptyState); } GUIPlayer::~GUIPlayer() { m_Client->stopSequencerInput(); m_Port->detach(); m_Client->close(); delete m_player; } void GUIPlayer::subscribe(const QString& portName) { try { if (!m_subscription.isEmpty()) { m_Port->unsubscribeTo(m_subscription); } m_subscription = portName; m_Port->subscribeTo(m_subscription); } catch (const SequencerError& err) { qWarning() << "SequencerError exception. Error code: " << err.code() << " (" << err.qstrError() << ")"; qWarning() << "Location: " << err.location(); } } void GUIPlayer::updateTimeLabel(int mins, int secs, int cnts) { static QChar fill('0'); QString stime = QString("%1:%2.%3").arg(mins,2,10,fill) .arg(secs,2,10,fill) .arg(cnts,2,10,fill); m_ui->lblTime->setText(stime); } void GUIPlayer::updateState(PlayerState newState) { if (m_state == newState) return; switch (newState) { case EmptyState: m_ui->actionPlay->setEnabled(false); m_ui->actionPause->setEnabled(false); m_ui->actionStop->setEnabled(false); statusBar()->showMessage("Please, load a song"); break; case PlayingState: m_ui->actionPlay->setEnabled(false); m_ui->actionPause->setEnabled(true); m_ui->actionStop->setEnabled(true); statusBar()->showMessage("Playing"); break; case PausedState: m_ui->actionPlay->setEnabled(false); m_ui->actionStop->setEnabled(true); statusBar()->showMessage("Paused"); break; case StoppedState: m_ui->actionPause->setChecked(false); m_ui->actionPause->setEnabled(false); m_ui->actionStop->setEnabled(false); m_ui->actionPlay->setEnabled(true); statusBar()->showMessage("Stopped"); break; default: statusBar()->showMessage("Not initialized"); break; } m_state = newState; } void GUIPlayer::play() { if (!m_song->isEmpty()) { if (m_player->getInitialPosition() == 0) { if (m_initialTempo == 0) return; QueueTempo firstTempo = m_Queue->getTempo(); firstTempo.setPPQ(m_song->getDivision()); firstTempo.setTempo(m_initialTempo); firstTempo.setTempoFactor(m_tempoFactor); m_Queue->setTempo(firstTempo); m_Client->drainOutput(); m_player->sendVolumeEvents(); } m_player->start(); updateState(PlayingState); } } void GUIPlayer::pause() { if (m_state == PlayingState || m_player->isRunning()) { m_player->stop(); m_player->setPosition(m_Queue->getStatus().getTickTime()); updateState(PausedState); } else if (!m_song->isEmpty()) { m_player->start(); updateState(PlayingState); } } void GUIPlayer::stop() { if (m_state == PlayingState || m_state == PausedState || m_player->isRunning()) m_player->stop(); if (m_initialTempo != 0) songFinished(); else updateState(StoppedState); } void GUIPlayer::progressDialogInit(const QString& type, int max) { m_pd = new QProgressDialog(0, 0, 0, max, this); m_pd->setWindowTitle(QString("Loading %1 file...").arg(type)); m_pd->setMinimumDuration(1000); m_pd->setValue(0); } void GUIPlayer::progressDialogUpdate(int pos) { if (m_pd != 0) { m_pd->setValue(pos); qApp->processEvents(); } } void GUIPlayer::progressDialogClose() { delete m_pd; // set to 0 by QPointer<> } void GUIPlayer::openFile(const QString& fileName) { QFileInfo finfo(fileName); if (finfo.exists()) { m_song->clear(); m_loadingMessages.clear(); m_tick = 0; m_initialTempo = 0; try { QString ext = finfo.suffix().toLower(); if (ext == "wrk") { progressDialogInit("Cakewalk", finfo.size()); m_wrk->readFromFile(fileName); } else if (ext == "mid" || ext == "midi" || ext == "kar") { progressDialogInit("MIDI", finfo.size()); m_smf->readFromFile(fileName); } else if (ext == "ove") { m_ove->readFromFile(fileName); } progressDialogUpdate(finfo.size()); if (m_song->isEmpty()) { m_ui->lblName->clear(); } else { m_song->sort(); m_player->setSong(m_song); m_ui->lblName->setText(finfo.fileName()); m_lastDirectory = finfo.absolutePath(); } } catch (...) { m_song->clear(); m_ui->lblName->clear(); } progressDialogClose(); if (m_initialTempo == 0) { m_initialTempo = 500000; } updateTimeLabel(0,0,0); updateTempoLabel(6.0e7f / m_initialTempo); m_ui->progressBar->setValue(0); if (!m_loadingMessages.isEmpty()) { m_loadingMessages.insert(0, "Warning, this file may be non-standard or damaged.<br>"); QMessageBox::warning(this, QSTR_APPNAME, m_loadingMessages); } if (m_song->isEmpty()) updateState(EmptyState); else updateState(StoppedState); } } void GUIPlayer::open() { QString fileName = QFileDialog::getOpenFileName(this, "Open MIDI File", m_lastDirectory, "All files (*.kar *.mid *.midi *.ove *.wrk);;" "Karaoke files (*.kar);;" "MIDI Files (*.mid *.midi);;" "Overture Files (*.ove);;" "Cakewalk files (*.wrk)" ); if (! fileName.isEmpty() ) { stop(); openFile(fileName); } } void GUIPlayer::setup() { bool ok; int current; QStringList items; QListIterator<PortInfo> it(m_Client->getAvailableOutputs()); while(it.hasNext()) { PortInfo p = it.next(); items << QString("%1:%2").arg(p.getClientName()).arg(p.getPort()); } current = items.indexOf(m_subscription); QString item = QInputDialog::getItem(this, "Player subscription", "Output port:", items, current, false, &ok); if (ok && !item.isEmpty()) subscribe(item); } void GUIPlayer::songFinished() { m_player->resetPosition(); updateState(StoppedState); } void GUIPlayer::playerStopped() { int portId = m_Port->getPortId(); for (int channel = 0; channel < 16; ++channel) { ControllerEvent ev1(channel, MIDI_CTL_ALL_NOTES_OFF, 0); ev1.setSource(portId); ev1.setSubscribers(); ev1.setDirect(); m_Client->outputDirect(&ev1); ControllerEvent ev2(channel, MIDI_CTL_ALL_SOUNDS_OFF, 0); ev2.setSource(portId); ev2.setSubscribers(); ev2.setDirect(); m_Client->outputDirect(&ev2); } m_Client->drainOutput(); } void GUIPlayer::updateTempoLabel(float ftempo) { QString stempo = QString("%1 bpm").arg(ftempo, 0, 'f', 2); m_ui->lblOther->setText(stempo); } void GUIPlayer::sequencerEvent(SequencerEvent *ev) { if ((ev->getSequencerType() == SND_SEQ_EVENT_ECHO) && (m_tick != 0)){ int pos = 100 * ev->getTick() / m_tick; const snd_seq_real_time_t* rt = m_Queue->getStatus().getRealtime(); int mins = rt->tv_sec / 60; int secs = rt->tv_sec % 60; int cnts = qFloor( rt->tv_nsec / 1.0e7 ); updateTempoLabel(m_Queue->getTempo().getRealBPM()); updateTimeLabel(mins, secs, cnts); m_ui->progressBar->setValue(pos); } delete ev; } void GUIPlayer::pitchShift(int value) { m_player->setPitchShift(value); } void GUIPlayer::tempoReset() { m_ui->sliderTempo->setValue(100); tempoSlider(100); } void GUIPlayer::volumeReset() { m_ui->volumeSlider->setValue(100); volumeSlider(100); } void GUIPlayer::tempoSlider(int value) { m_tempoFactor = (value*value + 100.0*value + 20000.0) / 40000.0; QueueTempo qtempo = m_Queue->getTempo(); qtempo.setTempoFactor(m_tempoFactor); m_Queue->setTempo(qtempo); m_Client->drainOutput(); if (!m_player->isRunning()) updateTempoLabel(qtempo.getRealBPM()); // Slider tooltip QString tip = QString("%1\%").arg(m_tempoFactor*100.0, 0, 'f', 0); m_ui->sliderTempo->setToolTip(tip); QToolTip::showText(QCursor::pos(), tip, this); } void GUIPlayer::volumeSlider(int value) { QString tip = QString::number(value)+'%'; m_ui->lblVolume->setText(tip); m_ui->volumeSlider->setToolTip(tip); m_player->setVolumeFactor(value); QToolTip::showText(QCursor::pos(), tip, this); } void GUIPlayer::dragEnterEvent( QDragEnterEvent * event ) { if (event->mimeData()->hasFormat("text/uri-list")) event->acceptProposedAction(); } void GUIPlayer::dropEvent( QDropEvent * event ) { QString data = event->mimeData()->text(); QString fileName = QUrl(data).path().trimmed(); while (fileName.endsWith(QChar::Null)) fileName.chop(1); if ( fileName.endsWith(".ove", Qt::CaseInsensitive) || fileName.endsWith(".mid", Qt::CaseInsensitive) || fileName.endsWith(".midi", Qt::CaseInsensitive) || fileName.endsWith(".kar", Qt::CaseInsensitive) || fileName.endsWith(".wrk", Qt::CaseInsensitive) ) { stop(); openFile(fileName); event->accept(); } else { QMessageBox::warning(this, QSTR_APPNAME, QString("Dropped file %1 is not supported").arg(fileName)); } } bool GUIPlayer::event( QEvent * event ) { if(event->type() == QEvent::Polish) { readSettings(); /* Process the command line arguments. The first argument should be a MIDI file name */ QStringList args = QCoreApplication::arguments(); if (args.size() > 1) { QString first = args.at(1); openFile(first); } event->accept(); } return QMainWindow::event(event); } void GUIPlayer::readSettings() { QSettings settings; settings.beginGroup("Window"); restoreGeometry(settings.value("Geometry").toByteArray()); restoreState(settings.value("State").toByteArray()); settings.endGroup(); settings.beginGroup("Preferences"); m_lastDirectory = settings.value("LastDirectory").toString(); QString midiConn = settings.value("MIDIConnection").toString(); settings.endGroup(); if (midiConn.length() > 0) subscribe(midiConn); } void GUIPlayer::writeSettings() { QSettings settings; settings.beginGroup("Window"); settings.setValue("Geometry", saveGeometry()); settings.setValue("State", saveState()); settings.endGroup(); settings.beginGroup("Preferences"); settings.setValue("LastDirectory", m_lastDirectory); settings.setValue("MIDIConnection", m_subscription); settings.endGroup(); } void GUIPlayer::closeEvent( QCloseEvent *event ) { writeSettings(); event->accept(); } void GUIPlayer::about() { if (m_aboutDlg == 0) m_aboutDlg = new About(this); m_aboutDlg->exec(); } void GUIPlayer::aboutQt() { qApp->aboutQt(); } void GUIPlayer::quit() { stop(); m_player->wait(); close(); } /* **************************************** * * SMF (Standard MIDI file) format handling * **************************************** */ void GUIPlayer::smfUpdateLoadProgress() { progressDialogUpdate(m_smf->getFilePos()); } void GUIPlayer::appendSMFEvent(SequencerEvent* ev) { unsigned long tick = m_smf->getCurrentTime(); ev->setSource(m_portId); if (ev->getSequencerType() != SND_SEQ_EVENT_TEMPO) { ev->setSubscribers(); } ev->scheduleTick(m_queueId, tick, false); m_song->append(ev); if (tick > m_tick) m_tick = tick; smfUpdateLoadProgress(); } void GUIPlayer::smfHeaderEvent(int format, int ntrks, int division) { m_song->setHeader(format, ntrks, division); smfUpdateLoadProgress(); } void GUIPlayer::smfNoteOnEvent(int chan, int pitch, int vol) { SequencerEvent* ev = new NoteOnEvent (chan, pitch, vol); appendSMFEvent(ev); } void GUIPlayer::smfNoteOffEvent(int chan, int pitch, int vol) { SequencerEvent* ev = new NoteOffEvent (chan, pitch, vol); appendSMFEvent(ev); } void GUIPlayer::smfKeyPressEvent(int chan, int pitch, int press) { SequencerEvent* ev = new KeyPressEvent (chan, pitch, press); appendSMFEvent(ev); } void GUIPlayer::smfCtlChangeEvent(int chan, int ctl, int value) { SequencerEvent* ev = new ControllerEvent (chan, ctl, value); appendSMFEvent(ev); } void GUIPlayer::smfPitchBendEvent(int chan, int value) { SequencerEvent* ev = new PitchBendEvent (chan, value); appendSMFEvent(ev); } void GUIPlayer::smfProgramEvent(int chan, int patch) { SequencerEvent* ev = new ProgramChangeEvent (chan, patch); appendSMFEvent(ev); } void GUIPlayer::smfChanPressEvent(int chan, int press) { SequencerEvent* ev = new ChanPressEvent (chan, press); appendSMFEvent(ev); } void GUIPlayer::smfSysexEvent(const QByteArray& data) { SequencerEvent* ev = new SysExEvent (data); appendSMFEvent(ev); } void GUIPlayer::smfTempoEvent(int tempo) { if ( m_initialTempo == 0 ) { m_initialTempo = tempo; } SequencerEvent* ev = new TempoEvent (m_queueId, tempo); appendSMFEvent(ev); } void GUIPlayer::smfErrorHandler(const QString& errorStr) { if (m_loadingMessages.length() < 1024) m_loadingMessages.append(QString("%1 at file offset %2<br>") .arg(errorStr).arg(m_smf->getFilePos())); } /* ********************************* * * Cakewalk WRK file format handling * ********************************* */ void GUIPlayer::wrkUpdateLoadProgress() { if (m_pd != 0) progressDialogUpdate(m_wrk->getFilePos()); } void GUIPlayer::appendWRKEvent(unsigned long ticks, SequencerEvent* ev) { ev->setSource(m_portId); if (ev->getSequencerType() != SND_SEQ_EVENT_TEMPO) { ev->setSubscribers(); } ev->scheduleTick(m_queueId, ticks, false); m_song->append(ev); if (ticks > m_tick) m_tick = ticks; wrkUpdateLoadProgress(); } void GUIPlayer::wrkErrorHandler(const QString& errorStr) { if (m_loadingMessages.length() < 1024) m_loadingMessages.append(QString("%1 at file offset %2<br>") .arg(errorStr).arg(m_wrk->getFilePos())); } void GUIPlayer::wrkFileHeader(int /*verh*/, int /*verl*/) { m_song->setHeader(1, 0, 120); wrkUpdateLoadProgress(); } void GUIPlayer::wrkTimeBase(int timebase) { m_song->setDivision(timebase); wrkUpdateLoadProgress(); } void GUIPlayer::wrkStreamEndEvent(long time) { unsigned long ticks = time; if (ticks > m_tick) m_tick = ticks; wrkUpdateLoadProgress(); } void GUIPlayer::wrkTrackHeader( const QString& /*name1*/, const QString& /*name2*/, int trackno, int channel, int pitch, int velocity, int /*port*/, bool /*selected*/, bool /*muted*/, bool /*loop*/ ) { TrackMapRec rec; rec.channel = channel; rec.pitch = pitch; rec.velocity = velocity; m_trackMap[trackno] = rec; wrkUpdateLoadProgress(); } void GUIPlayer::wrkNoteEvent(int track, long time, int chan, int pitch, int vol, int dur) { int channel = chan; TrackMapRec rec = m_trackMap[track]; int key = pitch + rec.pitch; int velocity = vol + rec.velocity; if (rec.channel > -1) channel = rec.channel; SequencerEvent* ev = new NoteEvent(channel, key, velocity, dur); appendWRKEvent(time, ev); } void GUIPlayer::wrkKeyPressEvent(int track, long time, int chan, int pitch, int press) { int channel = chan; TrackMapRec rec = m_trackMap[track]; int key = pitch + rec.pitch; if (rec.channel > -1) channel = rec.channel; SequencerEvent* ev = new KeyPressEvent(channel, key, press); appendWRKEvent(time, ev); } void GUIPlayer::wrkCtlChangeEvent(int track, long time, int chan, int ctl, int value) { int channel = chan; TrackMapRec rec = m_trackMap[track]; if (rec.channel > -1) channel = rec.channel; SequencerEvent* ev = new ControllerEvent(channel, ctl, value); appendWRKEvent(time, ev); } void GUIPlayer::wrkPitchBendEvent(int track, long time, int chan, int value) { int channel = chan; TrackMapRec rec = m_trackMap[track]; if (rec.channel > -1) channel = rec.channel; SequencerEvent* ev = new PitchBendEvent(channel, value); appendWRKEvent(time, ev); } void GUIPlayer::wrkProgramEvent(int track, long time, int chan, int patch) { int channel = chan; TrackMapRec rec = m_trackMap[track]; if (rec.channel > -1) channel = rec.channel; SequencerEvent* ev = new ProgramChangeEvent(channel, patch); appendWRKEvent(time, ev); } void GUIPlayer::wrkChanPressEvent(int track, long time, int chan, int press) { int channel = chan; TrackMapRec rec = m_trackMap[track]; if (rec.channel > -1) channel = rec.channel; SequencerEvent* ev = new ChanPressEvent(channel, press); appendWRKEvent(time, ev); } void GUIPlayer::wrkSysexEvent(int track, long time, int bank) { SysexEventRec rec; rec.track = track; rec.time = time; rec.bank = bank; m_savedSysexEvents.append(rec); wrkUpdateLoadProgress(); } void GUIPlayer::wrkSysexEventBank(int bank, const QString& /*name*/, bool autosend, int /*port*/, const QByteArray& data) { SysExEvent* ev = new SysExEvent(data); if (autosend) appendWRKEvent(0, ev->clone()); foreach(const SysexEventRec& rec, m_savedSysexEvents) { if (rec.bank == bank) { appendWRKEvent(rec.time, ev->clone()); } } delete ev; wrkUpdateLoadProgress(); } void GUIPlayer::wrkTempoEvent(long time, int tempo) { double bpm = tempo / 100.0; if ( m_initialTempo < 0 ) m_initialTempo = qRound( bpm ); SequencerEvent* ev = new TempoEvent(m_queueId, qRound ( 6e7 / bpm ) ); appendWRKEvent(time, ev); } void GUIPlayer::wrkTrackPatch(int track, int patch) { int channel = 0; TrackMapRec rec = m_trackMap[track]; if (rec.channel > -1) channel = rec.channel; wrkProgramEvent(track, 0, channel, patch); } void GUIPlayer::wrkNewTrackHeader( const QString& /*name*/, int trackno, int channel, int pitch, int velocity, int /*port*/, bool /*selected*/, bool /*muted*/, bool /*loop*/ ) { TrackMapRec rec; rec.channel = channel; rec.pitch = pitch; rec.velocity = velocity; m_trackMap[trackno] = rec; wrkUpdateLoadProgress(); } void GUIPlayer::wrkTrackVol(int track, int vol) { int channel = 0; int lsb, msb; TrackMapRec rec = m_trackMap[track]; if (rec.channel > -1) channel = rec.channel; if (vol < 128) wrkCtlChangeEvent(track, 0, channel, MIDI_CTL_MSB_MAIN_VOLUME, vol); else { lsb = vol % 0x80; msb = vol / 0x80; wrkCtlChangeEvent(track, 0, channel, MIDI_CTL_LSB_MAIN_VOLUME, lsb); wrkCtlChangeEvent(track, 0, channel, MIDI_CTL_MSB_MAIN_VOLUME, msb); } } void GUIPlayer::wrkTrackBank(int track, int bank) { // assume GM/GS bank method int channel = 0; int lsb, msb; TrackMapRec rec = m_trackMap[track]; if (rec.channel > -1) channel = rec.channel; lsb = bank % 0x80; msb = bank / 0x80; wrkCtlChangeEvent(track, 0, channel, MIDI_CTL_MSB_BANK, msb); wrkCtlChangeEvent(track, 0, channel, MIDI_CTL_LSB_BANK, lsb); } void GUIPlayer::wrkEndOfFile() { if (m_initialTempo < 0) m_initialTempo = 120; SequencerEvent* ev = new SystemEvent(SND_SEQ_EVENT_ECHO); appendWRKEvent(m_tick, ev); } /* ********************************* * * Overture OVE file format handling * ********************************* */ void GUIPlayer::appendOVEEvent(unsigned long ticks, SequencerEvent* ev) { ev->setSource(m_portId); if (ev->getSequencerType() != SND_SEQ_EVENT_TEMPO) ev->setSubscribers(); ev->scheduleTick(m_queueId, ticks, false); m_song->append(ev); if (ticks > m_tick) m_tick = ticks; } void GUIPlayer::oveErrorHandler(const QString& errorStr) { if (m_loadingMessages.length() < 1024) m_loadingMessages.append(errorStr); } void GUIPlayer::oveFileHeader(int quarter, int trackCount) { m_song->setHeader(1, trackCount, quarter); } void GUIPlayer::oveNoteOnEvent(int /*track*/, long tick, int channel, int pitch, int vol) { SequencerEvent* ev = new NoteOnEvent(channel, pitch, vol); appendOVEEvent(tick, ev); } void GUIPlayer::oveNoteOffEvent(int /*track*/, long tick, int channel, int pitch, int vol) { SequencerEvent* ev = new NoteOffEvent(channel, pitch, vol); appendOVEEvent(tick, ev); } void GUIPlayer::oveTrackPatch(int track, int channel, int patch) { int ch = channel; TrackMapRec rec = m_trackMap[track]; if (rec.channel > -1) ch = rec.channel; wrkProgramEvent(track, 0, ch, patch); } void GUIPlayer::oveTrackVol(int track, int channel, int vol) { int ch = channel; int lsb, msb; TrackMapRec rec = m_trackMap[track]; if (rec.channel > -1) ch = rec.channel; if (vol < 128) wrkCtlChangeEvent(track, 0, ch, MIDI_CTL_MSB_MAIN_VOLUME, vol); else { lsb = vol % 0x80; msb = vol / 0x80; wrkCtlChangeEvent(track, 0, ch, MIDI_CTL_LSB_MAIN_VOLUME, lsb); wrkCtlChangeEvent(track, 0, ch, MIDI_CTL_MSB_MAIN_VOLUME, msb); } } void GUIPlayer::oveTrackBank(int track, int channel, int bank) { // assume GM/GS bank method int ch = channel; int lsb, msb; TrackMapRec rec = m_trackMap[track]; if (rec.channel > -1) ch = rec.channel; lsb = bank % 0x80; msb = bank / 0x80; wrkCtlChangeEvent(track, 0, ch, MIDI_CTL_MSB_BANK, msb); wrkCtlChangeEvent(track, 0, ch, MIDI_CTL_LSB_BANK, lsb); }