comparison DPF-Prymula-audioplugins/dpf/distrho/src/jackbridge/SDL2Bridge.hpp @ 3:84e66ea83026

DPF-Prymula-audioplugins-0.231015-2
author prymula <prymula76@outlook.com>
date Mon, 16 Oct 2023 21:53:34 +0200
parents
children
comparison
equal deleted inserted replaced
2:cf2cb71d31dd 3:84e66ea83026
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