20#include <wx/sstream.h>
54#define AUDACITY_FILE_FORMAT_VERSION "1.3.0"
57#if !defined(__WXMSW__)
66#define PACK(b1, b2, b3, b4) ((b1 << 24) | (b2 << 16) | (b3 << 8) | b4)
102 "PRAGMA <schema>.application_id = %d;"
103 "PRAGMA <schema>.user_version = %u;"
117 "CREATE TABLE IF NOT EXISTS <schema>.project"
119 " id INTEGER PRIMARY KEY,"
137 "CREATE TABLE IF NOT EXISTS <schema>.autosave"
139 " id INTEGER PRIMARY KEY,"
158 "CREATE TABLE IF NOT EXISTS <schema>.sampleblocks"
160 " blockid INTEGER PRIMARY KEY AUTOINCREMENT,"
161 " sampleformat INTEGER,"
174 static std::optional<SQLiteBlobStream>
Open(
175 sqlite3* db,
const char* schema,
const char* table,
const char* column,
176 int64_t rowID,
bool readOnly)
noexcept
181 sqlite3_blob* blob =
nullptr;
183 const int rc = sqlite3_blob_open(
184 db, schema, table, column, rowID, readOnly ? 0 : 1, &blob);
189 return std::make_optional<SQLiteBlobStream>(blob, readOnly);
201 *
this = std::move(rhs);
223 return mBlob !=
nullptr;
228 if (
mBlob ==
nullptr)
231 const int rc = sqlite3_blob_close(
mBlob);
244 return SQLITE_MISUSE;
256 if (!
IsOpen() || ptr ==
nullptr)
257 return SQLITE_MISUSE;
261 if (availableBytes == 0)
266 else if (availableBytes <
size)
268 size = availableBytes;
296 static constexpr std::array<const char*, 2>
Columns = {
"dict",
"doc" };
299 sqlite3* db,
const char* schema,
const char* table,
343 size_t ReadData(
void* buffer,
size_t maxBytes)
override
352 maxBytes = std::min<size_t>(maxBytes, std::numeric_limits<int>::max());
353 auto bytesRead =
static_cast<int>(maxBytes);
355 if (SQLITE_OK !=
mBlobStream->Read(buffer, bytesRead))
364 else if (bytesRead == 0)
369 return static_cast<size_t>(bytesRead);
381 [](
int code, std::string_view message) {
383 wxLogMessage(
"SQLite error (%d): %s", code, message.data());
391 auto result = std::make_shared< ProjectFileIO >( parent );
420 wxLongLong freeSpace = 0;
423 if (wxGetDiskSpace(path, NULL, &freeSpace)) {
424 if (freeSpace < wxLongLong(wxLL(100 * 1048576))) {
429 XO(
"There is very little free disk space left on %s\n"
430 "Please select a bigger temporary directory location in\n"
431 "Directories Preferences.").Format( volume ),
432 "Error:_Disk_full_or_not_writable"
445 return connectionPtr.mpConnection !=
nullptr;
458 XO(
"Failed to open the project's database"),
460 "Error:_Disk_full_or_not_writable"
474 WriteXML(doc,
false, trackList.empty() ?
nullptr : &trackList);
493 if (fileName.empty())
496 if (fileName.empty())
507 wxFileName file(fileName);
508 file.SetFullName(
wxT(
""));
516 curConn = std::make_unique<DBConnection>(
518 auto rc = curConn->Open(fileName);
523 XO(
"Failed to open database file:\n\n%s").
Format(fileName),
551 if (!curConn->Close())
585 XO(
"Failed to discard connection")
596 file.SetFullName(
wxT(
""));
601 wxLogMessage(
"Failed to remove temporary project %s",
mPrevFileName);
616 if (!curConn->Close())
620 XO(
"Failed to restore connection")
637 curConn = std::move(conn);
645 return GuardedCall<int>(
646 [&]{
return cb(cols, vals,
names); },
653 char *errmsg =
nullptr;
655 const void *ptr = &callback;
657 const_cast<void*
>(ptr), &errmsg);
659 if (rc != SQLITE_ABORT && errmsg && !silent)
665 XO(
"Failed to execute a project file command:\n\n%s").
Format(query),
672 sqlite3_free(errmsg);
680 int rc =
Exec(sql, callback, silent);
683 if ( !(rc == SQLITE_OK || rc == SQLITE_ABORT) )
695 auto cb = [&result](
int cols,
char **vals,
char **){
702 return Query(sql, cb, silent);
707 bool success =
false;
708 auto cb = [&value, &success](
int cols,
char** vals,
char**)
712 const std::string_view valueString = vals[0];
714 success = std::errc() ==
716 valueString.data(), valueString.data() + valueString.length(),
724 return Query(sql, cb, silent) && success;
734 if (!
GetValue(
"SELECT Count(*) FROM sqlite_master WHERE type='table';", result))
742 XO(
"Project is in a read only directory\n(Unable to create the required temporary files)"),
752 if (wxStrtol<char **>(result,
nullptr, 10) == 0)
758 if (!
GetValue(
"PRAGMA application_ID;", result))
764 if (wxStrtoul<char **>(result,
nullptr, 10) !=
ProjectFileID)
766 SetError(
XO(
"This is not an Audacity project file"));
771 if (!
GetValue(
"PRAGMA user_version;", result))
784 XO(
"This project was created with a newer version of Audacity.\n\nYou will need to upgrade to open it.")
798 sql.Replace(
"<schema>", schema);
800 rc = sqlite3_exec(db, sql,
nullptr,
nullptr,
nullptr);
804 XO(
"Unable to initialize the project file")
829 auto contextData =
reinterpret_cast<ContextData*
>(sqlite3_user_data(context));
834 contextData->blockids.find(blockid) != contextData->blockids.end() ||
836 contextData->project, blockid));
844 ContextData contextData{
mProject, blockids };
846 auto cleanup =
finally([&]
849 sqlite3_create_function(db,
"inset", 1, SQLITE_UTF8 | SQLITE_DETERMINISTIC,
nullptr,
nullptr,
nullptr,
nullptr);
853 rc = sqlite3_create_function(db,
"inset", 1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, &contextData,
InSet,
nullptr,
nullptr);
860 SetDBError(
XO(
"Unable to add 'inset' function (can't verify blockids)"));
867 auto sql = wxString::Format(
868 "DELETE FROM sampleblocks WHERE %sinset(blockid);",
869 complement ?
"NOT " :
"" );
870 rc = sqlite3_exec(db, sql,
nullptr,
nullptr,
nullptr);
877 if( rc==SQLITE_READONLY)
879 SetDBError(
XO(
"Project is read only\n(Unable to work with the blockfiles)"));
880 else if( rc==SQLITE_LOCKED)
882 SetDBError(
XO(
"Project is locked\n(Unable to work with the blockfiles)"));
883 else if( rc==SQLITE_BUSY)
885 SetDBError(
XO(
"Project is busy\n(Unable to work with the blockfiles)"));
886 else if( rc==SQLITE_CORRUPT)
888 SetDBError(
XO(
"Project is corrupt\n(Unable to work with the blockfiles)"));
889 else if( rc==SQLITE_PERM)
891 SetDBError(
XO(
"Some permissions issue\n(Unable to work with the blockfiles)"));
892 else if( rc==SQLITE_IOERR)
894 SetDBError(
XO(
"A disk I/O error\n(Unable to work with the blockfiles)"));
895 else if( rc==SQLITE_AUTH)
897 SetDBError(
XO(
"Not authorized\n(Unable to work with the blockfiles)"));
906 int changes = sqlite3_changes(db);
909 wxLogInfo(
XO(
"Total orphan blocks deleted %d").Translation(), changes);
920 const std::vector<const TrackList *> &
tracks )
936 for (
auto trackList :
tracks)
943 auto cb = [&blockids](
int cols,
char **vals,
char **){
945 wxString{ vals[0] }.ToLongLong(&blockid);
946 blockids.insert(blockid);
950 if (!
Query(
"SELECT blockid FROM sampleblocks;", cb))
964 bool success =
false;
969 auto cleanup =
finally([&]
983 auto result = sqlite3_exec(db,
"ROLLBACK;",
nullptr,
nullptr,
nullptr);
986 if (result != SQLITE_OK && (rc == SQLITE_DONE || rc == SQLITE_OK))
990 "sqlite3.context",
"ProjectGileIO::CopyTo.cleanup");
993 XO(
"Failed to rollback transaction during import")
1000 sqlite3_exec(db,
"DETACH DATABASE outbound;",
nullptr,
nullptr,
nullptr);
1003 wxRemoveFile(destpath);
1009 wxString dbName = destpath;
1011 dbName.Replace(
"'",
"''");
1012 sql.Printf(
"ATTACH DATABASE '%s' AS outbound;", dbName.ToUTF8());
1014 rc = sqlite3_exec(db, sql,
nullptr,
nullptr,
nullptr);
1015 if (rc != SQLITE_OK)
1018 XO(
"Unable to attach destination database")
1027 if ( pConn->FastMode(
"outbound") != SQLITE_OK)
1030 XO(
"Unable to switch to fast journaling mode")
1045 sqlite3_stmt *stmt =
nullptr;
1046 auto cleanup =
finally([&]
1051 sqlite3_finalize(stmt);
1056 rc = sqlite3_prepare_v2(db,
1057 "INSERT INTO outbound.sampleblocks"
1058 " SELECT * FROM main.sampleblocks"
1059 " WHERE blockid = ?;",
1063 if (rc != SQLITE_OK)
1067 "sqlite3.context",
"ProjectGileIO::CopyTo.prepare");
1070 XO(
"Unable to prepare project file command:\n\n%s").
Format(sql)
1081 wxLongLong_t count = 0;
1082 wxLongLong_t total = blockids.size();
1091 sqlite3_exec(db,
"BEGIN;",
nullptr,
nullptr,
nullptr);
1094 for (
auto blockid : blockids)
1097 rc = sqlite3_bind_int64(stmt, 1, blockid);
1098 if (rc != SQLITE_OK)
1102 "sqlite3.context",
"ProjectGileIO::CopyTo.bind");
1105 XO(
"Failed to bind SQL parameter")
1112 rc = sqlite3_step(stmt);
1113 if (rc != SQLITE_DONE)
1117 "sqlite3.context",
"ProjectGileIO::CopyTo.step");
1120 XO(
"Failed to update the project file.\nThe following command failed:\n\n%s").
Format(sql)
1126 if (sqlite3_reset(stmt) != SQLITE_OK)
1130 "sqlite3.context",
"ProjectGileIO::CopyTo.reset");
1135 result = progress->Poll(++count, total);
1149 if (!
WriteDoc(isTemporary ?
"autosave" :
"project", doc,
"outbound"))
1155 sqlite3_exec(db,
"COMMIT;",
nullptr,
nullptr,
nullptr);
1159 rc = sqlite3_exec(db,
"DETACH DATABASE outbound;",
nullptr,
nullptr,
nullptr);
1160 if (rc != SQLITE_OK)
1166 XO(
"Destination project could not be detached")
1181 unsigned long long current = 0;
1185 for (
auto pTracks :
tracks)
1194 unsigned long long blockcount = 0;
1196 auto cb = [&blockcount](
int cols,
char **vals,
char **)
1199 wxString(vals[0]).ToULongLong(&blockcount);
1203 if (!
Query(
"SELECT Count(*) FROM sampleblocks;", cb) || blockcount == 0)
1215 wxLogDebug(
wxT(
"used = %lld total = %lld %lld"), current, total, total ? current / total : 0);
1216 if (!total || current / total > 80)
1218 wxLogDebug(
wxT(
"not compacting"));
1221 wxLogDebug(
wxT(
"compacting"));
1229 return connectionPtr.mpConnection;
1234 static const std::vector<wxString> strings {
1257 auto numberString = [](
int num) -> wxString {
1258 return num == 1 ? wxString{} : wxString::Format(
".%d", num);
1262 suffixes.push_back({});
1265 const auto name =
fn.GetName();
1268 fn.SetName(
name + numberString(nn++) + extra );
1269 result =
fn.GetFullPath();
1271 while( std::any_of(suffixes.begin(), suffixes.end(), [&](
auto &suffix){
1272 return wxFileExists(result + suffix);
1280 std::atomic_bool done = {
false};
1281 bool success =
false;
1282 auto thread = std::thread([&]
1284 success = wxRenameFile(src, dst);
1291 XO(
"Copying Project"),
XO(
"This may take several seconds"));
1297 using namespace std::chrono;
1298 std::this_thread::sleep_for(50ms);
1306 XO(
"Error Writing to File"),
1307 XO(
"Audacity failed to write file %s.\n"
1308 "Perhaps disk is full or not writable.\n"
1309 "For tips on freeing up space, click the help button.")
1311 "Error:_Disk_full_or_not_writable"
1329 std::vector< std::pair<FilePath, FilePath> > pairs{ { src, dst } };
1330 bool success =
false;
1331 auto cleanup =
finally([&]{
1336 for (
auto &pair : pairs) {
1337 if (!(pair.first.empty() && pair.second.empty()))
1338 wxRenameFile(pair.second, pair.first);
1344 auto srcName = src + suffix;
1345 if (wxFileExists(srcName)) {
1346 auto dstName = dst + suffix;
1349 pairs.push_back({ srcName, dstName });
1353 return (success =
true);
1358 if (!wxFileExists(filename))
1361 bool success = wxRemoveFile(filename);
1363 for (
const auto &suffix : suffixes) {
1364 auto file = filename + suffix;
1365 if (wxFileExists(file))
1366 success = wxRemoveFile(file) && success;
1384 if (!mPath.empty()) {
1393 if (!mPath.empty()) {
1394 if (!mSafety.empty()) {
1397 suffixes.push_back({});
1398 for (
const auto &suffix : suffixes) {
1399 auto path = mPath + suffix;
1400 if (wxFileExists(path))
1402 wxRenameFile(mSafety + suffix, mPath + suffix);
1409 const std::vector<const TrackList *> &
tracks,
bool force)
1443 wxString backName = origName +
"_compact_back";
1444 wxString tempName = origName +
"_compact_temp";
1466 if (wxFileName::GetSize(tempName) < wxFileName::GetSize(origName))
1469 if (wxRenameFile(origName, backName))
1472 if (wxRenameFile(tempName, origName))
1478 if (!wxRemoveFile(backName))
1483 wxLogWarning(
wxT(
"Compaction failed to delete backup %s"), backName);
1493 wxLogWarning(
wxT(
"Compaction failed to open new project %s"), origName);
1496 if (!wxRenameFile(origName, tempName))
1498 wxLogWarning(
wxT(
"Compaction failed to rename original %s to temp %s"),
1499 origName, tempName);
1504 wxLogWarning(
wxT(
"Compaction failed to rename temp %s to orig %s"),
1505 origName, tempName);
1508 if (!wxRenameFile(backName, origName))
1510 wxLogWarning(
wxT(
"Compaction failed to rename back %s to orig %s"),
1511 backName, origName);
1516 wxLogWarning(
wxT(
"Compaction failed to rename orig %s to back %s"),
1517 backName, origName);
1523 wxLogWarning(
wxT(
"Compaction failed to reopen %s"), origName);
1529 if (!wxRemoveFile(tempName))
1534 wxLogWarning(
wxT(
"Failed to delete temporary file...ignoring"));
1568 XO(
"[Project %02i] Audacity \"%s\"")
1569 .Format( number + 1,
1574 else if (
name.empty())
1583 name +=
_(
"(Recovered)");
1589 if (
auto pThis = wThis.lock())
1604 if (!fileName.empty() && fileName !=
mFileName)
1607 [wThis = weak_from_this()]
1609 if (
auto pThis = wThis.lock())
1642 wxString fileVersion;
1643 wxString audacityVersion;
1644 int requiredTags = 0;
1648 for (
auto pair : attrs)
1650 auto attr = pair.first;
1651 auto value = pair.second;
1654 .CallAttributeHandler( attr,
project, value ) )
1657 else if (attr ==
"version")
1659 fileVersion = value.ToWString();
1663 else if (attr ==
"audacityversion")
1665 audacityVersion = value.ToWString();
1670 if (requiredTags < 2)
1679 if (!wxSscanf(fileVersion,
wxT(
"%i.%i.%i"), &fver, &frel, &frev))
1690 int fileVer = ((fver *100)+frel)*100+frev;
1691 int codeVer = ((cver *100)+crel)*100+crev;
1693 if (codeVer<fileVer)
1696 auto msg =
XO(
"This file was saved using Audacity %s.\nYou are using Audacity %s. You may need to upgrade to a newer version to open this file.")
1697 .Format(audacityVersion, AUDACITY_VERSION_STRING);
1700 XO(
"Can't open project file"),
1702 "FAQ:Errors_opening_an_Audacity_project"
1708 if (tag !=
"project")
1733 xmlFile.
Write(
wxT(
"version=\"1.0\" "));
1734 xmlFile.
Write(
wxT(
"standalone=\"no\" "));
1740 xmlFile.
Write(
wxT(
"\"-//audacityproject-1.3.0//DTD//EN\" "));
1741 xmlFile.
Write(
wxT(
"\"http://audacity.sourceforge.net/xml/audacityproject-1.3.0.dtd\" "));
1756 xmlFile.
WriteAttr(
wxT(
"xmlns"),
wxT(
"http://audacity.sourceforge.net/xml/"));
1759 xmlFile.
WriteAttr(
wxT(
"audacityversion"), AUDACITY_VERSION_STRING);
1764 tracklist.Any().Visit([&](
const Track &t) {
1772 useTrack = &pendingTracks.SubstitutePendingChangedTrack(t);
1774 else if (useTrack->GetId() ==
TrackId{}) {
1794 if (
WriteDoc(
"autosave", autosave))
1812 rc = sqlite3_exec(db,
"DELETE FROM autosave;",
nullptr,
nullptr,
nullptr);
1813 if (rc != SQLITE_OK)
1819 XO(
"Failed to remove the autosave information from the project file.")
1831 const char *schema )
1844 "INSERT INTO %s.%s(id, dict, doc) VALUES(1, ?1, ?2)"
1845 " ON CONFLICT(id) DO UPDATE SET dict = ?1, doc = ?2;",
1848 sqlite3_stmt *stmt =
nullptr;
1849 auto cleanup =
finally([&]
1853 sqlite3_finalize(stmt);
1857 rc = sqlite3_prepare_v2(db, sql, -1, &stmt,
nullptr);
1858 if (rc != SQLITE_OK)
1865 XO(
"Unable to prepare project file command:\n\n%s").
Format(sql)
1877 sqlite3_bind_zeroblob(stmt, 1, dict.
GetSize()) ||
1878 sqlite3_bind_zeroblob(stmt, 2, data.
GetSize()))
1888 const auto reportError = [
this](
auto sql) {
1890 XO(
"Failed to update the project file.\nThe following command failed:\n\n%s")
1894 rc = sqlite3_step(stmt);
1896 if (rc != SQLITE_DONE)
1907 sqlite3_finalize(stmt);
1914 const wxString rowIDSql =
1915 wxString::Format(
"SELECT ROWID FROM %s.%s WHERE id = 1;", schema, table);
1917 if (!
GetValue(rowIDSql, rowID,
true))
1922 reportError(rowIDSql);
1926 const auto writeStream = [db, schema, table, rowID,
this](
const char* column,
const MemoryStream& stream) {
1941 for (
auto chunk : stream)
1943 if (SQLITE_OK != blobStream->Write(chunk.first, chunk.second))
1954 if (blobStream->Close() != SQLITE_OK)
1957 "sqlite3.rc", std::to_string(sqlite3_errcode(db)));
1960 "sqlite3.context",
"ProjectGileIO::WriteDoc::writeBlobStream");
1970 if (!writeStream(
"dict", dict))
1973 if (!writeStream(
"doc", data))
1976 const wxString setVersionSql =
1979 if (!
Query(setVersionSql.c_str(), [](
auto...) { return 0; }))
1984 reportError(setVersionSql);
1988 return transaction.
Commit();
1993 : mProjectFileIO{ projectFileIO }
2000 : mProjectFileIO{ other.mProjectFileIO }
2002 , mCommitted{ other.mCommitted }
2004 other.mCommitted =
true;
2010 mProjectFileIO.RestoreConnection();
2020 if (!mCommitted && !
mFileName.empty()) {
2022 mProjectFileIO.DiscardConnection();
2028 -> std::optional<TentativeConnection>
2030 auto now = std::chrono::high_resolution_clock::now();
2032 std::optional<TentativeConnection> result{ *
this };
2034 bool success =
false;
2044 GetValue(
"SELECT ROWID FROM main.autosave WHERE id = 1;", rowId,
true);
2046 int64_t rowsCount = 0;
2050 (!
GetValue(
"SELECT COUNT(1) FROM main.project;", rowsCount,
true) || rowsCount == 0))
2061 if (!useAutosave && !
GetValue(
"SELECT ROWID FROM main.project WHERE id = 1;", rowId,
false))
2067 DB(),
"main", useAutosave ?
"autosave" :
"project", rowId);
2074 XO(
"Unable to parse project information.")
2083 ->GetActiveBlockIDs();
2084 if (blockids.size() > 0)
2107 wxString queryResult;
2108 success =
GetValue(
"SELECT Count(*) FROM project;", queryResult);
2114 result->SetFileName(fileName);
2116 auto duration = std::chrono::high_resolution_clock::now() - now;
2119 "Project loaded in %lld ms",
2120 std::chrono::duration_cast<std::chrono::milliseconds>(duration).count());
2161 bool reopened =
false;
2163 if (
true == (moved =
MoveProject(savedName, fileName)))
2181 if (reopened && !moved) {
2190"The project's database failed to reopen, "
2191"possibly because of limited space on the storage device."),
2192 "Error:_Disk_full_or_not_writable"
2208 if (!
CopyTo(fileName,
XO(
"Saving project"),
false))
2211 XO(
"Error Saving Project"),
2213 "Error:_Disk_full_or_not_writable"
2219 Connection newConn = std::make_unique<DBConnection>(
2221 [
this]{ OnCheckpointFailure(); });
2234 std::atomic_bool done = {
false};
2235 bool success =
true;
2236 auto thread = std::thread([&]
2238 auto rc = newConn->Open(fileName);
2239 if (rc != SQLITE_OK)
2251 XO(
"Syncing"),
XO(
"This may take several seconds"));
2257 using namespace std::chrono;
2258 std::this_thread::sleep_for(50ms);
2267 XO(
"Error Saving Project"),
2268 XO(
"The project failed to open, possibly due to limited space\n"
2269 "on the storage device.\n\n%s").Format(
GetLastError()),
2270 "Error:_Disk_full_or_not_writable");
2275 if (!wxRemoveFile(fileName))
2277 wxLogMessage(
"Failed to remove destination project after open failure: %s", fileName);
2289 XO(
"Error Saving Project"),
2290 XO(
"Unable to remove autosave information, possibly due to limited space\n"
2291 "on the storage device.\n\n%s").Format(
GetLastError()),
2292 "Error:_Disk_full_or_not_writable");
2297 if (!wxRemoveFile(fileName))
2299 wxLogMessage(
"Failed to remove destination project after AutoSaveDelete failure: %s", fileName);
2319 Compact( { lastSaved ? lastSaved : empty.get() }, true );
2333 {},
XO(
"Error Saving Project"),
2335 "Error:_Disk_full_or_not_writable");
2359 return CopyTo(fileName,
XO(
"Backing up project"),
false,
true,
2373 wxLogDebug(
"Closing project with no database connection");
2390 wxFileName file(filename);
2391 file.SetFullName(
wxT(
""));
2431 wxLongLong freeSpace;
2432 if (wxGetDiskSpace(wxPathOnly(
mFileName), NULL, &freeSpace))
2436 constexpr auto limit = 1ll << 32;
2441 auto length = wxFile{
mFileName}.Length();
2444 if (length == wxInvalidSize)
2446 auto free = std::max<wxLongLong>(0, limit - length);
2459 const wxString &helpPage)
2493 currConn->SetError(msg, libraryError, errorCode);
2501 currConn->SetDBError(msg, libraryError, errorCode);
2517 currConn->SetBypass(
true );
2531 currConn->SetBypass(
false );
2547 const std::vector<const TrackList*> &trackLists)
const
2550 unsigned long long current = 0;
2556 for (
auto pTracks: trackLists)
2578 sqlite3_stmt* stmt =
nullptr;
2582 static const char* statement =
2584 sum(length(blockid) + length(sampleformat) +
2585 length(summin) + length(summax) + length(sumrms) +
2586 length(summary256) + length(summary64k) +
2588FROM sampleblocks;)";
2594 static const char* statement =
2596 length(blockid) + length(sampleformat) +
2597 length(summin) + length(summax) + length(sumrms) +
2598 length(summary256) + length(summary64k) +
2600FROM sampleblocks WHERE blockid = ?1;)";
2605 auto cleanup =
finally(
2608 if (stmt !=
nullptr)
2610 sqlite3_clear_bindings(stmt);
2611 sqlite3_reset(stmt);
2617 int rc = sqlite3_bind_int64(stmt, 1, blockid);
2619 if (rc != SQLITE_OK)
2622 "sqlite3.rc", std::to_string(rc));
2625 "sqlite3.context",
"ProjectFileIO::GetDiskUsage::bind");
2631 int rc = sqlite3_step(stmt);
2633 if (rc != SQLITE_ROW)
2638 "sqlite3.context",
"ProjectFileIO::GetDiskUsage::step");
2643 const int64_t
size = sqlite3_column_int64(stmt, 0);
2656 projectFileIO.SetBypass();
2665 projectFileIO.CloseProject();
2674 if ( !projectFileIO.AutoSave() )
2677 XO(
"Automatic database backup failed."),
2679 "Error:_Disk_full_or_not_writable"
@ Internal
Indicates internal failure from Audacity.
SimpleGuard< R > MakeSimpleGuard(R value) noexcept(noexcept(SimpleGuard< R >{ value }))
Convert a value to a handler function returning that value, suitable for GuardedCall<R>
Toolkit-neutral facade for basic user interface services.
std::unordered_set< SampleBlockID > SampleBlockIDSet
Declare functions to perform UTF-8 to std::wstring conversions.
Declare DBConnection, which maintains database connection and associated status and background thread...
std::unique_ptr< DBConnection > Connection
FromCharsResult FromChars(const char *buffer, const char *last, float &value) noexcept
Parse a string into a single precision floating point value, always uses the dot as decimal.
Declare functions to convert numeric types to string representation.
#define THROW_INCONSISTENCY_EXCEPTION
Throw InconsistencyException, using C++ preprocessor to identify the source code location.
std::unique_ptr< const BasicUI::WindowPlacement > ProjectFramePlacement(AudacityProject *project)
Make a WindowPlacement object suitable for project (which may be null)
#define AUDACITY_FILE_FORMAT_VERSION
static ProjectHistory::AutoSave::Scope scope
Install the callback from undo manager.
static const int ProjectFileID
static const AudacityProject::AttachedObjects::RegisteredFactory sFileIOKey
#define PACK(b1, b2, b3, b4)
static int ExecCallback(void *data, int cols, char **vals, char **names)
static const char * ProjectFileSchema
std::unordered_set< SampleBlockID > BlockIDs
@ CheckpointFailure
Failure happened in a worker thread.
@ ProjectTitleChange
A normal occurrence.
@ ProjectFilePathChange
A normal occurrence.
std::function< void(SampleBlockConstPtr) > BlockSpaceUsageAccumulator(unsigned long long &total)
#define ADD_EXCEPTION_CONTEXT(name, value)
TranslatableString Verbatim(wxString str)
Require calls to the one-argument constructor to go through this distinct global function name.
std::vector< Attribute > AttributesList
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Subclasses may hold information such as a parent window pointer for a dialog.
BufferedProjectBlobStream(sqlite3 *db, const char *schema, const char *table, int64_t rowID)
static constexpr std::array< const char *, 2 > Columns
bool OpenBlob(size_t index)
bool HasMoreData() const override
size_t ReadData(void *buffer, size_t maxBytes) override
std::optional< SQLiteBlobStream > mBlobStream
A facade-like class, that implements buffered reading from the underlying data stream.
Client code makes static instance from a factory of attachments; passes it to Get or Find as a retrie...
static ConnectionPtr & Get(AudacityProject &project)
void ThrowException(bool write) const
throw and show appropriate message box
sqlite3_stmt * Prepare(enum StatementID id, const char *sql)
static TranslatableString WriteFailureMessage(const wxFileName &fileName)
typename GlobalVariable< AutoSave, const std::function< void(AudacityProject &) >, nullptr, Options... >::Scope Scope
InvisibleTemporaryProject()
AudacityProject & Project()
std::shared_ptr< AudacityProject > mpProject
~InvisibleTemporaryProject()
A low overhead memory stream with O(1) append, low heap fragmentation and a linear memory view.
const size_t GetSize() const noexcept
CallbackReturn Publish(const ProjectFileIOMessage &message)
Send a message to connected callbacks.
static PendingTracks & Get(AudacityProject &project)
BackupProject(ProjectFileIO &projectFileIO, const FilePath &path)
Rename project file at path, and any auxiliary files, to backup path names.
~BackupProject()
if !IsOk() do nothing; else if Discard() was not called, undo the renaming
void Discard()
if !IsOk() do nothing; else remove backup files
Object associated with a project that manages reading and writing of Audacity project file formats,...
AudacityProject & mProject
DBConnection & GetConnection()
Return a reference to a connection, creating it as needed on demand; throw on failure.
bool AutoSave(bool recording=false)
void OnCheckpointFailure()
bool MoveProject(const FilePath &src, const FilePath &dst)
void UpdatePrefs() override
static bool RemoveProject(const FilePath &filename)
Remove any files associated with a project at given path; return true if successful.
bool UpdateSaved(const TrackList *tracks=nullptr)
bool CopyTo(const FilePath &destpath, const TranslatableString &msg, bool isTemporary, bool prune=false, const std::vector< const TrackList * > &tracks={})
const TranslatableString & GetLibraryError() const
void SetProjectTitle(int number=-1)
void UseConnection(Connection &&conn, const FilePath &filePath)
void SetDBError(const TranslatableString &msg, const TranslatableString &libraryError={}, int errorCode=-1)
Set stored errors and write to log; and default libraryError to what database library reports.
std::optional< TentativeConnection > LoadProject(const FilePath &fileName, bool ignoreAutosave)
bool GetValue(const char *sql, wxString &value, bool silent=false)
int64_t GetBlockUsage(SampleBlockID blockid)
static ProjectFileIO & Get(AudacityProject &project)
std::function< int(int cols, char **vals, char **names)> ExecCB
wxString GenerateDoc()
Return a strings representation of the active project XML doc.
void SetFileName(const FilePath &fileName)
const FilePath & GetFileName() const
bool OpenConnection(FilePath fileName={})
bool RenameOrWarn(const FilePath &src, const FilePath &dst)
Rename a file or put up appropriate warning message.
bool SaveProject(const FilePath &fileName, const TrackList *lastSaved)
void ShowError(const BasicUI::WindowPlacement &placement, const TranslatableString &dlogTitle, const TranslatableString &message, const wxString &helpPage)
Displays an error dialog with a button that offers help.
bool InstallSchema(sqlite3 *db, const char *schema="main")
void Compact(const std::vector< const TrackList * > &tracks, bool force=false)
int Exec(const char *query, const ExecCB &callback, bool silent=false)
int GetLastErrorCode() const
bool DeleteBlocks(const BlockIDs &blockids, bool complement)
bool SaveCopy(const FilePath &fileName)
bool ShouldCompact(const std::vector< const TrackList * > &tracks)
static FilePath SafetyFileName(const FilePath &src)
Generate a name for short-lived backup project files from an existing project.
bool Query(const char *sql, const ExecCB &callback, bool silent=false)
void WriteXMLHeader(XMLWriter &xmlFile) const
int64_t GetCurrentUsage(const std::vector< const TrackList * > &trackLists) const
XMLTagHandler * HandleXMLChild(const std::string_view &tag) override
const TranslatableString & GetLastError() const
void WriteXML(XMLWriter &xmlFile, bool recording=false, const TrackList *tracks=nullptr)
void SetError(const TranslatableString &msg, const TranslatableString &libraryError={}, int errorCode={})
Just set stored errors.
bool AutoSaveDelete(sqlite3 *db=nullptr)
static bool InitializeSQL()
static const std::vector< wxString > & AuxiliaryFileSuffixes()
static void InSet(sqlite3_context *context, int argc, sqlite3_value **argv)
std::shared_ptr< DBConnectionErrors > mpErrors
bool WriteDoc(const char *table, const ProjectSerializer &autosave, const char *schema="main")
ProjectFileIO(AudacityProject &project)
bool HandleXMLTag(const std::string_view &tag, const AttributesList &attrs) override
wxLongLong GetFreeDiskSpace() const
bool HasConnection() const
Return true if a connection is now open.
static int64_t GetDiskUsage(DBConnection &conn, SampleBlockID blockid)
const wxString & GetLastLog() const
a class used to (de)serialize the project catalog
static bool Decode(BufferedStreamReader &in, XMLTagHandler *handler)
const MemoryStream & GetData() const
const MemoryStream & GetDict() const
bool IsOpen() const noexcept
int Write(const void *ptr, int size) noexcept
~SQLiteBlobStream() noexcept
int Read(void *ptr, int &size) noexcept
SQLiteBlobStream(sqlite3_blob *blob, bool readOnly) noexcept
static std::optional< SQLiteBlobStream > Open(sqlite3 *db, const char *schema, const char *table, const char *column, int64_t rowID, bool readOnly) noexcept
SQLiteBlobStream & operator=(SQLiteBlobStream &&rhs) noexcept
bool IsEof() const noexcept
SQLiteBlobStream(SQLiteBlobStream &&rhs) noexcept
A MessageBoxException that shows a given, unvarying string.
Abstract base class for an object holding data associated with points on a time axis.
virtual void WriteXML(XMLWriter &xmlFile) const =0
An in-session identifier of track objects across undo states. It does not persist between sessions.
A flat linked list of tracks supporting Add, Remove, Clear, and Contains, serialization of the list o...
static TrackListHolder Create(AudacityProject *pOwner)
static TrackList & Get(AudacityProject &project)
RAII for a database transaction, possibly nested.
bool Commit()
Commit the transaction.
Holds a msgid for the translation catalog; may also bind format arguments.
static WaveTrackFactory & Get(AudacityProject &project)
const SampleBlockFactoryPtr & GetSampleBlockFactory() const
XMLTagHandler * CallObjectAccessor(const std::string_view &tag, Host &host)
static XMLMethodRegistry & Get()
Get the unique instance.
void CallWriters(const Host &host, XMLWriter &writer)
Wrapper to output XML data to strings.
This class is an interface which should be implemented by classes which wish to be able to load and s...
Base class for XMLFileWriter and XMLStringWriter that provides the general functionality for creating...
virtual void StartTag(const wxString &name)
void WriteAttr(const wxString &name, const Identifier &value)
virtual void EndTag(const wxString &name)
virtual void Write(const wxString &data)=0
PROJECT_FILE_IO_API void Remove(const FilePath &path)
PROJECT_FILE_IO_API void Add(const FilePath &path)
std::unique_ptr< GenericProgressDialog > MakeGenericProgress(const WindowPlacement &placement, const TranslatableString &title, const TranslatableString &message, int style=(ProgressAppModal|ProgressShowElapsedTime|ProgressSmooth))
Create and display a progress dialog (return nullptr if Services not installed)
void CallAfter(Action action)
Schedule an action to be done later, and in the main thread.
void ShowErrorDialog(const WindowPlacement &placement, const TranslatableString &dlogTitle, const TranslatableString &message, const ManualPageID &helpPage, const ErrorDialogOptions &options={})
Show an error dialog with a link to the manual for further help.
void Yield()
Dispatch waiting events, including actions enqueued by CallAfter.
std::unique_ptr< ProgressDialog > MakeProgress(const TranslatableString &title, const TranslatableString &message, unsigned flags=(ProgressShowStop|ProgressShowCancel), const TranslatableString &remainingLabelText={})
Create and display a progress dialog.
UTILITY_API const char *const * argv
A copy of argv; responsibility of application startup to assign it.
UTILITY_API int argc
A copy of argc; responsibility of application startup to assign it.
FILES_API bool IsOnFATFileSystem(const FilePath &path)
FILES_API wxString AbbreviatePath(const wxFileName &fileName)
Give enough of the path to identify the device. (On Windows, drive letter plus ':')
FILES_API wxString UnsavedProjectFileName()
FILES_API wxString TempDir()
WAVE_TRACK_API void InspectBlocks(const TrackList &tracks, BlockInspector inspector, SampleBlockIDSet *pIDs=nullptr)
std::unordered_set< SampleBlockID > SampleBlockIDSet
void swap(std::unique_ptr< Alg_seq > &a, std::unique_ptr< Alg_seq > &b)
void SetLogCallback(LogCallback callback)
Error Initialize() noexcept
std::wstring ToWString(const std::string &str)
Options for variations of error dialogs; the default is for modal dialogs.
ErrorDialogOptions && Log(std::wstring log_) &&
std::errc ec
A pointer to the first character not matching the pattern.
ProjectFileIO & mProjectFileIO
void SetFileName(const FilePath &fileName)
TentativeConnection(ProjectFileIO &projectFileIO)
static void OnUpdateSaved(AudacityProject &project, const ProjectSerializer &serializer)
static bool IsBlockLocked(const AudacityProject &project, int64_t blockId)
const BlockIDs & blockids
const AudacityProject & project