Skip to content

Commit

Permalink
BiquadFilter: don't use the shared coefficient buffers when params ar…
Browse files Browse the repository at this point in the history
…e polyphonic (#31)
  • Loading branch information
attilammagyar committed Aug 18, 2024
1 parent 27bf33e commit d24fd3f
Show file tree
Hide file tree
Showing 5 changed files with 156 additions and 24 deletions.
30 changes: 15 additions & 15 deletions src/dsp/biquad_filter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -681,8 +681,8 @@ bool BiquadFilter<InputSignalProducerClass, fixed_type>::initialize_low_pass_ren
);
can_use_shared_coefficients = (
can_use_shared_coefficients
&& frequency.get_envelope() == NULL
&& q.get_envelope() == NULL
&& !frequency.is_polyphonic()
&& !q.is_polyphonic()
);

FloatParamS::produce_if_not_constant(gain, round, sample_count);
Expand Down Expand Up @@ -789,8 +789,8 @@ bool BiquadFilter<InputSignalProducerClass, fixed_type>::initialize_high_pass_re
);
can_use_shared_coefficients = (
can_use_shared_coefficients
&& frequency.get_envelope() == NULL
&& q.get_envelope() == NULL
&& !frequency.is_polyphonic()
&& !q.is_polyphonic()
);

FloatParamS::produce_if_not_constant(gain, round, sample_count);
Expand Down Expand Up @@ -895,8 +895,8 @@ bool BiquadFilter<InputSignalProducerClass, fixed_type>::initialize_band_pass_re
);
can_use_shared_coefficients = (
can_use_shared_coefficients
&& frequency.get_envelope() == NULL
&& q.get_envelope() == NULL
&& !frequency.is_polyphonic()
&& !q.is_polyphonic()
);

FloatParamS::produce_if_not_constant(gain, round, sample_count);
Expand Down Expand Up @@ -1000,8 +1000,8 @@ bool BiquadFilter<InputSignalProducerClass, fixed_type>::initialize_notch_render
);
can_use_shared_coefficients = (
can_use_shared_coefficients
&& frequency.get_envelope() == NULL
&& q.get_envelope() == NULL
&& !frequency.is_polyphonic()
&& !q.is_polyphonic()
);

FloatParamS::produce_if_not_constant(gain, round, sample_count);
Expand Down Expand Up @@ -1108,9 +1108,9 @@ bool BiquadFilter<InputSignalProducerClass, fixed_type>::initialize_peaking_rend
);
can_use_shared_coefficients = (
can_use_shared_coefficients
&& frequency.get_envelope() == NULL
&& q.get_envelope() == NULL
&& gain.get_envelope() == NULL
&& !frequency.is_polyphonic()
&& !q.is_polyphonic()
&& !gain.is_polyphonic()
);

if (are_coefficients_constant) {
Expand Down Expand Up @@ -1240,8 +1240,8 @@ bool BiquadFilter<InputSignalProducerClass, fixed_type>::initialize_low_shelf_re
);
can_use_shared_coefficients = (
can_use_shared_coefficients
&& frequency.get_envelope() == NULL
&& gain.get_envelope() == NULL
&& !frequency.is_polyphonic()
&& !gain.is_polyphonic()
);

FloatParamS::produce_if_not_constant(q, round, sample_count);
Expand Down Expand Up @@ -1365,8 +1365,8 @@ bool BiquadFilter<InputSignalProducerClass, fixed_type>::initialize_high_shelf_r
);
can_use_shared_coefficients = (
can_use_shared_coefficients
&& frequency.get_envelope() == NULL
&& gain.get_envelope() == NULL
&& !frequency.is_polyphonic()
&& !gain.is_polyphonic()
);

FloatParamS::produce_if_not_constant(q, round, sample_count);
Expand Down
21 changes: 13 additions & 8 deletions src/dsp/param.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -636,19 +636,24 @@ Number FloatParam<evaluation>::get_value() const noexcept


