Mercurial > hg > pub > prymula > com
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