Skip to content

Commit

Permalink
Merge branch 'release-3.5.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
vsverchinsky committed Apr 22, 2024
2 parents 16df49d + 5427532 commit 678490d
Show file tree
Hide file tree
Showing 42 changed files with 8,931 additions and 17,955 deletions.
12 changes: 9 additions & 3 deletions CHANGELOG.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,22 @@ Audacity 3.5 adds cloud saving, beat detection, pitch shifting and more.

Other changes

Changes

Additions
* Added an option to skip plugin scanning to Preferences -> Effects.
* Added an overflow menu, as well as speed and pitch indicators to clips.
* Added subtitle formats for labels. Export to WebVTT and SubRip and import of SubRip files is now supported. (Thanks, Pokechu22 and Larry Bordowitz!)

Changes

* Reworked the plugin manager.
* Improved accessibility. (Thanks, David Bailes!)
* When zooming in vertically (Ctrl+scroll on the vertical scale), the zeroline now remains centered. Moving the vertical zoom off-center still is possible via Shift+scroll on the vertical scale.
* Changed the look of the selection adjust cursors (previously pointing fingers on Windows and macOS, now look like >| everywhere).
* Changed the timeline options button from a green triangle to a settings gear. From it, you can switch between Beats & Measures or hh:mm:ss timeline rulers, set various preferences regarding to looping, as well as playhead behaviors: "Scroll view to playhead" (previously: "Update display while playing") and "Continuous scrolling" (previously: Pinned Play Head).
* Improved BSD support. (Thanks, Brad Smith!)
* macOS: Changed the Audacity.app icon on macOS to fit new design guidelines.
* Added an overflow menu, as well as speed and pitch indicators to clips.
* Reordered some menus.
* Moved OK/Cancel buttons of effects to the bottom.

Removals

Expand All @@ -44,6 +49,7 @@ Audacity 3.5 adds cloud saving, beat detection, pitch shifting and more.
* Replaced "Advanced Vertical Zooming" with new always-on behavior.
* Removed some vertical zoom presets.
* Removed EQ XML to TXT converter, which was needed for the Audacity 2.3 -> 2.4 transition. It is now a plugin instead, downloadable at https://plugins.audacityteam.org/additional-resources/eq-curves/eq-xml-to-txt-converter.
* Removed the "Vocal reduction and isolation" effect. As a replacement on Windows and Linux, the Intel OpenVINO plugins work better and on a wider array of content, including mono tracks. The old effect still can be downloaded from https://plugins.audacityteam.org/nyquist-plugins/effect-plugins/filters-and-eq#vocal-reduction-and-isolation

Bugfixes

