Tabula Sonora 0.1.0
A native C++20 implementation of the Roland Sound Canvas VA synth voice
Loading...
Searching...
No Matches
voice_pool.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <array>
4#include <cstdint>
5#include <functional>
6#include <vector>
7
8namespace ts {
9
19
20class VoicePool;
21
29struct Voice {
30 const VoicePool* pool = nullptr;
31 int index = 0;
32
33 [[nodiscard]] VoiceState state() const noexcept;
34 [[nodiscard]] int channel() const noexcept;
35 [[nodiscard]] int note() const noexcept;
36 [[nodiscard]] int velocity() const noexcept;
38 [[nodiscard]] int note_group() const noexcept;
39
40 [[nodiscard]] bool is_active() const noexcept { return state() != VoiceState::free; }
41};
42
55class VoicePool {
56public:
58 static constexpr int default_polyphony = 64;
59
61 static constexpr int max_voices = default_polyphony;
62
67 static constexpr int growth_chunk = 64;
68
70 static constexpr int group_size = 4;
71
76 std::function<void(int)> stealing;
77
80
88 VoicePool(int polyphony, bool growing);
89
91 [[nodiscard]] int capacity() const noexcept { return capacity_; }
92
94 [[nodiscard]] bool grows() const noexcept { return growing_; }
95
97 [[nodiscard]] int high_water() const noexcept { return high_water_; }
98
105 [[nodiscard]] int steal_count() const noexcept { return steal_count_; }
106
108 [[nodiscard]] int growth_count() const noexcept { return growth_count_; }
109
114 [[nodiscard]] bool limit_was_reached() const noexcept
115 {
116 return steal_count_ > 0 || growth_count_ > 0;
117 }
118
120 [[nodiscard]] int active_count() const noexcept;
121
123 [[nodiscard]] int begin_note_group() noexcept { return ++next_note_group_; }
124
126 [[nodiscard]] Voice allocate(int channel, int note, int velocity, int note_group);
127
129 int release(int channel, int note) noexcept;
130
132 void free_slot(int index) noexcept
133 {
134 state_[static_cast<std::size_t>(index)] = VoiceState::free;
135 }
136
138 void reset() noexcept;
139
141 [[nodiscard]] std::vector<Voice> active() const;
142
143 [[nodiscard]] VoiceState state_of(int index) const noexcept
144 {
145 return state_[static_cast<std::size_t>(index)];
146 }
147
148 [[nodiscard]] int channel_of(int index) const noexcept
149 {
150 return channel_[static_cast<std::size_t>(index)];
151 }
152
153 [[nodiscard]] int note_of(int index) const noexcept
154 {
155 return note_[static_cast<std::size_t>(index)];
156 }
157
158 [[nodiscard]] int velocity_of(int index) const noexcept
159 {
160 return velocity_[static_cast<std::size_t>(index)];
161 }
162
163 [[nodiscard]] int note_group_of(int index) const noexcept
164 {
165 return note_group_[static_cast<std::size_t>(index)];
166 }
167
168private:
169 [[nodiscard]] int find_free() const noexcept;
170 [[nodiscard]] int find_oldest(VoiceState state) const noexcept;
171 void steal_group(int note_group, int except);
172
174 [[nodiscard]] int grow();
175
176 // Vectors rather than arrays: the capacity is a run-time choice now. The struct-of-arrays
177 // shape is unchanged, which is what the render loop cares about.
178 std::vector<VoiceState> state_;
179 std::vector<int> channel_;
180 std::vector<int> note_;
181 std::vector<int> velocity_;
182 std::vector<int> note_group_;
183 std::vector<std::int64_t> sequence_;
184
185 int steal_count_ = 0;
186 int growth_count_ = 0;
187 int capacity_ = default_polyphony;
188 int high_water_ = default_polyphony;
189 bool growing_ = false;
190 std::int64_t counter_ = 0;
191 int next_note_group_ = 0;
192};
193
194inline VoiceState Voice::state() const noexcept
195{
196 return pool->state_of(index);
197}
198
199inline int Voice::channel() const noexcept
200{
201 return pool->channel_of(index);
202}
203
204inline int Voice::note() const noexcept
205{
206 return pool->note_of(index);
207}
208
209inline int Voice::velocity() const noexcept
210{
211 return pool->velocity_of(index);
212}
213
214inline int Voice::note_group() const noexcept
215{
216 return pool->note_group_of(index);
217}
218
219} // namespace ts
The voice allocator.
Definition voice_pool.hpp:55
int steal_count() const noexcept
How many times a sounding voice was taken to make room.
Definition voice_pool.hpp:105
void reset() noexcept
Frees every voice immediately.
std::vector< Voice > active() const
The sounding voices.
bool grows() const noexcept
Whether this pool grows rather than steals.
Definition voice_pool.hpp:94
VoicePool(int polyphony, bool growing)
Creates a pool with a given polyphony, optionally allowed to grow past it.
int growth_count() const noexcept
How many times a growing pool allocated another chunk.
Definition voice_pool.hpp:108
VoicePool()
Creates a pool with the hardware's polyphony.
Definition voice_pool.hpp:79
int high_water() const noexcept
The largest capacity reached, which is what a growing render actually needed.
Definition voice_pool.hpp:97
std::function< void(int)> stealing
Called with the index of each slot taken from a sounding note, before it is reassigned.
Definition voice_pool.hpp:76
int begin_note_group() noexcept
Starts a new note group, to be shared by that note's partials.
Definition voice_pool.hpp:123
int note_of(int index) const noexcept
Definition voice_pool.hpp:153
int capacity() const noexcept
How many slots exist now. A growing pool's capacity rises during a render.
Definition voice_pool.hpp:91
int channel_of(int index) const noexcept
Definition voice_pool.hpp:148
int velocity_of(int index) const noexcept
Definition voice_pool.hpp:158
int release(int channel, int note) noexcept
Moves every held voice for a note into its release, and reports how many.
static constexpr int growth_chunk
How many slots a growing pool adds each time it would otherwise steal.
Definition voice_pool.hpp:67
static constexpr int group_size
Voices are rendered in groups of this size; the engine's layout is SIMD-shaped.
Definition voice_pool.hpp:70
void free_slot(int index) noexcept
Frees a voice whose release has finished.
Definition voice_pool.hpp:132
Voice allocate(int channel, int note, int velocity, int note_group)
Allocates one voice, stealing if necessary.
int active_count() const noexcept
Number of slots currently sounding.
static constexpr int default_polyphony
The hardware's polyphony, and this pool's default.
Definition voice_pool.hpp:58
static constexpr int max_voices
Kept as the name the rest of the engine sizes its scratch by.
Definition voice_pool.hpp:61
VoiceState state_of(int index) const noexcept
Definition voice_pool.hpp:143
int note_group_of(int index) const noexcept
Definition voice_pool.hpp:163
bool limit_was_reached() const noexcept
Whether this render's output depends on the polyphony setting.
Definition voice_pool.hpp:114
Definition control_decode.hpp:7
VoiceState
What a voice slot is currently doing.
Definition voice_pool.hpp:11
@ held
Sounding with the key still down.
Definition voice_pool.hpp:15
@ releasing
Sounding through its release after note-off.
Definition voice_pool.hpp:17
@ free
Unused and available.
Definition voice_pool.hpp:13
@ channel
A channel voice message.
Definition smf_reader.hpp:15
A handle to one voice slot: an index into the pool's parallel arrays plus the control-rate operations...
Definition voice_pool.hpp:29
int note_group() const noexcept
The note group this voice belongs to; partials of one note share it.
Definition voice_pool.hpp:214
bool is_active() const noexcept
Definition voice_pool.hpp:40
VoiceState state() const noexcept
Definition voice_pool.hpp:194
int note() const noexcept
Definition voice_pool.hpp:204
int channel() const noexcept
Definition voice_pool.hpp:199
int index
Definition voice_pool.hpp:31
const VoicePool * pool
Definition voice_pool.hpp:30
int velocity() const noexcept
Definition voice_pool.hpp:209