Print received sequencer events.
#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);
QDumpMIDI::QDumpMIDI()
{
m_Client->open();
m_Client->setClientName("DumpMIDI");
#ifndef USE_QEVENTS
connect( m_Client, &MidiClient::eventReceived,
this, &QDumpMIDI::sequencerEvent,
Qt::DirectConnection );
#endif
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->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
{
qDebug() << "Subscription made from"
}
void QDumpMIDI::subscribe(const QString& portName)
{
try {
qDebug() << "Trying to subscribe" << portName.toLocal8Bit().data();
m_Port->subscribeFrom(portName);
cerr <<
"SequencerError exception. Error code: " << err.
code()
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();
cerr <<
"SequencerError exception. Error code: " << err.
code()
cerr <<
"Location: " << err.
location() << endl;
throw;
}
}
{
dumpEvent(ev);
delete ev;
}
#ifdef USE_QEVENTS
void
QDumpMIDI::customEvent(
QEvent *ev)
{
if (ev->type() == SequencerEventType) {
if (sev != nullptr) {
dumpEvent(sev);
}
}
}
#else
void
{
dumpEvent(ev);
delete ev;
}
#endif
void
{
#ifdef WANT_TIMESTAMPS
cout << qSetFieldWidth(8) << right << sev->
getTick();
cout << qSetFieldWidth(0) << " ";
#endif
cout << qSetFieldWidth(3) << right << sev->
getSourceClient() << qSetFieldWidth(0) <<
":";
cout << qSetFieldWidth(3) << left << sev->
getSourcePort() << qSetFieldWidth(0) <<
" ";
case SND_SEQ_EVENT_NOTEON: {
if (e != nullptr) {
cout << qSetFieldWidth(23) << left << "Note on";
cout << qSetFieldWidth(2) << right << e->
getChannel() <<
" ";
cout << qSetFieldWidth(3) << e->
getKey() <<
" ";
}
break;
}
case SND_SEQ_EVENT_NOTEOFF: {
if (e != nullptr) {
cout << qSetFieldWidth(23) << left << "Note off";
cout << qSetFieldWidth(2) << right << e->
getChannel() <<
" ";
cout << qSetFieldWidth(3) << e->
getKey() <<
" ";
}
break;
}
case SND_SEQ_EVENT_KEYPRESS: {
if (e != nullptr) {
cout << qSetFieldWidth(23) << left << "Polyphonic aftertouch";
cout << qSetFieldWidth(2) << right << e->
getChannel() <<
" ";
cout << qSetFieldWidth(3) << e->
getKey() <<
" ";
}
break;
}
case SND_SEQ_EVENT_CONTROL14:
case SND_SEQ_EVENT_NONREGPARAM:
case SND_SEQ_EVENT_REGPARAM:
case SND_SEQ_EVENT_CONTROLLER: {
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: {
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: {
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: {
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: {
if (e != nullptr) {
cout << qSetFieldWidth(26) << left << "Song position pointer" << qSetFieldWidth(0);
}
break;
}
case SND_SEQ_EVENT_SONGSEL: {
if (e != nullptr) {
cout << qSetFieldWidth(26) << left << "Song select" << qSetFieldWidth(0);
}
break;
}
case SND_SEQ_EVENT_QFRAME: {
if (e != nullptr) {
cout << qSetFieldWidth(26) << left << "MTC quarter frame" << qSetFieldWidth(0);
}
break;
}
case SND_SEQ_EVENT_TIMESIGN: {
if (e != nullptr) {
cout << qSetFieldWidth(26) << left << "SMF time signature" << qSetFieldWidth(0);
cout << dec;
}
break;
}
case SND_SEQ_EVENT_KEYSIGN: {
if (e != nullptr) {
cout << qSetFieldWidth(26) << left << "SMF key signature" << qSetFieldWidth(0);
cout << dec;
}
break;
}
case SND_SEQ_EVENT_SETPOS_TICK: {
if (e != nullptr) {
cout << qSetFieldWidth(26) << left << "Set tick queue pos." << qSetFieldWidth(0);
}
break;
}
case SND_SEQ_EVENT_SETPOS_TIME: {
if (e != nullptr) {
cout << qSetFieldWidth(26) << left << "Set rt queue pos." << qSetFieldWidth(0);
}
break;
}
case SND_SEQ_EVENT_TEMPO: {
if (e != nullptr) {
cout << qSetFieldWidth(26) << left << "Set queue tempo";
cout << qSetFieldWidth(3) << right << e->
getQueue() << qSetFieldWidth(0) <<
" ";
}
break;
}
case SND_SEQ_EVENT_QUEUE_SKEW: {
if (e != nullptr) {
cout << qSetFieldWidth(26) << left << "Queue timer skew" << qSetFieldWidth(0);
}
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: {
if (e != nullptr) {
cout << qSetFieldWidth(26) << left << "Client start"
}
break;
}
case SND_SEQ_EVENT_CLIENT_EXIT: {
if (e != nullptr) {
cout << qSetFieldWidth(26) << left << "Client exit"
}
break;
}
case SND_SEQ_EVENT_CLIENT_CHANGE: {
if (e != nullptr) {
cout << qSetFieldWidth(26) << left << "Client changed"
}
break;
}
case SND_SEQ_EVENT_PORT_START: {
if (e != nullptr) {
cout << qSetFieldWidth(26) << left << "Port start" << qSetFieldWidth(0);
}
break;
}
case SND_SEQ_EVENT_PORT_EXIT: {
if (e != nullptr) {
cout << qSetFieldWidth(26) << left << "Port exit" << qSetFieldWidth(0);
}
break;
}
case SND_SEQ_EVENT_PORT_CHANGE: {
if (e != nullptr) {
cout << qSetFieldWidth(26) << left << "Port changed" << qSetFieldWidth(0);
}
break;
}
case SND_SEQ_EVENT_PORT_SUBSCRIBED: {
if (e != nullptr) {
cout << qSetFieldWidth(26) << left << "Port subscribed" << qSetFieldWidth(0);
}
break;
}
case SND_SEQ_EVENT_PORT_UNSUBSCRIBED: {
if (e != nullptr) {
cout << qSetFieldWidth(26) << left << "Port unsubscribed" << qSetFieldWidth(0);
}
break;
}
case SND_SEQ_EVENT_SYSEX: {
if (e != nullptr) {
cout << qSetFieldWidth(26) << left << "System exclusive" << qSetFieldWidth(0);
unsigned int i;
cout << hex << (
unsigned char) e->
getData()[i] <<
" ";
}
cout << dec;
}
break;
}
default:
cout << qSetFieldWidth(26) << "Unknown event type" << qSetFieldWidth(0);
};
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();
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.
int getValue() const
Gets the channel aftertouch value.
int getChannel() const
Gets the event's channel.
ALSA Event representing a change on some ALSA sequencer client on the system.
int getClient() const
Gets the client number.
Event representing a MIDI control change event.
uint getParam() const
Gets the controller event's parameter.
int getValue() const
Gets the controller event's value.
int getKey() const
Gets the MIDI note of this event.
int getVelocity() const
Gets the note velocity of this event.
Event representing a MIDI key pressure, or polyphonic after-touch event.
Event representing a note-off MIDI event.
Event representing a note-on MIDI event.
Event representing a MIDI bender, or pitch wheel event.
int getValue() const
Gets the MIDI pitch bend value, zero centered from -8192 to 8191.
ALSA Event representing a change on some ALSA sequencer port on the system.
int getPort() const
Gets the port number.
Event representing a MIDI program change event.
int getValue() const
Gets the MIDI program number.
ALSA Event representing a queue control command.
int getQueue() const
Gets the queue number.
int getValue() const
Gets the event's value.
Exception class for ALSA Sequencer errors.
unsigned char getSourceClient() const
Gets the source client id.
snd_seq_tick_time_t getTick() const
Gets the tick time of the event.
unsigned char getSourcePort() const
Gets the source port id.
snd_seq_event_type_t getSequencerType() const
Gets the sequencer event type.
ALSA Event representing a subscription between two ALSA clients and ports.
int getDestClient() const
Gets the destination client number.
int getDestPort() const
Gets the destination port number.
int getSenderClient() const
Gets the sender client number.
int getSenderPort() const
Gets the sender port number.
const snd_seq_addr_t * getSender()
Gets the sender address of the subscription (MIDI OUT port)
Event representing a MIDI system exclusive event.
ALSA Event representing a tempo change for an ALSA queue.
Generic event having a value property.
int getValue() const
Gets the event's value.
unsigned int getLength() const
Gets the data length.
const char * getData() const
Gets the data pointer.
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.
Drumstick ALSA library wrapper.
SequencerError Exception class.