Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor/make-sure-fft-clean-up #26

Merged
merged 4 commits into from
Nov 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions lib/algorithm/signature_generator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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))
{
}
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -203,7 +204,7 @@ void SignatureGenerator::doPeakRecognition()
void SignatureGenerator::resetSignatureGenerater()
{
next_signature_ = Signature(16000, 0);
samples_ring_buffer_ = RingBuffer<std::int16_t>(2048, 0);
samples_ring_buffer_ = RingBuffer<std::int16_t>(FFT_BUFFER_CHUNK_SIZE, 0);
fft_outputs_ = RingBuffer<fft::RealArray>(256, fft::RealArray(1025, 0.0));
spread_ffts_output_ = RingBuffer<fft::RealArray>(256, fft::RealArray(1025, 0.0));
}
4 changes: 3 additions & 1 deletion lib/algorithm/signature_generator.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -38,6 +39,7 @@ class SignatureGenerator
std::uint32_t sample_processed_;
double max_time_seconds_;

fft::FFT fft_object_;
Signature next_signature_;
RingBuffer<std::int16_t> samples_ring_buffer_;
RingBuffer<fft::RealArray> fft_outputs_;
Expand Down
61 changes: 37 additions & 24 deletions lib/utils/fft.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <cmath>
#include <algorithm>
#include <fftw3.h> // NOLINT [include_order]
#include <memory>
#include <vector>

namespace fft
Expand All @@ -14,53 +15,65 @@ using RealArray = std::vector<long double>;
class FFT
{
public:
template <typename Iterable> 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 <typename Iterable> 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<double>(input[i]);
input_data_buffer_.get()[i] = static_cast<double>(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;
const double min_val = 1e-10;
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<double, decltype(&fftw_free)> input_data_buffer_;
std::unique_ptr<fftw_complex, decltype(&fftw_free)> output_data_buffer_;
};
} // namespace fft

#endif // LIB_UTILS_FFT_H_
Loading