35 template <
typename SampleType>
38 if (! buffer.hasSampleRate())
41 m_sampleRateHz = buffer.getSampleRateHz();
42 m_numChannels = buffer.getNumChannels();
43 m_data.resize (m_numChannels);
45 const size_t numFrames = buffer.getNumFrames();
47 m_fftSize = nextPowerOfTwo (std::max<size_t> (1, numFrames));
48 m_numBins = m_fftSize / 2 + 1;
50 for (size_t channel = 0; channel < m_numChannels; ++channel)
52 m_data[channel].resize (m_numBins);
53 std::vector<std::complex<
double>> fftBuffer (m_fftSize);
54 const SampleType* samples = buffer[channel];
56 for (size_t frame = 0; frame < numFrames; ++frame)
57 fftBuffer[frame] =
static_cast<
double> (samples[frame]);
59 for (size_t frame = numFrames; frame < m_fftSize; ++frame)
60 fftBuffer[frame] = 0.0;
62 performFFT (fftBuffer);
64 for (size_t bin = 0; bin < m_numBins; ++bin)
65 m_data[channel][bin] = fftBuffer[bin];
69 static Spectrum zeros (size_t numChannels, size_t signalDurationFrames,
double sampleRateHz)
76 spectrum.m_sampleRateHz = sampleRateHz;
84 if (signalDurationFrames == 0)
87 spectrum.m_fftSize = nextPowerOfTwo (signalDurationFrames);
88 spectrum.m_numBins = spectrum.m_fftSize / 2 + 1;
90 spectrum.m_numChannels = numChannels;
91 spectrum.m_data.resize (
92 spectrum.m_numChannels,
93 std::vector<std::complex<
double>> (spectrum.m_numBins, std::complex<
double> (0.0, 0.0))
102 return m_numChannels;
120 return m_sampleRateHz;
126 if (binIndex >= m_numBins)
129 return static_cast<
double> (binIndex) * m_sampleRateHz / m_fftSize;
134 return m_sampleRateHz / m_fftSize;
138 std::complex<
double>
getBinValue (size_t channel, size_t binIndex)
const
140 if (channel >= m_numChannels)
143 if (binIndex >= m_numBins)
146 return m_data[channel][binIndex];
150 std::complex<
double>
getBinValue (size_t channel,
double frequencyHz)
const
168 const std::complex<
double>*
operator[] (size_t channel)
const
170 if (channel >= m_numChannels)
173 return m_data[channel].data();
179 if (channel >= m_numChannels)
182 return m_data[channel].data();
188 if (frequencyHz < 0.0)
191 const size_t bin =
roundToSizeT (frequencyHz * m_fftSize / m_sampleRateHz
);
192 return std::min (bin, m_numBins - 1);
213 const size_t startBin =
static_cast<size_t> (slice
.start);
214 const size_t stopBin =
static_cast<size_t> (slice
.stop);
217 std::min (startBin, numBins),
218 std::min (stopBin, numBins)
228 std::min (startBin, numBins),
229 std::min (stopBin, numBins)
241 Spectrum() =
default;
243 static size_t nextPowerOfTwo (size_t x)
253 static void performFFT (std::vector<std::complex<
double>>& data)
255 const size_t n = data.size();
262 for (size_t i = 1; i < n; ++i)
275 std::swap (data[i], data[j]);
278 for (size_t len = 2; len <= n; len <<= 1)
280 const double angle = -
hart::twoPi /
static_cast<
double> (len);
281 const std::complex<
double> wlen (std::cos (angle), std::sin (angle));
283 for (size_t i = 0; i < n; i += len)
285 std::complex<
double> w (1.0, 0.0);
287 for (size_t jj = 0; jj < len / 2; ++jj)
289 const std::complex<
double> u = data[i + jj];
290 const std::complex<
double> v = data[i + jj + len / 2] * w;
292 data[i + jj] = u + v;
293 data[i + jj + len / 2] = u - v;
301 double m_sampleRateHz = 0.0;
303 size_t m_numChannels = 0;
304 size_t m_fftSize = 0;
305 size_t m_numBins = 0;
307 std::vector<std::vector<std::complex<
double>>> m_data;
Container for audio data.
Thrown when a container index is out of range.
Thrown when sample rate is mismatched.
Thrown when an unexpected container size is encountered.
Frequency-domain representation of a multi-channel audio signal.
const std::complex< double > * operator[](size_t channel) const
Returns pointer to read-only magnitudes of a specific channel.
Spectrum(const AudioBuffer< SampleType > &buffer)
Constructs spectrum from an audio buffer.
static Spectrum zeros(size_t numChannels, size_t signalDurationFrames, double sampleRateHz)
std::complex< double > getBinValue(size_t channel, double frequencyHz) const
Returns complex value of a frequency bin, by frequency.
size_t getFFTSize() const
Returns FFT size.
double getSampleRateHz() const
Returns sample rate in Hz.
std::pair< size_t, size_t > getBinIndices(const Slice &slice) const
Returns a pair of indices representing a provided slice.
double getBinFrequencyHz(size_t binIndex) const
Returns frequency corresponding to a bin index.
double getBinMagnitude(size_t channel, size_t binIndex) const
Returns magnitude of a frequency bin, by bin index.
size_t getNumBins() const
Returns number of frequency bins per channel.
std::complex< double > getBinValue(size_t channel, size_t binIndex) const
Returns complex value of a frequency bin, by bin index.
size_t getNumChannels() const
Returns number of channels.
size_t findClosestBin(double frequencyHz) const
Finds closest FFT bin to a given frequency.
std::complex< double > * operator[](size_t channel)
Returns pointer to mutable magnitudes of a specific channel.
double getBinWidthHz() const
double getBinMagnitude(size_t channel, double frequencyHz) const
Returns magnitude of a frequency bin, by frequency.
Thrown when some metric is requested to return a value in an unsupported unit.
Thrown when an inappropriate value is encountered.
#define HART_THROW_OR_RETURN_VOID(ExceptionType, message)
Throws an exception if HART_DO_NOT_THROW_EXCEPTIONS is set, prints a message and returns otherwise.
#define hassert(condition)
Triggers a HartAssertException if the condition is false
#define HART_THROW_OR_RETURN(ExceptionType, message, returnValue)
Throws an exception if HART_DO_NOT_THROW_EXCEPTIONS is set, prints a message and returns a specified ...
constexpr double twoPi
2 * pi
static size_t roundToSizeT(SampleType x)
Rounds a floating point value to a size_t value.
Represents a slice of analysis data.