20template <
typename SampleType>
37 if (! input.hasSampleRate())
40 if (! output.hasSampleRate())
43 const SampleType inputSampleRateHz = input.getSampleRateHz();
44 const SampleType outputSampleRateHz = output.getSampleRateHz();
46 if (
hart::floatsEqual (inputSampleRateHz, (SampleType) 0)
47 || inputSampleRateHz < (SampleType) 0
48 || std::isnan (inputSampleRateHz)
49 || std::isinf (inputSampleRateHz)
55 if (
hart::floatsEqual (outputSampleRateHz, (SampleType) 0)
56 || outputSampleRateHz < (SampleType) 0
57 || std::isnan (outputSampleRateHz)
58 || std::isinf (outputSampleRateHz)
64 if (!
hart::floatsEqual (inputSampleRateHz, outputSampleRateHz))
67 if (input.getNumFrames() != output.getNumFrames())
70 if (input.getNumChannels() != output.getNumChannels())
73 m_sampleRateHz = inputSampleRateHz;
75 std::ostringstream ctorArgumentsRepresentationStream;
76 ctorArgumentsRepresentationStream << input <<
", " << output;
77 m_ctorArgumentsRepresentation = ctorArgumentsRepresentationStream.str();
79 m_numChannels = input.getNumChannels();
80 m_numFrames = input.getNumFrames();
81 m_frames.resize (m_numChannels * m_numFrames);
82 m_channelPointers.resize (m_numChannels);
83 updateChannelPointers();
85 calculateImpulseResponse (input, output);
101 hassert (! std::isnan (m_sampleRateHz));
102 hassert (! std::isinf (m_sampleRateHz));
103 hassert (m_sampleRateHz > (SampleType) 0);
105 return m_sampleRateHz;
112 stream <<
"ImpulseResponse (" << m_ctorArgumentsRepresentation <<
")";
118 const SampleType*
operator[] (size_t channel)
const
120 return m_channelPointers[channel];
127 ir.represent (stream);
132 std::string m_ctorArgumentsRepresentation;
133 size_t m_numChannels = 0;
134 size_t m_numFrames = 0;
135 double m_sampleRateHz =
nan<double>();
136 std::vector<SampleType> m_frames;
137 std::vector<SampleType*> m_channelPointers;
139 void updateChannelPointers()
141 for (size_t channel = 0; channel < m_numChannels; ++channel)
142 m_channelPointers[channel] = m_numFrames > 0 ? &m_frames[channel * m_numFrames] :
nullptr;
145 void calculateImpulseResponse (
const AudioBuffer<SampleType>& input,
const AudioBuffer<SampleType>& output)
149 for (size_t channel = 0; channel < m_numChannels; ++channel)
151 std::vector<std::complex<
double>> inputSpectrum (fftSize);
152 std::vector<std::complex<
double>> outputSpectrum (fftSize);
154 for (size_t frame = 0; frame < m_numFrames; ++frame)
156 inputSpectrum[frame] =
static_cast<
double> (input[channel][frame]);
157 outputSpectrum[frame] =
static_cast<
double> (output[channel][frame]);
160 performFFT (inputSpectrum,
false);
161 performFFT (outputSpectrum,
false);
163 for (size_t bin = 0; bin < fftSize; ++bin)
168 outputSpectrum[bin] /= inputSpectrum[bin];
171 performFFT (outputSpectrum,
true);
173 SampleType* irChannel = m_channelPointers[channel];
175 for (size_t frame = 0; frame < m_numFrames; ++frame)
176 irChannel[frame] =
static_cast<SampleType> (outputSpectrum[frame].real());
180 static void performFFT (std::vector<std::complex<
double>>& data,
bool isInverse)
182 const size_t n = data.size();
189 for (size_t i = 1; i < n; ++i)
202 std::swap (data[i], data[j]);
205 for (size_t len = 2; len <= n; len <<= 1)
208 const std::complex<
double> wlen (std::cos (angle), std::sin (angle));
210 for (size_t i = 0; i < n; i += len)
212 std::complex<
double> w (1.0, 0.0);
214 for (size_t jj = 0; jj < len / 2; ++jj)
216 const std::complex<
double> u = data[i + jj];
217 const std::complex<
double> v = data[i + jj + len / 2] * w;
219 data[i + jj] = u + v;
220 data[i + jj + len / 2] = u - v;
229 const double scale = 1.0 /
static_cast<
double> (n);
231 for (size_t i = 0; i < n; ++i)
Thrown when a numbers of channels is mismatched.
Container for representing an impulse response (IR)
size_t getNumFrames() const
Get number of frames (samples)
double getSampleRateHz() const
Get a sample rate metadata.
const SampleType * operator[](size_t channel) const
Get a raw pointer to a specific channel's read-only audio data.
ImpulseResponse(AudioBuffer< SampleType > input, AudioBuffer< SampleType > output)
Create an IR instance from a pait of time-domain signals.
void represent(std::ostream &stream) const
Prints readable representation of the IR.
size_t getNumChannels() const
Get number of channels.
Thrown when sample rate is mismatched or invalid.
Thrown when an unexpected container size is encountered.
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(ExceptionType, message)
Throws an exception if HART_DO_NOT_THROW_EXCEPTIONS is set, prints a message otherwise.
constexpr double twoPi
2 * pi
FloatType nan()
Returns a quiet NaN value for the given floating-point type.
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.