109 */ |
109 */ |
110 class RelationContext { |
110 class RelationContext { |
111 public: |
111 public: |
112 bool skip = false; |
112 bool skip = false; |
113 |
113 |
114 size_t indexOfEvent = -1; |
114 std::vector<relpipe::reader::handlers::AttributeMetadata> attributes; |
115 size_t indexOfChannel = -1; |
115 |
116 size_t indexOfNoteOn = -1; |
116 bool hasAttribute(const relpipe::common::type::StringX& name) { |
117 size_t indexOfNotePitch = -1; |
117 for (auto a : attributes) if (a.getAttributeName() == name) return true; |
118 size_t indexOfNoteVelocity = -1; |
118 return false; |
119 size_t indexOfControllerId = -1; |
119 } |
120 size_t indexOfControllerValue = -1; |
|
121 size_t indexOfRaw = -1; |
|
122 |
|
123 size_t attributeCount = 0; |
|
124 |
120 |
125 class RecordContext { |
121 class RecordContext { |
126 public: |
122 public: |
127 |
123 |
128 enum class Event { |
124 enum class Event { |
130 CONTROL, |
126 CONTROL, |
131 SYSEX, |
127 SYSEX, |
132 UNKNOWN |
128 UNKNOWN |
133 }; |
129 }; |
134 |
130 |
135 static Event parseEventType(const relpipe::common::type::StringX name) { |
131 static Event parseEventType(const relpipe::common::type::StringX& name) { |
136 if (name == L"note") return Event::NOTE; |
132 if (name == L"note") return Event::NOTE; |
137 else if (name == L"control") return Event::CONTROL; |
133 else if (name == L"control") return Event::CONTROL; |
138 else if (name == L"sysex") return Event::SYSEX; |
134 else if (name == L"sysex") return Event::SYSEX; |
139 else return Event::UNKNOWN; |
135 else return Event::UNKNOWN; |
140 } |
136 } |
199 |
195 |
200 void startRelation(const relpipe::common::type::StringX name, std::vector<relpipe::reader::handlers::AttributeMetadata> attributes) override { |
196 void startRelation(const relpipe::common::type::StringX name, std::vector<relpipe::reader::handlers::AttributeMetadata> attributes) override { |
201 // TODO: validate metadata and prepare attribute mappings (names and types are important, order does not matter) |
197 // TODO: validate metadata and prepare attribute mappings (names and types are important, order does not matter) |
202 |
198 |
203 relationContext = RelationContext(); |
199 relationContext = RelationContext(); |
204 relationContext.attributeCount = attributes.size(); |
200 relationContext.attributes = attributes; |
205 |
|
206 for (int i = 0; i < relationContext.attributeCount; i++) { |
|
207 const relpipe::common::type::StringX attributeName = attributes[i].getAttributeName(); |
|
208 if (attributeName == L"event") relationContext.indexOfEvent = i; |
|
209 else if (attributeName == L"channel") relationContext.indexOfChannel = i; |
|
210 else if (attributeName == L"controller_id") relationContext.indexOfControllerId = i; |
|
211 else if (attributeName == L"controller_value") relationContext.indexOfControllerValue = i; |
|
212 else if (attributeName == L"note_on") relationContext.indexOfNoteOn = i; |
|
213 else if (attributeName == L"note_pitch") relationContext.indexOfNotePitch = i; |
|
214 else if (attributeName == L"note_velocity") relationContext.indexOfNoteVelocity = i; |
|
215 else if (attributeName == L"raw") relationContext.indexOfRaw = i; |
|
216 } |
|
217 |
201 |
218 // TODO: check also data types and skipt relation if important attributes are missing |
202 // TODO: check also data types and skipt relation if important attributes are missing |
219 // TODO: configurable relation name |
203 // TODO: configurable relation name |
220 |
204 |
221 if (relationContext.indexOfRaw == -1) { |
205 if (!relationContext.hasAttribute(L"raw")) { |
222 relationContext.skip = true; |
206 relationContext.skip = true; |
223 fwprintf(stderr, L"Relation „%ls“ will be ignored because mandatory attribute „raw“ is missing.\n", name.c_str()); |
207 fwprintf(stderr, L"Relation „%ls“ will be ignored because mandatory attribute „raw“ is missing.\n", name.c_str()); |
224 } |
208 } |
225 |
209 |
226 } |
210 } |
231 // TODO: if (continueProcessing) {} ? |
215 // TODO: if (continueProcessing) {} ? |
232 |
216 |
233 RelationContext& rel = relationContext; |
217 RelationContext& rel = relationContext; |
234 RelationContext::RecordContext& rec = rel.recordContext; |
218 RelationContext::RecordContext& rec = rel.recordContext; |
235 |
219 |
236 if (rel.indexOfEvent == rec.attributeIndex) rec.event = rec.parseEventType(value); |
220 const auto attributeName = rel.attributes[rec.attributeIndex].getAttributeName(); |
237 else if (rel.indexOfChannel == rec.attributeIndex) rec.channel = std::stoi(value); |
221 |
238 else if (rel.indexOfControllerId == rec.attributeIndex) rec.controllerId = std::stoi(value); |
222 if (attributeName == L"event") rec.event = rec.parseEventType(value); |
239 else if (rel.indexOfControllerValue == rec.attributeIndex) rec.controllerValue = std::stoi(value); |
223 else if (attributeName == L"channel") rec.channel = std::stoi(value); |
240 else if (rel.indexOfNoteOn == rec.attributeIndex) rec.noteOn = value == L"true"; |
224 else if (attributeName == L"controller_id") rec.controllerId = std::stoi(value); |
241 else if (rel.indexOfNotePitch == rec.attributeIndex) rec.notePitch = std::stoi(value); |
225 else if (attributeName == L"controller_value") rec.controllerValue = std::stoi(value); |
242 else if (rel.indexOfNoteVelocity == rec.attributeIndex) rec.noteVelocity = std::stoi(value); |
226 else if (attributeName == L"note_on") rec.noteOn = value == L"true"; |
243 else if (rel.indexOfRaw == rec.attributeIndex) rec.raw = value; |
227 else if (attributeName == L"note_pitch") rec.notePitch = std::stoi(value); |
|
228 else if (attributeName == L"note_velocity") rec.noteVelocity = std::stoi(value); |
|
229 else if (attributeName == L"raw") rec.raw = value; |
244 |
230 |
245 rec.attributeIndex++; |
231 rec.attributeIndex++; |
246 |
232 |
247 if (rec.attributeIndex == rel.attributeCount) { |
233 if (rec.attributeIndex == rel.attributes.size()) { |
248 |
234 |
249 while (jack_ringbuffer_write_space(realTimeContext.ringBuffer) < sizeof (MidiMessage)) usleep(1000); // should not happen, the real-time thread should be faster |
235 while (jack_ringbuffer_write_space(realTimeContext.ringBuffer) < sizeof (MidiMessage)) usleep(1000); // should not happen, the real-time thread should be faster |
250 |
236 |
251 MidiMessage m; |
237 MidiMessage m; |
252 |
238 |