diff --git a/Assets/web/src/App.js b/Assets/web/src/App.js
index d0ae111..33b9b8e 100644
--- a/Assets/web/src/App.js
+++ b/Assets/web/src/App.js
@@ -55,6 +55,8 @@ function App() {
+
+
diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp
index 4f3de5e..b58a534 100644
--- a/Source/PluginProcessor.cpp
+++ b/Source/PluginProcessor.cpp
@@ -68,6 +68,8 @@ void WebViewPluginAudioProcessor::processBlock(juce::AudioBuffer& buffer,
shifter.SetPortamentoTime(state.getParameterAsValue("portTime").getValue());
shifter.SetHarmonyMix(state.getParameterAsValue("harmonyMix").getValue());
shifter.SetAutoTuneEnable(state.getParameterAsValue("autoTuneEnabled").getValue());
+ shifter.SetFreeze(state.getParameterAsValue("freezeEnabled").getValue());
+
juce::AudioBuffer const_buff;
const_buff.makeCopyOf(buffer);
shifter.Process(const_buff.getArrayOfReadPointers(), (float**)buffer.getArrayOfWritePointers(), buffer.getNumSamples());
diff --git a/Source/PluginProcessor.h b/Source/PluginProcessor.h
index 6031e52..0bf3bb7 100644
--- a/Source/PluginProcessor.h
+++ b/Source/PluginProcessor.h
@@ -93,6 +93,12 @@ public:
ParameterID("autoTuneEnabled"),
"AutoTune Enabled",
false);
+
+ toggleIds.push_back("freezeEnabled");
+ addToLayout(layout,
+ ParameterID("freezeEnabled"),
+ "Freeze Enabled",
+ false);
}
diff --git a/Source/Shifter.cpp b/Source/Shifter.cpp
index d59c6c0..3faf871 100644
--- a/Source/Shifter.cpp
+++ b/Source/Shifter.cpp
@@ -35,6 +35,7 @@ void Shifter::Init(float samplerate, int samplesPerBlock)
for (int i = 0; i < MAX_VOICES; ++i)
{
voices[i].Init(samplerate);
+ freeze_voices[i].Init(samplerate);
}
for (int i = 0; i < BUFFER_SIZE; ++i)
{
@@ -127,6 +128,11 @@ float Shifter::GetOutputEnvelopePeriod(int out_voice) {
return in_period * formant_preserve + voices[out_voice].CurrentPeriod() * (1.0 - formant_preserve);
}
+float Shifter::GetOutputEnvelopePeriodFreeze(int freeze_voice) {
+ //TODO add something so that low pitch ratios end up reducing formant_preservation
+ return freeze_period * formant_preserve + freeze_voices[freeze_voice].CurrentPeriod() * (1.0 - formant_preserve);
+}
+
int Shifter::GetPeakIndex() {
int index = in_playhead - in_period * 2;
if (index < 0)
@@ -152,39 +158,6 @@ int Shifter::GetPeakIndex() {
return max_index;
}
-//void Shifter::AddInterpolatedFrame(int voice, int max_index, float resampling_period) {
-// float period_ratio = resampling_period / out_periods[voice];
-// float f_index;
-// f_index = max_index - resampling_period;
-// if (f_index < 0)
-// {
-// f_index += BUFFER_SIZE;
-// }
-// float mult = 0;
-// int out_index = out_playhead;
-// for (int j = 0; j < resampling_period * 2; ++j)
-// {
-// // mult = .5
-// // * (1 - cosf(2 * PI_F * j / (period_to_use * 2 - 1)));
-// float interp = f_index - (int)f_index;
-// mult = .5
-// * (1 - cos_lookup[(int)((float)j / (resampling_period * 2.0) * 8191.0)]);
-// float value = ((1 - interp) * in_buffer[(int)f_index] + (interp)*in_buffer[(int)(f_index + 1) % 8192]) * mult;
-// out_buffer[0][out_index] += value * (1 - out_panning[voice]);
-// out_buffer[1][out_index] += value * out_panning[voice];
-//
-//
-// f_index += period_ratio;
-// if (f_index >= BUFFER_SIZE)
-// {
-// f_index -= BUFFER_SIZE;
-// }
-// if (++out_index >= BUFFER_SIZE)
-// {
-// out_index -= BUFFER_SIZE;
-// }
-// }
-//}
void Shifter::AddInterpolatedFrame(int voice, int max_index, float resampling_period) {
float period_ratio = in_period / resampling_period;
@@ -230,6 +203,22 @@ void Shifter::AddInterpolatedFrame(int voice, int max_index, float resampling_pe
void Shifter::GetSamples(float** output, const float* input, size_t size)
{
+ if (freeze_needs_copy) {
+ freeze_needs_copy = false;
+ //copy current buffer to freeze buffer
+ int index = GetPeakIndex();
+ memset(freeze_buffer, 0, sizeof(freeze_buffer));
+ CopyInputToFreezeBuffer(index);
+ //init freeze voices
+ for (int i = 0; i < MAX_VOICES; ++i) {
+ //freeze_voices[i].Init(sample_rate_);
+ if (voices[i].IsActive()) {
+ freeze_voices[i].Trigger(voices[i].GetMidiNote());
+ freeze_voices[i].panning = voices[i].panning;
+ freeze_voices[i].SetPortamentoTime(0.0001f);
+ }
+ }
+ }
for (int i = 0; i < size; ++i)
{
@@ -251,6 +240,20 @@ void Shifter::GetSamples(float** output, const float* input, size_t size)
}
}
+ //add freeze samples if necessary
+ for (int out_p = 0; out_p < MAX_VOICES; ++out_p)
+ {
+ freeze_voices[out_p].Process();
+ if (!freeze_voices[out_p].IsActive()) continue;
+ if (freeze_voices[out_p].PeriodOverflow())
+ {
+ float resampling_period = GetOutputEnvelopePeriodFreeze(out_p);
+
+ //add samples centered on that max
+ AddFreezeToOutput(out_p, resampling_period);
+ }
+ }
+
if (out_period_counter > out_period)
{
out_period_counter -= out_period;
@@ -328,4 +331,75 @@ void Shifter::SetHarmonyMix(float mix) {
harmony_mix = 1.0f;
melody_mix = (1.0f - mix) * 2.0f;
}
+}
+
+void Shifter::CopyInputToFreezeBuffer(int max_index) {
+ freeze_period = in_period;
+ float period_ratio = 1;
+ float f_index;
+ f_index = max_index - in_period;
+ if (f_index < 0)
+ {
+ f_index += BUFFER_SIZE;
+ }
+ float mult = 0;
+ for (int j = 0; j < in_period * 2; ++j)
+ {
+ mult = .5 * (1 - cos_lookup[(int)((float)j / (in_period * 2.0) * 8191.0)]);
+ float value = in_buffer[(int)f_index] * mult;
+ freeze_buffer[j] = value;
+ f_index += 1;
+ if (f_index >= BUFFER_SIZE)
+ {
+ f_index -= BUFFER_SIZE;
+ }
+ }
+}
+
+void Shifter::AddFreezeToOutput(int voice, float resampling_period) {
+ float period_ratio = freeze_period / resampling_period;
+ float f_index;
+ f_index = 0;
+ if (f_index < 0)
+ {
+ f_index += BUFFER_SIZE;
+ }
+ float mult = 0;
+ int out_index = out_playhead;
+ for (int j = 0; j < resampling_period * 2; ++j)
+ {
+ // mult = .5
+ // * (1 - cosf(2 * PI_F * j / (period_to_use * 2 - 1)));
+ float interp = f_index - (int)f_index;
+ //mult = .5 * (1 - cos_lookup[(int)((float)j / (resampling_period * 2.0) * 8191.0)]);
+ float value = ((1 - interp) * freeze_buffer[(int)f_index] + (interp)*freeze_buffer[(int)(f_index + 1) % 8192]);
+ value *= freeze_voices[voice].CurrentAmplitude();
+ out_buffer[0][out_index] += value * freeze_voices[voice].GetPanning(0) * freeze_volume;
+ out_buffer[1][out_index] += value * freeze_voices[voice].GetPanning(1) * freeze_volume;
+
+
+ f_index += period_ratio;
+ if (f_index >= BUFFER_SIZE)
+ {
+ f_index -= BUFFER_SIZE;
+ }
+ if (++out_index >= BUFFER_SIZE)
+ {
+ out_index -= BUFFER_SIZE;
+ }
+ }
+}
+
+void Shifter::SetFreeze(bool freeze) {
+ if (!freeze_mode && freeze) {
+ freeze_needs_copy = true;
+
+ }
+ else if (freeze_mode && !freeze) {
+ //release freeze voices
+ for (int i = 0; i < MAX_VOICES; ++i) {
+ freeze_voices[i].Release();
+ }
+ }
+ freeze_mode = freeze;
}
\ No newline at end of file
diff --git a/Source/Shifter.h b/Source/Shifter.h
index 54fdee1..e3e65ea 100644
--- a/Source/Shifter.h
+++ b/Source/Shifter.h
@@ -158,17 +158,22 @@ public:
float getOutputPitch() const { return sample_rate_ / out_period; }
void SetHarmonyMix(float mix);
void SetAutoTuneEnable(bool enable) { enable_autotune = enable; }
+ void SetFreeze(bool);
float out_midi = 40;
ShifterVoice voices[MAX_VOICES];
+ ShifterVoice freeze_voices[MAX_VOICES];
private:
void DetectPitch(const float* const* in, float** out, size_t size);
void SetRates();
void GetSamples(float** output, const float* input, size_t size);
float GetOutputEnvelopePeriod(int out_voice);
+ float GetOutputEnvelopePeriodFreeze(int freeze_voice);
int GetPeakIndex();
void AddInterpolatedFrame(int voice, int max_index, float period_to_use);
+ void AddFreezeToOutput(int voice, float resampling_period);
+ void CopyInputToFreezeBuffer(int);
Helmholtz helm;
// GranularSustain player;
@@ -189,6 +194,7 @@ private:
bool pitch_trigger = false;
float harmony_mix = 0.0f;
float melody_mix = 0.0f;
+ bool freeze_needs_copy;
int trigger_bank;
@@ -206,13 +212,14 @@ private:
float last_freqs[3];
float in_buffer[BUFFER_SIZE];
float out_buffer[2][BUFFER_SIZE];
+ float freeze_buffer[BUFFER_SIZE];
int out_playhead = 0;
int in_playhead = 0;
int last_autotune_midi = -1;
float out_period_filter_amount = 0.7f; // You can expose this as a parameter
-
+ bool freeze_mode = false;
float out_period = 0; //C3
float in_period = 366.936;
@@ -221,6 +228,8 @@ private:
float sample_rate_;
int blocksize;
bool enable_autotune = false;
+ float freeze_period;
+ float freeze_volume = 1;
MidiPitchSmoother out_midi_smoother;
};
#endif
\ No newline at end of file
diff --git a/Source/shifter_voice.h b/Source/shifter_voice.h
index 373fa46..6e07baf 100644
--- a/Source/shifter_voice.h
+++ b/Source/shifter_voice.h
@@ -34,6 +34,8 @@ public:
int GetMidiNote() const { return current_midi; }
bool onoff_;
+ float panning;
+
private:
Port portamento_;
Adsr amplitude_envelope_;
@@ -44,5 +46,5 @@ private:
float current_period_;
float current_amplitude;
float period_counter;
- float panning;
+
};
\ No newline at end of file