From 80d071261442a801f9124888d6ccf520c9d426bc Mon Sep 17 00:00:00 2001 From: Robin Davies Date: Thu, 17 Mar 2022 23:08:09 -0400 Subject: [PATCH] Debug Chorus and Delay. --- react/src/ToobMLView.tsx | 57 +++++++++++- src/CMakeLists.txt | 1 + src/Lv2Host.cpp | 193 +++++++++++++++++++-------------------- src/StdOutCapture.cpp | 100 ++++++++++++++++++++ src/StdOutCapture.hpp | 42 +++++++++ 5 files changed, 292 insertions(+), 101 deletions(-) create mode 100644 src/StdOutCapture.cpp create mode 100644 src/StdOutCapture.hpp diff --git a/react/src/ToobMLView.tsx b/react/src/ToobMLView.tsx index 489f238..f0366ab 100644 --- a/react/src/ToobMLView.tsx +++ b/react/src/ToobMLView.tsx @@ -25,7 +25,7 @@ import createStyles from '@mui/styles/createStyles'; import withStyles from '@mui/styles/withStyles'; import IControlViewFactory from './IControlViewFactory'; -import { PiPedalModelFactory, PiPedalModel } from "./PiPedalModel"; +import { PiPedalModelFactory, PiPedalModel,MonitorPortHandle } from "./PiPedalModel"; import { PedalBoardItem } from './PedalBoard'; import PluginControlView, { ControlGroup,ControlViewCustomization } from './PluginControlView'; import ToobFrequencyResponseView from './ToobFrequencyResponseView'; @@ -41,7 +41,7 @@ interface ToobMLProps extends WithStyles { } interface ToobMLState { - + gainEnabled: boolean; } const ToobMLView = @@ -50,22 +50,73 @@ const ToobMLView = implements ControlViewCustomization { model: PiPedalModel; + gainRef: React.RefObject; customizationId: number = 1; constructor(props: ToobMLProps) { super(props); this.model = PiPedalModelFactory.getInstance(); + this.gainRef = React.createRef(); this.state = { + gainEnabled: true } } + subscribedId?: number = undefined; + monitorPortHandle?: MonitorPortHandle = undefined; + removeGainEnabledSubscription() + { + if (this.monitorPortHandle) + { + this.model.unmonitorPort(this.monitorPortHandle); + this.monitorPortHandle = undefined; + } + } + addGainEnabledSubscription(instanceId: number) + { + this.removeGainEnabledSubscription(); + + this.monitorPortHandle = this.model.monitorPort(instanceId,"gainEnabled",0.1, + (value: number) => { + if (this.gainRef.current) + { + this.gainRef.current.style.opacity = value !== 0.0? "1.0": "0.4"; + } + this.setState({gainEnabled: value !== 0.0 }); + }); + } + + componentDidUpdate() + { + if (this.props.instanceId !== this.subscribedId) + { + this.removeGainEnabledSubscription(); + this.addGainEnabledSubscription(this.props.instanceId); + + } + } + componentDidMount() + { + this.addGainEnabledSubscription(this.props.instanceId); + } + componentWillUnmount() + { + this.removeGainEnabledSubscription(); + } + ModifyControls(controls: (React.ReactNode| ControlGroup)[]): (React.ReactNode| ControlGroup)[] { - let group = controls[1] as ControlGroup; + let group = controls[4] as ControlGroup; group.controls.splice(0,0, ( ) ); + + let gainControl: React.ReactElement = controls[3] as React.ReactElement; + if (gainControl) + { + controls[3] = (
{ gainControl}
); + } return controls; } render() { diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 641bc6c..77904d2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -102,6 +102,7 @@ add_custom_command( set (PIPEDAL_SOURCES + StdOutCapture.hpp StdOutCapture.cpp Ipv6Helpers.cpp Ipv6Helpers.hpp PluginPreset.cpp PluginPreset.hpp CpuGovernor.cpp CpuGovernor.hpp diff --git a/src/Lv2Host.cpp b/src/Lv2Host.cpp index 71bafc4..69f62d9 100644 --- a/src/Lv2Host.cpp +++ b/src/Lv2Host.cpp @@ -37,26 +37,26 @@ #include "lv2/lv2plug.in/ns/ext/buf-size/buf-size.h" #include "lv2/lv2plug.in/ns/ext/presets/presets.h" #include "lv2/lv2plug.in/ns/ext/port-props/port-props.h" -#include "lv2/lv2plug.in/ns/extensions/units/units.h" +#include "lv2/lv2plug.in/ns/extensions/units/units.h" #include "lv2/lv2plug.in/ns/ext/port-groups/port-groups.h" #include #include "PiPedalException.hpp" +#include "StdOutCapture.hpp" #include "Locale.hpp" - using namespace pipedal; #define MAP_CHECK() \ { \ if (GetPluginClass("http://lv2plug.in/ns/lv2core#Plugin") == nullptr) \ - throw PiPedalStateException("Map is broken"); \ + throw PiPedalStateException("Map is broken"); \ } #define PLUGIN_MAP_CHECK() \ { \ if (pHost->GetPluginClass("http://lv2plug.in/ns/lv2core#Plugin") == nullptr) \ - throw PiPedalStateException("Map is broken"); \ + throw PiPedalStateException("Map is broken"); \ } #define P_LV2_CORE_URI "http://lv2plug.in/ns/lv2core#" @@ -85,20 +85,18 @@ namespace pipedal static const char *LV2_MIDI_EVENT = "http://lv2plug.in/ns/ext/midi#MidiEvent"; static const char *LV2_DESIGNATION = "http://lv2plug.in/ns/lv2core#Designation"; - - class Lv2Host::Urids { + class Lv2Host::Urids + { public: Urids(MapFeature &mapFeature) { - atom_Float = mapFeature.GetUrid(LV2_ATOM__Float); + atom_Float = mapFeature.GetUrid(LV2_ATOM__Float); } LV2_URID atom_Float; }; } - - -void Lv2Host::LilvUris::Initialize(LilvWorld*pWorld) +void Lv2Host::LilvUris::Initialize(LilvWorld *pWorld) { rdfsComment = lilv_new_uri(pWorld, Lv2Host::RDFS_COMMENT_URI); logarithic_uri = lilv_new_uri(pWorld, LV2_PORT_LOGARITHMIC); @@ -123,10 +121,7 @@ void Lv2Host::LilvUris::Initialize(LilvWorld*pWorld) time_beatsPerMinute = lilv_new_uri(pWorld, LV2_TIME__beatsPerMinute); time_speed = lilv_new_uri(pWorld, LV2_TIME__speed); - appliesTo = lilv_new_uri(pWorld,LV2_CORE__appliesTo); - - - + appliesTo = lilv_new_uri(pWorld, LV2_CORE__appliesTo); } void Lv2Host::LilvUris::Free() @@ -199,9 +194,9 @@ static float nodeAsFloat(const LilvNode *node, float default_ = 0) return default_; } -class NodeAutoFree: public LilvNodePtr +class NodeAutoFree : public LilvNodePtr { - + // const LilvNode* returns must not be freed, by convention. private: NodeAutoFree(const LilvNode *node) @@ -213,7 +208,7 @@ public: { } NodeAutoFree(LilvNode *node) - :LilvNodePtr(node) + : LilvNodePtr(node) { } @@ -336,7 +331,7 @@ std::shared_ptr Lv2Host::GetPluginClass(const LilvPluginClass *p std::shared_ptr Lv2Host::MakePluginClass(const LilvPluginClass *pClass) { std::string uri = nodeAsString(lilv_plugin_class_get_uri(pClass)); - auto t = this->classesMap.find(uri); + auto t = this->classesMap.find(uri); if (t != classesMap.end()) { return t->second; @@ -385,6 +380,7 @@ void Lv2Host::LoadPluginClassesFromLilv() void Lv2Host::Load(const char *lv2Path) { + this->plugins_.clear(); this->ui_plugins_.clear(); if (!classesLoaded) @@ -395,8 +391,13 @@ void Lv2Host::Load(const char *lv2Path) free_world(); setenv("LV2_PATH", lv2Path, true); + StdOutCapture stdoutCapture; // captures lilv messages written to STDOUT. pWorld = lilv_world_new(); - lilv_world_load_all(pWorld); + { + + lilv_world_load_all(pWorld); + + } lilvUris.Initialize(pWorld); @@ -404,7 +405,6 @@ void Lv2Host::Load(const char *lv2Path) LoadPluginClassesFromLilv(); - LILV_FOREACH(plugins, iPlugin, plugins) { const LilvPlugin *lilvPlugin = lilv_plugins_get(plugins, iPlugin); @@ -441,8 +441,14 @@ void Lv2Host::Load(const char *lv2Path) this->plugins_.push_back(pluginInfo); } } + auto messages = stdoutCapture.GetOutputLines(); - const auto &collation = std::use_facet >(std::locale()); + for (const std::string & s: messages) + { + Lv2Log::info("lilv: " + s); + } + + const auto &collation = std::use_facet>(std::locale()); auto compare = [&collation]( const std::shared_ptr &left, const std::shared_ptr &right) @@ -485,7 +491,8 @@ void Lv2Host::Load(const char *lv2Path) } } } -}; +} +; class NodesAutoFree { @@ -526,7 +533,7 @@ static std::vector nodeAsStringArray(const LilvNodes *nodes) } const char *Lv2Host::RDFS_COMMENT_URI = "http://www.w3.org/2000/01/rdf-schema#" - "comment"; + "comment"; LilvNode *Lv2Host::get_comment(const std::string &uri) { @@ -540,9 +547,9 @@ static bool ports_sort_compare(std::shared_ptr &p1, const std::shar return p1->index() < p2->index(); } -bool Lv2PluginInfo::HasFactoryPresets(Lv2Host*lv2Host, const LilvPlugin*plugin) +bool Lv2PluginInfo::HasFactoryPresets(Lv2Host *lv2Host, const LilvPlugin *plugin) { - NodesAutoFree nodes = lilv_plugin_get_related(plugin,lv2Host->lilvUris.pset_Preset); + NodesAutoFree nodes = lilv_plugin_get_related(plugin, lv2Host->lilvUris.pset_Preset); bool result = false; LILV_FOREACH(nodes, iNode, nodes) { @@ -554,9 +561,9 @@ bool Lv2PluginInfo::HasFactoryPresets(Lv2Host*lv2Host, const LilvPlugin*plugin) Lv2PluginInfo::Lv2PluginInfo(Lv2Host *lv2Host, const LilvPlugin *pPlugin) { - const LilvNode* pluginUri = lilv_plugin_get_uri(pPlugin); - - this->has_factory_presets_ = HasFactoryPresets(lv2Host,pPlugin); + const LilvNode *pluginUri = lilv_plugin_get_uri(pPlugin); + + this->has_factory_presets_ = HasFactoryPresets(lv2Host, pPlugin); this->uri_ = nodeAsString(lilv_plugin_get_uri(pPlugin)); @@ -628,9 +635,7 @@ Lv2PluginInfo::Lv2PluginInfo(Lv2Host *lv2Host, const LilvPlugin *pPlugin) std::sort(ports_.begin(), ports_.end(), ports_sort_compare); - this->is_valid_ = isValid; - } std::vector supportedFeatures = { @@ -780,7 +785,6 @@ Lv2PortInfo::Lv2PortInfo(Lv2Host *host, const LilvPlugin *plugin, const LilvPort NodeAutoFree unitsValueUri = lilv_port_get(plugin, pPort, host->lilvUris.unitsUri); this->units_ = UriToUnits(nodeAsString(unitsValueUri)); - NodeAutoFree bufferType = lilv_port_get(plugin, pPort, host->lilvUris.bufferType_uri); this->buffer_type_ = ""; @@ -929,7 +933,6 @@ std::shared_ptr Lv2Host::GetPluginInfo(const std::string &uri) co return nullptr; } - Lv2PedalBoard *Lv2Host::CreateLv2PedalBoard(const PedalBoard &pedalBoard) { Lv2PedalBoard *pPedalBoard = new Lv2PedalBoard(); @@ -945,19 +948,19 @@ Lv2PedalBoard *Lv2Host::CreateLv2PedalBoard(const PedalBoard &pedalBoard) } } - -struct StateCallbackData { - Lv2Host*pHost; +struct StateCallbackData +{ + Lv2Host *pHost; std::vector *pResult; }; -void Lv2Host::fn_LilvSetPortValueFunc(const char* port_symbol, - void* user_data, - const void* value, - uint32_t size, - uint32_t type) +void Lv2Host::fn_LilvSetPortValueFunc(const char *port_symbol, + void *user_data, + const void *value, + uint32_t size, + uint32_t type) { - StateCallbackData *pData = (StateCallbackData*)user_data; + StateCallbackData *pData = (StateCallbackData *)user_data; Lv2Host *pHost = pData->pHost; std::vector *pResult = pData->pResult; LV2_URID valueType = (LV2_URID)type; @@ -965,35 +968,32 @@ void Lv2Host::fn_LilvSetPortValueFunc(const char* port_symbol, { throw PiPedalStateException("Preset format not supported."); } - pResult->push_back(ControlValue(port_symbol,*(float*)value)); - + pResult->push_back(ControlValue(port_symbol, *(float *)value)); } - - std::vector Lv2Host::LoadFactoryPluginPreset( - PedalBoardItem*pedalBoardItem, const std::string&presetUri) + PedalBoardItem *pedalBoardItem, const std::string &presetUri) { std::vector result; - const LilvPlugins*plugins = lilv_world_get_all_plugins(this->pWorld); + const LilvPlugins *plugins = lilv_world_get_all_plugins(this->pWorld); NodeAutoFree uriNode = lilv_new_uri(pWorld, pedalBoardItem->uri().c_str()); lilv_world_load_resource(pWorld, uriNode); - const LilvPlugin * plugin = lilv_plugins_get_by_uri (plugins, uriNode); + const LilvPlugin *plugin = lilv_plugins_get_by_uri(plugins, uriNode); if (plugin == nullptr) { throw PiPedalStateException("No such plugin."); } - - LilvNodes* presets = lilv_plugin_get_related(plugin, lilvUris.pset_Preset); - LILV_FOREACH(nodes, i, presets) { - const LilvNode* preset = lilv_nodes_get(presets, i); - lilv_world_load_resource(pWorld, preset); + LilvNodes *presets = lilv_plugin_get_related(plugin, lilvUris.pset_Preset); + LILV_FOREACH(nodes, i, presets) + { + const LilvNode *preset = lilv_nodes_get(presets, i); + lilv_world_load_resource(pWorld, preset); std::string thisPresetUri = nodeAsString(preset); if (presetUri == thisPresetUri) @@ -1001,62 +1001,62 @@ std::vector Lv2Host::LoadFactoryPluginPreset( /*********************************/ + // NodeAutoFree uriNode = lilv_new_uri(pWorld, presetUri.c_str()); - // NodeAutoFree uriNode = lilv_new_uri(pWorld, presetUri.c_str()); + auto lilvState = lilv_state_new_from_world(pWorld, GetLv2UridMap(), preset); - auto lilvState = lilv_state_new_from_world(pWorld, GetLv2UridMap(), preset); + if (lilvState == nullptr) + { + throw PiPedalStateException("Preset not found."); + } - if (lilvState == nullptr) - { - throw PiPedalStateException("Preset not found."); - } + StateCallbackData cbData; + cbData.pHost = this; + cbData.pResult = &result; + auto n = lilv_state_get_num_properties(lilvState); + lilv_state_emit_port_values(lilvState, fn_LilvSetPortValueFunc, &cbData); - - StateCallbackData cbData; - cbData.pHost = this; - cbData.pResult = &result; - auto n = lilv_state_get_num_properties(lilvState); - lilv_state_emit_port_values (lilvState, fn_LilvSetPortValueFunc,&cbData); - - lilv_state_free(lilvState); - break; + lilv_state_free(lilvState); + break; /*********************************/ } - - } - lilv_nodes_free(presets); + } + lilv_nodes_free(presets); return result; } -struct PresetCallbackState { +struct PresetCallbackState +{ Lv2Host *pHost; - std::map *values; + std::map *values; bool failed = false; }; -void Lv2Host::PortValueCallback(const char*symbol,void*user_data,const void* value,uint32_t size, uint32_t type) +void Lv2Host::PortValueCallback(const char *symbol, void *user_data, const void *value, uint32_t size, uint32_t type) { - PresetCallbackState *pState = static_cast(user_data); - Lv2Host*pHost = pState->pHost; + PresetCallbackState *pState = static_cast(user_data); + Lv2Host *pHost = pState->pHost; if (type == pHost->urids->atom_Float) { - (*pState->values)[symbol] = *static_cast(value); - } else { + (*pState->values)[symbol] = *static_cast(value); + } + else + { pState->failed = true; } } PluginPresets Lv2Host::GetFactoryPluginPresets(const std::string &pluginUri) { - const LilvPlugins*plugins = lilv_world_get_all_plugins(this->pWorld); + const LilvPlugins *plugins = lilv_world_get_all_plugins(this->pWorld); NodeAutoFree uriNode = lilv_new_uri(pWorld, pluginUri.c_str()); lilv_world_load_resource(pWorld, uriNode); - const LilvPlugin * plugin = lilv_plugins_get_by_uri (plugins, uriNode); + const LilvPlugin *plugin = lilv_plugins_get_by_uri(plugins, uriNode); if (plugin == nullptr) { throw PiPedalStateException("No such plugin."); @@ -1065,37 +1065,37 @@ PluginPresets Lv2Host::GetFactoryPluginPresets(const std::string &pluginUri) PluginPresets result; result.pluginUri_ = pluginUri; - LilvNodes* presets = lilv_plugin_get_related(plugin, lilvUris.pset_Preset); - LILV_FOREACH(nodes, i, presets) { - const LilvNode* preset = lilv_nodes_get(presets, i); - lilv_world_load_resource(pWorld, preset); + LilvNodes *presets = lilv_plugin_get_related(plugin, lilvUris.pset_Preset); + LILV_FOREACH(nodes, i, presets) + { + const LilvNode *preset = lilv_nodes_get(presets, i); + lilv_world_load_resource(pWorld, preset); - LilvState *state = lilv_state_new_from_world(pWorld,this->mapFeature.GetMap(),preset); + LilvState *state = lilv_state_new_from_world(pWorld, this->mapFeature.GetMap(), preset); if (state != nullptr) { std::string label = lilv_state_get_label(state); - std::map controlValues; - PresetCallbackState cbData { this, &controlValues,false}; - lilv_state_emit_port_values(state,PortValueCallback,(void*)&cbData); + std::map controlValues; + PresetCallbackState cbData{this, &controlValues, false}; + lilv_state_emit_port_values(state, PortValueCallback, (void *)&cbData); lilv_state_free(state); if (!cbData.failed) { - result.presets_.push_back(PluginPreset(result.nextInstanceId_++,std::move(label),std::move(controlValues))); + result.presets_.push_back(PluginPreset(result.nextInstanceId_++, std::move(label), std::move(controlValues))); } } - } - lilv_nodes_free(presets); + } + lilv_nodes_free(presets); return result; - } - Lv2Effect *Lv2Host::CreateEffect(const PedalBoardItem &pedalBoardItem) { auto info = this->GetPluginInfo(pedalBoardItem.uri()); - if (!info) return nullptr; + if (!info) + return nullptr; return new Lv2Effect(this, info, pedalBoardItem); } @@ -1112,8 +1112,6 @@ Lv2PortGroup::Lv2PortGroup(Lv2Host *lv2Host, const std::string &groupUri) name_ = nodeAsString(nameNode); } - - #define MAP_REF(class, name) \ json_map::reference(#name, &class ::name##_) @@ -1121,7 +1119,7 @@ Lv2PortGroup::Lv2PortGroup(Lv2Host *lv2Host, const std::string &groupUri) json_map::conditional_reference(#name, &class ::name##_, condition) json_map::storage_type Lv2PluginClasses::jmap{{ - //json_map::reference("char_", &JsonTestTarget::char_), + // json_map::reference("char_", &JsonTestTarget::char_), json_map::reference("classes", &Lv2PluginClasses::classes_), }}; @@ -1246,4 +1244,3 @@ json_map::storage_type Lv2PluginUiPortGroup::jmap{{ MAP_REF(Lv2PluginUiPortGroup, symbol), MAP_REF(Lv2PluginUiPortGroup, name), }}; - diff --git a/src/StdOutCapture.cpp b/src/StdOutCapture.cpp new file mode 100644 index 0000000..c27be3a --- /dev/null +++ b/src/StdOutCapture.cpp @@ -0,0 +1,100 @@ +// Copyright (c) 2022 Robin Davies +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +#include "StdOutCapture.hpp" +#include +#include +#include +#include +#include +#include +#include + +#ifdef __WIN32__ + +#define dup _dup +#define dup2 _dup2 + +#endif + + +using namespace pipedal; + +constexpr int STDOUT_HANDLE = 2; +StdOutCapture::StdOutCapture() +{ + this->tempFileName = std::tmpnam(nullptr); + + int redirectHandle = open(tempFileName.c_str(),O_CREAT | O_WRONLY | O_TRUNC); + this->oldStdout = dup(STDOUT_HANDLE); + dup2(redirectHandle,STDOUT_HANDLE); + close(redirectHandle); + + +} +StdOutCapture::~StdOutCapture() +{ + EndCapture(); + if (tempFileName.length() != 0) + { + remove(tempFileName.c_str()); + } +} + +void StdOutCapture::EndCapture() +{ + + if (this->oldStdout != -1) + { + dup2(this->oldStdout,STDOUT_HANDLE); + close(this->oldStdout); + this->oldStdout = -1; + chmod(this->tempFileName.c_str(),0644); + } +} + +std::vector StdOutCapture::GetOutputLines() { + EndCapture(); + + std::vector result; + + std::ifstream f(this->tempFileName); + + if (f.is_open()) + { + char buffer[1024]; + + std::string line; + while (true) + { + if (f.eof()) + { + break; + } + std::getline(f,line); + if (f.fail()) break; + if (line.length() != 0) + { + result.push_back(line); + } + } + } + return result; +} diff --git a/src/StdOutCapture.hpp b/src/StdOutCapture.hpp new file mode 100644 index 0000000..ed8c5c7 --- /dev/null +++ b/src/StdOutCapture.hpp @@ -0,0 +1,42 @@ +// Copyright (c) 2022 Robin Davies +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +#pragma once + +#include + +namespace pipedal { + +class StdOutCapture { +public: + StdOutCapture(); + ~StdOutCapture(); + + void EndCapture(); + + + std::vector GetOutputLines(); + +private: + std::string tempFileName; + int oldStdout = -1; + +}; + +} // namespace \ No newline at end of file