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 68 of file FormantShifter.cpp.

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

64{
65 mFft.reset();
66}

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>(
55 static_cast<int32_t>(fftSize));
56 const auto numBins = fftSize / 2 + 1;
57 mEnvelope.setSize(1, numBins);
58 mCepstrum.setSize(1, fftSize);
59 mEnvelopeReal.resize(numBins);
60 mWeights.resize(numBins);
61}
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: