diff --git a/lib/algorithm/signature_generator.cpp b/lib/algorithm/signature_generator.cpp index d80602d..258ff8c 100644 --- a/lib/algorithm/signature_generator.cpp +++ b/lib/algorithm/signature_generator.cpp @@ -8,8 +8,8 @@ SignatureGenerator::SignatureGenerator() : input_pending_processing_(), sample_processed_(0), max_time_seconds_(3.1), - next_signature_(16000, 0), samples_ring_buffer_(2048, 0), - fft_outputs_(256, fft::RealArray(1025, 0.0)), + fft_object_(FFT_BUFFER_CHUNK_SIZE), next_signature_(16000, 0), + samples_ring_buffer_(FFT_BUFFER_CHUNK_SIZE, 0), fft_outputs_(256, fft::RealArray(1025, 0.0)), spread_ffts_output_(256, fft::RealArray(1025, 0.0)) { } @@ -63,24 +63,25 @@ void SignatureGenerator::doFFT(const LowQualityTrack &input) samples_ring_buffer_.begin() + samples_ring_buffer_.position()); samples_ring_buffer_.position() += input.size(); - samples_ring_buffer_.position() %= 2048; + samples_ring_buffer_.position() %= FFT_BUFFER_CHUNK_SIZE; samples_ring_buffer_.num_written() += input.size(); - fft::RealArray excerpt_from_ring_buffer(2048, 0.0); + fft::RealArray excerpt_from_ring_buffer(FFT_BUFFER_CHUNK_SIZE, 0.0); std::copy(samples_ring_buffer_.begin() + samples_ring_buffer_.position(), samples_ring_buffer_.end(), excerpt_from_ring_buffer.begin()); std::copy(samples_ring_buffer_.begin(), samples_ring_buffer_.begin() + samples_ring_buffer_.position(), - excerpt_from_ring_buffer.begin() + 2048 - samples_ring_buffer_.position()); + excerpt_from_ring_buffer.begin() + FFT_BUFFER_CHUNK_SIZE - + samples_ring_buffer_.position()); - for (int i = 0; i < 2048; ++i) + for (std::size_t i = 0; i < FFT_BUFFER_CHUNK_SIZE; ++i) { excerpt_from_ring_buffer[i] *= HANNIG_MATRIX[i]; } - fft::RealArray real = fft::FFT::RFFT(excerpt_from_ring_buffer); + fft::RealArray real = fft_object_.RFFT(excerpt_from_ring_buffer); fft_outputs_.Append(real); } @@ -203,7 +204,7 @@ void SignatureGenerator::doPeakRecognition() void SignatureGenerator::resetSignatureGenerater() { next_signature_ = Signature(16000, 0); - samples_ring_buffer_ = RingBuffer(2048, 0); + samples_ring_buffer_ = RingBuffer(FFT_BUFFER_CHUNK_SIZE, 0); fft_outputs_ = RingBuffer(256, fft::RealArray(1025, 0.0)); spread_ffts_output_ = RingBuffer(256, fft::RealArray(1025, 0.0)); } diff --git a/lib/algorithm/signature_generator.h b/lib/algorithm/signature_generator.h index cd08510..bd08581 100644 --- a/lib/algorithm/signature_generator.h +++ b/lib/algorithm/signature_generator.h @@ -6,7 +6,8 @@ #include "utils/fft.h" #include "utils/ring_buffer.h" -constexpr auto MAX_PEAKS = 255u; +constexpr std::size_t MAX_PEAKS = 255u; +constexpr std::size_t FFT_BUFFER_CHUNK_SIZE = 2048u; class SignatureGenerator { @@ -38,6 +39,7 @@ class SignatureGenerator std::uint32_t sample_processed_; double max_time_seconds_; + fft::FFT fft_object_; Signature next_signature_; RingBuffer samples_ring_buffer_; RingBuffer fft_outputs_; diff --git a/lib/utils/fft.h b/lib/utils/fft.h index 31f4df6..914fef4 100644 --- a/lib/utils/fft.h +++ b/lib/utils/fft.h @@ -4,6 +4,7 @@ #include #include #include // NOLINT [include_order] +#include #include namespace fft @@ -14,28 +15,35 @@ using RealArray = std::vector; class FFT { public: - template static RealArray RFFT(const Iterable &input) + explicit FFT(std::uint32_t input_size) + : input_size_(input_size), + input_data_buffer_(fftw_alloc_real(input_size), fftw_free), + output_data_buffer_(fftw_alloc_complex(input_size / 2 + 1), fftw_free) { - // If input size is 0, return false. - if (input.empty()) + fftw_plan_ = fftw_plan_dft_r2c_1d(input_size, input_data_buffer_.get(), + output_data_buffer_.get(), FFTW_ESTIMATE); + } + FFT(const FFT &) = delete; + FFT &operator=(const FFT &) = delete; + FFT(FFT &&) = delete; + FFT &operator=(FFT &&) = delete; + + template RealArray RFFT(const Iterable &input) + { + if (input.size() != input_size_) { - throw std::invalid_argument("Input size cannot be 0"); + throw std::invalid_argument( + "Input size must be equal to the input size specified in the constructor"); } - std::size_t input_size = input.size(); - RealArray real_output(input_size / 2 + 1); - - double *in = fftw_alloc_real(input_size); - fftw_complex *out = fftw_alloc_complex(input_size / 2 + 1); + RealArray real_output(input_size_ / 2 + 1); // Copy and convert the input data to double - for (std::size_t i = 0; i < input_size; i++) + for (std::size_t i = 0; i < input_size_; i++) { - in[i] = static_cast(input[i]); + input_data_buffer_.get()[i] = static_cast(input[i]); } - - fftw_plan plan = fftw_plan_dft_r2c_1d(input_size, in, out, FFTW_ESTIMATE); - fftw_execute(plan); + fftw_execute(fftw_plan_); double real_val = 0.0; double imag_val = 0.0; @@ -43,24 +51,29 @@ class FFT const double scale_factor = 1.0 / (1 << 17); // do max((real^2 + imag^2) / (1 << 17), 0.0000000001) - for (std::size_t i = 0; i < input_size / 2 + 1; ++i) + for (std::size_t i = 0; i < input_size_ / 2 + 1; ++i) { - real_val = out[i][0]; - imag_val = out[i][1]; + real_val = output_data_buffer_.get()[i][0]; + imag_val = output_data_buffer_.get()[i][1]; real_val = (real_val * real_val + imag_val * imag_val) * scale_factor; real_output[i] = (real_val < min_val) ? min_val : real_val; } - - // Clean up - fftw_destroy_plan(plan); - fftw_free(in); - fftw_free(out); - return real_output; } -}; + virtual ~FFT() + { + fftw_destroy_plan(fftw_plan_); + fftw_cleanup(); + } + +private: + std::uint32_t input_size_; + fftw_plan fftw_plan_; + std::unique_ptr input_data_buffer_; + std::unique_ptr output_data_buffer_; +}; } // namespace fft #endif // LIB_UTILS_FFT_H_