SDL2Bridge.hpp (9112B)
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 17 #ifndef SDL_BRIDGE_HPP_INCLUDED 18 #define SDL_BRIDGE_HPP_INCLUDED 19 20 #include "NativeBridge.hpp" 21 #include "../../extra/ScopedDenormalDisable.hpp" 22 23 #include <SDL.h> 24 25 #ifndef SDL_HINT_AUDIO_DEVICE_APP_NAME 26 # define SDL_HINT_AUDIO_DEVICE_APP_NAME "SDL_AUDIO_DEVICE_APP_NAME" 27 #endif 28 29 #ifndef SDL_HINT_AUDIO_DEVICE_STREAM_NAME 30 # define SDL_HINT_AUDIO_DEVICE_STREAM_NAME "SDL_AUDIO_DEVICE_STREAM_NAME" 31 #endif 32 33 #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS == 0 34 # error SDL without audio does not make sense 35 #endif 36 37 struct SDL2Bridge : NativeBridge { 38 #if DISTRHO_PLUGIN_NUM_INPUTS > 0 39 SDL_AudioDeviceID captureDeviceId; 40 #endif 41 #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 42 SDL_AudioDeviceID playbackDeviceId; 43 #endif 44 45 SDL2Bridge() 46 : NativeBridge() 47 #if DISTRHO_PLUGIN_NUM_INPUTS > 0 48 , captureDeviceId(0) 49 #endif 50 #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 51 , playbackDeviceId(0) 52 #endif 53 {} 54 55 bool open(const char* const clientName) override 56 { 57 SDL_InitSubSystem(SDL_INIT_AUDIO); 58 59 SDL_AudioSpec requested; 60 std::memset(&requested, 0, sizeof(requested)); 61 requested.format = AUDIO_F32SYS; 62 requested.freq = 48000; 63 requested.samples = 512; 64 requested.userdata = this; 65 66 SDL_SetHint(SDL_HINT_AUDIO_DEVICE_APP_NAME, clientName); 67 // SDL_SetHint(SDL_HINT_AUDIO_RESAMPLING_MODE, "1"); 68 69 #if DISTRHO_PLUGIN_NUM_INPUTS > 0 70 SDL_SetHint(SDL_HINT_AUDIO_DEVICE_STREAM_NAME, "Capure"); 71 requested.channels = DISTRHO_PLUGIN_NUM_INPUTS_2; 72 requested.callback = AudioInputCallback; 73 74 SDL_AudioSpec receivedCapture; 75 captureDeviceId = SDL_OpenAudioDevice(nullptr, 1, &requested, &receivedCapture, 76 SDL_AUDIO_ALLOW_FREQUENCY_CHANGE|SDL_AUDIO_ALLOW_SAMPLES_CHANGE); 77 if (captureDeviceId == 0) 78 { 79 d_stderr2("Failed to open SDL capture device, error was: %s", SDL_GetError()); 80 #if DISTRHO_PLUGIN_NUM_OUTPUTS == 0 81 return false; 82 #endif 83 } 84 else if (receivedCapture.channels != DISTRHO_PLUGIN_NUM_INPUTS_2) 85 { 86 SDL_CloseAudioDevice(captureDeviceId); 87 captureDeviceId = 0; 88 d_stderr2("Invalid or missing audio input channels"); 89 return false; 90 } 91 #endif 92 93 #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 94 SDL_AudioSpec receivedPlayback; 95 SDL_SetHint(SDL_HINT_AUDIO_DEVICE_STREAM_NAME, "Playback"); 96 requested.channels = DISTRHO_PLUGIN_NUM_OUTPUTS_2; 97 requested.callback = AudioOutputCallback; 98 99 playbackDeviceId = SDL_OpenAudioDevice(nullptr, 0, &requested, &receivedPlayback, 100 SDL_AUDIO_ALLOW_FREQUENCY_CHANGE|SDL_AUDIO_ALLOW_SAMPLES_CHANGE); 101 if (playbackDeviceId == 0) 102 { 103 d_stderr2("Failed to open SDL playback device, error was: %s", SDL_GetError()); 104 return false; 105 } 106 107 if (receivedPlayback.channels != DISTRHO_PLUGIN_NUM_OUTPUTS_2) 108 { 109 SDL_CloseAudioDevice(playbackDeviceId); 110 playbackDeviceId = 0; 111 d_stderr2("Invalid or missing audio output channels"); 112 return false; 113 } 114 #endif 115 116 #if DISTRHO_PLUGIN_NUM_INPUTS > 0 && DISTRHO_PLUGIN_NUM_OUTPUTS > 0 117 // if using both input and output, make sure they match 118 if (receivedCapture.samples != receivedPlayback.samples && captureDeviceId != 0) 119 { 120 SDL_CloseAudioDevice(captureDeviceId); 121 SDL_CloseAudioDevice(playbackDeviceId); 122 captureDeviceId = playbackDeviceId = 0; 123 d_stderr2("Mismatch buffer size %u vs %u", receivedCapture.samples, receivedPlayback.samples); 124 return false; 125 } 126 if (receivedCapture.freq != receivedPlayback.freq && captureDeviceId != 0) 127 { 128 SDL_CloseAudioDevice(captureDeviceId); 129 SDL_CloseAudioDevice(playbackDeviceId); 130 captureDeviceId = playbackDeviceId = 0; 131 d_stderr2("Mismatch sample rate %u vs %u", receivedCapture.freq, receivedPlayback.freq); 132 return false; 133 } 134 #endif 135 136 #if DISTRHO_PLUGIN_NUM_INPUTS > 0 137 if (captureDeviceId != 0) 138 { 139 bufferSize = receivedCapture.samples; 140 sampleRate = receivedCapture.freq; 141 } 142 #endif 143 #if DISTRHO_PLUGIN_NUM_INPUTS > 0 && DISTRHO_PLUGIN_NUM_OUTPUTS > 0 144 else 145 #endif 146 #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 147 { 148 bufferSize = receivedPlayback.samples; 149 sampleRate = receivedPlayback.freq; 150 } 151 #endif 152 153 allocBuffers(true, false); 154 return true; 155 } 156 157 bool close() override 158 { 159 #if DISTRHO_PLUGIN_NUM_INPUTS > 0 160 if (captureDeviceId != 0) 161 { 162 SDL_CloseAudioDevice(captureDeviceId); 163 captureDeviceId = 0; 164 } 165 #endif 166 #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 167 DISTRHO_SAFE_ASSERT_RETURN(playbackDeviceId != 0, false); 168 SDL_CloseAudioDevice(playbackDeviceId); 169 playbackDeviceId = 0; 170 #endif 171 172 freeBuffers(); 173 return true; 174 } 175 176 bool activate() override 177 { 178 #if DISTRHO_PLUGIN_NUM_INPUTS > 0 179 if (captureDeviceId != 0) 180 SDL_PauseAudioDevice(captureDeviceId, 0); 181 #endif 182 #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 183 DISTRHO_SAFE_ASSERT_RETURN(playbackDeviceId != 0, false); 184 SDL_PauseAudioDevice(playbackDeviceId, 0); 185 #endif 186 return true; 187 } 188 189 bool deactivate() override 190 { 191 #if DISTRHO_PLUGIN_NUM_INPUTS > 0 192 if (captureDeviceId != 0) 193 SDL_PauseAudioDevice(captureDeviceId, 1); 194 #endif 195 #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 196 DISTRHO_SAFE_ASSERT_RETURN(playbackDeviceId != 0, false); 197 SDL_PauseAudioDevice(playbackDeviceId, 1); 198 #endif 199 return true; 200 } 201 202 #if DISTRHO_PLUGIN_NUM_INPUTS > 0 203 static void AudioInputCallback(void* const userData, uchar* const stream, const int len) 204 { 205 NativeBridge* const self = static_cast<NativeBridge*>(userData); 206 207 // safety checks 208 DISTRHO_SAFE_ASSERT_RETURN(stream != nullptr,); 209 DISTRHO_SAFE_ASSERT_RETURN(len > 0,); 210 211 if (self->jackProcessCallback == nullptr) 212 return; 213 214 const uint numFrames = static_cast<uint>(len / sizeof(float) / DISTRHO_PLUGIN_NUM_INPUTS_2); 215 DISTRHO_SAFE_ASSERT_UINT2_RETURN(numFrames == self->bufferSize, numFrames, self->bufferSize,); 216 217 const float* const fstream = (const float*)stream; 218 219 for (uint i=0; i<DISTRHO_PLUGIN_NUM_INPUTS_2; ++i) 220 { 221 for (uint j=0; j<numFrames; ++j) 222 self->audioBuffers[i][j] = fstream[j * DISTRHO_PLUGIN_NUM_INPUTS_2 + i]; 223 } 224 225 #if DISTRHO_PLUGIN_NUM_OUTPUTS == 0 226 // if there are no outputs, run process callback now 227 const ScopedDenormalDisable sdd; 228 self->jackProcessCallback(numFrames, self->jackProcessArg); 229 #endif 230 } 231 #endif 232 233 #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 234 static void AudioOutputCallback(void* const userData, uchar* const stream, const int len) 235 { 236 NativeBridge* const self = static_cast<NativeBridge*>(userData); 237 238 // safety checks 239 DISTRHO_SAFE_ASSERT_RETURN(stream != nullptr,); 240 DISTRHO_SAFE_ASSERT_RETURN(len > 0,); 241 242 if (self->jackProcessCallback == nullptr) 243 { 244 std::memset(stream, 0, len); 245 return; 246 } 247 248 const uint numFrames = static_cast<uint>(len / sizeof(float) / DISTRHO_PLUGIN_NUM_OUTPUTS_2); 249 DISTRHO_SAFE_ASSERT_UINT2_RETURN(numFrames == self->bufferSize, numFrames, self->bufferSize,); 250 251 const ScopedDenormalDisable sdd; 252 self->jackProcessCallback(numFrames, self->jackProcessArg); 253 254 float* const fstream = (float*)stream; 255 256 for (uint i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS_2; ++i) 257 { 258 for (uint j=0; j < numFrames; ++j) 259 fstream[j * DISTRHO_PLUGIN_NUM_OUTPUTS_2 + i] = self->audioBuffers[DISTRHO_PLUGIN_NUM_INPUTS + i][j]; 260 } 261 } 262 #endif 263 }; 264 265 #endif // SDL_BRIDGE_HPP_INCLUDED