attribute mapping v_0
authorFrantišek Kučera <franta-hg@frantovo.cz>
Sat, 03 Oct 2020 00:05:43 +0200
branchv_0
changeset 10 6aa881c10efd
parent 9 14cf28d7681c
child 11 1c0b9981eb6d
attribute mapping
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<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 *) &currentMidiMessage, 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;
 	}