Expand Down
10 changes: 6 additions & 4 deletions help/audacity.appdata.xml
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,14 @@
<url type="help">https://support.audacityteam.org/</url>
<url type="translate">https://support.audacityteam.org/community/contributing/translating</url>
<releases>
<release version="3.4.0" date="2023-11-01">
<release version="3.5.0" date="2024-04-22">
<description>
<p>New in Audacity 3.4: Our first dedicated music release, featuring beats &amp; bars, time stretching, a new exporter and more!</p>
<p>Watch the <a href="https://audacityteam.org/3.4.0-video">Release video</a> or read the <a href="https://support.audacityteam.org/additional-resources/changelog"> changelog</a> to learn more.</p>
<p>The changelog also contains information about subsequent 3.4.x patches, should any be available.</p>
<p>New in Audacity 3.5: Cloud saving, automatic tempo detection, pitch shifting and more!</p>
<p>Watch the release video or read the changelog to learn more.</p>
<p>The changelog also contains information about subsequent 3.5.x patches, should any be available.</p>
</description>
<url>https://support.audacityteam.org/additional-resources/changelog/audacity-3.5</url>
<url>https://www.audacityteam.org/3.5.0-video</url>
</release>
</releases>
</component>
6 changes: 4 additions & 2 deletions libraries/lib-cloud-audiocom/CloudSyncService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -566,7 +566,9 @@ void CloudSyncService::SyncCloudSnapshot(
// 1. Local snapshot ID matches the remote snapshot ID. Just complete the
// sync right away. If the project was modified locally, but not saved,
// the user will be prompted about the autosave.
if (localProjectInfo->SnapshotId == snapshotInfo.Id)
if (
localProjectInfo->SnapshotId == snapshotInfo.Id &&
localProjectInfo->SyncStatus != sync::DBProjectData::SyncStatusDownloading)
{
CompleteSync(
{ sync::ProjectSyncResult::StatusCode::Succeeded, {}, utf8Path });
Expand All @@ -575,7 +577,7 @@ void CloudSyncService::SyncCloudSnapshot(
// 2. Project sync was interrupted.
if (
mode == SyncMode::Normal &&
localProjectInfo->SyncStatus != sync::DBProjectData::SyncStatusSynced)
localProjectInfo->SyncStatus == sync::DBProjectData::SyncStatusUploading)
{
// There is not enough information to decide if the project has
// diverged. Just open it, so the sync can resume. If the project has
Expand Down
14 changes: 13 additions & 1 deletion libraries/lib-cloud-audiocom/NetworkUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ SyncResultCode GuessResultCode(IResponse& response) noexcept
code == NetworkError::HostNotFound ||
code == NetworkError::ProxyConnectionFailed ||
code == NetworkError::ProxyNotFound ||
code == NetworkError::RemoteHostClosed)
code == NetworkError::RemoteHostClosed ||
code == NetworkError::Timeout)
return SyncResultCode::ConnectionFailed;

if (code == NetworkError::HTTPError)
Expand Down Expand Up @@ -113,6 +114,17 @@ void SetCommonHeaders(Request& request)
common_headers::Authorization, oauthService.GetAccessToken());
}

bool IsUploadRecoverable(SyncResultCode code)
{
return code == SyncResultCode::Cancelled ||
code == SyncResultCode::ConnectionFailed ||
code == SyncResultCode::PaymentRequired ||
code == SyncResultCode::Unauthorized ||
code == SyncResultCode::Forbidden ||
code == SyncResultCode::InternalClientError ||
code == SyncResultCode::InternalServerError;
}

TransferStats& TransferStats::SetBytesTransferred(int64_t bytesTransferred)
{
BytesTransferred = bytesTransferred;
Expand Down
2 changes: 2 additions & 0 deletions libraries/lib-cloud-audiocom/NetworkUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,6 @@ struct ResponseResult final
audacity::network_manager::IResponse& response, bool readBody);

void SetCommonHeaders(audacity::network_manager::Request& request);

bool IsUploadRecoverable(SyncResultCode code);
} // namespace audacity::cloud::audiocom
110 changes: 78 additions & 32 deletions libraries/lib-cloud-audiocom/sync/CloudProjectsDatabase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,9 @@ CREATE TABLE IF NOT EXISTS migration
version INTEGER
);
INSERT OR IGNORE INTO migration (version) VALUES (0);
INSERT INTO migration (version) VALUES (0);
DELETE FROM migration WHERE ROWID != 1;
)";

const auto addProjectSyncedDialogShownColumn = R"(
Expand Down Expand Up @@ -198,29 +200,9 @@ void cloud::audiocom::sync::CloudProjectsDatabase::DeleteProject(
if (!connection)
return;

static const char* queries[] = {
"DELETE FROM projects WHERE project_id = ?",
"DELETE FROM block_hashes WHERE project_id = ?",
"DELETE FROM pending_snapshots WHERE project_id = ?",
"DELETE FROM pending_project_blobs WHERE project_id = ?",
"DELETE FROM pending_project_blocks WHERE project_id = ?",
"DELETE FROM project_users WHERE project_id = ?",
};

auto tx = connection->BeginTransaction("DeleteProject");

for (auto query : queries)
{
auto statement = connection->CreateStatement(query);

if (!statement)
return;

if (!statement->Prepare(projectId).Run().IsOk())
return;
}

tx.Commit();
if (DeleteProject(connection, projectId))
tx.Commit();
}

