1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-05-02 08:39:46 +02:00

Merge release-3.0.3 into master

Conflicts:
	src/AboutDialog.cpp
	src/prefs/ApplicationPrefs.cpp
This commit is contained in:
Paul Licameli 2021-07-12 14:41:27 -04:00
commit d0d64bf025
32 changed files with 464 additions and 192 deletions

View File

@ -74,9 +74,9 @@ jobs:
cache-name: cache-conan-modules
with:
path: ${{ env.CONAN_USER_HOME }}
key: host-${{ matrix.config.name }}-${{ hashFiles('cmake-proxies/CMakeLists.txt') }}
key: host-3-${{ matrix.config.name }}-${{ hashFiles('cmake-proxies/CMakeLists.txt') }}
restore-keys: |
host-${{ matrix.config.name }}-
host-3-${{ matrix.config.name }}-
- name: Configure
env:

View File

@ -19,7 +19,7 @@ endif ()
# still link to the alpha manual online.
# Set this value to 0 for alpha, 1 for beta, 2 for release builds
set( AUDACITY_BUILD_LEVEL 0 )
set( AUDACITY_BUILD_LEVEL 0 CACHE STRING "0 for alpha, 1 for beta, 2 for release builds" )
# The Audacity version
# Increment as appropriate after release of a new version, and set back

View File

