--- a/src/JackHandler.h Sat Oct 03 13:40:07 2020 +0200
+++ b/src/JackHandler.h Sat Oct 03 18:20:30 2020 +0200
@@ -59,7 +59,7 @@
* from the relpipe-reading thread to the jack-writing thread (callback).
*/
struct MidiMessage {
- uint8_t buffer[4096];
+ uint8_t buffer[4096] = {0};
uint32_t size;
uint32_t time;
};
@@ -130,7 +130,7 @@
else return Event::UNKNOWN;
}
- Event event;
+ Event event = Event::UNKNOWN;
relpipe::common::type::Integer channel;
relpipe::common::type::Boolean noteOn;
relpipe::common::type::Integer notePitch;
@@ -145,6 +145,11 @@
} relationContext;
+ static void jackErrorCallback(const char * message) {
+ std::wstring_convert < std::codecvt_utf8<wchar_t>> convertor; // TODO: local system encoding
+ std::wcerr << L"JACK: " << convertor.from_bytes(message) << std::endl;
+ }
+
public:
JackHandler(Configuration& configuration) : configuration(configuration) {
@@ -157,7 +162,8 @@
jack_set_process_callback(realTimeContext.jackClient, RealTimeContext::processCallback, &realTimeContext);
// TODO: report also other events (connections etc.)
- // TODO: jack_set_error_function() + avoid mixing char and wchar output
+ jack_set_error_function(jackErrorCallback);
+ jack_set_info_function(jackErrorCallback);
realTimeContext.jackPort = jack_port_register(realTimeContext.jackClient, "output", JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0);
if (realTimeContext.jackPort == nullptr) throw JackException(L"Could not register port.");
@@ -194,9 +200,9 @@
// TODO: check also data types and skipt relation if important attributes are missing
// TODO: configurable relation name
- if (relationContext.indexOfEvent == -1 || relationContext.indexOfRaw == -1) {
+ if (relationContext.indexOfRaw == -1) {
relationContext.skip = true;
- fwprintf(stderr, L"Relation „%ls“ will be ignored because mandatory attributes are missing.\n", name.c_str());
+ fwprintf(stderr, L"Relation „%ls“ will be ignored because mandatory attribute „raw“ is missing.\n", name.c_str());
}
}
@@ -206,39 +212,57 @@
// TODO: switch to RelationalReaderStringHandler
// TODO: if (continueProcessing) {} ?
- // memcpy(currentMidiMessage.buffer, ….buffer, ….size);
- // currentMidiMessage.size = …;
- // currentMidiMessage.time = …;
-
- {
- RelationContext& rel = relationContext;
- RelationContext::RecordContext& rec = rel.recordContext;
+ RelationContext& rel = relationContext;
+ RelationContext::RecordContext& rec = rel.recordContext;
- if (rel.indexOfEvent == rec.attributeIndex) rec.event = rec.parseEventType(value);
- else if (rel.indexOfChannel == rec.attributeIndex) rec.channel = std::stoi(value);
- else if (rel.indexOfControllerId == rec.attributeIndex) rec.controllerId = std::stoi(value);
- else if (rel.indexOfControllerValue == rec.attributeIndex) rec.controllerValue = std::stoi(value);
- else if (rel.indexOfNoteOn == rec.attributeIndex) rec.noteOn = value == L"true";
- else if (rel.indexOfNotePitch == rec.attributeIndex) rec.notePitch = std::stoi(value);
- else if (rel.indexOfNoteVelocity == rec.attributeIndex) rec.noteVelocity = std::stoi(value);
- else if (rel.indexOfRaw == rec.attributeIndex) rec.raw = value;
- }
+ if (rel.indexOfEvent == rec.attributeIndex) rec.event = rec.parseEventType(value);
+ else if (rel.indexOfChannel == rec.attributeIndex) rec.channel = std::stoi(value);
+ else if (rel.indexOfControllerId == rec.attributeIndex) rec.controllerId = std::stoi(value);
+ else if (rel.indexOfControllerValue == rec.attributeIndex) rec.controllerValue = std::stoi(value);
+ else if (rel.indexOfNoteOn == rec.attributeIndex) rec.noteOn = value == L"true";
+ else if (rel.indexOfNotePitch == rec.attributeIndex) rec.notePitch = std::stoi(value);
+ else if (rel.indexOfNoteVelocity == rec.attributeIndex) rec.noteVelocity = std::stoi(value);
+ else if (rel.indexOfRaw == rec.attributeIndex) rec.raw = value;
- relationContext.recordContext.attributeIndex++;
+ rec.attributeIndex++;
- if (relationContext.recordContext.attributeIndex == relationContext.attributeCount) {
+ if (rec.attributeIndex == rel.attributeCount) {
while (jack_ringbuffer_write_space(realTimeContext.ringBuffer) < sizeof (MidiMessage)) usleep(1000); // should not happen, the real-time thread should be faster
MidiMessage m;
- // TODO: convert relationContext.recordContext to m
// TODO: correct timing?
m.time = 0;
- m.size = 3;
- m.buffer[0] = 0x90;
- m.buffer[1] = 0x24;
- m.buffer[2] = 0x40;
+
+ if (rec.event == RelationContext::RecordContext::Event::NOTE) {
+ m.size = 3;
+ m.buffer[0] = (rec.noteOn ? 0x90 : 0x80) | rec.channel;
+ m.buffer[1] = rec.notePitch;
+ m.buffer[2] = rec.noteVelocity;
+ } else if (rec.event == RelationContext::RecordContext::Event::CONTROL) {
+ m.size = 3;
+ m.buffer[0] = 0xB0 | rec.channel;
+ m.buffer[1] = rec.controllerId;
+ m.buffer[2] = rec.controllerValue;
+ } else { // SysEx and other raw messages
+ m.size = 0;
+ size_t nibble = 0;
+ for (int i = 0; i < rec.raw.size() && m.size < sizeof (m.buffer); i++) {
+ wchar_t ch = rec.raw[i];
+
+ if (ch == L' ') continue;
+ else if (ch >= L'0' && ch <= L'9') m.buffer[m.size] += (ch - '0') /**/ << (nibble % 2 ? 0 : 4);
+ else if (ch >= L'a' && ch <= L'f') m.buffer[m.size] += (ch - 'a' + 10) << (nibble % 2 ? 0 : 4);
+ else if (ch >= L'A' && ch <= L'F') m.buffer[m.size] += (ch - 'A' + 10) << (nibble % 2 ? 0 : 4);
+ else throw JackException(L"Invalid character in the hexadeximal sequence: „" + std::wstring(1, ch) + L"“.");
+
+ nibble++;
+ if (nibble % 2 == 0) m.size++;
+ }
+ }
+
+ // TODO: if (m.size == 0) fwprintf(stderr, L"Missing data\n");
jack_ringbuffer_write(realTimeContext.ringBuffer, (const char *) &m, sizeof (m));