bool CloudProjectsDatabase::MarkProjectAsSynced(
Expand Down Expand Up @@ -331,13 +313,15 @@ bool CloudProjectsDatabase::UpdateProjectData(const DBProjectData& projectData)
if (!connection)
return false;

auto statement = connection->CreateStatement(
auto tx = connection->BeginTransaction("UpdateProjectData");

auto updateProjectData = connection->CreateStatement(
"INSERT OR REPLACE INTO projects (project_id, snapshot_id, saves_count, last_audio_preview_save, local_path, last_modified, last_read, sync_status, synced_dialog_shown) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)");

if (!statement)
if (!updateProjectData)
return false;

auto result = statement
auto result = updateProjectData
->Prepare(
projectData.ProjectId, projectData.SnapshotId,
projectData.SavesCount, projectData.LastAudioPreview,
Expand All @@ -346,7 +330,30 @@ bool CloudProjectsDatabase::UpdateProjectData(const DBProjectData& projectData)
projectData.FirstSyncDialogShown)
.Run();

return result.IsOk();
if (!result.IsOk())
return false;

auto listMissingProjects = connection->CreateStatement (
"SELECT project_id FROM projects WHERE project_id != ? AND local_path = ?");

if (!listMissingProjects)
return false;

auto missingProjects = listMissingProjects->Prepare(
projectData.ProjectId, projectData.LocalPath).Run();

for (auto row : missingProjects)
{
std::string missingProjectId;

if (!row.Get(0, missingProjectId))
return false;

if (!DeleteProject(connection, missingProjectId))
return false;
}

return tx.Commit().IsOk();
}

bool cloud::audiocom::sync::CloudProjectsDatabase::IsFirstSyncDialogShown(
Expand Down Expand Up @@ -492,13 +499,26 @@ void CloudProjectsDatabase::RemovePendingSnapshot(
if (!connection)
return;

auto statement = connection->CreateStatement(
"DELETE FROM pending_snapshots WHERE project_id = ? AND snapshot_id = ?");
static const char* queries[] = {
"DELETE FROM pending_snapshots WHERE project_id = ? AND snapshot_id = ?",
"DELETE FROM pending_project_blobs WHERE project_id = ? AND snapshot_id = ?",
"DELETE FROM pending_project_blocks WHERE project_id = ? AND snapshot_id = ?",
};

if (!statement)
return;
auto tx = connection->BeginTransaction("RemovePendingSnapshot");

statement->Prepare(projectId, snapshotId).Run();
for (auto query : queries)
{
auto statement = connection->CreateStatement(query);

if (!statement)
return;

if (!statement->Prepare(projectId, snapshotId).Run().IsOk())
return;
}

tx.Commit();
}

std::vector<PendingSnapshotData>
Expand Down Expand Up @@ -873,4 +893,30 @@ bool CloudProjectsDatabase::RunMigrations()
return tx.Commit().IsOk();
}

bool cloud::audiocom::sync::CloudProjectsDatabase::DeleteProject(
sqlite::SafeConnection::Lock& connection, std::string_view projectId)
{
static const char* queries[] = {
"DELETE FROM projects WHERE project_id = ?",
"DELETE FROM block_hashes WHERE project_id = ?",
"DELETE FROM pending_snapshots WHERE project_id = ?",
"DELETE FROM pending_project_blobs WHERE project_id = ?",
"DELETE FROM pending_project_blocks WHERE project_id = ?",
"DELETE FROM project_users WHERE project_id = ?",
};

for (auto query : queries)
{
auto statement = connection->CreateStatement(query);

if (!statement)
return false;

if (!statement->Prepare(projectId).Run().IsOk())
return false;
}

return true;
}

} // namespace audacity::cloud::audiocom::sync
2 changes: 2 additions & 0 deletions libraries/lib-cloud-audiocom/sync/CloudProjectsDatabase.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,8 @@ class CloudProjectsDatabase final
bool OpenConnection();
bool RunMigrations();

bool DeleteProject(sqlite::SafeConnection::Lock& connection, std::string_view projectId);

std::mutex mConnectionMutex;
std::shared_ptr<sqlite::SafeConnection> mConnection;
};
Expand Down
2 changes: 1 addition & 1 deletion libraries/lib-cloud-audiocom/sync/CloudSyncHousekeeper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ class Housekeeper final
if (!wxFileExists(path))
{
cloudProjectsDatabase.DeleteProject(project.ProjectId);
return;
continue;
}

