--- a/src/JackHandler.h Sat Oct 03 00:05:43 2020 +0200
+++ b/src/JackHandler.h Sat Oct 03 11:37:07 2020 +0200
@@ -52,14 +52,8 @@
Configuration& configuration;
std::wstring_convert<std::codecvt_utf8<wchar_t>> convertor; // TODO: local system encoding
- jack_client_t* jackClient = nullptr;
- jack_port_t* jackPort = nullptr;
- jack_ringbuffer_t* ringBuffer = nullptr;
-
std::atomic<bool> continueProcessing{true};
- const int RING_BUFFER_SIZE = 100;
-
/**
* Is passed through the ring buffer
* from the relpipe-reading thread to the jack-writing thread (callback).
@@ -71,6 +65,37 @@
};
/**
+ * JACK callbacks (called from the real-time thread)
+ */
+ class RealTimeContext {
+ public:
+ jack_client_t* jackClient = nullptr;
+ jack_port_t* jackPort = nullptr;
+ jack_ringbuffer_t* ringBuffer = nullptr;
+
+ const int RING_BUFFER_SIZE = 100;
+
+ int processCallback(jack_nframes_t frames) {
+ const size_t queuedMessages = jack_ringbuffer_read_space(ringBuffer) / sizeof (MidiMessage);
+ 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
+ jack_midi_clear_buffer(jackPortBuffer); // TODO: clean buffer?
+ for (size_t i = 0; i < queuedMessages && i < frames; i++) {
+ // TODO: correct timing?
+ MidiMessage m;
+ jack_ringbuffer_read(ringBuffer, (char*) &m, sizeof (MidiMessage));
+ jack_midi_data_t* midiData = jack_midi_event_reserve(jackPortBuffer, m.time, m.size);
+ memcpy(midiData, m.buffer, m.size);
+ }
+ return 0;
+ }
+
+ static int processCallback(jack_nframes_t frames, void* arg) {
+ return static_cast<RealTimeContext*> (arg)->processCallback(frames);
+ }
+
+ } realTimeContext;
+
+ /**
* Temporary storage of values read from relational input.
*/
class RelationContext {
@@ -123,24 +148,24 @@
JackHandler(Configuration& configuration) : configuration(configuration) {
// Initialize JACK connection:
std::string clientName = convertor.to_bytes(configuration.jackClientName);
- jackClient = jack_client_open(clientName.c_str(), JackNullOption, nullptr);
- if (jackClient == nullptr) throw JackException(L"Could not create JACK client.");
+ realTimeContext.jackClient = jack_client_open(clientName.c_str(), JackNullOption, nullptr);
+ if (realTimeContext.jackClient == nullptr) throw JackException(L"Could not create JACK client.");
- ringBuffer = jack_ringbuffer_create(RING_BUFFER_SIZE * sizeof (MidiMessage));
+ realTimeContext.ringBuffer = jack_ringbuffer_create(realTimeContext.RING_BUFFER_SIZE * sizeof (MidiMessage));
- jack_set_process_callback(jackClient, relpipe::out::jack::dequeueMessages, this);
+ jack_set_process_callback(realTimeContext.jackClient, RealTimeContext::processCallback, &realTimeContext);
// TODO: report also other events (connections etc.)
- jackPort = jack_port_register(jackClient, "output", JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0);
- if (jackPort == nullptr) throw JackException(L"Could not register port.");
+ realTimeContext.jackPort = jack_port_register(realTimeContext.jackClient, "output", JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0);
+ if (realTimeContext.jackPort == nullptr) throw JackException(L"Could not register port.");
if (mlockall(MCL_CURRENT | MCL_FUTURE)) fwprintf(stderr, L"Warning: Can not lock memory.\n");
- int jackError = jack_activate(jackClient);
+ int jackError = jack_activate(realTimeContext.jackClient);
if (jackError) throw JackException(L"Could not activate client.");
// Wait for a port connection, because it does not make much sense to send MIDI events nowhere:
- while (jack_port_connected(jackPort) < configuration.requiredJackConnections) usleep(10000);
+ while (jack_port_connected(realTimeContext.jackPort) < configuration.requiredJackConnections) usleep(10000);
// TODO: configurable auto-connection to another client/port
}
@@ -162,7 +187,7 @@
else if (attributeName == L"note_velocity") relationContext.indexOfNoteVelocity = i;
else if (attributeName == L"raw") relationContext.indexOfRaw = i;
}
-
+
// TODO: check also data types and skipt relation if important attributes are missing
// TODO: configurable relation name
}
@@ -196,7 +221,7 @@
relationContext.recordContext.attributeIndex++;
if (relationContext.recordContext.attributeIndex == relationContext.attributeCount) {
- if (jack_ringbuffer_write_space(ringBuffer) >= sizeof (MidiMessage)) {
+ if (jack_ringbuffer_write_space(realTimeContext.ringBuffer) >= sizeof (MidiMessage)) {
MidiMessage m;
// TODO: convert relationContext.recordContext to m
@@ -206,7 +231,7 @@
m.buffer[1] = 0x24;
m.buffer[2] = 0x40;
- jack_ringbuffer_write(ringBuffer, (const char *) &m, sizeof (m));
+ jack_ringbuffer_write(realTimeContext.ringBuffer, (const char *) &m, sizeof (m));
} else {
fwprintf(stderr, L"Error: ring buffer is full → skipping event.\n");
}
@@ -220,47 +245,25 @@
// TODO: send optional (configurable) MIDI events
// Wait until the ring buffer is empty
- while (continueProcessing && jack_ringbuffer_read_space(ringBuffer)) usleep(1000);
+ while (continueProcessing && jack_ringbuffer_read_space(realTimeContext.ringBuffer)) usleep(1000);
usleep(1000000);
// TODO: better waiting (ringBuffer might be empty, but events have not been sent to JACK yet)
// TODO: optionally mute all; probably enabled by default
}
- /**
- * TODO: use separate class/instance for JACK callbacks to minimize scope and prevent mistakes.
- */
- int dequeueMessages(jack_nframes_t frames) {
- const size_t queuedMessages = jack_ringbuffer_read_space(ringBuffer) / sizeof (MidiMessage);
- 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
- jack_midi_clear_buffer(jackPortBuffer); // TODO: clean buffer?
- for (size_t i = 0; i < queuedMessages && i < frames; i++) {
- // TODO: correct timing?
- MidiMessage m;
- jack_ringbuffer_read(ringBuffer, (char*) &m, sizeof (MidiMessage));
- jack_midi_data_t* midiData = jack_midi_event_reserve(jackPortBuffer, m.time, m.size);
- memcpy(midiData, m.buffer, m.size);
- }
- return 0;
- }
-
void finish(int sig) {
continueProcessing = false;
}
virtual ~JackHandler() {
// Close JACK connection:
- jack_deactivate(jackClient);
- jack_client_close(jackClient);
- jack_ringbuffer_free(ringBuffer);
+ jack_deactivate(realTimeContext.jackClient);
+ jack_client_close(realTimeContext.jackClient);
+ jack_ringbuffer_free(realTimeContext.ringBuffer);
}
};
-int dequeueMessages(jack_nframes_t frames, void* arg) {
- JackHandler* instance = (JackHandler*) arg;
- return instance->dequeueMessages(frames);
-}
-
}
}
}