diff 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
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/DPF-Prymula-audioplugins/dpf/distrho/src/jackbridge/RtAudioBridge.hpp	Mon Oct 16 21:53:34 2023 +0200
@@ -0,0 +1,417 @@
+/*
+ * RtAudio Bridge for DPF
+ * Copyright (C) 2021-2023 Filipe Coelho <falktx@falktx.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any purpose with
+ * or without fee is hereby granted, provided that the above copyright notice and this
+ * permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
+ * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
+ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef RTAUDIO_BRIDGE_HPP_INCLUDED
+#define RTAUDIO_BRIDGE_HPP_INCLUDED
+
+#include "NativeBridge.hpp"
+
+#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS == 0
+# error RtAudio without audio does not make sense
+#endif
+
+#if defined(DISTRHO_OS_MAC)
+# define __MACOSX_CORE__
+# define RTAUDIO_API_TYPE MACOSX_CORE
+# define RTMIDI_API_TYPE MACOSX_CORE
+#elif defined(DISTRHO_OS_WINDOWS) && !defined(_MSC_VER)
+# define __WINDOWS_WASAPI__
+# define __WINDOWS_MM__
+# define RTAUDIO_API_TYPE WINDOWS_WASAPI
+# define RTMIDI_API_TYPE WINDOWS_MM
+#else
+# if defined(HAVE_PULSEAUDIO)
+#  define __LINUX_PULSE__
+#  define RTAUDIO_API_TYPE LINUX_PULSE
+# elif defined(HAVE_ALSA)
+#  define RTAUDIO_API_TYPE LINUX_ALSA
+# endif
+# ifdef HAVE_ALSA
+#  define __LINUX_ALSA__
+#  define RTMIDI_API_TYPE LINUX_ALSA
+# endif
+#endif
+
+#ifdef RTAUDIO_API_TYPE
+# include "rtaudio/RtAudio.h"
+# include "rtmidi/RtMidi.h"
+# include "../../extra/ScopedPointer.hpp"
+# include "../../extra/String.hpp"
+# include "../../extra/ScopedDenormalDisable.hpp"
+
+using DISTRHO_NAMESPACE::ScopedDenormalDisable;
+using DISTRHO_NAMESPACE::ScopedPointer;
+using DISTRHO_NAMESPACE::String;
+
+struct RtAudioBridge : NativeBridge {
+    // pointer to RtAudio instance
+    ScopedPointer<RtAudio> handle;
+    bool captureEnabled = false;
+   #if defined(RTMIDI_API_TYPE) && DISTRHO_PLUGIN_WANT_MIDI_INPUT
+    std::vector<RtMidiIn> midiIns;
+   #endif
+   #if defined(RTMIDI_API_TYPE) && DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
+    std::vector<RtMidiOut> midiOuts;
+   #endif
+
+    // caching
+    String name;
+    uint nextBufferSize = 512;
+
+    RtAudioBridge()
+    {
+       #if defined(RTMIDI_API_TYPE) && (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT)
+        midiAvailable = true;
+       #endif
+    }
+
+    const char* getVersion() const noexcept
+    {
+        return RTAUDIO_VERSION;
+    }
+
+    bool open(const char* const clientName) override
+    {
+        name = clientName;
+        return _open(false);
+    }
+
+    bool close() override
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(handle != nullptr, false);
+
+        if (handle->isStreamRunning())
+        {
+            try {
+                handle->abortStream();
+            } DISTRHO_SAFE_EXCEPTION("handle->abortStream()");
+        }
+
+       #if DISTRHO_PLUGIN_NUM_INPUTS > 0
+        freeBuffers();
+       #endif
+        handle = nullptr;
+        return true;
+    }
+
+    bool activate() override
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(handle != nullptr, false);
+
+        try {
+            handle->startStream();
+        } DISTRHO_SAFE_EXCEPTION_RETURN("handle->startStream()", false);
+
+        return true;
+    }
+
+    bool deactivate() override
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(handle != nullptr, false);
+
+        try {
+            handle->stopStream();
+        } DISTRHO_SAFE_EXCEPTION_RETURN("handle->stopStream()", false);
+
+        return true;
+    }
+
+    bool isAudioInputEnabled() const override
+    {
+       #if DISTRHO_PLUGIN_NUM_INPUTS > 0
+        return captureEnabled;
+       #else
+        return false;
+       #endif
+    }
+
+    bool requestAudioInput() override
+    {
+       #if DISTRHO_PLUGIN_NUM_INPUTS > 0
+        // stop audio first
+        deactivate();
+        close();
+
+        // try to open with capture enabled
+        const bool ok = _open(true);
+
+        if (ok)
+            captureEnabled = true;
+        else
+            _open(false);
+
+        activate();
+        return ok;
+       #else
+        return false;
+       #endif
+    }
+
+    bool isMIDIEnabled() const override
+    {
+       #if defined(RTMIDI_API_TYPE) && DISTRHO_PLUGIN_WANT_MIDI_INPUT
+        if (!midiIns.empty())
+            return true;
+       #endif
+       #if defined(RTMIDI_API_TYPE) && DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
+        if (!midiOuts.empty())
+            return true;
+       #endif
+        return false;
+    }
+
+    bool requestMIDI() override
+    {
+        d_stdout("%s %d", __PRETTY_FUNCTION__, __LINE__);
+        // clear ports in use first
+       #if defined(RTMIDI_API_TYPE) && DISTRHO_PLUGIN_WANT_MIDI_INPUT
+        if (!midiIns.empty())
+        {
+            try {
+                midiIns.clear();
+            } catch (const RtMidiError& err) {
+                d_safe_exception(err.getMessage().c_str(), __FILE__, __LINE__);
+                return false;
+            } DISTRHO_SAFE_EXCEPTION_RETURN("midiIns.clear()", false);
+        }
+       #endif
+       #if defined(RTMIDI_API_TYPE) && DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
+        if (!midiOuts.size())
+        {
+            try {
+                midiOuts.clear();
+            } catch (const RtMidiError& err) {
+                d_safe_exception(err.getMessage().c_str(), __FILE__, __LINE__);
+                return false;
+            } DISTRHO_SAFE_EXCEPTION_RETURN("midiOuts.clear()", false);
+        }
+       #endif
+
+        // query port count
+       #if defined(RTMIDI_API_TYPE) && DISTRHO_PLUGIN_WANT_MIDI_INPUT
+        uint midiInCount;
+        try {
+            RtMidiIn midiIn(RtMidi::RTMIDI_API_TYPE, name.buffer());
+            midiInCount = midiIn.getPortCount();
+        } catch (const RtMidiError& err) {
+            d_safe_exception(err.getMessage().c_str(), __FILE__, __LINE__);
+            return false;
+        } DISTRHO_SAFE_EXCEPTION_RETURN("midiIn.getPortCount()", false);
+       #endif
+       #if defined(RTMIDI_API_TYPE) && DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
+        uint midiOutCount;
+        try {
+            RtMidiOut midiOut(RtMidi::RTMIDI_API_TYPE, name.buffer());
+            midiOutCount = midiOut.getPortCount();
+        } catch (const RtMidiError& err) {
+            d_safe_exception(err.getMessage().c_str(), __FILE__, __LINE__);
+            return false;
+        } DISTRHO_SAFE_EXCEPTION_RETURN("midiOut.getPortCount()", false);
+       #endif
+
+        // open all possible ports
+       #if defined(RTMIDI_API_TYPE) && DISTRHO_PLUGIN_WANT_MIDI_INPUT
+        for (uint i=0; i<midiInCount; ++i)
+        {
+            try {
+                RtMidiIn midiIn(RtMidi::RTMIDI_API_TYPE, name.buffer());
+                midiIn.setCallback(RtMidiCallback, this);
+                midiIn.openPort(i);
+                midiIns.push_back(std::move(midiIn));
+            } catch (const RtMidiError& err) {
+                d_safe_exception(err.getMessage().c_str(), __FILE__, __LINE__);
+            } DISTRHO_SAFE_EXCEPTION("midiIn.openPort()");
+        }
+       #endif
+       #if defined(RTMIDI_API_TYPE) && DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
+        for (uint i=0; i<midiOutCount; ++i)
+        {
+            try {
+                RtMidiOut midiOut(RtMidi::RTMIDI_API_TYPE, name.buffer());
+                midiOut.openPort(i);
+                midiOuts.push_back(std::move(midiOut));
+            } catch (const RtMidiError& err) {
+                d_safe_exception(err.getMessage().c_str(), __FILE__, __LINE__);
+            } DISTRHO_SAFE_EXCEPTION("midiOut.openPort()");
+        }
+       #endif
+
+        return true;
+    }
+
+    bool supportsBufferSizeChanges() const override
+    {
+        return true;
+    }
+
+    bool requestBufferSizeChange(const uint32_t newBufferSize) override
+    {
+        // stop audio first
+        deactivate();
+        close();
+
+        // try to open with new buffer size
+        nextBufferSize = newBufferSize;
+
+        const bool ok = _open(captureEnabled);
+
+        if (!ok)
+        {
+            // revert to old buffer size if new one failed
+            nextBufferSize = bufferSize;
+            _open(captureEnabled);
+        }
+
+        if (bufferSizeCallback != nullptr)
+            bufferSizeCallback(bufferSize, jackBufferSizeArg);
+
+        activate();
+        return ok;
+    }
+
+    bool _open(const bool withInput, RtAudio* tryingAgain = nullptr)
+    {
+        ScopedPointer<RtAudio> rtAudio;
+
+        if (tryingAgain == nullptr)
+        {
+            try {
+                rtAudio = new RtAudio(RtAudio::RTAUDIO_API_TYPE);
+            } DISTRHO_SAFE_EXCEPTION_RETURN("new RtAudio()", false);
+        }
+        else
+        {
+            rtAudio = tryingAgain;
+        }
+
+        uint rtAudioBufferFrames = nextBufferSize;
+
+       #if DISTRHO_PLUGIN_NUM_INPUTS > 0
+        RtAudio::StreamParameters inParams;
+       #endif
+        RtAudio::StreamParameters* inParamsPtr = nullptr;
+
+       #if DISTRHO_PLUGIN_NUM_INPUTS > 0
+        if (withInput)
+        {
+            inParams.deviceId = rtAudio->getDefaultInputDevice();
+            inParams.nChannels = DISTRHO_PLUGIN_NUM_INPUTS_2;
+            inParamsPtr = &inParams;
+        }
+       #endif
+
+       #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
+        RtAudio::StreamParameters outParams;
+        outParams.deviceId = tryingAgain != nullptr ? 1 : rtAudio->getDefaultOutputDevice();
+        outParams.nChannels = DISTRHO_PLUGIN_NUM_OUTPUTS_2;
+        RtAudio::StreamParameters* const outParamsPtr = &outParams;
+       #else
+        RtAudio::StreamParameters* const outParamsPtr = nullptr;
+       #endif
+
+        RtAudio::StreamOptions opts;
+        opts.flags = RTAUDIO_NONINTERLEAVED | RTAUDIO_ALSA_USE_DEFAULT;
+       #ifndef DISTRHO_OS_MAC
+       /* RtAudio in macOS uses a different than usual way to handle audio block size,
+        * where RTAUDIO_MINIMIZE_LATENCY makes CoreAudio use very low latencies (around 15 samples).
+        * That has serious performance drawbacks, so we skip that here.
+        */
+        opts.flags |= RTAUDIO_MINIMIZE_LATENCY;
+       #endif
+        opts.numberOfBuffers = 2;
+        opts.streamName = name.buffer();
+
+        try {
+            rtAudio->openStream(outParamsPtr, inParamsPtr, RTAUDIO_FLOAT32, 48000, &rtAudioBufferFrames,
+                                RtAudioCallback, this, &opts, nullptr);
+        } catch (const RtAudioError& err) {
+           #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
+            if (outParams.deviceId == 0 && rtAudio->getDeviceCount() > 1)
+                return _open(withInput, rtAudio.release());
+           #endif
+            d_safe_exception(err.getMessage().c_str(), __FILE__, __LINE__);
+            return false;
+        } DISTRHO_SAFE_EXCEPTION_RETURN("rtAudio->openStream()", false);
+
+        handle = rtAudio;
+        bufferSize = rtAudioBufferFrames;
+        sampleRate = handle->getStreamSampleRate();
+        allocBuffers(!withInput, true);
+        return true;
+    }
+
+    static int RtAudioCallback(void* const outputBuffer,
+                              #if DISTRHO_PLUGIN_NUM_INPUTS > 0
+                               void* const inputBuffer,
+                              #else
+                               void*,
+                              #endif
+                               const uint numFrames,
+                               const double /* streamTime */,
+                               const RtAudioStreamStatus /* status */,
+                               void* const userData)
+    {
+        RtAudioBridge* const self = static_cast<RtAudioBridge*>(userData);
+
+        if (self->jackProcessCallback == nullptr)
+        {
+            if (outputBuffer != nullptr)
+                std::memset((float*)outputBuffer, 0, sizeof(float)*numFrames*DISTRHO_PLUGIN_NUM_OUTPUTS_2);
+            return 0;
+        }
+
+       #if DISTRHO_PLUGIN_NUM_INPUTS > 0
+        if (float* const insPtr = static_cast<float*>(inputBuffer))
+        {
+            for (uint i=0; i<DISTRHO_PLUGIN_NUM_INPUTS_2; ++i)
+                self->audioBuffers[i] = insPtr + (i * numFrames);
+        }
+       #endif
+
+       #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
+        if (float* const outsPtr = static_cast<float*>(outputBuffer))
+        {
+            for (uint i=0; i<DISTRHO_PLUGIN_NUM_OUTPUTS_2; ++i)
+                self->audioBuffers[DISTRHO_PLUGIN_NUM_INPUTS + i] = outsPtr + (i * numFrames);
+        }
+       #endif
+
+        const ScopedDenormalDisable sdd;
+        self->jackProcessCallback(numFrames, self->jackProcessArg);
+
+        return 0;
+    }
+
+   #if defined(RTMIDI_API_TYPE) && DISTRHO_PLUGIN_WANT_MIDI_INPUT
+    static void RtMidiCallback(double /*timeStamp*/, std::vector<uchar>* message, void* userData)
+    {
+        const size_t len = message->size();
+        DISTRHO_SAFE_ASSERT_RETURN(len > 0 && len <= kMaxMIDIInputMessageSize,);
+
+        RtAudioBridge* const self = static_cast<RtAudioBridge*>(userData);
+
+        // TODO timestamp handling
+        self->midiInBufferPending.writeByte(static_cast<uint8_t>(len));
+        self->midiInBufferPending.writeCustomData(message->data(), len);
+        for (uint8_t i=0; i<len; ++i)
+            self->midiInBufferPending.writeByte(0);
+        self->midiInBufferPending.commitWrite();
+    }
+   #endif
+};
+
+#endif // RTAUDIO_API_TYPE
+#endif // RTAUDIO_BRIDGE_HPP_INCLUDED