Compare commits
2 Commits
fe6ee5e51e
...
e89620df27
| Author | SHA1 | Date | |
|---|---|---|---|
| e89620df27 | |||
| f4a0b995ba |
@ -55,6 +55,10 @@ function App() {
|
|||||||
<JuceSlider identifier="autoTuneSpeed" title="Auto Tune Speed" />
|
<JuceSlider identifier="autoTuneSpeed" title="Auto Tune Speed" />
|
||||||
<JuceSlider identifier="autoTuneDepth" title="Auto Tune Depth" />
|
<JuceSlider identifier="autoTuneDepth" title="Auto Tune Depth" />
|
||||||
<JuceSlider identifier="portTime" title="Portamento Speed" />
|
<JuceSlider identifier="portTime" title="Portamento Speed" />
|
||||||
|
|
||||||
|
<JuceCheckbox identifier="freezeEnabled" />
|
||||||
|
<JuceSlider identifier="freezePitch" title="Freeze Pitch" />
|
||||||
|
<JuceSlider identifier="freezeVolume" title="Freeze Volume" />
|
||||||
</Container>
|
</Container>
|
||||||
<MidiNoteInfo />
|
<MidiNoteInfo />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -68,6 +68,10 @@ void WebViewPluginAudioProcessor::processBlock(juce::AudioBuffer<float>& buffer,
|
|||||||
shifter.SetPortamentoTime(state.getParameterAsValue("portTime").getValue());
|
shifter.SetPortamentoTime(state.getParameterAsValue("portTime").getValue());
|
||||||
shifter.SetHarmonyMix(state.getParameterAsValue("harmonyMix").getValue());
|
shifter.SetHarmonyMix(state.getParameterAsValue("harmonyMix").getValue());
|
||||||
shifter.SetAutoTuneEnable(state.getParameterAsValue("autoTuneEnabled").getValue());
|
shifter.SetAutoTuneEnable(state.getParameterAsValue("autoTuneEnabled").getValue());
|
||||||
|
shifter.SetFreeze(state.getParameterAsValue("freezeEnabled").getValue());
|
||||||
|
shifter.SetFreezePitchAdjust(state.getParameterAsValue("freezePitch").getValue());
|
||||||
|
shifter.SetFreezeVolume(state.getParameterAsValue("freezeVolume").getValue());
|
||||||
|
|
||||||
juce::AudioBuffer<float> const_buff;
|
juce::AudioBuffer<float> const_buff;
|
||||||
const_buff.makeCopyOf(buffer);
|
const_buff.makeCopyOf(buffer);
|
||||||
shifter.Process(const_buff.getArrayOfReadPointers(), (float**)buffer.getArrayOfWritePointers(), buffer.getNumSamples());
|
shifter.Process(const_buff.getArrayOfReadPointers(), (float**)buffer.getArrayOfWritePointers(), buffer.getNumSamples());
|
||||||
|
|||||||
@ -86,6 +86,20 @@ public:
|
|||||||
"Portamento Speed",
|
"Portamento Speed",
|
||||||
NormalisableRange<float> {0.001f, 0.2f, .001f},
|
NormalisableRange<float> {0.001f, 0.2f, .001f},
|
||||||
.01f);
|
.01f);
|
||||||
|
|
||||||
|
sliderIds.push_back("freezePitch");
|
||||||
|
addToLayout<AudioParameterFloat>(layout,
|
||||||
|
ParameterID("freezePitch"),
|
||||||
|
"Freeze pitch",
|
||||||
|
NormalisableRange<float> {-12.0f, 12.0f, 1.00f},
|
||||||
|
0.0f);
|
||||||
|
|
||||||
|
sliderIds.push_back("freezeVolume");
|
||||||
|
addToLayout<AudioParameterFloat>(layout,
|
||||||
|
ParameterID("freezeVolume"),
|
||||||
|
"Freeze Volume",
|
||||||
|
NormalisableRange<float> {0.0f, 1.0f, .01f},
|
||||||
|
0.5f);
|
||||||
|
|
||||||
|
|
||||||
toggleIds.push_back("autoTuneEnabled");
|
toggleIds.push_back("autoTuneEnabled");
|
||||||
@ -93,6 +107,12 @@ public:
|
|||||||
ParameterID("autoTuneEnabled"),
|
ParameterID("autoTuneEnabled"),
|
||||||
"AutoTune Enabled",
|
"AutoTune Enabled",
|
||||||
false);
|
false);
|
||||||
|
|
||||||
|
toggleIds.push_back("freezeEnabled");
|
||||||
|
addToLayout<AudioParameterBool>(layout,
|
||||||
|
ParameterID("freezeEnabled"),
|
||||||
|
"Freeze Enabled",
|
||||||
|
false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -35,6 +35,7 @@ void Shifter::Init(float samplerate, int samplesPerBlock)
|
|||||||
for (int i = 0; i < MAX_VOICES; ++i)
|
for (int i = 0; i < MAX_VOICES; ++i)
|
||||||
{
|
{
|
||||||
voices[i].Init(samplerate);
|
voices[i].Init(samplerate);
|
||||||
|
freeze_voices[i].Init(samplerate);
|
||||||
}
|
}
|
||||||
for (int i = 0; i < BUFFER_SIZE; ++i)
|
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);
|
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 Shifter::GetPeakIndex() {
|
||||||
int index = in_playhead - in_period * 2;
|
int index = in_playhead - in_period * 2;
|
||||||
if (index < 0)
|
if (index < 0)
|
||||||
@ -152,39 +158,6 @@ int Shifter::GetPeakIndex() {
|
|||||||
return max_index;
|
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) {
|
void Shifter::AddInterpolatedFrame(int voice, int max_index, float resampling_period) {
|
||||||
float period_ratio = in_period / 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)
|
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.05f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
for (int i = 0; i < size; ++i)
|
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)
|
if (out_period_counter > out_period)
|
||||||
{
|
{
|
||||||
out_period_counter -= out_period;
|
out_period_counter -= out_period;
|
||||||
@ -328,4 +331,81 @@ void Shifter::SetHarmonyMix(float mix) {
|
|||||||
harmony_mix = 1.0f;
|
harmony_mix = 1.0f;
|
||||||
melody_mix = (1.0f - mix) * 2.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shifter::SetFreezePitchAdjust(float val) {
|
||||||
|
for(int i = 0; i < MAX_VOICES; ++i) {
|
||||||
|
freeze_voices[i].SetPitchAdjust(val);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -158,17 +158,24 @@ public:
|
|||||||
float getOutputPitch() const { return sample_rate_ / out_period; }
|
float getOutputPitch() const { return sample_rate_ / out_period; }
|
||||||
void SetHarmonyMix(float mix);
|
void SetHarmonyMix(float mix);
|
||||||
void SetAutoTuneEnable(bool enable) { enable_autotune = enable; }
|
void SetAutoTuneEnable(bool enable) { enable_autotune = enable; }
|
||||||
|
void SetFreeze(bool);
|
||||||
|
void SetFreezePitchAdjust(float val);
|
||||||
|
void SetFreezeVolume(float val) { freeze_volume = val; }
|
||||||
|
|
||||||
float out_midi = 40;
|
float out_midi = 40;
|
||||||
ShifterVoice voices[MAX_VOICES];
|
ShifterVoice voices[MAX_VOICES];
|
||||||
|
ShifterVoice freeze_voices[MAX_VOICES];
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void DetectPitch(const float* const* in, float** out, size_t size);
|
void DetectPitch(const float* const* in, float** out, size_t size);
|
||||||
void SetRates();
|
void SetRates();
|
||||||
void GetSamples(float** output, const float* input, size_t size);
|
void GetSamples(float** output, const float* input, size_t size);
|
||||||
float GetOutputEnvelopePeriod(int out_voice);
|
float GetOutputEnvelopePeriod(int out_voice);
|
||||||
|
float GetOutputEnvelopePeriodFreeze(int freeze_voice);
|
||||||
int GetPeakIndex();
|
int GetPeakIndex();
|
||||||
void AddInterpolatedFrame(int voice, int max_index, float period_to_use);
|
void AddInterpolatedFrame(int voice, int max_index, float period_to_use);
|
||||||
|
void AddFreezeToOutput(int voice, float resampling_period);
|
||||||
|
void CopyInputToFreezeBuffer(int);
|
||||||
|
|
||||||
Helmholtz helm;
|
Helmholtz helm;
|
||||||
// GranularSustain player;
|
// GranularSustain player;
|
||||||
@ -189,6 +196,7 @@ private:
|
|||||||
bool pitch_trigger = false;
|
bool pitch_trigger = false;
|
||||||
float harmony_mix = 0.0f;
|
float harmony_mix = 0.0f;
|
||||||
float melody_mix = 0.0f;
|
float melody_mix = 0.0f;
|
||||||
|
bool freeze_needs_copy;
|
||||||
|
|
||||||
int trigger_bank;
|
int trigger_bank;
|
||||||
|
|
||||||
@ -206,13 +214,14 @@ private:
|
|||||||
float last_freqs[3];
|
float last_freqs[3];
|
||||||
float in_buffer[BUFFER_SIZE];
|
float in_buffer[BUFFER_SIZE];
|
||||||
float out_buffer[2][BUFFER_SIZE];
|
float out_buffer[2][BUFFER_SIZE];
|
||||||
|
float freeze_buffer[BUFFER_SIZE];
|
||||||
int out_playhead = 0;
|
int out_playhead = 0;
|
||||||
int in_playhead = 0;
|
int in_playhead = 0;
|
||||||
int last_autotune_midi = -1;
|
int last_autotune_midi = -1;
|
||||||
|
|
||||||
float out_period_filter_amount = 0.7f; // You can expose this as a parameter
|
float out_period_filter_amount = 0.7f; // You can expose this as a parameter
|
||||||
|
|
||||||
|
bool freeze_mode = false;
|
||||||
|
|
||||||
float out_period = 0; //C3
|
float out_period = 0; //C3
|
||||||
float in_period = 366.936;
|
float in_period = 366.936;
|
||||||
@ -221,6 +230,8 @@ private:
|
|||||||
float sample_rate_;
|
float sample_rate_;
|
||||||
int blocksize;
|
int blocksize;
|
||||||
bool enable_autotune = false;
|
bool enable_autotune = false;
|
||||||
|
float freeze_period;
|
||||||
|
float freeze_volume = 1;
|
||||||
MidiPitchSmoother out_midi_smoother;
|
MidiPitchSmoother out_midi_smoother;
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
@ -54,7 +54,7 @@ void ShifterVoice::Release() {
|
|||||||
|
|
||||||
void ShifterVoice::Process() {
|
void ShifterVoice::Process() {
|
||||||
current_amplitude = amplitude_envelope_.Process(onoff_);
|
current_amplitude = amplitude_envelope_.Process(onoff_);
|
||||||
current_period_ = sample_rate_ / mtof(portamento_.Process((float)current_midi));
|
current_period_ = sample_rate_ / mtof(portamento_.Process((float)current_midi + pitch_adjust));
|
||||||
period_counter++;
|
period_counter++;
|
||||||
overflow_ = period_counter >= current_period_;
|
overflow_ = period_counter >= current_period_;
|
||||||
if (overflow_) {
|
if (overflow_) {
|
||||||
@ -93,4 +93,8 @@ float ShifterVoice::GetPanning(int channel) const {
|
|||||||
case 1:
|
case 1:
|
||||||
return panning > .5 ? 1.0 : panning * 2.0f;
|
return panning > .5 ? 1.0 : panning * 2.0f;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShifterVoice::SetPitchAdjust(float adj) {
|
||||||
|
pitch_adjust = adj;
|
||||||
}
|
}
|
||||||
@ -32,8 +32,11 @@ public:
|
|||||||
void SetAdsrTimes(float attack, float decay, float release);
|
void SetAdsrTimes(float attack, float decay, float release);
|
||||||
float GetPanning(int channel) const;
|
float GetPanning(int channel) const;
|
||||||
int GetMidiNote() const { return current_midi; }
|
int GetMidiNote() const { return current_midi; }
|
||||||
|
void SetPitchAdjust(float);
|
||||||
bool onoff_;
|
bool onoff_;
|
||||||
|
|
||||||
|
float panning;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Port portamento_;
|
Port portamento_;
|
||||||
Adsr amplitude_envelope_;
|
Adsr amplitude_envelope_;
|
||||||
@ -44,5 +47,6 @@ private:
|
|||||||
float current_period_;
|
float current_period_;
|
||||||
float current_amplitude;
|
float current_amplitude;
|
||||||
float period_counter;
|
float period_counter;
|
||||||
float panning;
|
float pitch_adjust = 0.0f;
|
||||||
|
|
||||||
};
|
};
|
||||||
Reference in New Issue
Block a user