Audacity 3.2.0
Public Member Functions | Public Attributes | Private Attributes | List of all members
FormantShifter Class Reference

#include <FormantShifter.h>

Collaboration diagram for FormantShifter:
[legend]

Public Member Functions

 FormantShifter (int sampleRate, double cutoffQuefrency, FormantShifterLoggerInterface &logger)
 
void Reset (size_t fftSize)
 
void Reset ()
 
void Process (const float *powerSpectrum, std::complex< float > *spectrum, double factor)
 Processes spectrum in place, or does nothing if Reset(fftSize) wasn't called or Reset() was called since. More...
 

Public Attributes

const double cutoffQuefrency
 

Private Attributes

const int mSampleRate
 
FormantShifterLoggerInterfacemLogger
 
std::unique_ptr< staffpad::audio::FourierTransformmFft
 
staffpad::SamplesComplex mEnvelope
 
staffpad::SamplesReal mCepstrum
 
std::vector< float > mEnvelopeReal
 
std::vector< float > mWeights
 

Detailed Description

Definition at line 27 of file FormantShifter.h.

Constructor & Destructor Documentation

◆ FormantShifter()

FormantShifter::FormantShifter ( int  sampleRate,
double  cutoffQuefrency,
FormantShifterLoggerInterface logger 
)

Definition at line 43 of file FormantShifter.cpp.

48 , mLogger { logger }
49{
50}
const double cutoffQuefrency
const int mSampleRate
FormantShifterLoggerInterface & mLogger

Member Function Documentation

◆ Process()

void FormantShifter::Process ( const float *  powerSpectrum,
std::complex< float > *  spectrum,
double  factor 
)

Processes spectrum in place, or does nothing if Reset(fftSize) wasn't called or Reset() was called since.

"Shifts" the frequency-domain envelope of the input signal. typically used for formant preservation. Tuned to work best with voice.

Parameters
powerSpectrumThe power of spectrum, i.e., powerSpectrum[i] = norm(spectrum[i]), i.e., the square root was NOT taken.
spectrumThe complex spectrum of the input signal.
factorThe factor by which to scale the position of the formants on the frequency axis.
Precondition
powerSpectrum and spectrum are not null and have size fftSize / 2 + 1.
factor > 0.

Definition at line 67 of file FormantShifter.cpp.

69{
70 assert(factor > 0);
71 if (factor <= 0 || cutoffQuefrency == 0 || !mFft)
72 return;
73
74 const auto fftSize = mFft->getSize();
75 const auto numBins = fftSize / 2 + 1;
76
77 mLogger.Log(fftSize, "fftSize");
78
79 // Take the log of the normalized magnitude. (This assumes that
80 // the window averages to 1.)
81 std::complex<float>* pEnv = mEnvelope.getPtr(0);
82 const float normalizer = FastLog2(fftSize);
83 std::transform(powSpec, powSpec + numBins, pEnv, [&](float power) {
84 return .5f * FastLog2(power) - normalizer;
85 });
86
87 // Get the cosine transform of the log magnitude, aka the cepstrum.
88 mFft->inverseReal(mEnvelope, mCepstrum);
89 auto pCepst = mCepstrum.getPtr(0);
90 mLogger.Log(pCepst, fftSize, "cepstrum");
91
92 // "Lifter" the cepstrum.
93 const auto binCutoff = int(mSampleRate * cutoffQuefrency * factor);
94 if (binCutoff < fftSize / 2)
95 std::fill(pCepst + binCutoff + 1, pCepst + fftSize - binCutoff, 0.f);
96 mLogger.Log(pCepst, fftSize, "cepstrumLiftered");
97
98 // Get the envelope back.
99 mFft->forwardReal(mCepstrum, mEnvelope);
100 std::transform(
101 pEnv, pEnv + numBins, mEnvelopeReal.begin(),
102 [fftSize = fftSize](const std::complex<float>& env) {
103 return std::exp2(env.real() / fftSize);
104 });
105 mLogger.Log(mEnvelopeReal.data(), numBins, "envelope");
106
107 // Get the weights, which are the ratio of the desired envelope to the
108 // current envelope (which has the effect of downsampling).
109 std::transform(
110 mEnvelopeReal.begin(), mEnvelopeReal.end(), mWeights.begin(),
111 [](float env) { return std::isnormal(env) ? 1.f / env : 0.f; });
112
113 const auto lastNonZeroedBin =
114 ResampleFreqDomain(mEnvelopeReal.data(), fftSize, factor);
115
116 mLogger.Log(mEnvelopeReal.data(), numBins, "envelopeResampled");
117 std::transform(
118 mEnvelopeReal.begin(), mEnvelopeReal.end(), mWeights.begin(),
119 mWeights.begin(), [](float env, float weight) {
120 // Limit the weights to 100, which corresponds to 20dB.
121 // Our purpose is to add (or remove) energy to formants, and it doesn't
122 // need to be by more than that. This way we also avoid unreasonable
123 // amplification.
124 return std::min(env * weight, 100.f);
125 });
126
127 // Say the signal was downsampled to pitch it up. The factor is then less
128 // than 1, and the resampler had to zero out the upper part of the envelope
129 // bins. For these, rather than zeroing the spec too, it sounds better
130 // to keep the original, even if no envelope correction is applied, else the
131 // signal looses a bit of clarity. At such high frequencies, we probably
132 // don't need a smooth frequency-domain transition and a jump is fine. (This
133 // is visible in the spec, in case you're curious.)
134 std::fill(mWeights.begin() + lastNonZeroedBin, mWeights.end(), 1.f);
135
136 mLogger.Log(mWeights.data(), mWeights.size(), "weights");
137
138 mLogger.Log(
139 spec, numBins, "magnitude",
140 [fftSize = fftSize](const std::complex<float>& spec) {
141 return std::abs(spec) / fftSize;
142 });
143
144 // Now apply the weights.
145 std::transform(
146 spec, spec + numBins, mWeights.begin(), spec,
147 std::multiplies<std::complex<float>>());
148
149 mLogger.Log(
150 spec, numBins, "weightedMagnitude",
151 [fftSize = fftSize](const std::complex<float>& spec) {
152 return std::abs(spec) / fftSize;
153 });
154
155 mLogger.ProcessFinished(spec, fftSize);
156}
constexpr float FastLog2(float x)
Approximates the base-2 logarithm of a float to two decimal places, adapted from https://stackoverflo...
Definition: MathApprox.h:32
std::unique_ptr< staffpad::audio::FourierTransform > mFft
std::vector< float > mEnvelopeReal
std::vector< float > mWeights
staffpad::SamplesReal mCepstrum
staffpad::SamplesComplex mEnvelope
virtual void Log(int value, const char *name) const =0
virtual void ProcessFinished(std::complex< float > *spectrum, size_t fftSize)=0
If not already, disables the logging and marks the spectrum with an audible event to make clear where...
T * getPtr(int32_t channel)
Definition: SamplesFloat.h:48
size_t ResampleFreqDomain(float *x, size_t fftSize, double factor)
constexpr fastfloat_really_inline int32_t power(int32_t q) noexcept
Definition: fast_float.h:1454

