# HG changeset patch # User František Kučera # Date 1601676343 -7200 # Node ID 6aa881c10efd8d85860079017fa026ced4eeb89c # Parent 14cf28d7681c187e7d52c0135f5d6adc9912516b attribute mapping diff -r 14cf28d7681c -r 6aa881c10efd src/JackHandler.h --- a/src/JackHandler.h Fri Oct 02 00:34:14 2020 +0200 +++ b/src/JackHandler.h Sat Oct 03 00:05:43 2020 +0200 @@ -1,6 +1,6 @@ /** * Relational pipes - * Copyright © 2018 František Kučera (Frantovo.cz, GlobalCode.info) + * Copyright © 2020 František Kučera (Frantovo.cz, GlobalCode.info) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -60,6 +60,10 @@ const int RING_BUFFER_SIZE = 100; + /** + * Is passed through the ring buffer + * from the relpipe-reading thread to the jack-writing thread (callback). + */ struct MidiMessage { uint8_t buffer[4096]; uint32_t size; @@ -67,13 +71,52 @@ }; /** - * The message being prepared before enqueing in the ringBuffer. - * Not the message dequeued from the ringBuffer (different thread). + * Temporary storage of values read from relational input. */ - MidiMessage currentMidiMessage; - size_t currentAttributeCount = 0; - size_t currentAttributeIndex = 0; + class RelationContext { + public: + size_t indexOfEvent = -1; + size_t indexOfChannel = -1; + size_t indexOfNoteOn = -1; + size_t indexOfNotePitch = -1; + size_t indexOfNoteVelocity = -1; + size_t indexOfControllerId = -1; + size_t indexOfControllerValue = -1; + size_t indexOfRaw = -1; + + size_t attributeCount = 0; + + class RecordContext { + public: + enum class Event { + NOTE, + CONTROL, + SYSEX, + UNKNOWN + }; + + static Event parseEventType(const relpipe::common::type::StringX name) { + if (name == L"note") return Event::NOTE; + else if (name == L"control") return Event::CONTROL; + else if (name == L"sysex") return Event::SYSEX; + else return Event::UNKNOWN; + } + + Event event; + relpipe::common::type::Integer channel; + relpipe::common::type::Boolean noteOn; + relpipe::common::type::Integer notePitch; + relpipe::common::type::Integer noteVelocity; + relpipe::common::type::Integer controllerId; + relpipe::common::type::Integer controllerValue; + relpipe::common::type::StringX raw; + + size_t attributeIndex = 0; + + } recordContext; + + } relationContext; public: @@ -105,10 +148,27 @@ void startRelation(const relpipe::common::type::StringX name, std::vector attributes) override { // TODO: validate metadata and prepare attribute mappings (names and types are important, order does not matter) - currentAttributeCount = attributes.size(); + relationContext = RelationContext(); + relationContext.attributeCount = attributes.size(); + + for (int i = 0; i < relationContext.attributeCount; i++) { + const relpipe::common::type::StringX attributeName = attributes[i].getAttributeName(); + if (attributeName == L"event") relationContext.indexOfEvent = i; + else if (attributeName == L"channel") relationContext.indexOfChannel = i; + else if (attributeName == L"controller_id") relationContext.indexOfControllerId = i; + else if (attributeName == L"controller_value") relationContext.indexOfControllerValue = i; + else if (attributeName == L"note_on") relationContext.indexOfNoteOn = i; + else if (attributeName == L"note_pitch") relationContext.indexOfNotePitch = i; + else if (attributeName == L"note_velocity") relationContext.indexOfNoteVelocity = i; + else if (attributeName == L"raw") relationContext.indexOfRaw = i; + } + + // TODO: check also data types and skipt relation if important attributes are missing + // TODO: configurable relation name } void attribute(const relpipe::common::type::StringX& value) override { + // TODO: switch to RelationalReaderStringHandler // TODO: append to current message + if this is last attribute, put whole message to the ring buffer // TODO: if (continueProcessing) {} ? @@ -118,24 +178,40 @@ // TODO: correct timing? // TODO: real data - currentMidiMessage.time = 0; - currentMidiMessage.size = 3; - currentMidiMessage.buffer[0] = 0x90; - currentMidiMessage.buffer[1] = 0x24; - currentMidiMessage.buffer[2] = 0x40; + + { + 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; + } - currentAttributeIndex++; + relationContext.recordContext.attributeIndex++; + + if (relationContext.recordContext.attributeIndex == relationContext.attributeCount) { + if (jack_ringbuffer_write_space(ringBuffer) >= sizeof (MidiMessage)) { + MidiMessage m; - if (currentAttributeIndex == currentAttributeCount) { - if (jack_ringbuffer_write_space(ringBuffer) >= sizeof (MidiMessage)) { - jack_ringbuffer_write(ringBuffer, (const char *) ¤tMidiMessage, sizeof (MidiMessage)); - std::cout << "jack_ringbuffer_write" << std::endl; + // TODO: convert relationContext.recordContext to m + m.time = 0; + m.size = 3; + m.buffer[0] = 0x90; + m.buffer[1] = 0x24; + m.buffer[2] = 0x40; + + jack_ringbuffer_write(ringBuffer, (const char *) &m, sizeof (m)); } else { fwprintf(stderr, L"Error: ring buffer is full → skipping event.\n"); } - currentMidiMessage = MidiMessage(); - currentAttributeIndex = 0; + relationContext.recordContext = RelationContext::RecordContext(); } } @@ -150,6 +226,9 @@ // TODO: optionally mute all; probably enabled by default } + /** + * TODO: use separate class/instance for JACK callbacks to minimize scope and prevent mistakes. + */ int dequeueMessages(jack_nframes_t frames) { const size_t queuedMessages = jack_ringbuffer_read_space(ringBuffer) / sizeof (MidiMessage); void* jackPortBuffer = jack_port_get_buffer(jackPort, frames); // jack_port_get_buffer() must be called outside the loop, otherwise it will multiply the MIDI events @@ -160,7 +239,6 @@ jack_ringbuffer_read(ringBuffer, (char*) &m, sizeof (MidiMessage)); jack_midi_data_t* midiData = jack_midi_event_reserve(jackPortBuffer, m.time, m.size); memcpy(midiData, m.buffer, m.size); - std::cout << "jack_midi_event_reserve" << std::endl; } return 0; }