template<ParamEvaluation evaluation>
bool FloatParam<evaluation>::is_following_leader() const noexcept
bool FloatParam<evaluation>::is_polyphonic() const noexcept
{
if (leader == NULL) {
return false;
if (leader != NULL) {
return leader->is_polyphonic();
}

if (leader->get_envelope() != NULL) {
return envelope != NULL || has_lfo_with_envelope();
}


template<ParamEvaluation evaluation>
bool FloatParam<evaluation>::is_following_leader() const noexcept
{
if (leader == NULL) {
return false;
}

LFO* const lfo = leader->get_lfo();

return lfo == NULL || !lfo->has_envelope();
return !leader->is_polyphonic();
}


Expand Down Expand Up @@ -1650,7 +1655,7 @@ bool FloatParam<evaluation>::has_lfo_with_envelope() const noexcept
return leader->has_lfo_with_envelope();
}

return lfo != NULL && lfo->has_envelope();
return envelope_state != NULL && lfo != NULL && lfo->has_envelope();
}


Expand Down
2 changes: 2 additions & 0 deletions src/dsp/param.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,8 @@ class FloatParam : public Param<Number, evaluation>

bool has_lfo_with_envelope() const noexcept;

bool is_polyphonic() const noexcept;

void start_envelope(
Seconds const time_offset,
Number const random_1,
Expand Down
89 changes: 88 additions & 1 deletion tests/test_biquad_filter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -706,7 +706,7 @@ class OtherSumOfSines : public SumOfSines
};


