Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 * Copyright (c) 2019-2020, William McPherson <willmcpherson2@gmail.com>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright notice, this
10 * list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include "KnobsWidget.h"
29#include "AudioEngine.h"
30#include "MainWidget.h"
31#include <LibGUI/BoxLayout.h>
32#include <LibGUI/Label.h>
33#include <LibGUI/Slider.h>
34
35KnobsWidget::KnobsWidget(AudioEngine& audio_engine, MainWidget& main_widget)
36 : m_audio_engine(audio_engine)
37 , m_main_widget(main_widget)
38{
39 set_layout<GUI::VerticalBoxLayout>();
40 set_fill_with_background_color(true);
41
42 m_labels_container = add<GUI::Widget>();
43 m_labels_container->set_layout<GUI::HorizontalBoxLayout>();
44 m_labels_container->set_size_policy(GUI::SizePolicy::Fill, GUI::SizePolicy::Fixed);
45 m_labels_container->set_preferred_size(0, 20);
46
47 m_octave_label = m_labels_container->add<GUI::Label>("Octave");
48 m_wave_label = m_labels_container->add<GUI::Label>("Wave");
49 m_attack_label = m_labels_container->add<GUI::Label>("Attack");
50 m_decay_label = m_labels_container->add<GUI::Label>("Decay");
51 m_sustain_label = m_labels_container->add<GUI::Label>("Sustain");
52 m_release_label = m_labels_container->add<GUI::Label>("Release");
53 m_delay_label = m_labels_container->add<GUI::Label>("Delay");
54
55 m_values_container = add<GUI::Widget>();
56 m_values_container->set_layout<GUI::HorizontalBoxLayout>();
57 m_values_container->set_size_policy(GUI::SizePolicy::Fill, GUI::SizePolicy::Fixed);
58 m_values_container->set_preferred_size(0, 10);
59
60 m_octave_value = m_values_container->add<GUI::Label>(String::number(m_audio_engine.octave()));
61 m_wave_value = m_values_container->add<GUI::Label>(wave_strings[m_audio_engine.wave()]);
62 m_attack_value = m_values_container->add<GUI::Label>(String::number(m_audio_engine.attack()));
63 m_decay_value = m_values_container->add<GUI::Label>(String::number(m_audio_engine.decay()));
64 m_sustain_value = m_values_container->add<GUI::Label>(String::number(m_audio_engine.sustain()));
65 m_release_value = m_values_container->add<GUI::Label>(String::number(m_audio_engine.release()));
66 m_delay_value = m_values_container->add<GUI::Label>(String::number(m_audio_engine.delay()));
67
68 m_knobs_container = add<GUI::Widget>();
69 m_knobs_container->set_layout<GUI::HorizontalBoxLayout>();
70
71 // FIXME: Implement vertical flipping in GUI::Slider, not here.
72
73 m_octave_knob = m_knobs_container->add<GUI::VerticalSlider>();
74 m_octave_knob->set_tooltip("Z: octave down, X: octave up");
75 m_octave_knob->set_range(octave_min - 1, octave_max - 1);
76 m_octave_knob->set_value((octave_max - 1) - (m_audio_engine.octave() - 1));
77 m_octave_knob->on_value_changed = [this](int value) {
78 int new_octave = octave_max - value;
79 if (m_change_octave)
80 m_main_widget.set_octave_and_ensure_note_change(new_octave == m_audio_engine.octave() + 1 ? Up : Down);
81 ASSERT(new_octave == m_audio_engine.octave());
82 m_octave_value->set_text(String::number(new_octave));
83 };
84
85 m_wave_knob = m_knobs_container->add<GUI::VerticalSlider>();
86 m_wave_knob->set_tooltip("C: cycle through waveforms");
87 m_wave_knob->set_range(0, last_wave);
88 m_wave_knob->set_value(last_wave - m_audio_engine.wave());
89 m_wave_knob->on_value_changed = [this](int value) {
90 int new_wave = last_wave - value;
91 m_audio_engine.set_wave(new_wave);
92 ASSERT(new_wave == m_audio_engine.wave());
93 m_wave_value->set_text(wave_strings[new_wave]);
94 };
95
96 constexpr int max_attack = 1000;
97 m_attack_knob = m_knobs_container->add<GUI::VerticalSlider>();
98 m_attack_knob->set_range(0, max_attack);
99 m_attack_knob->set_value(max_attack - m_audio_engine.attack());
100 m_attack_knob->set_step(100);
101 m_attack_knob->on_value_changed = [this](int value) {
102 int new_attack = max_attack - value;
103 m_audio_engine.set_attack(new_attack);
104 ASSERT(new_attack == m_audio_engine.attack());
105 m_attack_value->set_text(String::number(new_attack));
106 };
107
108 constexpr int max_decay = 1000;
109 m_decay_knob = m_knobs_container->add<GUI::VerticalSlider>();
110 m_decay_knob->set_range(0, max_decay);
111 m_decay_knob->set_value(max_decay - m_audio_engine.decay());
112 m_decay_knob->set_step(100);
113 m_decay_knob->on_value_changed = [this](int value) {
114 int new_decay = max_decay - value;
115 m_audio_engine.set_decay(new_decay);
116 ASSERT(new_decay == m_audio_engine.decay());
117 m_decay_value->set_text(String::number(new_decay));
118 };
119
120 constexpr int max_sustain = 1000;
121 m_sustain_knob = m_knobs_container->add<GUI::VerticalSlider>();
122 m_sustain_knob->set_range(0, max_sustain);
123 m_sustain_knob->set_value(max_sustain - m_audio_engine.sustain());
124 m_sustain_knob->set_step(100);
125 m_sustain_knob->on_value_changed = [this](int value) {
126 int new_sustain = max_sustain - value;
127 m_audio_engine.set_sustain(new_sustain);
128 ASSERT(new_sustain == m_audio_engine.sustain());
129 m_sustain_value->set_text(String::number(new_sustain));
130 };
131
132 constexpr int max_release = 1000;
133 m_release_knob = m_knobs_container->add<GUI::VerticalSlider>();
134 m_release_knob->set_range(0, max_release);
135 m_release_knob->set_value(max_release - m_audio_engine.release());
136 m_release_knob->set_step(100);
137 m_release_knob->on_value_changed = [this](int value) {
138 int new_release = max_release - value;
139 m_audio_engine.set_release(new_release);
140 ASSERT(new_release == m_audio_engine.release());
141 m_release_value->set_text(String::number(new_release));
142 };
143
144 constexpr int max_delay = 8;
145 m_delay_knob = m_knobs_container->add<GUI::VerticalSlider>();
146 m_delay_knob->set_range(0, max_delay);
147 m_delay_knob->set_value(max_delay - m_audio_engine.delay());
148 m_delay_knob->on_value_changed = [this](int value) {
149 int new_delay = max_delay - value;
150 m_audio_engine.set_delay(new_delay);
151 ASSERT(new_delay == m_audio_engine.delay());
152 m_delay_value->set_text(String::number(new_delay));
153 };
154}
155
156KnobsWidget::~KnobsWidget()
157{
158}
159
160void KnobsWidget::update_knobs()
161{
162 m_wave_knob->set_value(last_wave - m_audio_engine.wave());
163
164 // FIXME: This is needed because when the slider is changed directly, it
165 // needs to change the octave, but if the octave was changed elsewhere, we
166 // need to change the slider without changing the octave.
167 m_change_octave = false;
168 m_octave_knob->set_value(octave_max - m_audio_engine.octave());
169 m_change_octave = true;
170}