DISTRHO Plugin Framework
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.

266 lines
8.9KB

  1. /*
  2. * SDL Bridge for DPF
  3. * Copyright (C) 2021-2023 Filipe Coelho <falktx@falktx.com>
  4. *
  5. * Permission to use, copy, modify, and/or distribute this software for any purpose with
  6. * or without fee is hereby granted, provided that the above copyright notice and this
  7. * permission notice appear in all copies.
  8. *
  9. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
  10. * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
  11. * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
  12. * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
  13. * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  14. * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. */
  16. #ifndef SDL_BRIDGE_HPP_INCLUDED
  17. #define SDL_BRIDGE_HPP_INCLUDED
  18. #include "NativeBridge.hpp"
  19. #include "../../extra/ScopedDenormalDisable.hpp"
  20. #include <SDL.h>
  21. #ifndef SDL_HINT_AUDIO_DEVICE_APP_NAME
  22. # define SDL_HINT_AUDIO_DEVICE_APP_NAME "SDL_AUDIO_DEVICE_APP_NAME"
  23. #endif
  24. #ifndef SDL_HINT_AUDIO_DEVICE_STREAM_NAME
  25. # define SDL_HINT_AUDIO_DEVICE_STREAM_NAME "SDL_AUDIO_DEVICE_STREAM_NAME"
  26. #endif
  27. #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS == 0
  28. # error SDL without audio does not make sense
  29. #endif
  30. struct SDL2Bridge : NativeBridge {
  31. #if DISTRHO_PLUGIN_NUM_INPUTS > 0
  32. SDL_AudioDeviceID captureDeviceId;
  33. #endif
  34. #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
  35. SDL_AudioDeviceID playbackDeviceId;
  36. #endif
  37. SDL2Bridge()
  38. : NativeBridge()
  39. #if DISTRHO_PLUGIN_NUM_INPUTS > 0
  40. , captureDeviceId(0)
  41. #endif
  42. #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
  43. , playbackDeviceId(0)
  44. #endif
  45. {}
  46. bool open(const char* const clientName) override
  47. {
  48. SDL_InitSubSystem(SDL_INIT_AUDIO);
  49. SDL_AudioSpec requested;
  50. std::memset(&requested, 0, sizeof(requested));
  51. requested.format = AUDIO_F32SYS;
  52. requested.freq = 48000;
  53. requested.samples = 512;
  54. requested.userdata = this;
  55. SDL_SetHint(SDL_HINT_AUDIO_DEVICE_APP_NAME, clientName);
  56. // SDL_SetHint(SDL_HINT_AUDIO_RESAMPLING_MODE, "1");
  57. #if DISTRHO_PLUGIN_NUM_INPUTS > 0
  58. SDL_SetHint(SDL_HINT_AUDIO_DEVICE_STREAM_NAME, "Capure");
  59. requested.channels = DISTRHO_PLUGIN_NUM_INPUTS;
  60. requested.callback = AudioInputCallback;
  61. SDL_AudioSpec receivedCapture;
  62. captureDeviceId = SDL_OpenAudioDevice(nullptr, 1, &requested, &receivedCapture,
  63. SDL_AUDIO_ALLOW_FREQUENCY_CHANGE|SDL_AUDIO_ALLOW_SAMPLES_CHANGE);
  64. if (captureDeviceId == 0)
  65. {
  66. d_stderr2("Failed to open SDL capture device, error was: %s", SDL_GetError());
  67. #if DISTRHO_PLUGIN_NUM_OUTPUTS == 0
  68. return false;
  69. #endif
  70. }
  71. else if (receivedCapture.channels != DISTRHO_PLUGIN_NUM_INPUTS)
  72. {
  73. SDL_CloseAudioDevice(captureDeviceId);
  74. captureDeviceId = 0;
  75. d_stderr2("Invalid or missing audio input channels");
  76. return false;
  77. }
  78. #endif
  79. #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
  80. SDL_AudioSpec receivedPlayback;
  81. SDL_SetHint(SDL_HINT_AUDIO_DEVICE_STREAM_NAME, "Playback");
  82. requested.channels = DISTRHO_PLUGIN_NUM_OUTPUTS;
  83. requested.callback = AudioOutputCallback;
  84. playbackDeviceId = SDL_OpenAudioDevice(nullptr, 0, &requested, &receivedPlayback,
  85. SDL_AUDIO_ALLOW_FREQUENCY_CHANGE|SDL_AUDIO_ALLOW_SAMPLES_CHANGE);
  86. if (playbackDeviceId == 0)
  87. {
  88. d_stderr2("Failed to open SDL playback device, error was: %s", SDL_GetError());
  89. return false;
  90. }
  91. if (receivedPlayback.channels != DISTRHO_PLUGIN_NUM_OUTPUTS)
  92. {
  93. SDL_CloseAudioDevice(playbackDeviceId);
  94. playbackDeviceId = 0;
  95. d_stderr2("Invalid or missing audio output channels");
  96. return false;
  97. }
  98. #endif
  99. #if DISTRHO_PLUGIN_NUM_INPUTS > 0 && DISTRHO_PLUGIN_NUM_OUTPUTS > 0
  100. // if using both input and output, make sure they match
  101. if (receivedCapture.samples != receivedPlayback.samples && captureDeviceId != 0)
  102. {
  103. SDL_CloseAudioDevice(captureDeviceId);
  104. SDL_CloseAudioDevice(playbackDeviceId);
  105. captureDeviceId = playbackDeviceId = 0;
  106. d_stderr2("Mismatch buffer size %u vs %u", receivedCapture.samples, receivedPlayback.samples);
  107. return false;
  108. }
  109. if (receivedCapture.freq != receivedPlayback.freq && captureDeviceId != 0)
  110. {
  111. SDL_CloseAudioDevice(captureDeviceId);
  112. SDL_CloseAudioDevice(playbackDeviceId);
  113. captureDeviceId = playbackDeviceId = 0;
  114. d_stderr2("Mismatch sample rate %u vs %u", receivedCapture.freq, receivedPlayback.freq);
  115. return false;
  116. }
  117. #endif
  118. #if DISTRHO_PLUGIN_NUM_INPUTS > 0
  119. if (captureDeviceId != 0)
  120. {
  121. bufferSize = receivedCapture.samples;
  122. sampleRate = receivedCapture.freq;
  123. }
  124. #endif
  125. #if DISTRHO_PLUGIN_NUM_INPUTS > 0 && DISTRHO_PLUGIN_NUM_OUTPUTS > 0
  126. else
  127. #endif
  128. #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
  129. {
  130. bufferSize = receivedPlayback.samples;
  131. sampleRate = receivedPlayback.freq;
  132. }
  133. #endif
  134. allocBuffers(true, false);
  135. return true;
  136. }
  137. bool close() override
  138. {
  139. #if DISTRHO_PLUGIN_NUM_INPUTS > 0
  140. if (captureDeviceId != 0)
  141. {
  142. SDL_CloseAudioDevice(captureDeviceId);
  143. captureDeviceId = 0;
  144. }
  145. #endif
  146. #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
  147. DISTRHO_SAFE_ASSERT_RETURN(playbackDeviceId != 0, false);
  148. SDL_CloseAudioDevice(playbackDeviceId);
  149. playbackDeviceId = 0;
  150. #endif
  151. freeBuffers();
  152. return true;
  153. }
  154. bool activate() override
  155. {
  156. #if DISTRHO_PLUGIN_NUM_INPUTS > 0
  157. if (captureDeviceId != 0)
  158. SDL_PauseAudioDevice(captureDeviceId, 0);
  159. #endif
  160. #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
  161. DISTRHO_SAFE_ASSERT_RETURN(playbackDeviceId != 0, false);
  162. SDL_PauseAudioDevice(playbackDeviceId, 0);
  163. #endif
  164. return true;
  165. }
  166. bool deactivate() override
  167. {
  168. #if DISTRHO_PLUGIN_NUM_INPUTS > 0
  169. if (captureDeviceId != 0)
  170. SDL_PauseAudioDevice(captureDeviceId, 1);
  171. #endif
  172. #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
  173. DISTRHO_SAFE_ASSERT_RETURN(playbackDeviceId != 0, false);
  174. SDL_PauseAudioDevice(playbackDeviceId, 1);
  175. #endif
  176. return true;
  177. }
  178. #if DISTRHO_PLUGIN_NUM_INPUTS > 0
  179. static void AudioInputCallback(void* const userData, uchar* const stream, const int len)
  180. {
  181. NativeBridge* const self = static_cast<NativeBridge*>(userData);
  182. // safety checks
  183. DISTRHO_SAFE_ASSERT_RETURN(stream != nullptr,);
  184. DISTRHO_SAFE_ASSERT_RETURN(len > 0,);
  185. if (self->jackProcessCallback == nullptr)
  186. return;
  187. const uint numFrames = static_cast<uint>(len / sizeof(float) / DISTRHO_PLUGIN_NUM_INPUTS);
  188. DISTRHO_SAFE_ASSERT_UINT2_RETURN(numFrames == self->bufferSize, numFrames, self->bufferSize,);
  189. const float* const fstream = (const float*)stream;
  190. for (uint i=0; i<DISTRHO_PLUGIN_NUM_INPUTS; ++i)
  191. {
  192. for (uint j=0; j<numFrames; ++j)
  193. self->audioBuffers[i][j] = fstream[j * DISTRHO_PLUGIN_NUM_INPUTS + i];
  194. }
  195. #if DISTRHO_PLUGIN_NUM_OUTPUTS == 0
  196. // if there are no outputs, run process callback now
  197. const ScopedDenormalDisable sdd;
  198. self->jackProcessCallback(numFrames, self->jackProcessArg);
  199. #endif
  200. }
  201. #endif
  202. #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
  203. static void AudioOutputCallback(void* const userData, uchar* const stream, const int len)
  204. {
  205. NativeBridge* const self = static_cast<NativeBridge*>(userData);
  206. // safety checks
  207. DISTRHO_SAFE_ASSERT_RETURN(stream != nullptr,);
  208. DISTRHO_SAFE_ASSERT_RETURN(len > 0,);
  209. if (self->jackProcessCallback == nullptr)
  210. {
  211. std::memset(stream, 0, len);
  212. return;
  213. }
  214. const uint numFrames = static_cast<uint>(len / sizeof(float) / DISTRHO_PLUGIN_NUM_OUTPUTS);
  215. DISTRHO_SAFE_ASSERT_UINT2_RETURN(numFrames == self->bufferSize, numFrames, self->bufferSize,);
  216. const ScopedDenormalDisable sdd;
  217. self->jackProcessCallback(numFrames, self->jackProcessArg);
  218. float* const fstream = (float*)stream;
  219. for (uint i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
  220. {
  221. for (uint j=0; j < numFrames; ++j)
  222. fstream[j * DISTRHO_PLUGIN_NUM_OUTPUTS + i] = self->audioBuffers[DISTRHO_PLUGIN_NUM_INPUTS + i][j];
  223. }
  224. }
  225. #endif
  226. };
  227. #endif // SDL_BRIDGE_HPP_INCLUDED