Skip to content

Commit

Permalink
Adding -1 octave to PolyOctave (#353)
Browse files Browse the repository at this point in the history
* Adding -1 octave to PolyOctave

* Apply clang-format

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
  • Loading branch information
jatinchowdhury18 and github-actions[bot] committed Mar 3, 2024
1 parent 6b47748 commit 1b173e2
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 1 deletion.
1 change: 1 addition & 0 deletions modules/cmake/WarningFlags.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ if(WIN32)
-Wno-implicit-int-float-conversion
-Wno-implicit-const-int-float-conversion
-Wno-unsafe-buffer-usage
-Wno-unknown-warning-option
-Wno-header-hygiene
)
elseif((CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") OR (CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC"))
Expand Down
89 changes: 89 additions & 0 deletions src/processors/other/poly_octave/DelayPitchShifter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#pragma once

#include <pch.h>

namespace pitch_shift
{
template <typename T>
struct Delay
{
std::vector<T> delay_buffer {};
size_t write_pointer = 0;
T read_pointer {};
size_t N_int = 0;
T N {};
T phase_offset_01 {};
T read_pointer_increment = (T) 1;

void prepare (double sample_rate, T initial_phase_offset_01 = {})
{
N_int = static_cast<size_t> (sample_rate * 0.075);
delay_buffer.resize (2 * N_int, T {});
N = static_cast<T> (N_int);
write_pointer = 0;

phase_offset_01 = initial_phase_offset_01;
read_pointer = N * phase_offset_01;
}

void reset()
{
std::fill (delay_buffer.begin(), delay_buffer.end(), T {});
write_pointer = 0;
read_pointer = N * phase_offset_01;
}

T get_phase_01() const noexcept
{
const auto offset = static_cast<T> (write_pointer);
const auto wrapped_offset = read_pointer >= offset ? offset : offset - N;
return (read_pointer - wrapped_offset) / N;
}

T process_sample (T x) noexcept
{
delay_buffer[write_pointer] = x;
delay_buffer[write_pointer + N_int] = x;

const auto rp_int = static_cast<size_t> (read_pointer);
const auto alpha = read_pointer - static_cast<T> (rp_int);
const auto y = ((T) 1 - alpha) * delay_buffer[rp_int] + alpha * delay_buffer[rp_int + 1];

read_pointer = std::fmod (read_pointer + read_pointer_increment, N);
write_pointer = (write_pointer + 1) % N_int;

const auto amplitude_mod = (T) 0.5 * ((T) 1 - math_approx::cos<5> (juce::MathConstants<T>::twoPi * get_phase_01()));
return y * amplitude_mod;
}
};

template <typename T>
struct Processor
{
Delay<T> d_0 {};
Delay<T> d_half {};

void prepare (double sample_rate)
{
d_0.prepare (sample_rate);
d_half.prepare (sample_rate, (T) 0.5);
}

void reset()
{
d_0.reset();
d_half.reset();
}

void set_pitch_factor (T shift_factor)
{
d_0.read_pointer_increment = shift_factor;
d_half.read_pointer_increment = shift_factor;
}

T process_sample (T x) noexcept
{
return d_0.process_sample (x) + d_half.process_sample (x);
}
};
} // namespace pitch_shift
35 changes: 35 additions & 0 deletions src/processors/other/poly_octave/PolyOctave.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ namespace PolyOctaveTags
const String dryTag = "dry";
const String upOctaveTag = "up_octave";
const String up2OctaveTag = "up2_octave";
const String downOctaveTag = "down_octave";
} // namespace PolyOctaveTags

PolyOctave::PolyOctave (UndoManager* um)
Expand All @@ -28,6 +29,7 @@ PolyOctave::PolyOctave (UndoManager* um)
return 2 * x * x;
};
};
setupGainParam (PolyOctaveTags::downOctaveTag, downOctaveGain);
setupGainParam (PolyOctaveTags::dryTag, dryGain);
setupGainParam (PolyOctaveTags::upOctaveTag, upOctaveGain);
setupGainParam (PolyOctaveTags::up2OctaveTag, up2OctaveGain);
Expand All @@ -43,6 +45,7 @@ ParamLayout PolyOctave::createParameterLayout()
using namespace ParameterHelpers;
auto params = createBaseParams();

createPercentParameter (params, PolyOctaveTags::downOctaveTag, "-1 Oct", 0.0f);
createPercentParameter (params, PolyOctaveTags::dryTag, "+0 Oct", 0.5f);
createPercentParameter (params, PolyOctaveTags::upOctaveTag, "+1 Oct", 0.0f);
createPercentParameter (params, PolyOctaveTags::up2OctaveTag, "+2 Oct", 0.0f);
Expand All @@ -55,14 +58,22 @@ void PolyOctave::prepare (double sampleRate, int samplesPerBlock)
dryGain.prepare (sampleRate, samplesPerBlock);
up2OctaveGain.prepare (sampleRate, samplesPerBlock);
upOctaveGain.prepare (sampleRate, samplesPerBlock);
downOctaveGain.prepare (sampleRate, samplesPerBlock);

doubleBuffer.setMaxSize (2, samplesPerBlock);
up2OctaveBuffer.setMaxSize (2, static_cast<int> (ComplexERBFilterBank::float_2::size) * samplesPerBlock); // allocate extra space for SIMD
upOctaveBuffer.setMaxSize (2, static_cast<int> (ComplexERBFilterBank::float_2::size) * samplesPerBlock); // allocate extra space for SIMD
downOctaveBuffer.setMaxSize (2, samplesPerBlock);

