Skip to content

Commit b0fbe94

Browse files
committed
WIP LFO implementation
1 parent dcf0568 commit b0fbe94

File tree

2 files changed

+194
-0
lines changed

2 files changed

+194
-0
lines changed

example/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ add_example(storage_tank)
7171
add_example(strong_angular_quantities)
7272
add_example(total_energy)
7373
add_example(unmanned_aerial_vehicle example_utils)
74+
add_example(lfo)
7475

7576
add_subdirectory(glide_computer_lib)
7677
add_subdirectory(kalman_filter)

example/lfo.cpp

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
// The MIT License (MIT)
2+
//
3+
// Copyright (c) 2024 Roth Michaels
4+
//
5+
// Permission is hereby granted, free of charge, to any person obtaining a copy
6+
// of this software and associated documentation files (the "Software"), to deal
7+
// in the Software without restriction, including without limitation the rights
8+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
// copies of the Software, and to permit persons to whom the Software is
10+
// furnished to do so, subject to the following conditions:
11+
//
12+
// The above copyright notice and this permission notice shall be included in all
13+
// copies or substantial portions of the Software.
14+
//
15+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
// SOFTWARE.
22+
23+
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
24+
// !!! Before you commit any changes to this file please make sure to check if it !!!
25+
// !!! renders correctly in the documentation "Examples" section. !!!
26+
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
27+
28+
#ifdef MP_UNITS_IMPORT_STD
29+
import std;
30+
#else
31+
#include <exception>
32+
#include <iostream>
33+
#endif
34+
#ifdef MP_UNITS_MODULES
35+
import mp_units;
36+
#else
37+
#include <mp-units/ostream.h>
38+
// #include <mp-units/systems/cgs.h>
39+
// #include <mp-units/systems/international.h>
40+
#include <mp-units/systems/angular.h>
41+
#include <mp-units/systems/isq.h>
42+
#include <mp-units/systems/si.h>
43+
#endif
44+
45+
namespace third_party {
46+
47+
//! State of a playback engine for music host application.
48+
struct musical_context {
49+
float sample_rate; //!< samples per second
50+
float tempo; //!< beats per minute (quarter note == one beat)
51+
};
52+
53+
//! API provided by music host application to provide global info
54+
//! about the playback engine.
55+
musical_context get_musical_context()
56+
{
57+
// Example data, this would be variable in a real-world context
58+
return musical_context{.sample_rate = 8000.f, .tempo = 130.f};
59+
}
60+
} // namespace third_party
61+
62+
namespace {
63+
using namespace mp_units;
64+
65+
QUANTITY_SPEC(sample_count, dimensionless, is_kind);
66+
QUANTITY_SPEC(sample_duration, isq::time);
67+
QUANTITY_SPEC(sample_rate, isq::frequency, sample_count / isq::time);
68+
69+
inline constexpr struct sample final : named_unit<"Smpl", one, kind_of<sample_count>> {
70+
} sample;
71+
72+
QUANTITY_SPEC(unit_sample_amount, dimensionless, is_kind);
73+
74+
inline constexpr struct sample_value final : named_unit<"PCM", one, kind_of<unit_sample_amount>> {
75+
} sample_value;
76+
77+
QUANTITY_SPEC(beat_count, dimensionless, is_kind);
78+
QUANTITY_SPEC(beat_duration, isq::time);
79+
QUANTITY_SPEC(tempo, isq::frequency, beat_count / isq::time);
80+
81+
inline constexpr struct quarter_note final : named_unit<"q", one, kind_of<beat_count>> {
82+
} quarter_note;
83+
static_assert(detail::SymbolicArg<struct quarter_note>);
84+
static_assert(Unit<struct quarter_note>);
85+
86+
inline constexpr struct whole_note final : named_unit<"w", mag<4> * quarter_note> {
87+
} whole_note;
88+
inline constexpr struct half_note final : named_unit<"h", mag<2> * quarter_note> {
89+
} half_note;
90+
inline constexpr struct dotted_half_note final : named_unit<"h.", mag<3> * quarter_note> {
91+
} dotted_half_note;
92+
inline constexpr struct eigth_note final : named_unit<"8th", mag_ratio<1, 2> * quarter_note> {
93+
} eigth_note;
94+
inline constexpr struct dotted_quarter_note final : named_unit<"q.", mag<3> * eigth_note> {
95+
} dotted_quarter_note;
96+
inline constexpr struct quarter_note_triplet final : named_unit<"qt", mag_ratio<1, 3> * half_note> {
97+
} quarter_note_triplet;
98+
inline constexpr struct sixteenth_note final : named_unit<"16th", mag_ratio<1, 2> * eigth_note> {
99+
} sixteenth_note;
100+
inline constexpr struct dotted_eigth_note final : named_unit<"8th.", mag<3> * sixteenth_note> {
101+
} dotted_eigth_note;
102+
103+
inline constexpr struct beats_per_minute final : named_unit<"bpm", quarter_note / non_si::minute> {
104+
} beats_per_minute;
105+
106+
inline constexpr auto smpl = sample;
107+
108+
inline constexpr auto pcm = sample_value;
109+
110+
// inline constexpr auto n_wd = 3 * half_note;
111+
inline constexpr auto n_w = whole_note;
112+
// inline constexpr auto n_hd = dotted_half_note;
113+
// inline constexpr auto n_h = half_note;
114+
// inline constexpr auto n_qd = dotted_quarter_note;
115+
// inline constexpr auto n_q = quarter_note;
116+
// inline constexpr auto n_qt = quarter_note_triplet;
117+
// inline constexpr auto n_8thd = dotted_eigth_note;
118+
// inline constexpr auto n_8th = eigth_note;
119+
// inline constexpr auto n_16th = sixteenth_note;
120+
121+
// inline constexpr auto bpm = beats_per_minute;
122+
123+
//! Typesafe version of music application playback engine state
124+
struct musical_context {
125+
quantity<sample_rate[si::hertz], float> sample_rate;
126+
quantity<beats_per_minute, float> tempo;
127+
};
128+
129+
//! Typesafe wrapper around API for host application musical context
130+
musical_context get_musical_context()
131+
{
132+
auto context = third_party::get_musical_context();
133+
return musical_context{.sample_rate = context.sample_rate * si::hertz, .tempo = context.tempo * beats_per_minute};
134+
}
135+
136+
class sine_wave_osc {
137+
public:
138+
sine_wave_osc(const musical_context& context, QuantityOf<isq::frequency> auto freq) :
139+
m_context{context}, m_frequency{freq}
140+
{
141+
std::cout << std::format("Created LFO with starting frequency {} ({}) for sample rate {} at tempo {}\n", freq,
142+
m_frequency, context.sample_rate, context.tempo);
143+
}
144+
145+
quantity<si::hertz, float> get_frequency() const { return m_frequency; }
146+
147+
void set_frequency(QuantityOf<isq::frequency> auto freq)
148+
{
149+
m_frequency = freq;
150+
std::cout << std::format("Setting frequency to {} ({})\n", freq, m_frequency);
151+
}
152+
153+
void set_period(QuantityOf<isq::time> auto period)
154+
{
155+
m_frequency = 1.f / period;
156+
std::cout << std::format("Setting period to {} (i.e. frequency to {})\n", period, m_frequency);
157+
}
158+
159+
quantity<sample_value, float> operator()()
160+
{
161+
auto out = sin(m_phase);
162+
m_phase += step;
163+
return out;
164+
}
165+
166+
private:
167+
void update_step() { m_step = (angular::revolution * m_frequency) / m_context.sample_rate; }
168+
169+
quantity<sample_value> sin(quantity_point<angular::radian, float>)
170+
{
171+
return std::sin(m_phase.numerical_value_in(angular::radian)) * pcm;
172+
}
173+
174+
musical_context m_context;
175+
quantity<si::hertz, float> m_frequency;
176+
quantity_point<angular::radian, float> m_phase{0.f};
177+
quantity<angular::radian, float> m_step;
178+
};
179+
} // namespace
180+
181+
int main()
182+
{
183+
using namespace mp_units::si::unit_symbols;
184+
185+
const auto context = get_musical_context();
186+
187+
auto sin_gen = sine_wave_osc{context, 2 * Hz};
188+
189+
// TODO set_frequency and set_period calls for demonstrating use of different units
190+
191+
192+
auto buffer = std::vector<quantity<sample_value, float>>(2 * whole_note)
193+
}

0 commit comments

Comments
 (0)