Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
kit.hpp
Go to the documentation of this file.
1#pragma once
2
4
5#include <algorithm>
6#include <cstddef>
7#include <vector>
8
9namespace pulp::signal::drum {
10
40class Kit {
41public:
48 std::size_t add_voice(Voice* voice, int note, int choke_group = 0) {
49 slots_.push_back({voice, note, choke_group});
50 // Prepare on registration as well as in `prepare()`. Registering after
51 // the kit was prepared is a reasonable thing to do -- a caller may add
52 // a voice in response to a preset load -- and without this the new
53 // voice would run at whatever rate it was constructed with, which is
54 // audible as a wrongly-pitched drum rather than as an error.
55 if (voice != nullptr) {
56 set_output_oversampling(oversampling_);
57 voice->prepare(sample_rate_);
58 }
59 return slots_.size() - 1;
60 }
61
62 std::size_t voice_count() const { return slots_.size(); }
63
67 int latency_samples() const noexcept {
68 return OutputStage::latency_samples_for(oversampling_);
69 }
70
72 return oversampling_;
73 }
74
76 oversampling_ = factor;
77 for (auto& slot : slots_) {
78 if (slot.voice != nullptr)
79 slot.voice->set_output_oversampling(oversampling_);
80 }
81 for (const auto& slot : slots_) {
82 if (slot.voice != nullptr &&
83 slot.voice->output_oversampling() != oversampling_) {
84 // A source-compatible legacy Voice has no quality stage. Keep
85 // the entire sum honest and aligned by falling back to the
86 // zero-latency contract it reports.
87 oversampling_ = OutputOversampling::bypass;
88 for (auto& fallback_slot : slots_) {
89 if (fallback_slot.voice != nullptr)
90 fallback_slot.voice->set_output_oversampling(
91 oversampling_);
92 }
93 break;
94 }
95 }
96 }
97
98 void prepare(double sample_rate) {
99 sample_rate_ = sample_rate > 0.0 ? sample_rate : sample_rate_;
100 for (auto& slot : slots_) {
101 if (slot.voice != nullptr) {
102 slot.voice->set_output_oversampling(oversampling_);
103 slot.voice->prepare(sample_rate_);
104 }
105 }
106 }
107
109 void set_choke_fade_ms(float ms) { choke_fade_ms_ = std::max(ms, 0.1f); }
110
112 void reset() {
113 for (auto& slot : slots_) {
114 if (slot.voice != nullptr) slot.voice->reset();
115 }
116 }
117
121 void trigger(int note, float velocity) {
122 for (std::size_t i = 0; i < slots_.size(); ++i) {
123 if (slots_[i].note == note) {
124 trigger_slot(i, velocity);
125 return;
126 }
127 }
128 }
129
132 void trigger_slot(std::size_t index, float velocity) {
133 if (index >= slots_.size() || slots_[index].voice == nullptr) return;
134
135 slots_[index].voice->set_output_oversampling(oversampling_);
136 const int group = slots_[index].choke_group;
137 if (group != 0) {
138 for (std::size_t other = 0; other < slots_.size(); ++other) {
139 if (other == index || slots_[other].choke_group != group) continue;
140 if (slots_[other].voice != nullptr) {
141 slots_[other].voice->choke(choke_fade_ms_);
142 }
143 }
144 }
145 slots_[index].voice->note_on(velocity);
146 }
147
150 void choke_group(int group) {
151 if (group == 0) return;
152 for (auto& slot : slots_) {
153 if (slot.choke_group == group && slot.voice != nullptr) {
154 slot.voice->choke(choke_fade_ms_);
155 }
156 }
157 }
158
160 bool is_active() const {
161 for (const auto& slot : slots_) {
162 if (slot.voice != nullptr && slot.voice->is_active()) return true;
163 }
164 return false;
165 }
166
169 void process(float* out, int num_samples) {
170 if (out == nullptr || num_samples <= 0) return;
171 for (auto& slot : slots_) {
172 if (slot.voice != nullptr && slot.voice->is_active()) {
173 slot.voice->set_output_oversampling(oversampling_);
174 slot.voice->process(out, num_samples);
175 }
176 }
177 }
178
179private:
180 struct Slot {
181 Voice* voice = nullptr;
182 int note = 0;
183 int choke_group = 0;
184 };
185
186 std::vector<Slot> slots_;
187 double sample_rate_ = 44100.0;
188 float choke_fade_ms_ = 4.0f;
190};
191
192} // namespace pulp::signal::drum
A set of percussion voices summed into one output, with choke groups.
Definition kit.hpp:40
void choke_group(int group)
Fades out every voice in group.
Definition kit.hpp:150
void process(float *out, int num_samples)
Adds every active voice's output into out.
Definition kit.hpp:169
void reset()
Silences every voice immediately, without fades. For transport stops.
Definition kit.hpp:112
OutputOversampling output_oversampling() const noexcept
Definition kit.hpp:71
void set_output_oversampling(OutputOversampling factor)
Definition kit.hpp:75
void set_choke_fade_ms(float ms)
How long a choked voice takes to fade out, in milliseconds.
Definition kit.hpp:109
void trigger(int note, float velocity)
Triggers the voice registered under note, choking any other voice in the same group first.
Definition kit.hpp:121
bool is_active() const
Whether any voice is still rendering.
Definition kit.hpp:160
int latency_samples() const noexcept
One latency for the whole summed kit.
Definition kit.hpp:67
void trigger_slot(std::size_t index, float velocity)
Triggers by slot index, which a caller that kept the index from add_voice uses to avoid searching on ...
Definition kit.hpp:132
std::size_t add_voice(Voice *voice, int note, int choke_group=0)
Registers a voice under note, which is whatever key a caller wants to address it by – a MIDI note num...
Definition kit.hpp:48
void prepare(double sample_rate)
Definition kit.hpp:98
std::size_t voice_count() const
Definition kit.hpp:62
static constexpr int latency_samples_for(OutputOversampling factor) noexcept
Definition output_stage.hpp:97
The lifecycle every percussion voice shares.
Definition voice.hpp:52
void prepare(double sample_rate)
Fixes the sample rate and clears all state.
Definition voice.hpp:62
Definition clap.hpp:13
OutputOversampling
Realtime quality choices for the shared drum output nonlinearity.
Definition output_stage.hpp:20
@ x2
Definition output_stage.hpp:22
@ bypass
Definition output_stage.hpp:21
@ i
Definition formant_filter_bank.hpp:20