70 class RealTimeContext { |
71 class RealTimeContext { |
71 public: |
72 public: |
72 jack_client_t* jackClient = nullptr; |
73 jack_client_t* jackClient = nullptr; |
73 jack_port_t* jackPort = nullptr; |
74 jack_port_t* jackPort = nullptr; |
74 jack_ringbuffer_t* ringBuffer = nullptr; |
75 jack_ringbuffer_t* ringBuffer = nullptr; |
|
76 |
|
77 pthread_mutex_t processingLock = PTHREAD_MUTEX_INITIALIZER; |
|
78 pthread_cond_t processingDone = PTHREAD_COND_INITIALIZER; |
75 |
79 |
76 const int RING_BUFFER_SIZE = 100; |
80 const int RING_BUFFER_SIZE = 100; |
77 |
81 |
78 int processCallback(jack_nframes_t frames) { |
82 int processCallback(jack_nframes_t frames) { |
79 const size_t queuedMessages = jack_ringbuffer_read_space(ringBuffer) / sizeof (MidiMessage); |
83 const size_t queuedMessages = jack_ringbuffer_read_space(ringBuffer) / sizeof (MidiMessage); |
84 MidiMessage m; |
88 MidiMessage m; |
85 jack_ringbuffer_read(ringBuffer, (char*) &m, sizeof (MidiMessage)); |
89 jack_ringbuffer_read(ringBuffer, (char*) &m, sizeof (MidiMessage)); |
86 jack_midi_data_t* midiData = jack_midi_event_reserve(jackPortBuffer, m.time, m.size); |
90 jack_midi_data_t* midiData = jack_midi_event_reserve(jackPortBuffer, m.time, m.size); |
87 memcpy(midiData, m.buffer, m.size); |
91 memcpy(midiData, m.buffer, m.size); |
88 } |
92 } |
|
93 |
|
94 if (pthread_mutex_trylock(&processingLock) == 0) { |
|
95 pthread_cond_signal(&processingDone); |
|
96 pthread_mutex_unlock(&processingLock); |
|
97 } |
|
98 |
89 return 0; |
99 return 0; |
90 } |
100 } |
91 |
101 |
92 int syncCallback(jack_transport_state_t state, jack_position_t* position) { |
102 int syncCallback(jack_transport_state_t state, jack_position_t* position) { |
93 std::wcerr << L"syncCallback()" << std::endl; |
|
94 return true; |
103 return true; |
95 } |
104 } |
96 |
105 |
97 static int processCallback(jack_nframes_t frames, void* instance) { |
106 static int processCallback(jack_nframes_t frames, void* instance) { |
98 return static_cast<RealTimeContext*> (instance)->processCallback(frames); |
107 return static_cast<RealTimeContext*> (instance)->processCallback(frames); |
158 void finalize() { |
167 void finalize() { |
159 // Close JACK connection: |
168 // Close JACK connection: |
160 jack_deactivate(realTimeContext.jackClient); |
169 jack_deactivate(realTimeContext.jackClient); |
161 jack_client_close(realTimeContext.jackClient); |
170 jack_client_close(realTimeContext.jackClient); |
162 jack_ringbuffer_free(realTimeContext.ringBuffer); |
171 jack_ringbuffer_free(realTimeContext.ringBuffer); |
|
172 pthread_mutex_unlock(&realTimeContext.processingLock); |
163 } |
173 } |
164 |
174 |
165 void failInConstructor(const relpipe::common::type::StringX& errorMessage) { |
175 void failInConstructor(const relpipe::common::type::StringX& errorMessage) { |
166 finalize(); |
176 finalize(); |
167 throw JackException(errorMessage); |
177 throw JackException(errorMessage); |
168 } |
178 } |
169 |
179 |
170 public: |
180 public: |
171 |
181 |
172 JackHandler(Configuration& configuration) : configuration(configuration) { |
182 JackHandler(Configuration& configuration) : configuration(configuration) { |
|
183 pthread_mutex_unlock(&realTimeContext.processingLock); |
|
184 |
173 // Initialize JACK connection: |
185 // Initialize JACK connection: |
174 std::string clientName = convertor.to_bytes(configuration.jackClientName); |
186 std::string clientName = convertor.to_bytes(configuration.jackClientName); |
175 realTimeContext.jackClient = jack_client_open(clientName.c_str(), JackNullOption, nullptr); |
187 realTimeContext.jackClient = jack_client_open(clientName.c_str(), JackNullOption, nullptr); |
176 if (realTimeContext.jackClient == nullptr) failInConstructor(L"Could not create JACK client."); |
188 if (realTimeContext.jackClient == nullptr) failInConstructor(L"Could not create JACK client."); |
177 |
189 |
282 } |
294 } |
283 |
295 |
284 } |
296 } |
285 |
297 |
286 void endOfPipe() { |
298 void endOfPipe() { |
287 // Wait until the ring buffer is empty |
|
288 while (continueProcessing && jack_ringbuffer_read_space(realTimeContext.ringBuffer)) usleep(1000); |
|
289 usleep(1000000); |
|
290 // TODO: better waiting (ringBuffer might be empty, but events have not been sent to JACK yet) |
|
291 // TODO: optionally mute all; probably enabled by default |
299 // TODO: optionally mute all; probably enabled by default |
|
300 |
|
301 // Wait until the ring buffer is empty (messages dequeued from the buffer) and real-time cycle was finished (messages passed to JACK) |
|
302 while (continueProcessing && jack_ringbuffer_read_space(realTimeContext.ringBuffer)) pthread_cond_wait(&realTimeContext.processingDone, &realTimeContext.processingLock); |
292 } |
303 } |
293 |
304 |
294 void finish(int sig) { |
305 void finish(int sig) { |
295 continueProcessing = false; |
306 continueProcessing = false; |
296 } |
307 } |