Mercurial > hg > pub > prymula > com
comparison DPF-Prymula-audioplugins/dpf/distrho/src/jackbridge/RtAudioBridge.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 * RtAudio 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 RTAUDIO_BRIDGE_HPP_INCLUDED | |
18 #define RTAUDIO_BRIDGE_HPP_INCLUDED | |
19 | |
20 #include "NativeBridge.hpp" | |
21 | |
22 #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS == 0 | |
23 # error RtAudio without audio does not make sense | |
24 #endif | |
25 | |
26 #if defined(DISTRHO_OS_MAC) | |
27 # define __MACOSX_CORE__ | |
28 # define RTAUDIO_API_TYPE MACOSX_CORE | |
29 # define RTMIDI_API_TYPE MACOSX_CORE | |
30 #elif defined(DISTRHO_OS_WINDOWS) && !defined(_MSC_VER) | |
31 # define __WINDOWS_WASAPI__ | |
32 # define __WINDOWS_MM__ | |
33 # define RTAUDIO_API_TYPE WINDOWS_WASAPI | |
34 # define RTMIDI_API_TYPE WINDOWS_MM | |
35 #else | |
36 # if defined(HAVE_PULSEAUDIO) | |
37 # define __LINUX_PULSE__ | |
38 # define RTAUDIO_API_TYPE LINUX_PULSE | |
39 # elif defined(HAVE_ALSA) | |
40 # define RTAUDIO_API_TYPE LINUX_ALSA | |
41 # endif | |
42 # ifdef HAVE_ALSA | |
43 # define __LINUX_ALSA__ | |
44 # define RTMIDI_API_TYPE LINUX_ALSA | |
45 # endif | |
46 #endif | |
47 | |
48 #ifdef RTAUDIO_API_TYPE | |
49 # include "rtaudio/RtAudio.h" | |
50 # include "rtmidi/RtMidi.h" | |
51 # include "../../extra/ScopedPointer.hpp" | |
52 # include "../../extra/String.hpp" | |
53 # include "../../extra/ScopedDenormalDisable.hpp" | |
54 | |
55 using DISTRHO_NAMESPACE::ScopedDenormalDisable; | |
56 using DISTRHO_NAMESPACE::ScopedPointer; | |
57 using DISTRHO_NAMESPACE::String; | |
58 | |
59 struct RtAudioBridge : NativeBridge { | |
60 // pointer to RtAudio instance | |
61 ScopedPointer<RtAudio> handle; | |
62 bool captureEnabled = false; | |
63 #if defined(RTMIDI_API_TYPE) && DISTRHO_PLUGIN_WANT_MIDI_INPUT | |
64 std::vector<RtMidiIn> midiIns; | |
65 #endif | |
66 #if defined(RTMIDI_API_TYPE) && DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |
67 std::vector<RtMidiOut> midiOuts; | |
68 #endif | |
69 | |
70 // caching | |
71 String name; | |
72 uint nextBufferSize = 512; | |
73 | |
74 RtAudioBridge() | |
75 { | |
76 #if defined(RTMIDI_API_TYPE) && (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT) | |
77 midiAvailable = true; | |
78 #endif | |
79 } | |
80 | |
81 const char* getVersion() const noexcept | |
82 { | |
83 return RTAUDIO_VERSION; | |
84 } | |
85 | |
86 bool open(const char* const clientName) override | |
87 { | |
88 name = clientName; | |
89 return _open(false); | |
90 } | |
91 | |
92 bool close() override | |
93 { | |
94 DISTRHO_SAFE_ASSERT_RETURN(handle != nullptr, false); | |
95 | |
96 if (handle->isStreamRunning()) | |
97 { | |
98 try { | |
99 handle->abortStream(); | |
100 } DISTRHO_SAFE_EXCEPTION("handle->abortStream()"); | |
101 } | |
102 | |
103 #if DISTRHO_PLUGIN_NUM_INPUTS > 0 | |
104 freeBuffers(); | |
105 #endif | |
106 handle = nullptr; | |
107 return true; | |
108 } | |
109 | |
110 bool activate() override | |
111 { | |
112 DISTRHO_SAFE_ASSERT_RETURN(handle != nullptr, false); | |
113 | |
114 try { | |
115 handle->startStream(); | |
116 } DISTRHO_SAFE_EXCEPTION_RETURN("handle->startStream()", false); | |
117 | |
118 return true; | |
119 } | |
120 | |
121 bool deactivate() override | |
122 { | |
123 DISTRHO_SAFE_ASSERT_RETURN(handle != nullptr, false); | |
124 | |
125 try { | |
126 handle->stopStream(); | |
127 } DISTRHO_SAFE_EXCEPTION_RETURN("handle->stopStream()", false); | |
128 | |
129 return true; | |
130 } | |
131 | |
132 bool isAudioInputEnabled() const override | |
133 { | |
134 #if DISTRHO_PLUGIN_NUM_INPUTS > 0 | |
135 return captureEnabled; | |
136 #else | |
137 return false; | |
138 #endif | |
139 } | |
140 | |
141 bool requestAudioInput() override | |
142 { | |
143 #if DISTRHO_PLUGIN_NUM_INPUTS > 0 | |
144 // stop audio first | |
145 deactivate(); | |
146 close(); | |
147 | |
148 // try to open with capture enabled | |
149 const bool ok = _open(true); | |
150 | |
151 if (ok) | |
152 captureEnabled = true; | |
153 else | |
154 _open(false); | |
155 | |
156 activate(); | |
157 return ok; | |
158 #else | |
159 return false; | |
160 #endif | |
161 } | |
162 | |
163 bool isMIDIEnabled() const override | |
164 { | |
165 #if defined(RTMIDI_API_TYPE) && DISTRHO_PLUGIN_WANT_MIDI_INPUT | |
166 if (!midiIns.empty()) | |
167 return true; | |
168 #endif | |
169 #if defined(RTMIDI_API_TYPE) && DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |
170 if (!midiOuts.empty()) | |
171 return true; | |
172 #endif | |
173 return false; | |
174 } | |
175 | |
176 bool requestMIDI() override | |
177 { | |
178 d_stdout("%s %d", __PRETTY_FUNCTION__, __LINE__); | |
179 // clear ports in use first | |
180 #if defined(RTMIDI_API_TYPE) && DISTRHO_PLUGIN_WANT_MIDI_INPUT | |
181 if (!midiIns.empty()) | |
182 { | |
183 try { | |
184 midiIns.clear(); | |
185 } catch (const RtMidiError& err) { | |
186 d_safe_exception(err.getMessage().c_str(), __FILE__, __LINE__); | |
187 return false; | |
188 } DISTRHO_SAFE_EXCEPTION_RETURN("midiIns.clear()", false); | |
189 } | |
190 #endif | |
191 #if defined(RTMIDI_API_TYPE) && DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |
192 if (!midiOuts.size()) | |
193 { | |
194 try { | |
195 midiOuts.clear(); | |
196 } catch (const RtMidiError& err) { | |
197 d_safe_exception(err.getMessage().c_str(), __FILE__, __LINE__); | |
198 return false; | |
199 } DISTRHO_SAFE_EXCEPTION_RETURN("midiOuts.clear()", false); | |
200 } | |
201 #endif | |
202 | |
203 // query port count | |
204 #if defined(RTMIDI_API_TYPE) && DISTRHO_PLUGIN_WANT_MIDI_INPUT | |
205 uint midiInCount; | |
206 try { | |
207 RtMidiIn midiIn(RtMidi::RTMIDI_API_TYPE, name.buffer()); | |
208 midiInCount = midiIn.getPortCount(); | |
209 } catch (const RtMidiError& err) { | |
210 d_safe_exception(err.getMessage().c_str(), __FILE__, __LINE__); | |
211 return false; | |
212 } DISTRHO_SAFE_EXCEPTION_RETURN("midiIn.getPortCount()", false); | |
213 #endif | |
214 #if defined(RTMIDI_API_TYPE) && DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |
215 uint midiOutCount; | |
216 try { | |
217 RtMidiOut midiOut(RtMidi::RTMIDI_API_TYPE, name.buffer()); | |
218 midiOutCount = midiOut.getPortCount(); | |
219 } catch (const RtMidiError& err) { | |
220 d_safe_exception(err.getMessage().c_str(), __FILE__, __LINE__); | |
221 return false; | |
222 } DISTRHO_SAFE_EXCEPTION_RETURN("midiOut.getPortCount()", false); | |
223 #endif | |
224 | |
225 // open all possible ports | |
226 #if defined(RTMIDI_API_TYPE) && DISTRHO_PLUGIN_WANT_MIDI_INPUT | |
227 for (uint i=0; i<midiInCount; ++i) | |
228 { | |
229 try { | |
230 RtMidiIn midiIn(RtMidi::RTMIDI_API_TYPE, name.buffer()); | |
231 midiIn.setCallback(RtMidiCallback, this); | |
232 midiIn.openPort(i); | |
233 midiIns.push_back(std::move(midiIn)); | |
234 } catch (const RtMidiError& err) { | |
235 d_safe_exception(err.getMessage().c_str(), __FILE__, __LINE__); | |
236 } DISTRHO_SAFE_EXCEPTION("midiIn.openPort()"); | |
237 } | |
238 #endif | |
239 #if defined(RTMIDI_API_TYPE) && DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |
240 for (uint i=0; i<midiOutCount; ++i) | |
241 { | |
242 try { | |
243 RtMidiOut midiOut(RtMidi::RTMIDI_API_TYPE, name.buffer()); | |
244 midiOut.openPort(i); | |
245 midiOuts.push_back(std::move(midiOut)); | |
246 } catch (const RtMidiError& err) { | |
247 d_safe_exception(err.getMessage().c_str(), __FILE__, __LINE__); | |
248 } DISTRHO_SAFE_EXCEPTION("midiOut.openPort()"); | |
249 } | |
250 #endif | |
251 | |
252 return true; | |
253 } | |
254 | |
255 bool supportsBufferSizeChanges() const override | |
256 { | |
257 return true; | |
258 } | |
259 | |
260 bool requestBufferSizeChange(const uint32_t newBufferSize) override | |
261 { | |
262 // stop audio first | |
263 deactivate(); | |
264 close(); | |
265 | |
266 // try to open with new buffer size | |
267 nextBufferSize = newBufferSize; | |
268 | |
269 const bool ok = _open(captureEnabled); | |
270 | |
271 if (!ok) | |
272 { | |
273 // revert to old buffer size if new one failed | |
274 nextBufferSize = bufferSize; | |
275 _open(captureEnabled); | |
276 } | |
277 | |
278 if (bufferSizeCallback != nullptr) | |
279 bufferSizeCallback(bufferSize, jackBufferSizeArg); | |
280 | |
281 activate(); | |
282 return ok; | |
283 } | |
284 | |
285 bool _open(const bool withInput, RtAudio* tryingAgain = nullptr) | |
286 { | |
287 ScopedPointer<RtAudio> rtAudio; | |
288 | |
289 if (tryingAgain == nullptr) | |
290 { | |
291 try { | |
292 rtAudio = new RtAudio(RtAudio::RTAUDIO_API_TYPE); | |
293 } DISTRHO_SAFE_EXCEPTION_RETURN("new RtAudio()", false); | |
294 } | |
295 else | |
296 { | |
297 rtAudio = tryingAgain; | |
298 } | |
299 | |
300 uint rtAudioBufferFrames = nextBufferSize; | |
301 | |
302 #if DISTRHO_PLUGIN_NUM_INPUTS > 0 | |
303 RtAudio::StreamParameters inParams; | |
304 #endif | |
305 RtAudio::StreamParameters* inParamsPtr = nullptr; | |
306 | |
307 #if DISTRHO_PLUGIN_NUM_INPUTS > 0 | |
308 if (withInput) | |
309 { | |
310 inParams.deviceId = rtAudio->getDefaultInputDevice(); | |
311 inParams.nChannels = DISTRHO_PLUGIN_NUM_INPUTS_2; | |
312 inParamsPtr = &inParams; | |
313 } | |
314 #endif | |
315 | |
316 #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |
317 RtAudio::StreamParameters outParams; | |
318 outParams.deviceId = tryingAgain != nullptr ? 1 : rtAudio->getDefaultOutputDevice(); | |
319 outParams.nChannels = DISTRHO_PLUGIN_NUM_OUTPUTS_2; | |
320 RtAudio::StreamParameters* const outParamsPtr = &outParams; | |
321 #else | |
322 RtAudio::StreamParameters* const outParamsPtr = nullptr; | |
323 #endif | |
324 | |
325 RtAudio::StreamOptions opts; | |
326 opts.flags = RTAUDIO_NONINTERLEAVED | RTAUDIO_ALSA_USE_DEFAULT; | |
327 #ifndef DISTRHO_OS_MAC | |
328 /* RtAudio in macOS uses a different than usual way to handle audio block size, | |
329 * where RTAUDIO_MINIMIZE_LATENCY makes CoreAudio use very low latencies (around 15 samples). | |
330 * That has serious performance drawbacks, so we skip that here. | |
331 */ | |
332 opts.flags |= RTAUDIO_MINIMIZE_LATENCY; | |
333 #endif | |
334 opts.numberOfBuffers = 2; | |
335 opts.streamName = name.buffer(); | |
336 | |
337 try { | |
338 rtAudio->openStream(outParamsPtr, inParamsPtr, RTAUDIO_FLOAT32, 48000, &rtAudioBufferFrames, | |
339 RtAudioCallback, this, &opts, nullptr); | |
340 } catch (const RtAudioError& err) { | |
341 #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |
342 if (outParams.deviceId == 0 && rtAudio->getDeviceCount() > 1) | |
343 return _open(withInput, rtAudio.release()); | |
344 #endif | |
345 d_safe_exception(err.getMessage().c_str(), __FILE__, __LINE__); | |
346 return false; | |
347 } DISTRHO_SAFE_EXCEPTION_RETURN("rtAudio->openStream()", false); | |
348 | |
349 handle = rtAudio; | |
350 bufferSize = rtAudioBufferFrames; | |
351 sampleRate = handle->getStreamSampleRate(); | |
352 allocBuffers(!withInput, true); | |
353 return true; | |
354 } | |
355 | |
356 static int RtAudioCallback(void* const outputBuffer, | |
357 #if DISTRHO_PLUGIN_NUM_INPUTS > 0 | |
358 void* const inputBuffer, | |
359 #else | |
360 void*, | |
361 #endif | |
362 const uint numFrames, | |
363 const double /* streamTime */, | |
364 const RtAudioStreamStatus /* status */, | |
365 void* const userData) | |
366 { | |
367 RtAudioBridge* const self = static_cast<RtAudioBridge*>(userData); | |
368 | |
369 if (self->jackProcessCallback == nullptr) | |
370 { | |
371 if (outputBuffer != nullptr) | |
372 std::memset((float*)outputBuffer, 0, sizeof(float)*numFrames*DISTRHO_PLUGIN_NUM_OUTPUTS_2); | |
373 return 0; | |
374 } | |
375 | |
376 #if DISTRHO_PLUGIN_NUM_INPUTS > 0 | |
377 if (float* const insPtr = static_cast<float*>(inputBuffer)) | |
378 { | |
379 for (uint i=0; i<DISTRHO_PLUGIN_NUM_INPUTS_2; ++i) | |
380 self->audioBuffers[i] = insPtr + (i * numFrames); | |
381 } | |
382 #endif | |
383 | |
384 #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |
385 if (float* const outsPtr = static_cast<float*>(outputBuffer)) | |
386 { | |
387 for (uint i=0; i<DISTRHO_PLUGIN_NUM_OUTPUTS_2; ++i) | |
388 self->audioBuffers[DISTRHO_PLUGIN_NUM_INPUTS + i] = outsPtr + (i * numFrames); | |
389 } | |
390 #endif | |
391 | |
392 const ScopedDenormalDisable sdd; | |
393 self->jackProcessCallback(numFrames, self->jackProcessArg); | |
394 | |
395 return 0; | |
396 } | |
397 | |
398 #if defined(RTMIDI_API_TYPE) && DISTRHO_PLUGIN_WANT_MIDI_INPUT | |
399 static void RtMidiCallback(double /*timeStamp*/, std::vector<uchar>* message, void* userData) | |
400 { | |
401 const size_t len = message->size(); | |
402 DISTRHO_SAFE_ASSERT_RETURN(len > 0 && len <= kMaxMIDIInputMessageSize,); | |
403 | |
404 RtAudioBridge* const self = static_cast<RtAudioBridge*>(userData); | |
405 | |
406 // TODO timestamp handling | |
407 self->midiInBufferPending.writeByte(static_cast<uint8_t>(len)); | |
408 self->midiInBufferPending.writeCustomData(message->data(), len); | |
409 for (uint8_t i=0; i<len; ++i) | |
410 self->midiInBufferPending.writeByte(0); | |
411 self->midiInBufferPending.commitWrite(); | |
412 } | |
413 #endif | |
414 }; | |
415 | |
416 #endif // RTAUDIO_API_TYPE | |
417 #endif // RTAUDIO_BRIDGE_HPP_INCLUDED |