Browse Source

Add missing UI sounds

kiwec 2 months ago
parent
commit
895f4e57ba

+ 10 - 0
src/App/Osu/BanchoProtocol.h

@@ -236,6 +236,16 @@ class Room {
     u8 nb_open_slots = 0;
     Slot slots[16];
 
+    bool nb_ready() {
+        u8 nb = 0;
+        for(int i = 0; i < 16; i++) {
+            if(slots[i].has_player() && slots[i].is_ready()) {
+                nb++;
+            }
+        }
+        return nb;
+    }
+
     bool all_players_ready() {
         for(int i = 0; i < 16; i++) {
             if(slots[i].has_player() && !slots[i].is_ready()) {

+ 2 - 0
src/App/Osu/Beatmap.cpp

@@ -988,6 +988,8 @@ bool Beatmap::start() {
     osu->updateConfineCursor();
     osu->updateWindowsKeyDisable();
 
+    engine->getSound()->play(osu->getSkin()->m_expand);
+
     // NOTE: loading failures are handled dynamically in update(), so temporarily assume everything has worked in here
     return true;
 }

+ 1 - 0
src/App/Osu/Changelog.cpp

@@ -29,6 +29,7 @@ Changelog::Changelog() : ScreenBackable() {
     CHANGELOG latest;
     latest.title =
         UString::format("%.2f (%s, %s)", convar->getConVarByName("osu_version")->getFloat(), __DATE__, __TIME__);
+    latest.changes.push_back("- Added missing UI sounds");
     latest.changes.push_back("- Chat: added support for /me command");
     latest.changes.push_back("- Chat: added support for links");
     latest.changes.push_back("- Chat: added support for map links (auto-downloads)");

+ 17 - 1
src/App/Osu/Chat.cpp

@@ -53,7 +53,10 @@ ChatChannel::~ChatChannel() {
     m_chat->m_button_container->deleteBaseUIElement(btn);
 }
 
-void ChatChannel::onChannelButtonClick(CBaseUIButton *btn) { m_chat->switchToChannel(this); }
+void ChatChannel::onChannelButtonClick(CBaseUIButton *btn) {
+    engine->getSound()->play(osu->getSkin()->m_clickButton);
+    m_chat->switchToChannel(this);
+}
 
 void ChatChannel::add_message(ChatMessage msg) {
     const float line_height = 20;
@@ -382,6 +385,8 @@ void Chat::onKeyDown(KeyboardEvent &key) {
                                                      .text = m_input_box->getText(),
                                                  });
 
+            engine->getSound()->play(osu->getSkin()->m_messageSent);
+
             m_input_box->clear();
         }
         return;
@@ -418,6 +423,9 @@ void Chat::onKeyDown(KeyboardEvent &key) {
             auto new_chan = m_channels[(chan_index + 1) % m_channels.size()];
             switchToChannel(new_chan);
         }
+
+        engine->getSound()->play(osu->getSkin()->m_clickButton);
+
         return;
     }
 
@@ -505,6 +513,10 @@ void Chat::addChannel(UString channel_name, bool switch_to) {
     }
 
     updateLayout(osu->getScreenSize());
+
+    if(isVisible()) {
+        engine->getSound()->play(osu->getSkin()->m_expand);
+    }
 }
 
 void Chat::addMessage(UString channel_name, ChatMessage msg, bool mark_unread) {
@@ -673,6 +685,8 @@ void Chat::leave(UString channel_name) {
     }
 
     removeChannel(channel_name);
+
+    engine->getSound()->play(osu->getSkin()->m_closeChatTab);
 }
 
 void Chat::onDisconnect() {
@@ -736,6 +750,8 @@ void Chat::updateVisibility() {
 CBaseUIContainer *Chat::setVisible(bool visible) {
     if(visible == m_bVisible) return this;
 
+    engine->getSound()->play(osu->getSkin()->m_clickButton);
+
     if(visible && bancho.user_id <= 0) {
         osu->m_optionsMenu->askForLoginDetails();
         return this;

+ 3 - 0
src/App/Osu/Lobby.cpp

@@ -15,6 +15,8 @@
 #include "PromptScreen.h"
 #include "ResourceManager.h"
 #include "RichPresence.h"
+#include "Skin.h"
+#include "SoundEngine.h"
 #include "UIButton.h"
 
 RoomUIElement::RoomUIElement(Lobby* multi, Room* room, float x, float y, float width, float height)
@@ -98,6 +100,7 @@ void Lobby::onKeyDown(KeyboardEvent& key) {
         key.consume();
         setVisible(false);
         osu->m_mainMenu->setVisible(true);
+        engine->getSound()->play(osu->getSkin()->m_menuBack);
         return;
     }
 

+ 29 - 4
src/App/Osu/MainMenu.cpp

@@ -54,6 +54,7 @@ class MainMenuButton : public CBaseUIButton {
     MainMenuButton(MainMenu *mainMenu, float xPos, float yPos, float xSize, float ySize, UString name, UString text);
 
     void onMouseDownInside();
+    void onMouseInside();
 
    private:
     MainMenu *m_mainMenu;
@@ -1302,7 +1303,7 @@ MainMenuButton *MainMenu::addMainMenuButton(UString text) {
 }
 
 void MainMenu::onCubePressed() {
-    engine->getSound()->play(osu->getSkin()->getMenuHit());
+    engine->getSound()->play(osu->getSkin()->m_clickMainMenuCube);
 
     anim->moveQuadInOut(&m_fSizeAddAnim, 0.0f, 0.06f, 0.0f, false);
     anim->moveQuadInOut(&m_fSizeAddAnim, 0.12f, 0.06f, 0.07f, false);
@@ -1351,6 +1352,9 @@ void MainMenu::onPlayButtonPressed() {
     m_bMainMenuAnimFriendScheduled = false;
 
     osu->toggleSongBrowser();
+
+    engine->getSound()->play(osu->getSkin()->m_menuHit);
+    engine->getSound()->play(osu->getSkin()->m_clickSingleplayer);
 }
 
 void MainMenu::onMultiplayerButtonPressed() {
@@ -1361,16 +1365,23 @@ void MainMenu::onMultiplayerButtonPressed() {
 
     setVisible(false);
     osu->m_lobby->setVisible(true);
+
+    engine->getSound()->play(osu->getSkin()->m_menuHit);
+    engine->getSound()->play(osu->getSkin()->m_clickMultiplayer);
 }
 
 void MainMenu::onOptionsButtonPressed() {
     if(!osu->getOptionsMenu()->isVisible()) osu->toggleOptionsMenu();
+
+    engine->getSound()->play(osu->getSkin()->m_clickOptions);
 }
 
 void MainMenu::onExitButtonPressed() {
     m_fShutdownScheduledTime = engine->getTime() + 0.3f;
     m_bWasCleanShutdown = true;
     setMenuElementsVisible(false);
+
+    engine->getSound()->play(osu->getSkin()->m_clickExit);
 }
 
 void MainMenu::onPausePressed() {
@@ -1405,13 +1416,14 @@ MainMenuCubeButton::MainMenuCubeButton(MainMenu *mainMenu, float xPos, float yPo
 
 void MainMenuCubeButton::draw(Graphics *g) {
     // draw nothing
-    /// CBaseUIButton::draw(g);
 }
 
 void MainMenuCubeButton::onMouseInside() {
     anim->moveQuadInOut(&m_mainMenu->m_fSizeAddAnim, 0.12f, 0.15f, 0.0f, true);
 
     CBaseUIButton::onMouseInside();
+
+    engine->getSound()->play(osu->getSkin()->m_hoverMainMenuCube);
 }
 
 void MainMenuCubeButton::onMouseOutside() {
@@ -1428,7 +1440,20 @@ MainMenuButton::MainMenuButton(MainMenu *mainMenu, float xPos, float yPos, float
 
 void MainMenuButton::onMouseDownInside() {
     if(m_mainMenu->m_cube->isMouseInside()) return;
-
-    engine->getSound()->play(osu->getSkin()->getMenuHit());
     CBaseUIButton::onMouseDownInside();
 }
+
+void MainMenuButton::onMouseInside() {
+    if(m_mainMenu->m_cube->isMouseInside()) return;
+    CBaseUIButton::onMouseInside();
+
+    if(getName() == UString("Singleplayer")) {
+        engine->getSound()->play(osu->getSkin()->m_hoverSingleplayer);
+    } else if(getName() == UString("Multiplayer")) {
+        engine->getSound()->play(osu->getSkin()->m_hoverMultiplayer);
+    } else if(getName() == UString("Options")) {
+        engine->getSound()->play(osu->getSkin()->m_hoverOptions);
+    } else if(getName() == UString("Exit")) {
+        engine->getSound()->play(osu->getSkin()->m_hoverExit);
+    }
+}

+ 9 - 8
src/App/Osu/PauseMenu.cpp

@@ -44,9 +44,10 @@ PauseMenu::PauseMenu() : OsuScreen() {
 
     setSize(osu->getScreenWidth(), osu->getScreenHeight());
 
-    UIPauseMenuButton *continueButton = addButton([]() -> Image * { return osu->getSkin()->getPauseContinue(); });
-    UIPauseMenuButton *retryButton = addButton([]() -> Image * { return osu->getSkin()->getPauseRetry(); });
-    UIPauseMenuButton *backButton = addButton([]() -> Image * { return osu->getSkin()->getPauseBack(); });
+    UIPauseMenuButton *continueButton =
+        addButton([]() -> Image * { return osu->getSkin()->getPauseContinue(); }, "Resume");
+    UIPauseMenuButton *retryButton = addButton([]() -> Image * { return osu->getSkin()->getPauseRetry(); }, "Retry");
+    UIPauseMenuButton *backButton = addButton([]() -> Image * { return osu->getSkin()->getPauseBack(); }, "Quit");
 
     continueButton->setClickCallback(fastdelegate::MakeDelegate(this, &PauseMenu::onContinueClicked));
     retryButton->setClickCallback(fastdelegate::MakeDelegate(this, &PauseMenu::onRetryClicked));
@@ -134,7 +135,7 @@ void PauseMenu::onContinueClicked() {
     if(!m_bContinueEnabled) return;
     if(anim->isAnimating(&m_fDimAnim)) return;
 
-    engine->getSound()->play(osu->getSkin()->getMenuHit());
+    engine->getSound()->play(osu->getSkin()->m_clickPauseContinue);
     osu->getSelectedBeatmap()->pause();
 
     scheduleVisibilityChange(false);
@@ -143,7 +144,7 @@ void PauseMenu::onContinueClicked() {
 void PauseMenu::onRetryClicked() {
     if(anim->isAnimating(&m_fDimAnim)) return;
 
-    engine->getSound()->play(osu->getSkin()->getMenuHit());
+    engine->getSound()->play(osu->getSkin()->m_clickPauseRetry);
     osu->getSelectedBeatmap()->restart();
 
     scheduleVisibilityChange(false);
@@ -152,7 +153,7 @@ void PauseMenu::onRetryClicked() {
 void PauseMenu::onBackClicked() {
     if(anim->isAnimating(&m_fDimAnim)) return;
 
-    engine->getSound()->play(osu->getSkin()->getMenuHit());
+    engine->getSound()->play(osu->getSkin()->m_clickPauseBack);
     osu->getSelectedBeatmap()->stop();
 
     scheduleVisibilityChange(false);
@@ -401,8 +402,8 @@ void PauseMenu::setContinueEnabled(bool continueEnabled) {
     if(m_buttons.size() > 0) m_buttons[0]->setVisible(m_bContinueEnabled);
 }
 
-UIPauseMenuButton *PauseMenu::addButton(std::function<Image *()> getImageFunc) {
-    UIPauseMenuButton *button = new UIPauseMenuButton(getImageFunc, 0, 0, 0, 0, "");
+UIPauseMenuButton *PauseMenu::addButton(std::function<Image *()> getImageFunc, UString btn_name) {
+    UIPauseMenuButton *button = new UIPauseMenuButton(getImageFunc, 0, 0, 0, 0, btn_name);
     addBaseUIElement(button);
     m_buttons.push_back(button);
     return button;

+ 1 - 1
src/App/Osu/PauseMenu.h

@@ -33,7 +33,7 @@ class PauseMenu : public OsuScreen {
 
     void scheduleVisibilityChange(bool visible);
 
-    UIPauseMenuButton *addButton(std::function<Image *()> getImageFunc);
+    UIPauseMenuButton *addButton(std::function<Image *()> getImageFunc, UString name);
 
     bool m_bScheduledVisibilityChange;
     bool m_bScheduledVisibility;

+ 4 - 3
src/App/Osu/RankingScreen.cpp

@@ -442,6 +442,7 @@ CBaseUIContainer *RankingScreen::setVisible(bool visible) {
         // We backed out of the ranking screen, display the room again
         osu->m_room->setVisible(true);
         osu->m_chat->updateVisibility();
+        engine->getSound()->play(osu->getSkin()->m_menuBack);
 
         // Since we prevented on_map_change() from running while the ranking screen was visible, run it now.
         osu->m_room->on_map_change();
@@ -600,7 +601,7 @@ void RankingScreen::updateLayout() {
 
     m_songInfo->setSize(osu->getScreenWidth(),
                         max(m_songInfo->getMinimumHeight(),
-                                 m_rankingTitle->getSize().y * osu_rankingscreen_topbar_height_percent.getFloat()));
+                            m_rankingTitle->getSize().y * osu_rankingscreen_topbar_height_percent.getFloat()));
 
     m_rankings->setSize(osu->getScreenSize().x + 2, osu->getScreenSize().y - m_songInfo->getSize().y + 3);
     m_rankings->setRelPosY(m_songInfo->getSize().y - 1);
@@ -613,9 +614,9 @@ void RankingScreen::updateLayout() {
     m_rankingPanel->setScale(Osu::getImageScale(hardcodedOsuRankingPanelImageSize, 317.0f),
                              Osu::getImageScale(hardcodedOsuRankingPanelImageSize, 317.0f));
     m_rankingPanel->setSize(max(hardcodedOsuRankingPanelImageSize.x * m_rankingPanel->getScale().x,
-                                     m_rankingPanel->getImage()->getWidth() * m_rankingPanel->getScale().x),
+                                m_rankingPanel->getImage()->getWidth() * m_rankingPanel->getScale().x),
                             max(hardcodedOsuRankingPanelImageSize.y * m_rankingPanel->getScale().y,
-                                     m_rankingPanel->getImage()->getHeight() * m_rankingPanel->getScale().y));
+                                m_rankingPanel->getImage()->getHeight() * m_rankingPanel->getScale().y));
 
     m_rankingIndex->setSize(m_rankings->getSize().x + 2, osu->getScreenHeight() * 0.07f * uiScale);
     m_rankingIndex->setBackgroundColor(0xff745e13);

+ 28 - 1
src/App/Osu/RoomScreen.cpp

@@ -316,8 +316,15 @@ void RoomScreen::onChar(KeyboardEvent &key) {
 void RoomScreen::onResolutionChange(Vector2 newResolution) { updateLayout(newResolution); }
 
 CBaseUIContainer *RoomScreen::setVisible(bool visible) {
+    if(m_bVisible == visible) return this;
+
     // NOTE: Calling setVisible(false) does not quit the room! Call ragequit() instead.
     m_bVisible = visible;
+
+    if(visible) {
+        engine->getSound()->play(osu->getSkin()->m_menuBack);
+    }
+
     return this;
 }
 
@@ -482,7 +489,7 @@ void RoomScreen::updateLayout(Vector2 newResolution) {
 }
 
 // Exit to main menu
-void RoomScreen::ragequit() {
+void RoomScreen::ragequit(bool play_sound) {
     m_bVisible = false;
     bancho.match_started = false;
 
@@ -500,6 +507,10 @@ void RoomScreen::ragequit() {
 
     osu->m_modSelector->resetMods();
     osu->m_modSelector->enableModsFromFlags(osu->previous_mod_flags);
+
+    if(play_sound) {
+        engine->getSound()->play(osu->getSkin()->m_menuBack);
+    }
 }
 
 void RoomScreen::on_map_change() {
@@ -590,6 +601,20 @@ void RoomScreen::on_room_joined(Room room) {
 void RoomScreen::on_room_updated(Room room) {
     if(bancho.is_playing_a_multi_map() || !bancho.is_in_a_multi_room()) return;
 
+    if(bancho.room.nb_players < room.nb_players) {
+        engine->getSound()->play(osu->getSkin()->m_roomJoined);
+    } else if(bancho.room.nb_players > room.nb_players) {
+        engine->getSound()->play(osu->getSkin()->m_roomQuit);
+    }
+    if(bancho.room.nb_ready() < room.nb_ready()) {
+        engine->getSound()->play(osu->getSkin()->m_roomReady);
+    } else if(bancho.room.nb_ready() > room.nb_ready()) {
+        engine->getSound()->play(osu->getSkin()->m_roomNotReady);
+    }
+    if(!bancho.room.all_players_ready() && room.all_players_ready()) {
+        engine->getSound()->play(osu->getSkin()->m_matchConfirm);
+    }
+
     bool was_host = bancho.room.is_host();
     bool map_changed = bancho.room.map_id != room.map_id;
     bancho.room = room;
@@ -636,6 +661,8 @@ void RoomScreen::on_match_started(Room room) {
         bancho.match_started = true;
         osu->m_songBrowser2->m_bHasSelectedAndIsPlaying = true;
         osu->m_chat->updateVisibility();
+
+        engine->getSound()->play(osu->getSkin()->m_matchStart);
     } else {
         ragequit();  // map failed to load
     }

+ 1 - 1
src/App/Osu/RoomScreen.h

@@ -41,7 +41,7 @@ class RoomScreen : public OsuScreen {
 
     void updateLayout(Vector2 newResolution);
     void updateSettingsLayout(Vector2 newResolution);
-    void ragequit();
+    void ragequit(bool play_sound = true);
 
     void on_map_change();
     void on_room_joined(Room room);

+ 0 - 7
src/App/Osu/ScreenBackable.cpp

@@ -1,10 +1,3 @@
-//================ Copyright (c) 2016, PG, All rights reserved. =================//
-//
-// Purpose:		screen + back button
-//
-// $NoKeywords: $
-//===============================================================================//
-
 #include "ScreenBackable.h"
 
 #include "KeyBindings.h"

+ 41 - 1
src/App/Osu/Skin.cpp

@@ -758,7 +758,7 @@ void Skin::load() {
     checkLoadImage(&m_buttonMiddle, "button-middle", "OSU_SKIN_BUTTON_MIDDLE");
     checkLoadImage(&m_buttonRight, "button-right", "OSU_SKIN_BUTTON_RIGHT");
     randomizeFilePath();
-    m_menuBack = createSkinImage("menu-back", Vector2(225, 87), 54);
+    m_menuBackImg = createSkinImage("menu-back", Vector2(225, 87), 54);
     randomizeFilePath();
     m_selectionMode = createSkinImage("selection-mode", Vector2(90, 90),
                                       38);  // NOTE: should actually be Vector2(88, 90), but slightly overscale to
@@ -859,6 +859,46 @@ void Skin::load() {
     checkLoadSound(&m_spinnerBonus, "spinnerbonus", "OSU_SKIN_SPINNERBONUS_SND", true, true);
     checkLoadSound(&m_spinnerSpinSound, "spinnerspin", "OSU_SKIN_SPINNERSPIN_SND", false, true, true);
 
+    // UI feedback
+    checkLoadSound(&m_messageSent, "key-confirm", "OSU_SKIN_MESSAGE_SENT_SND", true, true, false);
+    checkLoadSound(&m_deletingText, "key-delete", "OSU_SKIN_DELETING_TEXT_SND", true, true, false);
+    checkLoadSound(&m_movingTextCursor, "key-movement", "OSU_MOVING_TEXT_CURSOR_SND", true, true, false);
+    checkLoadSound(&m_typing1, "key-press-1", "OSU_TYPING_1_SND", true, true, false);
+    checkLoadSound(&m_typing2, "key-press-2", "OSU_TYPING_2_SND", true, true, false);
+    checkLoadSound(&m_typing3, "key-press-3", "OSU_TYPING_3_SND", true, true, false);
+    checkLoadSound(&m_typing4, "key-press-4", "OSU_TYPING_4_SND", true, true, false);
+    checkLoadSound(&m_backButtonClick, "back-button-click", "OSU_BACK_BUTTON_CLICK_SND", true, true, false);
+    checkLoadSound(&m_backButtonHover, "back-button-hover", "OSU_BACK_BUTTON_HOVER_SND", true, true, false);
+    checkLoadSound(&m_menuBack, "menuback", "OSU_MENU_BACK_SND", true, true, false);
+    checkLoadSound(&m_closeChatTab, "click-close", "OSU_CLOSE_CHAT_TAB_SND", true, true, false);
+    checkLoadSound(&m_hoverButton, "click-short", "OSU_HOVER_BUTTON_SND", true, true, false);
+    checkLoadSound(&m_clickButton, "click-short-confirm", "OSU_CLICK_BUTTON_SND", true, true, false);
+    checkLoadSound(&m_clickMainMenuCube, "menu-play-click", "OSU_CLICK_MAIN_MENU_CUBE_SND", true, true, false);
+    checkLoadSound(&m_hoverMainMenuCube, "menu-play-hover", "OSU_HOVER_MAIN_MENU_CUBE_SND", true, true, false);
+    checkLoadSound(&m_clickSingleplayer, "menu-freeplay-click", "OSU_CLICK_SINGLEPLAYER_SND", true, true, false);
+    checkLoadSound(&m_hoverSingleplayer, "menu-freeplay-hover", "OSU_HOVER_SINGLEPLAYER_SND", true, true, false);
+    checkLoadSound(&m_clickMultiplayer, "menu-multiplayer-click", "OSU_CLICK_MULTIPLAYER_SND", true, true, false);
+    checkLoadSound(&m_hoverMultiplayer, "menu-multiplayer-hover", "OSU_HOVER_MULTIPLAYER_SND", true, true, false);
+    checkLoadSound(&m_clickOptions, "menu-options-click", "OSU_CLICK_OPTIONS_SND", true, true, false);
+    checkLoadSound(&m_hoverOptions, "menu-options-hover", "OSU_HOVER_OPTIONS_SND", true, true, false);
+    checkLoadSound(&m_clickExit, "menu-exit-click", "OSU_CLICK_EXIT_SND", true, true, false);
+    checkLoadSound(&m_hoverExit, "menu-exit-hover", "OSU_HOVER_EXIT_SND", true, true, false);
+    checkLoadSound(&m_clickPauseBack, "pause-back-click", "OSU_CLICK_QUIT_SONG_SND", true, true, false);
+    checkLoadSound(&m_hoverPauseBack, "pause-back-hover", "OSU_HOVER_QUIT_SONG_SND", true, true, false);
+    checkLoadSound(&m_clickPauseContinue, "pause-continue-click", "OSU_CLICK_RESUME_SONG_SND", true, true, false);
+    checkLoadSound(&m_hoverPauseContinue, "pause-continue-hover", "OSU_HOVER_RESUME_SONG_SND", true, true, false);
+    checkLoadSound(&m_clickPauseRetry, "pause-retry-click", "OSU_CLICK_RETRY_SONG_SND", true, true, false);
+    checkLoadSound(&m_hoverPauseRetry, "pause-retry-hover", "OSU_HOVER_RETRY_SONG_SND", true, true, false);
+    checkLoadSound(&m_expand, "select-expand", "OSU_EXPAND_SND", true, true, false);
+    checkLoadSound(&m_selectDifficulty, "select-difficulty", "OSU_SELECT_DIFFICULTY_SND", true, true, false);
+    checkLoadSound(&m_sliderbar, "sliderbar", "OSU_DRAG_SLIDER_SND", true, true, false);
+    checkLoadSound(&m_matchConfirm, "match-confirm", "OSU_ALL_PLAYERS_READY_SND", true, true, false);
+    checkLoadSound(&m_roomJoined, "match-join", "OSU_ROOM_JOINED_SND", true, true, false);
+    checkLoadSound(&m_roomQuit, "match-leave", "OSU_ROOM_QUIT_SND", true, true, false);
+    checkLoadSound(&m_roomNotReady, "match-notready", "OSU_ROOM_NOT_READY_SND", true, true, false);
+    checkLoadSound(&m_roomReady, "match-ready", "OSU_ROOM_READY_SND", true, true, false);
+    checkLoadSound(&m_matchStart, "match-start", "OSU_MATCH_START_SND", true, true, false);
+
     // others
     checkLoadSound(&m_combobreak, "combobreak", "OSU_SKIN_COMBOBREAK_SND", true, true);
     checkLoadSound(&m_failsound, "failsound", "OSU_SKIN_FAILSOUND_SND");

+ 71 - 2
src/App/Osu/Skin.h

@@ -188,7 +188,7 @@ class Skin {
     inline Image *getDefaultButtonLeft() { return m_defaultButtonLeft; }
     inline Image *getDefaultButtonMiddle() { return m_defaultButtonMiddle; }
     inline Image *getDefaultButtonRight() { return m_defaultButtonRight; }
-    inline SkinImage *getMenuBack2() { return m_menuBack; }
+    inline SkinImage *getMenuBack2() { return m_menuBackImg; }
     inline SkinImage *getSelectionMode() { return m_selectionMode; }
     inline SkinImage *getSelectionModeOver() { return m_selectionModeOver; }
     inline SkinImage *getSelectionMods() { return m_selectionMods; }
@@ -504,7 +504,7 @@ class Skin {
     Image *m_defaultButtonLeft;
     Image *m_defaultButtonMiddle;
     Image *m_defaultButtonRight;
-    SkinImage *m_menuBack;
+    SkinImage *m_menuBackImg;
     SkinImage *m_selectionMode;
     SkinImage *m_selectionModeOver;
     SkinImage *m_selectionMods;
@@ -583,6 +583,75 @@ class Skin {
     Sound *m_spinnerBonus;
     Sound *m_spinnerSpinSound;
 
+    // Plays when sending a message in chat
+    Sound *m_messageSent = NULL;
+
+    // Plays when deleting text in a message in chat
+    Sound *m_deletingText = NULL;
+
+    // Plays when changing the text cursor position
+    Sound *m_movingTextCursor = NULL;
+
+    // Plays when pressing a key for chat, search, edit, etc
+    Sound *m_typing1 = NULL;
+    Sound *m_typing2 = NULL;
+    Sound *m_typing3 = NULL;
+    Sound *m_typing4 = NULL;
+
+    // Plays when returning to the previous screen
+    Sound *m_menuBack = NULL;
+
+    // Plays when closing a chat tab
+    Sound *m_closeChatTab = NULL;
+
+    // Plays when hovering above all selectable boxes except beatmaps or main screen buttons
+    Sound *m_hoverButton = NULL;
+
+    // Plays when clicking to confirm a button or dropdown option, opening or
+    // closing chat, switching between chat tabs, or switching groups
+    Sound *m_clickButton = NULL;
+
+    // Main menu sounds
+    Sound *m_clickMainMenuCube = NULL;
+    Sound *m_hoverMainMenuCube = NULL;
+    Sound *m_clickSingleplayer = NULL;
+    Sound *m_hoverSingleplayer = NULL;
+    Sound *m_clickMultiplayer = NULL;
+    Sound *m_hoverMultiplayer = NULL;
+    Sound *m_clickOptions = NULL;
+    Sound *m_hoverOptions = NULL;
+    Sound *m_clickExit = NULL;
+    Sound *m_hoverExit = NULL;
+
+    // Pause menu sounds
+    Sound *m_clickPauseBack = NULL;
+    Sound *m_hoverPauseBack = NULL;
+    Sound *m_clickPauseContinue = NULL;
+    Sound *m_hoverPauseContinue = NULL;
+    Sound *m_clickPauseRetry = NULL;
+    Sound *m_hoverPauseRetry = NULL;
+
+    // Back button sounds
+    Sound *m_backButtonClick = NULL;
+    Sound *m_backButtonHover = NULL;
+
+    // Plays when switching into song selection, selecting a beatmap, opening dropdown boxes, opening chat tabs
+    Sound *m_expand = NULL;
+
+    // Plays when selecting a difficulty of a beatmap
+    Sound *m_selectDifficulty = NULL;
+
+    // Plays when changing the options via a slider
+    Sound *m_sliderbar = NULL;
+
+    // Multiplayer sounds
+    Sound *m_matchConfirm = NULL;  // all players are ready
+    Sound *m_roomJoined = NULL;    // a player joined
+    Sound *m_roomQuit = NULL;      // a player left
+    Sound *m_roomNotReady = NULL;  // a player is no longer ready
+    Sound *m_roomReady = NULL;     // a player is now ready
+    Sound *m_matchStart = NULL;    // match started
+
     Sound *m_combobreak;
     Sound *m_failsound;
     Sound *m_applause;

+ 11 - 1
src/App/Osu/SongBrowser/SongBrowser.cpp

@@ -426,6 +426,8 @@ SongBrowser::SongBrowser() : ScreenBackable() {
     m_groupButton->setClickCallback(fastdelegate::MakeDelegate(this, &SongBrowser::onGroupClicked));
 
     {
+        // TODO: Add hover sounds
+
         // "hardcoded" grouping tabs
         m_collectionsButton = addTopBarRightTabButton("Collections");
         m_collectionsButton->setClickCallback(fastdelegate::MakeDelegate(this, &SongBrowser::onGroupTabButtonClicked));
@@ -1321,10 +1323,13 @@ void SongBrowser::onChar(KeyboardEvent &e) {
 void SongBrowser::onResolutionChange(Vector2 newResolution) { ScreenBackable::onResolutionChange(newResolution); }
 
 CBaseUIContainer *SongBrowser::setVisible(bool visible) {
+    if(visible == m_bVisible) return this;
+
     m_bVisible = visible;
     m_bShiftPressed = false;  // seems to get stuck sometimes otherwise
 
     if(m_bVisible) {
+        engine->getSound()->play(osu->getSkin()->m_expand);
         RichPresence::onSongBrowser();
 
         updateLayout();
@@ -3344,7 +3349,10 @@ void SongBrowser::onSortChangeInt(UString text, bool autoScroll) {
     onAfterSortingOrGroupChange(autoScroll);
 }
 
-void SongBrowser::onGroupTabButtonClicked(CBaseUIButton *groupTabButton) { onGroupChange(groupTabButton->getText()); }
+void SongBrowser::onGroupTabButtonClicked(CBaseUIButton *groupTabButton) {
+    onGroupChange(groupTabButton->getText());
+    engine->getSound()->play(osu->getSkin()->m_clickButton);
+}
 
 void SongBrowser::onGroupNoGrouping() {
     m_group = GROUP::GROUP_NO_GROUPING;
@@ -3561,6 +3569,8 @@ void SongBrowser::onScoreClicked(CBaseUIButton *button) {
 
     osu->getSongBrowser()->setVisible(false);
     osu->getRankingScreen()->setVisible(true);
+
+    engine->getSound()->play(osu->getSkin()->m_menuHit);
 }
 
 void SongBrowser::onScoreContextMenu(ScoreButton *scoreButton, int id) {

+ 10 - 0
src/App/Osu/SongBrowser/SongDifficultyButton.cpp

@@ -15,6 +15,7 @@
 #include "Replay.h"
 #include "ResourceManager.h"
 #include "Skin.h"
+#include "SoundEngine.h"
 #include "Timer.h"
 
 using namespace std;
@@ -172,6 +173,15 @@ void SongDifficultyButton::mouse_update(bool *propagate_clicks) {
     }
 }
 
+void SongDifficultyButton::onClicked() {
+    engine->getSound()->play(osu->getSkin()->m_selectDifficulty);
+
+    // NOTE: Intentionally not calling Button::onClicked(), since that one plays another sound
+    CBaseUIButton::onClicked();
+
+    select(true, true);
+}
+
 void SongDifficultyButton::onSelected(bool wasSelected, bool autoSelectBottomMostChild, bool wasParentSelected) {
     Button::onSelected(wasSelected, autoSelectBottomMostChild, wasParentSelected);
 

+ 1 - 0
src/App/Osu/SongBrowser/SongDifficultyButton.h

@@ -12,6 +12,7 @@ class SongDifficultyButton : public SongButton {
 
     virtual void draw(Graphics *g);
     virtual void mouse_update(bool *propagate_clicks);
+    virtual void onClicked();
 
     virtual void updateGrade();
 

+ 5 - 1
src/App/Osu/SpectatorScreen.cpp

@@ -20,7 +20,9 @@
 #include "PromptScreen.h"
 #include "ResourceManager.h"
 #include "RoomScreen.h"
+#include "Skin.h"
 #include "SongBrowser/SongBrowser.h"
+#include "SoundEngine.h"
 #include "UIButton.h"
 #include "UserCard.h"
 
@@ -70,9 +72,10 @@ void start_spectating(i32 user_id) {
     osu->m_lobby->setVisible(false);
     osu->m_changelog->setVisible(false);
     osu->m_mainMenu->setVisible(false);
-    if(osu->m_room->isVisible()) osu->m_room->ragequit();
+    if(osu->m_room->isVisible()) osu->m_room->ragequit(false);
 
     osu->m_spectatorScreen->setVisible(true);
+    engine->getSound()->play(osu->getSkin()->m_menuHit);
 }
 
 void stop_spectating() {
@@ -95,6 +98,7 @@ void stop_spectating() {
 
     osu->m_spectatorScreen->setVisible(false);
     osu->m_mainMenu->setVisible(true);
+    engine->getSound()->play(osu->getSkin()->m_menuBack);
 }
 
 SpectatorScreen::SpectatorScreen() {

+ 8 - 0
src/App/Osu/UIBackButton.cpp

@@ -6,6 +6,7 @@
 #include "Osu.h"
 #include "Skin.h"
 #include "SkinImage.h"
+#include "SoundEngine.h"
 
 using namespace std;
 
@@ -53,10 +54,17 @@ void UIBackButton::mouse_update(bool *propagate_clicks) {
     CBaseUIButton::mouse_update(propagate_clicks);
 }
 
+void UIBackButton::onMouseDownInside() {
+    CBaseUIButton::onMouseDownInside();
+
+    engine->getSound()->play(osu->getSkin()->m_backButtonClick);
+}
+
 void UIBackButton::onMouseInside() {
     CBaseUIButton::onMouseInside();
 
     anim->moveQuadOut(&m_fAnimation, 1.0f, 0.1f, 0.0f, true);
+    engine->getSound()->play(osu->getSkin()->m_backButtonHover);
 }
 
 void UIBackButton::onMouseOutside() {

+ 1 - 0
src/App/Osu/UIBackButton.h

@@ -8,6 +8,7 @@ class UIBackButton : public CBaseUIButton {
     virtual void draw(Graphics *g);
     virtual void mouse_update(bool *propagate_clicks);
 
+    virtual void onMouseDownInside();
     virtual void onMouseInside();
     virtual void onMouseOutside();
 

+ 8 - 1
src/App/Osu/UIButton.cpp

@@ -5,6 +5,7 @@
 #include "Osu.h"
 #include "ResourceManager.h"
 #include "Skin.h"
+#include "SoundEngine.h"
 #include "TooltipOverlay.h"
 
 using namespace std;
@@ -87,7 +88,11 @@ void UIButton::mouse_update(bool *propagate_clicks) {
     m_bFocusStolenDelay = false;
 }
 
-void UIButton::onMouseInside() { m_fBrightness = 1.0f; }
+void UIButton::onMouseInside() {
+    m_fBrightness = 1.0f;
+
+    engine->getSound()->play(osu->getSkin()->m_hoverButton);
+}
 
 void UIButton::onMouseOutside() { m_fBrightness = 0.85f; }
 
@@ -97,6 +102,8 @@ void UIButton::onClicked() {
     CBaseUIButton::onClicked();
 
     animateClickColor();
+
+    engine->getSound()->play(osu->getSkin()->m_clickButton);
 }
 
 void UIButton::onFocusStolen() {

+ 8 - 0
src/App/Osu/UIContextMenu.cpp

@@ -8,6 +8,8 @@
 #include "Keyboard.h"
 #include "Mouse.h"
 #include "Osu.h"
+#include "Skin.h"
+#include "SoundEngine.h"
 #include "TooltipOverlay.h"
 
 UIContextMenuButton::UIContextMenuButton(float xPos, float yPos, float xSize, float ySize, UString name, UString text,
@@ -31,6 +33,10 @@ void UIContextMenuButton::mouse_update(bool *propagate_clicks) {
     }
 }
 
+void UIContextMenuButton::onMouseInside() { engine->getSound()->play(osu->getSkin()->m_hoverButton); }
+
+void UIContextMenuButton::onMouseDownInside() { engine->getSound()->play(osu->getSkin()->m_clickButton); }
+
 void UIContextMenuButton::setTooltipText(UString text) { m_tooltipTextLines = text.split("\n"); }
 
 UIContextMenuTextbox::UIContextMenuTextbox(float xPos, float yPos, float xSize, float ySize, UString name, int id)
@@ -267,6 +273,8 @@ void UIContextMenu::end(bool invertAnimation, bool clampUnderflowAndOverflowAndE
 
     m_fAnimation = 0.001f;
     anim->moveQuartOut(&m_fAnimation, 1.0f, 0.15f, true);
+
+    engine->getSound()->play(osu->getSkin()->m_expand);
 }
 
 void UIContextMenu::setVisible2(bool visible2) {

+ 3 - 0
src/App/Osu/UIContextMenu.h

@@ -71,6 +71,9 @@ class UIContextMenuButton : public CBaseUIButton {
 
     virtual void mouse_update(bool *propagate_clicks);
 
+    virtual void onMouseInside();
+    virtual void onMouseDownInside();
+
     inline int getID() const { return m_iID; }
 
     void setTooltipText(UString text);

+ 8 - 0
src/App/Osu/UIPauseMenuButton.cpp

@@ -54,6 +54,14 @@ void UIPauseMenuButton::onMouseInside() {
     const float animationDuration = 0.09f;
     anim->moveLinear(&m_vScale.x, m_vBaseScale.x * m_fScaleMultiplier, animationDuration, true);
     anim->moveLinear(&m_vScale.y, m_vBaseScale.y * m_fScaleMultiplier, animationDuration, true);
+
+    if(getName() == UString("Resume")) {
+        engine->getSound()->play(osu->getSkin()->m_hoverPauseContinue);
+    } else if(getName() == UString("Retry")) {
+        engine->getSound()->play(osu->getSkin()->m_hoverPauseRetry);
+    } else if(getName() == UString("Quit")) {
+        engine->getSound()->play(osu->getSkin()->m_hoverPauseBack);
+    }
 }
 
 void UIPauseMenuButton::onMouseOutside() {

+ 154 - 144
src/Engine/SoundEngine.cpp

@@ -26,16 +26,20 @@ void _volume(UString oldValue, UString newValue) {
 
 ConVar _volume_("volume", 1.0f, FCVAR_DEFAULT | FCVAR_PRIVATE, _volume);
 
-ConVar snd_ready_delay("snd_ready_delay", 0.0f, FCVAR_DEFAULT | FCVAR_PRIVATE, "after a sound engine restart, wait this many seconds before marking it as ready");
+ConVar snd_ready_delay("snd_ready_delay", 0.0f, FCVAR_DEFAULT | FCVAR_PRIVATE,
+                       "after a sound engine restart, wait this many seconds before marking it as ready");
 ConVar snd_output_device("snd_output_device", "Default", FCVAR_DEFAULT | FCVAR_PRIVATE);
 ConVar snd_restart("snd_restart");
 
 ConVar snd_freq("snd_freq", 44100, FCVAR_DEFAULT | FCVAR_PRIVATE, "output sampling rate in Hz");
-ConVar snd_updateperiod("snd_updateperiod", 10, FCVAR_DEFAULT | FCVAR_PRIVATE, "BASS_CONFIG_UPDATEPERIOD length in milliseconds");
+ConVar snd_updateperiod("snd_updateperiod", 10, FCVAR_DEFAULT | FCVAR_PRIVATE,
+                        "BASS_CONFIG_UPDATEPERIOD length in milliseconds");
 ConVar snd_dev_period("snd_dev_period", 10, FCVAR_DEFAULT | FCVAR_PRIVATE,
                       "BASS_CONFIG_DEV_PERIOD length in milliseconds, or if negative then in samples");
-ConVar snd_dev_buffer("snd_dev_buffer", 30, FCVAR_DEFAULT | FCVAR_PRIVATE, "BASS_CONFIG_DEV_BUFFER length in milliseconds");
-ConVar snd_async_buffer("snd_async_buffer", 65536, FCVAR_DEFAULT | FCVAR_PRIVATE, "BASS_CONFIG_ASYNCFILE_BUFFER length in bytes. Set to 0 to disable.");
+ConVar snd_dev_buffer("snd_dev_buffer", 30, FCVAR_DEFAULT | FCVAR_PRIVATE,
+                      "BASS_CONFIG_DEV_BUFFER length in milliseconds");
+ConVar snd_async_buffer("snd_async_buffer", 65536, FCVAR_DEFAULT | FCVAR_PRIVATE,
+                        "BASS_CONFIG_ASYNCFILE_BUFFER length in bytes. Set to 0 to disable.");
 
 ConVar snd_restrict_play_frame(
     "snd_restrict_play_frame", true, FCVAR_DEFAULT | FCVAR_PRIVATE,
@@ -310,144 +314,144 @@ void SoundEngine::updateOutputDevices(bool printInfo) {
 void display_bass_error() {
     auto code = BASS_ErrorGetCode();
     switch(code) {
-    case BASS_OK:
-        break;
-    case BASS_ERROR_MEM:
-        osu->getNotificationOverlay()->addNotification("BASS error: Memory error");
-        break;
-    case BASS_ERROR_FILEOPEN:
-        osu->getNotificationOverlay()->addNotification("BASS error: Can't open the file");
-        break;
-    case BASS_ERROR_DRIVER:
-        osu->getNotificationOverlay()->addNotification("BASS error: Can't find an available driver");
-        break;
-    case BASS_ERROR_BUFLOST:
-        osu->getNotificationOverlay()->addNotification("BASS error: The sample buffer was lost");
-        break;
-    case BASS_ERROR_HANDLE:
-        osu->getNotificationOverlay()->addNotification("BASS error: Invalid handle");
-        break;
-    case BASS_ERROR_FORMAT:
-        osu->getNotificationOverlay()->addNotification("BASS error: Unsupported sample format");
-        break;
-    case BASS_ERROR_POSITION:
-        osu->getNotificationOverlay()->addNotification("BASS error: Invalid position");
-        break;
-    case BASS_ERROR_INIT:
-        osu->getNotificationOverlay()->addNotification("BASS error: BASS_Init has not been successfully called");
-        break;
-    case BASS_ERROR_START:
-        osu->getNotificationOverlay()->addNotification("BASS error: BASS_Start has not been successfully called");
-        break;
-    case BASS_ERROR_SSL:
-        osu->getNotificationOverlay()->addNotification("BASS error: SSL/HTTPS support isn't available");
-        break;
-    case BASS_ERROR_REINIT:
-        osu->getNotificationOverlay()->addNotification("BASS error: Device needs to be reinitialized");
-        break;
-    case BASS_ERROR_ALREADY:
-        osu->getNotificationOverlay()->addNotification("BASS error: Already initialized");
-        break;
-    case BASS_ERROR_NOTAUDIO:
-        osu->getNotificationOverlay()->addNotification("BASS error: File does not contain audio");
-        break;
-    case BASS_ERROR_NOCHAN:
-        osu->getNotificationOverlay()->addNotification("BASS error: Can't get a free channel");
-        break;
-    case BASS_ERROR_ILLTYPE:
-        osu->getNotificationOverlay()->addNotification("BASS error: An illegal type was specified");
-        break;
-    case BASS_ERROR_ILLPARAM:
-        osu->getNotificationOverlay()->addNotification("BASS error: An illegal parameter was specified");
-        break;
-    case BASS_ERROR_NO3D:
-        osu->getNotificationOverlay()->addNotification("BASS error: No 3D support");
-        break;
-    case BASS_ERROR_NOEAX:
-        osu->getNotificationOverlay()->addNotification("BASS error: No EAX support");
-        break;
-    case BASS_ERROR_DEVICE:
-        osu->getNotificationOverlay()->addNotification("BASS error: Illegal device number");
-        break;
-    case BASS_ERROR_NOPLAY:
-        osu->getNotificationOverlay()->addNotification("BASS error: Not playing");
-        break;
-    case BASS_ERROR_FREQ:
-        osu->getNotificationOverlay()->addNotification("BASS error: Illegal sample rate");
-        break;
-    case BASS_ERROR_NOTFILE:
-        osu->getNotificationOverlay()->addNotification("BASS error: The stream is not a file stream");
-        break;
-    case BASS_ERROR_NOHW:
-        osu->getNotificationOverlay()->addNotification("BASS error: No hardware voices available");
-        break;
-    case BASS_ERROR_EMPTY:
-        osu->getNotificationOverlay()->addNotification("BASS error: The file has no sample data");
-        break;
-    case BASS_ERROR_NONET:
-        osu->getNotificationOverlay()->addNotification("BASS error: No internet connection could be opened");
-        break;
-    case BASS_ERROR_CREATE:
-        osu->getNotificationOverlay()->addNotification("BASS error: Couldn't create the file");
-        break;
-    case BASS_ERROR_NOFX:
-        osu->getNotificationOverlay()->addNotification("BASS error: Effects are not available");
-        break;
-    case BASS_ERROR_NOTAVAIL:
-        osu->getNotificationOverlay()->addNotification("BASS error: Requested data/action is not available");
-        break;
-    case BASS_ERROR_DECODE:
-        osu->getNotificationOverlay()->addNotification("BASS error: The channel is/isn't a decoding channel");
-        break;
-    case BASS_ERROR_DX:
-        osu->getNotificationOverlay()->addNotification("BASS error: A sufficient DirectX version is not installed");
-        break;
-    case BASS_ERROR_TIMEOUT:
-        osu->getNotificationOverlay()->addNotification("BASS error: Connection timeout");
-        break;
-    case BASS_ERROR_FILEFORM:
-        osu->getNotificationOverlay()->addNotification("BASS error: Unsupported file format");
-        break;
-    case BASS_ERROR_SPEAKER:
-        osu->getNotificationOverlay()->addNotification("BASS error: Unavailable speaker");
-        break;
-    case BASS_ERROR_VERSION:
-        osu->getNotificationOverlay()->addNotification("BASS error: Invalid BASS version");
-        break;
-    case BASS_ERROR_CODEC:
-        osu->getNotificationOverlay()->addNotification("BASS error: Codec is not available/supported");
-        break;
-    case BASS_ERROR_ENDED:
-        osu->getNotificationOverlay()->addNotification("BASS error: The channel/file has ended");
-        break;
-    case BASS_ERROR_BUSY:
-        osu->getNotificationOverlay()->addNotification("BASS error: The device is busy");
-        break;
-    case BASS_ERROR_UNSTREAMABLE:
-        osu->getNotificationOverlay()->addNotification("BASS error: Unstreamable file");
-        break;
-    case BASS_ERROR_PROTOCOL:
-        osu->getNotificationOverlay()->addNotification("BASS error: Unsupported protocol");
-        break;
-    case BASS_ERROR_DENIED:
-        osu->getNotificationOverlay()->addNotification("BASS error: Access Denied");
-        break;
-    case BASS_ERROR_WASAPI:
-        osu->getNotificationOverlay()->addNotification("WASAPI error: No WASAPI");
-        break;
-    case BASS_ERROR_WASAPI_BUFFER:
-        osu->getNotificationOverlay()->addNotification("WASAPI error: Invalid buffer size");
-        break;
-    case BASS_ERROR_WASAPI_CATEGORY:
-        osu->getNotificationOverlay()->addNotification("WASAPI error: Can't set category");
-        break;
-    case BASS_ERROR_WASAPI_DENIED:
-        osu->getNotificationOverlay()->addNotification("WASAPI error: Access denied");
-        break;
-    case BASS_ERROR_UNKNOWN:  // fallthrough
-    default:
-        osu->getNotificationOverlay()->addNotification("Unknown BASS error (%i)!", code);
-        break;
+        case BASS_OK:
+            break;
+        case BASS_ERROR_MEM:
+            osu->getNotificationOverlay()->addNotification("BASS error: Memory error");
+            break;
+        case BASS_ERROR_FILEOPEN:
+            osu->getNotificationOverlay()->addNotification("BASS error: Can't open the file");
+            break;
+        case BASS_ERROR_DRIVER:
+            osu->getNotificationOverlay()->addNotification("BASS error: Can't find an available driver");
+            break;
+        case BASS_ERROR_BUFLOST:
+            osu->getNotificationOverlay()->addNotification("BASS error: The sample buffer was lost");
+            break;
+        case BASS_ERROR_HANDLE:
+            osu->getNotificationOverlay()->addNotification("BASS error: Invalid handle");
+            break;
+        case BASS_ERROR_FORMAT:
+            osu->getNotificationOverlay()->addNotification("BASS error: Unsupported sample format");
+            break;
+        case BASS_ERROR_POSITION:
+            osu->getNotificationOverlay()->addNotification("BASS error: Invalid position");
+            break;
+        case BASS_ERROR_INIT:
+            osu->getNotificationOverlay()->addNotification("BASS error: BASS_Init has not been successfully called");
+            break;
+        case BASS_ERROR_START:
+            osu->getNotificationOverlay()->addNotification("BASS error: BASS_Start has not been successfully called");
+            break;
+        case BASS_ERROR_SSL:
+            osu->getNotificationOverlay()->addNotification("BASS error: SSL/HTTPS support isn't available");
+            break;
+        case BASS_ERROR_REINIT:
+            osu->getNotificationOverlay()->addNotification("BASS error: Device needs to be reinitialized");
+            break;
+        case BASS_ERROR_ALREADY:
+            osu->getNotificationOverlay()->addNotification("BASS error: Already initialized");
+            break;
+        case BASS_ERROR_NOTAUDIO:
+            osu->getNotificationOverlay()->addNotification("BASS error: File does not contain audio");
+            break;
+        case BASS_ERROR_NOCHAN:
+            osu->getNotificationOverlay()->addNotification("BASS error: Can't get a free channel");
+            break;
+        case BASS_ERROR_ILLTYPE:
+            osu->getNotificationOverlay()->addNotification("BASS error: An illegal type was specified");
+            break;
+        case BASS_ERROR_ILLPARAM:
+            osu->getNotificationOverlay()->addNotification("BASS error: An illegal parameter was specified");
+            break;
+        case BASS_ERROR_NO3D:
+            osu->getNotificationOverlay()->addNotification("BASS error: No 3D support");
+            break;
+        case BASS_ERROR_NOEAX:
+            osu->getNotificationOverlay()->addNotification("BASS error: No EAX support");
+            break;
+        case BASS_ERROR_DEVICE:
+            osu->getNotificationOverlay()->addNotification("BASS error: Illegal device number");
+            break;
+        case BASS_ERROR_NOPLAY:
+            osu->getNotificationOverlay()->addNotification("BASS error: Not playing");
+            break;
+        case BASS_ERROR_FREQ:
+            osu->getNotificationOverlay()->addNotification("BASS error: Illegal sample rate");
+            break;
+        case BASS_ERROR_NOTFILE:
+            osu->getNotificationOverlay()->addNotification("BASS error: The stream is not a file stream");
+            break;
+        case BASS_ERROR_NOHW:
+            osu->getNotificationOverlay()->addNotification("BASS error: No hardware voices available");
+            break;
+        case BASS_ERROR_EMPTY:
+            osu->getNotificationOverlay()->addNotification("BASS error: The file has no sample data");
+            break;
+        case BASS_ERROR_NONET:
+            osu->getNotificationOverlay()->addNotification("BASS error: No internet connection could be opened");
+            break;
+        case BASS_ERROR_CREATE:
+            osu->getNotificationOverlay()->addNotification("BASS error: Couldn't create the file");
+            break;
+        case BASS_ERROR_NOFX:
+            osu->getNotificationOverlay()->addNotification("BASS error: Effects are not available");
+            break;
+        case BASS_ERROR_NOTAVAIL:
+            osu->getNotificationOverlay()->addNotification("BASS error: Requested data/action is not available");
+            break;
+        case BASS_ERROR_DECODE:
+            osu->getNotificationOverlay()->addNotification("BASS error: The channel is/isn't a decoding channel");
+            break;
+        case BASS_ERROR_DX:
+            osu->getNotificationOverlay()->addNotification("BASS error: A sufficient DirectX version is not installed");
+            break;
+        case BASS_ERROR_TIMEOUT:
+            osu->getNotificationOverlay()->addNotification("BASS error: Connection timeout");
+            break;
+        case BASS_ERROR_FILEFORM:
+            osu->getNotificationOverlay()->addNotification("BASS error: Unsupported file format");
+            break;
+        case BASS_ERROR_SPEAKER:
+            osu->getNotificationOverlay()->addNotification("BASS error: Unavailable speaker");
+            break;
+        case BASS_ERROR_VERSION:
+            osu->getNotificationOverlay()->addNotification("BASS error: Invalid BASS version");
+            break;
+        case BASS_ERROR_CODEC:
+            osu->getNotificationOverlay()->addNotification("BASS error: Codec is not available/supported");
+            break;
+        case BASS_ERROR_ENDED:
+            osu->getNotificationOverlay()->addNotification("BASS error: The channel/file has ended");
+            break;
+        case BASS_ERROR_BUSY:
+            osu->getNotificationOverlay()->addNotification("BASS error: The device is busy");
+            break;
+        case BASS_ERROR_UNSTREAMABLE:
+            osu->getNotificationOverlay()->addNotification("BASS error: Unstreamable file");
+            break;
+        case BASS_ERROR_PROTOCOL:
+            osu->getNotificationOverlay()->addNotification("BASS error: Unsupported protocol");
+            break;
+        case BASS_ERROR_DENIED:
+            osu->getNotificationOverlay()->addNotification("BASS error: Access Denied");
+            break;
+        case BASS_ERROR_WASAPI:
+            osu->getNotificationOverlay()->addNotification("WASAPI error: No WASAPI");
+            break;
+        case BASS_ERROR_WASAPI_BUFFER:
+            osu->getNotificationOverlay()->addNotification("WASAPI error: Invalid buffer size");
+            break;
+        case BASS_ERROR_WASAPI_CATEGORY:
+            osu->getNotificationOverlay()->addNotification("WASAPI error: Can't set category");
+            break;
+        case BASS_ERROR_WASAPI_DENIED:
+            osu->getNotificationOverlay()->addNotification("WASAPI error: Access denied");
+            break;
+        case BASS_ERROR_UNKNOWN:  // fallthrough
+        default:
+            osu->getNotificationOverlay()->addNotification("Unknown BASS error (%i)!", code);
+            break;
     }
 }
 
@@ -619,11 +623,13 @@ bool SoundEngine::initializeOutputDevice(OUTPUT_DEVICE device) {
         auto flags = BASS_WASAPI_RAW | BASS_MIXER_NONSTOP | BASS_WASAPI_RAW;
         if(convar->getConVarByName("win_snd_wasapi_exclusive")->getBool()) {
             // BASS_WASAPI_EXCLUSIVE makes neosu have exclusive output to the sound card
-            // BASS_WASAPI_AUTOFORMAT chooses the best matching sample format, BASSWASAPI doesn't resample in exclusive mode
+            // BASS_WASAPI_AUTOFORMAT chooses the best matching sample format, BASSWASAPI doesn't resample in exclusive
+            // mode
             flags |= BASS_WASAPI_EXCLUSIVE | BASS_WASAPI_AUTOFORMAT;
         }
 
-        if(!BASS_WASAPI_Init(device.id, 0, 0, flags, bufferSize, updatePeriod, WASAPIPROC_BASS, (void *)g_bassOutputMixer)) {
+        if(!BASS_WASAPI_Init(device.id, 0, 0, flags, bufferSize, updatePeriod, WASAPIPROC_BASS,
+                             (void *)g_bassOutputMixer)) {
             const int errorCode = BASS_ErrorGetCode();
             ready_since = -1.0;
             debugLog("BASS_WASAPI_Init() failed.\n");
@@ -738,6 +744,10 @@ bool SoundEngine::play(Sound *snd, float pan, float pitch) {
         snd->m_fLastPlayTime = snd->m_fChannelCreationTime;
     }
 
+    if(Osu::debug->getBool()) {
+        debugLog("Playing %s\n", snd->getFilePath().c_str());
+    }
+
     return true;
 }
 

+ 0 - 7
src/GUI/CBaseUIButton.cpp

@@ -1,10 +1,3 @@
-//================ Copyright (c) 2013, PG, All rights reserved. =================//
-//
-// Purpose:		a simple button
-//
-// $NoKeywords: $button
-//===============================================================================//
-
 #include "CBaseUIButton.h"
 
 #include "Engine.h"

+ 1 - 12
src/GUI/CBaseUIButton.h

@@ -1,13 +1,4 @@
-//================ Copyright (c) 2013, PG, All rights reserved. =================//
-//
-// Purpose:		a simple button
-//
-// $NoKeywords: $button
-//===============================================================================//
-
-#ifndef CBASEUIBUTTON_H
-#define CBASEUIBUTTON_H
-
+#pragma once
 #include "CBaseUIElement.h"
 
 class McFont;
@@ -133,5 +124,3 @@ class CBaseUIButton : public CBaseUIElement {
     ButtonClickVoidCallback m_clickVoidCallback;
     ButtonClickCallback m_clickCallback;
 };
-
-#endif

+ 24 - 9
src/GUI/CBaseUISlider.cpp

@@ -4,6 +4,9 @@
 #include "Engine.h"
 #include "Keyboard.h"
 #include "Mouse.h"
+#include "Osu.h"
+#include "Skin.h"
+#include "SoundEngine.h"
 
 using namespace std;
 
@@ -86,33 +89,37 @@ void CBaseUISlider::mouse_update(bool *propagate_clicks) {
     if(m_bActive) {
         // calculate new values
         if(!m_bHorizontal) {
-            if(m_bAnimated)
+            if(m_bAnimated) {
                 anim->moveQuadOut(&m_vBlockPos.y,
                                   clamp<float>(mousepos.y - m_vGrabBackup.y, 0.0f, m_vSize.y - m_vBlockSize.y), 0.10f,
                                   0, true);
-            else
+            } else {
                 m_vBlockPos.y = clamp<float>(mousepos.y - m_vGrabBackup.y, 0.0f, m_vSize.y - m_vBlockSize.y);
+            }
 
             m_fCurPercent = clamp<float>(1.0f - (std::round(m_vBlockPos.y) / (m_vSize.y - m_vBlockSize.y)), 0.0f, 1.0f);
         } else {
-            if(m_bAnimated)
+            if(m_bAnimated) {
                 anim->moveQuadOut(&m_vBlockPos.x,
                                   clamp<float>(mousepos.x - m_vGrabBackup.x, 0.0f, m_vSize.x - m_vBlockSize.x), 0.10f,
                                   0, true);
-            else
+            } else {
                 m_vBlockPos.x = clamp<float>(mousepos.x - m_vGrabBackup.x, 0.0f, m_vSize.x - m_vBlockSize.x);
+            }
 
             m_fCurPercent = clamp<float>(std::round(m_vBlockPos.x) / (m_vSize.x - m_vBlockSize.x), 0.0f, 1.0f);
         }
 
         // set new value
         if(m_bAnimated) {
-            if(m_bLiveUpdate)
+            if(m_bLiveUpdate) {
                 setValue(lerp<float>(m_fMinValue, m_fMaxValue, m_fCurPercent), false);
-            else
+            } else {
                 m_fCurValue = lerp<float>(m_fMinValue, m_fMaxValue, m_fCurPercent);
-        } else
+            }
+        } else {
             setValue(lerp<float>(m_fMinValue, m_fMaxValue, m_fCurPercent), false);
+        }
 
         m_bHasChanged = true;
     } else {
@@ -122,10 +129,11 @@ void CBaseUISlider::mouse_update(bool *propagate_clicks) {
             if(wheelDelta != 0) {
                 const int multiplier = max(1, std::abs(wheelDelta) / 120);
 
-                if(wheelDelta > 0)
+                if(wheelDelta > 0) {
                     setValue(m_fCurValue + m_fKeyDelta * multiplier, m_bAnimated);
-                else
+                } else {
                     setValue(m_fCurValue - m_fKeyDelta * multiplier, m_bAnimated);
+                }
             }
         }
     }
@@ -210,6 +218,13 @@ CBaseUISlider *CBaseUISlider::setValue(float value, bool animate, bool call_call
 
     if(call_callback && changeCallbackCheck && m_sliderChangeCallback != NULL) {
         m_sliderChangeCallback(this);
+
+        if(m_bHasChanged) {
+            if(m_fLastSoundPlayTime + 0.05f < engine->getTime()) {
+                engine->getSound()->play(osu->getSkin()->m_sliderbar);
+                m_fLastSoundPlayTime = engine->getTime();
+            }
+        }
     }
 
     updateBlockPos();

+ 2 - 11
src/GUI/CBaseUISlider.h

@@ -1,16 +1,8 @@
-//================ Copyright (c) 2012, PG, All rights reserved. =================//
-//
-// Purpose:		a simple slider
-//
-// $NoKeywords: $
-//===============================================================================//
+#pragma once
 
 // TODO: fix vertical sliders
 // TODO: this entire class is a mess
 
-#ifndef CBASEUISLIDER_H
-#define CBASEUISLIDER_H
-
 #include "CBaseUIElement.h"
 
 class CBaseUISlider : public CBaseUIElement {
@@ -103,8 +95,7 @@ class CBaseUISlider : public CBaseUIElement {
     float m_fPrevValue;
 
     float m_fKeyDelta;
+    float m_fLastSoundPlayTime = 0.f;
 
     SliderChangeCallback m_sliderChangeCallback;
 };
-
-#endif

+ 18 - 0
src/GUI/CBaseUITextbox.cpp

@@ -12,7 +12,10 @@
 #include "Engine.h"
 #include "Keyboard.h"
 #include "Mouse.h"
+#include "Osu.h"
 #include "ResourceManager.h"
+#include "Skin.h"
+#include "SoundEngine.h"
 
 using namespace std;
 
@@ -322,6 +325,7 @@ void CBaseUITextbox::onKeyDown(KeyboardEvent &e) {
 
     switch(e.getKeyCode()) {
         case KEY_DELETE:
+            engine->getSound()->play(osu->getSkin()->m_deletingText);
             if(m_sText.length() > 0) {
                 if(hasSelectedText())
                     handleDeleteSelectedText();
@@ -344,6 +348,7 @@ void CBaseUITextbox::onKeyDown(KeyboardEvent &e) {
             break;
 
         case KEY_BACKSPACE:
+            engine->getSound()->play(osu->getSkin()->m_deletingText);
             if(m_sText.length() > 0) {
                 if(hasSelectedText())
                     handleDeleteSelectedText();
@@ -392,6 +397,8 @@ void CBaseUITextbox::onKeyDown(KeyboardEvent &e) {
             tickCaret();
             handleCaretKeyboardMove();
             updateCaretX();
+
+            engine->getSound()->play(osu->getSkin()->m_movingTextCursor);
         } break;
 
         case KEY_RIGHT: {
@@ -408,6 +415,8 @@ void CBaseUITextbox::onKeyDown(KeyboardEvent &e) {
             tickCaret();
             handleCaretKeyboardMove();
             updateCaretX();
+
+            engine->getSound()->play(osu->getSkin()->m_movingTextCursor);
         } break;
 
         case KEY_C:
@@ -434,6 +443,7 @@ void CBaseUITextbox::onKeyDown(KeyboardEvent &e) {
 
         case KEY_X:
             if(engine->getKeyboard()->isControlDown() && hasSelectedText()) {
+                engine->getSound()->play(osu->getSkin()->m_deletingText);
                 env->setClipBoardText(getSelectedText());
                 handleDeleteSelectedText();
             }
@@ -445,6 +455,8 @@ void CBaseUITextbox::onKeyDown(KeyboardEvent &e) {
             tickCaret();
             handleCaretKeyboardMove();
             updateCaretX();
+
+            engine->getSound()->play(osu->getSkin()->m_movingTextCursor);
             break;
 
         case KEY_END:
@@ -453,6 +465,8 @@ void CBaseUITextbox::onKeyDown(KeyboardEvent &e) {
             tickCaret();
             handleCaretKeyboardMove();
             updateCaretX();
+
+            engine->getSound()->play(osu->getSkin()->m_movingTextCursor);
             break;
     }
 }
@@ -484,6 +498,10 @@ void CBaseUITextbox::onChar(KeyboardEvent &e) {
     }
 
     tickCaret();
+
+    Sound *sounds[] = {osu->getSkin()->m_typing1, osu->getSkin()->m_typing2, osu->getSkin()->m_typing3,
+                       osu->getSkin()->m_typing4};
+    engine->getSound()->play(sounds[rand() % 4]);
 }
 
 void CBaseUITextbox::handleCaretKeyboardMove() {

+ 1 - 12
src/GUI/CBaseUITextbox.h

@@ -1,13 +1,4 @@
-//================ Copyright (c) 2015, PG, All rights reserved. =================//
-//
-// Purpose:		a not so simple textbox, revision 4
-//
-// $NoKeywords: $
-//===============================================================================//
-
-#ifndef CBASEUITEXTBOX_H
-#define CBASEUITEXTBOX_H
-
+#pragma once
 #include "CBaseUIElement.h"
 
 class McFont;
@@ -148,5 +139,3 @@ class CBaseUITextbox : public CBaseUIElement {
     int m_iSelectEnd;
     int m_iSelectX;
 };
-
-#endif