diff --git a/src/processors/other/poly_octave/PolyOctave.cpp b/src/processors/other/poly_octave/PolyOctave.cpp index 99c9ab11..33fc0f31 100644 --- a/src/processors/other/poly_octave/PolyOctave.cpp +++ b/src/processors/other/poly_octave/PolyOctave.cpp @@ -8,6 +8,7 @@ const String dryTag = "dry"; const String upOctaveTag = "up_octave"; const String up2OctaveTag = "up2_octave"; const String downOctaveTag = "down_octave"; +const String v1Tag = "v1_mode"; } // namespace PolyOctaveTags PolyOctave::PolyOctave (UndoManager* um) @@ -34,6 +35,9 @@ PolyOctave::PolyOctave (UndoManager* um) setupGainParam (PolyOctaveTags::upOctaveTag, upOctaveGain); setupGainParam (PolyOctaveTags::up2OctaveTag, up2OctaveGain); + ParameterHelpers::loadParameterPointer (v1_mode, vts, PolyOctaveTags::v1Tag); + addPopupMenuParameter (PolyOctaveTags::v1Tag); + uiOptions.backgroundColour = Colour { 0xff9fa09d }; uiOptions.powerColour = Colour { 0xffe70510 }; uiOptions.info.description = "A \"polyphonic octave generator\" effect."; @@ -49,6 +53,7 @@ ParamLayout PolyOctave::createParameterLayout() createPercentParameter (params, PolyOctaveTags::dryTag, "+0 Oct", 0.5f); createPercentParameter (params, PolyOctaveTags::upOctaveTag, "+1 Oct", 0.0f); createPercentParameter (params, PolyOctaveTags::up2OctaveTag, "+2 Oct", 0.0f); + emplace_param (params, PolyOctaveTags::v1Tag, "V1 Mode", false); // @TODO: version streaming return { params.begin(), params.end() }; } @@ -61,12 +66,12 @@ void PolyOctave::prepare (double sampleRate, int samplesPerBlock) downOctaveGain.prepare (sampleRate, samplesPerBlock); doubleBuffer.setMaxSize (2, samplesPerBlock); - up2OctaveBuffer.setMaxSize (2, static_cast (ComplexERBFilterBank::float_2::size) * samplesPerBlock); // allocate extra space for SIMD - upOctaveBuffer.setMaxSize (2, static_cast (ComplexERBFilterBank::float_2::size) * samplesPerBlock); // allocate extra space for SIMD - downOctaveBuffer.setMaxSize (2, samplesPerBlock); + up2OctaveBuffer_double.setMaxSize (2, 2 * samplesPerBlock); // allocate extra space for SIMD + upOctaveBuffer_double.setMaxSize (2, 2 * samplesPerBlock); // allocate extra space for SIMD + downOctaveBuffer_double.setMaxSize (2, samplesPerBlock); - FilterBankHelpers::designFilterBank (octaveUpFilterBank, 2.0, 5.0, 6.0, sampleRate); - FilterBankHelpers::designFilterBank (octaveUp2FilterBank, 3.0, 7.0, 4.0, sampleRate); + poly_octave_v1::designFilterBank (octaveUpFilterBank, 2.0, 5.0, 6.0, sampleRate); + poly_octave_v1::designFilterBank (octaveUp2FilterBank, 3.0, 7.0, 4.0, sampleRate); for (auto& shifter : downOctavePitchShifters) { @@ -87,37 +92,48 @@ void PolyOctave::prepare (double sampleRate, int samplesPerBlock) } void PolyOctave::processAudio (AudioBuffer& buffer) +{ + if (v1_mode->get()) + { + processAudioV1 (buffer); + return; + } + + +} + +void PolyOctave::processAudioV1 (AudioBuffer& buffer) { const auto numChannels = buffer.getNumChannels(); const auto numSamples = buffer.getNumSamples(); doubleBuffer.setCurrentSize (numChannels, numSamples); - upOctaveBuffer.setCurrentSize (numChannels, numSamples); - up2OctaveBuffer.setCurrentSize (numChannels, numSamples); - downOctaveBuffer.setCurrentSize (numChannels, numSamples); + upOctaveBuffer_double.setCurrentSize (numChannels, numSamples); + up2OctaveBuffer_double.setCurrentSize (numChannels, numSamples); + downOctaveBuffer_double.setCurrentSize (numChannels, numSamples); chowdsp::BufferMath::copyBufferData (buffer, doubleBuffer); // "down" processing - for (auto [ch, data_in, data_out] : chowdsp::buffer_iters::zip_channels (std::as_const (doubleBuffer), downOctaveBuffer)) + for (auto [ch, data_in, data_out] : chowdsp::buffer_iters::zip_channels (std::as_const (doubleBuffer), downOctaveBuffer_double)) { for (auto [x, y] : chowdsp::zip (data_in, data_out)) y = downOctavePitchShifters[(size_t) ch].process_sample (x); } // "up" processing - using float_2 = ComplexERBFilterBank::float_2; for (int ch = 0; ch < numChannels; ++ch) { + using poly_octave_v1::float_2; auto* dryData = doubleBuffer.getReadPointer (ch); - auto* upData = upOctaveBuffer.getWritePointer (ch); - auto* upDataSIMD = reinterpret_cast (upOctaveBuffer.getWritePointer (ch)); + auto* upData = upOctaveBuffer_double.getWritePointer (ch); + auto* upDataSIMD = reinterpret_cast (upOctaveBuffer_double.getWritePointer (ch)); jassert (juce::snapPointerToAlignment (upDataSIMD, xsimd::default_arch::alignment()) == upDataSIMD); std::fill (upDataSIMD, upDataSIMD + numSamples, float_2 {}); - auto* up2Data = up2OctaveBuffer.getWritePointer (ch); - auto* up2DataSIMD = reinterpret_cast (up2OctaveBuffer.getWritePointer (ch)); + auto* up2Data = up2OctaveBuffer_double.getWritePointer (ch); + auto* up2DataSIMD = reinterpret_cast (up2OctaveBuffer_double.getWritePointer (ch)); jassert (juce::snapPointerToAlignment (up2DataSIMD, xsimd::default_arch::alignment()) == up2DataSIMD); std::fill (up2DataSIMD, up2DataSIMD + numSamples, float_2 {}); @@ -125,7 +141,7 @@ void PolyOctave::processAudio (AudioBuffer& buffer) auto& up2FilterBank = octaveUp2FilterBank[static_cast (ch)]; static constexpr auto eps = std::numeric_limits::epsilon(); - for (size_t k = 0; k < ComplexERBFilterBank::numFilterBands; k += float_2::size) + for (size_t k = 0; k < poly_octave_v1::ComplexERBFilterBank::numFilterBands; k += float_2::size) { const auto filter_idx = k / float_2::size; auto& realFilter = upFilterBank.erbFilterReal[filter_idx]; @@ -134,8 +150,8 @@ void PolyOctave::processAudio (AudioBuffer& buffer) chowdsp::ScopedValue z_im { imagFilter.z[0] }; for (int n = 0; n < numSamples; ++n) { - auto x_re = FilterBankHelpers::processSample (realFilter, z_re.get(), dryData[n]); - auto x_im = FilterBankHelpers::processSample (imagFilter, z_im.get(), dryData[n]); + auto x_re = poly_octave_v1::processSample (realFilter, z_re.get(), dryData[n]); + auto x_im = poly_octave_v1::processSample (imagFilter, z_im.get(), dryData[n]); auto x_re_sq = x_re * x_re; auto x_im_sq = x_im * x_im; @@ -153,7 +169,7 @@ void PolyOctave::processAudio (AudioBuffer& buffer) for (int n = 0; n < numSamples; ++n) upData[n] = xsimd::reduce_add (upDataSIMD[n]); - for (size_t k = 0; k < ComplexERBFilterBank::numFilterBands; k += float_2::size) + for (size_t k = 0; k < poly_octave_v1::ComplexERBFilterBank::numFilterBands; k += float_2::size) { const auto filter_idx = k / float_2::size; auto& realFilter = up2FilterBank.erbFilterReal[filter_idx]; @@ -163,8 +179,8 @@ void PolyOctave::processAudio (AudioBuffer& buffer) for (int n = 0; n < numSamples; ++n) { - auto x_re = FilterBankHelpers::processSample (realFilter, z_re.get(), dryData[n]); - auto x_im = FilterBankHelpers::processSample (imagFilter, z_im.get(), dryData[n]); + auto x_re = poly_octave_v1::processSample (realFilter, z_re.get(), dryData[n]); + auto x_im = poly_octave_v1::processSample (imagFilter, z_im.get(), dryData[n]); auto x_re_sq = x_re * x_re; auto x_im_sq = x_im * x_im; @@ -178,23 +194,9 @@ void PolyOctave::processAudio (AudioBuffer& buffer) up2Data[n] = xsimd::reduce_add (up2DataSIMD[n]); } - chowdsp::BufferMath::applyGain (upOctaveBuffer, 2.0 / static_cast (ComplexERBFilterBank::numFilterBands)); - upOctaveGain.process (numSamples); - chowdsp::BufferMath::applyGainSmoothedBuffer (upOctaveBuffer, upOctaveGain); - - chowdsp::BufferMath::applyGain (up2OctaveBuffer, 2.0 / static_cast (ComplexERBFilterBank::numFilterBands)); - up2OctaveGain.process (numSamples); - chowdsp::BufferMath::applyGainSmoothedBuffer (up2OctaveBuffer, up2OctaveGain); - - chowdsp::BufferMath::applyGain (downOctaveBuffer, juce::Decibels::decibelsToGain (3.0f)); - downOctaveGain.process (numSamples); - chowdsp::BufferMath::applyGainSmoothedBuffer (downOctaveBuffer, downOctaveGain); - - dryGain.process (numSamples); - chowdsp::BufferMath::applyGainSmoothedBuffer (doubleBuffer, dryGain); - chowdsp::BufferMath::addBufferData (up2OctaveBuffer, doubleBuffer); - chowdsp::BufferMath::addBufferData (upOctaveBuffer, doubleBuffer); - chowdsp::BufferMath::addBufferData (downOctaveBuffer, doubleBuffer); + chowdsp::BufferMath::applyGain (upOctaveBuffer_double, 2.0 / static_cast (poly_octave_v1::ComplexERBFilterBank::numFilterBands)); + chowdsp::BufferMath::applyGain (up2OctaveBuffer_double, 2.0 / static_cast (poly_octave_v1::ComplexERBFilterBank::numFilterBands)); + chowdsp::BufferMath::applyGain (downOctaveBuffer_double, juce::Decibels::decibelsToGain (3.0f)); mixOutBuffer.setSize (numChannels, numSamples, false, false, true); up1OutBuffer.setSize (numChannels, numSamples, false, false, true); @@ -202,9 +204,22 @@ void PolyOctave::processAudio (AudioBuffer& buffer) down1OutBuffer.setSize (numChannels, numSamples, false, false, true); chowdsp::BufferMath::copyBufferData (doubleBuffer, mixOutBuffer); - chowdsp::BufferMath::copyBufferData (upOctaveBuffer, up1OutBuffer); - chowdsp::BufferMath::copyBufferData (up2OctaveBuffer, up2OutBuffer); - chowdsp::BufferMath::copyBufferData (downOctaveBuffer, down1OutBuffer); + chowdsp::BufferMath::copyBufferData (upOctaveBuffer_double, up1OutBuffer); + chowdsp::BufferMath::copyBufferData (up2OctaveBuffer_double, up2OutBuffer); + chowdsp::BufferMath::copyBufferData (downOctaveBuffer_double, down1OutBuffer); + + upOctaveGain.process (numSamples); + chowdsp::BufferMath::applyGainSmoothedBuffer (up1OutBuffer, upOctaveGain); + up2OctaveGain.process (numSamples); + chowdsp::BufferMath::applyGainSmoothedBuffer (up2OutBuffer, up2OctaveGain); + downOctaveGain.process (numSamples); + chowdsp::BufferMath::applyGainSmoothedBuffer (down1OutBuffer, downOctaveGain); + + dryGain.process (numSamples); + chowdsp::BufferMath::applyGainSmoothedBuffer (mixOutBuffer, dryGain); + chowdsp::BufferMath::addBufferData (up1OutBuffer, mixOutBuffer); + chowdsp::BufferMath::addBufferData (up2OutBuffer, mixOutBuffer); + chowdsp::BufferMath::addBufferData (down1OutBuffer, mixOutBuffer); dcBlocker[MixOutput].processBlock (mixOutBuffer); dcBlocker[Up1Output].processBlock (up1OutBuffer); @@ -217,6 +232,7 @@ void PolyOctave::processAudio (AudioBuffer& buffer) outputBuffers.getReference (Down1Output) = &down1OutBuffer; } + void PolyOctave::processAudioBypassed (AudioBuffer& buffer) { const auto numSamples = buffer.getNumSamples(); @@ -256,3 +272,19 @@ String PolyOctave::getTooltipForPort (int portIndex, bool isInput) return BaseProcessor::getTooltipForPort (portIndex, isInput); } + +void PolyOctave::fromXML (XmlElement* xml, const chowdsp::Version& version, bool loadPosition) +{ + BaseProcessor::fromXML (xml, version, loadPosition); + + using namespace std::string_view_literals; +#if JUCE_IOS + if (version <= chowdsp::Version { "2.1.0"sv }) +#else + if (version <= chowdsp::Version { "1.3.0"sv }) +#endif + { + // For versions before 1.3.0, default to v1 mode + v1_mode->setValueNotifyingHost (1.0f); + } +} diff --git a/src/processors/other/poly_octave/PolyOctave.h b/src/processors/other/poly_octave/PolyOctave.h index 08ebf96c..00ff96f6 100644 --- a/src/processors/other/poly_octave/PolyOctave.h +++ b/src/processors/other/poly_octave/PolyOctave.h @@ -2,6 +2,7 @@ #include "DelayPitchShifter.h" #include "processors/BaseProcessor.h" +#include "PolyOctaveFilterBankTypes.h" class PolyOctave : public BaseProcessor { @@ -15,14 +16,9 @@ class PolyOctave : public BaseProcessor void processAudio (AudioBuffer& buffer) override; void processAudioBypassed (AudioBuffer& buffer) override; - String getTooltipForPort (int portIndex, bool isInput) override; + void fromXML (XmlElement* xml, const chowdsp::Version& version, bool loadPosition) override; - struct ComplexERBFilterBank - { - static constexpr size_t numFilterBands = 44; - using float_2 = xsimd::batch; - std::array, numFilterBands / float_2::size> erbFilterReal, erbFilterImag; - }; + String getTooltipForPort (int portIndex, bool isInput) override; enum OutputPort { @@ -35,18 +31,22 @@ class PolyOctave : public BaseProcessor static constexpr auto numOutputs = (int) magic_enum::enum_count(); private: - chowdsp::SmoothedBufferValue dryGain {}; - chowdsp::SmoothedBufferValue upOctaveGain {}; - chowdsp::SmoothedBufferValue up2OctaveGain {}; - chowdsp::SmoothedBufferValue downOctaveGain {}; + void processAudioV1 (AudioBuffer& buffer); - chowdsp::Buffer doubleBuffer; - chowdsp::Buffer upOctaveBuffer; - chowdsp::Buffer up2OctaveBuffer; - chowdsp::Buffer downOctaveBuffer; + chowdsp::BoolParameter* v1_mode = nullptr; + + chowdsp::SmoothedBufferValue dryGain {}; + chowdsp::SmoothedBufferValue upOctaveGain {}; + chowdsp::SmoothedBufferValue up2OctaveGain {}; + chowdsp::SmoothedBufferValue downOctaveGain {}; - std::array octaveUpFilterBank; - std::array octaveUp2FilterBank; + // V1 processing stuff... + chowdsp::Buffer doubleBuffer; + chowdsp::Buffer upOctaveBuffer_double; + chowdsp::Buffer up2OctaveBuffer_double; + chowdsp::Buffer downOctaveBuffer_double; + std::array octaveUpFilterBank; + std::array octaveUp2FilterBank; std::array, 2> downOctavePitchShifters; std::array, (size_t) numOutputs> dcBlocker; diff --git a/src/processors/other/poly_octave/PolyOctaveFilterBandHelpers.h b/src/processors/other/poly_octave/PolyOctaveFilterBandHelpers.h index 02d02a36..0392a767 100644 --- a/src/processors/other/poly_octave/PolyOctaveFilterBandHelpers.h +++ b/src/processors/other/poly_octave/PolyOctaveFilterBandHelpers.h @@ -1,13 +1,12 @@ #pragma once -#include "PolyOctave.h" +#include "PolyOctaveFilterBankTypes.h" -namespace FilterBankHelpers +namespace poly_octave_v1 { // Reference for filter-bank design and octave shifting: // https://aaltodoc.aalto.fi/server/api/core/bitstreams/ff9e52cf-fd79-45eb-b695-93038244ec0e/content -using float_2 = PolyOctave::ComplexERBFilterBank::float_2; static_assert (float_2::size == 2); static_assert (xsimd::batch::size == 2); static constexpr auto vec_size = float_2::size; @@ -89,13 +88,13 @@ static auto designERBFilter (size_t erb_index, return center_target_freq; } -static void designFilterBank (std::array& filterBank, +static void designFilterBank (std::array& filterBank, double gamma, double erb_start, double q_ERB, double sampleRate) { - for (size_t kiter = 0; kiter < PolyOctave::ComplexERBFilterBank::numFilterBands; kiter += vec_size) + for (size_t kiter = 0; kiter < ComplexERBFilterBank::numFilterBands; kiter += vec_size) { alignas (16) std::array b_coeffs_cplx_real_simd[5]; alignas (16) std::array b_coeffs_cplx_imag_simd[5]; @@ -160,4 +159,9 @@ static void designFilterBank (std::array& f } } } + +void process_filter_bank() +{ + +} } // namespace FilterBankHelpers diff --git a/src/processors/other/poly_octave/PolyOctaveFilterBankTypes.h b/src/processors/other/poly_octave/PolyOctaveFilterBankTypes.h new file mode 100644 index 00000000..c203b071 --- /dev/null +++ b/src/processors/other/poly_octave/PolyOctaveFilterBankTypes.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace poly_octave_v1 +{ +using float_2 = xsimd::batch; +struct ComplexERBFilterBank +{ + static constexpr size_t numFilterBands = 44; + std::array, numFilterBands / float_2::size> erbFilterReal, erbFilterImag; +}; +}