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.

379 lines
10KB

  1. /*
  2. ZynAddSubFX - a software synthesizer
  3. AlsaEngine.cpp - ALSA Driver
  4. Copyright (C) 2009 Alan Calvert
  5. Copyright (C) 2014 Mark McCurry
  6. This program is free software; you can redistribute it and/or
  7. modify it under the terms of the GNU General Public License
  8. as published by the Free Software Foundation; either version 2
  9. of the License, or (at your option) any later version.
  10. */
  11. #include <stdlib.h>
  12. #include <iostream>
  13. #include <cmath>
  14. using namespace std;
  15. #include "../Misc/Util.h"
  16. #include "../Misc/Config.h"
  17. #include "InMgr.h"
  18. #include "AlsaEngine.h"
  19. #include "Compressor.h"
  20. #include "Nio.h"
  21. AlsaEngine::AlsaEngine(const SYNTH_T &synth)
  22. :AudioOut(synth)
  23. {
  24. audio.buffer = new short[synth.buffersize * 2];
  25. name = "ALSA";
  26. audio.handle = NULL;
  27. audio.peaks[0] = 0;
  28. midi.handle = NULL;
  29. midi.alsaId = -1;
  30. midi.pThread = 0;
  31. }
  32. AlsaEngine::~AlsaEngine()
  33. {
  34. Stop();
  35. delete[] audio.buffer;
  36. }
  37. void *AlsaEngine::_AudioThread(void *arg)
  38. {
  39. return (static_cast<AlsaEngine *>(arg))->AudioThread();
  40. }
  41. void *AlsaEngine::AudioThread()
  42. {
  43. set_realtime();
  44. return processAudio();
  45. }
  46. bool AlsaEngine::Start()
  47. {
  48. return openAudio() && openMidi();
  49. }
  50. void AlsaEngine::Stop()
  51. {
  52. if(getMidiEn())
  53. setMidiEn(false);
  54. if(getAudioEn())
  55. setAudioEn(false);
  56. snd_config_update_free_global();
  57. }
  58. void AlsaEngine::setMidiEn(bool nval)
  59. {
  60. if(nval)
  61. openMidi();
  62. else
  63. stopMidi();
  64. }
  65. bool AlsaEngine::getMidiEn() const
  66. {
  67. return midi.handle;
  68. }
  69. void AlsaEngine::setAudioEn(bool nval)
  70. {
  71. if(nval)
  72. openAudio();
  73. else
  74. stopAudio();
  75. }
  76. bool AlsaEngine::getAudioEn() const
  77. {
  78. return audio.handle;
  79. }
  80. void *AlsaEngine::_MidiThread(void *arg)
  81. {
  82. return static_cast<AlsaEngine *>(arg)->MidiThread();
  83. }
  84. void *AlsaEngine::MidiThread(void)
  85. {
  86. snd_seq_event_t *event;
  87. MidiEvent ev;
  88. set_realtime();
  89. while(snd_seq_event_input(midi.handle, &event) > 0) {
  90. //ensure ev is empty
  91. ev.channel = 0;
  92. ev.num = 0;
  93. ev.value = 0;
  94. ev.type = 0;
  95. if(!event)
  96. continue;
  97. switch(event->type) {
  98. case SND_SEQ_EVENT_NOTEON:
  99. if(event->data.note.note) {
  100. ev.type = M_NOTE;
  101. ev.channel = event->data.note.channel;
  102. ev.num = event->data.note.note;
  103. ev.value = event->data.note.velocity;
  104. InMgr::getInstance().putEvent(ev);
  105. }
  106. break;
  107. case SND_SEQ_EVENT_NOTEOFF:
  108. ev.type = M_NOTE;
  109. ev.channel = event->data.note.channel;
  110. ev.num = event->data.note.note;
  111. ev.value = 0;
  112. InMgr::getInstance().putEvent(ev);
  113. break;
  114. case SND_SEQ_EVENT_KEYPRESS:
  115. ev.type = M_PRESSURE;
  116. ev.channel = event->data.note.channel;
  117. ev.num = event->data.note.note;
  118. ev.value = event->data.note.velocity;
  119. InMgr::getInstance().putEvent(ev);
  120. break;
  121. case SND_SEQ_EVENT_PITCHBEND:
  122. ev.type = M_CONTROLLER;
  123. ev.channel = event->data.control.channel;
  124. ev.num = C_pitchwheel;
  125. ev.value = event->data.control.value;
  126. InMgr::getInstance().putEvent(ev);
  127. break;
  128. case SND_SEQ_EVENT_CONTROLLER:
  129. ev.type = M_CONTROLLER;
  130. ev.channel = event->data.control.channel;
  131. ev.num = event->data.control.param;
  132. ev.value = event->data.control.value;
  133. InMgr::getInstance().putEvent(ev);
  134. break;
  135. case SND_SEQ_EVENT_PGMCHANGE:
  136. ev.type = M_PGMCHANGE;
  137. ev.channel = event->data.control.channel;
  138. ev.num = event->data.control.value;
  139. InMgr::getInstance().putEvent(ev);
  140. break;
  141. case SND_SEQ_EVENT_RESET: // reset to power-on state
  142. ev.type = M_CONTROLLER;
  143. ev.channel = event->data.control.channel;
  144. ev.num = C_resetallcontrollers;
  145. ev.value = 0;
  146. InMgr::getInstance().putEvent(ev);
  147. break;
  148. case SND_SEQ_EVENT_PORT_SUBSCRIBED: // ports connected
  149. if(true)
  150. cout << "Info, alsa midi port connected" << endl;
  151. break;
  152. case SND_SEQ_EVENT_PORT_UNSUBSCRIBED: // ports disconnected
  153. if(true)
  154. cout << "Info, alsa midi port disconnected" << endl;
  155. break;
  156. case SND_SEQ_EVENT_SYSEX: // system exclusive
  157. case SND_SEQ_EVENT_SENSING: // midi device still there
  158. break;
  159. default:
  160. if(true)
  161. cout << "Info, other non-handled midi event, type: "
  162. << (int)event->type << endl;
  163. break;
  164. }
  165. snd_seq_free_event(event);
  166. }
  167. return NULL;
  168. }
  169. bool AlsaEngine::openMidi()
  170. {
  171. if(getMidiEn())
  172. return true;
  173. int alsaport;
  174. midi.handle = NULL;
  175. if(snd_seq_open(&midi.handle, "default", SND_SEQ_OPEN_INPUT, 0) != 0)
  176. return false;
  177. string clientname = "ZynAddSubFX";
  178. string postfix = Nio::getPostfix();
  179. if (!postfix.empty())
  180. clientname += "_" + postfix;
  181. if(Nio::pidInClientName)
  182. clientname += "_" + os_pid_as_padded_string();
  183. snd_seq_set_client_name(midi.handle, clientname.c_str());
  184. alsaport = snd_seq_create_simple_port(
  185. midi.handle,
  186. "ZynAddSubFX",
  187. SND_SEQ_PORT_CAP_WRITE
  188. | SND_SEQ_PORT_CAP_SUBS_WRITE,
  189. SND_SEQ_PORT_TYPE_SYNTH);
  190. if(alsaport < 0)
  191. return false;
  192. pthread_attr_t attr;
  193. pthread_attr_init(&attr);
  194. pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
  195. pthread_create(&midi.pThread, &attr, _MidiThread, this);
  196. return true;
  197. }
  198. void AlsaEngine::stopMidi()
  199. {
  200. if(!getMidiEn())
  201. return;
  202. snd_seq_t *handle = midi.handle;
  203. if((NULL != midi.handle) && midi.pThread)
  204. pthread_cancel(midi.pThread);
  205. midi.handle = NULL;
  206. if(handle)
  207. snd_seq_close(handle);
  208. }
  209. short *AlsaEngine::interleave(const Stereo<float *> &smps)
  210. {
  211. /**\todo TODO fix repeated allocation*/
  212. short *shortInterleaved = audio.buffer;
  213. memset(shortInterleaved, 0, bufferSize * 2 * sizeof(short));
  214. int idx = 0; //possible off by one error here
  215. double scaled;
  216. for(int frame = 0; frame < bufferSize; ++frame) { // with a nod to libsamplerate ...
  217. float l = smps.l[frame];
  218. float r = smps.r[frame];
  219. stereoCompressor(synth.samplerate, audio.peaks[0], l, r);
  220. scaled = l * (8.0f * 0x10000000);
  221. shortInterleaved[idx++] = (short int)(lrint(scaled) >> 16);
  222. scaled = r * (8.0f * 0x10000000);
  223. shortInterleaved[idx++] = (short int)(lrint(scaled) >> 16);
  224. }
  225. return shortInterleaved;
  226. }
  227. bool AlsaEngine::openAudio()
  228. {
  229. if(getAudioEn())
  230. return true;
  231. int rc = 0;
  232. /* Open PCM device for playback. */
  233. audio.handle = NULL;
  234. const char *device = getenv("ALSA_DEVICE");
  235. if(device == 0)
  236. device = "hw:0";
  237. rc = snd_pcm_open(&audio.handle, device,
  238. SND_PCM_STREAM_PLAYBACK, 0);
  239. if(rc < 0) {
  240. fprintf(stderr,
  241. "unable to open pcm device: %s\n",
  242. snd_strerror(rc));
  243. return false;
  244. }
  245. /* Allocate a hardware parameters object. */
  246. snd_pcm_hw_params_alloca(&audio.params);
  247. /* Fill it in with default values. */
  248. snd_pcm_hw_params_any(audio.handle, audio.params);
  249. /* Set the desired hardware parameters. */
  250. /* Interleaved mode */
  251. snd_pcm_hw_params_set_access(audio.handle, audio.params,
  252. SND_PCM_ACCESS_RW_INTERLEAVED);
  253. /* Signed 16-bit little-endian format */
  254. snd_pcm_hw_params_set_format(audio.handle, audio.params,
  255. SND_PCM_FORMAT_S16_LE);
  256. /* Two channels (stereo) */
  257. snd_pcm_hw_params_set_channels(audio.handle, audio.params, 2);
  258. audio.sampleRate = synth.samplerate;
  259. snd_pcm_hw_params_set_rate_near(audio.handle, audio.params,
  260. &audio.sampleRate, NULL);
  261. audio.frames = 512;
  262. snd_pcm_hw_params_set_period_size_near(audio.handle,
  263. audio.params, &audio.frames, NULL);
  264. audio.periods = 4;
  265. snd_pcm_hw_params_set_periods_near(audio.handle,
  266. audio.params, &audio.periods, NULL);
  267. /* Write the parameters to the driver */
  268. rc = snd_pcm_hw_params(audio.handle, audio.params);
  269. if(rc < 0) {
  270. fprintf(stderr,
  271. "unable to set hw parameters: %s\n",
  272. snd_strerror(rc));
  273. return false;
  274. }
  275. /* Set buffer size (in frames). The resulting latency is given by */
  276. /* latency = periodsize * periods / (rate * bytes_per_frame) */
  277. snd_pcm_hw_params_set_buffer_size(audio.handle,
  278. audio.params,
  279. synth.buffersize);
  280. //snd_pcm_hw_params_get_period_size(audio.params, &audio.frames, NULL);
  281. //snd_pcm_hw_params_get_period_time(audio.params, &val, NULL);
  282. pthread_attr_t attr;
  283. pthread_attr_init(&attr);
  284. pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
  285. pthread_create(&audio.pThread, &attr, _AudioThread, this);
  286. return true;
  287. }
  288. void AlsaEngine::stopAudio()
  289. {
  290. if(!getAudioEn())
  291. return;
  292. snd_pcm_t *handle = audio.handle;
  293. audio.handle = NULL;
  294. pthread_join(audio.pThread, NULL);
  295. snd_pcm_drain(handle);
  296. if(snd_pcm_close(handle))
  297. cout << "Error: in snd_pcm_close " << __LINE__ << ' ' << __FILE__
  298. << endl;
  299. }
  300. void *AlsaEngine::processAudio()
  301. {
  302. while(audio.handle) {
  303. audio.buffer = interleave(getNext());
  304. snd_pcm_t *handle = audio.handle;
  305. int rc = snd_pcm_writei(handle, audio.buffer, synth.buffersize);
  306. if(rc == -EPIPE) {
  307. /* EPIPE means underrun */
  308. cerr << "underrun occurred" << endl;
  309. snd_pcm_prepare(handle);
  310. }
  311. else
  312. if(rc < 0)
  313. cerr << "error from writei: " << snd_strerror(rc) << endl;
  314. }
  315. return NULL;
  316. }