diff --git a/FlucomaClients.cmake b/FlucomaClients.cmake index 3aaede947..314614d43 100644 --- a/FlucomaClients.cmake +++ b/FlucomaClients.cmake @@ -84,10 +84,12 @@ function(get_core_client_class name var) endfunction() # add_client(AmpFeature clients/rt/AmpFeatureClient.hpp CLASS RTAmpFeatureClient ) +add_client(AmpFeature2 clients/rt/AmpFeature2Client.hpp CLASS RTAmpFeature2Client ) add_client(AmpGate clients/rt/AmpGateClient.hpp CLASS RTAmpGateClient ) add_client(AmpSlice clients/rt/AmpSliceClient.hpp CLASS RTAmpSliceClient ) add_client(AudioTransport clients/rt/AudioTransportClient.hpp CLASS RTAudioTransportClient ) add_client(BufAmpFeature clients/rt/AmpFeatureClient.hpp CLASS NRTThreadedAmpFeatureClient ) +add_client(BufAmpFeature2 clients/rt/AmpFeature2Client.hpp CLASS NRTThreadedAmpFeature2Client ) add_client(BufAmpGate clients/rt/AmpGateClient.hpp CLASS NRTThreadedAmpGateClient ) add_client(BufAmpSlice clients/rt/AmpSliceClient.hpp CLASS NRTThreadedAmpSliceClient ) add_client(BufAudioTransport clients/rt/AudioTransportClient.hpp CLASS NRTThreadedAudioTransportClient ) diff --git a/include/algorithms/public/Envelope.hpp b/include/algorithms/public/Envelope.hpp index c625ef15f..f7dbf9f3c 100644 --- a/include/algorithms/public/Envelope.hpp +++ b/include/algorithms/public/Envelope.hpp @@ -17,6 +17,7 @@ under the European Union’s Horizon 2020 research and innovation programme #include "../../data/TensorTypes.hpp" #include #include +#include namespace fluid { namespace algorithm { @@ -36,7 +37,38 @@ class Envelope mInitialized = true; } - double processSample(const double in, + double processSample(const double in, + double floor, index fastRampUpTime, index slowRampUpTime, + index fastRampDownTime, index slowRampDownTime, + double hiPassFreq) + { + double clipped = processSampleCommon(in, floor, fastRampUpTime, slowRampUpTime, fastRampDownTime, slowRampDownTime, hiPassFreq); + double fast = mFastSlide.processSample(clipped); + double slow = mSlowSlide.processSample(clipped); + return fast - slow; + } + + std::tuple processSampleSeparate(const double in, + double floor, index fastRampUpTime, index slowRampUpTime, + index fastRampDownTime, index slowRampDownTime, + double hiPassFreq) + { + double clipped = processSampleCommon(in, floor, fastRampUpTime, slowRampUpTime, fastRampDownTime, slowRampDownTime, hiPassFreq); + double fast = mFastSlide.processSample(clipped); + double slow = mSlowSlide.processSample(clipped); + return std::make_tuple(fast, slow); + } + + bool initialized() { return mInitialized; } + +private: + void initFilters(double cutoff) + { + mHiPass1.init(cutoff); + mHiPass2.init(cutoff); + } + + double processSampleCommon(const double in, double floor, index fastRampUpTime, index slowRampUpTime, index fastRampDownTime, index slowRampDownTime, double hiPassFreq) @@ -57,18 +89,7 @@ class Envelope double rectified = abs(filtered); double dB = 20 * log10(rectified); double clipped = max(dB, floor); - double fast = mFastSlide.processSample(clipped); - double slow = mSlowSlide.processSample(clipped); - return fast - slow; - } - - bool initialized() { return mInitialized; } - -private: - void initFilters(double cutoff) - { - mHiPass1.init(cutoff); - mHiPass2.init(cutoff); + return clipped; } double mHiPassFreq{0}; diff --git a/include/clients/rt/AmpFeature2Client.hpp b/include/clients/rt/AmpFeature2Client.hpp new file mode 100644 index 000000000..531ab5e39 --- /dev/null +++ b/include/clients/rt/AmpFeature2Client.hpp @@ -0,0 +1,172 @@ +/* +Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/) +Copyright 2017-2019 University of Huddersfield. +Licensed under the BSD-3 License. +See license.md file in the project root for full license information. +This project has received funding from the European Research Council (ERC) +under the European Union’s Horizon 2020 research and innovation programme +(grant agreement No 725899). +*/ +#pragma once + +#include "../common/AudioClient.hpp" +#include "../common/BufferedProcess.hpp" +#include "../common/FluidBaseClient.hpp" +#include "../common/FluidNRTClientWrapper.hpp" +#include "../common/ParameterConstraints.hpp" +#include "../common/ParameterSet.hpp" +#include "../common/ParameterTrackChanges.hpp" +#include "../common/ParameterTypes.hpp" +#include "../../algorithms/public/Envelope.hpp" +#include + +namespace fluid { +namespace client { +namespace ampfeature2 { + +enum AmpFeature2ParamIndex { + kFastRampUpTime, + kFastRampDownTime, + kSlowRampUpTime, + kSlowRampDownTime, + kSilenceThreshold, + kHiPassFreq, +}; + +constexpr auto AmpFeature2Params = defineParameters( + LongParam("fastRampUp", "Fast Envelope Ramp Up Length", 1, Min(1)), + LongParam("fastRampDown", "Fast Envelope Ramp Down Length", 1, Min(1)), + LongParam("slowRampUp", "Slow Envelope Ramp Up Length", 100, Min(1)), + LongParam("slowRampDown", "Slow Envelope Ramp Down Length", 100, Min(1)), + FloatParam("floor", "Floor value (dB)", -145, Min(-144), Max(144)), + FloatParam("highPassFreq", "High-Pass Filter Cutoff", 85, Min(0))); + +class AmpFeature2Client : public FluidBaseClient, public AudioIn, public AudioOut +{ + +public: + using ParamDescType = decltype(AmpFeature2Params); + + using ParamSetViewType = ParameterSetView; + std::reference_wrapper mParams; + + void setParams(ParamSetViewType& p) { mParams = p; } + + template + auto& get() const + { + return mParams.get().template get(); + } + + static constexpr auto& getParameterDescriptors() { return AmpFeature2Params; } + + AmpFeature2Client(ParamSetViewType& p) : mParams(p) + { + audioChannelsIn(1); + audioChannelsOut(2); + FluidBaseClient::setInputLabels({"audio input"}); + FluidBaseClient::setOutputLabels({"fast curve", "slow curve"}); + } + + template + void process(std::vector>& input, + std::vector>& output, FluidContext&) + { + + if (!input[0].data() || (!output[0].data() && !output[1].data())) return; + + double hiPassFreq = std::min(get() / sampleRate(), 0.5); + + if (!mAlgorithm.initialized()) + { + mAlgorithm.init(get(), hiPassFreq); + } + + for (index i = 0; i < input[0].size(); i++) + { + double fast, slow; + std::tie(fast, slow) = mAlgorithm.processSampleSeparate( + input[0](i), get(), get(), + get(), get(), + get(), hiPassFreq); + output[0](i) = static_cast(fast); + output[1](i) = static_cast(slow); + } + } + index latency() { return 0; } + + void reset() + { + double hiPassFreq = std::min(get() / sampleRate(), 0.5); + mAlgorithm.init(get(), hiPassFreq); + } + +private: + algorithm::Envelope mAlgorithm; +}; + +template +struct NRTAmpFeature2 +{ + template + static Result process(Client& client, InputList& inputBuffers, + OutputList& outputBuffers, index nFrames, index nChans, + std::pair userPadding, FluidContext& c) + { + // expecting a single buffer in and a single buffer out + assert(inputBuffers.size() == 1); + assert(outputBuffers.size() == 1); + HostMatrix inputData(nChans, nFrames); + HostMatrix outputData(2 * nChans, nFrames); + + // ignoring userPadding as AmpFeature2 has no padding options and no latency + + double sampleRate{0}; + + for (index i = 0; i < nChans; ++i) + { + BufferAdaptor::ReadAccess thisInput(inputBuffers[0].buffer); + sampleRate = thisInput.sampleRate(); + inputData.row(i)(Slice(0, nFrames)) <<= + thisInput.samps(inputBuffers[0].startFrame, nFrames, + inputBuffers[0].startChan + i); + std::vector input{inputData.row(i)}; + + std::vector outputs { + outputData.row(i*2), + outputData.row(i*2 + 1), + }; + + client.reset(); + client.process(input, outputs, c); + } + + BufferAdaptor::Access thisOutput(outputBuffers[0]); + Result r = thisOutput.resize(nFrames, nChans * 2, sampleRate); + if (!r.ok()) return r; + for (index j = 0; j < nChans * 2; j+=2) { + thisOutput.samps(j) <<= + outputData.row(j)(Slice(0, nFrames)); + thisOutput.samps(j + 1) <<= + outputData.row(j + 1)(Slice(0, nFrames)); + } + + return {}; + } +}; +} // namespace ampfeature2 + +using RTAmpFeature2Client = ClientWrapper; + +auto constexpr NRTAmpFeature2Params = makeNRTParams( + InputBufferParam("source", "Source Buffer"), + BufferParam("features", "Feature Buffer")); + +using NRTAmpFeature2Client = + impl::NRTClientWrapper; + +using NRTThreadedAmpFeature2Client = NRTThreadingAdaptor; + +} // namespace client +} // namespace fluid