# HG changeset patch # User František Kučera # Date 1601742030 -7200 # Node ID 9ad606c80d3bbb775a6160f9eae41507fc96d566 # Parent a85c191709e639c20cb9b3cbbdedd0da5b174961 encode MIDI messages + process JACK error output diff -r a85c191709e6 -r 9ad606c80d3b src/JackHandler.h --- 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> 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));