TEST(when_no_params_have_envelopes_then_uses_cached_coefficients, {
TEST(when_no_params_are_polyphonic_then_uses_cached_coefficients, {
BiquadFilterSharedBuffers shared_buffers;
SumOfSines input(0.33, 440.0, 0.33, 3520.0, 0.33, 7040.0, CHANNELS);
SumOfSines expected_clones(0.0, 440.0, 0.33, 3520.0, 0.0, 7040.0, CHANNELS);
Expand Down Expand Up @@ -755,6 +755,93 @@ TEST(when_no_params_have_envelopes_then_uses_cached_coefficients, {
})


TEST(when_params_are_polyphonic_then_does_not_use_cached_coefficients, {
/* Compensate for the headroom of the bandwidth-limited LFO square wave. */
constexpr Number headroom = 1.1;

BiquadFilterSharedBuffers shared_buffers;
SumOfSines input(0.33, 440.0, 0.33, 3520.0, 0.33, 7040.0, CHANNELS);
SumOfSines expected_1(0.0, 440.0, 0.0, 3520.0, 0.33, 7040.0, CHANNELS);
SumOfSines expected_2(0.33, 440.0, 0.0, 3520.0, 0.0, 7040.0, CHANNELS);
Envelope envelope("ENV");
Envelope* envelopes[Constants::ENVELOPES] = {
&envelope, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL,
};
BiquadFilterTypeParam filter_type("TYP");
FloatParamS frequency(
"FRQ",
Constants::BIQUAD_FILTER_FREQUENCY_MIN,
Constants::BIQUAD_FILTER_FREQUENCY_MAX,
Constants::BIQUAD_FILTER_FREQUENCY_DEFAULT,
0.0,
envelopes
);
FloatParamS q(
"Q",
Constants::BIQUAD_FILTER_Q_MIN,
Constants::BIQUAD_FILTER_Q_MAX,
Constants::BIQUAD_FILTER_Q_DEFAULT,
0.0,
envelopes
);
FloatParamS gain(
"G",
Constants::BIQUAD_FILTER_GAIN_MIN,
Constants::BIQUAD_FILTER_GAIN_MAX,
Constants::BIQUAD_FILTER_GAIN_DEFAULT,
0.0,
envelopes
);
LFO lfo("LFO", true);
BiquadFilter<SumOfSines> filter_1(
input, filter_type, frequency, q, gain, &shared_buffers
);
BiquadFilter<SumOfSines> filter_2(
input, filter_type, frequency, q, gain, &shared_buffers
);

lfo.waveform.set_value(LFO::Oscillator_::SQUARE);
lfo.frequency.set_value(Constants::LFO_FREQUENCY_MIN);
lfo.phase.set_value(0.1);
lfo.min.set_value(0.0);
lfo.max.set_value(1.0);
lfo.amount.set_value(1.0);
lfo.amount_envelope.set_value(0.0);
lfo.start(0.0);

envelope.initial_value.set_value(1.0);
envelope.peak_value.set_value(1.0);
envelope.sustain_value.set_value(1.0);
envelope.final_value.set_value(1.0);

filter_type.set_value(BiquadFilter<SumOfSines>::BAND_PASS);
frequency.set_lfo(&lfo);
q.set_value(5.0);

shared_buffers.b0_buffer = new Sample[BLOCK_SIZE];
shared_buffers.b1_buffer = new Sample[BLOCK_SIZE];
shared_buffers.b2_buffer = new Sample[BLOCK_SIZE];
shared_buffers.a1_buffer = new Sample[BLOCK_SIZE];
shared_buffers.a2_buffer = new Sample[BLOCK_SIZE];

envelope.amount.set_value(filter_1.frequency.value_to_ratio(7040.0) * headroom);
filter_1.frequency.start_envelope(0.0, 0.0, 0.0);

envelope.amount.set_value(filter_2.frequency.value_to_ratio(440.0) * headroom);
filter_2.frequency.start_envelope(0.0, 0.0, 0.0);

test_filter(filter_1, input, expected_1, 0.11, 1, BLOCK_SIZE);
test_filter(filter_2, input, expected_2, 0.11, 1, BLOCK_SIZE);

delete[] shared_buffers.b0_buffer;
delete[] shared_buffers.b1_buffer;
delete[] shared_buffers.b2_buffer;
delete[] shared_buffers.a1_buffer;
delete[] shared_buffers.a2_buffer;
})


void test_fast_path_continuity(
Integer const block_size,
Integer const batch_size,
Expand Down
38 changes: 38 additions & 0 deletions tests/test_param.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3242,6 +3242,44 @@ TEST(can_tell_if_envelope_has_decayed, {
})


TEST(envelopes_and_lfo_envelopes_make_a_float_param_polyphonic, {
Envelope envelope("E");
Envelope* envelopes[Constants::ENVELOPES] = {
&envelope, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL,
};
LFO lfo("L");
FloatParamS leader("F", 0.0, 1.0, 0.5, 0.0, envelopes);
FloatParamS follower(leader);
FloatParamS non_poly_float_param("N", 0.0, 1.0, 0.5);

assert_false(leader.is_polyphonic(), "no controller");
assert_false(follower.is_polyphonic(), "no controller");
assert_false(non_poly_float_param.is_polyphonic(), "no controller");

leader.set_envelope(&envelope);
non_poly_float_param.set_envelope(&envelope);
assert_true(leader.is_polyphonic(), "envelope");
assert_true(follower.is_polyphonic(), "envelope");
assert_false(non_poly_float_param.is_polyphonic(), "envelope");
leader.set_envelope(NULL);
non_poly_float_param.set_envelope(NULL);

leader.set_lfo(&lfo);
non_poly_float_param.set_lfo(&lfo);
assert_false(leader.is_polyphonic(), "LFO without amount envelope");
assert_false(follower.is_polyphonic(), "LFO without amount envelope");
assert_false(non_poly_float_param.is_polyphonic(), "LFO without amount envelope");

lfo.amount_envelope.set_value(0);
assert_true(leader.is_polyphonic(), "LFO with amount envelope");
assert_true(follower.is_polyphonic(), "LFO with amount envelope");
assert_false(non_poly_float_param.is_polyphonic(), "LFO with amount envelope");
leader.set_lfo(NULL);
non_poly_float_param.set_lfo(NULL);
})


class Modulator : public SignalProducer
{
friend class SignalProducer;
Expand Down

0 comments on commit d24fd3f

Please sign in to comment.