58 |
58 |
59 std::atomic<bool> continueProcessing{true}; |
59 std::atomic<bool> continueProcessing{true}; |
60 |
60 |
61 const int RING_BUFFER_SIZE = 100; |
61 const int RING_BUFFER_SIZE = 100; |
62 |
62 |
|
63 /** |
|
64 * Is passed through the ring buffer |
|
65 * from the relpipe-reading thread to the jack-writing thread (callback). |
|
66 */ |
63 struct MidiMessage { |
67 struct MidiMessage { |
64 uint8_t buffer[4096]; |
68 uint8_t buffer[4096]; |
65 uint32_t size; |
69 uint32_t size; |
66 uint32_t time; |
70 uint32_t time; |
67 }; |
71 }; |
68 |
72 |
69 /** |
73 /** |
70 * The message being prepared before enqueing in the ringBuffer. |
74 * Temporary storage of values read from relational input. |
71 * Not the message dequeued from the ringBuffer (different thread). |
|
72 */ |
75 */ |
73 MidiMessage currentMidiMessage; |
76 class RelationContext { |
74 size_t currentAttributeCount = 0; |
77 public: |
75 size_t currentAttributeIndex = 0; |
78 size_t indexOfEvent = -1; |
76 |
79 size_t indexOfChannel = -1; |
|
80 size_t indexOfNoteOn = -1; |
|
81 size_t indexOfNotePitch = -1; |
|
82 size_t indexOfNoteVelocity = -1; |
|
83 size_t indexOfControllerId = -1; |
|
84 size_t indexOfControllerValue = -1; |
|
85 size_t indexOfRaw = -1; |
|
86 |
|
87 size_t attributeCount = 0; |
|
88 |
|
89 class RecordContext { |
|
90 public: |
|
91 |
|
92 enum class Event { |
|
93 NOTE, |
|
94 CONTROL, |
|
95 SYSEX, |
|
96 UNKNOWN |
|
97 }; |
|
98 |
|
99 static Event parseEventType(const relpipe::common::type::StringX name) { |
|
100 if (name == L"note") return Event::NOTE; |
|
101 else if (name == L"control") return Event::CONTROL; |
|
102 else if (name == L"sysex") return Event::SYSEX; |
|
103 else return Event::UNKNOWN; |
|
104 } |
|
105 |
|
106 Event event; |
|
107 relpipe::common::type::Integer channel; |
|
108 relpipe::common::type::Boolean noteOn; |
|
109 relpipe::common::type::Integer notePitch; |
|
110 relpipe::common::type::Integer noteVelocity; |
|
111 relpipe::common::type::Integer controllerId; |
|
112 relpipe::common::type::Integer controllerValue; |
|
113 relpipe::common::type::StringX raw; |
|
114 |
|
115 size_t attributeIndex = 0; |
|
116 |
|
117 } recordContext; |
|
118 |
|
119 } relationContext; |
77 |
120 |
78 public: |
121 public: |
79 |
122 |
80 JackHandler(Configuration& configuration) : configuration(configuration) { |
123 JackHandler(Configuration& configuration) : configuration(configuration) { |
81 // Initialize JACK connection: |
124 // Initialize JACK connection: |
103 } |
146 } |
104 |
147 |
105 void startRelation(const relpipe::common::type::StringX name, std::vector<relpipe::reader::handlers::AttributeMetadata> attributes) override { |
148 void startRelation(const relpipe::common::type::StringX name, std::vector<relpipe::reader::handlers::AttributeMetadata> attributes) override { |
106 // TODO: validate metadata and prepare attribute mappings (names and types are important, order does not matter) |
149 // TODO: validate metadata and prepare attribute mappings (names and types are important, order does not matter) |
107 |
150 |
108 currentAttributeCount = attributes.size(); |
151 relationContext = RelationContext(); |
|
152 relationContext.attributeCount = attributes.size(); |
|
153 |
|
154 for (int i = 0; i < relationContext.attributeCount; i++) { |
|
155 const relpipe::common::type::StringX attributeName = attributes[i].getAttributeName(); |
|
156 if (attributeName == L"event") relationContext.indexOfEvent = i; |
|
157 else if (attributeName == L"channel") relationContext.indexOfChannel = i; |
|
158 else if (attributeName == L"controller_id") relationContext.indexOfControllerId = i; |
|
159 else if (attributeName == L"controller_value") relationContext.indexOfControllerValue = i; |
|
160 else if (attributeName == L"note_on") relationContext.indexOfNoteOn = i; |
|
161 else if (attributeName == L"note_pitch") relationContext.indexOfNotePitch = i; |
|
162 else if (attributeName == L"note_velocity") relationContext.indexOfNoteVelocity = i; |
|
163 else if (attributeName == L"raw") relationContext.indexOfRaw = i; |
|
164 } |
|
165 |
|
166 // TODO: check also data types and skipt relation if important attributes are missing |
|
167 // TODO: configurable relation name |
109 } |
168 } |
110 |
169 |
111 void attribute(const relpipe::common::type::StringX& value) override { |
170 void attribute(const relpipe::common::type::StringX& value) override { |
|
171 // TODO: switch to RelationalReaderStringHandler |
112 // TODO: append to current message + if this is last attribute, put whole message to the ring buffer |
172 // TODO: append to current message + if this is last attribute, put whole message to the ring buffer |
113 // TODO: if (continueProcessing) {} ? |
173 // TODO: if (continueProcessing) {} ? |
114 |
174 |
115 // memcpy(currentMidiMessage.buffer, ….buffer, ….size); |
175 // memcpy(currentMidiMessage.buffer, ….buffer, ….size); |
116 // currentMidiMessage.size = …; |
176 // currentMidiMessage.size = …; |
117 // currentMidiMessage.time = …; |
177 // currentMidiMessage.time = …; |
118 |
178 |
119 // TODO: correct timing? |
179 // TODO: correct timing? |
120 // TODO: real data |
180 // TODO: real data |
121 currentMidiMessage.time = 0; |
181 |
122 currentMidiMessage.size = 3; |
182 { |
123 currentMidiMessage.buffer[0] = 0x90; |
183 RelationContext& rel = relationContext; |
124 currentMidiMessage.buffer[1] = 0x24; |
184 RelationContext::RecordContext& rec = rel.recordContext; |
125 currentMidiMessage.buffer[2] = 0x40; |
185 |
126 |
186 if (rel.indexOfEvent == rec.attributeIndex) rec.event = rec.parseEventType(value); |
127 currentAttributeIndex++; |
187 else if (rel.indexOfChannel == rec.attributeIndex) rec.channel = std::stoi(value); |
128 |
188 else if (rel.indexOfControllerId == rec.attributeIndex) rec.controllerId = std::stoi(value); |
129 if (currentAttributeIndex == currentAttributeCount) { |
189 else if (rel.indexOfControllerValue == rec.attributeIndex) rec.controllerValue = std::stoi(value); |
|
190 else if (rel.indexOfNoteOn == rec.attributeIndex) rec.noteOn = value == L"true"; |
|
191 else if (rel.indexOfNotePitch == rec.attributeIndex) rec.notePitch = std::stoi(value); |
|
192 else if (rel.indexOfNoteVelocity == rec.attributeIndex) rec.noteVelocity = std::stoi(value); |
|
193 else if (rel.indexOfRaw == rec.attributeIndex) rec.raw = value; |
|
194 } |
|
195 |
|
196 relationContext.recordContext.attributeIndex++; |
|
197 |
|
198 if (relationContext.recordContext.attributeIndex == relationContext.attributeCount) { |
130 if (jack_ringbuffer_write_space(ringBuffer) >= sizeof (MidiMessage)) { |
199 if (jack_ringbuffer_write_space(ringBuffer) >= sizeof (MidiMessage)) { |
131 jack_ringbuffer_write(ringBuffer, (const char *) ¤tMidiMessage, sizeof (MidiMessage)); |
200 MidiMessage m; |
132 std::cout << "jack_ringbuffer_write" << std::endl; |
201 |
|
202 // TODO: convert relationContext.recordContext to m |
|
203 m.time = 0; |
|
204 m.size = 3; |
|
205 m.buffer[0] = 0x90; |
|
206 m.buffer[1] = 0x24; |
|
207 m.buffer[2] = 0x40; |
|
208 |
|
209 jack_ringbuffer_write(ringBuffer, (const char *) &m, sizeof (m)); |
133 } else { |
210 } else { |
134 fwprintf(stderr, L"Error: ring buffer is full → skipping event.\n"); |
211 fwprintf(stderr, L"Error: ring buffer is full → skipping event.\n"); |
135 } |
212 } |
136 |
213 |
137 currentMidiMessage = MidiMessage(); |
214 relationContext.recordContext = RelationContext::RecordContext(); |
138 currentAttributeIndex = 0; |
|
139 } |
215 } |
140 |
216 |
141 } |
217 } |
142 |
218 |
143 void endOfPipe() { |
219 void endOfPipe() { |
148 usleep(1000000); |
224 usleep(1000000); |
149 // TODO: better waiting (ringBuffer might be empty, but events have not been sent to JACK yet) |
225 // TODO: better waiting (ringBuffer might be empty, but events have not been sent to JACK yet) |
150 // TODO: optionally mute all; probably enabled by default |
226 // TODO: optionally mute all; probably enabled by default |
151 } |
227 } |
152 |
228 |
|
229 /** |
|
230 * TODO: use separate class/instance for JACK callbacks to minimize scope and prevent mistakes. |
|
231 */ |
153 int dequeueMessages(jack_nframes_t frames) { |
232 int dequeueMessages(jack_nframes_t frames) { |
154 const size_t queuedMessages = jack_ringbuffer_read_space(ringBuffer) / sizeof (MidiMessage); |
233 const size_t queuedMessages = jack_ringbuffer_read_space(ringBuffer) / sizeof (MidiMessage); |
155 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 |
234 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 |
156 jack_midi_clear_buffer(jackPortBuffer); // TODO: clean buffer? |
235 jack_midi_clear_buffer(jackPortBuffer); // TODO: clean buffer? |
157 for (size_t i = 0; i < queuedMessages && i < frames; i++) { |
236 for (size_t i = 0; i < queuedMessages && i < frames; i++) { |
158 // TODO: correct timing? |
237 // TODO: correct timing? |
159 MidiMessage m; |
238 MidiMessage m; |
160 jack_ringbuffer_read(ringBuffer, (char*) &m, sizeof (MidiMessage)); |
239 jack_ringbuffer_read(ringBuffer, (char*) &m, sizeof (MidiMessage)); |
161 jack_midi_data_t* midiData = jack_midi_event_reserve(jackPortBuffer, m.time, m.size); |
240 jack_midi_data_t* midiData = jack_midi_event_reserve(jackPortBuffer, m.time, m.size); |
162 memcpy(midiData, m.buffer, m.size); |
241 memcpy(midiData, m.buffer, m.size); |
163 std::cout << "jack_midi_event_reserve" << std::endl; |
|
164 } |
242 } |
165 return 0; |
243 return 0; |
166 } |
244 } |
167 |
245 |
168 void finish(int sig) { |
246 void finish(int sig) { |