Serenity Operating System
at hosted 170 lines 7.9 kB view raw
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}