Skip to content

Commit

Permalink
Refactor Piano. Improve semantics, better separation of concerns
Browse files Browse the repository at this point in the history
  • Loading branch information
shriramters committed Jul 13, 2024
1 parent c5360e4 commit 312c324
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 54 deletions.
27 changes: 9 additions & 18 deletions src/main_screen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,20 +47,11 @@ std::shared_ptr<AppState> 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;
Expand All @@ -73,12 +64,14 @@ std::shared_ptr<AppState> 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;
}

Expand All @@ -93,13 +86,11 @@ std::shared_ptr<AppState> 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;
Expand Down Expand Up @@ -150,7 +141,7 @@ std::shared_ptr<AppState> 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<sf::String> pressed_notes = key_numbers_to_note_names(piano.getPressedNotes());
sf::String current_msg = "";
Expand All @@ -170,7 +161,7 @@ std::shared_ptr<AppState> 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());

Expand Down
74 changes: 43 additions & 31 deletions src/piano.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
// SPDX-License-Identifier: GPL-3.0-only
#include "piano.hpp"
#include <SFML/Window/Keyboard.hpp>
#include <fluidsynth/synth.h>

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;
Expand Down Expand Up @@ -53,7 +54,15 @@ inline bool isBlackKey(size_t index) {
}
}

Piano::Piano(std::array<float, 4>& pressed_note_colors) : note_colors(pressed_note_colors) {}
Piano::Piano(sf::RenderWindow &window, std::array<float, 4> &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;
Expand Down Expand Up @@ -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<size_t> Piano::getPressedNotes() {
std::vector<size_t> pressed_notes = {};
for (int i = 0; i < keys.size(); i++) {
Expand All @@ -124,47 +153,36 @@ std::vector<size_t> 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));
for (auto it = key_sprites.begin(); it != key_sprites.end(); it++) {
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;
}
}
}
}
}

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) {
Expand All @@ -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);
}
}

Expand Down
17 changes: 12 additions & 5 deletions src/piano.hpp
Original file line number Diff line number Diff line change
@@ -1,24 +1,31 @@
// SPDX-License-Identifier: GPL-3.0-only
#pragma once
#include "midi_audio_stream.hpp"
#include <SFML/Graphics.hpp>
#include <array>
#include <fluidsynth.h>
#include <vector>

class Piano : public sf::Drawable {
public:
Piano(std::array<float, 4>& pressed_note_colors);
void setKeyPressed(size_t midi_note_number, bool isPressed);
Piano(sf::RenderWindow& window, std::array<float, 4>& 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<size_t> 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<bool, 88> keys = {};
mutable std::array<sf::RectangleShape, 88> key_sprites;
sf::RenderWindow& window;
MidiAudioStream mas{};
fluid_synth_t* synth = nullptr;
std::array<float, 4>& note_colors;
};

0 comments on commit 312c324

Please sign in to comment.