Skip to content

Commit

Permalink
More docs. Made timeline skipping faster. Fixed a bug with pitch para…
Browse files Browse the repository at this point in the history
…ms not initializing on preset change.
  • Loading branch information
lukemcraig committed Jul 2, 2019
1 parent cc054f2 commit a98d84a
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 30 deletions.
20 changes: 10 additions & 10 deletions Plug-in/Source/GamelanizerParameters.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,19 @@
GamelanizerParameters::GamelanizerParameters()
{
// initialize parameterId arrays
for (int i = 0; i < GamelanizerConstants::maxLevels + 1; ++i)
for (auto i = 0; i < GamelanizerConstants::maxLevels + 1; ++i)
{
gainIds[i] = "gain" + String(i);
muteIds[i] = "mute" + String(i);
panIds[i] = "pan" + String(i);
}
for (int i = 0; i < GamelanizerConstants::maxLevels; ++i)
for (auto i = 0; i < GamelanizerConstants::maxLevels; ++i)
{
pitchIds[i] = "pitch" + String(i);
taperIds[i] = "taper" + String(i);
lpfIds[i] = "lpf" + String(i);
hpfIds[i] = "hpf" + String(i);
for (int j = 0; j < 4; ++j)
for (auto j = 0; j < 4; ++j)
dropIds[i][j] = "drop" + String(i) + "_" + String(j);
}
}
Expand Down Expand Up @@ -104,7 +104,7 @@ void GamelanizerParameters::addGainsToLayout(std::vector<std::unique_ptr<RangedA
Decibels::decibelsToGain(-10.0f),
Decibels::decibelsToGain(-12.0f)
};
for (int i = 0; i < GamelanizerConstants::maxLevels + 1; ++i)
for (auto i = 0; i < GamelanizerConstants::maxLevels + 1; ++i)
{
params.push_back(std::make_unique<AudioParameterFloat>(
getGainId(i),
Expand All @@ -121,7 +121,7 @@ void GamelanizerParameters::addGainsToLayout(std::vector<std::unique_ptr<RangedA

void GamelanizerParameters::addMutesToLayout(std::vector<std::unique_ptr<RangedAudioParameter>>& params)
{
for (int i = 0; i < GamelanizerConstants::maxLevels + 1; ++i)
for (auto i = 0; i < GamelanizerConstants::maxLevels + 1; ++i)
{
params.push_back(std::make_unique<AudioParameterBool>(
getMuteId(i),
Expand All @@ -147,7 +147,7 @@ void GamelanizerParameters::addPansToLayout(std::vector<std::unique_ptr<RangedAu
0, -100.0f, 100.0f, -50.0f, 50.0f
};

for (int i = 0; i < GamelanizerConstants::maxLevels + 1; ++i)
for (auto i = 0; i < GamelanizerConstants::maxLevels + 1; ++i)
{
params.push_back(std::make_unique<AudioParameterFloat>(
getPanId(i),
Expand All @@ -172,7 +172,7 @@ void GamelanizerParameters::addTapersToLayout(std::vector<std::unique_ptr<Ranged
std::array<float, GamelanizerConstants::maxLevels> taperDefaults{
0.0f, 0.0f, 0.1f, 0.2f
};
for (int i = 0; i < GamelanizerConstants::maxLevels; ++i)
for (auto i = 0; i < GamelanizerConstants::maxLevels; ++i)
{
params.push_back(std::make_unique<AudioParameterFloat>(
getTaperId(i),
Expand All @@ -198,7 +198,7 @@ void GamelanizerParameters::addFiltersToLayout(std::vector<std::unique_ptr<Range
}
return String(value, 1) + " Hz";
};
for (int i = 0; i < GamelanizerConstants::maxLevels; ++i)
for (auto i = 0; i < GamelanizerConstants::maxLevels; ++i)
{
params.push_back(std::make_unique<AudioParameterFloat>(
getHpfId(i),
Expand All @@ -214,7 +214,7 @@ void GamelanizerParameters::addFiltersToLayout(std::vector<std::unique_ptr<Range
std::array<float, GamelanizerConstants::maxLevels> lpfDefaults{
10000.0f, 9000.0f, 6000.0f, 5000.0f
};
for (int i = 0; i < GamelanizerConstants::maxLevels; ++i)
for (auto i = 0; i < GamelanizerConstants::maxLevels; ++i)
{
params.push_back(std::make_unique<AudioParameterFloat>(
getLpfId(i),
Expand All @@ -236,7 +236,7 @@ void GamelanizerParameters::addPitchesToLayout(std::vector<std::unique_ptr<Range
{
return String(value, 0) + " cents";
};
for (int i = 0; i < GamelanizerConstants::maxLevels; ++i)
for (auto i = 0; i < GamelanizerConstants::maxLevels; ++i)
{
params.push_back(std::make_unique<AudioParameterFloat>(
getPitchId(i),
Expand Down
33 changes: 19 additions & 14 deletions Plug-in/Source/PhaseVocoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ void PhaseVocoder::setParams(const float newPitchShiftFactor, const float newPit

resampler.maxNeedSamples = calculateMaximumNeededNumSamples(AnalysisFrames::analysisHopSize, oldPitchShiftFactor);

fft.window.amplitudeCompensationScale = static_cast<float>(synthesisHopSize / FftStruct::FftWindow::squaredWindowSum);
fft.window.amplitudeCompensationScale = static_cast<float>(synthesisHopSize / FftStruct::FftWindow::squaredWindowSum
);
}

void PhaseVocoder::loadNextParams()
Expand All @@ -86,11 +87,19 @@ int PhaseVocoder::calculateMaximumNeededNumSamples(const int desiredNumOut, cons
}


void PhaseVocoder::resampleHop()
void PhaseVocoder::resampleHop(const bool skipProcessing)
{
const auto numUsed = resampler.interpolator.process(pitchShiftFactor, resampler.queue.data.data(),
resampler.resamplerAnalysisHopBuffer,
AnalysisFrames::analysisHopSize);
int numUsed;
if (!skipProcessing)
{
numUsed = resampler.interpolator.process(pitchShiftFactor, resampler.queue.data.data(),
resampler.resamplerAnalysisHopBuffer,
AnalysisFrames::analysisHopSize);
}
else
{
numUsed = static_cast<int>(AnalysisFrames::analysisHopSize * pitchShiftFactor);
}
jassert(numUsed <= resampler.queue.writePosition);
popUsedSamples(numUsed);
}
Expand Down Expand Up @@ -136,8 +145,6 @@ void PhaseVocoder::copyAnalysisFrameToFftInOut()

void PhaseVocoder::storePhasesInBuffer()
{
// this is the first frame we've processed of this beat, so just store the phases without scaling them

// reinterpret_cast the fft buffer to complex
auto* complexBins = reinterpret_cast<std::complex<float>*>(fft.inOut);

Expand Down Expand Up @@ -220,33 +227,31 @@ void PhaseVocoder::scaleAnalysisFrame()
FloatVectorOperations::multiply(fft.inOut, fft.window.amplitudeCompensationScale, fftSize);
}

void PhaseVocoder::reset()
void PhaseVocoder::resetBetweenBeats()
{
previousFramePhases.isInitialized = false;
analysisFrames.initialized = false;
analysisFrames.writePosition = 0;
resampler.interpolator.reset();
loadNextParams();
}

void PhaseVocoder::fullReset()
{
// TODO why not in reset?
loadNextParams();
//TODO why not in reset?
// throw away the data on the resampler queue
resampler.queue.writePosition = 0;
reset();
resetBetweenBeats();
}

int PhaseVocoder::processSample(const float sampleValue, const bool skipProcessing)
{
jassert(static_cast<size_t>(resampler.queue.writePosition) < resampler.queue.data.size());
resampler.queue.data[resampler.queue.writePosition] = sampleValue;
resampler.queue.writePosition += 1;

// if the number of samples on the queue is at least the number needed to get the desired output length
if (resampler.queue.writePosition > resampler.maxNeedSamples)
{
resampleHop();
resampleHop(skipProcessing);
pushResampledHopOnToAnalysisFrameBuffer();
if (analysisFrames.initialized)
{
Expand Down
41 changes: 38 additions & 3 deletions Plug-in/Source/PhaseVocoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class PhaseVocoder
/**
* \brief Call this at the beginning of each new beat to reinitialize the phases.
*/
void reset();
void resetBetweenBeats();
/**
* \brief Call this at the beginning of playback or if the timeline position jumps around.
*/
Expand All @@ -73,6 +73,7 @@ class PhaseVocoder
int processSample(float sampleValue, bool skipProcessing);

const float* getFftInOutReadPointer() const { return fft.inOut; }

static int getFftSize() { return fftSize; }
//==============================================================================
private:
Expand Down Expand Up @@ -275,22 +276,56 @@ class PhaseVocoder
} previousFramePhases;

//==============================================================================
void setParams(const float newPitchShiftFactor, const float newPitchShiftFactorCents);
/**
* \brief Set new pitch shift factor and related member variables
* \param newPitchShiftFactor
* \param newPitchShiftFactorCents
*/
void setParams(float newPitchShiftFactor, float newPitchShiftFactorCents);

/**
* \brief Calculate the maximum number of samples the resampler might need to produce desiredNumOut
* \param desiredNumOut the analysis hop size
* \param oldPitchShiftFactor the previous pitch shift factor
* \return The maximum number of samples the resampler might need
*/
int calculateMaximumNeededNumSamples(int desiredNumOut, double oldPitchShiftFactor) const;
//==============================================================================
void resampleHop();
void resampleHop(bool skipProcessing);

void popUsedSamples(int numUsed);

void pushResampledHopOnToAnalysisFrameBuffer();

//==============================================================================
void scaleAnalysisFrame();

void copyAnalysisFrameToFftInOut();

void scaleAllFrequencyBinsAndStorePhaseBuffers();

std::complex<float> scaleFrequencyBin(int k, float mag, float currentPhase, float oldPhase);

/**
* \brief if this is the first frame we've processed of this beat, just store the phases without scaling them
*/
void storePhasesInBuffer();

//==============================================================================
/**
* \brief Calculate the phase of a complex frequency bin
* \return The phase
*/
static float complexBinPhase(const std::complex<float>& complexBin);

/**
* \brief Calculate the magnitude of a complex frequency bin
* \return The magnitude
*/
static float complexBinMag(const std::complex<float>& complexBin);

static float calculateFrequencyDeviation(float oldPhase, float currentPhase, int k);

static float wrapPhase(float phaseIn);
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PhaseVocoder)
Expand Down
9 changes: 6 additions & 3 deletions Plug-in/Source/PluginProcessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ void GamelanizerAudioProcessor::processSamples(const int64 numSamples, const flo
const bool skipProcessing)
{
#if MeasurePerformance
const auto startingTime = performanceMeasures.getNewStartingTime();
const auto startingTime = PerformanceMeasures::getNewStartingTime();
#endif
for (auto sample = 0; sample < numSamples; ++sample)
{
Expand Down Expand Up @@ -390,7 +390,7 @@ void GamelanizerAudioProcessor::processSample(const int level, const float sampl

//==============================================================================
void GamelanizerAudioProcessor::addSamplesToLevelsOutputBuffer(const int level, const float* samples,
const int nSamples)
const int nSamples)
{
const auto power = levelPowers[level];
const auto noteLength = static_cast<double>(beatSampleInfo.getBeatSampleLength()) / power;
Expand Down Expand Up @@ -497,7 +497,7 @@ void GamelanizerAudioProcessor::nextBeat()

//reset all phase vocoders
for (auto& pv : pvs)
pv.reset();
pv.resetBetweenBeats();

if (beatSampleInfo.isBeatB())
moveWritePosOnBeatB();
Expand Down Expand Up @@ -645,6 +645,9 @@ void GamelanizerAudioProcessor::setStateInformation(const void* data, const int
// because #setStateInformation will be called after #gamelanizerParametersVtsHelper is constructed,
// we need to force the smoothers to be at the correct values again
gamelanizerParametersVtsHelper.instantlyUpdateSmoothers();
// and then queue those new pitch shift parameters so they'll take effect at the beginning of playback
for (auto level = 0; level < GamelanizerConstants::maxLevels; ++level)
queuePhaseVocoderNextParams(level);
}
}
}
Expand Down
8 changes: 8 additions & 0 deletions Plug-in/Source/PluginProcessor.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,15 @@ class GamelanizerAudioProcessor : public AudioProcessor
~GamelanizerAudioProcessor();

//==============================================================================
/**
* \brief initialize the time and pitch shift settings of all the subdivision levels
*/
void initAllPhaseVocoders();

/**
* \brief queue the current pitch shift parameter for the subdivision level to change to when it's ready
* \param level the subdivision level
*/
void queuePhaseVocoderNextParams(int level);
//==============================================================================

Expand Down

0 comments on commit a98d84a

Please sign in to comment.