Commit 0b57f4e8 authored by Dan Povey's avatar Dan Povey
Browse files

trunk: changes to feature extraction classes so Compute can be done using a...

trunk: changes to feature extraction classes so Compute can be done using a const method (more friendly to multithreaded code).

git-svn-id: https://svn.code.sf.net/p/kaldi/code/trunk@4128 5e6a8d80-dfce-4ca6-a32a-6e07a63d50c8
parent 5ae9e388
......@@ -92,7 +92,7 @@ ext: test_dependencies ext_depend $(SUBDIRS) $(EXT_SUBDIRS)
# delete or comment out the lines below.
OPENFST_VER = $(shell grep 'PACKAGE_VERSION' ../tools/openfst/Makefile | sed -e 's:.*= ::')
test_dependencies:
@[ "$(OPENFST_VER)" == '1.2.10' ] || [ "$(OPENFST_VER)" == '1.3.2' ] || [ "$(OPENFST_VER)" == '1.3.3' ] || [ "$(OPENFST_VER)" == '1.3.4' ] || { echo "You now need openfst-1.2.10. Do: cd ../tools; svn update; ./install.sh; cd ../src; make depend; make"; exit 1; };
[ "$(OPENFST_VER)" == '1.3.2' ] || [ "$(OPENFST_VER)" == '1.3.3' ] || [ "$(OPENFST_VER)" == '1.3.4' ] || { echo "You now need openfst-1.3.2 or later. cd ../tools; svn update; ./install.sh; cd ../src; make depend; make"; exit 1; };
check_portaudio:
@[ -d ../tools/portaudio ] || ( cd ../tools; ./install_portaudio.sh )
......
......@@ -31,6 +31,11 @@ Fbank::Fbank(const FbankOptions &opts)
int32 padded_window_size = opts.frame_opts.PaddedWindowSize();
if ((padded_window_size & (padded_window_size-1)) == 0) // Is a power of two...
srfft_ = new SplitRadixRealFft<BaseFloat>(padded_window_size);
// We'll definitely need the filterbanks info for VTLN warping factor 1.0.
// [note: this call caches it.] The reason we call this here is to
// improve the efficiency of the "const" version of Compute().
GetMelBanks(1.0);
}
Fbank::~Fbank() {
......@@ -56,10 +61,50 @@ const MelBanks *Fbank::GetMelBanks(BaseFloat vtln_warp) {
return this_mel_banks;
}
const MelBanks *Fbank::GetMelBanks(BaseFloat vtln_warp,
bool *must_delete) const {
MelBanks *this_mel_banks = NULL;
std::map<BaseFloat, MelBanks*>::const_iterator iter =
mel_banks_.find(vtln_warp);
if (iter == mel_banks_.end()) {
this_mel_banks = new MelBanks(opts_.mel_opts,
opts_.frame_opts,
vtln_warp);
*must_delete = true;
} else {
this_mel_banks = iter->second;
*must_delete = false;
}
return this_mel_banks;
}
void Fbank::Compute(const VectorBase<BaseFloat> &wave,
BaseFloat vtln_warp,
Matrix<BaseFloat> *output,
Vector<BaseFloat> *wave_remainder) {
BaseFloat vtln_warp,
Matrix<BaseFloat> *output,
Vector<BaseFloat> *wave_remainder) {
const MelBanks *this_mel_banks = GetMelBanks(vtln_warp);
ComputeInternal(wave, *this_mel_banks, output, wave_remainder);
}
void Fbank::Compute(const VectorBase<BaseFloat> &wave,
BaseFloat vtln_warp,
Matrix<BaseFloat> *output,
Vector<BaseFloat> *wave_remainder) const {
bool must_delete_mel_banks;
const MelBanks *mel_banks = GetMelBanks(vtln_warp,
&must_delete_mel_banks);
ComputeInternal(wave, *mel_banks, output, wave_remainder);
if (must_delete_mel_banks)
delete mel_banks;
}
void Fbank::ComputeInternal(const VectorBase<BaseFloat> &wave,
const MelBanks &mel_banks,
Matrix<BaseFloat> *output,
Vector<BaseFloat> *wave_remainder) const {
KALDI_ASSERT(output != NULL);
// Get dimensions of output features
......@@ -77,6 +122,7 @@ void Fbank::Compute(const VectorBase<BaseFloat> &wave,
// Buffers
Vector<BaseFloat> window; // windowed waveform.
Vector<BaseFloat> mel_energies;
std::vector<BaseFloat> temp_buffer; // used by srfft.
BaseFloat log_energy;
// Compute all the freames, r is frame index..
......@@ -90,7 +136,7 @@ void Fbank::Compute(const VectorBase<BaseFloat> &wave,
log_energy = log(VecVec(window, window));
if (srfft_ != NULL) // Compute FFT using split-radix algorithm.
srfft_->Compute(window.Data(), true);
srfft_->Compute(window.Data(), true, &temp_buffer);
else // An alternative algorithm that works for non-powers-of-two.
RealFft(&window, true);
......@@ -98,9 +144,8 @@ void Fbank::Compute(const VectorBase<BaseFloat> &wave,
ComputePowerSpectrum(&window);
SubVector<BaseFloat> power_spectrum(window, 0, window.Dim()/2 + 1);
// Integrate with MelFiterbank over power spectrum
const MelBanks *this_mel_banks = GetMelBanks(vtln_warp);
this_mel_banks->Compute(power_spectrum, &mel_energies);
// Sum with MelFiterbank over power spectrum
mel_banks.Compute(power_spectrum, &mel_energies);
if (opts_.use_log_fbank)
mel_energies.ApplyLog(); // take the log.
......
......@@ -79,15 +79,33 @@ class Fbank {
explicit Fbank(const FbankOptions &opts);
~Fbank();
/// Will throw exception on failure (e.g. if file too short for
/// even one frame).
/// Will throw exception on failure (e.g. if file too short for even one
/// frame). The output "wave_remainder" is the last frame or two of the
/// waveform that it would be necessary to include in the next call to Compute
/// for the same utterance. It is not exactly the un-processed part (it may
/// have been partly processed), it's the start of the next window that we
/// have not already processed.
void Compute(const VectorBase<BaseFloat> &wave,
BaseFloat vtln_warp,
Matrix<BaseFloat> *output,
Vector<BaseFloat> *wave_remainder = NULL);
/// Const version of Compute()
void Compute(const VectorBase<BaseFloat> &wave,
BaseFloat vtln_warp,
Matrix<BaseFloat> *output,
Vector<BaseFloat> *wave_remainder = NULL) const;
private:
void ComputeInternal(const VectorBase<BaseFloat> &wave,
const MelBanks &mel_banks,
Matrix<BaseFloat> *output,
Vector<BaseFloat> *wave_remainder = NULL) const;
const MelBanks *GetMelBanks(BaseFloat vtln_warp);
const MelBanks *GetMelBanks(BaseFloat vtln_warp,
bool *must_delete) const;
FbankOptions opts_;
BaseFloat log_energy_floor_;
std::map<BaseFloat, MelBanks*> mel_banks_; // BaseFloat is VTLN coefficient.
......
......@@ -44,6 +44,11 @@ Mfcc::Mfcc(const MfccOptions &opts)
int32 padded_window_size = opts.frame_opts.PaddedWindowSize();
if ((padded_window_size & (padded_window_size-1)) == 0) // Is a power of two...
srfft_ = new SplitRadixRealFft<BaseFloat>(padded_window_size);
// We'll definitely need the filterbanks info for VTLN warping factor 1.0.
// [note: this call caches it.] The reason we call this here is to
// improve the efficiency of the "const" version of Compute().
GetMelBanks(1.0);
}
Mfcc::~Mfcc() {
......@@ -69,10 +74,50 @@ const MelBanks *Mfcc::GetMelBanks(BaseFloat vtln_warp) {
return this_mel_banks;
}
const MelBanks *Mfcc::GetMelBanks(BaseFloat vtln_warp, bool *must_delete) const {
MelBanks *this_mel_banks = NULL;
std::map<BaseFloat, MelBanks*>::const_iterator iter =
mel_banks_.find(vtln_warp);
if (iter == mel_banks_.end()) {
this_mel_banks = new MelBanks(opts_.mel_opts,
opts_.frame_opts,
vtln_warp);
*must_delete = true;
} else {
this_mel_banks = iter->second;
*must_delete = false;
}
return this_mel_banks;
}
void Mfcc::Compute(const VectorBase<BaseFloat> &wave,
BaseFloat vtln_warp,
Matrix<BaseFloat> *output,
Vector<BaseFloat> *wave_remainder) {
const MelBanks *this_mel_banks = GetMelBanks(vtln_warp);
ComputeInternal(wave, *this_mel_banks, output, wave_remainder);
}
void Mfcc::Compute(const VectorBase<BaseFloat> &wave,
BaseFloat vtln_warp,
Matrix<BaseFloat> *output,
Vector<BaseFloat> *wave_remainder) const {
bool must_delete_mel_banks;
const MelBanks *mel_banks = GetMelBanks(vtln_warp,
&must_delete_mel_banks);
ComputeInternal(wave, *mel_banks, output, wave_remainder);
if (must_delete_mel_banks)
delete mel_banks;
}
void Mfcc::ComputeInternal(const VectorBase<BaseFloat> &wave,
const MelBanks &mel_banks,
Matrix<BaseFloat> *output,
Vector<BaseFloat> *wave_remainder) const {
KALDI_ASSERT(output != NULL);
int32 rows_out = NumFrames(wave.Dim(), opts_.frame_opts),
cols_out = opts_.num_ceps;
......@@ -83,6 +128,7 @@ void Mfcc::Compute(const VectorBase<BaseFloat> &wave,
ExtractWaveformRemainder(wave, opts_.frame_opts, wave_remainder);
Vector<BaseFloat> window; // windowed waveform.
Vector<BaseFloat> mel_energies;
std::vector<BaseFloat> temp_buffer; // used by srfft.
for (int32 r = 0; r < rows_out; r++) { // r is frame index..
BaseFloat log_energy;
ExtractWindow(wave, r, opts_.frame_opts, feature_window_function_, &window,
......@@ -92,7 +138,7 @@ void Mfcc::Compute(const VectorBase<BaseFloat> &wave,
log_energy = log(VecVec(window, window));
if (srfft_ != NULL) // Compute FFT using the split-radix algorithm.
srfft_->Compute(window.Data(), true);
srfft_->Compute(window.Data(), true, &temp_buffer);
else // An alternative algorithm that works for non-powers-of-two.
RealFft(&window, true);
......@@ -100,9 +146,8 @@ void Mfcc::Compute(const VectorBase<BaseFloat> &wave,
ComputePowerSpectrum(&window);
SubVector<BaseFloat> power_spectrum(window, 0, window.Dim()/2 + 1);
const MelBanks *this_mel_banks = GetMelBanks(vtln_warp);
this_mel_banks->Compute(power_spectrum, &mel_energies);
mel_banks.Compute(power_spectrum, &mel_energies);
mel_energies.ApplyLog(); // take the log.
SubVector<BaseFloat> this_mfcc(output->Row(r));
......
......@@ -93,15 +93,29 @@ class Mfcc {
/// waveform that it would be necessary to include in the next call to Compute
/// for the same utterance. It is not exactly the un-processed part (it may
/// have been partly processed), it's the start of the next window that we
/// have not already processed. Will throw exception on failure (e.g. if file
/// too short for even one frame).
/// have not already processed.
void Compute(const VectorBase<BaseFloat> &wave,
BaseFloat vtln_warp,
Matrix<BaseFloat> *output,
Vector<BaseFloat> *wave_remainder = NULL);
/// Const version of Compute()
void Compute(const VectorBase<BaseFloat> &wave,
BaseFloat vtln_warp,
Matrix<BaseFloat> *output,
Vector<BaseFloat> *wave_remainder = NULL) const;
private:
void ComputeInternal(const VectorBase<BaseFloat> &wave,
const MelBanks &mel_banks,
Matrix<BaseFloat> *output,
Vector<BaseFloat> *wave_remainder = NULL) const;
const MelBanks *GetMelBanks(BaseFloat vtln_warp);
const MelBanks *GetMelBanks(BaseFloat vtln_warp,
bool *must_delete) const;
MfccOptions opts_;
Vector<BaseFloat> lifter_coeffs_;
Matrix<BaseFloat> dct_matrix_; // matrix we left-multiply by to perform DCT.
......
......@@ -39,6 +39,11 @@ Plp::Plp(const PlpOptions &opts)
int32 padded_window_size = opts.frame_opts.PaddedWindowSize();
if ((padded_window_size & (padded_window_size-1)) == 0) // Is a power of two...
srfft_ = new SplitRadixRealFft<BaseFloat>(padded_window_size);
// We'll definitely need the filterbanks info for VTLN warping factor 1.0.
// [note: this call caches it.] The reason we call this here is to
// improve the efficiency of the "const" version of Compute().
GetMelBanks(1.0);
}
Plp::~Plp() {
......@@ -71,6 +76,22 @@ const MelBanks *Plp::GetMelBanks(BaseFloat vtln_warp) {
return this_mel_banks;
}
const MelBanks *Plp::GetMelBanks(BaseFloat vtln_warp, bool *must_delete) const {
MelBanks *this_mel_banks = NULL;
std::map<BaseFloat, MelBanks*>::const_iterator iter =
mel_banks_.find(vtln_warp);
if (iter == mel_banks_.end()) {
this_mel_banks = new MelBanks(opts_.mel_opts,
opts_.frame_opts,
vtln_warp);
*must_delete = true;
} else {
this_mel_banks = iter->second;
*must_delete = false;
}
return this_mel_banks;
}
const Vector<BaseFloat> *Plp::GetEqualLoudness(BaseFloat vtln_warp) {
const MelBanks *this_mel_banks = GetMelBanks(vtln_warp);
Vector<BaseFloat> *ans = NULL;
......@@ -86,10 +107,62 @@ const Vector<BaseFloat> *Plp::GetEqualLoudness(BaseFloat vtln_warp) {
return ans;
}
const Vector<BaseFloat> *Plp::GetEqualLoudness(BaseFloat vtln_warp,
const MelBanks &mel_banks,
bool *must_delete) const {
Vector<BaseFloat> *ans = NULL;
std::map<BaseFloat, Vector<BaseFloat>*>::const_iterator iter
= equal_loudness_.find(vtln_warp);
if (iter == equal_loudness_.end()) {
ans = new Vector<BaseFloat>;
GetEqualLoudnessVector(mel_banks, ans);
*must_delete = true;
} else {
ans = iter->second;
*must_delete = false;
}
return ans;
}
void Plp::Compute(const VectorBase<BaseFloat> &wave,
BaseFloat vtln_warp,
Matrix<BaseFloat> *output,
Vector<BaseFloat> *wave_remainder) {
const MelBanks *mel_banks = GetMelBanks(vtln_warp);
const Vector<BaseFloat> *equal_loudness = GetEqualLoudness(vtln_warp);
ComputeInternal(wave, *mel_banks,
*equal_loudness,
output, wave_remainder);
}
void Plp::Compute(const VectorBase<BaseFloat> &wave,
BaseFloat vtln_warp,
Matrix<BaseFloat> *output,
Vector<BaseFloat> *wave_remainder) {
BaseFloat vtln_warp,
Matrix<BaseFloat> *output,
Vector<BaseFloat> *wave_remainder) const {
bool must_delete_mel_banks, must_delete_equal_loudness;
const MelBanks *mel_banks = GetMelBanks(vtln_warp,
&must_delete_mel_banks);
const Vector<BaseFloat> *equal_loudness
= GetEqualLoudness(vtln_warp, *mel_banks,
&must_delete_equal_loudness);
ComputeInternal(wave, *mel_banks, *equal_loudness,
output, wave_remainder);
if (must_delete_mel_banks)
delete mel_banks;
if (must_delete_equal_loudness)
delete equal_loudness;
}
void Plp::ComputeInternal(const VectorBase<BaseFloat> &wave,
const MelBanks &mel_banks,
const Vector<BaseFloat> &equal_loudness,
Matrix<BaseFloat> *output,
Vector<BaseFloat> *wave_remainder) const {
KALDI_ASSERT(output != NULL);
int32 rows_out = NumFrames(wave.Dim(), opts_.frame_opts),
cols_out = opts_.num_ceps;
......@@ -107,6 +180,8 @@ void Plp::Compute(const VectorBase<BaseFloat> &wave,
Vector<BaseFloat> raw_cepstrum(opts_.lpc_order); // not including C0,
// and size may differ from final size.
Vector<BaseFloat> final_cepstrum(opts_.num_ceps);
std::vector<BaseFloat> temp_buffer; // used by srfft.
KALDI_ASSERT(opts_.num_ceps <= opts_.lpc_order+1); // our num-ceps includes C0.
for (int32 r = 0; r < rows_out; r++) { // r is frame index..
BaseFloat log_energy;
......@@ -118,7 +193,7 @@ void Plp::Compute(const VectorBase<BaseFloat> &wave,
log_energy = log(VecVec(window, window));
if (srfft_ != NULL) // Compute FFT using split-radix algorithm.
srfft_->Compute(window.Data(), true);
srfft_->Compute(window.Data(), true, &temp_buffer);
else // An alternative algorithm that works for non-powers-of-two.
RealFft(&window, true);
......@@ -127,15 +202,10 @@ void Plp::Compute(const VectorBase<BaseFloat> &wave,
SubVector<BaseFloat> power_spectrum(window, 0, window.Dim()/2 + 1);
const MelBanks *this_mel_banks = GetMelBanks(vtln_warp);
this_mel_banks->Compute(power_spectrum, &mel_energies);
// HTK doesn't log the mel bank outputs for the PLPs' [HARDCODED]
// mel_energies.ApplyLog(); // take the log.
mel_energies.MulElements(*GetEqualLoudness(vtln_warp));
mel_banks.Compute(power_spectrum, &mel_energies);
mel_energies.MulElements(equal_loudness);
mel_energies.ApplyPow(opts_.compress_factor);
// duplicate first and last elements.
......
......@@ -102,14 +102,41 @@ class Plp {
int32 Dim() { return opts_.num_ceps; }
/// Will throw exception on failure (e.g. if file too short for even one
/// frame). The output "wave_remainder" is the last frame or two of the
/// waveform that it would be necessary to include in the next call to Compute
/// for the same utterance. It is not exactly the un-processed part (it may
/// have been partly processed), it's the start of the next window that we
/// have not already processed. Will throw exception on failure (e.g. if file
/// too short for even one frame).
void Compute(const VectorBase<BaseFloat> &wave,
BaseFloat vtln_warp,
Matrix<BaseFloat> *output,
Vector<BaseFloat> *wave_remainder = NULL);
/// Const version of Compute()
void Compute(const VectorBase<BaseFloat> &wave,
BaseFloat vtln_warp,
Matrix<BaseFloat> *output,
Vector<BaseFloat> *wave_remainder = NULL) const;
private:
void ComputeInternal(const VectorBase<BaseFloat> &wave,
const MelBanks &mel_banks,
const Vector<BaseFloat> &equal_loudness,
Matrix<BaseFloat> *output,
Vector<BaseFloat> *wave_remainder = NULL) const;
const MelBanks *GetMelBanks(BaseFloat vtln_warp);
const MelBanks *GetMelBanks(BaseFloat vtln_warp, bool *must_delete) const;
const Vector<BaseFloat> *GetEqualLoudness(BaseFloat vtln_warp);
const Vector<BaseFloat> *GetEqualLoudness(BaseFloat vtln_warp,
const MelBanks &mel_banks,
bool *must_delete) const;
PlpOptions opts_;
Vector<BaseFloat> lifter_coeffs_;
Matrix<BaseFloat> idft_bases_;
......
......@@ -66,10 +66,7 @@ void MatrixBase<Real>::Invert(Real *log_det, Real *det_sign,
} else {
if (log_det) *log_det = -std::numeric_limits<Real>::infinity();
if (det_sign) *det_sign = 0;
if(pivot) {
delete[] pivot;
pivot = NULL;
}
delete[] pivot;
return;
}
}
......
......@@ -3264,6 +3264,7 @@ template<typename Real> static void UnitTestSplitRadixComplexFft() {
MatrixIndexT N = 1 << logn;
MatrixIndexT twoN = 2*N;
std::vector<Real> temp_buffer;
SplitRadixComplexFft<Real> srfft(N);
for (MatrixIndexT p = 0; p < 3; p++) {
Vector<Real> v(twoN), w_base(twoN), w_alg(twoN), x_base(twoN), x_alg(twoN);
......@@ -3272,7 +3273,11 @@ template<typename Real> static void UnitTestSplitRadixComplexFft() {
if (N< 100) ComplexFt(v, &w_base, true);
w_alg.CopyFromVec(v);
srfft.Compute(w_alg.Data(), true);
if (rand() % 2 == 0)
srfft.Compute(w_alg.Data(), true);
else
srfft.Compute(w_alg.Data(), true, &temp_buffer);
if (N< 100) AssertEqual(w_base, w_alg, 0.01*N);
......@@ -3432,13 +3437,18 @@ template<typename Real> static void UnitTestSplitRadixRealFft() {
N = 1 << logn;
SplitRadixRealFft<Real> srfft(N);
std::vector<Real> temp_buffer;
for (MatrixIndexT q = 0; q < 3; q++) {
Vector<Real> v(N), w(N), x(N), y(N);
InitRand(&v);
w.CopyFromVec(v);
RealFftInefficient(&w, true);
y.CopyFromVec(v);
srfft.Compute(y.Data(), true); // test efficient one.
if (rand() % 2 == 0)
srfft.Compute(y.Data(), true);
else
srfft.Compute(y.Data(), true, &temp_buffer);
// KALDI_LOG <<"v = "<<v;
// KALDI_LOG << "Inefficient real fft of v is: "<< w;
// KALDI_LOG << "Efficient real fft of v is: "<< y;
......@@ -4131,7 +4141,7 @@ template<typename Real> static void UnitTestTriVecSolver() {
bool bad = false;
for (int32 i = 0; i < dim; i++) {
if (fabs(T(i, i)) < 0.1)
if (fabs(T(i, i)) < 0.2)
bad = true;
}
if (bad) {
......
......@@ -37,13 +37,12 @@ SplitRadixComplexFft<Real>::SplitRadixComplexFft(MatrixIndexT N) {
KALDI_ERR << "SplitRadixComplexFft called with invalid number of points "
<< N;
N_ = N;
logm_ = 0;
logn_ = 0;
while (N > 1) {
N >>= 1;
logm_ ++;
logn_ ++;
}
ComputeTables();
temp_buffer_ = NULL;
}
template<typename Real>
......@@ -53,8 +52,8 @@ void SplitRadixComplexFft<Real>::ComputeTables() {
Real *cn, *spcn, *smcn, *c3n, *spc3n, *smc3n;
Real ang, c, s;
lg2 = logm_ >> 1;
if (logm_ & 1) lg2++;
lg2 = logn_ >> 1;
if (logn_ & 1) lg2++;
brseed_ = new MatrixIndexT[1 << lg2];
brseed_[0] = 0;
brseed_[1] = 1;
......@@ -66,11 +65,11 @@ void SplitRadixComplexFft<Real>::ComputeTables() {
}
}
if (logm_ < 4) {
if (logn_ < 4) {
tab_ = NULL;
} else {
tab_ = new Real* [logm_-3];
for (i = logm_; i>=4 ; i--) {
tab_ = new Real* [logn_-3];
for (i = logn_; i>=4 ; i--) {
/* Compute a few constants */
m = 1 << i; m2 = m / 2; m4 = m2 / 2; m8 = m4 /2;
......@@ -101,12 +100,10 @@ template<typename Real>
SplitRadixComplexFft<Real>::~SplitRadixComplexFft() {
delete [] brseed_;
if (tab_ != NULL) {
for (MatrixIndexT i = 0; i < logm_-3; i++)
for (MatrixIndexT i = 0; i < logn_-3; i++)
delete [] tab_[i];
delete [] tab_;
}
// "delete" only does something if it's a non-NULL pointer.
delete [] temp_buffer_;
}
template<typename Real>
......@@ -116,49 +113,57 @@ void SplitRadixComplexFft<Real>::Compute(Real *xr, Real *xi, bool forward) const
xr = xi;
xi = tmp;
}
ComputeRecursive(xr, xi, logm_);
if (logm_ > 1) {
BitReversePermute(xr, logm_);
BitReversePermute(xi, logm_);
ComputeRecursive(xr, xi, logn_);
if (logn_ > 1) {
BitReversePermute(xr, logn_);
BitReversePermute(xi, logn_);
}
}
template<typename Real>
void SplitRadixComplexFft<Real>::Compute(Real *x, bool forward) {
if (temp_buffer_== NULL)
temp_buffer_ = new Real[N_];
void SplitRadixComplexFft<Real>::Compute(Real *x, bool forward,
std::vector<Real> *temp_buffer) const {
KALDI_ASSERT(temp_buffer != NULL);
if (temp_buffer->size() != N_)
temp_buffer->resize(N_);
Real *temp_ptr = &((*temp_buffer)[0]);
for (MatrixIndexT i = 0; i < N_; i++) {
x[i] = x[i*2]; // put the real part in the first half of x.
temp_buffer_[i] = x[i*2 + 1]; // put the imaginary part in temp_buffer.
x[i] = x[i * 2]; // put the real part in the first half of x.
temp_ptr[i] = x[i * 2 + 1]; // put the imaginary part in temp_buffer.
}
// copy the imaginary part back to the second half of x.
memcpy(static_cast<void*>(x + N_),
static_cast<void*>(temp_buffer_),
static_cast<void*>(temp_ptr),
sizeof(Real) * N_);
Compute(x, x + N_, forward);
// Now change the format back to interleaved.
memcpy(static_cast<void*>(temp_buffer_),
memcpy(static_cast<void*>(temp_ptr),
static_cast<void*>(x + N_),
sizeof(Real) * N_);
for (MatrixIndexT i = N_-1; i > 0; i--) { // don't include 0,
// in case MatrixIndexT is unsigned, the loop would not terminate.
// Treat it as a special case.
x[i*2] = x[i];