From c9dec8f5a9bf5291aaab0f954a746073407bfdb5 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Wed, 2 Sep 2020 14:36:16 -0400 Subject: [PATCH 1/4] Factor ProjectFileIO::GetConnection (returning reference not pointer) --- src/ProjectFileIO.cpp | 13 +++++++++++-- src/ProjectFileIO.h | 2 ++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/ProjectFileIO.cpp b/src/ProjectFileIO.cpp index 0ea12f42d..6121d477a 100644 --- a/src/ProjectFileIO.cpp +++ b/src/ProjectFileIO.cpp @@ -256,7 +256,7 @@ ProjectFileIO::~ProjectFileIO() { } -sqlite3 *ProjectFileIO::DB() +DBConnection &ProjectFileIO::GetConnection() { auto &curConn = CurrConn(); if (!curConn) @@ -270,9 +270,18 @@ sqlite3 *ProjectFileIO::DB() } } - return curConn->DB(); + return *curConn; } +sqlite3 *ProjectFileIO::DB() +{ + return GetConnection().DB(); +} + +/*! + @pre *CurConn() does not exist + @post *CurConn() exists or return value is false + */ bool ProjectFileIO::OpenConnection(FilePath fileName /* = {} */) { auto &curConn = CurrConn(); diff --git a/src/ProjectFileIO.h b/src/ProjectFileIO.h index 4434cb70f..b5374e80c 100644 --- a/src/ProjectFileIO.h +++ b/src/ProjectFileIO.h @@ -142,6 +142,8 @@ public: // 0 for success or non-zero to stop the query using ExecCB = std::function; + DBConnection &GetConnection(); + private: void WriteXMLHeader(XMLWriter &xmlFile) const; void WriteXML(XMLWriter &xmlFile, bool recording = false, const std::shared_ptr &tracks = nullptr) /* not override */; From eb7e67623e042b7ac447cf51a5da1851e64c7d24 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Wed, 2 Sep 2020 14:59:03 -0400 Subject: [PATCH 2/4] Demote error message storage from ProjectFileIO into DBConnection --- src/DBConnection.cpp | 22 ++++++++++++++++ src/DBConnection.h | 18 ++++++++++++++ src/ProjectFileIO.cpp | 58 +++++++++++++++++++------------------------ src/ProjectFileIO.h | 16 ++++++------ 4 files changed, 74 insertions(+), 40 deletions(-) diff --git a/src/DBConnection.cpp b/src/DBConnection.cpp index d0b3b8348..db91bd1f1 100644 --- a/src/DBConnection.cpp +++ b/src/DBConnection.cpp @@ -55,6 +55,28 @@ bool DBConnection::ShouldBypass() return mBypass; } +void DBConnection::SetError( + const TranslatableString &msg, const TranslatableString &libraryError) +{ + mLastError = msg; + mLibraryError = libraryError; +} + +void DBConnection::SetDBError( + const TranslatableString &msg, const TranslatableString &libraryError) +{ + mLastError = msg; + wxLogDebug(wxT("SQLite error: %s"), mLastError.Debug()); + printf(" Lib error: %s", mLastError.Debug().mb_str().data()); + + mLibraryError = libraryError.empty() + ? Verbatim(sqlite3_errmsg(DB())) : libraryError; + wxLogDebug(wxT(" Lib error: %s"), mLibraryError.Debug()); + printf(" Lib error: %s", mLibraryError.Debug().mb_str().data()); + + wxASSERT(false); +} + bool DBConnection::Open(const char *fileName) { wxASSERT(mDB == nullptr); diff --git a/src/DBConnection.h b/src/DBConnection.h index c208639c3..0d0dc0105 100644 --- a/src/DBConnection.h +++ b/src/DBConnection.h @@ -69,6 +69,21 @@ public: void SetBypass( bool bypass ); bool ShouldBypass(); + //! Just set stored errors + void SetError( + const TranslatableString &msg, + const TranslatableString &libraryError = {} ); + + //! Set stored errors and write to log; and default libraryError to what database library reports + void SetDBError( + const TranslatableString &msg, + const TranslatableString &libraryError = {} ); + + const TranslatableString &GetLastError() const + { return mLastError; } + const TranslatableString &GetLibraryError() const + { return mLibraryError; } + private: bool ModeConfig(sqlite3 *db, const char *schema, const char *config); @@ -88,6 +103,9 @@ private: std::map mStatements; + TranslatableString mLastError; + TranslatableString mLibraryError; + // Bypass transactions if database will be deleted after close bool mBypass; }; diff --git a/src/ProjectFileIO.cpp b/src/ProjectFileIO.cpp index 6121d477a..7ebbdd6f2 100644 --- a/src/ProjectFileIO.cpp +++ b/src/ProjectFileIO.cpp @@ -507,9 +507,9 @@ int ProjectFileIO::Exec(const char *query, const ExecCB &callback) if (rc != SQLITE_ABORT && errmsg) { SetDBError( - XO("Failed to execute a project file command:\n\n%s").Format(query) + XO("Failed to execute a project file command:\n\n%s").Format(query), + Verbatim(errmsg) ); - mLibraryError = Verbatim(errmsg); } if (errmsg) { @@ -1769,9 +1769,8 @@ bool ProjectFileIO::ImportProject(const FilePath &fileName) if (!xmlFile.ParseString(this, output.GetString())) { SetError( - XO("Unable to parse project information.") + XO("Unable to parse project information."), xmlFile.GetErrorStr() ); - mLibraryError = xmlFile.GetErrorStr(); return false; } @@ -1846,9 +1845,9 @@ bool ProjectFileIO::LoadProject(const FilePath &fileName) if (!success) { SetError( - XO("Unable to parse project information.") + XO("Unable to parse project information."), + xmlFile.GetErrorStr() ); - mLibraryError = xmlFile.GetErrorStr(); return false; } @@ -2122,37 +2121,30 @@ wxLongLong ProjectFileIO::GetFreeDiskSpace() const return -1; } -const TranslatableString &ProjectFileIO::GetLastError() const -{ - return mLastError; -} - -const TranslatableString &ProjectFileIO::GetLibraryError() const -{ - return mLibraryError; -} - -void ProjectFileIO::SetError(const TranslatableString &msg) -{ - mLastError = msg; - mLibraryError = {}; -} - -void ProjectFileIO::SetDBError(const TranslatableString &msg) +const TranslatableString &ProjectFileIO::GetLastError() { auto &currConn = CurrConn(); - mLastError = msg; - wxLogDebug(wxT("SQLite error: %s"), mLastError.Debug()); - printf(" Lib error: %s", mLastError.Debug().mb_str().data()); + return currConn->GetLastError(); +} - if (currConn) - { - mLibraryError = Verbatim(sqlite3_errmsg(currConn->DB())); - wxLogDebug(wxT(" Lib error: %s"), mLibraryError.Debug()); - printf(" Lib error: %s", mLibraryError.Debug().mb_str().data()); - } +const TranslatableString &ProjectFileIO::GetLibraryError() +{ + auto &currConn = CurrConn(); + return currConn->GetLibraryError(); +} - wxASSERT(false); +void ProjectFileIO::SetError( + const TranslatableString &msg, const TranslatableString &libraryError ) +{ + auto &currConn = CurrConn(); + currConn->SetError(msg, libraryError); +} + +void ProjectFileIO::SetDBError( + const TranslatableString &msg, const TranslatableString &libraryError) +{ + auto &currConn = CurrConn(); + currConn->SetDBError(msg, libraryError); } void ProjectFileIO::SetBypass() diff --git a/src/ProjectFileIO.h b/src/ProjectFileIO.h index b5374e80c..effc28c00 100644 --- a/src/ProjectFileIO.h +++ b/src/ProjectFileIO.h @@ -104,8 +104,8 @@ public: // specific database. This is the workhorse for the above 3 methods. static int64_t GetDiskUsage(DBConnection *conn, SampleBlockID blockid); - const TranslatableString &GetLastError() const; - const TranslatableString &GetLibraryError() const; + const TranslatableString &GetLastError(); + const TranslatableString &GetLibraryError(); // Provides a means to bypass "DELETE"s at shutdown if the database // is just going to be deleted anyway. This prevents a noticable @@ -199,8 +199,13 @@ private: bool prune = false, const std::shared_ptr &tracks = nullptr); - void SetError(const TranslatableString & msg); - void SetDBError(const TranslatableString & msg); + //! Just set stored errors + void SetError(const TranslatableString & msg, + const TranslatableString &libraryError = {}); + + //! Set stored errors and write to log; and default libraryError to what database library reports + void SetDBError(const TranslatableString & msg, + const TranslatableString &libraryError = {}); bool ShouldCompact(const std::shared_ptr &tracks); @@ -236,9 +241,6 @@ private: Connection mPrevConn; FilePath mPrevFileName; bool mPrevTemporary; - - TranslatableString mLastError; - TranslatableString mLibraryError; }; // Make a savepoint (a transaction, possibly nested) with the given name; From ddc2593282fa291dc4f49ab8b20adb3e8b10e331 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Wed, 2 Sep 2020 15:14:11 -0400 Subject: [PATCH 3/4] TransactionScope only requires DBConnection not ProjectFileIO --- src/ProjectFileIO.cpp | 32 ++++++++++++++++---------------- src/ProjectFileIO.h | 12 ++++++------ src/ProjectManager.cpp | 2 +- src/effects/Effect.cpp | 2 +- 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/ProjectFileIO.cpp b/src/ProjectFileIO.cpp index 7ebbdd6f2..8bd5ee103 100644 --- a/src/ProjectFileIO.cpp +++ b/src/ProjectFileIO.cpp @@ -423,11 +423,11 @@ void ProjectFileIO::UseConnection(Connection &&conn, const FilePath &filePath) SetFileName(filePath); } -bool ProjectFileIO::TransactionStart(const wxString &name) +bool TransactionScope::TransactionStart(const wxString &name) { char *errmsg = nullptr; - int rc = sqlite3_exec(DB(), + int rc = sqlite3_exec(mConnection.DB(), wxT("SAVEPOINT ") + name + wxT(";"), nullptr, nullptr, @@ -435,7 +435,7 @@ bool ProjectFileIO::TransactionStart(const wxString &name) if (errmsg) { - SetDBError( + mConnection.SetDBError( XO("Failed to create savepoint:\n\n%s").Format(name) ); sqlite3_free(errmsg); @@ -444,11 +444,11 @@ bool ProjectFileIO::TransactionStart(const wxString &name) return rc == SQLITE_OK; } -bool ProjectFileIO::TransactionCommit(const wxString &name) +bool TransactionScope::TransactionCommit(const wxString &name) { char *errmsg = nullptr; - int rc = sqlite3_exec(DB(), + int rc = sqlite3_exec(mConnection.DB(), wxT("RELEASE ") + name + wxT(";"), nullptr, nullptr, @@ -456,7 +456,7 @@ bool ProjectFileIO::TransactionCommit(const wxString &name) if (errmsg) { - SetDBError( + mConnection.SetDBError( XO("Failed to release savepoint:\n\n%s").Format(name) ); sqlite3_free(errmsg); @@ -465,11 +465,11 @@ bool ProjectFileIO::TransactionCommit(const wxString &name) return rc == SQLITE_OK; } -bool ProjectFileIO::TransactionRollback(const wxString &name) +bool TransactionScope::TransactionRollback(const wxString &name) { char *errmsg = nullptr; - int rc = sqlite3_exec(DB(), + int rc = sqlite3_exec(mConnection.DB(), wxT("ROLLBACK TO ") + name + wxT(";"), nullptr, nullptr, @@ -477,7 +477,7 @@ bool ProjectFileIO::TransactionRollback(const wxString &name) if (errmsg) { - SetDBError( + mConnection.SetDBError( XO("Failed to release savepoint:\n\n%s").Format(name) ); sqlite3_free(errmsg); @@ -2449,12 +2449,12 @@ int ProjectFileIO::get_varint(const unsigned char *ptr, int64_t *out) return 9; } -TransactionScope::TransactionScope(ProjectFileIO &projectFileIO, - const char *name) -: mIO(projectFileIO), +TransactionScope::TransactionScope( + DBConnection &connection, const char *name) +: mConnection(connection), mName(name) { - mInTrans = mIO.TransactionStart(mName); + mInTrans = TransactionStart(mName); if ( !mInTrans ) // To do, improve the message throw SimpleMessageBoxException( XO("Database error") ); @@ -2467,8 +2467,8 @@ TransactionScope::~TransactionScope() // Rollback AND REMOVE the transaction // -- must do both; rolling back a savepoint only rewinds it // without removing it, unlike the ROLLBACK command - if (!(mIO.TransactionRollback(mName) && - mIO.TransactionCommit(mName) ) ) + if (!(TransactionRollback(mName) && + TransactionCommit(mName) ) ) { // Do not throw from a destructor! // This has to be a no-fail cleanup that does the best that it can. @@ -2482,7 +2482,7 @@ bool TransactionScope::Commit() // Misuse of this class THROW_INCONSISTENCY_EXCEPTION; - mInTrans = !mIO.TransactionCommit(mName); + mInTrans = !TransactionCommit(mName); return mInTrans; } diff --git a/src/ProjectFileIO.h b/src/ProjectFileIO.h index effc28c00..62d70868b 100644 --- a/src/ProjectFileIO.h +++ b/src/ProjectFileIO.h @@ -130,10 +130,6 @@ public: // The last compact check found unused blocks in the project file bool HadUnused(); - bool TransactionStart(const wxString &name); - bool TransactionCommit(const wxString &name); - bool TransactionRollback(const wxString &name); - // In one SQL command, delete sample blocks with ids in the given set, or // (when complement is true), with ids not in the given set. bool DeleteBlocks(const BlockIDs &blockids, bool complement); @@ -250,13 +246,17 @@ private: class TransactionScope { public: - TransactionScope(ProjectFileIO &projectFileIO, const char *name); + TransactionScope(DBConnection &connection, const char *name); ~TransactionScope(); bool Commit(); private: - ProjectFileIO &mIO; + bool TransactionStart(const wxString &name); + bool TransactionCommit(const wxString &name); + bool TransactionRollback(const wxString &name); + + DBConnection &mConnection; bool mInTrans; wxString mName; }; diff --git a/src/ProjectManager.cpp b/src/ProjectManager.cpp index 835dbfe1e..5bbce8707 100644 --- a/src/ProjectManager.cpp +++ b/src/ProjectManager.cpp @@ -740,7 +740,7 @@ void ProjectManager::OnCloseWindow(wxCloseEvent & event) projectFileIO.SetBypass(); { - TransactionScope trans(projectFileIO, "Shutdown"); + TransactionScope trans(projectFileIO.GetConnection(), "Shutdown"); // This can reduce reference counts of sample blocks in the project's // tracks. diff --git a/src/effects/Effect.cpp b/src/effects/Effect.cpp index 0c8389a5d..395d66f95 100644 --- a/src/effects/Effect.cpp +++ b/src/effects/Effect.cpp @@ -1215,7 +1215,7 @@ bool Effect::DoEffect(double projectRate, // This is for performance purposes only, no additional recovery implied auto &pProject = *const_cast(FindProject()); // how to remove this const_cast? auto &pIO = ProjectFileIO::Get(pProject); - TransactionScope trans(pIO, "Effect"); + TransactionScope trans(pIO.GetConnection(), "Effect"); // Update track/group counts CountWaveTracks(); From e7b3c2b99f3091430a1c8835fea134d1707d182a Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Wed, 2 Sep 2020 15:17:51 -0400 Subject: [PATCH 4/4] Cut and paste TransactionScope into DBConnection.* files --- src/DBConnection.cpp | 101 +++++++++++++++++++++++++++++++++++++++++ src/DBConnection.h | 22 +++++++++ src/ProjectFileIO.cpp | 101 ----------------------------------------- src/ProjectFileIO.h | 23 ---------- src/ProjectManager.cpp | 1 + src/effects/Effect.cpp | 1 + 6 files changed, 125 insertions(+), 124 deletions(-) diff --git a/src/DBConnection.cpp b/src/DBConnection.cpp index db91bd1f1..f1b54b170 100644 --- a/src/DBConnection.cpp +++ b/src/DBConnection.cpp @@ -337,6 +337,107 @@ int DBConnection::CheckpointHook(void *data, sqlite3 *db, const char *schema, in return SQLITE_OK; } +bool TransactionScope::TransactionStart(const wxString &name) +{ + char *errmsg = nullptr; + + int rc = sqlite3_exec(mConnection.DB(), + wxT("SAVEPOINT ") + name + wxT(";"), + nullptr, + nullptr, + &errmsg); + + if (errmsg) + { + mConnection.SetDBError( + XO("Failed to create savepoint:\n\n%s").Format(name) + ); + sqlite3_free(errmsg); + } + + return rc == SQLITE_OK; +} + +bool TransactionScope::TransactionCommit(const wxString &name) +{ + char *errmsg = nullptr; + + int rc = sqlite3_exec(mConnection.DB(), + wxT("RELEASE ") + name + wxT(";"), + nullptr, + nullptr, + &errmsg); + + if (errmsg) + { + mConnection.SetDBError( + XO("Failed to release savepoint:\n\n%s").Format(name) + ); + sqlite3_free(errmsg); + } + + return rc == SQLITE_OK; +} + +bool TransactionScope::TransactionRollback(const wxString &name) +{ + char *errmsg = nullptr; + + int rc = sqlite3_exec(mConnection.DB(), + wxT("ROLLBACK TO ") + name + wxT(";"), + nullptr, + nullptr, + &errmsg); + + if (errmsg) + { + mConnection.SetDBError( + XO("Failed to release savepoint:\n\n%s").Format(name) + ); + sqlite3_free(errmsg); + } + + return rc == SQLITE_OK; +} + +TransactionScope::TransactionScope( + DBConnection &connection, const char *name) +: mConnection(connection), + mName(name) +{ + mInTrans = TransactionStart(mName); + if ( !mInTrans ) + // To do, improve the message + throw SimpleMessageBoxException( XO("Database error") ); +} + +TransactionScope::~TransactionScope() +{ + if (mInTrans) + { + // Rollback AND REMOVE the transaction + // -- must do both; rolling back a savepoint only rewinds it + // without removing it, unlike the ROLLBACK command + if (!(TransactionRollback(mName) && + TransactionCommit(mName) ) ) + { + // Do not throw from a destructor! + // This has to be a no-fail cleanup that does the best that it can. + } + } +} + +bool TransactionScope::Commit() +{ + if ( !mInTrans ) + // Misuse of this class + THROW_INCONSISTENCY_EXCEPTION; + + mInTrans = !TransactionCommit(mName); + + return mInTrans; +} + ConnectionPtr::~ConnectionPtr() { wxASSERT_MSG(!mpConnection, wxT("Project file was not closed at shutdown")); diff --git a/src/DBConnection.h b/src/DBConnection.h index 0d0dc0105..131b85e8d 100644 --- a/src/DBConnection.h +++ b/src/DBConnection.h @@ -110,6 +110,28 @@ private: bool mBypass; }; +// Make a savepoint (a transaction, possibly nested) with the given name; +// roll it back at destruction time, unless an explicit Commit() happened first. +// Commit() must not be called again after one successful call. +// An exception is thrown from the constructor if the transaction cannot open. +class TransactionScope +{ +public: + TransactionScope(DBConnection &connection, const char *name); + ~TransactionScope(); + + bool Commit(); + +private: + bool TransactionStart(const wxString &name); + bool TransactionCommit(const wxString &name); + bool TransactionRollback(const wxString &name); + + DBConnection &mConnection; + bool mInTrans; + wxString mName; +}; + using Connection = std::unique_ptr; // This object attached to the project simply holds the pointer to the diff --git a/src/ProjectFileIO.cpp b/src/ProjectFileIO.cpp index 8bd5ee103..a1b367c51 100644 --- a/src/ProjectFileIO.cpp +++ b/src/ProjectFileIO.cpp @@ -423,69 +423,6 @@ void ProjectFileIO::UseConnection(Connection &&conn, const FilePath &filePath) SetFileName(filePath); } -bool TransactionScope::TransactionStart(const wxString &name) -{ - char *errmsg = nullptr; - - int rc = sqlite3_exec(mConnection.DB(), - wxT("SAVEPOINT ") + name + wxT(";"), - nullptr, - nullptr, - &errmsg); - - if (errmsg) - { - mConnection.SetDBError( - XO("Failed to create savepoint:\n\n%s").Format(name) - ); - sqlite3_free(errmsg); - } - - return rc == SQLITE_OK; -} - -bool TransactionScope::TransactionCommit(const wxString &name) -{ - char *errmsg = nullptr; - - int rc = sqlite3_exec(mConnection.DB(), - wxT("RELEASE ") + name + wxT(";"), - nullptr, - nullptr, - &errmsg); - - if (errmsg) - { - mConnection.SetDBError( - XO("Failed to release savepoint:\n\n%s").Format(name) - ); - sqlite3_free(errmsg); - } - - return rc == SQLITE_OK; -} - -bool TransactionScope::TransactionRollback(const wxString &name) -{ - char *errmsg = nullptr; - - int rc = sqlite3_exec(mConnection.DB(), - wxT("ROLLBACK TO ") + name + wxT(";"), - nullptr, - nullptr, - &errmsg); - - if (errmsg) - { - mConnection.SetDBError( - XO("Failed to release savepoint:\n\n%s").Format(name) - ); - sqlite3_free(errmsg); - } - - return rc == SQLITE_OK; -} - static int ExecCallback(void *data, int cols, char **vals, char **names) { auto &cb = *static_cast(data); @@ -2448,41 +2385,3 @@ int ProjectFileIO::get_varint(const unsigned char *ptr, int64_t *out) return 9; } - -TransactionScope::TransactionScope( - DBConnection &connection, const char *name) -: mConnection(connection), - mName(name) -{ - mInTrans = TransactionStart(mName); - if ( !mInTrans ) - // To do, improve the message - throw SimpleMessageBoxException( XO("Database error") ); -} - -TransactionScope::~TransactionScope() -{ - if (mInTrans) - { - // Rollback AND REMOVE the transaction - // -- must do both; rolling back a savepoint only rewinds it - // without removing it, unlike the ROLLBACK command - if (!(TransactionRollback(mName) && - TransactionCommit(mName) ) ) - { - // Do not throw from a destructor! - // This has to be a no-fail cleanup that does the best that it can. - } - } -} - -bool TransactionScope::Commit() -{ - if ( !mInTrans ) - // Misuse of this class - THROW_INCONSISTENCY_EXCEPTION; - - mInTrans = !TransactionCommit(mName); - - return mInTrans; -} diff --git a/src/ProjectFileIO.h b/src/ProjectFileIO.h index 62d70868b..ee95cfae2 100644 --- a/src/ProjectFileIO.h +++ b/src/ProjectFileIO.h @@ -24,7 +24,6 @@ struct sqlite3_stmt; struct sqlite3_value; class AudacityProject; -class TransactionScope; class DBConnection; class ProjectSerializer; class SqliteSampleBlock; @@ -239,28 +238,6 @@ private: bool mPrevTemporary; }; -// Make a savepoint (a transaction, possibly nested) with the given name; -// roll it back at destruction time, unless an explicit Commit() happened first. -// Commit() must not be called again after one successful call. -// An exception is thrown from the constructor if the transaction cannot open. -class TransactionScope -{ -public: - TransactionScope(DBConnection &connection, const char *name); - ~TransactionScope(); - - bool Commit(); - -private: - bool TransactionStart(const wxString &name); - bool TransactionCommit(const wxString &name); - bool TransactionRollback(const wxString &name); - - DBConnection &mConnection; - bool mInTrans; - wxString mName; -}; - class wxTopLevelWindow; // TitleRestorer restores project window titles to what they were, in its destructor. diff --git a/src/ProjectManager.cpp b/src/ProjectManager.cpp index 5bbce8707..e30c5fae5 100644 --- a/src/ProjectManager.cpp +++ b/src/ProjectManager.cpp @@ -15,6 +15,7 @@ Paul Licameli split from AudacityProject.cpp #include "AdornedRulerPanel.h" #include "AudioIO.h" #include "Clipboard.h" +#include "DBConnection.h" #include "FileNames.h" #include "Menus.h" #include "ModuleManager.h" diff --git a/src/effects/Effect.cpp b/src/effects/Effect.cpp index 395d66f95..ada488b92 100644 --- a/src/effects/Effect.cpp +++ b/src/effects/Effect.cpp @@ -27,6 +27,7 @@ #include #include "../AudioIO.h" +#include "../DBConnection.h" #include "../LabelTrack.h" #include "../Mix.h" #include "../PluginManager.h"