Skip to content

Commit

Permalink
simplify & optimize threaded sample data transfer
Browse files Browse the repository at this point in the history
  • Loading branch information
cklosters committed Aug 13, 2024
1 parent 70c265f commit b14bd70
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 74 deletions.
150 changes: 78 additions & 72 deletions system_modules/napfft/src/fftbuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ namespace nap
uint full_buffer_size = (mHopSize > 1) ? data_size * 2 : data_size;;

// Create sample buffer
mSampleBuffer.resize(data_size * 2);
mSampleBufferFormatted.resize(data_size * 2);
mSampleBufferWindowed.resize(data_size);

// Compute hamming window
Expand Down Expand Up @@ -113,88 +113,94 @@ namespace nap

void FFTBuffer::supply(const std::vector<float>& samples)
{
const uint data_size = mContext->getSize();
const uint data_bytes = data_size * sizeof(float);

// Copy samples
{
std::lock_guard<std::mutex> lock(mSampleBufferMutex);
auto* half_ptr = mSampleBuffer.data() + mSampleBuffer.size() / 2;

// Copy second half to first half
std::memcpy(mSampleBuffer.data(), half_ptr, data_bytes);

// Copy new samples to second half
if (samples.size() == data_size)
{
std::memcpy(half_ptr, samples.data(), data_bytes);
}
else if (samples.size() > data_size)
{
// Zero-padding
mSampleBuffer.clear();
std::memcpy(half_ptr, samples.data(), data_bytes);
}
else
{
NAP_ASSERT_MSG(false, "Specified sample buffer size too small");
return;
}
mSampleBufferA = samples;
}

// TODO: Use notification to transform data on different thread!
mDirty = true;
}


void FFTBuffer::transform()
{
if (mDirty)
if (!mDirty)
return;

// Move original samples into sample storage
{
std::lock_guard<std::mutex> lock(mSampleBufferMutex);
mSampleBufferB = std::move(mSampleBufferA);
}

// Create formatted buffer
const uint data_size = mContext->getSize();
const uint data_bytes = data_size * sizeof(float);
auto* half_ptr = mSampleBufferFormatted.data() + mSampleBufferFormatted.size() / 2;

// Copy second half to first half
std::memcpy(mSampleBufferFormatted.data(), half_ptr, data_bytes);

// Copy new samples to second half
if (mSampleBufferB.size() == data_size)
{
std::memcpy(half_ptr, mSampleBufferB.data(), data_bytes);
}
else if (mSampleBufferB.size() > data_size)
{
// Zero-padding
mSampleBufferFormatted.clear();
std::memcpy(half_ptr, mSampleBufferB.data(), data_bytes);
}
else
{
NAP_ASSERT_MSG(false, "Specified sample buffer size too small");
return;
}

// Copy data to windowed array
const uint hop_count = static_cast<uint>(mOverlap);
std::fill(mComplexOutAverage.begin(), mComplexOutAverage.end(), 0.0f);

// Perform FFT
std::memcpy(mSampleBufferWindowed.data(), half_ptr, sizeof(float) * data_size);

for (uint h = 0; h < hop_count; h++)
{
const uint start = (h + 1) * mHopSize;

// Apply hamming window
for (uint i = 0; i < data_size; ++i)
mSampleBufferWindowed[i] = mSampleBufferFormatted[start + i] * mForwardHammingWindow[i];

// Scales amplitudes by nfft/2
kiss_fftr(mContext->mForwardConfig, static_cast<const float*>(mSampleBufferWindowed.data()), reinterpret_cast<kiss_fft_cpx*>(mComplexOut.data()));

// Add complex out to average
for (uint i = 0; i < mComplexOut.size(); ++i)
mComplexOutAverage[i] += mComplexOut[i];
}

// Average windowed buffer
if (hop_count > 1)
{
// Copy data to windowed array
const uint data_size = mContext->getSize();
const uint hop_count = static_cast<uint>(mOverlap);
std::fill(mComplexOutAverage.begin(), mComplexOutAverage.end(), 0.0f);

{
std::lock_guard<std::mutex> lock(mSampleBufferMutex);
auto* half_ptr = mSampleBuffer.data() + mSampleBuffer.size() / 2;

// Perform FFT
std::memcpy(mSampleBufferWindowed.data(), half_ptr, sizeof(float) * data_size);

for (uint h = 0; h < hop_count; h++)
{
const uint start = (h+1) * mHopSize;

// Apply hamming window
for (uint i = 0; i < data_size; ++i)
mSampleBufferWindowed[i] = mSampleBuffer[start + i] * mForwardHammingWindow[i];

// Scales amplitudes by nfft/2
kiss_fftr(mContext->mForwardConfig, static_cast<const float*>(mSampleBufferWindowed.data()), reinterpret_cast<kiss_fft_cpx*>(mComplexOut.data()));

// Add complex out to average
for (uint i = 0; i < mComplexOut.size(); ++i)
mComplexOutAverage[i] += mComplexOut[i];
}
}

// Average windowed buffer
if (hop_count > 1)
{
const float divisor = 1.0f / static_cast<float>(hop_count);
for (auto& c : mComplexOutAverage)
c *= divisor;
}

// Compute amplitudes and phase angles
for (uint i = 0; i < mBinCount; i++)
{
const auto& cpx = mComplexOutAverage[i];
const auto cpx_norm = cpx * mNormalizationFactor;
mAmplitude[i] = std::abs(cpx_norm);
mPhase[i] = std::arg(cpx_norm);
}
mDirty = false;
const float divisor = 1.0f / static_cast<float>(hop_count);
for (auto& c : mComplexOutAverage)
c *= divisor;
}

// Compute amplitudes and phase angles
for (uint i = 0; i < mBinCount; i++)
{
const auto& cpx = mComplexOutAverage[i];
const auto cpx_norm = cpx * mNormalizationFactor;
mAmplitude[i] = std::abs(cpx_norm);
mPhase[i] = std::arg(cpx_norm);
}

mDirty = false;
}


Expand Down
6 changes: 4 additions & 2 deletions system_modules/napfft/src/fftbuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,11 @@ namespace nap
float mHammingWindowSum; //< The sum of all window function coefficients
float mNormalizationFactor; //< Inverse of the window sum (2/sum)

std::vector<float> mSampleBuffer; //< The sample buffer is accessed on both the audio and main thread. Use mutex for read/write.
std::mutex mSampleBufferMutex; //< The mutex for accessing the sample buffer

std::mutex mSampleBufferMutex; //< The mutex for accessing the sample buffer
std::vector<float> mSampleBufferA; //< Samples provided by the audio node
std::vector<float> mSampleBufferB; //< Thread safe copy of original samples
std::vector<float> mSampleBufferFormatted; //< The sample buffer before application of a window function
std::vector<float> mSampleBufferWindowed; //< The sample buffer after application of a window function

uint mBinCount; //< The number of FFT bins
Expand Down

0 comments on commit b14bd70

Please sign in to comment.