diff --git a/Userland/Libraries/LibDSP/CMakeLists.txt b/Userland/Libraries/LibDSP/CMakeLists.txt index a5de6f120e..34fa31957a 100644 --- a/Userland/Libraries/LibDSP/CMakeLists.txt +++ b/Userland/Libraries/LibDSP/CMakeLists.txt @@ -3,6 +3,7 @@ set(SOURCES Track.cpp Effects.cpp Synthesizers.cpp + Keyboard.cpp ) serenity_lib(LibDSP dsp) diff --git a/Userland/Libraries/LibDSP/Keyboard.cpp b/Userland/Libraries/LibDSP/Keyboard.cpp new file mode 100644 index 0000000000..38483243a2 --- /dev/null +++ b/Userland/Libraries/LibDSP/Keyboard.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2021-2022, kleines Filmröllchen + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "Keyboard.h" +#include "Music.h" +#include +#include + +namespace LibDSP { + +void Keyboard::set_keyboard_note(u8 pitch, Keyboard::Switch note_switch) +{ + VERIFY(pitch < note_frequencies.size()); + + if (note_switch == Switch::Off) { + m_pressed_notes.remove(pitch); + return; + } + + auto fake_note = RollNote { + .on_sample = m_transport->time(), + .off_sample = NumericLimits::max(), + .pitch = pitch, + .velocity = NumericLimits::max(), + }; + + m_pressed_notes.set(pitch, fake_note); +} +void Keyboard::set_keyboard_note_in_active_octave(i8 octave_offset, Switch note_switch) +{ + u8 real_note = octave_offset + (m_virtual_keyboard_octave - 1) * notes_per_octave; + set_keyboard_note(real_note, note_switch); +} + +void Keyboard::change_virtual_keyboard_octave(Direction direction) +{ + if (direction == Direction::Up) { + if (m_virtual_keyboard_octave < octave_max) + ++m_virtual_keyboard_octave; + } else { + if (m_virtual_keyboard_octave > octave_min) + --m_virtual_keyboard_octave; + } +} + +ErrorOr Keyboard::set_virtual_keyboard_octave(u8 octave) +{ + if (octave <= octave_max && octave >= octave_min) { + m_virtual_keyboard_octave = octave; + return {}; + } + return Error::from_string_literal("Octave out of range"); +} + +bool Keyboard::is_pressed(u8 pitch) const +{ + return m_pressed_notes.get(pitch).has_value() && m_pressed_notes.get(pitch)->is_playing(m_transport->time()); +} + +bool Keyboard::is_pressed_in_active_octave(i8 octave_offset) const +{ + return is_pressed(octave_offset + virtual_keyboard_octave_base()); +} + +} diff --git a/Userland/Libraries/LibDSP/Keyboard.h b/Userland/Libraries/LibDSP/Keyboard.h new file mode 100644 index 0000000000..fa69bd29b2 --- /dev/null +++ b/Userland/Libraries/LibDSP/Keyboard.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2021-2022, kleines Filmröllchen + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include + +namespace LibDSP { + +class Keyboard : public RefCounted { + +public: + enum class Direction : bool { + Down, + Up, + }; + enum class Switch : bool { + Off, + On, + }; + + Keyboard(NonnullRefPtr transport) + : m_transport(move(transport)) + { + } + + u8 virtual_keyboard_octave() const { return m_virtual_keyboard_octave; } + u8 virtual_keyboard_octave_base() const { return (m_virtual_keyboard_octave - octave_min) * 12; } + // Automatically clips the octave between the minimum and maximum. + void change_virtual_keyboard_octave(Direction); + // Errors out if the set octave is out of range. + ErrorOr set_virtual_keyboard_octave(u8 octave); + + void set_keyboard_note(u8 pitch, Switch note_switch); + void set_keyboard_note_in_active_octave(i8 octave_offset, Switch note_switch); + + RollNotes const& notes() const { return m_pressed_notes; } + Optional note_at(u8 pitch) const { return m_pressed_notes.get(pitch); } + bool is_pressed(u8 pitch) const; + bool is_pressed_in_active_octave(i8 octave_offset) const; + +private: + u8 m_virtual_keyboard_octave { 4 }; + RollNotes m_pressed_notes; + + NonnullRefPtr m_transport; + + static constexpr int const octave_min = 1; + static constexpr int const octave_max = 7; +}; + +} diff --git a/Userland/Libraries/LibDSP/Music.h b/Userland/Libraries/LibDSP/Music.h index e82a57b789..884189a348 100644 --- a/Userland/Libraries/LibDSP/Music.h +++ b/Userland/Libraries/LibDSP/Music.h @@ -196,6 +196,7 @@ constexpr Array note_frequencies = { 3951.0664100489994, }; +constexpr size_t const notes_per_octave = 12; constexpr double const middle_c = note_frequencies[36]; }