15#include "signals/hart_audio_buffer_signal.hpp"
39 template <
typename SampleType>
40 Spectrum (
const AudioBuffer<SampleType>& buffer)
42 if (! buffer.hasSampleRate())
45 m_sampleRateHz = buffer.getSampleRateHz();
46 m_numChannels = buffer.getNumChannels();
47 m_data.resize (m_numChannels);
49 const size_t numFrames = buffer.getNumFrames();
50 m_sizeInTimeDomainFrames = numFrames;
53 m_numBins = m_fftSize / 2 + 1;
55 for (size_t channel = 0; channel < m_numChannels; ++channel)
57 m_data[channel].resize (m_numBins);
58 std::vector<std::complex<
double>> fftBuffer (m_fftSize);
59 const SampleType* samples = buffer[channel];
61 for (size_t frame = 0; frame < numFrames; ++frame)
62 fftBuffer[frame] =
static_cast<
double> (samples[frame]);
64 for (size_t frame = numFrames; frame < m_fftSize; ++frame)
65 fftBuffer[frame] = 0.0;
67 performFFT (fftBuffer,
false);
69 for (size_t bin = 0; bin < m_numBins; ++bin)
70 m_data[channel][bin] = fftBuffer[bin];
81 spectrum.m_sampleRateHz = sampleRateHz;
89 if (signalDurationFrames == 0)
93 spectrum.m_numBins = spectrum.m_fftSize / 2 + 1;
94 spectrum.m_sizeInTimeDomainFrames = signalDurationFrames;
96 spectrum.m_numChannels = numChannels;
97 spectrum.m_data.resize (
98 spectrum.m_numChannels,
99 std::vector<std::complex<
double>> (spectrum.m_numBins, std::complex<
double> (0.0, 0.0))
180 m_randomSeed = randomSeed;
191 if (lowCutoffFrequencyHz < 1.0)
194 m_lowCutoffFrequencyHz = lowCutoffFrequencyHz;
205 m_numChannels = numChannels;
220 m_signalDurationFrames = signalDurationFrames;
231 if (sampleRateHz < 0.0 ||
floatsEqual (sampleRateHz
, 0.0
) || std::isnan (sampleRateHz))
234 m_sampleRateHz = sampleRateHz;
239 double getBeta() {
return m_beta; }
242 double getLowCutoffHz() {
return m_lowCutoffFrequencyHz; }
245 double getSampleRateHz() {
return m_sampleRateHz; }
248 uint_fast32_t getRandomSeed() {
return m_randomSeed; }
251 size_t getNumChannels() {
return m_numChannels; }
254 size_t getDurationFrames() {
return m_signalDurationFrames; }
258 double m_lowCutoffFrequencyHz = 20.0;
264 ColouredNoise() =
default;
280 std::mt19937 randomNumberGenerator (options.getRandomSeed());
281 std::uniform_real_distribution<
double> phaseDistribution (0.0,
hart::twoPi);
282 std::uniform_int_distribution<
int> signDistribution (0, 1);
284 const double lowCutoffHz = options.getLowCutoffHz();
285 const double beta = options.getBeta();
286 const double magnitudeExponent = beta / 2.0;
288 for (size_t channel = 0; channel < spectrum.m_numChannels; ++channel)
290 for (size_t bin = 0; bin < spectrum.m_numBins; ++bin)
294 if (frequencyHz < lowCutoffHz || (frequencyHz == 0.0 && beta < 0.0))
296 spectrum.m_data[channel][bin] = std::complex<
double> (0.0, 0.0);
300 const double magnitude = std::pow (frequencyHz / lowCutoffHz, magnitudeExponent);
302 if (bin == 0 || (spectrum.m_fftSize % 2 == 0 && bin == spectrum.m_numBins - 1))
304 const double signedMagnitude = signDistribution (randomNumberGenerator) == 0 ? -magnitude : magnitude;
305 spectrum.m_data[channel][bin] = std::complex<
double> (signedMagnitude, 0.0);
309 const double phase = phaseDistribution (randomNumberGenerator);
310 spectrum.m_data[channel][bin] = std::complex<
double> (magnitude * std::cos (phase), magnitude * std::sin (phase));
320 return m_numChannels;
338 return m_sizeInTimeDomainFrames;
344 return m_sampleRateHz;
353 template <
typename SampleType>
356 AudioBuffer<SampleType> buffer (m_numChannels, m_sizeInTimeDomainFrames, m_sampleRateHz);
358 for (size_t channel = 0; channel < m_numChannels; ++channel)
360 std::vector<std::complex<
double>> fftBuffer (m_fftSize, std::complex<
double> (0.0, 0.0));
362 for (size_t bin = 0; bin < m_numBins; ++bin)
363 fftBuffer[bin] = m_data[channel][bin];
365 for (size_t bin = 1; bin + 1 < m_numBins; ++bin)
366 fftBuffer[m_fftSize - bin] = std::conj (m_data[channel][bin]);
368 performFFT (fftBuffer,
true);
369 SampleType* channelData = buffer[channel];
371 for (size_t frame = 0; frame < m_sizeInTimeDomainFrames; ++frame)
372 channelData[frame] =
static_cast<SampleType> (fftBuffer[frame].real());
385 template <
typename SampleType>
388 return toAudioBuffer<SampleType>().toSignal (loop);
401 if (m_numChannels != other.m_numChannels
402 || m_fftSize != other.m_fftSize
403 || m_numBins != other.m_numBins
404 || m_sizeInTimeDomainFrames != other.m_sizeInTimeDomainFrames)
410 for (size_t channel = 0; channel < m_numChannels; ++channel)
412 for (size_t bin = 0; bin < m_numBins; ++bin)
414 const std::complex<
double> thisValue = m_data[channel][bin];
415 const std::complex<
double> otherValue = other.m_data[channel][bin];
417 if (std::abs (std::real (thisValue - otherValue)) > toleranceLinear || std::abs (std::imag (thisValue - otherValue)) > toleranceLinear)
452 std::complex<
double>* channelData = tmp
[channel
];
455 channelData[bin] *= gainLinear;
464 if (binIndex >= m_numBins)
467 return static_cast<
double> (binIndex) * m_sampleRateHz / m_fftSize;
472 return m_sampleRateHz / m_fftSize;
476 std::complex<
double>
getBinValue (size_t channel, size_t binIndex)
const
478 if (channel >= m_numChannels)
481 if (binIndex >= m_numBins)
484 return m_data[channel][binIndex];
488 std::complex<
double>
getBinValue (size_t channel,
double frequencyHz)
const
506 const std::complex<
double>*
operator[] (size_t channel)
const
508 if (channel >= m_numChannels)
511 return m_data[channel].data();
517 if (channel >= m_numChannels)
520 return m_data[channel].data();
526 if (frequencyHz < 0.0)
529 const size_t bin =
roundToSizeT (frequencyHz * m_fftSize / m_sampleRateHz
);
530 return std::min (bin, m_numBins - 1);
551 const size_t startBin =
static_cast<size_t> (slice
.start);
552 const size_t stopBin =
static_cast<size_t> (slice
.stop);
555 std::min (startBin, numBins),
556 std::min (stopBin, numBins)
566 std::min (startBin, numBins),
567 std::min (stopBin, numBins)
582 stream <<
"<Spectrum>";
587 friend std::ostream& operator<< (std::ostream& stream,
const Spectrum& spectrum)
594 Spectrum() =
default;
596 static void performFFT (std::vector<std::complex<
double>>& data,
bool isInverse)
598 const size_t n = data.size();
605 for (size_t i = 1; i < n; ++i)
618 std::swap (data[i], data[j]);
621 for (size_t len = 2; len <= n; len <<= 1)
624 const std::complex<
double> wlen (std::cos (angle), std::sin (angle));
626 for (size_t i = 0; i < n; i += len)
628 std::complex<
double> w (1.0, 0.0);
630 for (size_t jj = 0; jj < len / 2; ++jj)
632 const std::complex<
double> u = data[i + jj];
633 const std::complex<
double> v = data[i + jj + len / 2] * w;
635 data[i + jj] = u + v;
636 data[i + jj + len / 2] = u - v;
645 const double scale = 1.0 /
static_cast<
double> (n);
647 for (size_t i = 0; i < n; ++i)
652 double m_sampleRateHz = 0.0;
654 size_t m_numChannels = 0;
655 size_t m_fftSize = 0;
656 size_t m_numBins = 0;
657 size_t m_sizeInTimeDomainFrames = 0;
659 std::vector<std::vector<std::complex<
double>>> m_data;
Thrown when a container index is out of range.
Thrown when sample rate is mismatched or invalid.
Thrown when an unexpected container size is encountered.
Options for colouredNoise() method.
ColouredNoise withNumChannels(size_t numChannels)
Sets number of channels.
static ColouredNoise violet()
Creates ColouredNoise instance with beta value for violet noise.
ColouredNoise withSampleRate(double sampleRateHz)
Sets sample rate.
ColouredNoise(float beta)
Creates ColouredNoise with a custom beta value.
static ColouredNoise brown()
Creates ColouredNoise instance with beta value for brown (a.k.a. red, or Brownian) noise.
static ColouredNoise blue()
Creates ColouredNoise instance with beta value for blue noise.
static ColouredNoise white()
Creates ColouredNoise instance with beta value for white noise.
static ColouredNoise pink()
Creates ColouredNoise instance with beta value for pink noise.
ColouredNoise withRandomSeed(uint_fast32_t randomSeed)
Sets random seed.
ColouredNoise withDuration(size_t signalDurationFrames)
Sets duration of time-domain audio that spectrum will represent.
ColouredNoise withLowCutoff(double lowCutoffFrequencyHz)
Sets the lowest meaningful frequency in the spectrum.
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.
bool equalsTo(const Spectrum &other, double toleranceLinear=1e-6) const
Checks whether this spectrum contains approximately the same data as another spectrum.
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.
size_t getSizeInTimeDomainFrames() const
Returns original audio buffer duration in frames.
double getBinFrequencyHz(size_t binIndex) const
Returns frequency corresponding to a bin index.
bool operator!=(const Spectrum &other) const
Checks whether two spectra differ beyond the default comparison tolerance.
Spectrum operator*(double gainLinear) const
Creates a new spectrum as a multiplying each complex bin value by a provided gain value.
double getBinMagnitude(size_t channel, size_t binIndex) const
Returns magnitude of a frequency bin, by bin index.
AudioBufferSignal< SampleType > toSignal(Loop loop=Loop::no) const
Converts the audio represented by this Spectrum to a Signal source.
size_t getNumBins() const
Returns number of frequency bins per channel.
void represent(std::ostream &stream) const
Prints readable representation of the spectrum object.
static Spectrum colouredNoise(ColouredNoise options)
Creates deterministic coloured noise in the frequency domain.
std::complex< double > getBinValue(size_t channel, size_t binIndex) const
Returns complex value of a frequency bin, by bin index.
BetaFor
Common values for beta argument in the colouredNoise() factory.
@ brownNoise
beta value for brown (a.k.a. red, or Brownian) noise, for the colouredNoise() factory
@ violetNoise
beta value for violet noise, for the colouredNoise() factory
@ pinkNoise
beta value for pink noise, for the colouredNoise() factory
@ blueNoise
beta value for blue noise, for the colouredNoise() factory
@ whiteNoise
beta value for white noise, for the colouredNoise() factory
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
static Spectrum zeros(size_t numChannels=CLIConfig::getInstance().getDefaultNumInputChannels(), size_t signalDurationFrames=CLIConfig::getInstance().getDefaultRenderDurationFrames(), double sampleRateHz=CLIConfig::getInstance().getDefaultSampleRateHz())
double getBinMagnitude(size_t channel, double frequencyHz) const
Returns magnitude of a frequency bin, by frequency.
bool operator==(const Spectrum &other) const
Checks whether two spectra contain approximately the same data.
AudioBuffer< SampleType > toAudioBuffer() const
Converts this spectrum to a time-domain audio buffer.
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.
static size_t nextPowerOfTwo(size_t x)
Finds next power of 2 after a non-negative number x.
static SampleType floatsEqual(SampleType a, SampleType b, SampleType epsilon=(SampleType) 1e-8)
Compares two floating point numbers within a given tolerance.
Loop
Helper values for something that could loop, like a Signal.
Holds values set by the user via CLI interface.
uint_fast32_t getRandomSeed() const
Gets random seed set by a "`--seed`/`-s`" argument.
size_t getDefaultNumInputChannels() const
double getDefaultSampleRateHz() const
static CLIConfig & getInstance()
Get the singleton instance.
size_t getDefaultRenderDurationFrames() const
Represents a slice of analysis data.