Skip to content

Commit bf0aa02

Browse files
committed
templatize sidechain send checks
1 parent f9a6001 commit bf0aa02

5 files changed

Lines changed: 65 additions & 46 deletions

File tree

src/dsp/engine_processor.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,12 @@ class EngineProcessor {
7272
float* output_frame = &output_samples[kStereoChannelCount * frame];
7373

7474
for (const auto& [_, processor] : instruments) {
75-
processor->Process(delay_frame, sidechain_frame, true, output_frame);
75+
processor->Process<true>(delay_frame, sidechain_frame, output_frame);
7676
}
7777
sidechain_.Process(sidechain_frame, current_params_.sidechain_mix,
7878
current_params_.sidechain_threshold_db, current_params_.sidechain_ratio);
7979
for (const auto& [_, processor] : instruments) {
80-
processor->Process(delay_frame, sidechain_frame, false, output_frame);
80+
processor->Process<false>(delay_frame, sidechain_frame, output_frame);
8181
}
8282

8383
delay_filter_.Process(delay_frame, output_frame, current_params_.delay_mix,

src/dsp/instrument_processor.cpp

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,38 +16,39 @@ namespace barely {
1616

1717
namespace {
1818

19-
template <SliceMode kSliceMode>
19+
template <SliceMode kSliceMode, bool kIsSidechainSend>
2020
VoiceCallback GetVoiceCallback(OscMode osc_mode) noexcept {
2121
switch (osc_mode) {
2222
case OscMode::kMix:
23-
return Voice::Process<OscMode::kMix, kSliceMode>;
23+
return Voice::Process<OscMode::kMix, kSliceMode, kIsSidechainSend>;
2424
case OscMode::kAm:
25-
return Voice::Process<OscMode::kAm, kSliceMode>;
25+
return Voice::Process<OscMode::kAm, kSliceMode, kIsSidechainSend>;
2626
case OscMode::kEnvelopeFollower:
27-
return Voice::Process<OscMode::kEnvelopeFollower, kSliceMode>;
27+
return Voice::Process<OscMode::kEnvelopeFollower, kSliceMode, kIsSidechainSend>;
2828
case OscMode::kFm:
29-
return Voice::Process<OscMode::kFm, kSliceMode>;
29+
return Voice::Process<OscMode::kFm, kSliceMode, kIsSidechainSend>;
3030
case OscMode::kMf:
31-
return Voice::Process<OscMode::kMf, kSliceMode>;
31+
return Voice::Process<OscMode::kMf, kSliceMode, kIsSidechainSend>;
3232
case OscMode::kRing:
33-
return Voice::Process<OscMode::kRing, kSliceMode>;
33+
return Voice::Process<OscMode::kRing, kSliceMode, kIsSidechainSend>;
3434
default:
3535
assert(!"Invalid oscillator mode");
36-
return Voice::Process<OscMode::kMix, kSliceMode>;
36+
return Voice::Process<OscMode::kMix, kSliceMode, kIsSidechainSend>;
3737
}
3838
}
3939

40+
template <bool kIsSidechainSend>
4041
VoiceCallback GetVoiceCallback(OscMode osc_mode, SliceMode slice_mode) noexcept {
4142
switch (slice_mode) {
4243
case SliceMode::kSustain:
43-
return GetVoiceCallback<SliceMode::kSustain>(osc_mode);
44+
return GetVoiceCallback<SliceMode::kSustain, kIsSidechainSend>(osc_mode);
4445
case SliceMode::kLoop:
45-
return GetVoiceCallback<SliceMode::kLoop>(osc_mode);
46+
return GetVoiceCallback<SliceMode::kLoop, kIsSidechainSend>(osc_mode);
4647
case SliceMode::kOnce:
47-
return GetVoiceCallback<SliceMode::kOnce>(osc_mode);
48+
return GetVoiceCallback<SliceMode::kOnce, kIsSidechainSend>(osc_mode);
4849
default:
4950
assert(!"Invalid slice mode");
50-
return GetVoiceCallback<SliceMode::kSustain>(osc_mode);
51+
return GetVoiceCallback<SliceMode::kSustain, kIsSidechainSend>(osc_mode);
5152
}
5253
}
5354

@@ -110,7 +111,8 @@ void InstrumentProcessor::SetControl(ControlType type, float value) noexcept {
110111
break;
111112
case ControlType::kOscMode:
112113
osc_mode_ = static_cast<OscMode>(value);
113-
voice_callback_ = GetVoiceCallback(osc_mode_, slice_mode_);
114+
voice_callback_send_ = GetVoiceCallback<true>(osc_mode_, slice_mode_);
115+
voice_callback_receive_ = GetVoiceCallback<false>(osc_mode_, slice_mode_);
114116
break;
115117
case ControlType::kOscNoiseMix:
116118
params_.voice_params.osc_noise_mix = value;
@@ -128,7 +130,8 @@ void InstrumentProcessor::SetControl(ControlType type, float value) noexcept {
128130
break;
129131
case ControlType::kSliceMode:
130132
slice_mode_ = static_cast<SliceMode>(value);
131-
voice_callback_ = GetVoiceCallback(osc_mode_, slice_mode_);
133+
voice_callback_send_ = GetVoiceCallback<true>(osc_mode_, slice_mode_);
134+
voice_callback_receive_ = GetVoiceCallback<false>(osc_mode_, slice_mode_);
132135
break;
133136
case ControlType::kBitCrusherDepth:
134137
// Offset the bit depth by 1 to normalize the range.

src/dsp/instrument_processor.h

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,21 @@ class InstrumentProcessor {
3030

3131
/// Processes the next output samples.
3232
///
33+
/// @tparam kIsSidechainSend Denotes whether the sidechain frame is for send or receive.
3334
/// @param delay_frame Delay send frame.
3435
/// @param sidechain_frame Sidechain send frame.
35-
/// @param is_sidechain_send Denotes whether the sidechain frame is for send or receive.
3636
/// @param output_frame Output frame.
37-
// TODO(#174): Template `is_sidechain_send`.
37+
template <bool kIsSidechainSend = false>
3838
void Process(float delay_frame[kStereoChannelCount], float sidechain_frame[kStereoChannelCount],
39-
bool is_sidechain_send, float output_frame[kStereoChannelCount]) noexcept {
39+
float output_frame[kStereoChannelCount]) noexcept {
4040
for (VoiceState& voice_state : voice_states_) {
41-
voice_callback_(voice_state.voice, params_, delay_frame, sidechain_frame, is_sidechain_send,
42-
output_frame);
41+
if constexpr (kIsSidechainSend) {
42+
voice_callback_send_(voice_state.voice, params_, delay_frame, sidechain_frame,
43+
output_frame);
44+
} else {
45+
voice_callback_receive_(voice_state.voice, params_, delay_frame, sidechain_frame,
46+
output_frame);
47+
}
4348
}
4449
}
4550

@@ -90,7 +95,8 @@ class InstrumentProcessor {
9095
// Acquires a new voice.
9196
Voice& AcquireVoice(float pitch) noexcept;
9297

93-
VoiceCallback voice_callback_ = Voice::Process<OscMode::kMix, SliceMode::kSustain>;
98+
VoiceCallback voice_callback_send_ = Voice::Process<OscMode::kMix, SliceMode::kSustain, true>;
99+
VoiceCallback voice_callback_receive_ = Voice::Process<OscMode::kMix, SliceMode::kSustain, false>;
94100
std::array<VoiceState, kMaxVoiceCount> voice_states_;
95101
int voice_count_ = 8;
96102

src/dsp/instrument_processor_test.cpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,23 +40,23 @@ TEST(InstrumentProcessorTest, SingleVoice) {
4040
std::array<float, kStereoChannelCount> output;
4141

4242
output.fill(0.0f);
43-
processor.Process(delay.data(), sidechain.data(), /*is_sidechain_send=*/false, output.data());
43+
processor.Process(delay.data(), sidechain.data(), output.data());
4444
for (int channel = 0; channel < kStereoChannelCount; ++channel) {
4545
EXPECT_FLOAT_EQ(output[channel], 0.0f);
4646
}
4747

4848
processor.SetNoteOn(0.0f, kNoteControls);
4949

5050
output.fill(0.0f);
51-
processor.Process(delay.data(), sidechain.data(), /*is_sidechain_send=*/false, output.data());
51+
processor.Process(delay.data(), sidechain.data(), output.data());
5252
for (int channel = 0; channel < kStereoChannelCount; ++channel) {
5353
EXPECT_FLOAT_EQ(output[channel], 0.5f * kSamples[0]);
5454
}
5555

5656
processor.SetNoteOff(0.0f);
5757

5858
output.fill(0.0f);
59-
processor.Process(delay.data(), sidechain.data(), /*is_sidechain_send=*/false, output.data());
59+
processor.Process(delay.data(), sidechain.data(), output.data());
6060
for (int channel = 0; channel < kStereoChannelCount; ++channel) {
6161
EXPECT_FLOAT_EQ(output[channel], 0.0f);
6262
}
@@ -77,7 +77,7 @@ TEST(InstrumentProcessorTest, MaxVoices) {
7777
std::array<float, kStereoChannelCount> output;
7878

7979
output.fill(0.0f);
80-
processor.Process(delay.data(), sidechain.data(), /*is_sidechain_send=*/false, output.data());
80+
processor.Process(delay.data(), sidechain.data(), output.data());
8181
for (int channel = 0; channel < kStereoChannelCount; ++channel) {
8282
EXPECT_FLOAT_EQ(output[channel], 0.0f);
8383
}
@@ -89,7 +89,7 @@ TEST(InstrumentProcessorTest, MaxVoices) {
8989
expected_output += kSamples[i];
9090

9191
output.fill(0.0f);
92-
processor.Process(delay.data(), sidechain.data(), /*is_sidechain_send=*/false, output.data());
92+
processor.Process(delay.data(), sidechain.data(), output.data());
9393
for (int channel = 0; channel < kStereoChannelCount; ++channel) {
9494
EXPECT_FLOAT_EQ(output[channel], 0.5f * expected_output) << i;
9595
}
@@ -99,7 +99,7 @@ TEST(InstrumentProcessorTest, MaxVoices) {
9999
processor.SetNoteOn(static_cast<float>(kVoiceCount), kNoteControls);
100100

101101
output.fill(0.0f);
102-
processor.Process(delay.data(), sidechain.data(), /*is_sidechain_send=*/false, output.data());
102+
processor.Process(delay.data(), sidechain.data(), output.data());
103103
for (int channel = 0; channel < kStereoChannelCount; ++channel) {
104104
EXPECT_FLOAT_EQ(output[channel], 0.5f * expected_output) << i;
105105
}
@@ -120,15 +120,15 @@ TEST(InstrumentProcessorTest, NoVoice) {
120120
std::array<float, kStereoChannelCount> output;
121121

122122
output.fill(0.0f);
123-
processor.Process(delay.data(), sidechain.data(), /*is_sidechain_send=*/false, output.data());
123+
processor.Process(delay.data(), sidechain.data(), output.data());
124124
for (int channel = 0; channel < kStereoChannelCount; ++channel) {
125125
EXPECT_FLOAT_EQ(output[channel], 0.0f);
126126
}
127127

128128
processor.SetNoteOn(0.0f, kNoteControls);
129129

130130
output.fill(0.0f);
131-
processor.Process(delay.data(), sidechain.data(), /*is_sidechain_send=*/false, output.data());
131+
processor.Process(delay.data(), sidechain.data(), output.data());
132132
for (int channel = 0; channel < kStereoChannelCount; ++channel) {
133133
EXPECT_FLOAT_EQ(output[channel], 0.0f);
134134
}

src/dsp/voice.h

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -98,19 +98,19 @@ class Voice {
9898
///
9999
/// @tparam kOscMode Oscillator mode.
100100
/// @tparam kSliceMode Slice mode.
101+
/// @param kIsSidechainSend Denotes whether the sidechain frame is for send or receive.
101102
/// @param voice Voice.
102103
/// @param params Instrument parameters.
103104
/// @param delay_frame Delay send frame.
104105
/// @param sidechain_frame Sidechain send frame.
105-
/// @param is_sidechain_send Denotes whether the sidechain frame is for send or receive.
106106
/// @param output_frame Output frame.
107-
template <OscMode kOscMode, SliceMode kSliceMode>
107+
template <OscMode kOscMode, SliceMode kSliceMode, bool kIsSidechainSend>
108108
static void Process(Voice& voice, const InstrumentParams& params,
109109
float delay_frame[kStereoChannelCount],
110-
float sidechain_frame[kStereoChannelCount], bool is_sidechain_send,
110+
float sidechain_frame[kStereoChannelCount],
111111
float output_frame[kStereoChannelCount]) noexcept {
112-
voice.Process<kOscMode, kSliceMode>(params, delay_frame, sidechain_frame, is_sidechain_send,
113-
output_frame);
112+
voice.Process<kOscMode, kSliceMode, kIsSidechainSend>(params, delay_frame, sidechain_frame,
113+
output_frame);
114114
}
115115

116116
/// Returns whether the voice is currently active (i.e., playing).
@@ -161,15 +161,24 @@ class Voice {
161161
void set_slice(const Slice* slice) noexcept { slice_ = slice; }
162162

163163
private:
164-
template <OscMode kOscMode, SliceMode kSliceMode>
164+
template <OscMode kOscMode, SliceMode kSliceMode, bool kIsSidechainSend>
165165
void Process(const InstrumentParams& params, float delay_frame[kStereoChannelCount],
166-
float sidechain_frame[kStereoChannelCount], bool is_sidechain_send,
166+
float sidechain_frame[kStereoChannelCount],
167167
float output_frame[kStereoChannelCount]) noexcept {
168-
if (!IsActive() || ((is_sidechain_send && params_.sidechain_send <= 0.0f) ||
169-
(!is_sidechain_send && params_.sidechain_send > 0.0f))) {
168+
if (!IsActive()) {
170169
return;
171170
}
172171

172+
if constexpr (kIsSidechainSend) {
173+
if (params_.sidechain_send <= 0.0f) {
174+
return;
175+
}
176+
} else {
177+
if (params_.sidechain_send > 0.0f) {
178+
return;
179+
}
180+
}
181+
173182
if constexpr (kSliceMode == SliceMode::kOnce) {
174183
if (!IsSliceActive()) {
175184
envelope_.Stop();
@@ -234,13 +243,15 @@ class Voice {
234243
float left_output = left_gain * output;
235244
float right_output = right_gain * output;
236245

237-
if (is_sidechain_send) {
246+
if constexpr (kIsSidechainSend) {
238247
sidechain_frame[0] += params_.sidechain_send * left_output;
239248
sidechain_frame[1] += params_.sidechain_send * right_output;
240-
} else if (params_.sidechain_send < 0.0f) {
241-
const float sidechain_send = -params_.sidechain_send;
242-
left_output = std::lerp(left_output, sidechain_frame[0] * left_output, sidechain_send);
243-
right_output = std::lerp(right_output, sidechain_frame[1] * right_output, sidechain_send);
249+
} else {
250+
if (params_.sidechain_send < 0.0f) {
251+
const float sidechain_send = -params_.sidechain_send;
252+
left_output = std::lerp(left_output, sidechain_frame[0] * left_output, sidechain_send);
253+
right_output = std::lerp(right_output, sidechain_frame[1] * right_output, sidechain_send);
254+
}
244255
}
245256

246257
delay_frame[0] += params_.delay_send * left_output;
@@ -296,11 +307,10 @@ class Voice {
296307
/// @param params Instrument parameters.
297308
/// @param delay_frame Delay send frame.
298309
/// @param sidechain_frame Sidechain send frame.
299-
/// @param is_sidechain_send Denotes whether the sidechain frame is for send or receive.
300310
/// @param output_frame Output frame.
301311
using VoiceCallback = void (*)(Voice& voice, const InstrumentParams& params,
302312
float delay_frame[kStereoChannelCount],
303-
float sidechain_frame[kStereoChannelCount], bool is_sidechain_send,
313+
float sidechain_frame[kStereoChannelCount],
304314
float output_frame[kStereoChannelCount]);
305315

306316
} // namespace barely

0 commit comments

Comments
 (0)