Audacity 3.2.0
StftFrameProvider.cpp
Go to the documentation of this file.
1/* SPDX-License-Identifier: GPL-2.0-or-later */
2/*!********************************************************************
3
4 Audacity: A Digital Audio Editor
5
6 StftFrameProvider.cpp
7
8 Matthieu Hodgkinson
9
10**********************************************************************/
11#include "StftFrameProvider.h"
12#include "MirTypes.h"
13#include "MirUtils.h"
14
15#include <algorithm>
16#include <cassert>
17#include <cmath>
18#include <numeric>
19
20namespace MIR
21{
22namespace
23{
24constexpr auto twoPi = 2 * 3.14159265358979323846;
25
27{
28 // 2048 frame size for sample rate 44.1kHz
29 return 1 << (11 + (int)std::round(std::log2(sampleRate / 44100.)));
30}
31
32double GetHopSize(int sampleRate, long long numSamples)
33{
34 // Aim for a hop size closest to 10ms, yet dividing `numSamples` to a power
35 // of two. This will spare us the need for resampling when we need to get the
36 // autocorrelation of the ODF using an FFT.
37 const auto idealHopSize = 0.01 * sampleRate;
38 const int exponent = std::round(std::log2(numSamples / idealHopSize));
39 if (exponent < 0)
40 return 0;
41 const auto numFrames = 1 << exponent;
42 return 1. * numSamples / numFrames;
43}
44} // namespace
45
47 : mAudio { audio }
48 , mFftSize { GetFrameSize(audio.GetSampleRate()) }
49 , mHopSize { GetHopSize(audio.GetSampleRate(), audio.GetNumSamples()) }
50 , mWindow { GetNormalizedHann(mFftSize) }
51 , mNumFrames { mHopSize > 0 ? static_cast<int>(std::round(
52 audio.GetNumSamples() / mHopSize)) :
53 0 }
54 , mNumSamples { audio.GetNumSamples() }
55{
56 assert(mNumFrames == 0 || IsPowOfTwo(mNumFrames));
57}
58
60{
62 return false;
63 frame.resize(mFftSize, 0.f);
64 const int firstReadPosition = mHopSize - mFftSize;
65 int start = std::round(firstReadPosition + mNumFramesProvided * mHopSize);
66 while (start < 0)
67 start += mNumSamples;
68 const auto end = std::min<long long>(start + mFftSize, mNumSamples);
69 const auto numToRead = end - start;
70 mAudio.ReadFloats(frame.data(), start, numToRead);
71 // It's not impossible that some user drops a file so short that `mFftSize >
72 // mNumSamples`. In that case we won't be returning a meaningful
73 // STFT, but that's a use case we're not interested in. We just need to make
74 // sure we don't crash.
75 const auto numRemaining = std::min(mFftSize - numToRead, mNumSamples);
76 if (numRemaining > 0)
77 mAudio.ReadFloats(frame.data() + numToRead, 0, numRemaining);
78 std::transform(
79 frame.begin(), frame.end(), mWindow.begin(), frame.begin(),
80 std::multiplies<float>());
82 return true;
83}
84
86{
87 return mNumFrames;
88}
89
91{
92 return mAudio.GetSampleRate();
93}
94
96{
97 return 1. * mAudio.GetSampleRate() / mHopSize;
98}
99
101{
102 return mFftSize;
103}
104} // namespace MIR
int min(int a, int b)
MockedAudio audio
virtual void ReadFloats(float *buffer, long long where, size_t numFrames) const =0
virtual double GetSampleRate() const =0
const long long mNumSamples
bool GetNextFrame(PffftFloatVector &frame)
StftFrameProvider(const MirAudioReader &source)
const MirAudioReader & mAudio
const std::vector< float > mWindow
double GetHopSize(int sampleRate, long long numSamples)
std::vector< float > GetNormalizedHann(int size)
Definition: MirUtils.cpp:80
constexpr auto IsPowOfTwo(int x)
Definition: MirUtils.h:28
const char * end(const char *str) noexcept
Definition: StringUtils.h:106
fastfloat_really_inline void round(adjusted_mantissa &am, callback cb) noexcept
Definition: fast_float.h:2512
STL namespace.
A vector of floats guaranteeing alignment as demanded by pffft.