JackEngine.cpp (13285B)
1 /* 2 ZynAddSubFX - a software synthesizer 3 4 JackEngine.cpp - Jack Driver 5 Copyright (C) 2009 Alan Calvert 6 Copyright (C) 2014 Mark McCurry 7 8 This program is free software; you can redistribute it and/or 9 modify it under the terms of the GNU General Public License 10 as published by the Free Software Foundation; either version 2 11 of the License, or (at your option) any later version. 12 */ 13 14 #include <iostream> 15 16 #include <jack/midiport.h> 17 #ifdef JACK_HAS_METADATA_API 18 # include <jack/metadata.h> 19 #endif // JACK_HAS_METADATA_API 20 #include "jack_osc.h" 21 #include <fcntl.h> 22 #include <sys/stat.h> 23 #include <cassert> 24 #include <cstring> 25 #include <unistd.h> // access() 26 #include <fstream> // std::istream 27 28 #include "Nio.h" 29 #include "Compressor.h" 30 #include "OutMgr.h" 31 #include "InMgr.h" 32 #include "Misc/Util.h" 33 34 #include "JackEngine.h" 35 36 extern char *instance_name; 37 38 namespace zyn { 39 40 using namespace std; 41 42 JackEngine::JackEngine(const SYNTH_T &synth) 43 :AudioOut(synth), jackClient(NULL) 44 { 45 name = "JACK"; 46 audio.jackSamplerate = 0; 47 audio.jackNframes = 0; 48 audio.peaks[0] = 0; 49 for(int i = 0; i < 2; ++i) { 50 audio.ports[i] = NULL; 51 audio.portBuffs[i] = NULL; 52 } 53 midi.inport = NULL; 54 midi.jack_sync = false; 55 osc.oscport = NULL; 56 } 57 58 bool JackEngine::connectServer(string server) 59 { 60 bool autostart_jack = true; 61 if(jackClient) 62 return true; 63 64 string clientname = "zynaddsubfx"; 65 string postfix = Nio::getPostfix(); 66 if(!postfix.empty()) 67 clientname += "_" + postfix; 68 if(Nio::pidInClientName) 69 clientname += "_" + os_pid_as_padded_string(); 70 71 jack_status_t jackstatus; 72 bool use_server_name = server.size() && server.compare("default") != 0; 73 jack_options_t jopts = (jack_options_t) 74 (((!instance_name 75 && use_server_name) ? JackServerName : 76 JackNullOption) 77 | ((autostart_jack) ? JackNullOption : 78 JackNoStartServer)); 79 80 if(instance_name) 81 jackClient = jack_client_open(instance_name, jopts, &jackstatus); 82 else { 83 if(use_server_name) 84 jackClient = jack_client_open( 85 clientname.c_str(), jopts, &jackstatus, 86 server.c_str()); 87 else 88 jackClient = jack_client_open( 89 clientname.c_str(), jopts, &jackstatus); 90 } 91 92 93 if(NULL != jackClient) 94 return true; 95 else 96 cerr << "Error, failed to open jack client on server: " << server 97 << " status " << jackstatus << endl; 98 return false; 99 } 100 101 bool JackEngine::connectJack() 102 { 103 connectServer(""); 104 if(NULL != jackClient) { 105 setBufferSize(jack_get_buffer_size(jackClient)); 106 jack_set_error_function(_errorCallback); 107 jack_set_info_function(_infoCallback); 108 if(jack_set_buffer_size_callback(jackClient, _bufferSizeCallback, this)) 109 cerr << "Error setting the bufferSize callback" << endl; 110 if((jack_set_xrun_callback(jackClient, _xrunCallback, this))) 111 cerr << "Error setting jack xrun callback" << endl; 112 if(jack_set_process_callback(jackClient, _processCallback, this)) { 113 cerr << "Error, JackEngine failed to set process callback" << endl; 114 return false; 115 } 116 if(jack_activate(jackClient)) { 117 cerr << "Error, failed to activate jack client" << endl; 118 return false; 119 } 120 121 return true; 122 } 123 else 124 cerr << "Error, NULL jackClient through Start()" << endl; 125 return false; 126 } 127 128 void JackEngine::disconnectJack() 129 { 130 if(jackClient) { 131 cout << "Deactivating and closing JACK client" << endl; 132 133 jack_deactivate(jackClient); 134 jack_client_close(jackClient); 135 jackClient = NULL; 136 } 137 } 138 139 bool JackEngine::Start() 140 { 141 return openMidi() && openAudio(); 142 } 143 144 void JackEngine::Stop() 145 { 146 stopMidi(); 147 stopAudio(); 148 } 149 150 void JackEngine::setMidiEn(bool nval) 151 { 152 if(nval) 153 openMidi(); 154 else 155 stopMidi(); 156 } 157 158 bool JackEngine::getMidiEn() const 159 { 160 return midi.inport; 161 } 162 163 void JackEngine::setAudioEn(bool nval) 164 { 165 if(nval) 166 openAudio(); 167 else 168 stopAudio(); 169 } 170 171 bool JackEngine::getAudioEn() const 172 { 173 return audio.ports[0]; 174 } 175 176 bool JackEngine::openAudio() 177 { 178 if(getAudioEn()) 179 return true; 180 181 if(!getMidiEn()) 182 if(!connectJack()) 183 return false; 184 185 186 const char *portnames[] = { "out_1", "out_2" }; 187 for(int port = 0; port < 2; ++port) 188 audio.ports[port] = jack_port_register( 189 jackClient, 190 portnames[port], 191 JACK_DEFAULT_AUDIO_TYPE, 192 JackPortIsOutput 193 | JackPortIsTerminal, 194 0); 195 if((NULL != audio.ports[0]) && (NULL != audio.ports[1])) { 196 audio.jackSamplerate = jack_get_sample_rate(jackClient); 197 audio.jackNframes = jack_get_buffer_size(jackClient); 198 samplerate = audio.jackSamplerate; 199 bufferSize = audio.jackNframes; 200 201 202 //Attempt to autoConnect when specified 203 if(Nio::autoConnect) { 204 const char **outPorts = jack_get_ports( 205 jackClient, 206 NULL, 207 NULL, 208 JackPortIsPhysical 209 | JackPortIsInput); 210 if(outPorts != NULL) { 211 //Verify that stereo is available 212 assert(outPorts[0]); 213 assert(outPorts[1]); 214 215 //Connect to physical outputs 216 jack_connect(jackClient, jack_port_name( 217 audio.ports[0]), outPorts[0]); 218 jack_connect(jackClient, jack_port_name( 219 audio.ports[1]), outPorts[1]); 220 } 221 else 222 cerr << "Warning, No outputs to autoconnect to" << endl; 223 } 224 midi.jack_sync = true; 225 osc.oscport = jack_port_register(jackClient, "osc", 226 JACK_DEFAULT_OSC_TYPE, JackPortIsInput, 0); 227 #ifdef JACK_HAS_METADATA_API 228 jack_uuid_t uuid = jack_port_uuid(osc.oscport); 229 jack_set_property(jackClient, uuid, "http://jackaudio.org/metadata/event-types", JACK_EVENT_TYPE__OSC, "text/plain"); 230 #endif // JACK_HAS_METADATA_API 231 return true; 232 } 233 else 234 cerr << "Error, failed to register jack audio ports" << endl; 235 midi.jack_sync = false; 236 return false; 237 } 238 239 void JackEngine::stopAudio() 240 { 241 for(int i = 0; i < 2; ++i) { 242 jack_port_t *port = audio.ports[i]; 243 audio.ports[i] = NULL; 244 if(jackClient != NULL && NULL != port) 245 jack_port_unregister(jackClient, port); 246 } 247 midi.jack_sync = false; 248 if(osc.oscport) { 249 if (jackClient != NULL) { 250 jack_port_unregister(jackClient, osc.oscport); 251 #ifdef JACK_HAS_METADATA_API 252 jack_uuid_t uuid = jack_port_uuid(osc.oscport); 253 jack_remove_property(jackClient, uuid, "http://jackaudio.org/metadata/event-types"); 254 #endif // JACK_HAS_METADATA_API 255 } 256 } 257 if(!getMidiEn()) 258 disconnectJack(); 259 } 260 261 bool JackEngine::openMidi() 262 { 263 if(getMidiEn()) 264 return true; 265 if(!getAudioEn()) 266 if(!connectJack()) 267 return false; 268 269 midi.inport = jack_port_register(jackClient, "midi_input", 270 JACK_DEFAULT_MIDI_TYPE, 271 JackPortIsInput | JackPortIsTerminal, 0); 272 return midi.inport; 273 } 274 275 void JackEngine::stopMidi() 276 { 277 jack_port_t *port = midi.inport; 278 midi.inport = NULL; 279 if(port) 280 jack_port_unregister(jackClient, port); 281 282 if(!getAudioEn()) 283 disconnectJack(); 284 } 285 286 int JackEngine::clientId() 287 { 288 if(NULL != jackClient) 289 return (long)jack_client_thread_id(jackClient); 290 else 291 return -1; 292 } 293 294 string JackEngine::clientName() 295 { 296 if(NULL != jackClient) 297 return string(jack_get_client_name(jackClient)); 298 else 299 cerr << "Error, clientName() with null jackClient" << endl; 300 return string("Oh, yoshimi :-("); 301 } 302 303 int JackEngine::_processCallback(jack_nframes_t nframes, void *arg) 304 { 305 return static_cast<JackEngine *>(arg)->processCallback(nframes); 306 } 307 308 int JackEngine::processCallback(jack_nframes_t nframes) 309 { 310 bool okaudio = true; 311 312 handleMidi(nframes); 313 if((NULL != audio.ports[0]) && (NULL != audio.ports[1])) 314 okaudio = processAudio(nframes); 315 return okaudio ? 0 : -1; 316 } 317 318 bool JackEngine::processAudio(jack_nframes_t nframes) 319 { 320 //handle rt osc events first 321 void *oscport = jack_port_get_buffer(osc.oscport, nframes); 322 size_t osc_packets = jack_osc_get_event_count(oscport); 323 324 for(size_t i = 0; i < osc_packets; ++i) { 325 jack_osc_event_t event; 326 if(jack_osc_event_get(&event, oscport, i)) 327 continue; 328 if(*event.buffer!='/') //Bundles are unhandled 329 continue; 330 //TODO validate message length 331 OutMgr::getInstance().applyOscEventRt((char*)event.buffer); 332 } 333 334 for(int port = 0; port < 2; ++port) { 335 audio.portBuffs[port] = 336 (jsample_t *)jack_port_get_buffer(audio.ports[port], nframes); 337 if(NULL == audio.portBuffs[port]) { 338 cerr << "Error, failed to get jack audio port buffer: " 339 << port << endl; 340 return false; 341 } 342 } 343 344 Stereo<float *> smp = getNext(); 345 346 //Assumes size of smp.l == nframes 347 memcpy(audio.portBuffs[0], smp.l, bufferSize * sizeof(float)); 348 memcpy(audio.portBuffs[1], smp.r, bufferSize * sizeof(float)); 349 350 //Make sure the audio output doesn't overflow 351 if(isOutputCompressionEnabled) 352 { 353 for(int frame = 0; frame != bufferSize; ++frame) { 354 float &l = audio.portBuffs[0][frame]; 355 float &r = audio.portBuffs[1][frame]; 356 stereoCompressor(synth.samplerate, audio.peaks[0], l, r); 357 } 358 } 359 return true; 360 } 361 362 int JackEngine::_xrunCallback(void *) 363 { 364 cerr << "Jack reports xrun" << endl; 365 return 0; 366 } 367 368 void JackEngine::_errorCallback(const char *msg) 369 { 370 cerr << "Jack reports error: " << msg << endl; 371 } 372 373 void JackEngine::_infoCallback(const char *msg) 374 { 375 cerr << "Jack info message: " << msg << endl; 376 } 377 378 int JackEngine::_bufferSizeCallback(jack_nframes_t nframes, void *arg) 379 { 380 return static_cast<JackEngine *>(arg)->bufferSizeCallback(nframes); 381 } 382 383 int JackEngine::bufferSizeCallback(jack_nframes_t nframes) 384 { 385 cerr << "Jack buffer resized" << endl; 386 setBufferSize(nframes); 387 return 0; 388 } 389 390 void JackEngine::handleMidi(unsigned long frames) 391 { 392 if(!midi.inport) 393 return; 394 void *midi_buf = jack_port_get_buffer(midi.inport, frames); 395 jack_midi_event_t jack_midi_event; 396 jack_nframes_t event_index = 0; 397 unsigned char buf[3]; 398 unsigned char type; 399 400 while(jack_midi_event_get(&jack_midi_event, midi_buf, 401 event_index++) == 0) { 402 MidiEvent ev = {}; 403 404 memset(buf, 0, sizeof(buf)); 405 memcpy(buf, jack_midi_event.buffer, 406 std::min(sizeof(buf), jack_midi_event.size)); 407 408 /* make sure the values are within range */ 409 buf[1] &= 0x7F; 410 buf[2] &= 0x7F; 411 type = buf[0] & 0xF0; 412 ev.channel = buf[0] & 0x0F; 413 ev.time = midi.jack_sync ? jack_midi_event.time : 0; 414 415 switch(type) { 416 case 0x80: /* note-off */ 417 ev.type = M_NOTE; 418 ev.num = buf[1]; 419 ev.value = 0; 420 InMgr::getInstance().putEvent(ev); 421 break; 422 423 case 0x90: /* note-on */ 424 ev.type = M_NOTE; 425 ev.num = buf[1]; 426 ev.value = buf[2]; 427 InMgr::getInstance().putEvent(ev); 428 break; 429 430 case 0xA0: /* pressure, aftertouch */ 431 ev.type = M_PRESSURE; 432 ev.num = buf[1]; 433 ev.value = buf[2]; 434 InMgr::getInstance().putEvent(ev); 435 break; 436 437 case 0xB0: /* controller */ 438 ev.type = M_CONTROLLER; 439 ev.num = buf[1]; 440 ev.value = buf[2]; 441 InMgr::getInstance().putEvent(ev); 442 break; 443 444 case 0xC0: /* program change */ 445 ev.type = M_PGMCHANGE; 446 ev.num = buf[1]; 447 InMgr::getInstance().putEvent(ev); 448 break; 449 450 case 0xE0: /* pitch bend */ 451 ev.type = M_CONTROLLER; 452 ev.num = C_pitchwheel; 453 ev.value = ((buf[2] << 7) | buf[1]) - 8192; 454 InMgr::getInstance().putEvent(ev); 455 break; 456 457 default: 458 for (size_t x = 0; x < jack_midi_event.size; x += 3) { 459 size_t y = jack_midi_event.size - x; 460 if (y >= 3) { 461 memcpy(buf, (uint8_t *)jack_midi_event.buffer + x, 3); 462 } else { 463 memset(buf, 0, sizeof(buf)); 464 memcpy(buf, (uint8_t *)jack_midi_event.buffer + x, y); 465 } 466 midiProcess(buf[0], buf[1], buf[2]); 467 } 468 break; 469 } 470 } 471 } 472 473 }