7#include "signals/hart_signal.hpp"
18template<
typename SampleType>
47 double durationSeconds = 1.0,
48 double startFrequencyHz = 20.0,
49 double endFrequencyHz = 20.0e3,
52 double initialPhaseRadians = 0.0
54 m_durationSeconds (durationSeconds),
55 m_startFrequencyHz (startFrequencyHz),
56 m_endFrequencyHz (endFrequencyHz),
59 m_initialPhaseRadians (
wrapPhase (initialPhaseRadians
)),
60 m_currentPhaseRadians (m_initialPhaseRadians),
62 m_isFixedFrequency (
floatsEqual (m_startFrequencyHz
, m_endFrequencyHz
)),
63 m_frequencyRatio (m_endFrequencyHz / m_startFrequencyHz)
65 if (durationSeconds < 0)
68 if (startFrequencyHz <= 0 || endFrequencyHz <= 0)
78 return SineSweep (durationSeconds, m_startFrequencyHz, m_endFrequencyHz, m_type, m_loop, m_initialPhaseRadians);
87 return SineSweep (m_durationSeconds, startFrequencyHz, m_endFrequencyHz, m_type, m_loop, m_initialPhaseRadians);
96 return SineSweep (m_durationSeconds, m_startFrequencyHz, endFrequencyHz, m_type, m_loop, m_initialPhaseRadians);
105 return SineSweep (m_durationSeconds, m_startFrequencyHz, m_endFrequencyHz, type, m_loop, m_initialPhaseRadians);
116 return SineSweep (m_durationSeconds, m_startFrequencyHz, m_endFrequencyHz, m_type, loop, m_initialPhaseRadians);
125 return SineSweep (m_durationSeconds, m_startFrequencyHz, m_endFrequencyHz, m_type, m_loop, initialPhaseRadians);
130 void prepare (
double sampleRateHz, size_t , size_t )
override
132 m_sampleRateHz = sampleRateHz;
133 m_durationFrames =
roundToSizeT (m_durationSeconds * m_sampleRateHz
);
138 if (m_generateSilence)
140 fillWithSilence (output);
144 for (size_t frame = 0; frame < output.getNumFrames(); ++frame)
146 const SampleType value = (SampleType) std::sin (m_currentPhaseRadians);
148 for (size_t channel = 0; channel < output.getNumChannels(); ++channel)
149 output[channel][frame] = value;
153 if (m_posFrames == m_durationFrames)
155 if (m_loop ==
Loop::yes)
158 m_reverseFrequencyDirection = ! m_reverseFrequencyDirection;
164 fillWithSilence (output, frame + 1);
166 m_generateSilence =
true;
171 const double currentFrequencyHz = frequencyAtFrame (m_posFrames, m_reverseFrequencyDirection);
172 m_currentPhaseRadians +=
hart::twoPi * currentFrequencyHz / m_sampleRateHz;
173 m_currentPhaseRadians =
wrapPhase (m_currentPhaseRadians
);
180 m_currentPhaseRadians = m_initialPhaseRadians;
182 m_reverseFrequencyDirection =
false;
187 stream <<
"SineSweep ("
189 << m_durationSeconds <<
"_s, "
191 << m_startFrequencyHz <<
"_Hz, "
192 << m_endFrequencyHz <<
"_Hz, "
193 << (m_type ==
SweepType::linear ?
", SweepType::linear" :
"SweepType::log")
194 << (m_loop ==
Loop::yes ?
", Loop::yes)" :
", Loop::no)");
198 const double m_durationSeconds;
199 const double m_startFrequencyHz;
200 const double m_endFrequencyHz;
204 double m_sampleRateHz = 0.0;
205 size_t m_durationFrames = 0;
206 size_t m_posFrames = 0;
207 const double m_initialPhaseRadians;
208 double m_currentPhaseRadians;
209 bool m_generateSilence;
210 const bool m_isFixedFrequency;
211 const double m_frequencyRatio;
212 bool m_reverseFrequencyDirection =
false;
214 void fillWithSilence (
AudioBuffer<SampleType>& output, size_t startingFrame = 0)
216 if (startingFrame >= output.getNumFrames())
219 for (size_t channel = 0; channel < output.getNumChannels(); ++channel)
220 std::fill (output[channel] + startingFrame, output[channel] + output.getNumFrames(), (SampleType) 0);
223 double frequencyAtFrame (size_t offsetFrames,
bool reverseFrequencyDirection)
const
225 if (m_isFixedFrequency)
226 return m_startFrequencyHz;
228 hassert (offsetFrames < m_durationFrames);
229 const double offsetSeconds =
static_cast<
double> (offsetFrames) / m_sampleRateHz;
231 double portion = offsetSeconds / m_durationSeconds;
232 portion = reverseFrequencyDirection ? 1.0 - portion : portion;
235 return m_startFrequencyHz + (m_endFrequencyHz - m_startFrequencyHz) * portion;
238 return m_startFrequencyHz * std::pow (m_frequencyRatio, portion);
void renderNextBlock(AudioBuffer< SampleType > &output) override
Renders next block audio for the signal.
SineSweep withDuration(double durationSeconds)
Returns a new SineSweep instance with specified duration.
SineSweep withEndFrequency(double endFrequencyHz)
Returns a new SineSweep instance with specified end frequency.
SweepType
Determines how to change the frequency.
@ linear
Linear sweep, for a white noise-like spectrum.
@ log
Logarithmic sweep, for a pink noise-like spectrum.
SineSweep(double durationSeconds=1.0, double startFrequencyHz=20.0, double endFrequencyHz=20.0e3, SweepType type=SweepType::log, Loop loop=Loop::no, double initialPhaseRadians=0.0)
Creates a sine sweep signal.
void represent(std::ostream &stream) const override
Makes a text representation of this Signal for test failure outputs.
bool supportsNumChannels(size_t) const override
Tells the host whether this Signal is capable of generating audio for a certain amount of channels.
SineSweep withType(SweepType type)
Returns a new SineSweep instance with specified sweep type.
void prepare(double sampleRateHz, size_t, size_t) override
Prepare the signal for rendering.
SineSweep withLoop(Loop loop)
Returns a new SineSweep instance with specified loop preference.
void reset() override
Resets the Signal to initial state.
SineSweep withPhase(double initialPhaseRadians)
Returns a new SineSweep instance with specified initial phase.
SineSweep withStartFrequency(double startFrequencyHz)
Returns a new SineSweep instance with specified start frequency.
Loop
Determines what to do after frequency sweep is done.
@ no
Stop after finishing one sweep.
@ yes
Keep on sweeping back and forth.
std::ostream & secPrecision(std::ostream &stream)
Sets number of decimal places for values in seconds.
std::ostream & hzPrecision(std::ostream &stream)
Sets number of decimal places for values in hertz.
constexpr double twoPi
2 * pi
SampleType wrapPhase(const SampleType phaseRadians)
Keeps phase in 0..twoPi range.
static size_t roundToSizeT(SampleType x)
Rounds a floating point value to a size_t value.
static SampleType floatsEqual(SampleType a, SampleType b, SampleType epsilon=(SampleType) 1e-8)
Compares two floating point numbers within a given tolerance.
#define HART_THROW_OR_RETURN_VOID(ExceptionType, message)
#define hassert(condition)
#define HART_SIGNAL_DECLARE_ALIASES_FOR(ClassName)