Serenity Operating System
1/*
2 * Copyright (c) 2021, kleines Filmröllchen <filmroellchen@serenityos.org>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include "Effects.h"
8#include <AK/FixedArray.h>
9#include <math.h>
10
11namespace DSP::Effects {
12
13Delay::Delay(NonnullRefPtr<Transport> transport)
14 : EffectProcessor(move(transport))
15 , m_delay_decay("Decay"_short_string, 0.01, 0.99, 0.33, Logarithmic::No)
16 , m_delay_time("Delay Time"_string, 3, 2000, 900, Logarithmic::Yes)
17 , m_dry_gain("Dry"_short_string, 0, 1, 0.9, Logarithmic::No)
18{
19
20 m_parameters.append(m_delay_decay);
21 m_parameters.append(m_delay_time);
22 m_parameters.append(m_dry_gain);
23
24 m_delay_time.register_change_listener([this](auto const&) {
25 this->handle_delay_time_change();
26 });
27 handle_delay_time_change();
28}
29
30void Delay::handle_delay_time_change()
31{
32 // We want a delay buffer that can hold samples filling the specified number of milliseconds.
33 double seconds = static_cast<double>(m_delay_time) / 1000.0;
34 size_t sample_count = ceil(seconds * m_transport->sample_rate());
35 if (sample_count != m_delay_buffer.size()) {
36 m_delay_buffer.resize(sample_count, true);
37 m_delay_index %= max(m_delay_buffer.size(), 1);
38 }
39}
40
41void Delay::process_impl(Signal const& input_signal, Signal& output_signal)
42{
43 auto const& samples = input_signal.get<FixedArray<Sample>>();
44 auto& output = output_signal.get<FixedArray<Sample>>();
45 for (size_t i = 0; i < output.size(); ++i) {
46 auto& out = output[i];
47 auto const& sample = samples[i];
48 out += sample.log_multiplied(static_cast<double>(m_dry_gain));
49 out += m_delay_buffer[m_delay_index].log_multiplied(m_delay_decay);
50
51 // This is also convenient for disabling the delay effect by setting the buffer size to 0
52 if (m_delay_buffer.size() >= 1)
53 m_delay_buffer[m_delay_index++] = out;
54
55 if (m_delay_index >= m_delay_buffer.size())
56 m_delay_index = 0;
57 }
58}
59
60Mastering::Mastering(NonnullRefPtr<Transport> transport)
61 : EffectProcessor(move(transport))
62 , m_pan("Pan"_short_string, -1, 1, 0, Logarithmic::No)
63 , m_volume("Volume"_short_string, 0, 1, 1, Logarithmic::No)
64 , m_muted("Mute"_short_string, false)
65{
66 m_parameters.append(m_muted);
67 m_parameters.append(m_volume);
68 m_parameters.append(m_pan);
69}
70
71void Mastering::process_impl(Signal const& input_signal, Signal& output)
72{
73 process_to_fixed_array(input_signal, output.get<FixedArray<Sample>>());
74}
75
76void Mastering::process_to_fixed_array(Signal const& input_signal, FixedArray<Sample>& output)
77{
78 if (m_muted) {
79 output.fill_with({});
80 return;
81 }
82
83 auto const& input = input_signal.get<FixedArray<Sample>>();
84 for (size_t i = 0; i < input.size(); ++i) {
85 auto sample = input[i];
86 sample.log_multiply(static_cast<float>(m_volume));
87 sample.pan(static_cast<float>(m_pan));
88 output[i] = sample;
89 }
90}
91
92}