Mercurial > hg > pub > prymula > com
view 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 source
/* * 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