153 static void jackErrorCallback(const char * message) { |
153 static void jackErrorCallback(const char * message) { |
154 std::wstring_convert < std::codecvt_utf8<wchar_t>> convertor; // TODO: local system encoding |
154 std::wstring_convert < std::codecvt_utf8<wchar_t>> convertor; // TODO: local system encoding |
155 std::wcerr << L"JACK: " << convertor.from_bytes(message) << std::endl; |
155 std::wcerr << L"JACK: " << convertor.from_bytes(message) << std::endl; |
156 } |
156 } |
157 |
157 |
|
158 void finalize() { |
|
159 // Close JACK connection: |
|
160 jack_deactivate(realTimeContext.jackClient); |
|
161 jack_client_close(realTimeContext.jackClient); |
|
162 jack_ringbuffer_free(realTimeContext.ringBuffer); |
|
163 } |
|
164 |
|
165 void failInConstructor(const relpipe::common::type::StringX& errorMessage) { |
|
166 finalize(); |
|
167 throw JackException(errorMessage); |
|
168 } |
|
169 |
158 public: |
170 public: |
159 |
171 |
160 JackHandler(Configuration& configuration) : configuration(configuration) { |
172 JackHandler(Configuration& configuration) : configuration(configuration) { |
161 // Initialize JACK connection: |
173 // Initialize JACK connection: |
162 std::string clientName = convertor.to_bytes(configuration.jackClientName); |
174 std::string clientName = convertor.to_bytes(configuration.jackClientName); |
163 realTimeContext.jackClient = jack_client_open(clientName.c_str(), JackNullOption, nullptr); |
175 realTimeContext.jackClient = jack_client_open(clientName.c_str(), JackNullOption, nullptr); |
164 if (realTimeContext.jackClient == nullptr) throw JackException(L"Could not create JACK client."); |
176 if (realTimeContext.jackClient == nullptr) failInConstructor(L"Could not create JACK client."); |
165 |
177 |
166 realTimeContext.ringBuffer = jack_ringbuffer_create(realTimeContext.RING_BUFFER_SIZE * sizeof (MidiMessage)); |
178 realTimeContext.ringBuffer = jack_ringbuffer_create(realTimeContext.RING_BUFFER_SIZE * sizeof (MidiMessage)); |
167 |
179 |
168 jack_set_process_callback(realTimeContext.jackClient, RealTimeContext::processCallback, &realTimeContext); |
180 jack_set_process_callback(realTimeContext.jackClient, RealTimeContext::processCallback, &realTimeContext); |
169 jack_set_sync_callback(realTimeContext.jackClient, RealTimeContext::syncCallback, &realTimeContext); |
181 jack_set_sync_callback(realTimeContext.jackClient, RealTimeContext::syncCallback, &realTimeContext); |
170 // TODO: report also other events (connections etc.) |
182 // TODO: report also other events (connections etc.) |
171 jack_set_error_function(jackErrorCallback); |
183 jack_set_error_function(jackErrorCallback); |
172 jack_set_info_function(jackErrorCallback); |
184 jack_set_info_function(jackErrorCallback); |
173 |
185 |
174 realTimeContext.jackPort = jack_port_register(realTimeContext.jackClient, "output", JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0); |
186 realTimeContext.jackPort = jack_port_register(realTimeContext.jackClient, "output", JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0); |
175 if (realTimeContext.jackPort == nullptr) throw JackException(L"Could not register port."); |
187 if (realTimeContext.jackPort == nullptr) failInConstructor(L"Could not register the JACK port."); |
176 |
188 |
177 if (mlockall(MCL_CURRENT | MCL_FUTURE)) fwprintf(stderr, L"Warning: Can not lock memory.\n"); |
189 if (mlockall(MCL_CURRENT | MCL_FUTURE)) fwprintf(stderr, L"Warning: Can not lock memory.\n"); |
178 |
190 |
179 int jackError = jack_activate(realTimeContext.jackClient); |
191 int jackError = jack_activate(realTimeContext.jackClient); |
180 if (jackError) throw JackException(L"Could not activate client."); |
192 if (jackError) failInConstructor(L"Could not activate the JACK client."); |
181 |
193 |
182 |
194 |
183 // Connect to configured destination ports: |
195 // Connect to configured destination ports: |
184 const char* jackPortName = jack_port_name(realTimeContext.jackPort); |
196 const char* jackPortName = jack_port_name(realTimeContext.jackPort); |
185 for (auto destinationPort : configuration.portsToConnect) { |
197 for (auto destinationPort : configuration.portsToConnect) { |
186 int error = jack_connect(realTimeContext.jackClient, jackPortName, convertor.to_bytes(destinationPort).c_str()); |
198 int error = jack_connect(realTimeContext.jackClient, jackPortName, convertor.to_bytes(destinationPort).c_str()); |
187 if (error) throw JackException(L"Connection to the JACK port failed: " + destinationPort); |
199 if (error) failInConstructor(L"Connection to the JACK port failed: " + destinationPort); |
188 } |
200 } |
189 |
201 |
190 // Wait for a port connection, because it does not make much sense to send MIDI events nowhere: |
202 // Wait for a port connection, because it does not make much sense to send MIDI events nowhere: |
191 while (jack_port_connected(realTimeContext.jackPort) < configuration.requiredJackConnections) usleep(10000); |
203 while (jack_port_connected(realTimeContext.jackPort) < configuration.requiredJackConnections) usleep(10000); |
192 |
204 |
193 // TODO: finalize on exceptions |
|
194 } |
205 } |
195 |
206 |
196 void startRelation(const relpipe::common::type::StringX name, std::vector<relpipe::reader::handlers::AttributeMetadata> attributes) override { |
207 void startRelation(const relpipe::common::type::StringX name, std::vector<relpipe::reader::handlers::AttributeMetadata> attributes) override { |
197 // TODO: validate metadata and prepare attribute mappings (names and types are important, order does not matter) |
208 // TODO: validate metadata and prepare attribute mappings (names and types are important, order does not matter) |
198 |
209 |