drumstick  1.1.0
dumpmid.cpp

Print received sequencer events

/*
MIDI Sequencer C++ library
Copyright (C) 2006-2016, Pedro Lopez-Cabanillas <plcl@users.sf.net>
This library 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 library 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 DUMPMIDI_H_
#define DUMPMIDI_H_
/* MidiClient can deliver SequencerEvents with only
* signals or posting QEvents to the QApplication loop */
#undef USE_QEVENTS
//#define USE_QEVENTS
/* Tp get timestamped events from ALSA, you need a running queue */
//#undef WANT_TIMESTAMPS
#define WANT_TIMESTAMPS
#include <QObject>
#include <QReadWriteLock>
#include "alsaevent.h"
#include "alsaclient.h"
#include "alsaport.h"
#include "alsaqueue.h"
#include "subscription.h"
using namespace drumstick;
class QDumpMIDI : public QObject
{
Q_OBJECT
public:
QDumpMIDI();
virtual ~QDumpMIDI();
void dumpEvent(SequencerEvent* ev);
void subscribe(const QString& portName);
void stop();
bool stopped();
void run();
public slots:
void subscription(MidiPort* port, Subscription* subs);
#ifdef USE_QEVENTS
protected:
virtual void customEvent( QEvent *ev );
#else
void sequencerEvent( SequencerEvent* ev );
#endif
private:
MidiClient* m_Client;
MidiPort* m_Port;
#ifdef WANT_TIMESTAMPS
MidiQueue* m_Queue;
#endif
bool m_Stopped;
QReadWriteLock m_mutex;
};
#endif /*DUMPMIDI_H_*/
/*
MIDI Sequencer C++ library
Copyright (C) 2006-2016, Pedro Lopez-Cabanillas <plcl@users.sf.net>
This library 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 library 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 "dumpmid.h"
#include "cmdlineargs.h"
#include <signal.h>
#include <QObject>
#include <QString>
#include <QCoreApplication>
#include <QTextStream>
#include <QtDebug>
#include <QReadLocker>
#include <QWriteLocker>
static QTextStream cout(stdout, QIODevice::WriteOnly);
static QTextStream cerr(stderr, QIODevice::WriteOnly);
QDumpMIDI::QDumpMIDI()
: QObject()
{
m_Client = new MidiClient(this);
m_Client->open();
m_Client->setClientName("DumpMIDI");
#ifndef USE_QEVENTS // using signals instead
connect( m_Client, SIGNAL(eventReceived(SequencerEvent*)),
SLOT(sequencerEvent(SequencerEvent*)),
Qt::DirectConnection );
#endif
m_Port = new MidiPort(this);
m_Port->attach( m_Client );
m_Port->setPortName("DumpMIDI port");
m_Port->setCapability( SND_SEQ_PORT_CAP_WRITE |
SND_SEQ_PORT_CAP_SUBS_WRITE );
m_Port->setPortType( SND_SEQ_PORT_TYPE_APPLICATION |
SND_SEQ_PORT_TYPE_MIDI_GENERIC );
#ifdef WANT_TIMESTAMPS
m_Queue = m_Client->createQueue("DumpMIDI");
m_Port->setTimestamping(true);
//m_Port->setTimestampReal(true);
m_Port->setTimestampQueue(m_Queue->getId());
#endif
connect( m_Port, SIGNAL(subscribed(MidiPort*,Subscription*)),
SLOT(subscription(MidiPort*,Subscription*)));
qDebug() << "Trying to subscribe from Announce";
m_Port->subscribeFromAnnounce();
}
QDumpMIDI::~QDumpMIDI()
{
m_Port->detach();
delete m_Port;
m_Client->close();
delete m_Client;
}
bool
QDumpMIDI::stopped()
{
QReadLocker locker(&m_mutex);
return m_Stopped;
}
void
QDumpMIDI::stop()
{
QWriteLocker locker(&m_mutex);
m_Stopped = true;
}
void
QDumpMIDI::subscription(MidiPort*, Subscription* subs)
{
qDebug() << "Subscription made from"
<< subs->getSender()->client << ":"
<< subs->getSender()->port;
}
void QDumpMIDI::subscribe(const QString& portName)
{
try {
qDebug() << "Trying to subscribe" << portName.toLocal8Bit().data();
m_Port->subscribeFrom(portName);
} catch (const SequencerError& err) {
cerr << "SequencerError exception. Error code: " << err.code()
<< " (" << err.qstrError() << ")" << endl;
cerr << "Location: " << err.location() << endl;
throw err;
}
}
void QDumpMIDI::run()
{
cout << "Press Ctrl+C to exit" << endl;
#ifdef WANT_TIMESTAMPS
cout << "___Ticks ";
#endif
cout << "Source_ Event_________________ Ch _Data__" << endl;
try {
#ifdef USE_QEVENTS
m_Client->addListener(this);
m_Client->setEventsEnabled(true);
#endif
m_Client->setRealTimeInput(false);
m_Client->startSequencerInput();
#ifdef WANT_TIMESTAMPS
m_Queue->start();
#endif
m_Stopped = false;
while (!stopped()) {
#ifdef USE_QEVENTS
QApplication::sendPostedEvents();
#endif
sleep(1);
}
#ifdef WANT_TIMESTAMPS
m_Queue->stop();
#endif
m_Client->stopSequencerInput();
} catch (const SequencerError& err) {
cerr << "SequencerError exception. Error code: " << err.code()
<< " (" << err.qstrError() << ")" << endl;
cerr << "Location: " << err.location() << endl;
throw err;
}
}
#ifdef USE_QEVENTS
void
QDumpMIDI::customEvent(QEvent *ev)
{
if (ev->type() == SequencerEventType) {
SequencerEvent* sev = static_cast<SequencerEvent*>(ev);
if (sev != NULL) {
dumpEvent(sev);
}
}
}
#else
void
QDumpMIDI::sequencerEvent(SequencerEvent *ev)
{
dumpEvent(ev);
delete ev;
}
#endif
void
QDumpMIDI::dumpEvent(SequencerEvent* sev)
{
#ifdef WANT_TIMESTAMPS
cout << qSetFieldWidth(8) << right << sev->getTick();
/* More timestamp options:
cout << sev->getRealTimeSecs();
cout << sev->getRealTimeNanos(); */
/* Getting the time from the queue status object;
QueueStatus sts = m_Queue->getStatus();
cout << qSetFieldWidth(8) << right << sts.getClockTime();
cout << sts.getTickTime(); */
cout << qSetFieldWidth(0) << " ";
#endif
cout << qSetFieldWidth(3) << right << sev->getSourceClient() << qSetFieldWidth(0) << ":";
cout << qSetFieldWidth(3) << left << sev->getSourcePort() << qSetFieldWidth(0) << " ";
switch (sev->getSequencerType()) {
case SND_SEQ_EVENT_NOTEON: {
NoteOnEvent* e = static_cast<NoteOnEvent*>(sev);
if (e != NULL) {
cout << qSetFieldWidth(23) << left << "Note on";
cout << qSetFieldWidth(2) << right << e->getChannel() << " ";
cout << qSetFieldWidth(3) << e->getKey() << " ";
cout << qSetFieldWidth(3) << e->getVelocity();
}
break;
}
case SND_SEQ_EVENT_NOTEOFF: {
NoteOffEvent* e = static_cast<NoteOffEvent*>(sev);
if (e != NULL) {
cout << qSetFieldWidth(23) << left << "Note off";
cout << qSetFieldWidth(2) << right << e->getChannel() << " ";
cout << qSetFieldWidth(3) << e->getKey() << " ";
cout << qSetFieldWidth(3) << e->getVelocity();
}
break;
}
case SND_SEQ_EVENT_KEYPRESS: {
KeyPressEvent* e = static_cast<KeyPressEvent*>(sev);
if (e != NULL) {
cout << qSetFieldWidth(23) << left << "Polyphonic aftertouch";
cout << qSetFieldWidth(2) << right << e->getChannel() << " ";
cout << qSetFieldWidth(3) << e->getKey() << " ";
cout << qSetFieldWidth(3) << e->getVelocity();
}
break;
}
case SND_SEQ_EVENT_CONTROL14:
case SND_SEQ_EVENT_NONREGPARAM:
case SND_SEQ_EVENT_REGPARAM:
case SND_SEQ_EVENT_CONTROLLER: {
ControllerEvent* e = static_cast<ControllerEvent*>(sev);
if (e != NULL) {
cout << qSetFieldWidth(23) << left << "Control change";
cout << qSetFieldWidth(2) << right << e->getChannel() << " ";
cout << qSetFieldWidth(3) << e->getParam() << " ";
cout << qSetFieldWidth(3) << e->getValue();
}
break;
}
case SND_SEQ_EVENT_PGMCHANGE: {
ProgramChangeEvent* e = static_cast<ProgramChangeEvent*>(sev);
if (e != NULL) {
cout << qSetFieldWidth(23) << left << "Program change";
cout << qSetFieldWidth(2) << right << e->getChannel() << " ";
cout << qSetFieldWidth(3) << e->getValue();
}
break;
}
case SND_SEQ_EVENT_CHANPRESS: {
ChanPressEvent* e = static_cast<ChanPressEvent*>(sev);
if (e != NULL) {
cout << qSetFieldWidth(23) << left << "Channel aftertouch";
cout << qSetFieldWidth(2) << right << e->getChannel() << " ";
cout << qSetFieldWidth(3) << e->getValue();
}
break;
}
case SND_SEQ_EVENT_PITCHBEND: {
PitchBendEvent* e = static_cast<PitchBendEvent*>(sev);
if (e != NULL) {
cout << qSetFieldWidth(23) << left << "Pitch bend";
cout << qSetFieldWidth(2) << right << e->getChannel() << " ";
cout << qSetFieldWidth(5) << e->getValue();
}
break;
}
case SND_SEQ_EVENT_SONGPOS: {
ValueEvent* e = static_cast<ValueEvent*>(sev);
if (e != NULL) {
cout << qSetFieldWidth(26) << left << "Song position pointer" << qSetFieldWidth(0);
cout << e->getValue();
}
break;
}
case SND_SEQ_EVENT_SONGSEL: {
ValueEvent* e = static_cast<ValueEvent*>(sev);
if (e != NULL) {
cout << qSetFieldWidth(26) << left << "Song select" << qSetFieldWidth(0);
cout << e->getValue();
}
break;
}
case SND_SEQ_EVENT_QFRAME: {
ValueEvent* e = static_cast<ValueEvent*>(sev);
if (e != NULL) {
cout << qSetFieldWidth(26) << left << "MTC quarter frame" << qSetFieldWidth(0);
cout << e->getValue();
}
break;
}
case SND_SEQ_EVENT_TIMESIGN: {
ValueEvent* e = static_cast<ValueEvent*>(sev);
if (e != NULL) {
cout << qSetFieldWidth(26) << left << "SMF time signature" << qSetFieldWidth(0);
cout << hex << e->getValue();
cout << dec;
}
break;
}
case SND_SEQ_EVENT_KEYSIGN: {
ValueEvent* e = static_cast<ValueEvent*>(sev);
if (e != NULL) {
cout << qSetFieldWidth(26) << left << "SMF key signature" << qSetFieldWidth(0);
cout << hex << e->getValue();
cout << dec;
}
break;
}
case SND_SEQ_EVENT_SETPOS_TICK: {
QueueControlEvent* e = static_cast<QueueControlEvent*>(sev);
if (e != NULL) {
cout << qSetFieldWidth(26) << left << "Set tick queue pos." << qSetFieldWidth(0);
cout << e->getQueue();
}
break;
}
case SND_SEQ_EVENT_SETPOS_TIME: {
QueueControlEvent* e = static_cast<QueueControlEvent*>(sev);
if (e != NULL) {
cout << qSetFieldWidth(26) << left << "Set rt queue pos." << qSetFieldWidth(0);
cout << e->getQueue();
}
break;
}
case SND_SEQ_EVENT_TEMPO: {
TempoEvent* e = static_cast<TempoEvent*>(sev);
if (e != NULL) {
cout << qSetFieldWidth(26) << left << "Set queue tempo";
cout << qSetFieldWidth(3) << right << e->getQueue() << qSetFieldWidth(0) << " ";
cout << e->getValue();
}
break;
}
case SND_SEQ_EVENT_QUEUE_SKEW: {
QueueControlEvent* e = static_cast<QueueControlEvent*>(sev);
if (e != NULL) {
cout << qSetFieldWidth(26) << left << "Queue timer skew" << qSetFieldWidth(0);
cout << e->getQueue();
}
break;
}
case SND_SEQ_EVENT_START:
cout << left << "Start";
break;
case SND_SEQ_EVENT_STOP:
cout << left << "Stop";
break;
case SND_SEQ_EVENT_CONTINUE:
cout << left << "Continue";
break;
case SND_SEQ_EVENT_CLOCK:
cout << left << "Clock";
break;
case SND_SEQ_EVENT_TICK:
cout << left << "Tick";
break;
case SND_SEQ_EVENT_TUNE_REQUEST:
cout << left << "Tune request";
break;
case SND_SEQ_EVENT_RESET:
cout << left << "Reset";
break;
case SND_SEQ_EVENT_SENSING:
cout << left << "Active Sensing";
break;
case SND_SEQ_EVENT_CLIENT_START: {
ClientEvent* e = static_cast<ClientEvent*>(sev);
if (e != NULL) {
cout << qSetFieldWidth(26) << left << "Client start"
<< qSetFieldWidth(0) << e->getClient();
}
break;
}
case SND_SEQ_EVENT_CLIENT_EXIT: {
ClientEvent* e = static_cast<ClientEvent*>(sev);
if (e != NULL) {
cout << qSetFieldWidth(26) << left << "Client exit"
<< qSetFieldWidth(0) << e->getClient();
}
break;
}
case SND_SEQ_EVENT_CLIENT_CHANGE: {
ClientEvent* e = static_cast<ClientEvent*>(sev);
if (e != NULL) {
cout << qSetFieldWidth(26) << left << "Client changed"
<< qSetFieldWidth(0) << e->getClient();
}
break;
}
case SND_SEQ_EVENT_PORT_START: {
PortEvent* e = static_cast<PortEvent*>(sev);
if (e != NULL) {
cout << qSetFieldWidth(26) << left << "Port start" << qSetFieldWidth(0);
cout << e->getClient() << ":" << e->getPort();
}
break;
}
case SND_SEQ_EVENT_PORT_EXIT: {
PortEvent* e = static_cast<PortEvent*>(sev);
if (e != NULL) {
cout << qSetFieldWidth(26) << left << "Port exit" << qSetFieldWidth(0);
cout << e->getClient() << ":" << e->getPort();
}
break;
}
case SND_SEQ_EVENT_PORT_CHANGE: {
PortEvent* e = static_cast<PortEvent*>(sev);
if (e != NULL) {
cout << qSetFieldWidth(26) << left << "Port changed" << qSetFieldWidth(0);
cout << e->getClient() << ":" << e->getPort();
}
break;
}
case SND_SEQ_EVENT_PORT_SUBSCRIBED: {
SubscriptionEvent* e = static_cast<SubscriptionEvent*>(sev);
if (e != NULL) {
cout << qSetFieldWidth(26) << left << "Port subscribed" << qSetFieldWidth(0);
cout << e->getSenderClient() << ":" << e->getSenderPort() << " -> ";
cout << e->getDestClient() << ":" << e->getDestPort();
}
break;
}
case SND_SEQ_EVENT_PORT_UNSUBSCRIBED: {
SubscriptionEvent* e = static_cast<SubscriptionEvent*>(sev);
if (e != NULL) {
cout << qSetFieldWidth(26) << left << "Port unsubscribed" << qSetFieldWidth(0);
cout << e->getSenderClient() << ":" << e->getSenderPort() << " -> ";
cout << e->getDestClient() << ":" << e->getDestPort();
}
break;
}
case SND_SEQ_EVENT_SYSEX: {
SysExEvent* e = static_cast<SysExEvent*>(sev);
if (e != NULL) {
cout << qSetFieldWidth(26) << left << "System exclusive" << qSetFieldWidth(0);
unsigned int i;
for (i = 0; i < e->getLength(); ++i) {
cout << hex << (unsigned char) e->getData()[i] << " ";
}
cout << dec;
}
break;
}
default:
cout << qSetFieldWidth(26) << "Unknown event type" << qSetFieldWidth(0);
cout << sev->getSequencerType();
};
cout << qSetFieldWidth(0) << endl;
}
QDumpMIDI* test;
void signalHandler(int sig)
{
if (sig == SIGINT)
qDebug() << "Caught a SIGINT. Exiting";
else if (sig == SIGTERM)
qDebug() << "Caught a SIGTERM. Exiting";
test->stop();
}
int main(int argc, char **argv)
{
const QString errorstr = "Fatal error from the ALSA sequencer. "
"This usually happens when the kernel doesn't have ALSA support, "
"or the device node (/dev/snd/seq) doesn't exists, "
"or the kernel module (snd_seq) is not loaded. "
"Please check your ALSA/MIDI configuration.";
CmdLineArgs args;
args.setUsage("[port]");
args.addOptionalArgument("port", "Source MIDI port");
args.parse(argc, argv);
try {
test = new QDumpMIDI();
signal(SIGINT, signalHandler);
signal(SIGTERM, signalHandler);
QVariant portName = args.getArgument("port");
if (!portName.isNull())
test->subscribe(portName.toString());
test->run();
} catch (const SequencerError& ex) {
cerr << errorstr + " Returned error was: " + ex.qstrError() << endl;
} catch (...) {
cerr << errorstr << endl;
}
delete test;
return 0;
}