Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

JackEngine.cpp 12KB

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