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