@ -246,7 +246,7 @@ namespace
{
static constexpr int MaxUserCommentLength = 2000;
auto frame = new wxFrame(
auto dialog = new wxDialog(
nullptr,
wxID_ANY,
_("Problem Report for Audacity"),
@ -254,83 +254,89 @@ namespace
wxDefaultSize,
wxDEFAULT_FRAME_STYLE & ~(wxRESIZE_BORDER | wxMAXIMIZE_BOX)//disable frame resize
);
frame->SetOwnBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_FRAMEBK));
//fixes focus issue with Windows build-in screen reader, but breaks VoiceOver
#if defined(__WXMSW__)
dialog->SetFocus();
#endif
auto mainLayout = new wxBoxSizer(wxVERTICAL);
auto headerText = new wxStaticText(frame, wxID_ANY, header);
headerText->SetFont(wxFont(wxFontInfo().Bold()));
auto headerLayout = new wxBoxSizer(wxHORIZONTAL);
headerLayout->Add(new wxStaticBitmap(frame, wxID_ANY, wxIcon(warning)));
headerLayout->Add(new wxStaticBitmap(dialog, wxID_ANY, wxIcon(warning)));
headerLayout->AddSpacer(5);
auto headerText = new wxStaticText(dialog, wxID_ANY, header);
headerText->SetFont(wxFont(wxFontInfo().Bold()));
headerLayout->Add(headerText, wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
mainLayout->Add(headerLayout, wxSizerFlags().Border(wxALL));
if (onSend != nullptr)
{
mainLayout->AddSpacer(5);
mainLayout->Add(new wxStaticText(dialog, wxID_ANY, _("Click \"Send\" to submit the report to Audacity. This information is collected anonymously.")), wxSizerFlags().Border(wxALL));
}
mainLayout->AddSpacer(10);
mainLayout->Add(new wxStaticText(dialog, wxID_ANY, _("Problem details")), wxSizerFlags().Border(wxALL));
auto dumpTextCtrl = new wxTextCtrl(dialog, wxID_ANY, dump, wxDefaultPosition, wxSize(500, 300), wxTE_RICH | wxTE_READONLY | wxTE_MULTILINE | wxTE_DONTWRAP);
dumpTextCtrl->SetFont(wxFont(wxFontInfo().Family(wxFONTFAMILY_TELETYPE)));
dumpTextCtrl->ShowPosition(0);//scroll to top
mainLayout->Add(dumpTextCtrl, wxSizerFlags().Border(wxALL).Expand());
auto buttonsLayout = new wxBoxSizer(wxHORIZONTAL);
wxTextCtrl* commentCtrl = nullptr;
if (onSend != nullptr)
{
commentCtrl = new wxTextCtrl(frame, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(500, 100), wxTE_MULTILINE);
mainLayout->AddSpacer(10);
mainLayout->Add(new wxStaticText(dialog, wxID_ANY, _("Comments")), wxSizerFlags().Border(wxALL));
commentCtrl = new wxTextCtrl(dialog, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(500, 100), wxTE_MULTILINE);
commentCtrl->SetMaxLength(MaxUserCommentLength);
mainLayout->Add(commentCtrl, wxSizerFlags().Border(wxALL).Expand());
}
if (onSend != nullptr)
if (onSend != nullptr && commentCtrl != nullptr)
{
auto okButton = new wxButton(frame, wxID_ANY, XC("&Don't send", "crash reporter button"));
auto sendButton = new wxButton(frame, wxID_ANY, XC("&Send", "crash reporter button"));
auto dontSendButton = new wxButton(dialog, wxID_ANY, XC("&Don't send", "crash reporter button"));
auto sendButton = new wxButton(dialog, wxID_ANY, XC("&Send", "crash reporter button"));
okButton->Bind(wxEVT_BUTTON, [frame](wxCommandEvent&)
dontSendButton->Bind(wxEVT_BUTTON, [dialog](wxCommandEvent&)
{
frame->Close(true);
dialog->Close(true);
});
sendButton->Bind(wxEVT_BUTTON, [frame, commentCtrl, onSend](wxCommandEvent&)
sendButton->Bind(wxEVT_BUTTON, [dialog, commentCtrl, onSend](wxCommandEvent&)
{
if (onSend(commentCtrl->GetValue()))
{
frame->Close(true);
dialog->Close(true);
}
});
buttonsLayout->Add(okButton);
buttonsLayout->Add(dontSendButton);
buttonsLayout->AddSpacer(5);
buttonsLayout->Add(sendButton);
}
else
{
auto okButton = new wxButton(frame, wxID_OK, wxT("OK"));
okButton->Bind(wxEVT_BUTTON, [frame](wxCommandEvent&)
auto okButton = new wxButton(dialog, wxID_OK, wxT("OK"));
okButton->Bind(wxEVT_BUTTON, [dialog](wxCommandEvent&)
{
frame->Close(true);
dialog->Close(true);
});
buttonsLayout->Add(okButton);
}
mainLayout->Add(headerLayout, wxSizerFlags().Border(wxALL));
if (onSend != nullptr)
{
mainLayout->AddSpacer(5);
mainLayout->Add(new wxStaticText(frame, wxID_ANY, _("Click \"Send\" to submit the report to Audacity. This information is collected anonymously.")), wxSizerFlags().Border(wxALL));
}
mainLayout->AddSpacer(10);
mainLayout->Add(new wxStaticText(frame, wxID_ANY, _("Problem details")), wxSizerFlags().Border(wxALL));
auto dumpTextCtrl = new wxTextCtrl(frame, wxID_ANY, dump, wxDefaultPosition, wxSize(500, 300), wxTE_RICH | wxTE_READONLY | wxTE_MULTILINE | wxTE_DONTWRAP);
dumpTextCtrl->SetFont(wxFont(wxFontInfo().Family(wxFONTFAMILY_TELETYPE)));
dumpTextCtrl->ShowPosition(0);//scroll to top
mainLayout->Add(dumpTextCtrl, wxSizerFlags().Border(wxALL).Expand());
if (onSend != nullptr)
{
mainLayout->AddSpacer(10);
mainLayout->Add(new wxStaticText(frame, wxID_ANY, _("Comments")), wxSizerFlags().Border(wxALL));
mainLayout->Add(commentCtrl, wxSizerFlags().Border(wxALL).Expand());
}
mainLayout->Add(buttonsLayout, wxSizerFlags().Border(wxALL).Align(wxALIGN_RIGHT));
frame->SetSizerAndFit(mainLayout);
dialog->SetSizerAndFit(mainLayout);
frame->Show(true);
dialog->Bind(wxEVT_CLOSE_WINDOW, [dialog](wxCloseEvent&) {
dialog->Destroy();
});
dialog->Show(true);
}
}
@ -386,7 +392,6 @@ void CrashReportApp::OnInitCmdLine(wxCmdLineParser& parser)
{
static const wxCmdLineEntryDesc cmdLineEntryDesc[] =
{
{ wxCMD_LINE_SWITCH, "h", "help", "Display help on the command line parameters", wxCMD_LINE_VAL_NONE, wxCMD_LINE_OPTION_HELP },
{ wxCMD_LINE_SWITCH, "s", "silent", "Send without displaying the confirmation dialog" },
{ wxCMD_LINE_OPTION, "u", "url", "Crash report server URL", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL },
{ wxCMD_LINE_OPTION, "a", "args", "A set of arguments to send", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL },

View File

@ -45,7 +45,11 @@ if( ${_OPT}package_manual )
) \
")
install( DIRECTORY "${out_dir}/${host}/" DESTINATION "help/manual" )
if( "${CMAKE_GENERATOR}" MATCHES "Xcode" )
install( DIRECTORY "${out_dir}/${host}/" DESTINATION "${_APPDIR}/help/manual" )
elseif( "${CMAKE_GENERATOR}" MATCHES "Visual Studio*" )
install( DIRECTORY "${out_dir}/${host}/" DESTINATION "help/manual" )
endif()
endif()
if( NOT CMAKE_SYSTEM_NAME MATCHES "Darwin" )

View File

@ -4822,7 +4822,7 @@ msgstr "Herzlich Willkommen zu Audacity-Version %s"
#: src/ProjectManager.cpp
#, c-format
msgid "%sSave changes to %s?"
msgstr "%sÄnderungen speichern nach %s?"
msgstr "%sÄnderungen an %s speichern?"
#: src/ProjectManager.cpp
msgid "Save project before closing?"

View File

@ -22,17 +22,22 @@ fi
cmake --build build -j "${cpus}" --config "${AUDACITY_BUILD_TYPE}"
BIN_OUTPUT_DIR=build/bin/${AUDACITY_BUILD_TYPE}
SHARED_OUTPUT_DIR=build/shared/${AUDACITY_BUILD_TYPE}
SYMBOLS_OUTPUT_DIR=debug
mkdir ${SYMBOLS_OUTPUT_DIR}
if [[ "${OSTYPE}" == msys* ]]; then # Windows
# copy PDBs to debug folder...
find ${BIN_OUTPUT_DIR} -name '*.pdb' | xargs -I % cp % ${SYMBOLS_OUTPUT_DIR}
find ${BIN_OUTPUT_DIR} -name '*.pdb' | xargs -I % cp -v % ${SYMBOLS_OUTPUT_DIR}
find ${SHARED_OUTPUT_DIR} -name '*.pdb' | xargs -I % cp -v % ${SYMBOLS_OUTPUT_DIR}
find $(cygpath ${CONAN_USER_HOME}) -name '*.pdb' | xargs -I % cp -v % ${SYMBOLS_OUTPUT_DIR}
# and remove debug symbol files from the file tree before archieving
find ${BIN_OUTPUT_DIR} -name '*.iobj' -o -name '*.ipdb' -o -name '*.pdb' -o -name '*.ilk' | xargs rm -f
elif [[ "${OSTYPE}" == darwin* ]]; then # macOS
find ${BIN_OUTPUT_DIR} -name '*.dSYM' | xargs -J % mv % ${SYMBOLS_OUTPUT_DIR}
find ${SHARED_OUTPUT_DIR} -name '*.dSYM' | xargs -J % mv % ${SYMBOLS_OUTPUT_DIR}
find ${CONAN_USER_HOME}/dsyms -name '*.dSYM' | xargs -J % cp -R % ${SYMBOLS_OUTPUT_DIR}
else # Linux & others
chmod +x scripts/ci/linux/split_debug_symbols.sh
find ${BIN_OUTPUT_DIR} -type f -executable -o -name '*.so' | xargs -n 1 scripts/ci/linux/split_debug_symbols.sh

View File

@ -58,11 +58,32 @@ fi
if [[ ${GIT_BRANCH} == release* ]]; then
cmake_args+=(
-D audacity_package_manual=yes
-D AUDACITY_BUILD_LEVEL=2
)
fi
# Configure Audacity
cmake "${cmake_args[@]}"
if [[ "${OSTYPE}" == msys* ]]; then # Windows
# On Windows, preserve PDB files before clearing the build cache
conanUnixPath=$(cygpath ${CONAN_USER_HOME})
pdbOutputPath="${conanUnixPath}/pdbs"
ls -la ${conanUnixPath}
mkdir -p "${pdbOutputPath}"
find "${conanUnixPath}/.conan" -name '*.pdb' '!' -name "vc14?.pdb" -type f | xargs -I % cp -v % ${pdbOutputPath}
elif [[ "${OSTYPE}" == darwin* ]]; then # macOS
# On macOS - find all the .dylib files and generate dSYMs from them
# in the same folder.
# dsymutil requires *.o files, so we need to generate files before clearing
# the build directories.
chmod +x scripts/ci/macos/generate_dsym.sh
scripts/ci/macos/generate_dsym.sh
fi
# Remove build directories and sources to reduce the cache size.
conan remove "*" --src --builds --force

View File

@ -0,0 +1,25 @@
#!/usr/bin/env bash
set -xe
function extractDSym()
{
local lib=$1
local libfile=$(basename $lib)
local libname="${libfile%.*}"
local targetdir=$2
local dsymfile=${targetdir}/${libname}.dSYM
if [[ -d "$dsymfile" ]]; then
echo "Skipping dSYM generation for $libfile: dSYM exists"
elif [[ ! -L "$lib" && "$lib" != "{}" && $lib != *"dSYM"* ]]; then
echo "Extracting dSYMs from $libfile to $dsymfile"
dsymutil "$lib" -o "$dsymfile"
fi
}
export -f extractDSym
mkdir -p "$CONAN_USER_HOME/dsyms"
find $CONAN_USER_HOME -name "*.dylib" | xargs -I {} bash -c "extractDSym \"{}\" \"$CONAN_USER_HOME/dsyms\""

View File

@ -6,10 +6,10 @@ set -euxo pipefail
cd build
cpack -C "${AUDACITY_BUILD_TYPE}" --verbose
if [[ "${OSTYPE}" == msys* && ${GIT_BRANCH} == release* ]]; then # Windows
cmake --build . --target innosetup --config "${AUDACITY_BUILD_TYPE}"
else
cpack -C "${AUDACITY_BUILD_TYPE}" --verbose
fi
# Remove the temporary directory

View File

@ -11,6 +11,6 @@ curl -sL https://sentry.io/get-cli/ | bash
SYMBOLS=$(find debug | xargs)
${INSTALL_DIR}/sentry-cli --auth-token ${SENTRY_AUTH_TOKEN} --url ${SENTRY_HOST} upload-dif \
${INSTALL_DIR}/sentry-cli --auth-token ${SENTRY_AUTH_TOKEN} --url https://${SENTRY_HOST} upload-dif \
--org ${SENTRY_ORG_SLUG} \
--project ${SENTRY_PROJECT_SLUG} ${SYMBOLS}

View File

@ -714,7 +714,7 @@ void AColor::PreComputeGradient() {
gradient_pre[selected][1][i][2] = (unsigned char) (255 * b);
}
// colorScheme 2: Grayscale
// colorScheme 3: Inverse Grayscale
for (int i = 0; i < gradientSteps; i++) {
float r, g, b;
float value = float(i) / gradientSteps;
@ -743,12 +743,12 @@ void AColor::PreComputeGradient() {
b = 1.0f;
break;
}
gradient_pre[selected][2][i][0] = (unsigned char)(255 * r);
gradient_pre[selected][2][i][1] = (unsigned char)(255 * g);
gradient_pre[selected][2][i][2] = (unsigned char)(255 * b);
gradient_pre[selected][3][i][0] = (unsigned char)(255 * r);
gradient_pre[selected][3][i][1] = (unsigned char)(255 * g);
gradient_pre[selected][3][i][2] = (unsigned char)(255 * b);
}
// colorScheme 3: Inv. Grayscale (=Old grayscale)
// colorScheme 2: Grayscale (=Old grayscale)
for (int i = 0; i<gradientSteps; i++) {
float r, g, b;
float value = float(i)/gradientSteps;
@ -781,9 +781,9 @@ void AColor::PreComputeGradient() {
b = 1.0f;
break;
}
gradient_pre[selected][3][i][0] = (unsigned char) (255 * r);
gradient_pre[selected][3][i][1] = (unsigned char) (255 * g);
gradient_pre[selected][3][i][2] = (unsigned char) (255 * b);
gradient_pre[selected][2][i][0] = (unsigned char) (255 * r);
gradient_pre[selected][2][i][1] = (unsigned char) (255 * g);
gradient_pre[selected][2][i][2] = (unsigned char) (255 * b);
}
}
}

View File

@ -74,6 +74,10 @@ hold information about one contributor to Audacity.
#define REV_IDENT (XO("No revision identifier was provided").Translation())
#endif
#if defined(HAS_SENTRY_REPORTING) || defined(HAVE_UPDATES_CHECK) || defined(USE_BREAKPAD)
#define HAS_PRIVACY_POLICY
#endif
// To substitute into many other translatable strings
static const auto ProgramName =
//XO("Audacity");
@ -129,7 +133,11 @@ void AboutDialog::CreateCreditsList()
// The Audacity Team: developers and support
AddCredit(wxT("Anton Gerasimov"), developerFormat, roleTeamMember);
AddCredit(wxT("Jouni Helminen"), roleTeamMember);
AddCredit(wxT("Peter Jonas"), developerFormat, roleTeamMember);
AddCredit(wxT("Martin Keary"), roleTeamMember);
AddCredit(wxT("Paul Licameli"), developerFormat, roleTeamMember);
AddCredit(wxT("Anita Sudan"), roleTeamMember);
AddCredit(wxT("Vitaly Sverchinsky"), developerFormat, roleTeamMember);
AddCredit(wxT("Dmitry Vedenko"), developerFormat, roleTeamMember);
@ -825,7 +833,11 @@ void AboutDialog::PopulateInformationPage( ShuttleGui & S )
void AboutDialog::PopulateLicensePage( ShuttleGui & S )
{
S.StartNotebookPage( XO("GPL License") );
#if defined(HAS_PRIVACY_POLICY)
S.StartNotebookPage(XC("Legal", "about dialog"));
#else
S.StartNotebookPage(XO("GPL License"));
#endif
S.StartVerticalLay(1);
HtmlWindow *html = safenew LinkingHtmlWindow(S.GetParent(), -1,
wxDefaultPosition,
@ -838,7 +850,9 @@ void AboutDialog::PopulateLicensePage( ShuttleGui & S )
// better proportionally spaced.
//
// The GPL is not to be translated....
wxString PageText= FormatHtmlText(
constexpr auto GPL_TEXT =
wxT(" <center>GNU GENERAL PUBLIC LICENSE\n</center>")
wxT(" <center>Version 2, June 1991\n</center>")
wxT("<p><p>")
@ -1120,7 +1134,37 @@ wxT("OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\n
wxT("TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\n")
wxT("YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\n")
wxT("PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\n")
wxT("POSSIBILITY OF SUCH DAMAGES.\n"));
wxT("POSSIBILITY OF SUCH DAMAGES.\n");
#if defined(HAS_PRIVACY_POLICY)
/* i18n-hint: For "About Audacity...": Title for Privacy Policy section */
const auto privacyPolicyTitle = XC("PRIVACY POLICY", "about dialog");
/* i18n-hint: For "About Audacity...": Title of hyperlink to the privacy policy. This is an object of "See". */
const auto privacyPolicyURLText = XO("our Privacy Policy");
/* i18n-hint: %s will be replaced with "our Privacy Policy" */
const auto privacyPolicyText = XO(
"App update checking and error reporting require network access. "
"These features are optional. See %s "
"for more information.")
.Format(
Verbatim(
"[[https://www.audacityteam.org/about/desktop-privacy-notice/|%s]]")
.Format(privacyPolicyURLText));
const wxString privacyPolicyHTML = wxString(
wxT("<center>") +
privacyPolicyTitle.Translation() +
wxT("</center><p>") +
privacyPolicyText.Translation() +
wxT("</p><br/><br/>")
);
wxString PageText = FormatHtmlText(privacyPolicyHTML + GPL_TEXT);
#else
wxString PageText = FormatHtmlText(GPL_TEXT);
#endif
html->SetPage( PageText );

View File

@ -241,7 +241,7 @@ void PopulatePreferences()
wxYES_NO, NULL);
if (action == wxYES) // reset
{
gPrefs->DeleteAll();
ResetPreferences();
writeLang = true;
}
}

View File

@ -1823,6 +1823,44 @@ int AudioIO::StartStream(const TransportTracks &tracks,
return mStreamToken;
}
void AudioIO::DelayActions(bool recording)
{
mDelayingActions = recording;
}
bool AudioIO::DelayingActions() const
{
return mDelayingActions || (mPortStreamV19 && mNumCaptureChannels > 0);
}
void AudioIO::CallAfterRecording(PostRecordingAction action)
{
if (!action)
return;
{
std::lock_guard<std::mutex> guard{ mPostRecordingActionMutex };
if (mPostRecordingAction) {
// Enqueue it, even if perhaps not still recording,
// but it wasn't cleared yet
mPostRecordingAction = [
prevAction = std::move(mPostRecordingAction),
nextAction = std::move(action)
]{ prevAction(); nextAction(); };
return;
}
else if (DelayingActions()) {
mPostRecordingAction = std::move(action);
return;
}
}
// Don't delay it except until idle time.
// (Recording might start between now and then, but won't go far before
// the action is done. So the system isn't bulletproof yet.)
wxTheApp->CallAfter(std::move(action));
}
bool AudioIO::AllocateBuffers(
const AudioIOStartStreamOptions &options,
const TransportTracks &tracks, double t0, double t1, double sampleRate,
@ -2416,6 +2454,21 @@ void AudioIO::StopStream()
if (pListener && mNumCaptureChannels > 0)
pListener->OnAudioIOStopRecording();
wxTheApp->CallAfter([this]{
if (mPortStreamV19 && mNumCaptureChannels > 0)
// Recording was restarted between StopStream and idle time
// So the actions can keep waiting
return;
// In case some other thread was waiting on the mutex too:
std::this_thread::yield();
std::lock_guard<std::mutex> guard{ mPostRecordingActionMutex };
if (mPostRecordingAction) {
mPostRecordingAction();
mPostRecordingAction = {};
}
DelayActions(false);
});
//
// Only set token to 0 after we're totally finished with everything
//

View File

@ -18,9 +18,9 @@
#include "AudioIOBase.h" // to inherit
#include "PlaybackSchedule.h" // member variable
#include <functional>
#include <memory>
#include <mutex>
#include <utility>
#include <wx/atomic.h> // member variable
@ -621,6 +621,12 @@ public:
* by the specified amount from where it is now */
void SeekStream(double seconds) { mSeek = seconds; }
using PostRecordingAction = std::function<void()>;
//! Enqueue action for main thread idle time, not before the end of any recording in progress
/*! This may be called from non-main threads */
void CallAfterRecording(PostRecordingAction action);
#ifdef EXPERIMENTAL_SCRUBBING_SUPPORT
bool IsScrubbing() const { return IsBusy() && mScrubState != 0; }
@ -733,15 +739,18 @@ public:
static void Init();
static void Deinit();
/*! For purposes of CallAfterRecording, treat time from now as if
recording (when argument is true) or not necessarily so (false) */
void DelayActions(bool recording);
private:
bool DelayingActions() const;
/** \brief Set the current VU meters - this should be done once after
* each call to StartStream currently */
void SetMeters();
/** \brief Opens the portaudio stream(s) used to do playback or recording
* (or both) through.
*
@ -787,6 +796,10 @@ private:
*
* If bOnlyBuffers is specified, it only cleans up the buffers. */
void StartStreamCleanup(bool bOnlyBuffers = false);
std::mutex mPostRecordingActionMutex;
PostRecordingAction mPostRecordingAction;
bool mDelayingActions{ false };
};
static constexpr unsigned ScrubPollInterval_ms = 50;

View File

@ -1055,7 +1055,7 @@ list( APPEND DEFINES
__STDC_CONSTANT_MACROS
STRICT
>
$<$<BOOL:${USE_UPDATE_CHECK}>:
$<$<BOOL:${${_OPT}has_updates_check}>:
HAVE_UPDATES_CHECK
>
)
@ -1375,6 +1375,7 @@ if( "${CMAKE_GENERATOR}" MATCHES "Xcode|Visual Studio*" )
USE_SOURCE_PERMISSIONS
PATTERN "*.pdb" EXCLUDE
PATTERN "*.ilk" EXCLUDE
PATTERN "*.dSYM" EXCLUDE
)
else()
if( CMAKE_SYSTEM_NAME MATCHES "Darwin" )

View File

@ -886,6 +886,9 @@ void PluginManager::Load()
if (!registry.HasGroup(REGROOT))
{
// Must start over
// This DeleteAll affects pluginregistry.cfg only, not audacity.cfg
// That is, the memory of on/off states of effect (and generator,
// analyzer, and tool) plug-ins
registry.DeleteAll();
registry.Flush();
return;
@ -1223,7 +1226,7 @@ void PluginManager::Save()
{}, {}, FileNames::PluginRegistry());
auto &registry = *pRegistry;
// Clear it out
// Clear pluginregistry.cfg (not audacity.cfg)
registry.DeleteAll();
// Write the version string

View File

@ -63,6 +63,9 @@
#include "MemoryX.h"
#include <memory>
BoolSetting DefaultUpdatesCheckingFlag{
L"/Update/DefaultUpdatesChecking", true };
std::unique_ptr<FileConfig> ugPrefs {};
FileConfig *gPrefs = nullptr;
@ -179,6 +182,25 @@ void InitPreferences( std::unique_ptr<FileConfig> uPrefs )
wxConfigBase::Set(gPrefs);
}
void ResetPreferences()
{
// Future: make this a static registry table, so the settings objects
// don't need to be defined in this source code file to avoid dependency
// cycles
std::pair<BoolSetting &, bool> stickyBoolSettings[] {
{DefaultUpdatesCheckingFlag, 0},
// ... others?
};
for (auto &pair : stickyBoolSettings)
pair.second = pair.first.Read();
bool savedValue = DefaultUpdatesCheckingFlag.Read();
gPrefs->DeleteAll();
for (auto &pair : stickyBoolSettings)
pair.first.Write(pair.second);
}
void FinishPreferences()
{
if (gPrefs) {

View File

@ -48,6 +48,12 @@
class wxFileName;
void InitPreferences( std::unique_ptr<FileConfig> uPrefs );
//! Call this to reset preferences to an (almost)-"new" default state
/*!
There is at least one exception to that: user preferences we want to make
more "sticky." Notably, whether automatic update checking is preferred.
*/
void ResetPreferences();
void FinishPreferences();
extern AUDACITY_DLL_API FileConfig *gPrefs;
@ -423,4 +429,7 @@ struct AUDACITY_DLL_API PreferenceInitializer {
static void ReinitializeAll();
};
// Special extra-sticky settings
extern AUDACITY_DLL_API BoolSetting DefaultUpdatesCheckingFlag;
#endif

View File

@ -40,6 +40,7 @@
#include <wx/timer.h>
#include <wx/dynlib.h> //<! For windows.h
#include "AudioIO.h"
#include "ShuttleGui.h"
#include "ProjectAudioManager.h"
#include "ProjectFileIO.h"
@ -481,58 +482,64 @@ int TimerRecordDialog::RunWaitDialog()
{
auto updateResult = ProgressResult::Success;
if (m_DateTime_Start > wxDateTime::UNow())
updateResult = this->WaitForStart();
const auto gAudioIO = AudioIO::Get();
gAudioIO->DelayActions(true);
{
auto cleanup = finally([gAudioIO]{ gAudioIO->DelayActions(false); });
if (updateResult != ProgressResult::Success) {
// Don't proceed, but don't treat it as canceled recording. User just canceled waiting.
return POST_TIMER_RECORD_CANCEL_WAIT;
} else {
// Record for specified time.
ProjectAudioManager::Get( mProject ).OnRecord(false);
bool bIsRecording = true;
if (m_DateTime_Start > wxDateTime::UNow())
updateResult = this->WaitForStart();
auto sPostAction = Verbatim(
m_pTimerAfterCompleteChoiceCtrl->GetStringSelection() );
if (updateResult != ProgressResult::Success) {
// Don't proceed, but don't treat it as canceled recording. User just canceled waiting.
return POST_TIMER_RECORD_CANCEL_WAIT;
} else {
// Record for specified time.
ProjectAudioManager::Get( mProject ).OnRecord(false);
bool bIsRecording = true;
// Two column layout.
TimerProgressDialog::MessageTable columns{
{
XO("Recording start:") ,
XO("Duration:") ,
XO("Recording end:") ,
{} ,
XO("Automatic Save enabled:") ,
XO("Automatic Export enabled:") ,
XO("Action after Timer Recording:") ,
},
{
GetDisplayDate(m_DateTime_Start) ,
Verbatim( m_TimeSpan_Duration.Format() ),
GetDisplayDate(m_DateTime_End) ,
{} ,
(m_bAutoSaveEnabled ? XO("Yes") : XO("No")) ,
(m_bAutoExportEnabled ? XO("Yes") : XO("No")) ,
sPostAction ,
auto sPostAction = Verbatim(
m_pTimerAfterCompleteChoiceCtrl->GetStringSelection() );
// Two column layout.
TimerProgressDialog::MessageTable columns{
{
XO("Recording start:") ,
XO("Duration:") ,
XO("Recording end:") ,
{} ,
XO("Automatic Save enabled:") ,
XO("Automatic Export enabled:") ,
XO("Action after Timer Recording:") ,
},
{
GetDisplayDate(m_DateTime_Start) ,
Verbatim( m_TimeSpan_Duration.Format() ),
GetDisplayDate(m_DateTime_End) ,
{} ,
(m_bAutoSaveEnabled ? XO("Yes") : XO("No")) ,
(m_bAutoExportEnabled ? XO("Yes") : XO("No")) ,
sPostAction ,
}
};
TimerProgressDialog
progress(m_TimeSpan_Duration.GetMilliseconds().GetValue(),
XO("Audacity Timer Record Progress"),
columns,
pdlgHideCancelButton | pdlgConfirmStopCancel);
// Make sure that start and end time are updated, so we always get the full
// duration, even if there's some delay getting here.
wxTimerEvent dummyTimerEvent;
this->OnTimer(dummyTimerEvent);
// Loop for progress display during recording.
while (bIsRecording && (updateResult == ProgressResult::Success)) {
updateResult = progress.UpdateProgress();
wxMilliSleep(kTimerInterval);
bIsRecording = (wxDateTime::UNow() <= m_DateTime_End); // Call UNow() again for extra accuracy...
}
};
TimerProgressDialog
progress(m_TimeSpan_Duration.GetMilliseconds().GetValue(),
XO("Audacity Timer Record Progress"),
columns,
pdlgHideCancelButton | pdlgConfirmStopCancel);
// Make sure that start and end time are updated, so we always get the full
// duration, even if there's some delay getting here.
wxTimerEvent dummyTimerEvent;
this->OnTimer(dummyTimerEvent);
// Loop for progress display during recording.
while (bIsRecording && (updateResult == ProgressResult::Success)) {
updateResult = progress.UpdateProgress();
wxMilliSleep(kTimerInterval);
bIsRecording = (wxDateTime::UNow() <= m_DateTime_End); // Call UNow() again for extra accuracy...
}
}

View File

@ -648,6 +648,7 @@ wxString EffectManager::GetPreset(const PluginID & ID, const wxString & params,
return preset;
}
// This cleans a config "file" backed by a string in memory.
eap.DeleteAll();
eap.Write(wxT("Use Preset"), preset);

View File

@ -106,17 +106,40 @@
return intgr ;
}
#elif (defined (WIN32) || defined (_WIN32)) && defined(_M_3X64)
#elif (defined (WIN32) || defined (_WIN32)) && defined(_M_X64)
#include <math.h>
#include <immintrin.h>
#include <emmintrin.h>
__inline long int
lrintf (float flt)
#ifdef _MSC_VER
#pragma function(lrint, lrintf)
#endif
__inline
long int lrint(double flt)
{
return _mm_cvt_ss2si(_mm_set_ss(flt));
return _mm_cvtsd_si32(_mm_set_sd(flt));
}
__inline
long int lrintf (float flt)
{
return _mm_cvtss_si32(_mm_set_ss(flt));
}
__inline
long long int llrint(double flt)
{
return _mm_cvtsd_si64(_mm_set_sd(flt));
}
__inline
long long int llrintf(float flt)
{
return _mm_cvtss_si64(_mm_set_ss(flt));
}
#elif (HAVE_LRINT && HAVE_LRINTF)
/* These defines enable functionality introduced with the 1999 ISO C
@ -146,6 +169,6 @@
#include <math.h>
#define lrint(dbl) ((int)rint(dbl))
#define lrintf(flt) ((int)rint(flt))
#define lrintf(flt) ((int)rint(flt))
#endif

View File

@ -393,7 +393,7 @@ void OnResetConfig(const CommandContext &context)
menuManager.mLastAnalyzer = "";
menuManager.mLastTool = "";
gPrefs->DeleteAll();
ResetPreferences();
// Directory will be reset on next restart.
FileNames::UpdateDefaultPath(FileNames::Operation::Temp, TempDirectory::DefaultTempDir());

View File

@ -70,8 +70,7 @@ void ApplicationPrefs::PopulateOrExchange(ShuttleGui & S)
S.StartStatic(XO("Update Audacity"));
{
S.TieCheckBox(XO("&Check for Updates"),
UpdatesCheckingSettings::DefaultUpdatesCheckingFlag);
S.TieCheckBox(XO("&Check for Updates"), DefaultUpdatesCheckingFlag);
}
S.EndStatic();
S.EndScroller();

View File

@ -182,7 +182,7 @@ void SpectrogramSettings::ColorSchemeEnumSetting::Migrate(wxString &value)
// Migrate old grayscale option to Color scheme choice
bool isGrayscale = (gPrefs->Read(wxT("/Spectrum/Grayscale"), 0L) != 0);
if (isGrayscale && !gPrefs->Read(wxT("/Spectrum/ColorScheme"), &value)) {
value = GetColorSchemeNames().at(csInvGrayscale).Internal();
value = GetColorSchemeNames().at(csGrayscale).Internal();
Write(value);
gPrefs->Flush();
}

View File

@ -56,23 +56,41 @@ bool UpdateDataParser::HandleXMLTag(const wxChar* tag, const wxChar** attrs)
return true;
}
if (wxStrcmp(tag, mXmlTagNames[XmlParsedTags::kWindowsTag]) == 0)
const wxPlatformInfo& info = wxPlatformInfo::Get();
constexpr bool is32Bit = sizeof(void*) == 4;
constexpr bool is64Bit = sizeof(void*) == 8;
if (is32Bit)
{
if (wxPlatformInfo::Get().GetOperatingSystemId() & wxOS_WINDOWS)
mXmlParsingState = XmlParsedTags::kOsTag;
return true;
if (wxStrcmp(tag, mXmlTagNames[XmlParsedTags::kWin32Tag]) == 0)
{
if (info.GetOperatingSystemId() & wxOS_WINDOWS)
mXmlParsingState = XmlParsedTags::kOsTag;
return true;
}
}
if (is64Bit)
{
if (wxStrcmp(tag, mXmlTagNames[XmlParsedTags::kWin64Tag]) == 0)
{
if (info.GetOperatingSystemId() & wxOS_WINDOWS)
mXmlParsingState = XmlParsedTags::kOsTag;
return true;
}
}
if (wxStrcmp(tag, mXmlTagNames[XmlParsedTags::kMacosTag]) == 0)
{
if (wxPlatformInfo::Get().GetOperatingSystemId() & wxOS_MAC)
if (info.GetOperatingSystemId() & wxOS_MAC)
mXmlParsingState = XmlParsedTags::kOsTag;
return true;
}
if (wxStrcmp(tag, mXmlTagNames[XmlParsedTags::kLinuxTag]) == 0)
{
if (wxPlatformInfo::Get().GetOperatingSystemId() & wxOS_UNIX_LINUX)
if (info.GetOperatingSystemId() & wxOS_UNIX_LINUX)
mXmlParsingState = XmlParsedTags::kOsTag;
return true;
}

View File

@ -36,7 +36,8 @@ private:
kUpdateTag,
kDescriptionTag,
kOsTag,
kWindowsTag,
kWin32Tag,
kWin64Tag,
kMacosTag,
kLinuxTag,
kVersionTag,
@ -48,7 +49,8 @@ private:
{ XmlParsedTags::kUpdateTag, wxT("Updates") },
{ XmlParsedTags::kDescriptionTag, wxT("Description") },
{ XmlParsedTags::kOsTag, wxT("OS") },
{ XmlParsedTags::kWindowsTag, wxT("Windows") },
{ XmlParsedTags::kWin32Tag, wxT("Win32") },
{ XmlParsedTags::kWin64Tag, wxT("Win64") },
{ XmlParsedTags::kMacosTag, wxT("Macos") },
{ XmlParsedTags::kLinuxTag, wxT("Linux") },
{ XmlParsedTags::kVersionTag, wxT("Version") },

View File

@ -10,6 +10,7 @@
#include "UpdateManager.h"
#include "UpdatePopupDialog.h"
#include "AudioIO.h"
#include "NetworkManager.h"
#include "IResponse.h"
#include "Request.h"
@ -21,26 +22,23 @@
#include <wx/frame.h>
#include <mutex>
BoolSetting UpdatesCheckingSettings::DefaultUpdatesCheckingFlag{
L"/Update/DefaultUpdatesChecking", true };
#include <cstdint>
static const char* prefsUpdateScheduledTime = "/Update/UpdateScheduledTime";
using Clock = std::chrono::system_clock;
using TimePoint = Clock::time_point;
using Duration = TimePoint::duration;
constexpr Duration updatesCheckInterval = std::chrono::hours(12);
enum { ID_TIMER = wxID_HIGHEST + 1 };
BEGIN_EVENT_TABLE(UpdateManager, wxEvtHandler)
EVT_TIMER(ID_TIMER, UpdateManager::OnTimer)
END_EVENT_TABLE()
UpdateManager::UpdateManager()
: mUpdateCheckingInterval(
std::chrono::milliseconds(std::chrono::hours(12)).count())
{}
UpdateManager::~UpdateManager()
{}
UpdateManager& UpdateManager::GetInstance()
{
static UpdateManager updateManager;
@ -64,38 +62,48 @@ VersionPatch UpdateManager::GetVersionPatch() const
return mVersionPatch;
}
void UpdateManager::GetUpdates()
void UpdateManager::GetUpdates(bool ignoreNetworkErrors)
{
const audacity::network_manager::Request request("https://updates.audacityteam.org/feed/latest.xml");
auto response = audacity::network_manager::NetworkManager::GetInstance().doGet(request);
response->setRequestFinishedCallback([response, this](audacity::network_manager::IResponse*) {
response->setRequestFinishedCallback([response, ignoreNetworkErrors, this](audacity::network_manager::IResponse*) {
auto gAudioIO = AudioIO::Get();
if (response->getError() != audacity::network_manager::NetworkError::NoError)
{
wxTheApp->CallAfter([] {ShowExceptionDialog(nullptr,
XC("Error checking for update", "update dialog"),
XC("Unable to connect to Audacity update server.", "update dialog"),
wxString());
});
return;
if (!ignoreNetworkErrors)
{
gAudioIO->CallAfterRecording([] {
ShowExceptionDialog(
nullptr, XC("Error checking for update", "update dialog"),
XC("Unable to connect to Audacity update server.",
"update dialog"),
wxString());
});
}
return;
}
if (!mUpdateDataParser.Parse(response->readAll<VersionPatch::UpdateDataFormat>(), &mVersionPatch))
{
wxTheApp->CallAfter([] {ShowExceptionDialog(nullptr,
XC("Error checking for update", "update dialog"),
XC("Update data was corrupted.", "update dialog"),
wxString());
});
return;
if (!ignoreNetworkErrors)
{
gAudioIO->CallAfterRecording([] {
ShowExceptionDialog(
nullptr, XC("Error checking for update", "update dialog"),
XC("Update data was corrupted.", "update dialog"),
wxString());
});
}
return;
}
if (mVersionPatch.version > CurrentBuildVersion())
{
wxTheApp->CallAfter([this] {
gAudioIO->CallAfterRecording([this] {
UpdatePopupDialog dlg(nullptr, mVersionPatch);
const int code = dlg.ShowModal();
@ -109,41 +117,56 @@ void UpdateManager::GetUpdates()
wxString());
}
}
});
});
}
});
});
}
void UpdateManager::OnTimer(wxTimerEvent& WXUNUSED(event))
{
bool updatesCheckingEnabled = UpdatesCheckingSettings::DefaultUpdatesCheckingFlag.Read();
bool updatesCheckingEnabled = DefaultUpdatesCheckingFlag.Read();
if (updatesCheckingEnabled && IsTimeForUpdatesChecking())
GetUpdates();
GetUpdates(true);
mTimer.StartOnce(mUpdateCheckingInterval);
mTimer.StartOnce(std::chrono::duration_cast<std::chrono::milliseconds>(
updatesCheckInterval)
.count());
}
bool UpdateManager::IsTimeForUpdatesChecking()
{
long long nextUpdatesCheckingTime = std::stoll(
gPrefs->Read(prefsUpdateScheduledTime, "0").ToStdString());
// We use atoll here, so there is no need to handle the exception,
// if prefsUpdateScheduledTime is corrupted.
// atoll will return 0 on failure, which suits us well.
const TimePoint nextUpdatesCheckingTime(std::chrono::milliseconds(
atoll(gPrefs->Read(prefsUpdateScheduledTime, "0").c_str())));
// Get current time in milliseconds
auto now_ms = std::chrono::time_point_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now());
auto currentTimeInMillisec = std::chrono::duration_cast<std::chrono::milliseconds>(
now_ms.time_since_epoch()).count();
// Get current time
const TimePoint currentTime = Clock::now();
// If next update time 0 or less then current time -> show update dialog,
// else this condition allow us to avoid from duplicating update notifications.
if (nextUpdatesCheckingTime < currentTimeInMillisec)
if (nextUpdatesCheckingTime < currentTime)
{
nextUpdatesCheckingTime = currentTimeInMillisec + mUpdateCheckingInterval;
gPrefs->Write(prefsUpdateScheduledTime,
wxString(std::to_string(nextUpdatesCheckingTime)));
// Round down the nextUpdatesChecking time to a day.
// This is required to ensure, that update is
// checked daily
using DayDuration =
std::chrono::duration<int32_t, std::ratio<60 * 60 * 24>>;
const auto postponeUpdateUntil =
std::chrono::time_point_cast<DayDuration>(
currentTime) + DayDuration(1);
const std::chrono::milliseconds postponeUpdateUntilMS(
postponeUpdateUntil.time_since_epoch());
gPrefs->Write(
prefsUpdateScheduledTime,
wxString(std::to_string(postponeUpdateUntilMS.count())));
gPrefs->Flush();
return true;

View File

@ -18,10 +18,6 @@
#include <wx/event.h>
#include <wx/timer.h>
namespace UpdatesCheckingSettings {
extern AUDACITY_DLL_API BoolSetting DefaultUpdatesCheckingFlag;
}
/// A class that managing of updates.
/**
Opt-in request and show update dialog by the scheduled time.
@ -31,13 +27,12 @@ namespace UpdatesCheckingSettings {
class UpdateManager final : public wxEvtHandler
{
public:
UpdateManager();
~UpdateManager();
UpdateManager() = default;
static UpdateManager& GetInstance();
static void Start();
void GetUpdates();
void GetUpdates(bool ignoreNetworkErrors);
VersionPatch GetVersionPatch() const;
@ -46,7 +41,6 @@ private:
VersionPatch mVersionPatch;
wxTimer mTimer;
const int mUpdateCheckingInterval;
void OnTimer(wxTimerEvent& event);

View File

@ -45,7 +45,7 @@ UpdatePopupDialog::UpdatePopupDialog (wxWindow* parent, const VersionPatch& vers
S.Id (DontShowID).AddCheckBox (
XO ("Don't show this again at start up"),
!UpdatesCheckingSettings::DefaultUpdatesCheckingFlag.Read());
!DefaultUpdatesCheckingFlag.Read());
S.Prop(1).AddSpace(1, 0, 1);
@ -80,7 +80,7 @@ void UpdatePopupDialog::OnSkip (wxCommandEvent&)
void UpdatePopupDialog::OnDontShow (wxCommandEvent& event)
{
UpdatesCheckingSettings::DefaultUpdatesCheckingFlag.Write(!event.IsChecked());
DefaultUpdatesCheckingFlag.Write(!event.IsChecked());
}
HtmlWindow* UpdatePopupDialog::AddHtmlContent (wxWindow* parent)

View File

@ -12,10 +12,10 @@
if( BUILDING_64_BIT )
set( INSTALLER_SUFFIX "x64" )
set( INSTALLER_X64_MODE "ArchitecturesInstallIn64BitMode=x64")
set( INSTALLER_X64_MODE "ArchitecturesInstallIn64BitMode=x64" )
else()
set( INSTALLER_SUFFIX "x86" )
set( INSTALLER_X64_MODE "ArchitecturesInstallIn64BitMode=x64")
set( INSTALLER_X64_MODE "" )
endif()
if( SIGN )

View File

@ -96,7 +96,7 @@ Source: ".\FirstTimeModel.ini"; DestDir: "{app}"; DestName: "FirstTime.ini"; Per
Source: "Additional\README.txt"; DestDir: "{app}"; Flags: ignoreversion
Source: "Additional\LICENSE.txt"; DestDir: "{app}"; Flags: ignoreversion
Source: "{#AppExe}"; DestDir: "{app}"; Flags: ignoreversion
Source: "Package\*.exe"; DestDir: "{app}"; Flags: ignoreversion
; Manual, which should be got from the manual wiki using ..\scripts\mw2html_audacity\wiki2htm.bat
@MANUAL@