From f9a1bcd7bd4203696ae85fc6530ac3ac87daf3ec Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Tue, 1 Dec 2020 21:42:33 +0100 Subject: [PATCH] Split VST2 specific functionality into Vst2Logger --- meson.build | 3 + src/common/communication/common.h | 1 + src/common/communication/vst2.h | 7 +- src/common/logging/common.cpp | 513 +---------------------------- src/common/logging/common.h | 53 +-- src/common/logging/vst2.cpp | 530 ++++++++++++++++++++++++++++++ src/common/logging/vst2.h | 66 ++++ src/plugin/bridges/vst2.cpp | 10 +- src/plugin/bridges/vst2.h | 4 +- src/plugin/host-process.h | 1 + src/wine-host/bridges/vst2.h | 1 - 11 files changed, 617 insertions(+), 572 deletions(-) create mode 100644 src/common/logging/vst2.cpp create mode 100644 src/common/logging/vst2.h diff --git a/meson.build b/meson.build index d488a3d1..9471ffa0 100644 --- a/meson.build +++ b/meson.build @@ -85,6 +85,7 @@ shared_library( 'src/common/serialization/vst2.cpp', 'src/common/configuration.cpp', 'src/common/logging/common.cpp', + 'src/common/logging/vst2.cpp', 'src/common/plugins.cpp', 'src/common/utils.cpp', 'src/plugin/bridges/vst2.cpp', @@ -204,6 +205,7 @@ if with_vst3 'yabridge-vst3', [ 'src/common/logging/common.cpp', + 'src/common/logging/vst2.cpp', 'src/plugin/vst3-plugin.cpp', version_header, ], @@ -229,6 +231,7 @@ host_sources = [ 'src/common/serialization/vst2.cpp', 'src/common/configuration.cpp', 'src/common/logging/common.cpp', + 'src/common/logging/vst2.cpp', 'src/common/plugins.cpp', 'src/common/utils.cpp', 'src/wine-host/bridges/vst2.cpp', diff --git a/src/common/communication/common.h b/src/common/communication/common.h index c4757296..39b97b4b 100644 --- a/src/common/communication/common.h +++ b/src/common/communication/common.h @@ -18,6 +18,7 @@ #include #include +#include #ifdef __WINE__ #include "../wine-host/boost-fix.h" diff --git a/src/common/communication/vst2.h b/src/common/communication/vst2.h index f0476355..8ecab67b 100644 --- a/src/common/communication/vst2.h +++ b/src/common/communication/vst2.h @@ -18,7 +18,8 @@ #include -#include "../logging/common.h" +#include "../logging/vst2.h" +#include "../serialization/vst2.h" #include "common.h" /** @@ -151,7 +152,7 @@ class EventHandler : public AdHocSocketHandler { */ template intptr_t send_event(D& data_converter, - std::optional> logging, + std::optional> logging, int opcode, int index, intptr_t value, @@ -226,7 +227,7 @@ class EventHandler : public AdHocSocketHandler { * @relates passthrough_event */ template - void receive_events(std::optional> logging, + void receive_events(std::optional> logging, F callback) { // Reading, processing, and writing back event data from the sockets // works in the same way regardless of which socket we're using diff --git a/src/common/logging/common.cpp b/src/common/logging/common.cpp index d77891cb..55ba54e7 100644 --- a/src/common/logging/common.cpp +++ b/src/common/logging/common.cpp @@ -46,7 +46,7 @@ constexpr char logging_verbosity_environment_variable[] = Logger::Logger(std::shared_ptr stream, Verbosity verbosity_level, std::string prefix) - : stream(stream), verbosity(verbosity_level), prefix(prefix) {} + : verbosity(verbosity_level), stream(stream), prefix(prefix) {} Logger Logger::create_from_environment(std::string prefix) { auto env = boost::this_process::environment(); @@ -108,514 +108,3 @@ void Logger::log_trace(const std::string& message) { log(message); } } - -void Logger::log_get_parameter(int index) { - if (BOOST_UNLIKELY(verbosity >= Verbosity::most_events)) { - std::ostringstream message; - message << ">> getParameter() " << index; - - log(message.str()); - } -} - -void Logger::log_get_parameter_response(float value) { - if (BOOST_UNLIKELY(verbosity >= Verbosity::most_events)) { - std::ostringstream message; - message << " getParameter() :: " << value; - - log(message.str()); - } -} - -void Logger::log_set_parameter(int index, float value) { - if (BOOST_UNLIKELY(verbosity >= Verbosity::most_events)) { - std::ostringstream message; - message << ">> setParameter() " << index << " = " << value; - - log(message.str()); - } -} - -void Logger::log_set_parameter_response() { - if (BOOST_UNLIKELY(verbosity >= Verbosity::most_events)) { - log(" setParameter() :: OK"); - } -} - -void Logger::log_event(bool is_dispatch, - int opcode, - int index, - intptr_t value, - const EventPayload& payload, - float option, - const std::optional& value_payload) { - if (BOOST_UNLIKELY(verbosity >= Verbosity::most_events)) { - if (should_filter_event(is_dispatch, opcode)) { - return; - } - - std::ostringstream message; - if (is_dispatch) { - message << ">> dispatch() "; - } else { - message << ">> audioMasterCallback() "; - } - - const auto opcode_name = opcode_to_string(is_dispatch, opcode); - if (opcode_name) { - message << *opcode_name; - } else { - message << ""; - } - - message << "(index = " << index << ", value = " << value - << ", option = " << option << ", data = "; - - // Only used during `effSetSpeakerArrangement` and - // `effGetSpeakerArrangement` - if (value_payload) { - std::visit( - overload{ - [&](auto) {}, - [&](const DynamicSpeakerArrangement& speaker_arrangement) { - message << "<" << speaker_arrangement.speakers.size() - << " input_speakers>, "; - }}, - *value_payload); - } - - std::visit( - overload{ - [&](const std::nullptr_t&) { message << ""; }, - [&](const std::string& s) { - if (s.size() < 32) { - message << "\"" << s << "\""; - } else { - // Long strings contain binary data that we probably - // don't want to print - message << "<" << s.size() << " bytes>"; - } - }, - [&](const ChunkData& chunk) { - message << "<" << chunk.buffer.size() << " byte chunk>"; - }, - [&](const native_size_t& window_id) { - message << ""; - }, - [&](const AEffect&) { message << ""; }, - [&](const DynamicVstEvents& events) { - message << "<" << events.events.size() << " midi_events>"; - }, - [&](const DynamicSpeakerArrangement& speaker_arrangement) { - message << "<" << speaker_arrangement.speakers.size() - << " output_speakers>"; - }, - [&](const VstIOProperties&) { message << ""; }, - [&](const VstMidiKeyName&) { message << ""; }, - [&](const VstParameterProperties&) { - message << ""; - }, - [&](const WantsAEffectUpdate&) { message << ""; }, - [&](const WantsChunkBuffer&) { - message << ""; - }, - [&](const WantsVstRect&) { message << ""; }, - [&](const WantsVstTimeInfo&) { message << ""; }, - [&](const WantsString&) { message << ""; }}, - payload); - - message << ")"; - - log(message.str()); - } -} - -void Logger::log_event_response( - bool is_dispatch, - int opcode, - intptr_t return_value, - const EventResultPayload& payload, - const std::optional& value_payload) { - if (BOOST_UNLIKELY(verbosity >= Verbosity::most_events)) { - if (should_filter_event(is_dispatch, opcode)) { - return; - } - - std::ostringstream message; - if (is_dispatch) { - message << " dispatch() :: "; - } else { - message << " audioMasterCallback() :: "; - } - - message << return_value; - - // Only used during `effSetSpeakerArrangement` and - // `effGetSpeakerArrangement` - if (value_payload) { - std::visit( - overload{ - [&](auto) {}, - [&](const DynamicSpeakerArrangement& speaker_arrangement) { - message << ", <" << speaker_arrangement.speakers.size() - << " input_speakers>"; - }}, - *value_payload); - } - - std::visit( - overload{ - [&](const std::nullptr_t&) {}, - [&](const std::string& s) { - if (s.size() < 32) { - message << ", \"" << s << "\""; - } else { - // Long strings contain binary data that we probably - // don't want to print - message << ", <" << s.size() << " bytes>"; - } - }, - [&](const ChunkData& chunk) { - message << ", <" << chunk.buffer.size() << " byte chunk>"; - }, - [&](const AEffect&) { message << ", "; }, - [&](const DynamicSpeakerArrangement& speaker_arrangement) { - message << ", <" << speaker_arrangement.speakers.size() - << " output_speakers>"; - }, - [&](const VstIOProperties&) { message << ", "; }, - [&](const VstMidiKeyName&) { message << ", "; }, - [&](const VstParameterProperties& props) { - message << ", "; - }, - [&](const VstRect& rect) { - message << ", {l: " << rect.left << ", t: " << rect.top - << ", r: " << rect.right << ", b: " << rect.bottom - << "}"; - }, - [&](const VstTimeInfo& info) { - message << ", <" - << "quarter_notes = " << info.ppqPos - << ", samples = " << info.samplePos << ">"; - }}, - payload); - - log(message.str()); - } -} - -bool Logger::should_filter_event(bool is_dispatch, int opcode) const { - if (verbosity >= Verbosity::all_events) { - return false; - } - - // Filter out log messages related to these events by default since they are - // called tens of times per second - // TODO: Figure out what opcode 52 is - if ((is_dispatch && - (opcode == effEditIdle || opcode == 52 || opcode == effIdle)) || - (!is_dispatch && (opcode == audioMasterGetTime || - opcode == audioMasterGetCurrentProcessLevel))) { - return true; - } - - return false; -} - -std::optional opcode_to_string(bool is_dispatch, int opcode) { - if (is_dispatch) { - // Opcodes for a plugin's dispatch function - switch (opcode) { - case effOpen: - return "effOpen"; - break; - case effClose: - return "effClose"; - break; - case effSetProgram: - return "effSetProgram"; - break; - case effGetProgram: - return "effGetProgram"; - break; - case effSetProgramName: - return "effSetProgramName"; - break; - case effGetProgramName: - return "effGetProgramName"; - break; - case effGetParamLabel: - return "effGetParamLabel"; - break; - case effGetParamDisplay: - return "effGetParamDisplay"; - break; - case effGetParamName: - return "effGetParamName"; - break; - case effSetSampleRate: - return "effSetSampleRate"; - break; - case effSetBlockSize: - return "effSetBlockSize"; - break; - case effMainsChanged: - return "effMainsChanged"; - break; - case effEditGetRect: - return "effEditGetRect"; - break; - case effEditOpen: - return "effEditOpen"; - break; - case effEditClose: - return "effEditClose"; - break; - case effEditIdle: - return "effEditIdle"; - break; - case effEditTop: - return "effEditTop"; - break; - case effIdentify: - return "effIdentify"; - break; - case effGetChunk: - return "effGetChunk"; - break; - case effSetChunk: - return "effSetChunk"; - break; - case effProcessEvents: - return "effProcessEvents"; - break; - case effCanBeAutomated: - return "effCanBeAutomated"; - break; - case effGetProgramNameIndexed: - return "effGetProgramNameIndexed"; - break; - case effGetPlugCategory: - return "effGetPlugCategory"; - break; - case effGetEffectName: - return "effGetEffectName"; - break; - case effGetParameterProperties: - return "effGetParameterProperties"; - break; - case effGetVendorString: - return "effGetVendorString"; - break; - case effGetProductString: - return "effGetProductString"; - break; - case effGetVendorVersion: - return "effGetVendorVersion"; - break; - case effCanDo: - return "effCanDo"; - break; - case effIdle: - return "effIdle"; - break; - case effGetVstVersion: - return "effGetVstVersion"; - break; - case effBeginSetProgram: - return "effBeginSetProgram"; - break; - case effEndSetProgram: - return "effEndSetProgram"; - break; - case effShellGetNextPlugin: - return "effShellGetNextPlugin"; - break; - case effBeginLoadBank: - return "effBeginLoadBank"; - break; - case effBeginLoadProgram: - return "effBeginLoadProgram"; - break; - case effStartProcess: - return "effStartProcess"; - break; - case effStopProcess: - return "effStopProcess"; - break; - case effGetInputProperties: - return "effGetInputProperties"; - break; - case effGetOutputProperties: - return "effGetOutputProperties"; - break; - case effGetMidiKeyName: - return "effGetMidiKeyName"; - break; - case effSetSpeakerArrangement: - return "effSetSpeakerArrangement"; - break; - case effGetSpeakerArrangement: - return "effGetSpeakerArrangement "; - break; - default: - return std::nullopt; - break; - } - } else { - // Opcodes for the host callback - switch (opcode) { - case audioMasterAutomate: - return "audioMasterAutomate"; - break; - case audioMasterVersion: - return "audioMasterVersion"; - break; - case audioMasterCurrentId: - return "audioMasterCurrentId"; - break; - case audioMasterIdle: - return "audioMasterIdle"; - break; - case audioMasterPinConnected: - return "audioMasterPinConnected"; - break; - case audioMasterWantMidi: - return "audioMasterWantMidi"; - break; - case audioMasterGetTime: - return "audioMasterGetTime"; - break; - case audioMasterProcessEvents: - return "audioMasterProcessEvents"; - break; - case audioMasterSetTime: - return "audioMasterSetTime"; - break; - case audioMasterTempoAt: - return "audioMasterTempoAt"; - break; - case audioMasterGetNumAutomatableParameters: - return "audioMasterGetNumAutomatableParameters"; - break; - case audioMasterGetParameterQuantization: - return "audioMasterGetParameterQuantization"; - break; - case audioMasterIOChanged: - return "audioMasterIOChanged"; - break; - case audioMasterNeedIdle: - return "audioMasterNeedIdle"; - break; - case audioMasterSizeWindow: - return "audioMasterSizeWindow"; - break; - case audioMasterGetSampleRate: - return "audioMasterGetSampleRate"; - break; - case audioMasterGetBlockSize: - return "audioMasterGetBlockSize"; - break; - case audioMasterGetInputLatency: - return "audioMasterGetInputLatency"; - break; - case audioMasterGetOutputLatency: - return "audioMasterGetOutputLatency"; - break; - case audioMasterGetPreviousPlug: - return "audioMasterGetPreviousPlug"; - break; - case audioMasterGetNextPlug: - return "audioMasterGetNextPlug"; - break; - case audioMasterWillReplaceOrAccumulate: - return "audioMasterWillReplaceOrAccumulate"; - break; - case audioMasterGetCurrentProcessLevel: - return "audioMasterGetCurrentProcessLevel"; - break; - case audioMasterGetAutomationState: - return "audioMasterGetAutomationState"; - break; - case audioMasterOfflineStart: - return "audioMasterOfflineStart"; - break; - case audioMasterOfflineRead: - return "audioMasterOfflineRead"; - break; - case audioMasterOfflineWrite: - return "audioMasterOfflineWrite"; - break; - case audioMasterOfflineGetCurrentPass: - return "audioMasterOfflineGetCurrentPass"; - break; - case audioMasterOfflineGetCurrentMetaPass: - return "audioMasterOfflineGetCurrentMetaPass"; - break; - case audioMasterSetOutputSampleRate: - return "audioMasterSetOutputSampleRate"; - break; - case audioMasterGetSpeakerArrangement: - return "audioMasterGetSpeakerArrangement"; - break; - case audioMasterGetVendorString: - return "audioMasterGetVendorString"; - break; - case audioMasterGetProductString: - return "audioMasterGetProductString"; - break; - case audioMasterGetVendorVersion: - return "audioMasterGetVendorVersion"; - break; - case audioMasterVendorSpecific: - return "audioMasterVendorSpecific"; - break; - case audioMasterSetIcon: - return "audioMasterSetIcon"; - break; - case audioMasterCanDo: - return "audioMasterCanDo"; - break; - case audioMasterGetLanguage: - return "audioMasterGetLanguage"; - break; - case audioMasterOpenWindow: - return "audioMasterOpenWindow"; - break; - case audioMasterCloseWindow: - return "audioMasterCloseWindow"; - break; - case audioMasterGetDirectory: - return "audioMasterGetDirectory"; - break; - case audioMasterUpdateDisplay: - return "audioMasterUpdateDisplay"; - break; - case audioMasterBeginEdit: - return "audioMasterBeginEdit"; - break; - case audioMasterEndEdit: - return "audioMasterEndEdit"; - break; - case audioMasterOpenFileSelector: - return "audioMasterOpenFileSelector"; - break; - case audioMasterCloseFileSelector: - return "audioMasterCloseFileSelector"; - break; - case audioMasterEditFile: - return "audioMasterEditFile"; - break; - case audioMasterGetChunkFile: - return "audioMasterGetChunkFile"; - break; - case audioMasterGetInputSpeakerArrangement: - return "audioMasterGetInputSpeakerArrangement"; - break; - default: - return std::nullopt; - break; - } - } -} diff --git a/src/common/logging/common.h b/src/common/logging/common.h index 06390ef2..c26ecc8f 100644 --- a/src/common/logging/common.h +++ b/src/common/logging/common.h @@ -20,10 +20,6 @@ #include #include -// TODO: Split up the plugin API specific logging functions so we don't have to -// include a bunch of stuff we don't need -#include "../serialization/vst2.h" - /** * Super basic logging facility meant for debugging malfunctioning VST * plugins. This is also used to redirect the output of the Wine process @@ -97,29 +93,6 @@ class Logger { */ void log(const std::string& message); - // The following functions are for logging specific events, they are only - // enabled for verbosity levels higher than 1 (i.e. `Verbosity::events`) - void log_get_parameter(int index); - void log_get_parameter_response(float vlaue); - void log_set_parameter(int index, float value); - void log_set_parameter_response(); - // If `is_dispatch` is `true`, then use opcode names from the plugin's - // dispatch function. Otherwise use names for the host callback function - // opcodes. - void log_event(bool is_dispatch, - int opcode, - int index, - intptr_t value, - const EventPayload& payload, - float option, - const std::optional& value_payload); - void log_event_response( - bool is_dispatch, - int opcode, - intptr_t return_value, - const EventResultPayload& payload, - const std::optional& value_payload); - /** * Log a message that should only be printed when the `verbosity` is set to * `all_events`. This should only be used for simple primitive messages @@ -130,38 +103,20 @@ class Logger { */ void log_trace(const std::string& message); - private: /** - * Determine whether an event should be filtered based on the current - * verbosity level. + * The verbosity level of this logger instance. Based on this certain + * messages may or may not be shown. */ - bool should_filter_event(bool is_dispatch, int opcode) const; + const Verbosity verbosity; + private: /** * The output stream to write the log messages to. Typically either STDERR * or a file stream. */ std::shared_ptr stream; - /** - * The verbosity level of this logger instance. Based on this certain - * messages may or may not be shown. - */ - Verbosity verbosity; /** * A prefix that gets prepended before every message. */ std::string prefix; }; - -/** - * Convert an event opcode to a human readable string for debugging purposes. - * See `src/include/vestige/aeffectx.h` for a complete list of these opcodes. - * - * @param is_dispatch Whether to use opcodes for the `dispatch` function. Will - * use the names from the host callback function if set to false. - * @param opcode The opcode of the event. - * - * @return Either the name from `aeffectx.h`, or a nullopt if it was not listed - * there. - */ -std::optional opcode_to_string(bool is_dispatch, int opcode); diff --git a/src/common/logging/vst2.cpp b/src/common/logging/vst2.cpp new file mode 100644 index 00000000..0c2406c7 --- /dev/null +++ b/src/common/logging/vst2.cpp @@ -0,0 +1,530 @@ +// yabridge: a Wine VST bridge +// Copyright (C) 2020 Robbert van der Helm +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "vst2.h" + +#include + +std::optional opcode_to_string(bool is_dispatch, int opcode) { + if (is_dispatch) { + // Opcodes for a plugin's dispatch function + switch (opcode) { + case effOpen: + return "effOpen"; + break; + case effClose: + return "effClose"; + break; + case effSetProgram: + return "effSetProgram"; + break; + case effGetProgram: + return "effGetProgram"; + break; + case effSetProgramName: + return "effSetProgramName"; + break; + case effGetProgramName: + return "effGetProgramName"; + break; + case effGetParamLabel: + return "effGetParamLabel"; + break; + case effGetParamDisplay: + return "effGetParamDisplay"; + break; + case effGetParamName: + return "effGetParamName"; + break; + case effSetSampleRate: + return "effSetSampleRate"; + break; + case effSetBlockSize: + return "effSetBlockSize"; + break; + case effMainsChanged: + return "effMainsChanged"; + break; + case effEditGetRect: + return "effEditGetRect"; + break; + case effEditOpen: + return "effEditOpen"; + break; + case effEditClose: + return "effEditClose"; + break; + case effEditIdle: + return "effEditIdle"; + break; + case effEditTop: + return "effEditTop"; + break; + case effIdentify: + return "effIdentify"; + break; + case effGetChunk: + return "effGetChunk"; + break; + case effSetChunk: + return "effSetChunk"; + break; + case effProcessEvents: + return "effProcessEvents"; + break; + case effCanBeAutomated: + return "effCanBeAutomated"; + break; + case effGetProgramNameIndexed: + return "effGetProgramNameIndexed"; + break; + case effGetPlugCategory: + return "effGetPlugCategory"; + break; + case effGetEffectName: + return "effGetEffectName"; + break; + case effGetParameterProperties: + return "effGetParameterProperties"; + break; + case effGetVendorString: + return "effGetVendorString"; + break; + case effGetProductString: + return "effGetProductString"; + break; + case effGetVendorVersion: + return "effGetVendorVersion"; + break; + case effCanDo: + return "effCanDo"; + break; + case effIdle: + return "effIdle"; + break; + case effGetVstVersion: + return "effGetVstVersion"; + break; + case effBeginSetProgram: + return "effBeginSetProgram"; + break; + case effEndSetProgram: + return "effEndSetProgram"; + break; + case effShellGetNextPlugin: + return "effShellGetNextPlugin"; + break; + case effBeginLoadBank: + return "effBeginLoadBank"; + break; + case effBeginLoadProgram: + return "effBeginLoadProgram"; + break; + case effStartProcess: + return "effStartProcess"; + break; + case effStopProcess: + return "effStopProcess"; + break; + case effGetInputProperties: + return "effGetInputProperties"; + break; + case effGetOutputProperties: + return "effGetOutputProperties"; + break; + case effGetMidiKeyName: + return "effGetMidiKeyName"; + break; + case effSetSpeakerArrangement: + return "effSetSpeakerArrangement"; + break; + case effGetSpeakerArrangement: + return "effGetSpeakerArrangement "; + break; + default: + return std::nullopt; + break; + } + } else { + // Opcodes for the host callback + switch (opcode) { + case audioMasterAutomate: + return "audioMasterAutomate"; + break; + case audioMasterVersion: + return "audioMasterVersion"; + break; + case audioMasterCurrentId: + return "audioMasterCurrentId"; + break; + case audioMasterIdle: + return "audioMasterIdle"; + break; + case audioMasterPinConnected: + return "audioMasterPinConnected"; + break; + case audioMasterWantMidi: + return "audioMasterWantMidi"; + break; + case audioMasterGetTime: + return "audioMasterGetTime"; + break; + case audioMasterProcessEvents: + return "audioMasterProcessEvents"; + break; + case audioMasterSetTime: + return "audioMasterSetTime"; + break; + case audioMasterTempoAt: + return "audioMasterTempoAt"; + break; + case audioMasterGetNumAutomatableParameters: + return "audioMasterGetNumAutomatableParameters"; + break; + case audioMasterGetParameterQuantization: + return "audioMasterGetParameterQuantization"; + break; + case audioMasterIOChanged: + return "audioMasterIOChanged"; + break; + case audioMasterNeedIdle: + return "audioMasterNeedIdle"; + break; + case audioMasterSizeWindow: + return "audioMasterSizeWindow"; + break; + case audioMasterGetSampleRate: + return "audioMasterGetSampleRate"; + break; + case audioMasterGetBlockSize: + return "audioMasterGetBlockSize"; + break; + case audioMasterGetInputLatency: + return "audioMasterGetInputLatency"; + break; + case audioMasterGetOutputLatency: + return "audioMasterGetOutputLatency"; + break; + case audioMasterGetPreviousPlug: + return "audioMasterGetPreviousPlug"; + break; + case audioMasterGetNextPlug: + return "audioMasterGetNextPlug"; + break; + case audioMasterWillReplaceOrAccumulate: + return "audioMasterWillReplaceOrAccumulate"; + break; + case audioMasterGetCurrentProcessLevel: + return "audioMasterGetCurrentProcessLevel"; + break; + case audioMasterGetAutomationState: + return "audioMasterGetAutomationState"; + break; + case audioMasterOfflineStart: + return "audioMasterOfflineStart"; + break; + case audioMasterOfflineRead: + return "audioMasterOfflineRead"; + break; + case audioMasterOfflineWrite: + return "audioMasterOfflineWrite"; + break; + case audioMasterOfflineGetCurrentPass: + return "audioMasterOfflineGetCurrentPass"; + break; + case audioMasterOfflineGetCurrentMetaPass: + return "audioMasterOfflineGetCurrentMetaPass"; + break; + case audioMasterSetOutputSampleRate: + return "audioMasterSetOutputSampleRate"; + break; + case audioMasterGetSpeakerArrangement: + return "audioMasterGetSpeakerArrangement"; + break; + case audioMasterGetVendorString: + return "audioMasterGetVendorString"; + break; + case audioMasterGetProductString: + return "audioMasterGetProductString"; + break; + case audioMasterGetVendorVersion: + return "audioMasterGetVendorVersion"; + break; + case audioMasterVendorSpecific: + return "audioMasterVendorSpecific"; + break; + case audioMasterSetIcon: + return "audioMasterSetIcon"; + break; + case audioMasterCanDo: + return "audioMasterCanDo"; + break; + case audioMasterGetLanguage: + return "audioMasterGetLanguage"; + break; + case audioMasterOpenWindow: + return "audioMasterOpenWindow"; + break; + case audioMasterCloseWindow: + return "audioMasterCloseWindow"; + break; + case audioMasterGetDirectory: + return "audioMasterGetDirectory"; + break; + case audioMasterUpdateDisplay: + return "audioMasterUpdateDisplay"; + break; + case audioMasterBeginEdit: + return "audioMasterBeginEdit"; + break; + case audioMasterEndEdit: + return "audioMasterEndEdit"; + break; + case audioMasterOpenFileSelector: + return "audioMasterOpenFileSelector"; + break; + case audioMasterCloseFileSelector: + return "audioMasterCloseFileSelector"; + break; + case audioMasterEditFile: + return "audioMasterEditFile"; + break; + case audioMasterGetChunkFile: + return "audioMasterGetChunkFile"; + break; + case audioMasterGetInputSpeakerArrangement: + return "audioMasterGetInputSpeakerArrangement"; + break; + default: + return std::nullopt; + break; + } + } +} + +void Vst2Logger::log_get_parameter(int index) { + if (BOOST_UNLIKELY(verbosity >= Verbosity::most_events)) { + std::ostringstream message; + message << ">> getParameter() " << index; + + log(message.str()); + } +} + +void Vst2Logger::log_get_parameter_response(float value) { + if (BOOST_UNLIKELY(verbosity >= Verbosity::most_events)) { + std::ostringstream message; + message << " getParameter() :: " << value; + + log(message.str()); + } +} + +void Vst2Logger::log_set_parameter(int index, float value) { + if (BOOST_UNLIKELY(verbosity >= Verbosity::most_events)) { + std::ostringstream message; + message << ">> setParameter() " << index << " = " << value; + + log(message.str()); + } +} + +void Vst2Logger::log_set_parameter_response() { + if (BOOST_UNLIKELY(verbosity >= Verbosity::most_events)) { + log(" setParameter() :: OK"); + } +} + +void Vst2Logger::log_event(bool is_dispatch, + int opcode, + int index, + intptr_t value, + const EventPayload& payload, + float option, + const std::optional& value_payload) { + if (BOOST_UNLIKELY(verbosity >= Verbosity::most_events)) { + if (should_filter_event(is_dispatch, opcode)) { + return; + } + + std::ostringstream message; + if (is_dispatch) { + message << ">> dispatch() "; + } else { + message << ">> audioMasterCallback() "; + } + + const auto opcode_name = opcode_to_string(is_dispatch, opcode); + if (opcode_name) { + message << *opcode_name; + } else { + message << ""; + } + + message << "(index = " << index << ", value = " << value + << ", option = " << option << ", data = "; + + // Only used during `effSetSpeakerArrangement` and + // `effGetSpeakerArrangement` + if (value_payload) { + std::visit( + overload{ + [&](auto) {}, + [&](const DynamicSpeakerArrangement& speaker_arrangement) { + message << "<" << speaker_arrangement.speakers.size() + << " input_speakers>, "; + }}, + *value_payload); + } + + std::visit( + overload{ + [&](const std::nullptr_t&) { message << ""; }, + [&](const std::string& s) { + if (s.size() < 32) { + message << "\"" << s << "\""; + } else { + // Long strings contain binary data that we probably + // don't want to print + message << "<" << s.size() << " bytes>"; + } + }, + [&](const ChunkData& chunk) { + message << "<" << chunk.buffer.size() << " byte chunk>"; + }, + [&](const native_size_t& window_id) { + message << ""; + }, + [&](const AEffect&) { message << ""; }, + [&](const DynamicVstEvents& events) { + message << "<" << events.events.size() << " midi_events>"; + }, + [&](const DynamicSpeakerArrangement& speaker_arrangement) { + message << "<" << speaker_arrangement.speakers.size() + << " output_speakers>"; + }, + [&](const VstIOProperties&) { message << ""; }, + [&](const VstMidiKeyName&) { message << ""; }, + [&](const VstParameterProperties&) { + message << ""; + }, + [&](const WantsAEffectUpdate&) { message << ""; }, + [&](const WantsChunkBuffer&) { + message << ""; + }, + [&](const WantsVstRect&) { message << ""; }, + [&](const WantsVstTimeInfo&) { message << ""; }, + [&](const WantsString&) { message << ""; }}, + payload); + + message << ")"; + + log(message.str()); + } +} + +void Vst2Logger::log_event_response( + bool is_dispatch, + int opcode, + intptr_t return_value, + const EventResultPayload& payload, + const std::optional& value_payload) { + if (BOOST_UNLIKELY(verbosity >= Verbosity::most_events)) { + if (should_filter_event(is_dispatch, opcode)) { + return; + } + + std::ostringstream message; + if (is_dispatch) { + message << " dispatch() :: "; + } else { + message << " audioMasterCallback() :: "; + } + + message << return_value; + + // Only used during `effSetSpeakerArrangement` and + // `effGetSpeakerArrangement` + if (value_payload) { + std::visit( + overload{ + [&](auto) {}, + [&](const DynamicSpeakerArrangement& speaker_arrangement) { + message << ", <" << speaker_arrangement.speakers.size() + << " input_speakers>"; + }}, + *value_payload); + } + + std::visit( + overload{ + [&](const std::nullptr_t&) {}, + [&](const std::string& s) { + if (s.size() < 32) { + message << ", \"" << s << "\""; + } else { + // Long strings contain binary data that we probably + // don't want to print + message << ", <" << s.size() << " bytes>"; + } + }, + [&](const ChunkData& chunk) { + message << "<" << chunk.buffer.size() << " byte chunk>"; + }, + [&](const AEffect&) { message << ", "; }, + [&](const DynamicSpeakerArrangement& speaker_arrangement) { + message << ", <" << speaker_arrangement.speakers.size() + << " output_speakers>"; + }, + [&](const VstIOProperties&) { message << ", "; }, + [&](const VstMidiKeyName&) { message << ", "; }, + [&](const VstParameterProperties& props) { + message << ", "; + }, + [&](const VstRect& rect) { + message << ", {l: " << rect.left << ", t: " << rect.top + << ", r: " << rect.right << ", b: " << rect.bottom + << "}"; + }, + [&](const VstTimeInfo& info) { + message << ", <" + << "quarter_notes = " << info.ppqPos + << ", samples = " << info.samplePos << ">"; + }}, + payload); + + log(message.str()); + } +} + +bool Vst2Logger::should_filter_event(bool is_dispatch, int opcode) const { + if (verbosity >= Verbosity::all_events) { + return false; + } + + // Filter out log messages related to these events by default since they are + // called tens of times per second + // TODO: Figure out what opcode 52 is + if ((is_dispatch && + (opcode == effEditIdle || opcode == 52 || opcode == effIdle)) || + (!is_dispatch && (opcode == audioMasterGetTime || + opcode == audioMasterGetCurrentProcessLevel))) { + return true; + } + + return false; +} diff --git a/src/common/logging/vst2.h b/src/common/logging/vst2.h new file mode 100644 index 00000000..841b46bf --- /dev/null +++ b/src/common/logging/vst2.h @@ -0,0 +1,66 @@ +// yabridge: a Wine VST bridge +// Copyright (C) 2020 Robbert van der Helm +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#pragma once + +#include "../serialization/vst2.h" +#include "common.h" + +/** + * Convert an event opcode to a human readable string for debugging purposes. + * See `src/include/vestige/aeffectx.h` for a complete list of these opcodes. + * + * @param is_dispatch Whether to use opcodes for the `dispatch` function. Will + * use the names from the host callback function if set to false. + * @param opcode The opcode of the event. + * + * @return Either the name from `aeffectx.h`, or a nullopt if it was not listed + * there. + */ +std::optional opcode_to_string(bool is_dispatch, int opcode); + +class Vst2Logger : public Logger { + public: + // The following functions are for logging specific events, they are only + // enabled for verbosity levels higher than 1 (i.e. `Verbosity::events`) + void log_get_parameter(int index); + void log_get_parameter_response(float vlaue); + void log_set_parameter(int index, float value); + void log_set_parameter_response(); + // If `is_dispatch` is `true`, then use opcode names from the plugin's + // dispatch function. Otherwise use names for the host callback function + // opcodes. + void log_event(bool is_dispatch, + int opcode, + int index, + intptr_t value, + const EventPayload& payload, + float option, + const std::optional& value_payload); + void log_event_response( + bool is_dispatch, + int opcode, + intptr_t return_value, + const EventResultPayload& payload, + const std::optional& value_payload); + + private: + /** + * Determine whether an event should be filtered based on the current + * verbosity level. + */ + bool should_filter_event(bool is_dispatch, int opcode) const; +}; diff --git a/src/plugin/bridges/vst2.cpp b/src/plugin/bridges/vst2.cpp index 299363c5..7944a29c 100644 --- a/src/plugin/bridges/vst2.cpp +++ b/src/plugin/bridges/vst2.cpp @@ -127,7 +127,7 @@ Vst2PluginBridge::Vst2PluginBridge(audioMasterCallback host_callback) // lockstep anyway host_callback_handler = std::jthread([&]() { sockets.vst_host_callback.receive_events( - std::pair(logger, false), + std::pair(logger, false), [&](Event& event, bool /*on_main_thread*/) { // MIDI events sent from the plugin back to the host are a // special case here. They have to sent during the @@ -431,8 +431,8 @@ intptr_t Vst2PluginBridge::dispatch(AEffect* /*plugin*/, try { // TODO: Add some kind of timeout? return_value = sockets.host_vst_dispatch.send_event( - converter, std::pair(logger, true), opcode, - index, value, data, option); + converter, std::pair(logger, true), + opcode, index, value, data, option); } catch (const boost::system::system_error& a) { // Thrown when the socket gets closed because the VST plugin // loaded into the Wine process crashed during shutdown @@ -483,8 +483,8 @@ intptr_t Vst2PluginBridge::dispatch(AEffect* /*plugin*/, // receiving function temporarily allocate a large enough buffer rather than // to have a bunch of allocated memory sitting around doing nothing. return sockets.host_vst_dispatch.send_event( - converter, std::pair(logger, true), opcode, index, value, - data, option); + converter, std::pair(logger, true), opcode, index, + value, data, option); } template diff --git a/src/plugin/bridges/vst2.h b/src/plugin/bridges/vst2.h index 45c9e3d2..ce28a7aa 100644 --- a/src/plugin/bridges/vst2.h +++ b/src/plugin/bridges/vst2.h @@ -25,7 +25,7 @@ #include "../../common/communication/vst2.h" #include "../../common/configuration.h" -#include "../../common/logging/common.h" +#include "../../common/logging/vst2.h" #include "../host-process.h" /** @@ -171,7 +171,7 @@ class Vst2PluginBridge { * * @see Logger::create_from_env */ - Logger logger; + Vst2Logger logger; /** * The version of Wine currently in use. Used in the debug output on plugin diff --git a/src/plugin/host-process.h b/src/plugin/host-process.h index c1196b25..ca5987c0 100644 --- a/src/plugin/host-process.h +++ b/src/plugin/host-process.h @@ -28,6 +28,7 @@ #include "../common/communication/common.h" #include "../common/logging/common.h" #include "../common/plugins.h" +#include "../common/serialization/common.h" #include "utils.h" /** diff --git a/src/wine-host/bridges/vst2.h b/src/wine-host/bridges/vst2.h index 11014889..070357b3 100644 --- a/src/wine-host/bridges/vst2.h +++ b/src/wine-host/bridges/vst2.h @@ -31,7 +31,6 @@ #include "../../common/communication/vst2.h" #include "../../common/configuration.h" -#include "../../common/logging/common.h" #include "../editor.h" #include "../utils.h"