22#include <wx/sstream.h>
50#define AUDACITY_FILE_FORMAT_VERSION "1.3.0"
53#if !defined(__WXMSW__)
62#define PACK(b1, b2, b3, b4) ((b1 << 24) | (b2 << 16) | (b3 << 8) | b4)
98 "PRAGMA <schema>.application_id = %d;"
99 "PRAGMA <schema>.user_version = %u;"
113 "CREATE TABLE IF NOT EXISTS <schema>.project"
115 " id INTEGER PRIMARY KEY,"
133 "CREATE TABLE IF NOT EXISTS <schema>.autosave"
135 " id INTEGER PRIMARY KEY,"
154 "CREATE TABLE IF NOT EXISTS <schema>.sampleblocks"
156 " blockid INTEGER PRIMARY KEY AUTOINCREMENT,"
157 " sampleformat INTEGER,"
178 mRc = sqlite3_config(SQLITE_CONFIG_URI, 1);
179 if (
mRc == SQLITE_OK)
182 if (
mRc == SQLITE_OK)
184 mRc = sqlite3_initialize();
189 if (
mRc == SQLITE_OK)
195 auto vfs = sqlite3_vfs_find(
"unix-excl");
198 sqlite3_vfs_register(vfs, 1);
207 (void) sqlite3_shutdown();
210 static void LogCallback(
void *WXUNUSED(arg),
int code,
const char *msg)
212 wxLogMessage(
"sqlite3 message: (%d) %s", code, msg);
221 static std::optional<SQLiteBlobStream>
Open(
222 sqlite3* db,
const char* schema,
const char* table,
const char* column,
223 int64_t rowID,
bool readOnly)
noexcept
228 sqlite3_blob* blob =
nullptr;
230 const int rc = sqlite3_blob_open(
231 db, schema, table, column, rowID, readOnly ? 0 : 1, &blob);
236 return std::make_optional<SQLiteBlobStream>(blob, readOnly);
248 *
this = std::move(rhs);
270 return mBlob !=
nullptr;
275 if (
mBlob ==
nullptr)
278 const int rc = sqlite3_blob_close(
mBlob);
291 return SQLITE_MISUSE;
303 if (!
IsOpen() || ptr ==
nullptr)
304 return SQLITE_MISUSE;
308 if (availableBytes == 0)
313 else if (availableBytes <
size)
315 size = availableBytes;
343 static constexpr std::array<const char*, 2>
Columns = {
"dict",
"doc" };
346 sqlite3* db,
const char* schema,
const char* table,
390 size_t ReadData(
void* buffer,
size_t maxBytes)
override
399 maxBytes = std::min<size_t>(maxBytes, std::numeric_limits<int>::max());
400 auto bytesRead =
static_cast<int>(maxBytes);
402 if (SQLITE_OK !=
mBlobStream->Read(buffer, bytesRead))
411 else if (bytesRead == 0)
416 return static_cast<size_t>(bytesRead);
425 return sqliteIniter.
mRc == SQLITE_OK;
433 bShowProjectNumbers ? pProject->GetProjectNumber() : -1 );
441 if( window.IsIconized() )
452 return ptr->GetProjectName().empty();
472 auto result = std::make_shared< ProjectFileIO >( parent );
489 : mProject{ project }
501 wxLongLong freeSpace = 0;
504 if (wxGetDiskSpace(path, NULL, &freeSpace)) {
505 if (freeSpace < wxLongLong(wxLL(100 * 1048576))) {
510 XO(
"There is very little free disk space left on %s\n"
511 "Please select a bigger temporary directory location in\n"
512 "Directories Preferences.").Format( volume ),
513 "Error:_Disk_full_or_not_writable"
526 return connectionPtr.mpConnection !=
nullptr;
539 XO(
"Failed to open the project's database"),
541 "Error:_Disk_full_or_not_writable"
555 WriteXML(doc,
false, trackList.empty() ?
nullptr : &trackList);
574 if (fileName.empty())
577 if (fileName.empty())
588 wxFileName file(fileName);
589 file.SetFullName(
wxT(
""));
597 curConn = std::make_unique<DBConnection>(
599 auto rc = curConn->Open(fileName);
604 XO(
"Failed to open database file:\n\n%s").
Format(fileName),
632 if (!curConn->Close())
666 XO(
"Failed to discard connection")
677 file.SetFullName(
wxT(
""));
682 wxLogMessage(
"Failed to remove temporary project %s",
mPrevFileName);
697 if (!curConn->Close())
701 XO(
"Failed to restore connection")
718 curConn = std::move(conn);
726 return GuardedCall<int>(
727 [&]{
return cb(cols, vals,
names); },
734 char *errmsg =
nullptr;
736 const void *ptr = &callback;
738 const_cast<void*
>(ptr), &errmsg);
740 if (rc != SQLITE_ABORT && errmsg && !silent)
746 XO(
"Failed to execute a project file command:\n\n%s").
Format(query),
753 sqlite3_free(errmsg);
761 int rc =
Exec(sql, callback, silent);
764 if ( !(rc == SQLITE_OK || rc == SQLITE_ABORT) )
776 auto cb = [&result](
int cols,
char **vals,
char **){
783 return Query(sql, cb, silent);
788 bool success =
false;
789 auto cb = [&value, &success](
int cols,
char** vals,
char**)
793 const std::string_view valueString = vals[0];
795 success = std::errc() ==
797 valueString.data(), valueString.data() + valueString.length(),
805 return Query(sql, cb, silent) && success;
815 if (!
GetValue(
"SELECT Count(*) FROM sqlite_master WHERE type='table';", result))
823 XO(
"Project is in a read only directory\n(Unable to create the required temporary files)"),
833 if (wxStrtol<char **>(result,
nullptr, 10) == 0)
839 if (!
GetValue(
"PRAGMA application_ID;", result))
845 if (wxStrtoul<char **>(result,
nullptr, 10) !=
ProjectFileID)
847 SetError(
XO(
"This is not an Audacity project file"));
852 if (!
GetValue(
"PRAGMA user_version;", result))
865 XO(
"This project was created with a newer version of Audacity.\n\nYou will need to upgrade to open it.")
879 sql.Replace(
"<schema>", schema);
881 rc = sqlite3_exec(db, sql,
nullptr,
nullptr,
nullptr);
885 XO(
"Unable to initialize the project file")
904 sqlite3_result_int(context, blockids->find(blockid) != blockids->end());
912 auto cleanup =
finally([&]
915 sqlite3_create_function(db,
"inset", 1, SQLITE_UTF8 | SQLITE_DETERMINISTIC,
nullptr,
nullptr,
nullptr,
nullptr);
919 const void *p = &blockids;
920 rc = sqlite3_create_function(db,
"inset", 1, SQLITE_UTF8 | SQLITE_DETERMINISTIC,
const_cast<void*
>(p),
InSet,
nullptr,
nullptr);
927 SetDBError(
XO(
"Unable to add 'inset' function (can't verify blockids)"));
934 auto sql = wxString::Format(
935 "DELETE FROM sampleblocks WHERE %sinset(blockid);",
936 complement ?
"NOT " :
"" );
937 rc = sqlite3_exec(db, sql,
nullptr,
nullptr,
nullptr);
944 if( rc==SQLITE_READONLY)
946 SetDBError(
XO(
"Project is read only\n(Unable to work with the blockfiles)"));
947 else if( rc==SQLITE_LOCKED)
949 SetDBError(
XO(
"Project is locked\n(Unable to work with the blockfiles)"));
950 else if( rc==SQLITE_BUSY)
952 SetDBError(
XO(
"Project is busy\n(Unable to work with the blockfiles)"));
953 else if( rc==SQLITE_CORRUPT)
955 SetDBError(
XO(
"Project is corrupt\n(Unable to work with the blockfiles)"));
956 else if( rc==SQLITE_PERM)
958 SetDBError(
XO(
"Some permissions issue\n(Unable to work with the blockfiles)"));
959 else if( rc==SQLITE_IOERR)
961 SetDBError(
XO(
"A disk I/O error\n(Unable to work with the blockfiles)"));
962 else if( rc==SQLITE_AUTH)
964 SetDBError(
XO(
"Not authorized\n(Unable to work with the blockfiles)"));
973 int changes = sqlite3_changes(db);
976 wxLogInfo(
XO(
"Total orphan blocks deleted %d").Translation(), changes);
987 const std::vector<const TrackList *> &tracks )
1001 for (
auto trackList : tracks)
1008 auto cb = [&blockids](
int cols,
char **vals,
char **){
1010 wxString{ vals[0] }.ToLongLong(&blockid);
1011 blockids.insert(blockid);
1015 if (!
Query(
"SELECT blockid FROM sampleblocks;", cb))
1025 WriteXML(doc,
false, tracks.empty() ?
nullptr : tracks[0]);
1029 bool success =
false;
1034 auto cleanup =
finally([&]
1048 auto result = sqlite3_exec(db,
"ROLLBACK;",
nullptr,
nullptr,
nullptr);
1051 if (result != SQLITE_OK && (rc == SQLITE_DONE || rc == SQLITE_OK))
1055 "sqlite3.context",
"ProjectGileIO::CopyTo.cleanup");
1058 XO(
"Failed to rollback transaction during import")
1065 sqlite3_exec(db,
"DETACH DATABASE outbound;",
nullptr,
nullptr,
nullptr);
1068 wxRemoveFile(destpath);
1074 wxString dbName = destpath;
1076 dbName.Replace(
"'",
"''");
1077 sql.Printf(
"ATTACH DATABASE '%s' AS outbound;", dbName.ToUTF8());
1079 rc = sqlite3_exec(db, sql,
nullptr,
nullptr,
nullptr);
1080 if (rc != SQLITE_OK)
1083 XO(
"Unable to attach destination database")
1092 if ( pConn->FastMode(
"outbound") != SQLITE_OK)
1095 XO(
"Unable to switch to fast journaling mode")
1110 sqlite3_stmt *stmt =
nullptr;
1111 auto cleanup =
finally([&]
1116 sqlite3_finalize(stmt);
1121 rc = sqlite3_prepare_v2(db,
1122 "INSERT INTO outbound.sampleblocks"
1123 " SELECT * FROM main.sampleblocks"
1124 " WHERE blockid = ?;",
1128 if (rc != SQLITE_OK)
1132 "sqlite3.context",
"ProjectGileIO::CopyTo.prepare");
1135 XO(
"Unable to prepare project file command:\n\n%s").
Format(sql)
1145 wxLongLong_t count = 0;
1146 wxLongLong_t total = blockids.size();
1155 sqlite3_exec(db,
"BEGIN;",
nullptr,
nullptr,
nullptr);
1158 for (
auto blockid : blockids)
1161 rc = sqlite3_bind_int64(stmt, 1, blockid);
1162 if (rc != SQLITE_OK)
1166 "sqlite3.context",
"ProjectGileIO::CopyTo.bind");
1169 XO(
"Failed to bind SQL parameter")
1176 rc = sqlite3_step(stmt);
1177 if (rc != SQLITE_DONE)
1181 "sqlite3.context",
"ProjectGileIO::CopyTo.step");
1184 XO(
"Failed to update the project file.\nThe following command failed:\n\n%s").
Format(sql)
1190 if (sqlite3_reset(stmt) != SQLITE_OK)
1194 "sqlite3.context",
"ProjectGileIO::CopyTo.reset");
1199 result = progress.
Update(++count, total);
1213 if (!
WriteDoc(isTemporary ?
"autosave" :
"project", doc,
"outbound"))
1219 sqlite3_exec(db,
"COMMIT;",
nullptr,
nullptr,
nullptr);
1223 rc = sqlite3_exec(db,
"DETACH DATABASE outbound;",
nullptr,
nullptr,
nullptr);
1224 if (rc != SQLITE_OK)
1230 XO(
"Destination project could not be detached")
1245 unsigned long long current = 0;
1249 for (
auto pTracks : tracks)
1258 unsigned long long blockcount = 0;
1260 auto cb = [&blockcount](
int cols,
char **vals,
char **)
1263 wxString(vals[0]).ToULongLong(&blockcount);
1267 if (!
Query(
"SELECT Count(*) FROM sampleblocks;", cb) || blockcount == 0)
1279 wxLogDebug(
wxT(
"used = %lld total = %lld %lld"), current, total, total ? current / total : 0);
1280 if (!total || current / total > 80)
1282 wxLogDebug(
wxT(
"not compacting"));
1285 wxLogDebug(
wxT(
"compacting"));
1293 return connectionPtr.mpConnection;
1298 static const std::vector<wxString> strings {
1321 auto numberString = [](
int num) -> wxString {
1322 return num == 1 ? wxString{} : wxString::Format(
".%d", num);
1326 suffixes.push_back({});
1329 const auto name =
fn.GetName();
1332 fn.SetName(
name + numberString(nn++) + extra );
1333 result =
fn.GetFullPath();
1335 while( std::any_of(suffixes.begin(), suffixes.end(), [&](
auto &suffix){
1336 return wxFileExists(result + suffix);
1344 std::atomic_bool done = {
false};
1345 bool success =
false;
1346 auto thread = std::thread([&]
1348 success = wxRenameFile(src, dst);
1355 XO(
"Copying Project"),
XO(
"This may take several seconds"));
1361 using namespace std::chrono;
1362 std::this_thread::sleep_for(50ms);
1370 XO(
"Error Writing to File"),
1371 XO(
"Audacity failed to write file %s.\n"
1372 "Perhaps disk is full or not writable.\n"
1373 "For tips on freeing up space, click the help button.")
1375 "Error:_Disk_full_or_not_writable"
1393 std::vector< std::pair<FilePath, FilePath> > pairs{ { src, dst } };
1394 bool success =
false;
1395 auto cleanup =
finally([&]{
1400 for (
auto &pair : pairs) {
1401 if (!(pair.first.empty() && pair.second.empty()))
1402 wxRenameFile(pair.second, pair.first);
1408 auto srcName = src + suffix;
1409 if (wxFileExists(srcName)) {
1410 auto dstName = dst + suffix;
1413 pairs.push_back({ srcName, dstName });
1417 return (success =
true);
1422 if (!wxFileExists(filename))
1425 bool success = wxRemoveFile(filename);
1427 for (
const auto &suffix : suffixes) {
1428 auto file = filename + suffix;
1429 if (wxFileExists(file))
1430 success = wxRemoveFile(file) && success;
1448 if (!mPath.empty()) {
1457 if (!mPath.empty()) {
1458 if (!mSafety.empty()) {
1461 suffixes.push_back({});
1462 for (
const auto &suffix : suffixes) {
1463 auto path = mPath + suffix;
1464 if (wxFileExists(path))
1466 wxRenameFile(mSafety + suffix, mPath + suffix);
1473 const std::vector<const TrackList *> &tracks,
bool force)
1507 wxString backName = origName +
"_compact_back";
1508 wxString tempName = origName +
"_compact_temp";
1530 if (wxFileName::GetSize(tempName) < wxFileName::GetSize(origName))
1533 if (wxRenameFile(origName, backName))
1536 if (wxRenameFile(tempName, origName))
1542 if (!wxRemoveFile(backName))
1547 wxLogWarning(
wxT(
"Compaction failed to delete backup %s"), backName);
1557 wxLogWarning(
wxT(
"Compaction failed to open new project %s"), origName);
1560 if (!wxRenameFile(origName, tempName))
1562 wxLogWarning(
wxT(
"Compaction failed to rename original %s to temp %s"),
1563 origName, tempName);
1568 wxLogWarning(
wxT(
"Compaction failed to rename temp %s to orig %s"),
1569 origName, tempName);
1572 if (!wxRenameFile(backName, origName))
1574 wxLogWarning(
wxT(
"Compaction failed to rename back %s to orig %s"),
1575 backName, origName);
1580 wxLogWarning(
wxT(
"Compaction failed to rename orig %s to back %s"),
1581 backName, origName);
1587 wxLogWarning(
wxT(
"Compaction failed to reopen %s"), origName);
1593 if (!wxRemoveFile(tempName))
1598 wxLogWarning(
wxT(
"Failed to delete temporary file...ignoring"));
1629 auto &window = *pWindow;
1630 wxString
name = project.GetProjectName();
1638 XO(
"[Project %02i] Audacity \"%s\"")
1639 .Format( number + 1,
1653 name +=
_(
"(Recovered)");
1656 if (
name != window.GetTitle())
1658 window.SetTitle(
name );
1659 window.SetName(
name);
1662 if (
auto pThis = wThis.lock())
1691 project.SetProjectName({});
1695 project.SetProjectName(wxFileName(
mFileName).GetName());
1705 wxString fileVersion;
1706 wxString audacityVersion;
1707 int requiredTags = 0;
1711 for (
auto pair : attrs)
1713 auto attr = pair.first;
1714 auto value = pair.second;
1717 .CallAttributeHandler( attr, project, value ) )
1720 else if (attr ==
"version")
1722 fileVersion = value.ToWString();
1726 else if (attr ==
"audacityversion")
1728 audacityVersion = value.ToWString();
1733 if (requiredTags < 2)
1742 if (!wxSscanf(fileVersion,
wxT(
"%i.%i.%i"), &fver, &frel, &frev))
1753 int fileVer = ((fver *100)+frel)*100+frev;
1754 int codeVer = ((cver *100)+crel)*100+crev;
1756 if (codeVer<fileVer)
1759 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.")
1760 .Format(audacityVersion, AUDACITY_VERSION_STRING);
1763 XO(
"Can't open project file"),
1765 "FAQ:Errors_opening_an_Audacity_project"
1771 if (tag !=
"project")
1796 xmlFile.
Write(
wxT(
"version=\"1.0\" "));
1797 xmlFile.
Write(
wxT(
"standalone=\"no\" "));
1803 xmlFile.
Write(
wxT(
"\"-//audacityproject-1.3.0//DTD//EN\" "));
1804 xmlFile.
Write(
wxT(
"\"http://audacity.sourceforge.net/xml/audacityproject-1.3.0.dtd\" "));
1819 xmlFile.
WriteAttr(
wxT(
"xmlns"),
wxT(
"http://audacity.sourceforge.net/xml/"));
1822 xmlFile.
WriteAttr(
wxT(
"audacityversion"), AUDACITY_VERSION_STRING);
1826 tracklist.Any().Visit([&](
const Track *t)
1837 else if ( useTrack->GetId() ==
TrackId{} ) {
1843 useTrack->WriteXML(xmlFile);
1857 if (
WriteDoc(
"autosave", autosave))
1875 rc = sqlite3_exec(db,
"DELETE FROM autosave;",
nullptr,
nullptr,
nullptr);
1876 if (rc != SQLITE_OK)
1882 XO(
"Failed to remove the autosave information from the project file.")
1894 const char *schema )
1907 "INSERT INTO %s.%s(id, dict, doc) VALUES(1, ?1, ?2)"
1908 " ON CONFLICT(id) DO UPDATE SET dict = ?1, doc = ?2;",
1911 sqlite3_stmt *stmt =
nullptr;
1912 auto cleanup =
finally([&]
1916 sqlite3_finalize(stmt);
1920 rc = sqlite3_prepare_v2(db, sql, -1, &stmt,
nullptr);
1921 if (rc != SQLITE_OK)
1928 XO(
"Unable to prepare project file command:\n\n%s").
Format(sql)
1940 sqlite3_bind_zeroblob(stmt, 1, dict.
GetSize()) ||
1941 sqlite3_bind_zeroblob(stmt, 2, data.
GetSize()))
1951 const auto reportError = [
this](
auto sql) {
1953 XO(
"Failed to update the project file.\nThe following command failed:\n\n%s")
1957 rc = sqlite3_step(stmt);
1959 if (rc != SQLITE_DONE)
1970 sqlite3_finalize(stmt);
1977 const wxString rowIDSql =
1978 wxString::Format(
"SELECT ROWID FROM %s.%s WHERE id = 1;", schema, table);
1980 if (!
GetValue(rowIDSql, rowID,
true))
1985 reportError(rowIDSql);
1989 const auto writeStream = [db, schema, table, rowID,
this](
const char* column,
const MemoryStream& stream) {
2004 for (
auto chunk : stream)
2006 if (SQLITE_OK != blobStream->Write(chunk.first, chunk.second))
2017 if (blobStream->Close() != SQLITE_OK)
2020 "sqlite3.rc", std::to_string(sqlite3_errcode(db)));
2023 "sqlite3.context",
"ProjectGileIO::WriteDoc::writeBlobStream");
2033 if (!writeStream(
"dict", dict))
2036 if (!writeStream(
"doc", data))
2039 const auto requiredVersion =
2042 const wxString setVersionSql =
2043 wxString::Format(
"PRAGMA user_version = %u", requiredVersion.GetPacked());
2045 if (!
Query(setVersionSql.c_str(), [](
auto...) { return 0; }))
2050 reportError(setVersionSql);
2054 return transaction.
Commit();
2059 auto now = std::chrono::high_resolution_clock::now();
2061 bool success =
false;
2063 auto cleanup =
finally([&]
2083 GetValue(
"SELECT ROWID FROM main.autosave WHERE id = 1;", rowId,
true);
2085 int64_t rowsCount = 0;
2089 (!
GetValue(
"SELECT COUNT(1) FROM main.project;", rowsCount,
true) || rowsCount == 0))
2100 if (!useAutosave && !
GetValue(
"SELECT ROWID FROM main.project WHERE id = 1;", rowId,
false))
2108 DB(),
"main", useAutosave ?
"autosave" :
"project", rowId);
2115 XO(
"Unable to parse project information.")
2124 ->GetActiveBlockIDs();
2125 if (blockids.size() > 0)
2151 success =
GetValue(
"SELECT Count(*) FROM project;", result);
2165 auto duration = std::chrono::high_resolution_clock::now() - now;
2168 "Project loaded in %lld ms",
2169 std::chrono::duration_cast<std::chrono::milliseconds>(duration).count());
2208 bool reopened =
false;
2210 if (
true == (moved =
MoveProject(savedName, fileName)))
2228 if (reopened && !moved) {
2233 wxTheApp->CallAfter([
this]{
2237"The project's database failed to reopen, "
2238"possibly because of limited space on the storage device."),
2239 "Error:_Disk_full_or_not_writable"
2255 if (!
CopyTo(fileName,
XO(
"Saving project"),
false))
2258 XO(
"Error Saving Project"),
2260 "Error:_Disk_full_or_not_writable"
2266 Connection newConn = std::make_unique<DBConnection>(
2268 [
this]{ OnCheckpointFailure(); });
2281 std::atomic_bool done = {
false};
2282 bool success =
true;
2283 auto thread = std::thread([&]
2285 auto rc = newConn->Open(fileName);
2286 if (rc != SQLITE_OK)
2298 XO(
"Syncing"),
XO(
"This may take several seconds"));
2304 using namespace std::chrono;
2305 std::this_thread::sleep_for(50ms);
2314 XO(
"Error Saving Project"),
2315 XO(
"The project failed to open, possibly due to limited space\n"
2316 "on the storage device.\n\n%s").Format(
GetLastError()),
2317 "Error:_Disk_full_or_not_writable");
2322 if (!wxRemoveFile(fileName))
2324 wxLogMessage(
"Failed to remove destination project after open failure: %s", fileName);
2336 XO(
"Error Saving Project"),
2337 XO(
"Unable to remove autosave information, possibly due to limited space\n"
2338 "on the storage device.\n\n%s").Format(
GetLastError()),
2339 "Error:_Disk_full_or_not_writable");
2344 if (!wxRemoveFile(fileName))
2346 wxLogMessage(
"Failed to remove destination project after AutoSaveDelete failure: %s", fileName);
2365 Compact( { lastSaved ? lastSaved : empty.get() }, true );
2379 XO(
"Error Saving Project"),
2381 "Error:_Disk_full_or_not_writable"
2407 return CopyTo(fileName,
XO(
"Backing up project"),
false,
true,
2421 wxLogDebug(
"Closing project with no database connection");
2438 wxFileName file(filename);
2439 file.SetFullName(
wxT(
""));
2476 wxLongLong freeSpace;
2477 if (wxGetDiskSpace(wxPathOnly(
mFileName), NULL, &freeSpace))
2481 constexpr auto limit = 1ll << 32;
2486 auto length = wxFile{
mFileName}.Length();
2489 if (length == wxInvalidSize)
2491 auto free = std::max<wxLongLong>(0, limit - length);
2492 freeSpace =
std::min(freeSpace, free);
2504 const wxString &helpPage)
2538 currConn->SetError(msg, libraryError, errorCode);
2546 currConn->SetDBError(msg, libraryError, errorCode);
2562 currConn->SetBypass(
true );
2576 currConn->SetBypass(
false );
2592 const std::vector<const TrackList*> &trackLists)
const
2594 unsigned long long current = 0;
2600 for (
auto pTracks: trackLists)
2622 sqlite3_stmt* stmt =
nullptr;
2626 static const char* statement =
2628 sum(length(blockid) + length(sampleformat) +
2629 length(summin) + length(summax) + length(sumrms) +
2630 length(summary256) + length(summary64k) +
2632FROM sampleblocks;)";
2638 static const char* statement =
2640 length(blockid) + length(sampleformat) +
2641 length(summin) + length(summax) + length(sumrms) +
2642 length(summary256) + length(summary64k) +
2644FROM sampleblocks WHERE blockid = ?1;)";
2649 auto cleanup =
finally(
2652 if (stmt !=
nullptr)
2654 sqlite3_clear_bindings(stmt);
2655 sqlite3_reset(stmt);
2661 int rc = sqlite3_bind_int64(stmt, 1, blockid);
2663 if (rc != SQLITE_OK)
2666 "sqlite3.rc", std::to_string(rc));
2669 "sqlite3.context",
"ProjectFileIO::GetDiskUsage::bind");
2675 int rc = sqlite3_step(stmt);
2677 if (rc != SQLITE_ROW)
2682 "sqlite3.context",
"ProjectFileIO::GetDiskUsage::step");
2687 const int64_t
size = sqlite3_column_int64(stmt, 0);
2700 projectFileIO.SetBypass();
2706 try { wxTheApp->Yield(); }
catch(...) {}
2709 projectFileIO.CloseProject();
2711 try { wxTheApp->Yield(); }
catch(...) {}
2715static ProjectHistory::AutoSave::Scope
scope {
2718 if ( !projectFileIO.AutoSave() )
2721 XO(
"Automatic database backup failed."),
2723 "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.
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
const TranslatableString name
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
static void RefreshAllTitles(bool bShowProjectNumbers)
std::unordered_set< SampleBlockID > BlockIDs
@ CheckpointFailure
Failure happened in a worker thread.
@ ProjectTitleChange
A normal occurrence.
wxFrame * FindProjectFrame(AudacityProject *project)
Get a pointer to the window associated with a project, or null if the given pointer is null,...
AUDACITY_DLL_API wxFrame & GetProjectFrame(AudacityProject &project)
Get the top-level window associated with the project (as a wxFrame only, when you do not need to use ...
accessors for certain important windows associated with each project
std::function< void(const SampleBlock &) > 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.
void InspectBlocks(const TrackList &tracks, BlockInspector inspector, SampleBlockIDSet *pIDs)
std::unordered_set< SampleBlockID > SampleBlockIDSet
std::vector< Attribute > AttributesList
const_iterator end() const
Container::value_type value_type
const_iterator begin() const
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
const wxString & GetProjectName() const
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)
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.
ProgressResult Update(int value, const TranslatableString &message={})
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 LoadProject(const FilePath &fileName, bool ignoreAutosave)
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.
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
static void LogCallback(void *WXUNUSED(arg), int code, const char *msg)
A MessageBoxException that shows a given, unvarying string.
TitleRestorer(wxTopLevelWindow &window, AudacityProject &project)
Abstract base class for an object holding data associated with points on a time axis.
std::shared_ptr< Track > SubstitutePendingChangedTrack()
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 std::shared_ptr< TrackList > 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
void Add(const FilePath &path)
void Remove(const FilePath &path)
std::unique_ptr< GenericProgressDialog > MakeGenericProgress(const WindowPlacement &placement, const TranslatableString &title, const TranslatableString &message)
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.
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()
void swap(std::unique_ptr< Alg_seq > &a, std::unique_ptr< Alg_seq > &b)
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.