diff --git a/vibration_convert/core/algorithm/conversion/src/conversion_fft.cpp b/vibration_convert/core/algorithm/conversion/src/conversion_fft.cpp index e2c2196b9de87757a3cec93b8d3c40dfb1bd0b90..92939717da033cac9d159894c19f7b016554390c 100644 --- a/vibration_convert/core/algorithm/conversion/src/conversion_fft.cpp +++ b/vibration_convert/core/algorithm/conversion/src/conversion_fft.cpp @@ -93,7 +93,7 @@ int32_t ConversionFFT::Process(const std::vector &values, int32_t &frame frameCount = 0; bool isFrameFull = false; size_t valuesSize = values.size(); - for (size_t j = 0; j < valuesSize; j++) { + for (size_t j = 0; j < valuesSize; ++j) { // add value to buffer at current pos if (pos_ < static_cast(fftResult_.buffer.size())) { fftResult_.buffer[pos_++] = static_cast(values[j]); @@ -111,7 +111,7 @@ int32_t ConversionFFT::Process(const std::vector &values, int32_t &frame // reset pos to start of hop pos_ = para_.windowSize - para_.hopSize; /** shift buffer back by one hop size. */ - for (int32_t i = 0; i < pos_; i++) { + for (int32_t i = 0; i < pos_; ++i) { fftResult_.buffer[i] = fftResult_.buffer[i + para_.hopSize]; } isFftCalcFinish_ = true; @@ -138,7 +138,7 @@ float ConversionFFT::GetSpectralFlatness() const } float geometricMean = 0.0F; float arithmaticMean = 0.0F; - for (int32_t i = 0; i < bins_; i++) { + for (int32_t i = 0; i < bins_; ++i) { if (fftResult_.magnitudes[i] != 0) { geometricMean += logf(fftResult_.magnitudes[i]); } @@ -154,7 +154,7 @@ float ConversionFFT::GetSpectralCentroid() const { float x = 0.0F; float y = 0.0F; - for (int32_t i = 0; i < bins_; i++) { + for (int32_t i = 0; i < bins_; ++i) { x += fabs(fftResult_.magnitudes[i]) * i; y += fabs(fftResult_.magnitudes[i]); } @@ -195,15 +195,15 @@ float ConversionIFFT::Process(const std::vector &mags, const std::vector< } // add to output // shift back by one hop - for (int32_t i = 0; i < (para_.fftSize - para_.hopSize); i++) { + for (int32_t i = 0; i < (para_.fftSize - para_.hopSize); ++i) { fftResult_.buffer[i] = fftResult_.buffer[i + para_.hopSize]; } // clear the end chunk - for (int32_t i = 0; i < para_.hopSize; i++) { + for (int32_t i = 0; i < para_.hopSize; ++i) { fftResult_.buffer[i + para_.fftSize - para_.hopSize] = 0.0F; } // merge new output - for (int32_t i = 0; i < para_.fftSize; i++) { + for (int32_t i = 0; i < para_.fftSize; ++i) { fftResult_.buffer[i] += ifftOut_[i]; } } @@ -234,17 +234,14 @@ int32_t ConversionOctave::Init(float samplingRate, int32_t nBandsInTheFFT, int32 // this isn't currently configurable (used once here then no effect), but here's some reasoning firstOctaveFrequency_ = 55.0F; // for each spectrum[] bin, calculate the mapping into the appropriate average[] bin. - auto spe2avg = new (std::nothrow) int32_t[nSpectrum_]; - CHKPR(spe2avg, Sensors::ERROR); - std::shared_ptr spe2avgShared(spe2avg, std::default_delete()); - spe2avg_ = std::move(spe2avgShared); + spe2avg_ = MakeSharedArray(static_cast(nSpectrum_)); int32_t avgIdx = 0; float averageFreq = firstOctaveFrequency_; // the "top" of the first averaging bin // we're looking for the "top" of the first spectrum bin, and i'm just sort of // guessing that this is where it is (or possibly spectrumFrequencySpan/2?) // ... either way it's probably close enough for these purposes float spectrumFreq = spectrumFrequencySpan_; - for (int32_t speIdx = 0; speIdx < nSpectrum_; speIdx++) { + for (int32_t speIdx = 0; speIdx < nSpectrum_; ++speIdx) { while (spectrumFreq > averageFreq) { ++avgIdx; averageFreq *= averageFrequencyIncrement_; @@ -253,19 +250,9 @@ int32_t ConversionOctave::Init(float samplingRate, int32_t nBandsInTheFFT, int32 spectrumFreq += spectrumFrequencySpan_; } nAverages_ = avgIdx; - auto averages = new (std::nothrow) float[nAverages_]; - CHKPR(averages, Sensors::ERROR); - std::shared_ptr averagesShared(averages, std::default_delete()); - averages_ = std::move(averagesShared); - auto peaks = new (std::nothrow) float[nAverages_]; - CHKPR(peaks, Sensors::ERROR); - std::shared_ptr peaksShared(peaks, std::default_delete()); - peaks_ = std::move(peaksShared); - auto peakHoldTimes = new (std::nothrow) int32_t[nAverages_]; - CHKPR(peakHoldTimes, Sensors::ERROR); - std::shared_ptr peakHoldTimesShared(peakHoldTimes, std::default_delete()); - peakHoldTimes_ = std::move(peakHoldTimesShared); - + averages_ = MakeSharedArray(static_cast(nAverages_)); + peaks_ = MakeSharedArray(static_cast(nAverages_)); + peakHoldTimes_ = MakeSharedArray(static_cast(nAverages_)); peakHoldTime_ = 0; peakDecayRate_ = 0.9F; linearEQIntercept_ = 1.0F; @@ -286,12 +273,12 @@ int32_t ConversionOctave::Calculate(const std::vector &fftData) int32_t lastAvgIdx = 0; // tracks when we've crossed into a new averaging bin, so store current average float sum = 0.0F; // running total of spectrum data int32_t count = 0; // count of spectrums accumulated (for averaging) - for (int32_t speIdx = 0; speIdx < nSpectrum_; speIdx++) { + for (int32_t speIdx = 0; speIdx < nSpectrum_; ++speIdx) { ++count; sum += (fftData[speIdx] * (linearEQIntercept_ + speIdx * linearEQSlope_)); int32_t avgIdx = *(spe2avg_.get() + speIdx); if (avgIdx != lastAvgIdx) { - for (int32_t j = lastAvgIdx; j < avgIdx; j++) { + for (int32_t j = lastAvgIdx; j < avgIdx; ++j) { *(averages_.get() + j) = sum / static_cast(count); } count = 0; @@ -304,7 +291,7 @@ int32_t ConversionOctave::Calculate(const std::vector &fftData) *(averages_.get() + lastAvgIdx) = sum / count; } // update the peaks separately - for (int32_t i = 0; i < nAverages_; i++) { + for (int32_t i = 0; i < nAverages_; ++i) { if (IsGreatOrEqual(*(averages_.get() + i), *(peaks_.get() + i))) { // save new peak level, also reset the hold timer *(peaks_.get() + i) = *(averages_.get() + i); diff --git a/vibration_convert/core/algorithm/conversion/src/fft.cpp b/vibration_convert/core/algorithm/conversion/src/fft.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dba1a427b9695f6931c4ba0d9374188c5ccbcf9c --- /dev/null +++ b/vibration_convert/core/algorithm/conversion/src/fft.cpp @@ -0,0 +1,447 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "fft.h" + +#include +#include +#include +#include +#include + +#include "sensor_log.h" +#include "sensors_errors.h" + +namespace OHOS { +namespace Sensors { +namespace { +constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, SENSOR_LOG_DOMAIN, "Fft" }; +constexpr uint32_t MAX_FAST_BITS { 16 }; +constexpr int32_t NUM_SAMPLES_MIN { 2 }; +constexpr double HAMMING_WND_UP { 0.54 }; +constexpr double HAMMING_WND_DOWN { 0.46 }; +constexpr float VOLUME_MIN { 0.000001 }; +constexpr double AMP_TO_DB_COEF { 20.0 }; +constexpr bool TRANSFORM_INVERSE_FLAG { true }; +} // namespace + +Fft::~Fft() +{ + if (fftBitTable_ == nullptr) { + return; + } + for (uint32_t i = 0; i < MAX_FAST_BITS; ++i) { + if (fftBitTable_[i] != nullptr) { + free(fftBitTable_[i]); + fftBitTable_[i] = nullptr; + } + } + free(fftBitTable_); + fftBitTable_ = nullptr; +} + +inline uint32_t Fft::FastReverseBits(uint32_t pos, uint32_t numBits) +{ + if (numBits <= MAX_FAST_BITS && numBits >= 1) { + return fftBitTable_[numBits - 1][pos]; + } + return ReverseBits(pos, numBits); +} + +int32_t Fft::AlgInitFFT() +{ + // use malloc for 16 byte alignment + fftBitTable_ = (uint32_t **)malloc(MAX_FAST_BITS * sizeof(uint32_t *)); + CHKPR(fftBitTable_, Sensors::ERROR); + uint32_t length = 2; + for (uint32_t j = 1; j <= MAX_FAST_BITS; ++j) { + fftBitTable_[j - 1] = (uint32_t *)malloc(length * sizeof(uint32_t)); + CHKPR(fftBitTable_[j - 1], Sensors::ERROR); + for (uint32_t i = 0; i < length; ++i) { + fftBitTable_[j - 1][i] = ReverseBits(i, j); + } + length <<= 1; + } + return Sensors::SUCCESS; +} + +/* + * Complex Fast Fourier Transform + */ +int32_t Fft::AlgFFT(bool inverseTransform, FftParaAndResult ¶Res) +{ + uint32_t numSamples = static_cast(paraRes.numSamples); + if (!IsPowerOfTwo(numSamples)) { + SEN_HILOGE("The parameter is invalid,numSamples:%{public}d", numSamples); + return Sensors::PARAMETER_ERROR; + } + if (fftBitTable_ == nullptr) { + if (AlgInitFFT() != Sensors::SUCCESS) { + SEN_HILOGE("fftBitTable_ failed to allocate memory"); + return Sensors::ERROR; + } + } + double angleNumerator = 2 * M_PI; + if (inverseTransform) { + angleNumerator = -angleNumerator; + } + uint32_t numBits = ObtainNumberOfBits(numSamples); + if (numBits < 1) { + SEN_HILOGE("numBits is an invalid value"); + return Sensors::ERROR; + } + /* + * Do simultaneous data copy and bit-reversal ordering into outputs... + */ + for (uint32_t i = 0; i < numSamples; ++i) { + uint32_t j = FastReverseBits(i, numBits); + paraRes.realOut[j] = paraRes.realIn[i]; + paraRes.imagOut[j] = (paraRes.imagIn.empty()) ? 0.0F : paraRes.imagIn[i]; + } + + /* + * Do the FFT itself... + */ + int32_t blockEnd = 1; + for (int32_t blockSize = 2; blockSize <= paraRes.numSamples; blockSize <<= 1) { + double deltaAngle = angleNumerator / static_cast(blockSize); + float twoSinMagnitude = sin(-2 * deltaAngle); + float oneSinMagnitude = sin(-deltaAngle); + float twoCosMagnitude = cos(-2 * deltaAngle); + float oneCosMagnitude = cos(-deltaAngle); + float w = 2 * oneCosMagnitude; + for (int32_t i = 0; i < paraRes.numSamples; i += blockSize) { + float secondAmpReal = twoCosMagnitude; + float firstAmpReal = oneCosMagnitude; + float thirdAmpImaginary = twoSinMagnitude; + float secondAmpImaginary = oneSinMagnitude; + for (int32_t j = i, n = 0; n < blockEnd; ++j, ++n) { + float zerothAmpReal = w * firstAmpReal - secondAmpReal; + secondAmpReal = firstAmpReal; + firstAmpReal = zerothAmpReal; + float firstAmpImaginary = w * secondAmpImaginary - thirdAmpImaginary; + thirdAmpImaginary = secondAmpImaginary; + secondAmpImaginary = firstAmpImaginary; + int32_t k = j + blockEnd; + float real = zerothAmpReal * paraRes.realOut[k] - firstAmpImaginary * paraRes.imagOut[k]; + float imaginary = zerothAmpReal * paraRes.imagOut[k] + firstAmpImaginary * paraRes.realOut[k]; + paraRes.realOut[k] = paraRes.realOut[j] - real; + paraRes.imagOut[k] = paraRes.imagOut[j] - imaginary; + paraRes.realOut[j] += real; + paraRes.imagOut[j] += imaginary; + } + } + blockEnd = blockSize; + } + + /* + ** Need to normalize if inverse transform... + */ + if (inverseTransform) { + float denom = static_cast(paraRes.numSamples); + for (int32_t i = 0; i < paraRes.numSamples; ++i) { + paraRes.realOut[i] /= denom; + paraRes.imagOut[i] /= denom; + } + } + return Sensors::SUCCESS; +} + +/* + * Real Fast Fourier Transform + * + * This function was based on the code in Numerical Recipes in C. + * In Num. Rec., the inner loop is based on a single 1-based array + * of interleaved real and imaginary numbers. Because we have two + * separate zero-based arrays, our indices are quite different. + * Here is the correspondence between Num. Rec. indices and our indices: + * + * i1 <-> real[i] + * i2 <-> imag[i] + * i3 <-> real[n/2-i] + * i4 <-> imag[n/2-i] + */ +int32_t Fft::AlgRealFFT(FftParaAndResult ¶Res) +{ + if (paraRes.numSamples == 0) { + SEN_HILOGE("The divisor should not be 0"); + return Sensors::PARAMETER_ERROR; + } + int32_t half = paraRes.numSamples / 2; + float theta = M_PI / half; + for (int32_t i = 0; i < half; ++i) { + para_.realIn[i] = paraRes.realIn[2 * i]; + para_.imagIn[i] = paraRes.realIn[2 * i + 1]; + } + AlgFFT(!TRANSFORM_INVERSE_FLAG, para_); + float wtemp = static_cast(sin(F_HALF * theta)); + float wpr = -2.0 * wtemp * wtemp; + float wpi = static_cast(sin(theta)); + float wr = 1.0 + wpr; + float wi = wpi; + float previousHalfReal = 0.0F; + for (int32_t i = 1; i < (half / 2); ++i) { + int32_t i3 = half - i; + previousHalfReal = F_HALF * (para_.realOut[i] + para_.realOut[i3]); + float previousHalfIm = F_HALF * (para_.imagOut[i] - para_.imagOut[i3]); + float lastHalfReal = F_HALF * (para_.imagOut[i] + para_.imagOut[i3]); + float lastHalfIm = -F_HALF * (para_.realOut[i] - para_.realOut[i3]); + para_.realOut[i] = previousHalfReal + wr * lastHalfReal - wi * lastHalfIm; + para_.imagOut[i] = previousHalfIm + wr * lastHalfIm + wi * lastHalfReal; + para_.realOut[i3] = previousHalfReal - wr * lastHalfReal + wi * lastHalfIm; + para_.imagOut[i3] = -previousHalfIm + wr * lastHalfIm + wi * lastHalfReal; + wtemp = wr; + wr = wtemp * wpr - wi * wpi + wr; + wi = wi * wpr + wtemp * wpi + wi; + } + previousHalfReal = para_.realOut[0]; + para_.realOut[0] = previousHalfReal + para_.imagOut[0]; + para_.imagOut[0] = previousHalfReal - para_.imagOut[0]; + for (int32_t i = 0; i < half; ++i) { + paraRes.realOut[i] = para_.realOut[i]; + paraRes.imagOut[i] = para_.imagOut[i]; + } + return Sensors::SUCCESS; +} + +/* + * AlgPowerSpectrum + * + * This function computes the same as AlgRealFFT, above, but + * adds the squares of the real and imaginary part of each + * coefficient, extracting the power and throwing away the + * phase. + * + * For speed, it does not call AlgRealFFT, but duplicates some + * of its code. + */ +int32_t Fft::AlgPowerSpectrum(int32_t numSamples, const std::vector &in, std::vector &out) +{ + if (numSamples == 0) { + SEN_HILOGE("The divisor should not be 0"); + return Sensors::PARAMETER_ERROR; + } + int32_t half = numSamples / 2; + float theta = M_PI / half; + for (int32_t i = 0; i < half; ++i) { + para_.realIn[i] = in[2 * i]; + para_.imagIn[i] = in[2 * i + 1]; + } + AlgFFT(!TRANSFORM_INVERSE_FLAG, para_); + float wtemp = static_cast(sin(F_HALF * theta)); + float wpr = -2.0 * wtemp * wtemp; + float wpi = static_cast(sin(theta)); + float wr = 1.0 + wpr; + float wi = wpi; + float previousHalfReal = 0.0F; + float rt = 0.0F; + float it = 0.0F; + for (int32_t i = 1; i < (half / 2); ++i) { + int32_t i3 = half - i; + previousHalfReal = F_HALF * (para_.realOut[i] + para_.realOut[i3]); + float previousHalfIm = F_HALF * (para_.imagOut[i] - para_.imagOut[i3]); + float lastHalfReal = F_HALF * (para_.imagOut[i] + para_.imagOut[i3]); + float lastHalfIm = -F_HALF * (para_.realOut[i] - para_.realOut[i3]); + rt = previousHalfReal + wr * lastHalfReal - wi * lastHalfIm; + it = previousHalfIm + wr * lastHalfIm + wi * lastHalfReal; + out[i] = rt * rt + it * it; + rt = previousHalfReal - wr * lastHalfReal + wi * lastHalfIm; + it = -previousHalfIm + wr * lastHalfIm + wi * lastHalfReal; + out[i3] = rt * rt + it * it; + wr = (wtemp = wr) * wpr - wi * wpi + wr; + wi = wi * wpr + wtemp * wpi + wi; + } + rt = (previousHalfReal = para_.realOut[0]) + para_.imagOut[0]; + it = previousHalfReal - para_.imagOut[0]; + out[0] = rt * rt + it * it; + rt = para_.realOut[half / 2]; + it = para_.imagOut[half / 2]; + out[half / 2] = rt * rt + it * it; + return Sensors::SUCCESS; +} + +int32_t Fft::WindowFunc(int32_t whichFunction, int32_t numSamples, float *out) +{ + if (numSamples < NUM_SAMPLES_MIN) { + SEN_HILOGE("numSamples are less than 2"); + return Sensors::PARAMETER_ERROR; + } + switch (whichFunction) { + case WND_TYPE_BARTLETT: { + float samplesCount = static_cast(numSamples); + // Bartlett (triangular) window + for (int32_t i = 0; i < (numSamples / 2); ++i) { + out[i] *= i / (samplesCount / 2); + out[i + (numSamples / 2)] *= 1.0 - (i / (samplesCount / 2)); + } + return Sensors::SUCCESS; + } + case WND_TYPE_HAMMING: { + // Hamming + for (int32_t i = 0; i < numSamples; ++i) { + out[i] *= HAMMING_WND_UP - HAMMING_WND_DOWN * cos(2 * M_PI * i / (numSamples - 1)); + } + return Sensors::SUCCESS; + } + case WND_TYPE_HANNING: { + // Hanning + for (int32_t i = 0; i < numSamples; ++i) { + out[i] *= F_HALF - F_HALF * cos(2 * M_PI * i / (numSamples - 1)); + } + return Sensors::SUCCESS; + } + default: { + SEN_HILOGE("WindowType: %{public}d invalid", whichFunction); + return Sensors::ERROR; + } + } +} + +int32_t Fft::GenWindow(int32_t whichFunction, int32_t numSamples, std::vector &window) +{ + if (numSamples < NUM_SAMPLES_MIN) { + SEN_HILOGE("numSamples are less than 2"); + return Sensors::PARAMETER_ERROR; + } + switch (whichFunction) { + case WND_TYPE_BARTLETT: { + float samplesCount = static_cast(numSamples); + // Bartlett (triangular) window + for (int32_t i = 0; i < (numSamples / 2); ++i) { + window[i] = i / (samplesCount / 2); + window[i + (numSamples / 2)] = 1.0 - (i / (samplesCount / 2)); + } + return Sensors::SUCCESS; + } + case WND_TYPE_HAMMING: { + for (int32_t i = 0; i < numSamples; ++i) { + window[i] = HAMMING_WND_UP - HAMMING_WND_DOWN * cos(2 * M_PI * i / (numSamples - 1)); + } + return Sensors::SUCCESS; + } + case WND_TYPE_HANNING: { + // Hanning + for (int32_t i = 0; i < numSamples; ++i) { + window[i] = F_HALF - F_HALF * cos(2 * M_PI * i / (numSamples - 1)); + } + return Sensors::SUCCESS; + } + default: { + SEN_HILOGE("WindowType: %{public}d invalid", whichFunction); + return Sensors::ERROR; + } + } +} + +void Fft::Init(int32_t fftSize) +{ + fftSize_ = fftSize; + half_ = fftSize / 2; + + fftParaRes_ = FftParaAndResult(fftSize_); + para_ = FftParaAndResult(half_); +} + +std::vector Fft::GetReal() const +{ + return fftParaRes_.realOut; +} + +std::vector Fft::GetImg() const +{ + return fftParaRes_.imagOut; +} + +void Fft::CalcFFT(const std::vector &data, const std::vector &window) +{ + // windowing + for (int32_t i = 0; i < fftSize_; ++i) { + fftParaRes_.realIn[i] = data[i] * window[i]; + } + // fftParaRes_.realIn include realIn and imageIn! + AlgRealFFT(fftParaRes_); +} + +void Fft::ConvertPolar(std::vector &magnitude, std::vector &phase) +{ + for (int32_t i = 0; i < half_; ++i) { + /* compute power */ + float power = pow(fftParaRes_.realOut[i], 2) + pow(fftParaRes_.imagOut[i], 2); + /* compute magnitude and phase */ + magnitude[i] = sqrt(power); + phase[i] = atan2(fftParaRes_.imagOut[i], fftParaRes_.realOut[i]); + } +} + +/* Calculate the power spectrum */ +void Fft::CalculatePowerSpectrum(const std::vector &data, const std::vector &window, + std::vector &magnitude, std::vector &phase) +{ + CalcFFT(data, window); + ConvertPolar(magnitude, phase); +} + +void Fft::ConvertDB(const std::vector &in, std::vector &out) +{ + for (int32_t i = 0; i < half_; ++i) { + if (IsLessNotEqual(in[i], VOLUME_MIN)) { + // less than 0.1 nV + out[i] = 0.0F; // out of range + } else { + out[i] = AMP_TO_DB_COEF * log10(in[i] + 1); // get to to db scale + } + } +} + +void Fft::ConvertCart(const std::vector &magnitude, const std::vector &phase) +{ + /* get real and imag part */ + for (int32_t i = 0; i < half_; ++i) { + fftParaRes_.realIn[i] = magnitude[i] * cos(phase[i]); + fftParaRes_.imagIn[i] = magnitude[i] * sin(phase[i]); + } + /* zero negative frequencies */ + std::fill(fftParaRes_.realIn.begin() + half_, fftParaRes_.realIn.begin() + half_ + half_, 0); + std::fill(fftParaRes_.imagIn.begin() + half_, fftParaRes_.imagIn.begin() + half_ + half_, 0); +} + +void Fft::CalcIFFT(const std::vector &window, std::vector &finalOut) +{ + // second parameter indicates inverse transform + fftParaRes_.numSamples = fftSize_; + AlgFFT(TRANSFORM_INVERSE_FLAG, fftParaRes_); + for (int32_t i = 0; i < fftSize_; ++i) { + finalOut[i] += fftParaRes_.realOut[i] * window[i]; + } +} + +void Fft::InverseFFTComplex(const std::vector &window, const std::vector &real, + const std::vector &imaginary, std::vector &finalOut) +{ + for (int32_t i = 0; i < half_; ++i) { + fftParaRes_.realOut[i] = real[i]; + fftParaRes_.imagOut[i] = imaginary[i]; + } + CalcIFFT(window, finalOut); +} + +void Fft::InversePowerSpectrum(const std::vector &window, const std::vector &magnitude, + const std::vector &phase, std::vector &finalOut) +{ + ConvertCart(magnitude, phase); + CalcIFFT(window, finalOut); +} +} // namespace Sensors +} // namespace OHOS \ No newline at end of file diff --git a/vibration_convert/core/utils/include/utils.h b/vibration_convert/core/utils/include/utils.h index be01a4a991ef1af6db51897201e17676889507ea..f6a328702e31a217fee6a39ece02c23ee6138a9a 100644 --- a/vibration_convert/core/utils/include/utils.h +++ b/vibration_convert/core/utils/include/utils.h @@ -182,6 +182,12 @@ inline bool IsEqual(const T& left, const T& right) return (std::abs(left - right) <= std::numeric_limits::epsilon()); } +template +decltype(auto) MakeSharedArray(size_t size) +{ + return std::shared_ptr(new T[size], std::default_delete()); +} + inline double ConvertHtkMel(double frequencies) { double mels = (frequencies - MIN_F) / FSP;