Skip to content

Commit aec61bc

Browse files
committed
Finish LFO example
1 parent f4fe2bd commit aec61bc

File tree

1 file changed

+52
-3
lines changed

1 file changed

+52
-3
lines changed

example/audio/1-lfo.cpp

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#ifdef MP_UNITS_IMPORT_STD
2525
import std;
2626
#else
27+
#include <cassert>
2728
#include <iostream>
2829
#endif
2930
#ifdef MP_UNITS_MODULES
@@ -41,6 +42,7 @@ import mp_units;
4142
namespace {
4243
using namespace mp_units;
4344

45+
//! A DSP generator class that generates sample values for a sine wave oscillator.
4446
class sine_wave_osc {
4547
public:
4648
sine_wave_osc(const audio::musical_context& context, QuantityOf<isq::frequency> auto freq) :
@@ -65,13 +67,21 @@ class sine_wave_osc {
6567
std::cout << MP_UNITS_STD_FMT::format("Setting period to {} (i.e. frequency to {})\n", period, m_frequency);
6668
}
6769

70+
void set_period(QuantityOf<audio::beat_count> auto period)
71+
{
72+
std::cout << MP_UNITS_STD_FMT::format("Setting period to {} -- ", period);
73+
set_period(period / m_context.tempo);
74+
}
75+
6876
quantity<audio::sample_value, float> operator()()
6977
{
7078
auto out = angular::sin(m_phase.quantity_from_zero());
7179
m_phase += m_step;
7280
return out;
7381
}
7482

83+
void reset() { m_phase = phase_t{0.f * angular::radian}; }
84+
7585
private:
7686
using phase_t = quantity_point<angular::radian, mp_units::default_point_origin(angular::radian), float>;
7787

@@ -90,11 +100,50 @@ int main()
90100

91101
const auto context = audio::get_musical_context();
92102

93-
103+
// Sine oscillators are sometimes used as a "low-frequency oscillator"
104+
// (LFO) that runs at a frequency below the range of human hearing and
105+
// is used a source of modulation for other paramters in an audio
106+
// algorithm.
94107
auto sin_gen = sine_wave_osc{context, 1 * Hz};
95108

96-
// TODO set_frequency and set_period calls for demonstrating use of different units
109+
// Depending on the use-case sometimes an LFO will be set with a frequency in Hz
110+
sin_gen.set_frequency(13 * Hz);
111+
112+
// for some use-cases it is more convient for a user to set the period
113+
sin_gen.set_period(42 * s);
114+
115+
// and in some other use-cases setting the period in musical note duration is more intuitive
116+
sin_gen.set_period(1 * audio::half_note);
117+
118+
// Our oscillator can be used to generate sample values for a buffer
119+
// of audio samples. In this example we will create a buffer with
120+
// duration equal to 2 measures of 4/4 music (i.e. 2 whole notes at
121+
// the current tempo):
122+
const auto beats = 2 * audio::whole_note;
123+
const auto buffer_duration = beats / context.tempo;
124+
const auto buffer_size = (buffer_duration * context.sample_rate).in(audio::sample);
125+
126+
std::cout << MP_UNITS_STD_FMT::format("\nCreating buffer with size:\n\t{}\n\t{}\n\t{}\n\n", beats, buffer_duration,
127+
buffer_size);
128+
129+
using buffer_t = std::vector<quantity<audio::sample_value, float>>;
130+
131+
auto buffer_1 = buffer_t(std::size_t(buffer_size.numerical_value_in(audio::sample)));
132+
133+
std::cout << MP_UNITS_STD_FMT::format("Filling buffer with values from LFO @ {}", sin_gen.get_frequency());
134+
std::generate(begin(buffer_1), end(buffer_1), sin_gen);
135+
136+
assert(buffer_1.size() > 0u);
137+
std::cout << MP_UNITS_STD_FMT::format("\nLFO Values:\n[{}", buffer_1[0u]);
138+
for (std::size_t i = 1u; i < buffer_1.size(); ++i) {
139+
std::cout << MP_UNITS_STD_FMT::format(", {}", buffer_1[i]);
140+
}
141+
std::cout << "]\n\n";
97142

143+
// generated values should be the same after resetting oscillator
144+
sin_gen.reset();
145+
auto buffer_2 = buffer_t(buffer_1.size());
146+
std::generate(begin(buffer_2), end(buffer_2), sin_gen);
98147

99-
// auto buffer = std::vector<quantity<audio::sample_value, float>>(2 * audio::whole_note)
148+
return buffer_1 == buffer_2;
100149
}

0 commit comments

Comments
 (0)