const auto lastAccess =
Expand Down
39 changes: 20 additions & 19 deletions libraries/lib-cloud-audiocom/sync/DataUploader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ struct DataUploader::UploadOperation final :

std::string MimeType;
UploadData Data;
std::shared_ptr<IResponse> NetworkResponse;

ResponseResult CurrentResult;
CancellationContextPtr CancelContext;
Expand All @@ -70,31 +69,33 @@ struct DataUploader::UploadOperation final :
Request request { Target.UploadUrl };
request.setHeader(common_headers::ContentType, MimeType);

ResponsePtr networkResponse;

if (std::holds_alternative<std::vector<uint8_t>>(Data))
{
auto data = *std::get_if<std::vector<uint8_t>>(&Data);

NetworkResponse = NetworkManager::GetInstance().doPut(
networkResponse = NetworkManager::GetInstance().doPut(
request, data.data(), data.size());
CancelContext->OnCancelled(NetworkResponse);
}
else
{
auto filePath = *std::get_if<std::string>(&Data);

NetworkResponse = NetworkManager::GetInstance().doPut(
networkResponse = NetworkManager::GetInstance().doPut(
request, CreateRequestPayloadStream(filePath));
CancelContext->OnCancelled(NetworkResponse);
}

NetworkResponse->setRequestFinishedCallback(
[this, retriesLeft, operation = weak_from_this()](auto)
CancelContext->OnCancelled(networkResponse);

networkResponse->setRequestFinishedCallback(
[this, retriesLeft, networkResponse, operation = weak_from_this()](auto)
{
auto strongThis = operation.lock();
if (!strongThis)
return;

CurrentResult = GetResponseResult(*NetworkResponse, false);
CurrentResult = GetResponseResult(*networkResponse, false);

if (CurrentResult.Code == SyncResultCode::Success)
ConfirmUpload(RetriesCount);
Expand All @@ -106,7 +107,7 @@ struct DataUploader::UploadOperation final :
FailUpload(RetriesCount);
});

NetworkResponse->setUploadProgressCallback(
networkResponse->setUploadProgressCallback(
[this, operation = weak_from_this()](
int64_t current, int64_t total)
{
Expand All @@ -129,18 +130,18 @@ struct DataUploader::UploadOperation final :
Data = {};
Request request { Target.SuccessUrl };

NetworkResponse =
auto networkResponse =
NetworkManager::GetInstance().doPost(request, nullptr, 0);
CancelContext->OnCancelled(NetworkResponse);
CancelContext->OnCancelled(networkResponse);

NetworkResponse->setRequestFinishedCallback(
[this, retriesLeft, operation = weak_from_this()](auto)
networkResponse->setRequestFinishedCallback(
[this, retriesLeft, networkResponse, operation = weak_from_this()](auto)
{
auto strongThis = operation.lock();
if (!strongThis)
return;

CurrentResult = GetResponseResult(*NetworkResponse, false);
CurrentResult = GetResponseResult(*networkResponse, false);

if (CurrentResult.Code == SyncResultCode::Success)
{
Expand Down Expand Up @@ -170,18 +171,18 @@ struct DataUploader::UploadOperation final :

Request request { Target.FailUrl };

NetworkResponse =
auto networkResponse =
NetworkManager::GetInstance().doPost(request, nullptr, 0);
CancelContext->OnCancelled(NetworkResponse);
CancelContext->OnCancelled(networkResponse);

NetworkResponse->setRequestFinishedCallback(
[this, retriesLeft, operation = weak_from_this()](auto)
networkResponse->setRequestFinishedCallback(
[this, retriesLeft, networkResponse, operation = weak_from_this()](auto)
{
auto strongThis = operation.lock();
if (!strongThis)
return;

const auto result = GetResponseResult(*NetworkResponse, false);
const auto result = GetResponseResult(*networkResponse, false);

if (
result.Code == SyncResultCode::ConnectionFailed &&
Expand Down
Loading

0 comments on commit 678490d

Please sign in to comment.