FilterBankHelpers::designFilterBank (octaveUpFilterBank, 2.0, 5.0, 6.0, sampleRate);
FilterBankHelpers::designFilterBank (octaveUp2FilterBank, 3.0, 7.0, 4.0, sampleRate);

for (auto& shifter : downOctavePitchShifters)
{
shifter.prepare (sampleRate);
shifter.set_pitch_factor (0.5);
}

for (auto& busDCBlocker : dcBlocker)
{
busDCBlocker.prepare (2);
Expand All @@ -72,6 +83,7 @@ void PolyOctave::prepare (double sampleRate, int samplesPerBlock)
mixOutBuffer.setSize (2, samplesPerBlock);
up1OutBuffer.setSize (2, samplesPerBlock);
up2OutBuffer.setSize (2, samplesPerBlock);
down1OutBuffer.setSize (2, samplesPerBlock);
}

void PolyOctave::processAudio (AudioBuffer<float>& buffer)
Expand All @@ -82,9 +94,18 @@ void PolyOctave::processAudio (AudioBuffer<float>& buffer)
doubleBuffer.setCurrentSize (numChannels, numSamples);
upOctaveBuffer.setCurrentSize (numChannels, numSamples);
up2OctaveBuffer.setCurrentSize (numChannels, numSamples);
downOctaveBuffer.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 [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)
{
Expand Down Expand Up @@ -165,26 +186,35 @@ void PolyOctave::processAudio (AudioBuffer<float>& buffer)
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);

mixOutBuffer.setSize (numChannels, numSamples, false, false, true);
up1OutBuffer.setSize (numChannels, numSamples, false, false, true);
up2OutBuffer.setSize (numChannels, numSamples, false, false, true);
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);

dcBlocker[MixOutput].processBlock (mixOutBuffer);
dcBlocker[Up1Output].processBlock (up1OutBuffer);
dcBlocker[Up2Output].processBlock (up2OutBuffer);
dcBlocker[Down1Output].processBlock (down1OutBuffer);

outputBuffers.getReference (MixOutput) = &mixOutBuffer;
outputBuffers.getReference (Up1Output) = &up1OutBuffer;
outputBuffers.getReference (Up2Output) = &up2OutBuffer;
outputBuffers.getReference (Down1Output) = &down1OutBuffer;
}

void PolyOctave::processAudioBypassed (AudioBuffer<float>& buffer)
Expand All @@ -194,14 +224,17 @@ void PolyOctave::processAudioBypassed (AudioBuffer<float>& buffer)
mixOutBuffer.setSize (buffer.getNumChannels(), numSamples, false, false, true);
up1OutBuffer.setSize (1, numSamples, false, false, true);
up2OutBuffer.setSize (1, numSamples, false, false, true);
down1OutBuffer.setSize (1, numSamples, false, false, true);

chowdsp::BufferMath::copyBufferData (buffer, mixOutBuffer);
up1OutBuffer.clear();
up2OutBuffer.clear();
down1OutBuffer.clear();

outputBuffers.getReference (MixOutput) = &mixOutBuffer;
outputBuffers.getReference (Up1Output) = &up1OutBuffer;
outputBuffers.getReference (Up2Output) = &up2OutBuffer;
outputBuffers.getReference (Down1Output) = &down1OutBuffer;
}

String PolyOctave::getTooltipForPort (int portIndex, bool isInput)
Expand All @@ -216,6 +249,8 @@ String PolyOctave::getTooltipForPort (int portIndex, bool isInput)
return "+1 Octave Output";
case OutputPort::Up2Output:
return "+2 Octave Output";
case OutputPort::Down1Output:
return "-1 Octave Output";
}
}

Expand Down
8 changes: 7 additions & 1 deletion src/processors/other/poly_octave/PolyOctave.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once

#include "DelayPitchShifter.h"
#include "processors/BaseProcessor.h"

class PolyOctave : public BaseProcessor
Expand All @@ -26,8 +27,9 @@ class PolyOctave : public BaseProcessor
enum OutputPort
{
MixOutput,
Up1Output,
Up2Output,
Up1Output,
Down1Output,
};

static constexpr auto numOutputs = (int) magic_enum::enum_count<OutputPort>();
Expand All @@ -36,19 +38,23 @@ class PolyOctave : public BaseProcessor
chowdsp::SmoothedBufferValue<double> dryGain {};
chowdsp::SmoothedBufferValue<double> upOctaveGain {};
chowdsp::SmoothedBufferValue<double> up2OctaveGain {};
chowdsp::SmoothedBufferValue<double> downOctaveGain {};

chowdsp::Buffer<double> doubleBuffer;
chowdsp::Buffer<double> upOctaveBuffer;
chowdsp::Buffer<double> up2OctaveBuffer;
chowdsp::Buffer<double> downOctaveBuffer;

std::array<ComplexERBFilterBank, 2> octaveUpFilterBank;
std::array<ComplexERBFilterBank, 2> octaveUp2FilterBank;
std::array<pitch_shift::Processor<double>, 2> downOctavePitchShifters;

std::array<chowdsp::FirstOrderHPF<float>, (size_t) numOutputs> dcBlocker;

juce::AudioBuffer<float> mixOutBuffer;
juce::AudioBuffer<float> up1OutBuffer;
juce::AudioBuffer<float> up2OutBuffer;
juce::AudioBuffer<float> down1OutBuffer;

JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PolyOctave)
};

0 comments on commit 1b173e2

Please sign in to comment.