#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 // using signals instead
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;
}
}
#ifdef USE_QEVENTS
void
QDumpMIDI::customEvent(
QEvent *ev)
{
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;
}