--- 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<relpipe::reader::handlers::AttributeMetadata> 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;
}