hydrogen 1.2.3
JackMidiDriver.cpp
Go to the documentation of this file.
1/*-
2 * Copyright (c) 2011 Hans Petter Selasky <hselasky@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
28
29#if defined(H2CORE_HAVE_JACK) || _DOXYGEN_
30
32#include <core/Hydrogen.h>
33#include <core/Globals.h>
34#include <core/EventQueue.h>
35#include <core/Basics/Note.h>
38
39#ifdef H2CORE_HAVE_LASH
41#endif
42
43namespace H2Core
44{
45
46void
48{
49 pthread_mutex_lock(&mtx);
50}
51
52void
54{
55 pthread_mutex_unlock(&mtx);
56}
57
58void
59JackMidiDriver::JackMidiWrite(jack_nframes_t nframes)
60{
61 int error;
62 int events;
63 int i;
64 void *buf;
65 jack_midi_event_t event;
66 uint8_t buffer[13];// 13 is needed if we get sysex goto messages
67
68 if (input_port == nullptr) {
69 return;
70 }
71
72 buf = jack_port_get_buffer(input_port, nframes);
73 if (buf == nullptr) {
74 return;
75 }
76
77#ifdef JACK_MIDI_NEEDS_NFRAMES
78 events = jack_midi_get_event_count(buf, nframes);
79#else
80 events = jack_midi_get_event_count(buf);
81#endif
82
83 for (i = 0; i < events; i++) {
84 MidiMessage msg;
85
86#ifdef JACK_MIDI_NEEDS_NFRAMES
87 error = jack_midi_event_get(&event, buf, i, nframes);
88#else
89 error = jack_midi_event_get(&event, buf, i);
90#endif
91 if (error) {
92 continue;
93 }
94
95 if (running < 1) {
96 continue;
97 }
98
99 error = event.size;
100 if (error > (int)sizeof(buffer)) {
101 error = (int)sizeof(buffer);
102 }
103
104 memset(buffer, 0, sizeof(buffer));
105 memcpy(buffer, event.buffer, error);
106
107 msg.setType( buffer[ 0 ] );
108 if ( msg.m_type == MidiMessage::SYSEX ) {
109 if ( buffer[ 3 ] == 06 ){// MMC message
110 for ( int i = 0; i < sizeof(buffer) && i<6; i++ ) {
111 msg.m_sysexData.push_back( buffer[ i ] );
112 }
113 }
114 else {
115 for ( int i = 0; i < sizeof(buffer); i++ ) {
116 msg.m_sysexData.push_back( buffer[ i ] );
117 }
118 }
119 }
120 else {
121 // All other MIDI messages
122 msg.m_nData1 = buffer[1];
123 msg.m_nData2 = buffer[2];
124 }
125 handleMidiMessage( msg );
126 }
127}
128
129void
130JackMidiDriver::handleOutgoingControlChange( int param, int value, int channel )
131{
132 uint8_t buffer[4];
133
134 if (channel < 0 || channel > 15) {
135 return;
136 }
137
138 if (param < 0 || param > 127) {
139 return;
140 }
141
142 if (value < 0 || value > 127) {
143 return;
144 }
145
146 buffer[0] = 0xB0 | channel; /* note off */
147 buffer[1] = param;
148 buffer[2] = value;
149 buffer[3] = 0;
150
151 JackMidiOutEvent(buffer, 3);
152}
153
154void
155JackMidiDriver::JackMidiRead(jack_nframes_t nframes)
156{
157 uint8_t *buffer;
158 void *buf;
159 jack_nframes_t t;
160 uint8_t data[1];
161 uint8_t len;
162
163 if (output_port == nullptr) {
164 return;
165 }
166
167 buf = jack_port_get_buffer(output_port, nframes);
168 if (buf == nullptr) {
169 return;
170 }
171
172#ifdef JACK_MIDI_NEEDS_NFRAMES
173 jack_midi_clear_buffer(buf, nframes);
174#else
175 jack_midi_clear_buffer(buf);
176#endif
177
178 t = 0;
179 lock();
180 while ((t < nframes) &&
181 (rx_out_pos != rx_in_pos)) {
182
183 len = jack_buffer[4 * rx_in_pos];
184 if (len == 0) {
185 rx_in_pos++;
187 rx_in_pos = 0;
188 }
189 continue;
190 }
191
192#ifdef JACK_MIDI_NEEDS_NFRAMES
193 buffer = jack_midi_event_reserve(buf, t, len, nframes);
194#else
195 buffer = jack_midi_event_reserve(buf, t, len);
196#endif
197 if (buffer == nullptr) {
198 break;
199 }
200 t++;
201 rx_in_pos++;
203 rx_in_pos = 0;
204 }
205 memcpy(buffer, jack_buffer + (4 * rx_in_pos) + 1, len);
206 }
207 unlock();
208}
209
210void
211JackMidiDriver::JackMidiOutEvent(uint8_t buf[4], uint8_t len)
212{
213 uint32_t next_pos;
214
215 lock();
216
217 next_pos = rx_out_pos + 1;
218 if (next_pos >= JACK_MIDI_BUFFER_MAX) {
219 next_pos = 0;
220 }
221
222 if (next_pos == rx_in_pos) {
223 /* buffer is full */
224 unlock();
225 return;
226 }
227
228 if (len > 3) {
229 len = 3;
230 }
231
232 jack_buffer[(4 * next_pos)] = len;
233 jack_buffer[(4 * next_pos) + 1] = buf[0];
234 jack_buffer[(4 * next_pos) + 2] = buf[1];
235 jack_buffer[(4 * next_pos) + 3] = buf[2];
236
237 rx_out_pos = next_pos;
238
239 unlock();
240}
241
242static int
243JackMidiProcessCallback(jack_nframes_t nframes, void *arg)
244{
245 JackMidiDriver *jmd = (JackMidiDriver *)arg;
246
247 if (nframes <= 0) {
248 return (0);
249 }
250
251 jmd->JackMidiRead(nframes);
252 jmd->JackMidiWrite(nframes);
253
254 return (0);
255}
256
257static void
263
266{
267 pthread_mutex_init(&mtx, nullptr);
268
269 running = 0;
270 rx_in_pos = 0;
271 rx_out_pos = 0;
272 output_port = nullptr;
273 input_port = nullptr;
274
275 QString jackMidiClientId = "Hydrogen";
276
277#ifdef H2CORE_HAVE_OSC
279 QString nsmClientId = pref->getNsmClientId();
280
281 if(!nsmClientId.isEmpty()){
282 jackMidiClientId = nsmClientId;
283 }
284#endif
285
286 jackMidiClientId.append("-midi");
287
288 jack_client = jack_client_open(jackMidiClientId.toLocal8Bit(),
289 JackNoStartServer, nullptr);
290
291 if (jack_client == nullptr) {
292 return;
293 }
294
295 jack_set_process_callback(jack_client,
297
298 jack_on_shutdown(jack_client,
299 JackMidiShutdown, nullptr);
300
301 output_port = jack_port_register(
302 jack_client, "TX", JACK_DEFAULT_MIDI_TYPE,
303 JackPortIsOutput, 0);
304
305 input_port = jack_port_register(
306 jack_client, "RX", JACK_DEFAULT_MIDI_TYPE,
307 JackPortIsInput, 0);
308
309 jack_activate(jack_client);
310}
311
313{
314
315 if (jack_client != nullptr)
316 {
317 if( jack_port_unregister( jack_client, input_port) != 0){
318 ERRORLOG("Failed to unregister jack midi input out");
319 }
320
321 if( jack_port_unregister( jack_client, output_port) != 0){
322 ERRORLOG("Failed to unregister jack midi input out");
323 }
324
325 //jack_port_unregister( jack_client, output_port);
326 if( jack_deactivate(jack_client) != 0){
327 ERRORLOG("Failed to unregister jack midi input out");
328 }
329
330 if( jack_client_close(jack_client) != 0){
331 ERRORLOG("Failed close jack midi client");
332 }
333 }
334 pthread_mutex_destroy(&mtx);
335
336}
337
338void
340{
341 running ++;
342}
343
344void
346{
347 running --;
348}
349
350std::vector<QString>
352{
353 std::vector<QString> inputList;
354
355 inputList.push_back("Default");
356
357 return inputList;
358}
359
360std::vector<QString>
362{
363 std::vector<QString> outputList;
364
365 outputList.push_back("Default");
366
367 return outputList;
368}
369
370void
371JackMidiDriver::getPortInfo(const QString& sPortName, int& nClient, int& nPort)
372{
373 if ( sPortName == Preferences::getNullMidiPort() ) {
374 nClient = -1;
375 nPort = -1;
376 return;
377 }
378
379 nClient = 0;
380 nPort = 0;
381}
382
384{
385
386 uint8_t buffer[4];
387 int channel;
388 int key;
389 int vel;
390
391 channel = pNote->get_instrument()->get_midi_out_channel();
392 if (channel < 0 || channel > 15) {
393 return;
394 }
395
396 key = pNote->get_midi_key();
397 if (key < 0 || key > 127) {
398 return;
399 }
400
401 vel = pNote->get_midi_velocity();
402 if (vel < 0 || vel > 127) {
403 return;
404 }
405
406 buffer[0] = 0x80 | channel; /* note off */
407 buffer[1] = key;
408 buffer[2] = 0;
409 buffer[3] = 0;
410
411 JackMidiOutEvent(buffer, 3);
412
413 buffer[0] = 0x90 | channel; /* note on */
414 buffer[1] = key;
415 buffer[2] = vel;
416 buffer[3] = 0;
417
418 JackMidiOutEvent(buffer, 3);
419}
420
421void
422JackMidiDriver::handleQueueNoteOff(int channel, int key, int vel)
423{
424 uint8_t buffer[4];
425
426 if (channel < 0 || channel > 15) {
427 return;
428 }
429
430 if (key < 0 || key > 127) {
431 return;
432 }
433
434 if (vel < 0 || vel > 127) {
435 return;
436 }
437
438 buffer[0] = 0x80 | channel; /* note off */
439 buffer[1] = key;
440 buffer[2] = 0;
441 buffer[3] = 0;
442
443 JackMidiOutEvent(buffer, 3);
444}
445
447{
448 auto pInstrList = Hydrogen::get_instance()->getSong()->getInstrumentList();
449 std::shared_ptr<Instrument> pCurInstr;
450 unsigned int numInstruments = pInstrList->size();
451 unsigned int i = 0;
452 int channel = 0;
453 int key = 0;
454
455 for (i = 0; i < numInstruments; i++) {
456 pCurInstr = pInstrList->get(i);
457
458 channel = pCurInstr->get_midi_out_channel();
459 if (channel < 0 || channel > 15) {
460 continue;
461 }
462
463 key = pCurInstr->get_midi_out_note();
464 if (key < 0 || key > 127) {
465 continue;
466 }
467
468 handleQueueNoteOff(channel, key, 0);
469 }
470}
471
472};
473
474#endif /* H2CORE_HAVE_JACK */
475
#define JACK_MIDI_BUFFER_MAX
#define ERRORLOG(x)
Definition Object.h:239
std::shared_ptr< Song > getSong() const
Get the current song.
Definition Hydrogen.h:122
static Hydrogen * get_instance()
Returns the current Hydrogen instance __instance.
Definition Hydrogen.h:83
void raiseError(unsigned nErrorCode)
Definition Hydrogen.cpp:774
void JackMidiOutEvent(uint8_t *buf, uint8_t len)
jack_port_t * output_port
void getPortInfo(const QString &sPortName, int &nClient, int &nPort)
virtual void open() override
void JackMidiWrite(jack_nframes_t nframes)
void JackMidiRead(jack_nframes_t nframes)
uint8_t jack_buffer[JACK_MIDI_BUFFER_MAX *4]
virtual std::vector< QString > getInputPortList() override
virtual void close() override
virtual void handleQueueNoteOff(int channel, int key, int velocity) override
virtual std::vector< QString > getOutputPortList() override
virtual void handleQueueAllNoteOff() override
virtual void handleOutgoingControlChange(int param, int value, int channel) override
jack_client_t * jack_client
virtual void handleQueueNote(Note *pNote) override
MIDI input base class.
Definition MidiInput.h:39
void handleMidiMessage(const MidiMessage &msg)
Definition MidiInput.cpp:52
MidiMessageType m_type
Definition MidiCommon.h:87
void setType(int nStatusByte)
Derives and set m_type (and if applicable m_nChannel) using the statusByte of an incoming MIDI messag...
std::vector< unsigned char > m_sysexData
Definition MidiCommon.h:91
MIDI input base class.
Definition MidiOutput.h:41
A note plays an associated instrument with a velocity left and right pan.
Definition Note.h:102
std::shared_ptr< Instrument > get_instrument()
__instrument accessor
Definition Note.h:500
int get_midi_key() const
return scaled key for midi output, !!! DO NOT CHECK IF INSTRUMENT IS SET !!!
Definition Note.h:682
int get_midi_velocity() const
midi velocity accessor
Definition Note.h:690
Manager for User Preferences File (singleton)
Definition Preferences.h:78
static Preferences * get_instance()
Returns a pointer to the current Preferences singleton stored in __instance.
QString getNsmClientId(void)
static QString getNullMidiPort()
Choice of m_sMidiPortName and m_sMidiOutputPortName in case no port/device was selected.
#define UNUSED(v)
Definition Globals.h:42
static void JackMidiShutdown(void *arg)
static int JackMidiProcessCallback(jack_nframes_t nframes, void *arg)