155 |
160 |
156 realTimeContext.ringBuffer = jack_ringbuffer_create(realTimeContext.RING_BUFFER_SIZE * sizeof (MidiMessage)); |
161 realTimeContext.ringBuffer = jack_ringbuffer_create(realTimeContext.RING_BUFFER_SIZE * sizeof (MidiMessage)); |
157 |
162 |
158 jack_set_process_callback(realTimeContext.jackClient, RealTimeContext::processCallback, &realTimeContext); |
163 jack_set_process_callback(realTimeContext.jackClient, RealTimeContext::processCallback, &realTimeContext); |
159 // TODO: report also other events (connections etc.) |
164 // TODO: report also other events (connections etc.) |
160 // TODO: jack_set_error_function() + avoid mixing char and wchar output |
165 jack_set_error_function(jackErrorCallback); |
|
166 jack_set_info_function(jackErrorCallback); |
161 |
167 |
162 realTimeContext.jackPort = jack_port_register(realTimeContext.jackClient, "output", JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0); |
168 realTimeContext.jackPort = jack_port_register(realTimeContext.jackClient, "output", JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0); |
163 if (realTimeContext.jackPort == nullptr) throw JackException(L"Could not register port."); |
169 if (realTimeContext.jackPort == nullptr) throw JackException(L"Could not register port."); |
164 |
170 |
165 if (mlockall(MCL_CURRENT | MCL_FUTURE)) fwprintf(stderr, L"Warning: Can not lock memory.\n"); |
171 if (mlockall(MCL_CURRENT | MCL_FUTURE)) fwprintf(stderr, L"Warning: Can not lock memory.\n"); |
192 } |
198 } |
193 |
199 |
194 // TODO: check also data types and skipt relation if important attributes are missing |
200 // TODO: check also data types and skipt relation if important attributes are missing |
195 // TODO: configurable relation name |
201 // TODO: configurable relation name |
196 |
202 |
197 if (relationContext.indexOfEvent == -1 || relationContext.indexOfRaw == -1) { |
203 if (relationContext.indexOfRaw == -1) { |
198 relationContext.skip = true; |
204 relationContext.skip = true; |
199 fwprintf(stderr, L"Relation „%ls“ will be ignored because mandatory attributes are missing.\n", name.c_str()); |
205 fwprintf(stderr, L"Relation „%ls“ will be ignored because mandatory attribute „raw“ is missing.\n", name.c_str()); |
200 } |
206 } |
201 |
207 |
202 } |
208 } |
203 |
209 |
204 void attribute(const relpipe::common::type::StringX& value) override { |
210 void attribute(const relpipe::common::type::StringX& value) override { |
205 if (relationContext.skip) return; |
211 if (relationContext.skip) return; |
206 // TODO: switch to RelationalReaderStringHandler |
212 // TODO: switch to RelationalReaderStringHandler |
207 // TODO: if (continueProcessing) {} ? |
213 // TODO: if (continueProcessing) {} ? |
208 |
214 |
209 // memcpy(currentMidiMessage.buffer, ….buffer, ….size); |
215 RelationContext& rel = relationContext; |
210 // currentMidiMessage.size = …; |
216 RelationContext::RecordContext& rec = rel.recordContext; |
211 // currentMidiMessage.time = …; |
217 |
212 |
218 if (rel.indexOfEvent == rec.attributeIndex) rec.event = rec.parseEventType(value); |
213 { |
219 else if (rel.indexOfChannel == rec.attributeIndex) rec.channel = std::stoi(value); |
214 RelationContext& rel = relationContext; |
220 else if (rel.indexOfControllerId == rec.attributeIndex) rec.controllerId = std::stoi(value); |
215 RelationContext::RecordContext& rec = rel.recordContext; |
221 else if (rel.indexOfControllerValue == rec.attributeIndex) rec.controllerValue = std::stoi(value); |
216 |
222 else if (rel.indexOfNoteOn == rec.attributeIndex) rec.noteOn = value == L"true"; |
217 if (rel.indexOfEvent == rec.attributeIndex) rec.event = rec.parseEventType(value); |
223 else if (rel.indexOfNotePitch == rec.attributeIndex) rec.notePitch = std::stoi(value); |
218 else if (rel.indexOfChannel == rec.attributeIndex) rec.channel = std::stoi(value); |
224 else if (rel.indexOfNoteVelocity == rec.attributeIndex) rec.noteVelocity = std::stoi(value); |
219 else if (rel.indexOfControllerId == rec.attributeIndex) rec.controllerId = std::stoi(value); |
225 else if (rel.indexOfRaw == rec.attributeIndex) rec.raw = value; |
220 else if (rel.indexOfControllerValue == rec.attributeIndex) rec.controllerValue = std::stoi(value); |
226 |
221 else if (rel.indexOfNoteOn == rec.attributeIndex) rec.noteOn = value == L"true"; |
227 rec.attributeIndex++; |
222 else if (rel.indexOfNotePitch == rec.attributeIndex) rec.notePitch = std::stoi(value); |
228 |
223 else if (rel.indexOfNoteVelocity == rec.attributeIndex) rec.noteVelocity = std::stoi(value); |
229 if (rec.attributeIndex == rel.attributeCount) { |
224 else if (rel.indexOfRaw == rec.attributeIndex) rec.raw = value; |
|
225 } |
|
226 |
|
227 relationContext.recordContext.attributeIndex++; |
|
228 |
|
229 if (relationContext.recordContext.attributeIndex == relationContext.attributeCount) { |
|
230 |
230 |
231 while (jack_ringbuffer_write_space(realTimeContext.ringBuffer) < sizeof (MidiMessage)) usleep(1000); // should not happen, the real-time thread should be faster |
231 while (jack_ringbuffer_write_space(realTimeContext.ringBuffer) < sizeof (MidiMessage)) usleep(1000); // should not happen, the real-time thread should be faster |
232 |
232 |
233 MidiMessage m; |
233 MidiMessage m; |
234 |
234 |
235 // TODO: convert relationContext.recordContext to m |
|
236 // TODO: correct timing? |
235 // TODO: correct timing? |
237 m.time = 0; |
236 m.time = 0; |
238 m.size = 3; |
237 |
239 m.buffer[0] = 0x90; |
238 if (rec.event == RelationContext::RecordContext::Event::NOTE) { |
240 m.buffer[1] = 0x24; |
239 m.size = 3; |
241 m.buffer[2] = 0x40; |
240 m.buffer[0] = (rec.noteOn ? 0x90 : 0x80) | rec.channel; |
|
241 m.buffer[1] = rec.notePitch; |
|
242 m.buffer[2] = rec.noteVelocity; |
|
243 } else if (rec.event == RelationContext::RecordContext::Event::CONTROL) { |
|
244 m.size = 3; |
|
245 m.buffer[0] = 0xB0 | rec.channel; |
|
246 m.buffer[1] = rec.controllerId; |
|
247 m.buffer[2] = rec.controllerValue; |
|
248 } else { // SysEx and other raw messages |
|
249 m.size = 0; |
|
250 size_t nibble = 0; |
|
251 for (int i = 0; i < rec.raw.size() && m.size < sizeof (m.buffer); i++) { |
|
252 wchar_t ch = rec.raw[i]; |
|
253 |
|
254 if (ch == L' ') continue; |
|
255 else if (ch >= L'0' && ch <= L'9') m.buffer[m.size] += (ch - '0') /**/ << (nibble % 2 ? 0 : 4); |
|
256 else if (ch >= L'a' && ch <= L'f') m.buffer[m.size] += (ch - 'a' + 10) << (nibble % 2 ? 0 : 4); |
|
257 else if (ch >= L'A' && ch <= L'F') m.buffer[m.size] += (ch - 'A' + 10) << (nibble % 2 ? 0 : 4); |
|
258 else throw JackException(L"Invalid character in the hexadeximal sequence: „" + std::wstring(1, ch) + L"“."); |
|
259 |
|
260 nibble++; |
|
261 if (nibble % 2 == 0) m.size++; |
|
262 } |
|
263 } |
|
264 |
|
265 // TODO: if (m.size == 0) fwprintf(stderr, L"Missing data\n"); |
242 |
266 |
243 jack_ringbuffer_write(realTimeContext.ringBuffer, (const char *) &m, sizeof (m)); |
267 jack_ringbuffer_write(realTimeContext.ringBuffer, (const char *) &m, sizeof (m)); |
244 |
268 |
245 relationContext.recordContext = RelationContext::RecordContext(); |
269 relationContext.recordContext = RelationContext::RecordContext(); |
246 } |
270 } |