1
0

2 Achegas d9c3e70e06 ... bd1efc3266

Autor SHA1 Mensaxe Data
  Clément Wolf bd1efc3266 Update audio engine hai 2 semanas
  Clément Wolf ac9405c529 Fix memory leaks hai 2 semanas

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

@@ -1127,15 +1127,11 @@ float Beatmap::getIdealVolume() {
         return volume * modifier;
     }
 
-    auto device_id = BASS_GetDevice();
-    BASS_SetDevice(0);
-
     auto decoder = BASS_StreamCreateFile(false, m_selectedDifficulty2->getFullSoundFilePath().c_str(), 0, 0,
                                          BASS_STREAM_DECODE | BASS_SAMPLE_FLOAT);
     if(!decoder) {
         debugLog("BASS_StreamCreateFile() returned error %d on file %s\n", BASS_ErrorGetCode(),
                  m_selectedDifficulty2->getFullSoundFilePath().c_str());
-        BASS_SetDevice(device_id);
         return volume;
     }
 
@@ -1145,7 +1141,6 @@ float Beatmap::getIdealVolume() {
     if(!BASS_ChannelSetPosition(decoder, position, BASS_POS_BYTE)) {
         debugLog("BASS_ChannelSetPosition() returned error %d\n", BASS_ErrorGetCode());
         BASS_ChannelFree(decoder);
-        BASS_SetDevice(device_id);
         return volume;
     }
 
@@ -1153,7 +1148,6 @@ float Beatmap::getIdealVolume() {
     if(!loudness) {
         debugLog("BASS_Loudness_Start() returned error %d\n", BASS_ErrorGetCode());
         BASS_ChannelFree(decoder);
-        BASS_SetDevice(device_id);
         return volume;
     }
 
@@ -1169,7 +1163,6 @@ float Beatmap::getIdealVolume() {
 
     BASS_Loudness_Stop(loudness);
     BASS_ChannelFree(decoder);
-    BASS_SetDevice(device_id);
 
     last_song = m_selectedDifficulty2->getFullSoundFilePath();
     modifier = (level / -16.f);
@@ -1722,7 +1715,7 @@ void Beatmap::loadMusic(bool stream, bool prescan) {
         if(!stream) engine->getResourceManager()->requestNextLoadAsync();
 
         m_music = engine->getResourceManager()->loadSoundAbs(
-            m_selectedDifficulty2->getFullSoundFilePath(), "OSU_BEATMAP_MUSIC", stream, false, false, false,
+            m_selectedDifficulty2->getFullSoundFilePath(), "OSU_BEATMAP_MUSIC", stream, false, false,
             m_bForceStreamPlayback &&
                 prescan);  // m_bForceStreamPlayback = prescan necessary! otherwise big mp3s will go out of sync
         m_music->setVolume(getIdealVolume());

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

@@ -152,6 +152,8 @@ Chat::Chat(Osu *osu) : OsuScreen(osu) {
     updateLayout(m_osu->getScreenSize());
 }
 
+Chat::~Chat() { delete m_button_container; }
+
 void Chat::draw(Graphics *g) {
     const bool isAnimating = anim->isAnimating(&m_fAnimation);
     if(!m_bVisible && !isAnimating) return;

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

@@ -36,6 +36,7 @@ struct ChatChannel {
 class Chat : public OsuScreen {
    public:
     Chat(Osu *osu);
+    ~Chat();
 
     virtual void draw(Graphics *g);
     virtual void mouse_update(bool *propagate_clicks);

+ 1 - 1
src/App/Osu/DatabaseBeatmap.cpp

@@ -151,7 +151,7 @@ DatabaseBeatmap::DatabaseBeatmap(Osu *osu, std::vector<DatabaseBeatmap *> *diffi
 
 DatabaseBeatmap::~DatabaseBeatmap() {
     for(size_t i = 0; i < m_difficulties->size(); i++) {
-        delete(*m_difficulties)[i];
+        delete((*m_difficulties)[i]);
     }
     SAFE_DELETE(m_difficulties);
 }

+ 60 - 48
src/App/Osu/MainMenu.cpp

@@ -255,13 +255,13 @@ MainMenu::MainMenu(Osu *osu) : OsuScreen(osu) {
     m_pauseButton->setClickCallback(fastdelegate::MakeDelegate(this, &MainMenu::onPausePressed));
     addBaseUIElement(m_pauseButton);
 
-    m_updateAvailableButton =
-        new UIButton(m_osu, 0, 0, 0, 0, "",
-                     Osu::debug->getBool() ? "Debug mode, update check disabled" : "Checking for updates ...");
-    m_updateAvailableButton->setUseDefaultSkin();
-    m_updateAvailableButton->setClickCallback(fastdelegate::MakeDelegate(this, &MainMenu::onUpdatePressed));
-    m_updateAvailableButton->setColor(0x2200ff00);
-    m_updateAvailableButton->setTextColor(0x22ffffff);
+    if(env->getOS() == Environment::OS::OS_WINDOWS) {
+        m_updateAvailableButton = new UIButton(m_osu, 0, 0, 0, 0, "", "Checking for updates ...");
+        m_updateAvailableButton->setUseDefaultSkin();
+        m_updateAvailableButton->setClickCallback(fastdelegate::MakeDelegate(this, &MainMenu::onUpdatePressed));
+        m_updateAvailableButton->setColor(0x2200ff00);
+        m_updateAvailableButton->setTextColor(0x22ffffff);
+    }
 
     m_versionButton = new CBaseUIButton(0, 0, 0, 0, "", "");
     UString versionString = UString::format("Version %.2f", Osu::version->getFloat());
@@ -273,6 +273,9 @@ MainMenu::MainMenu(Osu *osu) : OsuScreen(osu) {
 }
 
 MainMenu::~MainMenu() {
+    SAFE_DELETE(preloaded_beatmapset);
+    SAFE_DELETE(m_updateAvailableButton);
+
     anim->deleteExistingAnimation(&m_fUpdateButtonAnim);
 
     anim->deleteExistingAnimation(&m_fMainMenuAnimFriendEyeFollowX);
@@ -287,8 +290,6 @@ MainMenu::~MainMenu() {
     anim->deleteExistingAnimation(&m_fStartupAnim);
     anim->deleteExistingAnimation(&m_fStartupAnim2);
 
-    SAFE_DELETE(m_updateAvailableButton);
-
     // if the user didn't click on the update notification during this session, quietly remove it so it's not annoying
     if(m_bWasCleanShutdown) writeVersionFile();
 }
@@ -446,7 +447,7 @@ void MainMenu::draw(Graphics *g) {
     OsuScreen::draw(g);
 
     // draw update check button
-    {
+    if(m_updateAvailableButton != nullptr) {
         if(m_osu->getUpdateHandler()->getStatus() == UpdateHandler::STATUS::STATUS_SUCCESS_INSTALLATION) {
             g->push3DScene(McRect(m_updateAvailableButton->getPos().x, m_updateAvailableButton->getPos().y,
                                   m_updateAvailableButton->getSize().x, m_updateAvailableButton->getSize().y));
@@ -921,7 +922,10 @@ void MainMenu::mouse_update(bool *propagate_clicks) {
 
     // update and focus handling
     OsuScreen::mouse_update(propagate_clicks);
-    m_updateAvailableButton->mouse_update(propagate_clicks);
+
+    if(m_updateAvailableButton != nullptr) {
+        m_updateAvailableButton->mouse_update(propagate_clicks);
+    }
 
     // handle automatic menu closing
     if(m_fMainMenuButtonCloseTime != 0.0f && engine->getTime() > m_fMainMenuButtonCloseTime) {
@@ -998,39 +1002,41 @@ void MainMenu::mouse_update(bool *propagate_clicks) {
     }
 
     // handle update checker and status text
-    switch(m_osu->getUpdateHandler()->getStatus()) {
-        case UpdateHandler::STATUS::STATUS_UP_TO_DATE:
-            if(m_updateAvailableButton->isVisible()) {
-                m_updateAvailableButton->setText("");
-                m_updateAvailableButton->setVisible(false);
-            }
-            break;
-        case UpdateHandler::STATUS::STATUS_CHECKING_FOR_UPDATE:
-            m_updateAvailableButton->setText("Checking for updates ...");
-            break;
-        case UpdateHandler::STATUS::STATUS_DOWNLOADING_UPDATE:
-            m_updateAvailableButton->setText("Downloading ...");
-            break;
-        case UpdateHandler::STATUS::STATUS_INSTALLING_UPDATE:
-            m_updateAvailableButton->setText("Installing ...");
-            break;
-        case UpdateHandler::STATUS::STATUS_SUCCESS_INSTALLATION:
-            if(engine->getTime() > m_fUpdateButtonTextTime && anim->isAnimating(&m_fUpdateButtonAnim) &&
-               m_fUpdateButtonAnim > 0.175f) {
-                m_fUpdateButtonTextTime = m_fUpdateButtonAnimTime;
-
-                m_updateAvailableButton->setColor(0xff00ff00);
-                m_updateAvailableButton->setTextColor(0xffffffff);
-
-                if(m_updateAvailableButton->getText().find("ready") != -1)
-                    m_updateAvailableButton->setText("Click here to restart now!");
-                else
-                    m_updateAvailableButton->setText("A new version of neosu is ready!");
-            }
-            break;
-        case UpdateHandler::STATUS::STATUS_ERROR:
-            m_updateAvailableButton->setText("Update Error! Click to retry ...");
-            break;
+    if(m_updateAvailableButton != nullptr) {
+        switch(m_osu->getUpdateHandler()->getStatus()) {
+            case UpdateHandler::STATUS::STATUS_UP_TO_DATE:
+                if(m_updateAvailableButton->isVisible()) {
+                    m_updateAvailableButton->setText("");
+                    m_updateAvailableButton->setVisible(false);
+                }
+                break;
+            case UpdateHandler::STATUS::STATUS_CHECKING_FOR_UPDATE:
+                m_updateAvailableButton->setText("Checking for updates ...");
+                break;
+            case UpdateHandler::STATUS::STATUS_DOWNLOADING_UPDATE:
+                m_updateAvailableButton->setText("Downloading ...");
+                break;
+            case UpdateHandler::STATUS::STATUS_INSTALLING_UPDATE:
+                m_updateAvailableButton->setText("Installing ...");
+                break;
+            case UpdateHandler::STATUS::STATUS_SUCCESS_INSTALLATION:
+                if(engine->getTime() > m_fUpdateButtonTextTime && anim->isAnimating(&m_fUpdateButtonAnim) &&
+                   m_fUpdateButtonAnim > 0.175f) {
+                    m_fUpdateButtonTextTime = m_fUpdateButtonAnimTime;
+
+                    m_updateAvailableButton->setColor(0xff00ff00);
+                    m_updateAvailableButton->setTextColor(0xffffffff);
+
+                    if(m_updateAvailableButton->getText().find("ready") != -1)
+                        m_updateAvailableButton->setText("Click here to restart now!");
+                    else
+                        m_updateAvailableButton->setText("A new version of neosu is ready!");
+                }
+                break;
+            case UpdateHandler::STATUS::STATUS_ERROR:
+                m_updateAvailableButton->setText("Update Error! Click to retry ...");
+                break;
+        }
     }
 
     if(m_osu->getUpdateHandler()->getStatus() == UpdateHandler::STATUS::STATUS_SUCCESS_INSTALLATION &&
@@ -1072,7 +1078,7 @@ void MainMenu::selectRandomBeatmap() {
         if(nb_mapsets == 0) return;
 
         m_osu->getSongBrowser()->getSelectedBeatmap()->deselect();
-        SAFE_DELETE(preloaded_beatmap);
+        SAFE_DELETE(preloaded_beatmapset);
 
         for(int i = 0; i < 10; i++) {
             auto mapset_folder = songs_folder;
@@ -1088,12 +1094,15 @@ void MainMenu::selectRandomBeatmap() {
             auto beatmap_diffs = beatmap->getDifficulties();
             if(beatmap_diffs.size() == 0) {
                 debugLog("Beatmap '%s' has no difficulties!\n", mapset_folder.c_str());
+                delete beatmap;
                 continue;
             }
 
+            preloaded_beatmapset = beatmap;
             preloaded_beatmap = beatmap_diffs[rand() % beatmap_diffs.size()];
             preloaded_beatmap->do_not_store = true;
             m_osu->getSongBrowser()->onDifficultySelected(preloaded_beatmap, false);
+
             return;
         }
 
@@ -1183,9 +1192,12 @@ void MainMenu::updateLayout() {
     m_pauseButton->setRelPos(m_osu->getScreenWidth() - m_pauseButton->getSize().x * 2 - 10 * dpiScale,
                              m_pauseButton->getSize().y + 10 * dpiScale);
 
-    m_updateAvailableButton->setSize(375 * dpiScale, 50 * dpiScale);
-    m_updateAvailableButton->setPos(m_osu->getScreenWidth() / 2 - m_updateAvailableButton->getSize().x / 2,
-                                    m_osu->getScreenHeight() - m_updateAvailableButton->getSize().y - 10 * dpiScale);
+    if(m_updateAvailableButton != nullptr) {
+        m_updateAvailableButton->setSize(375 * dpiScale, 50 * dpiScale);
+        m_updateAvailableButton->setPos(
+            m_osu->getScreenWidth() / 2 - m_updateAvailableButton->getSize().x / 2,
+            m_osu->getScreenHeight() - m_updateAvailableButton->getSize().y - 10 * dpiScale);
+    }
 
     m_versionButton->onResized();  // HACKHACK: framework, setSizeToContent() does not update string metrics
     m_versionButton->setSizeToContent(8 * dpiScale, 8 * dpiScale);

+ 2 - 1
src/App/Osu/MainMenu.h

@@ -59,6 +59,7 @@ class MainMenu : public OsuScreen, public MouseListener {
     virtual void mouse_update(bool *propagate_clicks);
 
     DatabaseBeatmap *preloaded_beatmap = nullptr;
+    DatabaseBeatmap *preloaded_beatmapset = nullptr;
     void selectRandomBeatmap();
 
     virtual void onKeyDown(KeyboardEvent &e);
@@ -128,7 +129,7 @@ class MainMenu : public OsuScreen, public MouseListener {
     std::vector<MainMenuButton *> m_menuElements;
 
     MainMenuPauseButton *m_pauseButton;
-    UIButton *m_updateAvailableButton;
+    UIButton *m_updateAvailableButton = nullptr;
     CBaseUIButton *m_versionButton;
 
     bool m_bDrawVersionNotificationArrow;

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

@@ -1387,6 +1387,13 @@ OptionsMenu::OptionsMenu(Osu *osu) : ScreenBackable(osu) {
         onHighQualitySlidersConVarChange("", osu_options_high_quality_sliders.getString());
 }
 
+OptionsMenu::~OptionsMenu() {
+    // TODO @kiwec: remove them from containers first
+    // SAFE_DELETE(m_asioBufferSizeSlider);
+    // SAFE_DELETE(m_wasapiBufferSizeSlider);
+    // SAFE_DELETE(m_wasapiPeriodSizeSlider);
+}
+
 void OptionsMenu::draw(Graphics *g) {
     const bool isAnimating = anim->isAnimating(&m_fAnimation);
     if(!m_bVisible && !isAnimating) return;

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

@@ -37,6 +37,7 @@ class ConVar;
 class OptionsMenu : public ScreenBackable, public NotificationOverlayKeyListener {
    public:
     OptionsMenu(Osu *osu);
+    ~OptionsMenu();
 
     virtual void draw(Graphics *g);
     virtual void mouse_update(bool *propagate_clicks);

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

@@ -514,7 +514,6 @@ Osu::~Osu() {
     SAFE_DELETE(m_windowManager);
 
     for(int i = 0; i < m_screens.size(); i++) {
-        debugLog("%i\n", i);
         SAFE_DELETE(m_screens[i]);
     }
 

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

@@ -20,7 +20,6 @@ class OsuScreen : public CBaseUIContainer {
         m_osu = osu;
         m_bVisible = false;
     }
-    virtual ~OsuScreen() { ; }
 
     virtual void onResolutionChange(Vector2 newResolution) { (void)newResolution; }
 

+ 23 - 0
src/App/Osu/RoomScreen.cpp

@@ -192,6 +192,29 @@ RoomScreen::RoomScreen(Osu *osu) : OsuScreen(osu) {
     updateLayout(m_osu->getScreenSize());
 }
 
+RoomScreen::~RoomScreen() {
+    m_settings->getContainer()->empty();
+    SAFE_DELETE(m_room_name);
+    SAFE_DELETE(m_change_password_btn);
+    SAFE_DELETE(m_host);
+    SAFE_DELETE(m_room_name_iptl);
+    SAFE_DELETE(m_room_name_ipt);
+    SAFE_DELETE(m_select_map_btn);
+    SAFE_DELETE(m_select_mods_btn);
+    SAFE_DELETE(m_change_win_condition_btn);
+    SAFE_DELETE(m_win_condition);
+    SAFE_DELETE(map_label);
+    SAFE_DELETE(m_map_title);
+    SAFE_DELETE(m_map_stars);
+    SAFE_DELETE(m_map_attributes);
+    SAFE_DELETE(m_map_attributes2);
+    SAFE_DELETE(mods_label);
+    SAFE_DELETE(m_freemod);
+    SAFE_DELETE(m_no_mods_selected);
+    SAFE_DELETE(m_mods);
+    SAFE_DELETE(m_ready_btn);
+}
+
 void RoomScreen::draw(Graphics *g) {
     if(!m_bVisible) return;
 

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

@@ -27,6 +27,7 @@ class UIModList : public CBaseUIContainer {
 class RoomScreen : public OsuScreen {
    public:
     RoomScreen(Osu *osu);
+    ~RoomScreen();
 
     virtual void draw(Graphics *g) override;
     virtual void mouse_update(bool *propagate_clicks) override;

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

@@ -610,7 +610,54 @@ SongBrowser::~SongBrowser() {
     engine->getResourceManager()->destroyResource(m_dynamicStarCalculator);
     engine->getResourceManager()->destroyResource(m_backgroundSearchMatcher);
 
-    // leak memory, who cares we're closing neosu anyway
+    m_songBrowser->getContainer()->empty();
+
+    for(size_t i = 0; i < m_songButtons.size(); i++) {
+        delete m_songButtons[i];
+    }
+    for(size_t i = 0; i < m_collectionButtons.size(); i++) {
+        delete m_collectionButtons[i];
+    }
+    for(size_t i = 0; i < m_artistCollectionButtons.size(); i++) {
+        delete m_artistCollectionButtons[i];
+    }
+    for(size_t i = 0; i < m_difficultyCollectionButtons.size(); i++) {
+        delete m_difficultyCollectionButtons[i];
+    }
+    for(size_t i = 0; i < m_bpmCollectionButtons.size(); i++) {
+        delete m_bpmCollectionButtons[i];
+    }
+    for(size_t i = 0; i < m_creatorCollectionButtons.size(); i++) {
+        delete m_creatorCollectionButtons[i];
+    }
+    for(size_t i = 0; i < m_dateaddedCollectionButtons.size(); i++) {
+        delete m_dateaddedCollectionButtons[i];
+    }
+    for(size_t i = 0; i < m_lengthCollectionButtons.size(); i++) {
+        delete m_lengthCollectionButtons[i];
+    }
+    for(size_t i = 0; i < m_titleCollectionButtons.size(); i++) {
+        delete m_titleCollectionButtons[i];
+    }
+
+    m_scoreBrowser->getContainer()->empty();
+    for(size_t i = 0; i < m_scoreButtonCache.size(); i++) {
+        delete m_scoreButtonCache[i];
+    }
+    SAFE_DELETE(m_scoreBrowserScoresStillLoadingElement);
+    SAFE_DELETE(m_scoreBrowserNoRecordsYetElement);
+
+    for(size_t i = 0; i < m_sortingMethods.size(); i++) {
+        delete m_sortingMethods[i].comparator;
+    }
+
+    SAFE_DELETE(m_search);
+    SAFE_DELETE(m_topbarLeft);
+    SAFE_DELETE(m_topbarRight);
+    SAFE_DELETE(m_bottombar);
+    SAFE_DELETE(m_scoreBrowser);
+    SAFE_DELETE(m_songBrowser);
+    SAFE_DELETE(m_db);
 }
 
 void SongBrowser::draw(Graphics *g) {

+ 2 - 8
src/App/Osu/VolumeOverlay.cpp

@@ -261,20 +261,14 @@ bool VolumeOverlay::canChangeVolume() {
 }
 
 void VolumeOverlay::gainFocus() {
-    if(engine->getSound()->isWASAPI()) {
-        // NOTE: wasapi exclusive mode controls the system volume, so don't bother
-        return;
-    }
+    if(engine->getSound()->hasExclusiveOutput()) return;
 
     m_fVolumeInactiveToActiveAnim = 0.0f;
     anim->moveLinear(&m_fVolumeInactiveToActiveAnim, 1.0f, 0.3f, 0.1f, true);
 }
 
 void VolumeOverlay::loseFocus() {
-    if(engine->getSound()->isWASAPI()) {
-        // NOTE: wasapi exclusive mode controls the system volume, so don't bother
-        return;
-    }
+    if(engine->getSound()->hasExclusiveOutput()) return;
 
     m_bVolumeInactiveToActiveScheduled = true;
     anim->deleteExistingAnimation(&m_fVolumeInactiveToActiveAnim);

+ 5 - 13
src/Engine/Font.cpp

@@ -7,13 +7,6 @@
 
 #include "Font.h"
 
-#include <freetype/freetype.h>
-#include <freetype/ftbitmap.h>
-#include <freetype/ftglyph.h>
-#include <freetype/ftoutln.h>
-#include <freetype/fttrigon.h>
-#include <ft2build.h>
-
 #include "ConVar.h"
 #include "Engine.h"
 #include "ResourceManager.h"
@@ -23,9 +16,6 @@ ConVar r_drawstring_max_string_length("r_drawstring_max_string_length", 65536, F
                                       "maximum number of characters per call, sanity/memory buffer limit");
 ConVar r_debug_drawstring_unbind("r_debug_drawstring_unbind", false, FCVAR_NONE);
 
-static void renderFTGlyphToTextureAtlas(FT_Library library, FT_Face face, wchar_t ch, TextureAtlas *textureAtlas,
-                                        bool antialiasing,
-                                        std::unordered_map<wchar_t, McFont::GLYPH_METRICS> *glyphMetrics);
 static unsigned char *unpackMonoBitmap(FT_Bitmap bitmap);
 
 const wchar_t McFont::UNKNOWN_CHAR;
@@ -251,9 +241,9 @@ bool McFont::hasGlyph(wchar_t ch) const { return (m_vGlyphMetrics.find(ch) != m_
 
 // helper functions
 
-static void renderFTGlyphToTextureAtlas(FT_Library library, FT_Face face, wchar_t ch, TextureAtlas *textureAtlas,
-                                        bool antialiasing,
-                                        std::unordered_map<wchar_t, McFont::GLYPH_METRICS> *glyphMetrics) {
+void McFont::renderFTGlyphToTextureAtlas(FT_Library library, FT_Face face, wchar_t ch, TextureAtlas *textureAtlas,
+                                         bool antialiasing,
+                                         std::unordered_map<wchar_t, McFont::GLYPH_METRICS> *glyphMetrics) {
     // load current glyph
     if(FT_Load_Glyph(face, FT_Get_Char_Index(face, ch), antialiasing ? FT_LOAD_TARGET_NORMAL : FT_LOAD_TARGET_MONO)) {
         // engine->showMessageError("Font Error", "FT_Load_Glyph() failed!");
@@ -282,6 +272,8 @@ static void renderFTGlyphToTextureAtlas(FT_Library library, FT_Face face, wchar_
     const int width = bitmap.width;
     const int height = bitmap.rows;
 
+    debugLog("Creating texture for font %s (%dpt)\n", m_sFilePath.c_str(), m_iFontSize);
+
     // build texture
     Vector2 atlasPos;
     if(width > 0 && height > 0) {

+ 10 - 11
src/Engine/Font.h

@@ -1,12 +1,10 @@
-//================ Copyright (c) 2015, PG, All rights reserved. =================//
-//
-// Purpose:		freetype font wrapper
-//
-// $NoKeywords: $fnt
-//===============================================================================//
-
-#ifndef FONT_H
-#define FONT_H
+#pragma once
+#include <freetype/freetype.h>
+#include <freetype/ftbitmap.h>
+#include <freetype/ftglyph.h>
+#include <freetype/ftoutln.h>
+#include <freetype/fttrigon.h>
+#include <ft2build.h>
 
 #include "Resource.h"
 
@@ -70,6 +68,9 @@ class McFont : public Resource {
     bool addGlyph(wchar_t ch);
 
     void addAtlasGlyphToVao(Graphics *g, wchar_t ch, float &advanceX, VertexArrayObject *vao);
+    void renderFTGlyphToTextureAtlas(FT_Library library, FT_Face face, wchar_t ch, TextureAtlas *textureAtlas,
+                                     bool antialiasing,
+                                     std::unordered_map<wchar_t, McFont::GLYPH_METRICS> *glyphMetrics);
 
     int m_iFontSize;
     bool m_bAntialiasing;
@@ -86,5 +87,3 @@ class McFont : public Resource {
 
     GLYPH_METRICS m_errorGlyph;
 };
-
-#endif

+ 2 - 2
src/Engine/ResourceManager.cpp

@@ -374,7 +374,7 @@ McFont *ResourceManager::loadFont(std::string filepath, std::string resourceName
 }
 
 Sound *ResourceManager::loadSoundAbs(std::string filepath, std::string resourceName, bool stream, bool overlayable,
-                                     bool threeD, bool loop, bool prescan) {
+                                     bool loop, bool prescan) {
     // check if it already exists
     if(resourceName.length() > 0) {
         Resource *temp = checkIfExistsAndHandle(resourceName);
@@ -382,7 +382,7 @@ Sound *ResourceManager::loadSoundAbs(std::string filepath, std::string resourceN
     }
 
     // create instance and load it
-    Sound *snd = new Sound(filepath, stream, overlayable, threeD, loop, prescan);
+    Sound *snd = new Sound(filepath, stream, overlayable, loop, prescan);
     snd->setName(resourceName);
 
     loadResource(snd, true);

+ 1 - 1
src/Engine/ResourceManager.h

@@ -94,7 +94,7 @@ class ResourceManager {
 
     // sounds
     Sound *loadSoundAbs(std::string filepath, std::string resourceName, bool stream = false, bool overlayable = false,
-                        bool threeD = false, bool loop = false, bool prescan = false);
+                        bool loop = false, bool prescan = false);
 
     // shaders
     Shader *loadShader(std::string vertexShaderFilePath, std::string fragmentShaderFilePath, std::string resourceName);

+ 24 - 46
src/Engine/Sound.cpp

@@ -21,12 +21,10 @@ ConVar snd_wav_file_min_size("snd_wav_file_min_size", 51, FCVAR_NONE,
                              "minimum file size in bytes for WAV files to be considered valid (everything below will "
                              "fail to load), this is a workaround for BASS crashes");
 
-Sound::Sound(std::string filepath, bool stream, bool overlayable, bool threeD, bool loop, bool prescan)
-    : Resource(filepath) {
+Sound::Sound(std::string filepath, bool stream, bool overlayable, bool loop, bool prescan) : Resource(filepath) {
     m_sample = 0;
     m_stream = 0;
     m_bStream = stream;
-    m_bIs3d = threeD;
     m_bIsLooped = loop;
     m_bPrescan = prescan;
     m_bIsOverlayable = overlayable;
@@ -38,35 +36,19 @@ Sound::Sound(std::string filepath, bool stream, bool overlayable, bool threeD, b
 std::vector<HCHANNEL> Sound::getActiveChannels() {
     std::vector<HCHANNEL> channels;
 
-    if(engine->getSound()->isMixing()) {
-        if(m_bStream) {
-            if(BASS_Mixer_ChannelGetMixer(m_stream) != 0) {
-                channels.push_back(m_stream);
-            }
-        } else {
-            for(auto chan : mixer_channels) {
-                if(BASS_Mixer_ChannelGetMixer(chan) != 0) {
-                    channels.push_back(chan);
-                }
-            }
-
-            // Only keep channels that are still playing
-            mixer_channels = channels;
+    if(m_bStream) {
+        if(BASS_Mixer_ChannelGetMixer(m_stream) != 0) {
+            channels.push_back(m_stream);
         }
     } else {
-        if(m_bStream) {
-            if(BASS_ChannelIsActive(m_stream) == BASS_ACTIVE_PLAYING) {
-                channels.push_back(m_stream);
-            }
-        } else {
-            HCHANNEL chans[MAX_OVERLAPPING_SAMPLES] = {0};
-            int nb = BASS_SampleGetChannels(m_sample, chans);
-            for(int i = 0; i < nb; i++) {
-                if(BASS_ChannelIsActive(chans[i]) == BASS_ACTIVE_PLAYING) {
-                    channels.push_back(chans[i]);
-                }
+        for(auto chan : mixer_channels) {
+            if(BASS_Mixer_ChannelGetMixer(chan) != 0) {
+                channels.push_back(chan);
             }
         }
+
+        // Only keep channels that are still playing
+        mixer_channels = channels;
     }
 
     return channels;
@@ -76,15 +58,11 @@ HCHANNEL Sound::getChannel() {
     if(m_bStream) {
         return m_stream;
     } else {
-        if(engine->getSound()->isMixing()) {
-            // If we want to be able to control samples after playing them, we
-            // have to store them here, since bassmix only accepts DECODE streams.
-            auto chan = BASS_SampleGetChannel(m_sample, BASS_SAMCHAN_STREAM | BASS_STREAM_DECODE);
-            mixer_channels.push_back(chan);
-            return chan;
-        } else {
-            return BASS_SampleGetChannel(m_sample, 0);
-        }
+        // If we want to be able to control samples after playing them, we
+        // have to store them here, since bassmix only accepts DECODE streams.
+        auto chan = BASS_SampleGetChannel(m_sample, BASS_SAMCHAN_STREAM | BASS_STREAM_DECODE);
+        mixer_channels.push_back(chan);
+        return chan;
     }
 }
 
@@ -139,17 +117,13 @@ void Sound::initAsync() {
             return;
         }
 
-        auto fx_flags = BASS_FX_FREESOURCE;
-        if(engine->getSound()->isMixing()) fx_flags |= BASS_STREAM_DECODE;
-        m_stream = BASS_FX_TempoCreate(m_stream, fx_flags);
+        m_stream = BASS_FX_TempoCreate(m_stream, BASS_FX_FREESOURCE | BASS_STREAM_DECODE);
         if(!m_stream) {
             debugLog("BASS_FX_TempoCreate() returned error %d on file %s\n", BASS_ErrorGetCode(), m_sFilePath.c_str());
             return;
         }
     } else {
         auto flags = BASS_SAMPLE_FLOAT | BASS_SAMPLE_OVER_POS;
-        if(m_bIs3d) flags |= BASS_SAMPLE_3D | BASS_SAMPLE_MONO;
-
         m_sample =
             BASS_SampleLoad(false, m_sFilePath.c_str(), 0, 0, m_bIsOverlayable ? MAX_OVERLAPPING_SAMPLES : 1, flags);
         if(!m_sample) {
@@ -168,14 +142,18 @@ void Sound::destroy() {
     m_fLastPlayTime = 0.0;
 
     if(m_bStream) {
-        if(engine->getSound()->isMixing()) {
-            BASS_Mixer_ChannelRemove(m_stream);
-        }
-
+        BASS_Mixer_ChannelRemove(m_stream);
         BASS_ChannelStop(m_stream);
         BASS_StreamFree(m_stream);
         m_stream = 0;
     } else {
+        for(auto chan : mixer_channels) {
+            BASS_Mixer_ChannelRemove(chan);
+            BASS_ChannelStop(chan);
+            BASS_ChannelFree(chan);
+        }
+        mixer_channels.clear();
+
         BASS_SampleStop(m_sample);
         BASS_SampleFree(m_sample);
         m_sample = 0;

+ 1 - 3
src/Engine/Sound.h

@@ -21,7 +21,7 @@ class Sound : public Resource {
     typedef unsigned long SOUNDHANDLE;
 
    public:
-    Sound(std::string filepath, bool stream, bool overlayable, bool threeD, bool loop, bool prescan);
+    Sound(std::string filepath, bool stream, bool overlayable, bool loop, bool prescan);
     virtual ~Sound() { destroy(); }
 
     std::vector<HCHANNEL> mixer_channels;
@@ -50,7 +50,6 @@ class Sound : public Resource {
     bool isFinished();
 
     inline bool isStream() const { return m_bStream; }
-    inline bool is3d() const { return m_bIs3d; }
     inline bool isLooped() const { return m_bIsLooped; }
     inline bool isOverlayable() const { return m_bIsOverlayable; }
 
@@ -66,7 +65,6 @@ class Sound : public Resource {
 
     bool m_bPaused = false;
     bool m_bStream;
-    bool m_bIs3d;
     bool m_bIsLooped;
     bool m_bPrescan;
     bool m_bIsOverlayable;

+ 55 - 119
src/Engine/SoundEngine.cpp

@@ -65,12 +65,6 @@ ConVar asio_buffer_size("asio_buffer_size", -1, FCVAR_NONE,
 
 ConVar osu_universal_offset_hardcoded("osu_universal_offset_hardcoded", 0.0f, FCVAR_NONE);
 
-DWORD CALLBACK OutputWasapiProc(void *buffer, DWORD length, void *user) {
-    if(engine->getSound()->g_bassOutputMixer == 0) return 0;
-    const int c = BASS_ChannelGetData(engine->getSound()->g_bassOutputMixer, buffer, length);
-    return c < 0 ? 0 : c;
-}
-
 void _RESTART_SOUND_ENGINE_ON_CHANGE(UString oldValue, UString newValue) {
     const int oldValueMS = std::round(oldValue.toFloat() * 1000.0f);
     const int newValueMS = std::round(newValue.toFloat() * 1000.0f);
@@ -326,26 +320,23 @@ bool SoundEngine::initializeOutputDevice(OUTPUT_DEVICE device) {
     debugLog("SoundEngine: initializeOutputDevice( %s ) ...\n", device.name.toUtf8());
 
     if(m_currentOutputDevice.driver == OutputDriver::BASS) {
-        BASS_SetDevice(0);
-        BASS_Free();
         BASS_SetDevice(m_currentOutputDevice.id);
-
         BASS_Free();
-    } else if(m_currentOutputDevice.driver == OutputDriver::BASS_ASIO) {
+        BASS_SetDevice(0);
+    }
+
 #ifdef _WIN32
-        g_bassOutputMixer = 0;
+    if(m_currentOutputDevice.driver == OutputDriver::BASS_ASIO) {
         BASS_ASIO_Free();
-#endif
-        BASS_Free();
     } else if(m_currentOutputDevice.driver == OutputDriver::BASS_WASAPI) {
-#ifdef _WIN32
-        g_bassOutputMixer = 0;
         BASS_WASAPI_Free();
-#endif
-        BASS_Free();
     }
+#endif
 
-    if(device.driver == OutputDriver::NONE) {
+    g_bassOutputMixer = 0;
+    BASS_Free();  // free "No sound" device
+
+    if(device.driver == OutputDriver::NONE || (device.driver == OutputDriver::BASS && device.id == 0)) {
         m_bReady = true;
         m_currentOutputDevice = device;
         snd_output_device.setValue(m_currentOutputDevice.name);
@@ -359,14 +350,18 @@ bool SoundEngine::initializeOutputDevice(OUTPUT_DEVICE device) {
     }
 
     if(device.driver == OutputDriver::BASS) {
-        // NOTE: only used by osu atm (new osu uses 5 instead of 10, but not tested enough for offset problems)
-        BASS_SetConfig(BASS_CONFIG_UPDATEPERIOD, 10);
+        // Normal output: render playback buffer every 5ms (buffer is 100ms large)
+        BASS_SetConfig(BASS_CONFIG_UPDATEPERIOD, 5);
         BASS_SetConfig(BASS_CONFIG_UPDATETHREADS, 1);
     } else if(device.driver == OutputDriver::BASS_ASIO || device.driver == OutputDriver::BASS_WASAPI) {
+        // ASIO/WASAPI: let driver decide when to render playback buffer
         BASS_SetConfig(BASS_CONFIG_UPDATEPERIOD, 0);
         BASS_SetConfig(BASS_CONFIG_UPDATETHREADS, 0);
     }
 
+    // Base offset compensation. Gets modified later when using ASIO driver.
+    osu_universal_offset_hardcoded.setValue(-25.0f);
+
     // allow users to override some defaults (but which may cause beatmap desyncs)
     // we only want to set these if their values have been explicitly modified (to avoid sideeffects in the default
     // case, and for my sanity)
@@ -388,36 +383,40 @@ bool SoundEngine::initializeOutputDevice(OUTPUT_DEVICE device) {
     hwnd = winEnv->getHwnd();
 #endif
 
-    int bass_device_id = device.id;
-    unsigned int runtimeFlags = BASS_DEVICE_STEREO | BASS_DEVICE_FREQ;
+    // We initialize a "No sound" device for measuring loudness and mixing sounds,
+    // regardless of the device we'll use for actual output.
+    unsigned int runtimeFlags = BASS_DEVICE_STEREO | BASS_DEVICE_FREQ | BASS_DEVICE_NOSPEAKER;
+    if(!BASS_Init(0, freq, runtimeFlags | BASS_DEVICE_SOFTWARE, hwnd, NULL)) {
+        m_bReady = false;
+        engine->showMessageError("Sound Error", UString::format("BASS_Init(0) failed (%i)!", BASS_ErrorGetCode()));
+        return false;
+    }
+
     if(device.driver == OutputDriver::BASS) {
-        // Regular BASS: we still want a "No sound" device to check for loudness
-        if(!BASS_Init(0, freq, runtimeFlags | BASS_DEVICE_NOSPEAKER, hwnd, NULL)) {
+        if(!BASS_Init(device.id, freq, runtimeFlags, hwnd, NULL)) {
             m_bReady = false;
-            engine->showMessageError("Sound Error", UString::format("BASS_Init(0) failed (%i)!", BASS_ErrorGetCode()));
+            engine->showMessageError("Sound Error",
+                                     UString::format("BASS_Init(%d) failed (%i)!", device.id, BASS_ErrorGetCode()));
             return false;
         }
-    } else if(device.driver == OutputDriver::BASS_ASIO || device.driver == OutputDriver::BASS_WASAPI) {
-        // ASIO and WASAPI: Initialize BASS on "No sound" device
-        runtimeFlags |= BASS_DEVICE_NOSPEAKER;
-        bass_device_id = 0;
     }
 
-    if(!BASS_Init(bass_device_id, freq, runtimeFlags, hwnd, NULL)) {
+    auto mixer_flags = BASS_SAMPLE_FLOAT | BASS_MIXER_NONSTOP | BASS_MIXER_RESUME;
+    if(device.driver != OutputDriver::BASS) mixer_flags |= BASS_STREAM_DECODE;
+    g_bassOutputMixer = BASS_Mixer_StreamCreate(freq, 2, mixer_flags);
+    if(g_bassOutputMixer == 0) {
         m_bReady = false;
         engine->showMessageError("Sound Error",
-                                 UString::format("BASS_Init(%d) failed (%i)!", bass_device_id, BASS_ErrorGetCode()));
+                                 UString::format("BASS_Mixer_StreamCreate() failed (%i)!", BASS_ErrorGetCode()));
         return false;
     }
 
-    // Starting with bass 2020 2.4.15.2 which has all offset problems
-    // fixed, this is the non-dsound backend compensation.
-    // Gets overwritten later if ASIO or WASAPI driver is used.
-    // Depends on BASS_CONFIG_UPDATEPERIOD/BASS_CONFIG_DEV_BUFFER.
-    osu_universal_offset_hardcoded.setValue(15.0f);
+    // Switch to "No sound" device for all future sound processing
+    // Only g_bassOutputMixer will be output to the actual device!
+    BASS_SetDevice(0);
 
-    if(device.driver == OutputDriver::BASS_ASIO) {
 #ifdef _WIN32
+    if(device.driver == OutputDriver::BASS_ASIO) {
         if(!BASS_ASIO_Init(device.id, 0)) {
             m_bReady = false;
             engine->showMessageError("Sound Error",
@@ -442,15 +441,6 @@ bool SoundEngine::initializeOutputDevice(OUTPUT_DEVICE device) {
             slider->setKeyDelta(info.bufgran == -1 ? info.bufmin : info.bufgran);
         }
 
-        auto mixer_flags = BASS_SAMPLE_FLOAT | BASS_STREAM_DECODE | BASS_MIXER_NONSTOP;
-        g_bassOutputMixer = BASS_Mixer_StreamCreate(sample_rate, 2, mixer_flags);
-        if(g_bassOutputMixer == 0) {
-            m_bReady = false;
-            engine->showMessageError("Sound Error",
-                                     UString::format("BASS_Mixer_StreamCreate() failed (%i)!", BASS_ErrorGetCode()));
-            return false;
-        }
-
         if(!BASS_ASIO_ChannelEnableBASS(false, 0, g_bassOutputMixer, true)) {
             m_bReady = false;
             engine->showMessageError("Sound Error", UString::format("BASS_ASIO_ChannelEnableBASS() failed (code %i)!",
@@ -470,20 +460,18 @@ bool SoundEngine::initializeOutputDevice(OUTPUT_DEVICE device) {
         osu_universal_offset_hardcoded.setValue(-(actual_latency + 25.0f));
         debugLog("ASIO: wanted %f ms, got %f ms latency. Sample rate: %f Hz\n", wanted_latency, actual_latency,
                  sample_rate);
-#endif
     }
 
     if(device.driver == OutputDriver::BASS_WASAPI) {
-#ifdef _WIN32
         const float bufferSize = std::round(win_snd_wasapi_buffer_size.getFloat() * 1000.0f) / 1000.0f;    // in seconds
         const float updatePeriod = std::round(win_snd_wasapi_period_size.getFloat() * 1000.0f) / 1000.0f;  // in seconds
 
         // BASS_WASAPI_RAW ignores sound "enhancements" that some sound cards offer (adds latency)
         // BASS_MIXER_NONSTOP prevents some sound cards from going to sleep when there is no output
+        // BASS_WASAPI_EXCLUSIVE makes neosu have exclusive output to the sound card
         auto flags = BASS_WASAPI_RAW | BASS_MIXER_NONSTOP | BASS_WASAPI_EXCLUSIVE;
 
-        debugLog("WASAPI bufferSize = %f, updatePeriod = %f\n", bufferSize, updatePeriod);
-        if(!BASS_WASAPI_Init(device.id, 0, 0, flags, bufferSize, updatePeriod, OutputWasapiProc, NULL)) {
+        if(!BASS_WASAPI_Init(device.id, 0, 0, flags, bufferSize, updatePeriod, WASAPIPROC_BASS, g_bassOutputMixer)) {
             const int errorCode = BASS_ErrorGetCode();
             if(errorCode == BASS_ERROR_WASAPI_BUFFER) {
                 debugLog("Sound Error: BASS_WASAPI_Init() failed with BASS_ERROR_WASAPI_BUFFER!");
@@ -495,28 +483,14 @@ bool SoundEngine::initializeOutputDevice(OUTPUT_DEVICE device) {
             return false;
         }
 
-        BASS_WASAPI_INFO wasapiInfo;
-        BASS_WASAPI_GetInfo(&wasapiInfo);
-        auto mixer_flags = BASS_SAMPLE_FLOAT | BASS_STREAM_DECODE | BASS_MIXER_NONSTOP;
-        g_bassOutputMixer = BASS_Mixer_StreamCreate(wasapiInfo.freq, wasapiInfo.chans, mixer_flags);
-        if(g_bassOutputMixer == 0) {
-            m_bReady = false;
-            engine->showMessageError("Sound Error",
-                                     UString::format("BASS_Mixer_StreamCreate() failed (%i)!", BASS_ErrorGetCode()));
-            return false;
-        }
-
         if(!BASS_WASAPI_Start()) {
             m_bReady = false;
             engine->showMessageError("Sound Error",
                                      UString::format("BASS_WASAPI_Start() failed (%i)!", BASS_ErrorGetCode()));
             return false;
         }
-
-        // since we use the newer bass/fx dlls for wasapi builds anyway (which have different time handling)
-        osu_universal_offset_hardcoded.setValue(-25.0f);
-#endif
     }
+#endif
 
     m_bReady = true;
     m_currentOutputDevice = device;
@@ -583,24 +557,25 @@ bool SoundEngine::play(Sound *snd, float pan, float pitch) {
 
     BASS_ChannelFlags(channel, snd->isLooped() ? BASS_SAMPLE_LOOP : 0, BASS_SAMPLE_LOOP);
 
-    if(isMixing()) {
-        if(BASS_Mixer_ChannelGetMixer(channel) != 0) return false;
+    if(BASS_Mixer_ChannelGetMixer(channel) != 0) return false;
 
-        auto flags = BASS_MIXER_DOWNMIX | BASS_MIXER_NORAMPIN;
-        if(snd->isStream()) {
-            flags |= BASS_STREAM_AUTOFREE;
-        }
+    auto flags = BASS_MIXER_DOWNMIX | BASS_MIXER_NORAMPIN;
+    if(snd->isStream()) {
+        flags |= BASS_STREAM_AUTOFREE;
+    }
 
-        if(!BASS_Mixer_StreamAddChannel(g_bassOutputMixer, channel, flags)) {
-            debugLog("BASS_Mixer_StreamAddChannel() failed (%i)!\n", BASS_ErrorGetCode());
-            return false;
-        }
-    } else {
-        if(BASS_ChannelIsActive(channel) == BASS_ACTIVE_PLAYING) return false;
+    if(!BASS_Mixer_StreamAddChannel(g_bassOutputMixer, channel, flags)) {
+        debugLog("BASS_Mixer_StreamAddChannel() failed (%i)!\n", BASS_ErrorGetCode());
+        return false;
+    }
 
-        if(!BASS_ChannelPlay(channel, false)) {
-            debugLog("SoundEngine::play() couldn't BASS_ChannelPlay(), errorcode %i\n", BASS_ErrorGetCode());
-            return false;
+    // Make sure the mixer is playing! Duh.
+    if(m_currentOutputDevice.driver == OutputDriver::BASS) {
+        if(BASS_ChannelIsActive(g_bassOutputMixer) != BASS_ACTIVE_PLAYING) {
+            if(!BASS_ChannelPlay(g_bassOutputMixer, false)) {
+                debugLog("SoundEngine::play() couldn't BASS_ChannelPlay(), errorcode %i\n", BASS_ErrorGetCode());
+                return false;
+            }
         }
     }
 
@@ -609,32 +584,6 @@ bool SoundEngine::play(Sound *snd, float pan, float pitch) {
     return true;
 }
 
-bool SoundEngine::play3d(Sound *snd, Vector3 pos) {
-    if(!m_bReady || snd == NULL || !snd->isReady() || !snd->is3d()) return false;
-    if(snd_restrict_play_frame.getBool() && engine->getTime() <= snd->getLastPlayTime()) return false;
-
-    HCHANNEL channel = snd->getChannel();
-    if(channel == 0) {
-        debugLog("SoundEngine::play3d() failed to get channel, errorcode %i\n", BASS_ErrorGetCode());
-        return false;
-    }
-
-    BASS_3DVECTOR bassPos = BASS_3DVECTOR(pos.x, pos.y, pos.z);
-    if(!BASS_ChannelSet3DPosition(channel, &bassPos, NULL, NULL)) {
-        debugLog("SoundEngine::play3d() couldn't BASS_ChannelSet3DPosition(), errorcode %i\n", BASS_ErrorGetCode());
-        return false;
-    }
-
-    BASS_Apply3D();
-    if(BASS_ChannelPlay(channel, false)) {
-        snd->setLastPlayTime(engine->getTime());
-        return true;
-    } else {
-        debugLog("SoundEngine::play3d() couldn't BASS_ChannelPlay(), errorcode %i\n", BASS_ErrorGetCode());
-        return false;
-    }
-}
-
 void SoundEngine::pause(Sound *snd) {
     if(!m_bReady || snd == NULL || !snd->isReady()) return;
     if(!snd->isStream()) {
@@ -724,19 +673,6 @@ void SoundEngine::setVolume(float volume) {
     }
 }
 
-void SoundEngine::set3dPosition(Vector3 headPos, Vector3 viewDir, Vector3 viewUp) {
-    if(!m_bReady) return;
-
-    BASS_3DVECTOR bassHeadPos = BASS_3DVECTOR(headPos.x, headPos.y, headPos.z);
-    BASS_3DVECTOR bassViewDir = BASS_3DVECTOR(viewDir.x, viewDir.y, viewDir.z);
-    BASS_3DVECTOR bassViewUp = BASS_3DVECTOR(viewUp.x, viewUp.y, viewUp.z);
-
-    if(!BASS_Set3DPosition(&bassHeadPos, NULL, &bassViewDir, &bassViewUp))
-        debugLog("SoundEngine::set3dPosition() couldn't BASS_Set3DPosition(), errorcode %i\n", BASS_ErrorGetCode());
-    else
-        BASS_Apply3D();  // apply the changes
-}
-
 void SoundEngine::onFreqChanged(UString oldValue, UString newValue) {
     (void)oldValue;
     (void)newValue;

+ 1 - 6
src/Engine/SoundEngine.h

@@ -36,20 +36,15 @@ class SoundEngine {
     void update();
 
     bool play(Sound *snd, float pan = 0.0f, float pitch = 1.0f);
-    bool play3d(Sound *snd, Vector3 pos);
     void pause(Sound *snd);
     void stop(Sound *snd);
 
     bool isASIO() { return m_currentOutputDevice.driver == OutputDriver::BASS_ASIO; }
     bool isWASAPI() { return m_currentOutputDevice.driver == OutputDriver::BASS_WASAPI; }
-    bool isMixing() {
-        return m_currentOutputDevice.driver == OutputDriver::BASS_ASIO ||
-               m_currentOutputDevice.driver == OutputDriver::BASS_WASAPI;
-    }
+    bool hasExclusiveOutput() { return isASIO() || isWASAPI(); }
 
     bool setOutputDevice(OUTPUT_DEVICE device);
     void setVolume(float volume);
-    void set3dPosition(Vector3 headPos, Vector3 viewDir, Vector3 viewUp);
 
     OUTPUT_DEVICE getDefaultDevice();
     OUTPUT_DEVICE getWantedDevice();

+ 1 - 0
src/Util/cbase.h

@@ -134,6 +134,7 @@ struct zarray {
             nb = nb_initial;
         }
     }
+    ~zarray() { free(memory); }
 
     void push_back(T t) {
         if(nb + 1 > max) {