References cutoffQuefrency, FastLog2(), staffpad::SamplesFloat< T >::getPtr(), FormantShifterLoggerInterface::Log(), mCepstrum, mEnvelope, mEnvelopeReal, mFft, mLogger, mSampleRate, mWeights, fast_float::detail::power(), FormantShifterLoggerInterface::ProcessFinished(), and anonymous_namespace{FormantShifter.cpp}::ResampleFreqDomain().

Referenced by anonymous_namespace{StaffPadTimeAndPitch.cpp}::CreateTimeAndPitch().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ Reset() [1/2]

void FormantShifter::Reset ( )

Definition at line 62 of file FormantShifter.cpp.

63{
64 mFft.reset();
65}

References mFft.

◆ Reset() [2/2]

void FormantShifter::Reset ( size_t  fftSize)

Definition at line 52 of file FormantShifter.cpp.

53{
54 mFft = std::make_unique<staffpad::audio::FourierTransform>(fftSize);
55 const auto numBins = fftSize / 2 + 1;
56 mEnvelope.setSize(1, numBins);
57 mCepstrum.setSize(1, fftSize);
58 mEnvelopeReal.resize(numBins);
59 mWeights.resize(numBins);
60}
void setSize(int32_t numChannels, int32_t samples)
Definition: SamplesFloat.h:21

References mCepstrum, mEnvelope, mEnvelopeReal, mFft, mWeights, and staffpad::SamplesFloat< T >::setSize().

Referenced by StaffPadTimeAndPitch::OnFormantPreservationChange(), and StaffPadTimeAndPitch::StaffPadTimeAndPitch().

Here is the call graph for this function:
Here is the caller graph for this function:

Member Data Documentation

◆ cutoffQuefrency

const double FormantShifter::cutoffQuefrency

Definition at line 30 of file FormantShifter.h.

Referenced by Process().

◆ mCepstrum

staffpad::SamplesReal FormantShifter::mCepstrum
private

Definition at line 64 of file FormantShifter.h.

Referenced by Process(), and Reset().

◆ mEnvelope

staffpad::SamplesComplex FormantShifter::mEnvelope
private

Definition at line 63 of file FormantShifter.h.

Referenced by Process(), and Reset().

◆ mEnvelopeReal

std::vector<float> FormantShifter::mEnvelopeReal
private

Definition at line 65 of file FormantShifter.h.

Referenced by Process(), and Reset().

◆ mFft

std::unique_ptr<staffpad::audio::FourierTransform> FormantShifter::mFft
private

Definition at line 62 of file FormantShifter.h.

Referenced by Process(), and Reset().

◆ mLogger

FormantShifterLoggerInterface& FormantShifter::mLogger
private

Definition at line 61 of file FormantShifter.h.

Referenced by Process().

◆ mSampleRate

const int FormantShifter::mSampleRate
private

Definition at line 60 of file FormantShifter.h.

Referenced by Process().

◆ mWeights

std::vector<float> FormantShifter::mWeights
private

Definition at line 66 of file FormantShifter.h.

Referenced by Process(), and Reset().


The documentation for this class was generated from the following files: