zynaddsubfx

ZynAddSubFX open source synthesizer
Log | Files | Refs | Submodules | LICENSE

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 }