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.

368 lines
10KB

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