From 312c324ac23fff08ff368465670c770cfb287d13 Mon Sep 17 00:00:00 2001 From: Shriram Ravindranathan Date: Sat, 13 Jul 2024 18:59:03 +0530 Subject: [PATCH] Refactor Piano. Improve semantics, better separation of concerns --- src/main_screen.cpp | 27 ++++++----------- src/piano.cpp | 74 ++++++++++++++++++++++++++------------------- src/piano.hpp | 17 ++++++++--- 3 files changed, 64 insertions(+), 54 deletions(-) diff --git a/src/main_screen.cpp b/src/main_screen.cpp index f2792f1..2138890 100644 --- a/src/main_screen.cpp +++ b/src/main_screen.cpp @@ -47,20 +47,11 @@ std::shared_ptr MainScreen::Run() { return nullptr; } - // Audio - MidiAudioStream mas; - fluid_synth_t* synth = mas.getSynth(); - std::string assets_path = APP_ASSETS_PATH; - std::string sound_font_path = assets_path + "/soundfonts/TimGM6mb.sf2"; - fluid_synth_sfload(synth, sound_font_path.c_str(), 1); - fluid_synth_set_gain(synth, preferences.piano.gain); - // ImGui if (!ImGui::SFML::Init(window)) return nullptr; sf::Clock deltaClock; - // TODO: Remember choice from config.ini sf::Font font; if (!font.loadFromFile(preferences.ui.font.path)) { std::cerr << "Error loading font" << std::endl; @@ -73,12 +64,14 @@ std::shared_ptr MainScreen::Run() { title.setPosition(window.getSize().x / 2.f - title.getGlobalBounds().width / 2, 50); // Piano - Piano piano(preferences.piano.pressed_note_colors); - mas.play(); + std::string sound_font_path = std::string(APP_ASSETS_PATH) + "/soundfonts/TimGM6mb.sf2"; + Piano piano(window, preferences.piano.pressed_note_colors); + fluid_synth_sfload(piano.getSynth(), sound_font_path.c_str(), 1); + fluid_synth_set_gain(piano.getSynth(), preferences.piano.gain); // Images sf::Texture logo; - if (!logo.loadFromFile(assets_path + "/images/chordcat.png")) { + if (!logo.loadFromFile(std::string(APP_ASSETS_PATH) + "/images/chordcat.png")) { std::cerr << "Error loading logo" << std::endl; } @@ -93,13 +86,11 @@ std::shared_ptr MainScreen::Run() { switch (message[0] >> 4) { case 9: if ((int)message[2] > 0) { - piano.setKeyPressed((int)message[1], true); - fluid_synth_noteon(synth, 0, (int)message[1], (int)message[2]); + piano.keyOn((int)message[1], (int)message[2]); break; } case 8: - piano.setKeyPressed((int)message[1], false); - fluid_synth_noteoff(synth, 0, (int)message[1]); + piano.keyOff((int)message[1]); break; default: break; @@ -150,7 +141,7 @@ std::shared_ptr MainScreen::Run() { window.getSize().x / 2.f - portinfo_text.getGlobalBounds().width / 2, 100); } ImGui::SFML::ProcessEvent(event); - piano.processEvent(event, window, synth); + piano.processEvent(event); } std::vector pressed_notes = key_numbers_to_note_names(piano.getPressedNotes()); sf::String current_msg = ""; @@ -170,7 +161,7 @@ std::shared_ptr MainScreen::Run() { chord_notes_text.setPosition(window.getSize().x / 3.f, 150); // Fluid Synth Stuff - fluid_synth_set_gain(synth, preferences.piano.gain); + fluid_synth_set_gain(piano.getSynth(), preferences.piano.gain); ImGui::SFML::Update(window, deltaClock.restart()); diff --git a/src/piano.cpp b/src/piano.cpp index 3a0fa60..7406b13 100644 --- a/src/piano.cpp +++ b/src/piano.cpp @@ -1,9 +1,10 @@ // SPDX-License-Identifier: GPL-3.0-only #include "piano.hpp" #include +#include inline int getNoteFromKeyCode(sf::Keyboard::Key keycode) { - int index = 3; // start from C not A + int index = 21 + 3; // 21 is A0, 21 + 3 is C1 switch(keycode) { case sf::Keyboard::Z: index+=0; break; @@ -53,7 +54,15 @@ inline bool isBlackKey(size_t index) { } } -Piano::Piano(std::array& pressed_note_colors) : note_colors(pressed_note_colors) {} +Piano::Piano(sf::RenderWindow &window, std::array &pressed_note_colors) + : window(window), note_colors(pressed_note_colors) { + synth = mas.getSynth(); + mas.play(); +} + +fluid_synth_t* Piano::getSynth() { + return synth; +} void Piano::draw(sf::RenderTarget& target, sf::RenderStates states) const { float key_width_white = target.getView().getSize().x / 52; @@ -109,12 +118,32 @@ void Piano::draw(sf::RenderTarget& target, sf::RenderStates states) const { } } -void Piano::setKeyPressed(size_t midi_note, bool isPressed) { +void Piano::keyOn(int midi_note, int velocity) { + size_t index = midi_note - 21; // 21 is A0; + if (index < keys.size() && index >= 0 && !keys[index]) { + fluid_synth_noteon(synth, 0, midi_note, velocity); + keys[index] = true; + } +} + +void Piano::keyOff(int midi_note) { + size_t index = midi_note - 21; // 21 is A0; + if (index < keys.size() && index >= 0 && keys[index]) { + fluid_synth_noteoff(synth, 0, midi_note); + keys[index] = false; + } +} + +void Piano::keyToggle(int midi_note) { size_t index = midi_note - 21; // 21 is A0; if (index < keys.size() && index >= 0) { - keys[index] = isPressed; + keys[index] ? + fluid_synth_noteoff(synth, 0, midi_note): + fluid_synth_noteon(synth, 0, midi_note, 100); + keys[index] = !keys[index]; } } + std::vector Piano::getPressedNotes() { std::vector pressed_notes = {}; for (int i = 0; i < keys.size(); i++) { @@ -124,13 +153,12 @@ std::vector Piano::getPressedNotes() { return pressed_notes; } -void Piano::processEvent(sf::Event &event, sf::RenderWindow &window, - fluid_synth_t *synth) { - this->mouseEvent(event, window, synth); - this->keyboardEvent(event, window, synth); +void Piano::processEvent(sf::Event &event) { + this->mouseEvent(event); + this->keyboardEvent(event); } -void Piano::mouseEvent(sf::Event& event, sf::RenderWindow& window, fluid_synth_t* synth) { +void Piano::mouseEvent(sf::Event& event) { if (event.type == sf::Event::MouseButtonReleased) { if (event.mouseButton.button == sf::Mouse::Left) { sf::Vector2f worldPos = window.mapPixelToCoords(sf::Mouse::getPosition(window)); @@ -138,24 +166,14 @@ void Piano::mouseEvent(sf::Event& event, sf::RenderWindow& window, fluid_synth_t if (isBlackKey(std::distance(key_sprites.begin(), it))) if (it->getGlobalBounds().contains(worldPos)) { size_t index = std::distance(key_sprites.begin(), it); - if (keys[index]) { - fluid_synth_noteoff(synth, 0, (int)(index + 21)); - } else { - fluid_synth_noteon(synth, 0, (int)(index + 21), 100); - } - keys[index] = !keys[index]; + keyToggle(index + 21); return; } } for (auto it = key_sprites.begin(); it != key_sprites.end(); it++) { if (it->getGlobalBounds().contains(worldPos)) { size_t index = std::distance(key_sprites.begin(), it); - if (keys[index]) { - fluid_synth_noteoff(synth, 0, (int)(index + 21)); - } else { - fluid_synth_noteon(synth, 0, (int)(index + 21), 100); - } - keys[index] = !keys[index]; + keyToggle(index + 21); return; } } @@ -163,8 +181,8 @@ void Piano::mouseEvent(sf::Event& event, sf::RenderWindow& window, fluid_synth_t } } -void Piano::keyboardEvent(sf::Event& event, sf::RenderWindow& window, fluid_synth_t* synth) { - static unsigned int octave = 1; +void Piano::keyboardEvent(sf::Event& event) { + static int octave = 1; if (event.type == sf::Event::KeyPressed) { if(event.key.code == sf::Keyboard::Hyphen) { @@ -177,19 +195,13 @@ void Piano::keyboardEvent(sf::Event& event, sf::RenderWindow& window, fluid_synt } int note = getNoteFromKeyCode(event.key.code); if(note < 0) return; - size_t index = note + octave * 12; - if(index >= keys.size() || keys[index]) return; - fluid_synth_noteon(synth, 0, (int)(index + 21), 100); - keys[index] = true; + keyOn(note + octave * 12, 100); } if (event.type == sf::Event::KeyReleased) { int note = getNoteFromKeyCode(event.key.code); if(note < 0) return; - size_t index = note + octave * 12; - if(index >= keys.size() || !keys[index]) return; - fluid_synth_noteoff(synth, 0, (int)(index + 21)); - keys[index] = false; + keyOff(note + octave * 12); } } diff --git a/src/piano.hpp b/src/piano.hpp index 3deb608..5f2fa12 100644 --- a/src/piano.hpp +++ b/src/piano.hpp @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only #pragma once +#include "midi_audio_stream.hpp" #include #include #include @@ -7,18 +8,24 @@ class Piano : public sf::Drawable { public: - Piano(std::array& pressed_note_colors); - void setKeyPressed(size_t midi_note_number, bool isPressed); + Piano(sf::RenderWindow& window, std::array& pressed_note_colors); + void keyOn(int midi_note_number, int velocity); + void keyOff(int midi_note_number); + void keyToggle(int midi_note_number); void clearAllKeys(); std::vector getPressedNotes(); - void processEvent(sf::Event& event, sf::RenderWindow& window, fluid_synth_t* synth); + fluid_synth_t* getSynth(); + void processEvent(sf::Event& event); float key_aspect_ratio = 4.f; private: virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const; - void mouseEvent(sf::Event& event, sf::RenderWindow& window, fluid_synth_t* synth); - void keyboardEvent(sf::Event& event, sf::RenderWindow& window, fluid_synth_t* synth); + void mouseEvent(sf::Event& event); + void keyboardEvent(sf::Event& event); std::array keys = {}; mutable std::array key_sprites; + sf::RenderWindow& window; + MidiAudioStream mas{}; + fluid_synth_t* synth = nullptr; std::array& note_colors; };