From 0a275255736e29556e80ffdaaa1339fdd90d25f4 Mon Sep 17 00:00:00 2001 From: casey langen Date: Thu, 29 Dec 2022 13:37:38 -0800 Subject: [PATCH] Added stubs for PortAudio output plugin. --- CMakeLists.txt | 15 +- src/plugins/portaudioout/CMakeLists.txt | 14 ++ src/plugins/portaudioout/PortAudioOut.cpp | 159 ++++++++++++++++++ src/plugins/portaudioout/PortAudioOut.h | 76 +++++++++ src/plugins/portaudioout/config.h | 53 ++++++ .../portaudioout/portaudioout_plugin.cpp | 69 ++++++++ 6 files changed, 379 insertions(+), 7 deletions(-) create mode 100644 src/plugins/portaudioout/CMakeLists.txt create mode 100644 src/plugins/portaudioout/PortAudioOut.cpp create mode 100644 src/plugins/portaudioout/PortAudioOut.h create mode 100644 src/plugins/portaudioout/config.h create mode 100644 src/plugins/portaudioout/portaudioout_plugin.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 444f4211d..a55b66c3b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -109,22 +109,23 @@ add_dependencies(musikcubed musikcore) # tag readers add_plugin("src/plugins/taglib_plugin" "taglibreader") # outputs -add_plugin("src/plugins/nullout" "nullout") add_plugin("src/plugins/alsaout" "alsaout") -add_plugin("src/plugins/pulseout" "pulseout") -add_plugin("src/plugins/pipewireout" "pipewireout") -add_plugin("src/plugins/sndioout" "sndioout") add_plugin("src/plugins/coreaudioout" "coreaudioout") +add_plugin("src/plugins/nullout" "nullout") +add_plugin("src/plugins/pipewireout" "pipewireout") +add_plugin("src/plugins/portaudioout" "portaudioout") +add_plugin("src/plugins/pulseout" "pulseout") +add_plugin("src/plugins/sndioout" "sndioout") # remotes -add_plugin("src/plugins/server" "server") -add_plugin("src/plugins/mpris" "mpris") add_plugin("src/plugins/macosmediakeys" "macosmediakeys") +add_plugin("src/plugins/mpris" "mpris") +add_plugin("src/plugins/server" "server") # streams add_plugin("src/plugins/httpdatastream" "httpdatastream") # decoders add_plugin("src/plugins/ffmpegdecoder" "ffmpegdecoder") -add_plugin("src/plugins/libopenmptdecoder" "openmptdecoder") add_plugin("src/plugins/gmedecoder" "gmedecoder") +add_plugin("src/plugins/libopenmptdecoder" "openmptdecoder") # encoders add_plugin("src/plugins/stockencoders" "stockencoders") # dsps diff --git a/src/plugins/portaudioout/CMakeLists.txt b/src/plugins/portaudioout/CMakeLists.txt new file mode 100644 index 000000000..c8757944c --- /dev/null +++ b/src/plugins/portaudioout/CMakeLists.txt @@ -0,0 +1,14 @@ +set (portaudioout_SOURCES + portaudioout_plugin.cpp + PortAudioOut.cpp +) + +find_library(LIBPORTAUDIO "portaudio") + +if ("${LIBPORTAUDIO}" STREQUAL "LIBPORTAUDIO-NOTFOUND") + disable_plugin(portaudioout) +else() + add_library(portaudioout SHARED ${portaudioout_SOURCES}) + target_link_libraries(portaudioout ${LIBPORTAUDIO}) + message(STATUS "[portaudioout] using libportaudio at: ${LIBPORTAUDIO}") +endif() \ No newline at end of file diff --git a/src/plugins/portaudioout/PortAudioOut.cpp b/src/plugins/portaudioout/PortAudioOut.cpp new file mode 100644 index 000000000..c9d59ee17 --- /dev/null +++ b/src/plugins/portaudioout/PortAudioOut.cpp @@ -0,0 +1,159 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2004-2021 musikcube team +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the name of the author nor the names of other contributors may +// be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// + +#include "PortAudioOut.h" + +#include +#include +#include +#include + +using namespace musik::core::sdk; + +#ifdef WIN32 + #include + #define usleep(x) Sleep(x / 1000); + #define DLLEXPORT __declspec(dllexport) +#else + #include + #define DLLEXPORT +#endif + +#define PREF_DEFAULT_SAMPLE_RATE "default_sample_rate" +#define TAG "PortAudioOut" + +static int defaultSampleRate = 48000; +static IPreferences* prefs = nullptr; +static IDebug* debug = nullptr; + +static void reloadMultiplier() { + if (::prefs) { + ::defaultSampleRate = prefs->GetInt(PREF_DEFAULT_SAMPLE_RATE, defaultSampleRate); + } +} + +extern "C" DLLEXPORT void SetDebug(IDebug* debug) { + ::debug = debug; +} + +extern "C" DLLEXPORT void SetPreferences(IPreferences* prefs) { + ::prefs = prefs; +} + +extern "C" DLLEXPORT musik::core::sdk::ISchema* GetSchema() { + auto schema = new TSchema<>(); + schema->AddInt(PREF_DEFAULT_SAMPLE_RATE, defaultSampleRate, 4096, 192000); + return schema; +} + +static void logPaError(const std::string& method, PaError error) { + std::string err = method + "() return code: " + std::to_string(error); + if (error != 0) { + ::debug->Warning(TAG, err.c_str()); + } + else { + ::debug->Info(TAG, err.c_str()); + } +} + +PortAudioOut::PortAudioOut() { + this->volume = 1.0f; + this->state = StateStopped; + logPaError("Pa_Initialize", Pa_Initialize()); +} + +PortAudioOut::~PortAudioOut() { + logPaError("Pa_Terminate", Pa_Terminate()); +} + +void PortAudioOut::Release() { + delete this; +} + +void PortAudioOut::Pause() { + this->state = StatePaused; +} + +void PortAudioOut::Resume() { + reloadMultiplier(); + this->state = StatePlaying; +} + +void PortAudioOut::SetVolume(double volume) { + this->volume = volume; +} + +double PortAudioOut::GetVolume() { + return this->volume; +} + +void PortAudioOut::Stop() { + this->state = StateStopped; +} + +void PortAudioOut::Drain() { + +} + +IDeviceList* PortAudioOut::GetDeviceList() { + return nullptr; +} + +bool PortAudioOut::SetDefaultDevice(const char* deviceId) { + return false; +} + +IDevice* PortAudioOut::GetDefaultDevice() { + return nullptr; +} + +OutputState PortAudioOut::Play(IBuffer *buffer, IBufferProvider *provider) { + if (this->state == StatePaused) { + return OutputState::InvalidState; + } + + /* order of operations matters, otherwise overflow. */ + int micros = ((buffer->Samples() * 1000) / buffer->SampleRate() * 1000) / buffer->Channels(); + usleep((long)((float) micros)); + provider->OnBufferProcessed(buffer); + return OutputState::BufferWritten; +} + +double PortAudioOut::Latency() { + return 0.0; +} + +int PortAudioOut::GetDefaultSampleRate() { + return defaultSampleRate; +} diff --git a/src/plugins/portaudioout/PortAudioOut.h b/src/plugins/portaudioout/PortAudioOut.h new file mode 100644 index 000000000..147084a88 --- /dev/null +++ b/src/plugins/portaudioout/PortAudioOut.h @@ -0,0 +1,76 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2004-2021 musikcube team +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the name of the author nor the names of other contributors may +// be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "config.h" + +#include +#include + +using namespace musik::core::sdk; + +class PortAudioOut : public IOutput { + public: + PortAudioOut(); + ~PortAudioOut(); + + /* IPlugin */ + const char* Name() override { return "PortAudio"; }; + void Release() override; + + /* IOutput */ + void Pause() override; + void Resume() override; + void SetVolume(double volume) override; + double GetVolume() override; + void Stop() override; + OutputState Play(IBuffer *buffer, IBufferProvider *provider) override; + double Latency() override; + void Drain() override; + IDeviceList* GetDeviceList() override; + bool SetDefaultDevice(const char* deviceId) override; + IDevice* GetDefaultDevice() override; + int GetDefaultSampleRate() override; + + private: + enum State { + StateStopped, + StatePaused, + StatePlaying + }; + + State state; + double volume; +}; diff --git a/src/plugins/portaudioout/config.h b/src/plugins/portaudioout/config.h new file mode 100644 index 000000000..a2b16d490 --- /dev/null +++ b/src/plugins/portaudioout/config.h @@ -0,0 +1,53 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2004-2021 musikcube team +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the name of the author nor the names of other contributors may +// be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#ifdef WIN32 + #ifndef WINVER + #define WINVER 0x0601 + #endif + + #ifndef _WIN32_WINNT + #define _WIN32_WINNT 0x0601 + #endif + + #define WIN32_LEAN_AND_MEAN + #define NOMINMAX + #define DLLEXPORT __declspec(dllexport) + + #include +#else + #define DLLEXPORT +#endif diff --git a/src/plugins/portaudioout/portaudioout_plugin.cpp b/src/plugins/portaudioout/portaudioout_plugin.cpp new file mode 100644 index 000000000..0b7b93ac6 --- /dev/null +++ b/src/plugins/portaudioout/portaudioout_plugin.cpp @@ -0,0 +1,69 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2004-2021 musikcube team +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the name of the author nor the names of other contributors may +// be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// + +#include "config.h" + +#include +#include +#include +#include "PortAudioOut.h" + +class PortAudioPlugin : public musik::core::sdk::IPlugin { + public: + void Release() noexcept override { delete this; } + const char* Name() override { return "PortAudio IOutput"; } + const char* Version() override { return "0.1.0"; } + const char* Author() override { return "clangen"; } + const char* Guid() override { return "521934a5-2263-41a4-9852-937d0b63f196"; } + bool Configurable() override { return false; } + void Configure() override { } + void Reload() override { } + int SdkVersion() override { return musik::core::sdk::SdkVersion; } +}; + +#ifdef WIN32 +BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { + return true; +} +#endif + +extern "C" DLLEXPORT musik::core::sdk::IPlugin* GetPlugin() { + return new PortAudioPlugin(); +} + +extern "C" DLLEXPORT musik::core::sdk::IOutput* GetAudioOutput() { + return new PortAudioOut(); +} + +extern "C" DLLEXPORT musik::core::sdk::ISchema* GetSchema(); \ No newline at end of file