drumstick 2.6.1
dumpmid.cpp

Print received sequencer events.

Print received sequencer events

/*
MIDI Sequencer C++ library
Copyright (C) 2006-2022, 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#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
/* To get timestamped events from ALSA, you need a running queue */
//#undef WANT_TIMESTAMPS
#define WANT_TIMESTAMPS
#include <QObject>
#include <QReadWriteLock>
class QDumpMIDI : public QObject
{
Q_OBJECT
public:
QDumpMIDI();
virtual ~QDumpMIDI();
void dumpEvent(drumstick::ALSA::SequencerEvent* ev);
void subscribe(const QString& portName);
void stop();
bool stopped();
void run();
public slots:
void subscription( drumstick::ALSA::MidiPort* port, drumstick::ALSA::Subscription* subs );
#ifdef USE_QEVENTS
protected:
virtual void customEvent( QEvent *ev );
#else
void sequencerEvent( drumstick::ALSA::SequencerEvent* ev );
#endif
private:
#ifdef WANT_TIMESTAMPS
#endif
bool m_Stopped;
QReadWriteLock m_mutex;
};
#endif /*DUMPMIDI_H_*/
Classes managing ALSA Sequencer clients.
Classes managing ALSA Sequencer events.
Classes managing ALSA Sequencer ports.
Classes managing ALSA Sequencer queues.
The QEvent class is the base class of all event classes.
The QObject class is the base class of all Qt objects.
Client management.
Definition: alsaclient.h:209
Port management.
Definition: alsaport.h:115
Queue management.
Definition: alsaqueue.h:191
Base class for the event's hierarchy.
Definition: alsaevent.h:58
Subscription management.
Definition: subscription.h:87
Classes managing ALSA sequencer subscriptions.
/*
MIDI Sequencer C++ library
Copyright (C) 2006-2022, 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include "dumpmid.h"
#include <QCommandLineParser>
#include <QCoreApplication>
#include <QObject>
#include <QReadLocker>
#include <QString>
#include <QTextStream>
#include <QWriteLocker>
#include <QIODevice>
#include <QtDebug>
#include <csignal>
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
#define right Qt::right
#define left Qt::left
#define hex Qt::hex
#define dec Qt::dec
#define endl Qt::endl
#endif
QTextStream cout(stdout, QIODevice::WriteOnly);
QTextStream cerr(stderr, QIODevice::WriteOnly);
using namespace drumstick::ALSA;
QDumpMIDI::QDumpMIDI()
: QObject(), m_Stopped(false)
{
m_Client = new MidiClient(this);
m_Client->open();
m_Client->setClientName("DumpMIDI");
#ifndef USE_QEVENTS // using signals instead
connect( m_Client, &MidiClient::eventReceived,
this, &QDumpMIDI::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, &MidiPort::subscribed, this, &QDumpMIDI::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;
}
}
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
QCoreApplication::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;
}
}
#ifdef USE_QEVENTS
void
QDumpMIDI::customEvent(QEvent *ev)
{
if (ev->type() == SequencerEventType) {
SequencerEvent* sev = static_cast<SequencerEvent*>(ev);
if (sev != nullptr) {
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 != nullptr) {
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 != nullptr) {
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 != nullptr) {
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 != nullptr) {
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 != nullptr) {
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 != nullptr) {
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 != nullptr) {
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 != nullptr) {
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 != nullptr) {
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 != nullptr) {
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 != nullptr) {
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 != nullptr) {
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 != nullptr) {
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 != nullptr) {
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 != nullptr) {
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 != nullptr) {
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 != nullptr) {
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 != nullptr) {
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 != nullptr) {
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 != nullptr) {
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 != nullptr) {
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 != nullptr) {
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 != nullptr) {
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 != nullptr) {
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 != nullptr) {
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 = QStringLiteral("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.");
const QString PGM_NAME = QStringLiteral("drumstick-dumpmid");
const QString PGM_DESCRIPTION = QStringLiteral("Drumstick command line utility for decoding MIDI events");
QCoreApplication app(argc, argv);
QCoreApplication::setApplicationName(PGM_NAME);
QCoreApplication::setApplicationVersion(QStringLiteral(QT_STRINGIFY(VERSION)));
QCommandLineParser parser;
parser.setApplicationDescription(PGM_DESCRIPTION);
auto helpOption = parser.addHelpOption();
auto versionOption = parser.addVersionOption();
QCommandLineOption portOption({"p", "port"}, "Source MIDI Port.", "client:port");
parser.addOption(portOption);
parser.process(app);
if (parser.isSet(versionOption) || parser.isSet(helpOption)) {
return 0;
}
try {
test = new QDumpMIDI();
signal(SIGINT, signalHandler);
signal(SIGTERM, signalHandler);
if (parser.isSet(portOption)) {
QString portName = parser.value(portOption);
test->subscribe(portName);
} else {
cerr << "Port argument is mandatory" << endl;
parser.showHelp();
}
test->run();
} catch (const SequencerError& ex) {
cerr << ERRORSTR << " Returned error was: " << ex.qstrError() << endl;
} catch (...) {
cerr << ERRORSTR << endl;
}
delete test;
return 0;
}
Event representing a MIDI channel pressure or after-touch event.
Definition: alsaevent.h:439
int getValue() const
Gets the channel aftertouch value.
Definition: alsaevent.h:458
int getChannel() const
Gets the event's channel.
Definition: alsaevent.h:169
ALSA Event representing a change on some ALSA sequencer client on the system.
Definition: alsaevent.h:719
int getClient() const
Gets the client number.
Definition: alsaevent.h:732
Event representing a MIDI control change event.
Definition: alsaevent.h:328
uint getParam() const
Gets the controller event's parameter.
Definition: alsaevent.h:349
int getValue() const
Gets the controller event's value.
Definition: alsaevent.h:361
int getKey() const
Gets the MIDI note of this event.
Definition: alsaevent.h:192
int getVelocity() const
Gets the note velocity of this event.
Definition: alsaevent.h:204
Event representing a MIDI key pressure, or polyphonic after-touch event.
Definition: alsaevent.h:305
Event representing a note-off MIDI event.
Definition: alsaevent.h:282
Event representing a note-on MIDI event.
Definition: alsaevent.h:259
Event representing a MIDI bender, or pitch wheel event.
Definition: alsaevent.h:407
int getValue() const
Gets the MIDI pitch bend value, zero centered from -8192 to 8191.
Definition: alsaevent.h:426
ALSA Event representing a change on some ALSA sequencer port on the system.
Definition: alsaevent.h:740
int getPort() const
Gets the port number.
Definition: alsaevent.h:753
Event representing a MIDI program change event.
Definition: alsaevent.h:375
int getValue() const
Gets the MIDI program number.
Definition: alsaevent.h:394
ALSA Event representing a queue control command.
Definition: alsaevent.h:552
int getQueue() const
Gets the queue number.
Definition: alsaevent.h:566
int getValue() const
Gets the event's value.
Definition: alsaevent.h:576
Exception class for ALSA Sequencer errors.
unsigned char getSourceClient() const
Gets the source client id.
Definition: alsaevent.h:79
snd_seq_tick_time_t getTick() const
Gets the tick time of the event.
Definition: alsaevent.h:91
unsigned char getSourcePort() const
Gets the source port id.
Definition: alsaevent.h:85
snd_seq_event_type_t getSequencerType() const
Gets the sequencer event type.
Definition: alsaevent.h:71
ALSA Event representing a subscription between two ALSA clients and ports.
Definition: alsaevent.h:673
int getDestClient() const
Gets the destination client number.
Definition: alsaevent.h:706
int getDestPort() const
Gets the destination port number.
Definition: alsaevent.h:711
int getSenderClient() const
Gets the sender client number.
Definition: alsaevent.h:696
int getSenderPort() const
Gets the sender port number.
Definition: alsaevent.h:701
const snd_seq_addr_t * getSender()
Gets the sender address of the subscription (MIDI OUT port)
Event representing a MIDI system exclusive event.
Definition: alsaevent.h:498
ALSA Event representing a tempo change for an ALSA queue.
Definition: alsaevent.h:656
Generic event having a value property.
Definition: alsaevent.h:629
int getValue() const
Gets the event's value.
Definition: alsaevent.h:643
unsigned int getLength() const
Gets the data length.
Definition: alsaevent.h:483
const char * getData() const
Gets the data pointer.
Definition: alsaevent.h:488
QString qstrError() const
Gets the human readable error message from the error code.
int code() const
Gets the numeric error code.
const QString & location() const
Gets the location of the error code as provided in the constructor.
const QEvent::Type SequencerEventType
Constant SequencerEventType is the QEvent::type() of any SequencerEvent object to be used to check th...
Definition: alsaevent.h:49
Drumstick ALSA library wrapper.
Definition: alsaclient.cpp:68
SequencerError Exception class.