1 /*
2     JackEngine.cpp
3 
4     Copyright 2009-2011, Alan Calvert
5     Copyright 2014-2019, Will Godfrey & others
6 
7     This file is part of yoshimi, which is free software: you can
8     redistribute it and/or modify it under the terms of the GNU General
9     Public License as published by the Free Software Foundation, either
10     version 2 of the License, or (at your option) any later version.
11 
12     yoshimi is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15     GNU General Public License for more details.
16 
17     You should have received a copy of the GNU General Public License
18     along with yoshimi.  If not, see <http://www.gnu.org/licenses/>.
19 
20 */
21 
22 #include <errno.h>
23 #include <iostream>
24 
25 #include <jack/midiport.h>
26 #include <jack/thread.h>
27 #include <unistd.h>     // for usleep()
28 
29 #include "Misc/Config.h"
30 #include "Misc/FormatFuncs.h"
31 #include "MusicIO/JackEngine.h"
32 
33 using func::asString;
34 using func::asHexString;
35 
36 
JackEngine(SynthEngine * _synth,BeatTracker * _beatTracker)37 JackEngine::JackEngine(SynthEngine *_synth, BeatTracker *_beatTracker) :
38     MusicIO(_synth, _beatTracker),
39     jackClient(NULL),
40     internalbuff(0)
41 {
42     audio.jackSamplerate = 0;
43     audio.jackNframes = 0;
44     for (int i = 0; i < (2 * NUM_MIDI_PARTS + 2); ++i)
45     {
46         audio.ports[i] = NULL;
47         audio.portBuffs[i] = NULL;
48     }
49     midiPort = NULL;
50 }
51 
52 
connectServer(std::string server)53 bool JackEngine::connectServer(std::string server)
54 {
55     for (int tries = 0; tries < 3 && !jackClient; ++tries)
56     {
57         if (!openJackClient(server) && tries < 2)
58         {
59             synth->getRuntime().Log("Failed to open jack client, trying again", _SYS_::LogError);
60             usleep(3333);
61         }
62     }
63     if (jackClient)
64     {
65         synth->getRuntime().setRtprio(jack_client_max_real_time_priority(jackClient));
66         audio.jackSamplerate = jack_get_sample_rate(jackClient);
67         audio.jackNframes = jack_get_buffer_size(jackClient);
68         return true;
69     }
70     else
71     {
72         synth->getRuntime().Log("Failed to open jack client on server " + server);
73     }
74     return false;
75 }
76 
77 
openJackClient(std::string server)78 bool JackEngine::openJackClient(std::string server)
79 {
80     int jopts = JackNullOption;
81     jack_status_t jstatus;
82     std::string clientname = "yoshimi";
83     if (synth->getRuntime().nameTag.size())
84         clientname += ("-" + synth->getRuntime().nameTag);
85 
86     //Andrew Deryabin: for multi-instance support add unique id to
87     //instances other then default (0)
88     unsigned int synthUniqueId = synth->getUniqueId();
89     if (synthUniqueId > 0)
90     {
91         char sUniqueId [256];
92         memset(sUniqueId, 0, sizeof(sUniqueId));
93         snprintf(sUniqueId, sizeof(sUniqueId), "%d", synthUniqueId);
94         clientname += ("-" + std::string(sUniqueId));
95     }
96     //std::cout << " C name " << clientname << std::endl;
97     bool named_server = server.size() > 0 && server.compare("default") != 0;
98     if (named_server)
99         jopts |= JackServerName;
100     if (!synth->getRuntime().startJack)
101         jopts |= JackNoStartServer;
102     #if defined(JACK_SESSION)
103         if (synth->getRuntime().restoreJackSession && synth->getRuntime().jackSessionUuid.size())
104         {
105             jopts |= JackSessionID;
106             if (named_server)
107                 jackClient = jack_client_open(clientname.c_str(), (jack_options_t)jopts,
108                                               &jstatus, synth->getRuntime().jackServer.c_str(),
109                                               synth->getRuntime().jackSessionUuid.c_str());
110             else
111                 jackClient = jack_client_open(clientname.c_str(), (jack_options_t)jopts,
112                                               &jstatus, synth->getRuntime().jackSessionUuid.c_str());
113         }
114         else
115         {
116             if (named_server)
117                 jackClient = jack_client_open(clientname.c_str(), (jack_options_t)jopts,
118                                               &jstatus, synth->getRuntime().jackServer.c_str());
119             else
120                 jackClient = jack_client_open(clientname.c_str(), (jack_options_t)jopts, &jstatus);
121         }
122     #else
123         if (named_server)
124             jackClient = jack_client_open(clientname.c_str(), (jack_options_t)jopts,
125                                           &jstatus, synth->getRuntime().jackServer.c_str());
126         else
127             jackClient = jack_client_open(clientname.c_str(), (jack_options_t)jopts, &jstatus);
128     #endif
129     if (jackClient)
130         return true;
131     else
132         synth->getRuntime().Log("Failed jack_client_open(), status: " + asHexString((int)jstatus), 1);
133     return false;
134 }
135 
136 
Start(void)137 bool JackEngine::Start(void)
138 {
139     bool jackPortsRegistered = true;
140     internalbuff = synth->getRuntime().Buffersize;
141     jack_set_xrun_callback(jackClient, _xrunCallback, this);
142     #if defined(JACK_SESSION)
143         if (jack_set_session_callback
144             && jack_set_session_callback(jackClient, _jsessionCallback, this))
145             synth->getRuntime().Log("Set jack session callback failed");
146     #endif
147 
148     if (jack_set_process_callback(jackClient, _processCallback, this))
149     {
150         synth->getRuntime().Log("JackEngine failed to set process callback");
151         goto bail_out;
152     }
153 
154     if (!latencyPrep())
155     {
156         synth->getRuntime().Log("Jack latency prep failed ");
157         goto bail_out;
158     }
159 
160     if (!jack_activate(jackClient) && jackPortsRegistered)
161     {
162         if (!synth->getRuntime().restoreJackSession && synth->getRuntime().connectJackaudio && !connectJackPorts())
163         {
164             synth->getRuntime().Log("Failed to connect jack audio ports");
165             goto bail_out;
166         }
167     }
168     else
169     {
170         synth->getRuntime().Log("Failed to activate jack client");
171         goto bail_out;
172     }
173     /*
174      * TODO fix this - now moved to where it should be.
175      * Shows identical results but doesn't connect.
176      * Original 1.4.1 version also fails - it used to work.
177      */
178      /* pre V 1.3.0 was this:
179      if (Runtime.midiEngine  == jack_midi and jack_connect(jackClient,Runtime.midiDevice.c_str(),jack_port_name(midi.port)))
180          Runtime.Log("Didn't find jack MIDI source '" + Runtime.midiDevice + "'");
181     */
182 
183     // style-wise I think the next bit is the wrong place
184     /*if (synth->getRuntime().midiEngine  == jack_midi
185       && !synth->getRuntime().midiDevice.empty()
186       && synth->getRuntime().midiDevice != "default")
187     {
188         if (jack_connect(jackClient, synth->getRuntime().midiDevice.c_str(), jack_port_name(midiPort)))
189         {
190             synth->getRuntime().Log("Didn't find jack MIDI source '"
191             + synth->getRuntime().midiDevice + "'", _SYS_::LogError);
192             synth->getRuntime().midiDevice = "";
193         }
194     }*/
195     return true;
196 
197 bail_out:
198     Close();
199     return false;
200 }
201 
202 
Close(void)203 void JackEngine::Close(void)
204 {
205     if (synth->getRuntime().runSynth)
206     {
207         synth->getRuntime().runSynth = false;
208     }
209 
210     if (NULL != jackClient)
211     {
212         int chk;
213         for (int chan = 0; chan < (2*NUM_MIDI_PARTS+2); ++chan)
214         {
215             if (NULL != audio.ports[chan])
216                 jack_port_unregister(jackClient, audio.ports[chan]);
217             audio.ports[chan] = NULL;
218         }
219         if (NULL != midiPort)
220         {
221             if ((chk = jack_port_unregister(jackClient, midiPort)))
222                 synth->getRuntime().Log("Failed to close jack client, status: " + asString(chk));
223             midiPort = NULL;
224         }
225         chk = jack_deactivate(jackClient);
226         if (chk)
227             synth->getRuntime().Log("Failed to close jack client, status: " + asString(chk));
228 
229         jackClient = NULL;
230     }
231 }
232 
233 
registerAudioPort(int partnum)234 void JackEngine::registerAudioPort(int partnum)
235 {
236     int portnum = partnum * 2;
237     if (partnum >=0 && partnum < NUM_MIDI_PARTS)
238     {
239         if (audio.ports [portnum] != NULL)
240         {
241             synth->getRuntime().Log("Jack port " + asString(partnum) + " already registered!", 2);
242             return;
243         }
244         /* This has a hack to stop all enabled parts from resistering
245          * individual ports (at startup) if part is not configured for
246          * direct O/P.
247          */
248         std::string portName;
249         if (synth->part [partnum] && synth->partonoffRead(partnum) && (synth->part [partnum]->Paudiodest > 1))
250         {
251             portName = "track_" + asString(partnum + 1) + "_l";
252             audio.ports[portnum] = jack_port_register(jackClient, portName.c_str(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
253             portName = "track_" + asString(partnum + 1) + "_r";
254             audio.ports[portnum + 1] = jack_port_register(jackClient, portName.c_str(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
255 
256             if (audio.ports [portnum])
257             {
258                 synth->getRuntime().Log("Registered jack port " + asString(partnum + 1));
259             }
260             else
261             {
262                 synth->getRuntime().Log("Error registering jack port " + asString(partnum + 1));
263             }
264         }
265     }
266 }
267 
268 
openAudio(void)269 bool JackEngine::openAudio(void)
270 {
271     if (jackClient == 0)
272     {
273         if (!connectServer(synth->getRuntime().audioDevice))
274         {
275             return false;
276         }
277     }
278     // Register mixed outputs
279     audio.ports[2 * NUM_MIDI_PARTS] = jack_port_register(jackClient, "left", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
280     audio.ports[2 * NUM_MIDI_PARTS + 1] = jack_port_register(jackClient, "right", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
281 
282     bool jackPortsRegistered = true;
283     if (!audio.ports[2 * NUM_MIDI_PARTS] || !audio.ports[2 * NUM_MIDI_PARTS + 1])
284         jackPortsRegistered = false;
285 
286     if (jackPortsRegistered)
287         return prepBuffers() && latencyPrep();
288     else
289         synth->getRuntime().Log("Failed to register jack audio ports");
290     Close();
291     return false;
292 }
293 
294 
openMidi(void)295 bool JackEngine::openMidi(void)
296 {
297     synth->setBPMAccurate(true);
298 
299     if (jackClient == 0)
300     {
301         if (!connectServer(synth->getRuntime().midiDevice))
302         {
303             return false;
304         }
305     }
306 
307     const char *port_name = "midi in";
308     midiPort = jack_port_register(jackClient, port_name,
309                                    JACK_DEFAULT_MIDI_TYPE,
310                                    JackPortIsInput, 0);
311     if (!midiPort)
312     {
313         synth->getRuntime().Log("Failed to register jack midi port");
314         return false;
315     }
316 
317     std::cout << "client " << jackClient<< "  device " << synth->getRuntime().midiDevice << "  port " << jack_port_name(midiPort) << std::endl;
318     if (jack_connect(jackClient, synth->getRuntime().midiDevice.c_str(), jack_port_name(midiPort)))
319         {
320             synth->getRuntime().Log("Didn't find jack MIDI source '"
321             + synth->getRuntime().midiDevice + "'");
322             //synth->getRuntime().midiDevice = "";
323         }
324 
325     return true;
326 }
327 
328 
connectJackPorts(void)329 bool JackEngine::connectJackPorts(void)
330 {
331     const char** playback_ports = jack_get_ports(jackClient, NULL, NULL,
332                                                  JackPortIsPhysical|JackPortIsInput);
333 	if (!playback_ports)
334     {
335         synth->getRuntime().Log("No physical jack playback ports found.");
336         return false;
337 	}
338     int ret;
339     for (int port = 0; port < 2 && (NULL != audio.ports[port + NUM_MIDI_PARTS*2]); ++port)
340     {
341         const char *port_name = jack_port_name(audio.ports[port + NUM_MIDI_PARTS * 2]);
342         if ((ret = jack_connect(jackClient, port_name, playback_ports[port])))
343         {
344             if (ret == EEXIST)
345             {
346             synth->getRuntime().Log(std::string(port_name)
347                         + " is already connected to jack port " + std::string(playback_ports[port])
348                         + ", status " + asString(ret));
349             }
350             else
351             {
352             synth->getRuntime().Log("Cannot connect " + std::string(port_name)
353                         + " to jack port " + std::string(playback_ports[port])
354                         + ", status " + asString(ret));
355             return false;
356             }
357         }
358     }
359     return true;
360 }
361 
362 
clientId(void)363 int JackEngine::clientId(void)
364 {
365     if (jackClient)
366         return long(jack_client_thread_id(jackClient));
367     else
368         return -1;
369 }
370 
371 
clientName(void)372 std::string JackEngine::clientName(void)
373 {
374     if (jackClient)
375         return std::string(jack_get_client_name(jackClient));
376     else
377         synth->getRuntime().Log("clientName() with null jackClient");
378     return std::string("Oh, yoshimi :-(");
379 }
380 
381 
_processCallback(jack_nframes_t nframes,void * arg)382 int JackEngine::_processCallback(jack_nframes_t nframes, void *arg)
383 {
384     return static_cast<JackEngine*>(arg)->processCallback(nframes);
385 }
386 
387 
processCallback(jack_nframes_t nframes)388 int JackEngine::processCallback(jack_nframes_t nframes)
389 {
390     bool okaudio = true;
391     bool okmidi = true;
392 
393     if (midiPort) {
394         // input exists, using jack midi
395         handleBeatValues(nframes);
396         okmidi = processMidi(nframes);
397     }
398     if (audio.ports[NUM_MIDI_PARTS * 2] && audio.ports[NUM_MIDI_PARTS * 2 + 1])
399         // (at least) main outputs exist, using jack audio
400         okaudio = processAudio(nframes);
401     return (okaudio && okmidi) ? 0 : -1;
402 }
403 
404 
processAudio(jack_nframes_t nframes)405 bool JackEngine::processAudio(jack_nframes_t nframes)
406 {
407     // Part buffers
408     for (int port = 0; port < 2 * NUM_MIDI_PARTS; ++port)
409     {
410         if (audio.ports [port])
411         {
412             audio.portBuffs[port] =
413                     (float*)jack_port_get_buffer(audio.ports[port], nframes);
414             if (!audio.portBuffs[port])
415             {
416                 synth->getRuntime().Log("Failed to get jack audio port buffer: " + asString(port));
417                 return false;
418             }
419         }
420     }
421     // And mixed outputs
422     audio.portBuffs[2 * NUM_MIDI_PARTS] = (float*)jack_port_get_buffer(audio.ports[2 * NUM_MIDI_PARTS], nframes);
423     if (!audio.portBuffs[2 * NUM_MIDI_PARTS])
424     {
425         synth->getRuntime().Log("Failed to get jack audio port buffer: " + asString(2 * NUM_MIDI_PARTS));
426         return false;
427     }
428     audio.portBuffs[2 * NUM_MIDI_PARTS + 1] = (float*)jack_port_get_buffer(audio.ports[2 * NUM_MIDI_PARTS + 1], nframes);
429     if (!audio.portBuffs[2 * NUM_MIDI_PARTS + 1])
430     {
431         synth->getRuntime().Log("Failed to get jack audio port buffer: " + asString(2 * NUM_MIDI_PARTS + 1));
432         return false;
433     }
434 
435     BeatTracker::BeatValues beats(beatTracker->getBeatValues());
436     int framesize;
437     if (nframes <= internalbuff)
438     {
439         synth->setBeatValues(beats.songBeat, beats.monotonicBeat, beats.bpm);
440         framesize = sizeof(float) * nframes;
441         synth->MasterAudio(zynLeft, zynRight, nframes);
442         sendAudio(framesize, 0);
443     }
444     else
445     {
446         framesize = sizeof(float) * internalbuff;
447         for (unsigned int pos = 0; pos < nframes; pos += internalbuff)
448         {
449             float bpmInc = (float)pos * beats.bpm / (audio.jackSamplerate * 60.0f);
450             synth->setBeatValues(beats.songBeat + bpmInc, beats.monotonicBeat + bpmInc, beats.bpm);
451             synth->MasterAudio(zynLeft, zynRight, internalbuff);
452             sendAudio(framesize, pos);
453         }
454     }
455     return true;
456 }
457 
458 
sendAudio(int framesize,unsigned int offset)459 void JackEngine::sendAudio(int framesize, unsigned int offset)
460 {
461     // Part outputs
462     int currentmax = synth->getRuntime().NumAvailableParts;
463     for (int port = 0, idx = 0; idx < 2 * NUM_MIDI_PARTS; port++ , idx += 2)
464     {
465         if (audio.ports [idx])
466         {
467             if (jack_port_connected(audio.ports[idx])) // just a few % improvement.
468             {
469                 float *lpoint = audio.portBuffs[idx] + offset;
470                 float *rpoint = audio.portBuffs[idx + 1] + offset;
471                 if ((synth->part[port]->Paudiodest & 2) && port < currentmax)
472                 {
473                     memcpy(lpoint, zynLeft[port], framesize);
474                     memcpy(rpoint, zynRight[port], framesize);
475                 }
476                 else
477                 {
478                     memset(lpoint, 0, framesize);
479                     memset(rpoint, 0, framesize);
480                 }
481             }
482         }
483     }
484     // And mixed outputs
485     float *Lpoint = audio.portBuffs[2 * NUM_MIDI_PARTS] + offset;
486     float *Rpoint = audio.portBuffs[2 * NUM_MIDI_PARTS + 1] + offset;
487     memcpy(Lpoint, zynLeft[NUM_MIDI_PARTS], framesize);
488     memcpy(Rpoint, zynRight[NUM_MIDI_PARTS], framesize);
489 }
490 
491 
processMidi(jack_nframes_t nframes)492 bool JackEngine::processMidi(jack_nframes_t nframes)
493 {
494     void *portBuf = jack_port_get_buffer(midiPort, nframes);
495     if (!portBuf)
496     {
497         synth->getRuntime().Log("Bad midi jack_port_get_buffer");
498         return  false;
499     }
500 
501     unsigned int idx;
502     jack_midi_event_t jEvent;
503     jack_nframes_t eventCount = jack_midi_get_event_count(portBuf);
504 
505     for (idx = 0; idx < eventCount; ++idx)
506     {
507         if (!jack_midi_event_get(&jEvent, portBuf, idx))
508         {
509             if (jEvent.size >= 1 && jEvent.size <= 4) // no interest in zero sized or long events
510             {
511                 //std::cout << "Offset " << int(jEvent.time) << std::endl;
512                 setMidi(jEvent.buffer[0], jEvent.buffer[1], jEvent.buffer[2]);
513             }
514         }
515     }
516     return true;
517 }
518 
handleBeatValues(jack_nframes_t nframes)519 void JackEngine::handleBeatValues(jack_nframes_t nframes)
520 {
521     jack_position_t pos;
522     jack_transport_state_t state = jack_transport_query(jackClient, &pos);
523 
524     BeatTracker::BeatValues beats(beatTracker->getBeatValues());
525 
526     if (pos.valid & JackPositionBBT)
527         beats.bpm = pos.beats_per_minute;
528 
529     float bpmInc = (float)nframes * beats.bpm
530         / ((float)audio.jackSamplerate * 60.0f);
531 
532     beats.monotonicBeat += bpmInc;
533 
534     if (!(pos.valid & JackPositionBBT) || state == JackTransportStopped)
535         // If stopped, keep oscillating.
536         beats.songBeat += bpmInc;
537     else
538     {
539         // If rolling, sync to exact beat.
540         beats.songBeat = (float)pos.tick / (float)pos.ticks_per_beat;
541         beats.songBeat += pos.beat - 1;
542         beats.songBeat += (pos.bar - 1) * pos.beats_per_bar;
543     }
544 
545     beatTracker->setBeatValues(beats);
546 }
547 
548 
_xrunCallback(void * arg)549 int JackEngine::_xrunCallback(void *arg)
550 {
551     ((JackEngine *)arg)->synth->getRuntime().Log("xrun reported", _SYS_::LogNotSerious);
552     return 0;
553 }
554 
555 
latencyPrep(void)556 bool JackEngine::latencyPrep(void)
557 {
558 #if defined(JACK_LATENCY)  // >= 0.120.1 API
559 
560     if (jack_set_latency_callback(jackClient, _latencyCallback, this))
561     {
562         synth->getRuntime().Log("Set latency callback failed");
563         return false;
564     }
565     return true;
566 
567 #else // < 0.120.1 API
568 
569     for (int i = 0; i < 2 * NUM_MIDI_PARTS + 2; ++i)
570     {
571         if (jack_port_set_latency && audio.ports[i])
572             jack_port_set_latency(audio.ports[i], jack_get_buffer_size(jackClient));
573     }
574     if (jack_recompute_total_latencies)
575         jack_recompute_total_latencies(jackClient);
576     return true;
577 
578 #endif
579 }
580 
581 
582 #if defined(JACK_SESSION)
583 
_jsessionCallback(jack_session_event_t * event,void * arg)584 void JackEngine::_jsessionCallback(jack_session_event_t *event, void *arg)
585 {
586     return static_cast<JackEngine*>(arg)->jsessionCallback(event);
587 }
588 
jsessionCallback(jack_session_event_t * event)589 void JackEngine::jsessionCallback(jack_session_event_t *event)
590 {
591     std::string uuid = std::string(event->client_uuid);
592     std::string filename = std::string("yoshimi-") + uuid + std::string(".xml");
593     std::string filepath = std::string(event->session_dir) + filename;
594     synth->getRuntime().setJackSessionSave((int)event->type, filepath);
595     std::string cmd = synth->getRuntime().programCmd() + std::string(" -U ") + uuid
596                  + std::string(" -u ${SESSION_DIR}") + filename;
597     event->command_line = strdup(cmd.c_str());
598     if (jack_session_reply(jackClient, event))
599         synth->getRuntime().Log("Jack session reply failed");
600     jack_session_event_free(event);
601 }
602 
603 #endif
604 
605 
606 #if defined(JACK_LATENCY)
607 
_latencyCallback(jack_latency_callback_mode_t mode,void * arg)608 void JackEngine::_latencyCallback(jack_latency_callback_mode_t mode, void *arg)
609 {
610     return static_cast<JackEngine*>(arg)->latencyCallback(mode);
611 }
612 
613 
latencyCallback(jack_latency_callback_mode_t mode)614 void JackEngine::latencyCallback(jack_latency_callback_mode_t mode)
615 {
616     if (mode == JackCaptureLatency)
617     {
618         for (int i = 0; i < 2 * NUM_MIDI_PARTS + 2; ++i)
619         {
620             jack_latency_range_t range;
621             if (audio.ports[i])
622             {
623                 jack_port_get_latency_range(audio.ports[i], mode, &range);
624                 range.min++;
625                 range.max += audio.jackNframes;
626                 jack_port_set_latency_range(audio.ports[i], JackPlaybackLatency, &range);
627             }
628         }
629     }
630 }
631 
632 #endif
633