-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Outgoing MIDI debugging messages #1004
Changes from 6 commits
8fcf96c
6aa4607
258556e
653136a
047de0b
f698d84
6033525
6d9f433
6e8b4d0
4d708cd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -85,6 +85,9 @@ void Controller::send(QList<int> data, unsigned int length) { | |
// If you change this implementation, also change it in HidController (That | ||
// function is required due to HID devices having report IDs) | ||
|
||
// The length parameter is here for backwards compatibility for when scripts | ||
// were required to specify it. | ||
length = data.size(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. length is not used before, I think you can remove the parameter. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is Q_INVOKABLE. The length parameter has been required for scripts, so I'm leaving it for backwards compatibility. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll add a comment to clarify that. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. aaaa. wait. or you could use the minimum of both, to keep the old functionality. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think the |
||
QByteArray msg(length, 0); | ||
for (unsigned int i = 0; i < length; ++i) { | ||
msg[i] = data.at(i); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ | |
* @brief HSS1394-based MIDI backend | ||
*/ | ||
|
||
#include "controllers/midi/midiutils.h" | ||
#include "controllers/midi/hss1394controller.h" | ||
#include "controllers/controllerdebug.h" | ||
#include "util/time.h" | ||
|
@@ -168,18 +169,15 @@ int Hss1394Controller::close() { | |
return 0; | ||
} | ||
|
||
void Hss1394Controller::sendWord(unsigned int word) { | ||
unsigned char data[3]; | ||
data[0] = word & 0xFF; | ||
data[1] = (word >> 8) & 0xFF; | ||
data[2] = (word >> 16) & 0xFF; | ||
|
||
QString message = QString("%1 %2 %3").arg( | ||
QString("%1").arg(data[0], 2, 16, QChar('0')), | ||
QString("%1").arg(data[1], 2, 16, QChar('0')), | ||
QString("%1").arg(data[2], 2, 16, QChar('0'))); | ||
void Hss1394Controller::sendShortMsg(unsigned char status, unsigned char byte1, | ||
unsigned char byte2) { | ||
unsigned char data[3] = { status, byte1, byte2 }; | ||
|
||
int bytesSent = m_pChannel->SendChannelBytes(data, 3); | ||
controllerDebug(formatMidiMessage(getName(), | ||
status, byte1, byte2, | ||
MidiUtils::channelFromStatus(status), | ||
MidiUtils::opCodeFromStatus(status))); | ||
|
||
//if (bytesSent != 3) { | ||
// qDebug()<<"ERROR: Sent" << bytesSent << "of 3 bytes:" << message; | ||
|
@@ -191,6 +189,7 @@ void Hss1394Controller::send(QByteArray data) { | |
int bytesSent = m_pChannel->SendChannelBytes( | ||
(unsigned char*)data.constData(), data.size()); | ||
|
||
controllerDebug(formatSysexMessage(getName(), data)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The same here. There should be no formating if controller debug is disabled. |
||
//if (bytesSent != length) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Pegasus-RPG why is this commented out? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't remember. Am I listed as the last person to touch that line? If so, maybe the HSS1394 spec said that it wasn't implemented and always returns 0 or something. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, that line hasn't been modified since you created the file. If the hss1394 library actually does indicate an error somehow, it would be good to handle that. |
||
// qDebug()<<"ERROR: Sent" << bytesSent << "of" << length << "bytes (SysEx)"; | ||
// //m_pChannel->Flush(); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -51,7 +51,8 @@ class Hss1394Controller : public MidiController { | |
virtual int close(); | ||
|
||
private: | ||
void sendWord(unsigned int word); | ||
void sendShortMsg(unsigned char status, unsigned char byte1, | ||
unsigned char byte2); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What gain is there in eliminating the ability to send data words? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The way it was before, with sendShortMsg() passing the computed word to sendWord(), the sendWord() function that actually did the sending would have to undo the operations of sendShortMsg() to print the three separate MIDI bytes. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That may be the case in HSS1394Controller, but not in PortMidiController. https://github.com/mixxxdj/mixxx/pull/1004/files#diff-ab47417f917c4a5cea304eded0d68381R213 |
||
|
||
// The sysex data must already contain the start byte 0xf0 and the end byte | ||
// 0xf7. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -160,53 +160,78 @@ void MidiController::destroyOutputHandlers() { | |
} | ||
} | ||
|
||
QString formatMidiMessage(const QString& controllerName, | ||
unsigned char status, unsigned char control, | ||
unsigned char value, unsigned char channel, | ||
unsigned char opCode, mixxx::Duration timestamp) { | ||
QString MidiController::formatMidiMessage(const QString& controllerName, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm curious why you've added the class name to the definitions. Isn't it implied? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, it isn't implied. That was actually confusing me for a bit before I got help in ##c++-basic on Freenode. I was getting a linker error when I added it to the class as a protected member function but didn't add the class name to the definition. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How were you able to build before then? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It wasn't in the header. It was only defined in the .cpp file, but I had to make it a protected member of the class to reuse it in PortMidiController::sendShortMsg() and Hss1394Controller::sendShortMsg(). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: since formatMidiMessage/formatSysexMessage don't use any member variables/methods of MidiController, they could move to MidiUtils (src/controllers/midi/midiutils.h). Would be cleaner that way. |
||
unsigned char status, unsigned char control, | ||
unsigned char value, unsigned char channel, | ||
unsigned char opCode, mixxx::Duration timestamp) { | ||
QString msg2; | ||
if (timestamp == mixxx::Duration::fromMillis(0)) { | ||
msg2 = "outgoing:"; | ||
} else { | ||
msg2 = QString("t:%1").arg(timestamp.formatMillisWithUnit()); | ||
} | ||
switch (opCode) { | ||
case MIDI_PITCH_BEND: | ||
return QString("%1: t:%2 status 0x%3: pitch bend ch %4, value 0x%5") | ||
.arg(controllerName, timestamp.formatMillisWithUnit(), | ||
return QString("%1: %2 status 0x%3: pitch bend ch %4, value 0x%5") | ||
.arg(controllerName, msg2, | ||
QString::number(status, 16).toUpper(), | ||
QString::number(channel+1, 10), | ||
QString::number((value << 7) | control, 16).toUpper().rightJustified(4,'0')); | ||
case MIDI_SONG_POS: | ||
return QString("%1: t:%5 status 0x%3: song position 0x%4") | ||
.arg(controllerName, timestamp.formatMillisWithUnit(), | ||
return QString("%1: %2 status 0x%3: song position 0x%4") | ||
.arg(controllerName, msg2, | ||
QString::number(status, 16).toUpper(), | ||
QString::number((value << 7) | control, 16).toUpper().rightJustified(4,'0')); | ||
case MIDI_PROGRAM_CH: | ||
case MIDI_CH_AFTERTOUCH: | ||
return QString("%1: t:%2 status 0x%3 (ch %4, opcode 0x%5), value 0x%6") | ||
.arg(controllerName, timestamp.formatMillisWithUnit(), | ||
return QString("%1: %2 status 0x%3 (ch %4, opcode 0x%5), value 0x%6") | ||
.arg(controllerName, msg2, | ||
QString::number(status, 16).toUpper(), | ||
QString::number(channel+1, 10), | ||
QString::number((status & 255)>>4, 16).toUpper(), | ||
QString::number(control, 16).toUpper().rightJustified(2,'0')); | ||
case MIDI_SONG: | ||
return QString("%1: t:%2 status 0x%3: select song #%4") | ||
.arg(controllerName, timestamp.formatMillisWithUnit(), | ||
return QString("%1: %2 status 0x%3: select song #%4") | ||
.arg(controllerName, msg2, | ||
QString::number(status, 16).toUpper(), | ||
QString::number(control+1, 10)); | ||
case MIDI_NOTE_OFF: | ||
case MIDI_NOTE_ON: | ||
case MIDI_AFTERTOUCH: | ||
case MIDI_CC: | ||
return QString("%1: t:%2 status 0x%3 (ch %4, opcode 0x%5), ctrl 0x%6, val 0x%7") | ||
.arg(controllerName, timestamp.formatMillisWithUnit(), | ||
return QString("%1: %2 status 0x%3 (ch %4, opcode 0x%5), ctrl 0x%6, val 0x%7") | ||
.arg(controllerName, msg2, | ||
QString::number(status, 16).toUpper(), | ||
QString::number(channel+1, 10), | ||
QString::number((status & 255)>>4, 16).toUpper(), | ||
QString::number(control, 16).toUpper().rightJustified(2,'0'), | ||
QString::number(value, 16).toUpper().rightJustified(2,'0')); | ||
default: | ||
return QString("%1: t:%2 status 0x%3") | ||
.arg(controllerName, timestamp.formatMillisWithUnit(), | ||
return QString("%1: %2 status 0x%3") | ||
.arg(controllerName, msg2, | ||
QString::number(status, 16).toUpper()); | ||
} | ||
} | ||
|
||
QString MidiController::formatSysexMessage(const QString& controllerName, const QByteArray& data, | ||
mixxx::Duration timestamp) { | ||
QString msg2; | ||
if (timestamp == mixxx::Duration::fromMillis(0)) { | ||
msg2 = "outgoing:"; | ||
} else { | ||
msg2 = QString("t:%1").arg(timestamp.formatMillisWithUnit()); | ||
} | ||
QString message = QString("%1: %2 %3 byte sysex: [") | ||
.arg(controllerName).arg(msg2) | ||
.arg(data.size()); | ||
for (int i = 0; i < data.size(); ++i) { | ||
message += QString("%1%2").arg( | ||
QString("%1").arg((unsigned char)(data.at(i)), 2, 16, QChar('0')).toUpper(), | ||
QString("%1").arg((i < (data.size()-1)) ? ' ' : ']')); | ||
} | ||
return message; | ||
} | ||
|
||
void MidiController::learnTemporaryInputMappings(const MidiInputMappings& mappings) { | ||
foreach (const MidiInputMapping& mapping, mappings) { | ||
m_temporaryInputMappings.insert(mapping.key.key, mapping); | ||
|
@@ -497,19 +522,6 @@ double MidiController::computeValue(MidiOptions options, double _prevmidivalue, | |
return _newmidivalue; | ||
} | ||
|
||
QString formatSysexMessage(const QString& controllerName, const QByteArray& data, | ||
mixxx::Duration timestamp) { | ||
QString message = QString("%1: t:%2 %3 bytes: [") | ||
.arg(controllerName).arg(timestamp.formatMillisWithUnit()) | ||
.arg(data.size()); | ||
for (int i = 0; i < data.size(); ++i) { | ||
message += QString("%1%2").arg( | ||
QString("%1").arg((unsigned char)(data.at(i)), 2, 16, QChar('0')).toUpper(), | ||
QString("%1").arg((i < (data.size()-1)) ? ' ' : ']')); | ||
} | ||
return message; | ||
} | ||
|
||
void MidiController::receive(QByteArray data, mixxx::Duration timestamp) { | ||
controllerDebug(formatSysexMessage(getName(), data, timestamp)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Uhh, Here is a real CPU burner. Please hide it behind the ControllerDebug::enabled() flag There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Doesn't the controllerDebug() macro already do this?
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, you're right -- controllerDebug(...) is transformed so it only costs a potential branch miss when disabled. |
||
|
||
|
@@ -557,10 +569,3 @@ void MidiController::processInputMapping(const MidiInputMapping& mapping, | |
qWarning() << "MidiController: No script function specified for" | ||
<< formatSysexMessage(getName(), data, timestamp); | ||
} | ||
|
||
void MidiController::sendShortMsg(unsigned char status, unsigned char byte1, | ||
unsigned char byte2) { | ||
unsigned int word = (((unsigned int)byte2) << 16) | | ||
(((unsigned int)byte1) << 8) | status; | ||
sendWord(word); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -57,12 +57,24 @@ class MidiController : public Controller { | |
unsigned char value); | ||
|
||
protected: | ||
Q_INVOKABLE void sendShortMsg(unsigned char status, unsigned char byte1, unsigned char byte2); | ||
Q_INVOKABLE virtual void sendShortMsg(unsigned char status, | ||
unsigned char byte1, unsigned char byte2) = 0; | ||
// Alias for send() | ||
Q_INVOKABLE inline void sendSysexMsg(QList<int> data, unsigned int length) { | ||
send(data, length); | ||
Q_INVOKABLE inline void sendSysexMsg(QList<int> data, unsigned int length = 0) { | ||
Q_UNUSED(length); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The same here, all the length and int to char looks scary. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was wondering that as well, but I am being careful not to break the QtScript interface. I could try making sendSysexMsg accept a QByteArray and test if it works. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's because QtScript passes the data as a QList. It was quite difficult getting this translation to work properly. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @daschuer QtScript can't pass us anything other than a list of ints, so that's why. The length argument was always unnecessary (and error prone) because the real length is already stored in the list itself. I can't remember if QtScript will drop extra arguments when calling C++ objects. Javascript certainly will if you're calling a normally JS function with more arguments than it expects. @Be-ing did you test that? We have a more efficient ByteArray class available to scripts that we use for passing bytearrays into the script engine: |
||
send(data); | ||
} | ||
|
||
QString formatMidiMessage(const QString& controllerName, | ||
unsigned char status, unsigned char control, | ||
unsigned char value, unsigned char channel, | ||
unsigned char opCode, | ||
mixxx::Duration timestamp = mixxx::Duration::fromMillis(0)); | ||
QString formatSysexMessage(const QString& controllerName, | ||
const QByteArray& data, | ||
mixxx::Duration timestamp = mixxx::Duration::fromMillis(0)); | ||
|
||
|
||
protected slots: | ||
virtual void receive(unsigned char status, unsigned char control, | ||
unsigned char value, mixxx::Duration timestamp); | ||
|
@@ -88,7 +100,6 @@ class MidiController : public Controller { | |
const QByteArray& data, | ||
mixxx::Duration timestamp); | ||
|
||
virtual void sendWord(unsigned int word) = 0; | ||
double computeValue(MidiOptions options, double _prevmidivalue, double _newmidivalue); | ||
void createOutputHandlers(); | ||
void updateAllOutputs(); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,7 @@ | |
* | ||
*/ | ||
|
||
#include "controllers/midi/midiutils.h" | ||
#include "controllers/midi/portmidicontroller.h" | ||
#include "controllers/controllerdebug.h" | ||
|
||
|
@@ -203,14 +204,28 @@ bool PortMidiController::poll() { | |
return numEvents > 0; | ||
} | ||
|
||
void PortMidiController::sendWord(unsigned int word) { | ||
void PortMidiController::sendShortMsg(unsigned char status, unsigned char byte1, | ||
unsigned char byte2) { | ||
if (m_pOutputDevice.isNull() || !m_pOutputDevice->isOpen()) { | ||
return; | ||
} | ||
|
||
unsigned int word = (((unsigned int)byte2) << 16) | | ||
(((unsigned int)byte1) << 8) | status; | ||
|
||
PmError err = m_pOutputDevice->writeShort(word); | ||
if (err != pmNoError) { | ||
qWarning() << "PortMidi sendShortMsg error:" << Pm_GetErrorText(err); | ||
if (err == pmNoError) { | ||
controllerDebug(formatMidiMessage(getName(), | ||
status, byte1, byte2, | ||
MidiUtils::channelFromStatus(status), | ||
MidiUtils::opCodeFromStatus(status))); | ||
} else { | ||
qWarning() << "Error sending short message" | ||
<< formatMidiMessage(getName(), | ||
status, byte1, byte2, | ||
MidiUtils::channelFromStatus(status), | ||
MidiUtils::opCodeFromStatus(status)) | ||
<< "\nPortMidi error:" << Pm_GetErrorText(err); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. On Windows it's \r\n. Instead I would either keep them on the same line or make a separate qWarning if you want it on a separate line (I don't think QtDebug supports std::endl). |
||
} | ||
} | ||
|
||
|
@@ -229,7 +244,9 @@ void PortMidiController::send(QByteArray data) { | |
} | ||
|
||
PmError err = m_pOutputDevice->writeSysEx((unsigned char*)data.constData()); | ||
if (err != pmNoError) { | ||
if (err == pmNoError) { | ||
controllerDebug(formatSysexMessage(getName(), data)); | ||
} else { | ||
qWarning() << "PortMidi sendSysexMsg error:" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you want to print the formatted message on error here too? |
||
<< Pm_GetErrorText(err); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should also mention this in the header definition of send() and belongs in the header at the definition of Controller::send and MidiController::sendSysexMessage.