51 std::wstring_convert<std::codecvt_utf8<wchar_t>> convertor; // TODO: local system encoding |
52 std::wstring_convert<std::codecvt_utf8<wchar_t>> convertor; // TODO: local system encoding |
52 |
53 |
53 jack_client_t* jackClient = nullptr; |
54 jack_client_t* jackClient = nullptr; |
54 jack_port_t* jackPort = nullptr; |
55 jack_port_t* jackPort = nullptr; |
55 jack_ringbuffer_t* ringBuffer = nullptr; |
56 jack_ringbuffer_t* ringBuffer = nullptr; |
|
57 pthread_mutex_t messageThreadLock = PTHREAD_MUTEX_INITIALIZER; |
|
58 pthread_cond_t dataReady = PTHREAD_COND_INITIALIZER; |
56 |
59 |
57 std::atomic<bool> continueProcessing{true}; |
60 std::atomic<bool> continueProcessing{true}; |
58 |
61 |
59 const int RING_BUFFER_SIZE = 100; |
62 const int RING_BUFFER_SIZE = 100; |
60 |
63 |
61 struct MidiMessage { |
64 struct MidiMessage { |
62 uint8_t buffer[4096]; |
65 uint8_t buffer[4096]; |
63 uint32_t size; |
66 uint32_t size; |
64 uint32_t time; |
67 uint32_t time; |
65 }; |
68 }; |
|
69 |
|
70 /** |
|
71 * The message being prepared before enqueing in the ringBuffer. |
|
72 * Not the message dequeued from the ringBuffer (different thread). |
|
73 */ |
|
74 MidiMessage currentMidiMessage; |
|
75 size_t currentAttributeCount = 0; |
|
76 size_t currentAttributeIndex = 0; |
|
77 |
66 |
78 |
67 public: |
79 public: |
68 |
80 |
69 JackHandler(Configuration& configuration) : configuration(configuration) { |
81 JackHandler(Configuration& configuration) : configuration(configuration) { |
70 // Initialize JACK connection: |
82 // Initialize JACK connection: |
87 } |
99 } |
88 |
100 |
89 void startRelation(const relpipe::common::type::StringX name, std::vector<relpipe::reader::handlers::AttributeMetadata> attributes) override { |
101 void startRelation(const relpipe::common::type::StringX name, std::vector<relpipe::reader::handlers::AttributeMetadata> attributes) override { |
90 // TODO: validate metadata and prepare attribute mappings (names and types are important, order does not matter) |
102 // TODO: validate metadata and prepare attribute mappings (names and types are important, order does not matter) |
91 |
103 |
|
104 currentAttributeCount = attributes.size(); |
92 } |
105 } |
93 |
106 |
94 void attribute(const relpipe::common::type::StringX& value) override { |
107 void attribute(const relpipe::common::type::StringX& value) override { |
95 // TODO: append to current message + if this is last attribute, put whole message to the ring buffer |
108 // TODO: append to current message + if this is last attribute, put whole message to the ring buffer |
96 // TODO: if (continueProcessing) {} ? |
109 // TODO: if (continueProcessing) {} ? |
|
110 |
|
111 if (currentAttributeIndex < currentAttributeCount) { |
|
112 // memcpy(currentMidiMessage.buffer, ….buffer, ….size); |
|
113 // currentMidiMessage.size = …; |
|
114 // currentMidiMessage.time = …; |
|
115 currentAttributeIndex++; |
|
116 } else { |
|
117 if (jack_ringbuffer_write_space(ringBuffer) >= sizeof (MidiMessage)) { |
|
118 jack_ringbuffer_write(ringBuffer, (const char *) ¤tMidiMessage, sizeof (MidiMessage)); |
|
119 } else { |
|
120 fwprintf(stderr, L"Error: ring buffer is full → skipping event.\n"); |
|
121 } |
|
122 |
|
123 if (pthread_mutex_trylock(&messageThreadLock) == 0) { |
|
124 pthread_cond_signal(&dataReady); |
|
125 pthread_mutex_unlock(&messageThreadLock); |
|
126 } |
|
127 |
|
128 currentMidiMessage = MidiMessage(); |
|
129 currentAttributeIndex = 0; |
|
130 } |
|
131 |
97 } |
132 } |
98 |
133 |
99 void endOfPipe() { |
134 void endOfPipe() { |
100 // TODO: send optional (configurable) MIDI events |
135 // TODO: send optional (configurable) MIDI events |
101 // TODO: wait until the ring buffer is empty |
136 |
|
137 // Wait until the ring buffer is empty |
|
138 while (continueProcessing && jack_ringbuffer_read_space(ringBuffer)) usleep(1000); |
102 } |
139 } |
103 |
140 |
104 int dequeueMessages(jack_nframes_t frames) { |
141 int dequeueMessages(jack_nframes_t frames) { |
105 |
142 |
|
143 std::cout << "dequeueMessages(" << frames << ")" << std::endl; // TODO: remove debug message |
106 |
144 |
107 // TODO: send events from the ring buffer to JACK + correct timing |
145 // Process messages from the ring buffer queue: |
108 std::cout << "dequeueMessages(" << frames << ")" << std::endl; |
146 pthread_mutex_lock(&messageThreadLock); |
|
147 while (continueProcessing) { // TODO: is continueProcessing needed? |
|
148 const size_t queuedMessages = jack_ringbuffer_read_space(ringBuffer) / sizeof (MidiMessage); |
|
149 for (size_t i = 0; i < queuedMessages; ++i) { |
|
150 MidiMessage m; |
|
151 jack_ringbuffer_read(ringBuffer, (char*) &m, sizeof (MidiMessage)); |
|
152 // TODO: send events from the ring buffer to JACK + correct timing |
|
153 std::cout << "will process MidiMessage: " << &m << "" << std::endl; // TODO: remove debug message |
|
154 } |
|
155 pthread_cond_wait(&dataReady, &messageThreadLock); |
|
156 } |
|
157 pthread_mutex_unlock(&messageThreadLock); |
109 |
158 |
110 |
159 |
111 return 0; |
160 return 0; |
112 } |
161 } |
113 |
162 |