diff --git a/lib-src/header-substitutes/allegro.h b/lib-src/header-substitutes/allegro.h new file mode 100644 index 000000000..d1667b4dc --- /dev/null +++ b/lib-src/header-substitutes/allegro.h @@ -0,0 +1,8 @@ +// -*- mode: c++ -*- +// Indirectly include Allegro header so that we can disable warnings about unused parameters +// when compiling Audacity itself. + +#pragma warning( push ) +#pragma warning( disable : 4100) +#include "../portsmf/allegro.h" +#pragma warning( pop ) diff --git a/lib-src/header-substitutes/sbsms.h b/lib-src/header-substitutes/sbsms.h new file mode 100644 index 000000000..814316624 --- /dev/null +++ b/lib-src/header-substitutes/sbsms.h @@ -0,0 +1,8 @@ +// -*- mode: c++ -*- +// Indirectly include SBSMS header so that we can disable warnings about unused parameters +// when compiling Audacity itself. + +#pragma warning( push ) +#pragma warning( disable : 4100) +#include "../sbsms/include/sbsms.h" +#pragma warning( pop ) diff --git a/src/AudacityApp.cpp b/src/AudacityApp.cpp index 677d5111f..21b41142f 100644 --- a/src/AudacityApp.cpp +++ b/src/AudacityApp.cpp @@ -1063,6 +1063,10 @@ void AudacityApp::OnFatalException() exit(-1); } + +#pragma warning( push ) +#pragma warning( disable : 4702) // unreachable code warning. + bool AudacityApp::OnExceptionInMainLoop() { // This function is invoked from catch blocks in the wxWidgets framework, @@ -1099,10 +1103,10 @@ bool AudacityApp::OnExceptionInMainLoop() // Let the inherited function do throw; again and whatever else it does. return wxApp::OnExceptionInMainLoop(); } - // Shouldn't ever reach this line return false; } +#pragma warning( pop ) #if defined(EXPERIMENTAL_CRASH_REPORT) void AudacityApp::GenerateCrashReport(wxDebugReport::Context ctx) diff --git a/src/AudioIO.h b/src/AudioIO.h index 40e997ed0..556c662c3 100644 --- a/src/AudioIO.h +++ b/src/AudioIO.h @@ -29,7 +29,7 @@ #include "../lib-src/portmidi/pm_common/portmidi.h" #include "../lib-src/portmidi/porttime/porttime.h" #include // Allegro include fails if this header isn't included do to no memcpy -#include "../lib-src/portsmf/allegro.h" +#include "../lib-src/header-substitutes/allegro.h" class NoteTrack; using NoteTrackArray = std::vector < NoteTrack* >; diff --git a/src/BlockFile.cpp b/src/BlockFile.cpp index 0493e78f8..b56329302 100644 --- a/src/BlockFile.cpp +++ b/src/BlockFile.cpp @@ -556,7 +556,7 @@ size_t BlockFile::CommonReadData( else { auto channels = info.channels; wxASSERT(channels >= 1); - wxASSERT(channel < channels); + wxASSERT(channel < (unsigned int)channels); if (channels == 1 && format == int16Sample && diff --git a/src/Envelope.cpp b/src/Envelope.cpp index 37676e138..5750422df 100644 --- a/src/Envelope.cpp +++ b/src/Envelope.cpp @@ -67,7 +67,7 @@ bool Envelope::ConsistencyCheck() for ( size_t ii = 0, count = mEnv.size(); ii < count; ) { // Find range of points with equal T const double thisT = mEnv[ii].GetT(); - double nextT; + double nextT = 0.0f; auto nextI = ii + 1; while ( nextI < count && thisT == ( nextT = mEnv[nextI].GetT() ) ) ++nextI; @@ -1080,7 +1080,7 @@ void Envelope::SetTrackLen( double trackLen, double sampleDur ) // Preserve the left-side limit at trackLen. auto range = EqualRange( trackLen, sampleDur ); bool needPoint = ( range.first == range.second && trackLen < mTrackLen ); - double value; + double value=0.0; if ( needPoint ) value = GetValueRelative( trackLen ); @@ -1344,7 +1344,7 @@ void Envelope::GetValues // Getting many envelope values, corresponding to pixel columns, which may // not be uniformly spaced in time when there is a fisheye. - double prevDiscreteTime, prevSampleVal, nextSampleVal; + double prevDiscreteTime=0.0, prevSampleVal=0.0, nextSampleVal=0.0; for ( int xx = 0; xx < bufferLen; ++xx ) { auto time = zoomInfo.PositionToTime( xx, -leftOffset ); if ( sampleDur <= 0 ) diff --git a/src/FFT.cpp b/src/FFT.cpp index 9b6123671..e84cc7682 100644 --- a/src/FFT.cpp +++ b/src/FFT.cpp @@ -357,8 +357,9 @@ const wxChar *WindowFuncName(int whichFunction) } } -void NewWindowFunc(int whichFunction, size_t NumSamples, bool extraSample, float *in) +void NewWindowFunc(int whichFunction, size_t NumSamplesIn, bool extraSample, float *in) { + int NumSamples = (int)NumSamplesIn; if (extraSample) { wxASSERT(NumSamples > 0); --NumSamples; @@ -533,7 +534,7 @@ void DerivativeOfWindowFunc(int whichFunction, size_t NumSamples, bool extraSamp wxASSERT(NumSamples > 0); --NumSamples; // in[0] *= 1.0f; - for (int ii = 1; ii < NumSamples; ++ii) + for (int ii = 1; ii < (int)NumSamples; ++ii) in[ii] = 0.0f; in[NumSamples] *= -1.0f; return; @@ -585,7 +586,7 @@ void DerivativeOfWindowFunc(int whichFunction, size_t NumSamples, bool extraSamp in[0] *= coeff0; if (!extraSample) --NumSamples; - for (int ii = 0; ii < NumSamples; ++ii) + for (int ii = 0; ii < (int)NumSamples; ++ii) in[ii] *= - coeff1 * sin(ii * multiplier); if (extraSample) in[NumSamples] *= - coeff0; @@ -599,7 +600,7 @@ void DerivativeOfWindowFunc(int whichFunction, size_t NumSamples, bool extraSamp // Hanning const double multiplier = 2 * M_PI / NumSamples; const double coeff1 = -0.5 * multiplier; - for (int ii = 0; ii < NumSamples; ++ii) + for (int ii = 0; ii < (int)NumSamples; ++ii) in[ii] *= - coeff1 * sin(ii * multiplier); if (extraSample) in[NumSamples] = 0.0f; @@ -611,7 +612,7 @@ void DerivativeOfWindowFunc(int whichFunction, size_t NumSamples, bool extraSamp const double multiplier = 2 * M_PI / NumSamples; const double multiplier2 = 2 * multiplier; const double coeff1 = -0.5 * multiplier, coeff2 = 0.08 * multiplier2; - for (int ii = 0; ii < NumSamples; ++ii) + for (int ii = 0; ii < (int)NumSamples; ++ii) in[ii] *= - coeff1 * sin(ii * multiplier) - coeff2 * sin(ii * multiplier2); if (extraSample) in[NumSamples] = 0.0f; @@ -625,7 +626,7 @@ void DerivativeOfWindowFunc(int whichFunction, size_t NumSamples, bool extraSamp const double multiplier3 = 3 * multiplier; const double coeff1 = -0.48829 * multiplier, coeff2 = 0.14128 * multiplier2, coeff3 = -0.01168 * multiplier3; - for (int ii = 0; ii < NumSamples; ++ii) + for (int ii = 0; ii < (int)NumSamples; ++ii) in[ii] *= - coeff1 * sin(ii * multiplier) - coeff2 * sin(ii * multiplier2) - coeff3 * sin(ii * multiplier3); if (extraSample) in[NumSamples] = 0.0f; @@ -636,7 +637,7 @@ void DerivativeOfWindowFunc(int whichFunction, size_t NumSamples, bool extraSamp // Welch const float N = NumSamples; const float NN = NumSamples * NumSamples; - for (int ii = 0; ii < NumSamples; ++ii) { + for (int ii = 0; ii < (int)NumSamples; ++ii) { in[ii] *= 4 * (N - ii - ii) / NN; } if (extraSample) @@ -668,7 +669,7 @@ void DerivativeOfWindowFunc(int whichFunction, size_t NumSamples, bool extraSamp in[0] *= exp(A * 0.25) * (1 - invN); if (!extraSample) --NumSamples; - for (int ii = 1; ii < NumSamples; ++ii) { + for (int ii = 1; ii < (int)NumSamples; ++ii) { const float iOverN = ii * invN; in[ii] *= exp(A * (0.25 + (iOverN * iOverN) - iOverN)) * (2 * ii * invNN - invN); } diff --git a/src/NoteTrack.h b/src/NoteTrack.h index 74570196c..70746b82e 100644 --- a/src/NoteTrack.h +++ b/src/NoteTrack.h @@ -20,7 +20,7 @@ #if defined(USE_MIDI) -#include "allegro.h" +#include "../lib-src/header-substitutes/allegro.h" // define this switch to play MIDI during redisplay to sonify run times // Note that if SONIFY is defined, the default MIDI device will be opened diff --git a/src/Sequence.cpp b/src/Sequence.cpp index 7d499da9c..b7847fb72 100644 --- a/src/Sequence.cpp +++ b/src/Sequence.cpp @@ -420,7 +420,7 @@ std::unique_ptr Sequence::Copy(sampleCount s0, sampleCount s1) const // Nonnegative result is length of block0 or less: blocklen = ( std::min(s1, block0.start + file->GetLength()) - s0 ).as_size_t(); - wxASSERT(file->IsAlias() || (blocklen <= mMaxSamples)); // Vaughan, 2012-02-29 + wxASSERT(file->IsAlias() || (blocklen <= (int)mMaxSamples)); // Vaughan, 2012-02-29 ensureSampleBufferSize(buffer, mSampleFormat, bufferSize, blocklen); Get(b0, buffer.ptr(), mSampleFormat, s0, blocklen, true); @@ -440,7 +440,7 @@ std::unique_ptr Sequence::Copy(sampleCount s0, sampleCount s1) const const auto &file = block.f; // s1 is within block: blocklen = (s1 - block.start).as_size_t(); - wxASSERT(file->IsAlias() || (blocklen <= mMaxSamples)); // Vaughan, 2012-02-29 + wxASSERT(file->IsAlias() || (blocklen <= (int)mMaxSamples)); // Vaughan, 2012-02-29 if (blocklen < (int)file->GetLength()) { ensureSampleBufferSize(buffer, mSampleFormat, bufferSize, blocklen); Get(b1, buffer.ptr(), mSampleFormat, block.start, blocklen, true); diff --git a/src/TrackArtist.cpp b/src/TrackArtist.cpp index 98fe6cef4..178379555 100644 --- a/src/TrackArtist.cpp +++ b/src/TrackArtist.cpp @@ -2581,7 +2581,7 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &waveTrackCache, } else { int specIndex = (xx - fisheyeLeft) * nBins; - wxASSERT(specIndex >= 0 && specIndex < specCache.freq.size()); + wxASSERT(specIndex >= 0 && specIndex < (int)specCache.freq.size()); uncached = &specCache.freq[specIndex]; } diff --git a/src/TrackPanelDrawingContext.h b/src/TrackPanelDrawingContext.h index 1fa28103d..e9f29bae4 100644 --- a/src/TrackPanelDrawingContext.h +++ b/src/TrackPanelDrawingContext.h @@ -21,20 +21,17 @@ class wxDC; // MSVC 2013 says this can't be instantiated - but in fact it can // using {} syntax. +// As it's a bogus warning caused by a bug in MSVC2013, it's Ok to disable it. +#pragma warning( push ) +#pragma warning( disable : 4510) +#pragma warning( disable : 4610) + struct TrackPanelDrawingContext { wxDC &dc; UIHandlePtr target; wxMouseState lastState; - - // MSVC 2013 has a bug and reports - // warning C4610: struct 'TrackPanelDrawingContext' - // can never be instantiated -#ifdef _MSC_VER - // Add a default initialiser here to workaround that? - //TrackPanelDrawingContext(); - -#endif - }; +#pragma warning( pop ) + #endif diff --git a/src/WaveClip.cpp b/src/WaveClip.cpp index 160ec9f2c..48a25cebe 100644 --- a/src/WaveClip.cpp +++ b/src/WaveClip.cpp @@ -126,19 +126,19 @@ public: //if they are both off the cache boundary in the same direction, the cache is missed, //so we are safe, and don't need to track this one. - if((invalStart<0 && invalEnd <0) || (invalStart>=len && invalEnd >= len)) + if((invalStart<0 && invalEnd <0) || (invalStart>=(long)len && invalEnd >= (long)len)) return; //in all other cases, we need to clip the boundries so they make sense with the cache. //for some reason, the cache is set up to access up to array[len], not array[len-1] if(invalStart <0) invalStart =0; - else if(invalStart > len) + else if((size_t)invalStart > len) invalStart = len; if(invalEnd <0) invalEnd =0; - else if(invalEnd > len) + else if((size_t)invalEnd > len) invalEnd = len; @@ -153,13 +153,13 @@ public: { //if the regions intersect OR are pixel adjacent InvalidRegion ®ion = mRegions[i]; - if(region.start <= invalEnd+1 - && region.end + 1 >= invalStart) + if(region.start <= (size_t)(invalEnd+1) + && (region.end + 1) >= (size_t)invalStart) { //take the union region - if(region.start > invalStart) + if(region.start > (size_t)invalStart) region.start = invalStart; - if(region.end < invalEnd) + if((int)region.end < invalEnd) region.end = invalEnd; added=true; break; @@ -206,7 +206,7 @@ public: } //if we are past the end of the region we added, we are past the area of regions that might be oversecting. - if(region.start > invalEnd) + if(invalEnd < 0 || region.start > (size_t)invalEnd) { break; } @@ -842,7 +842,7 @@ bool SpecCache::CalculateOneSpectrum from = sampleCount( where[0].as_double() + xx * (rate / pixelsPerSecond) ); - else if (xx > len) + else if ((size_t)xx > len) from = sampleCount( where[len].as_double() + (xx - len) * (rate / pixelsPerSecond) ); @@ -857,7 +857,7 @@ bool SpecCache::CalculateOneSpectrum auto nBins = settings.NBins(); if (from < 0 || from >= numSamples) { - if (xx >= 0 && xx < len) { + if (xx >= 0 && xx < (int)len) { // Pixel column is out of bounds of the clip! Should not happen. float *const results = &out[nBins * xx]; std::fill(results, results + nBins, 0.0f); @@ -1276,8 +1276,8 @@ bool WaveClip::GetSpectrogram(WaveTrackCache &waveTrackCache, // old cache doesn't match. It won't happen in resize, since the // spectrum view is pinned to left side of window. wxASSERT( - (copyBegin >= 0 && copyEnd == numPixels) || // copied the end - (copyBegin == 0 && copyEnd <= numPixels) // copied the beginning + (copyBegin >= 0 && copyEnd == (int)numPixels) || // copied the end + (copyBegin == 0 && copyEnd <= (int)numPixels) // copied the beginning ); int zeroBegin = copyBegin > 0 ? 0 : copyEnd-copyBegin; diff --git a/src/effects/ChangePitch.cpp b/src/effects/ChangePitch.cpp index c1a52f4b6..ff48c9e49 100644 --- a/src/effects/ChangePitch.cpp +++ b/src/effects/ChangePitch.cpp @@ -21,7 +21,6 @@ the pitch without changing the tempo. #include "ChangePitch.h" #if USE_SBSMS -#include "sbsms.h" #include #endif diff --git a/src/effects/ChangeTempo.cpp b/src/effects/ChangeTempo.cpp index 3b7a9df84..8b015baf2 100644 --- a/src/effects/ChangeTempo.cpp +++ b/src/effects/ChangeTempo.cpp @@ -20,7 +20,7 @@ #if USE_SOUNDTOUCH #if USE_SBSMS -#include "sbsms.h" +#include "../../../lib-src/header-substitutes/sbsms.h" #include #endif diff --git a/src/effects/Contrast.cpp b/src/effects/Contrast.cpp index e99733194..b48a46670 100644 --- a/src/effects/Contrast.cpp +++ b/src/effects/Contrast.cpp @@ -1,615 +1,618 @@ -/********************************************************************** - - Audacity: A Digital Audio Editor - - Contrast.cpp - -\class ContrastDialog -\brief Dialog used for Contrast menu item - -*//*******************************************************************/ - -#include "../Audacity.h" -#include "Contrast.h" - -#include "../WaveTrack.h" -#include "../Prefs.h" -#include "../Project.h" -#include "../ShuttleGui.h" -#include "../FileNames.h" -#include "../widgets/LinkingHtmlWindow.h" -#include "../widgets/HelpSystem.h" -#include "../widgets/NumericTextCtrl.h" -#include "../widgets/ErrorDialog.h" - -#include -#include - -#if defined(__WXMSW__) && !defined(__CYGWIN__) -#include -#define finite(x) _finite(x) -#endif - -#include -#include -#include - -#include "../PlatformCompatibility.h" - -#define DB_MAX_LIMIT 0.0 // Audio is massively distorted. -#define WCAG2_PASS 20.0 // dB difference required to pass WCAG2 test. - -bool ContrastDialog::GetDB(float &dB) -{ - float rms = float(0.0); - int numberSelecteTracks = 0; - - // For stereo tracks: sqrt((mean(L)+mean(R))/2) - bool isStereo = false; - double meanSq = 0.0; - - AudacityProject *p = GetActiveProject(); - SelectedTrackListOfKindIterator iter(Track::Wave, p->GetTracks()); - WaveTrack *t = (WaveTrack *) iter.First(); - while (t) { - numberSelecteTracks++; - if (numberSelecteTracks > 1 && !isStereo) { - AudacityMessageDialog m(NULL, _("You can only measure one track at a time."), _("Error"), wxOK); - m.ShowModal(); - return false; - } - isStereo = t->GetLinked(); - - wxASSERT(mT0 <= mT1); - - // Ignore whitespace beyond ends of track. - if(mT0 < t->GetStartTime()) - mT0 = t->GetStartTime(); - if(mT1 > t->GetEndTime()) - mT1 = t->GetEndTime(); - - auto SelT0 = t->TimeToLongSamples(mT0); - auto SelT1 = t->TimeToLongSamples(mT1); - - if(SelT0 > SelT1) - { - AudacityMessageDialog m(NULL, _("Invalid audio selection.\nPlease ensure that audio is selected."), _("Error"), wxOK); - m.ShowModal(); - return false; - } - - if(SelT0 == SelT1) - { - AudacityMessageDialog m(NULL, _("Nothing to measure.\nPlease select a section of a track."), _("Error"), wxOK); - m.ShowModal(); - return false; - } - - // Don't throw in this analysis dialog - rms = ((WaveTrack *)t)->GetRMS(mT0, mT1, false); - meanSq += rms * rms; - t = (WaveTrack *) iter.Next(); - } - // TODO: This works for stereo, provided the audio clips are in both channels. - // We should really count gaps between clips as silence. - rms = (meanSq > 0.0)? sqrt(meanSq/(double)numberSelecteTracks) : 0.0; - - if(numberSelecteTracks == 0) { - AudacityMessageDialog m(NULL, _("Please select an audio track."), _("Error"), wxOK); - m.ShowModal(); - return false; - } - - dB = (rms == 0.0)? -INFINITY : LINEAR_TO_DB(rms); - return true; -} - -void ContrastDialog::SetStartAndEndTime() -{ - AudacityProject *p = GetActiveProject(); - mT0 = p->mViewInfo.selectedRegion.t0(); - mT1 = p->mViewInfo.selectedRegion.t1(); -} - - -// WDR: class implementations - -//---------------------------------------------------------------------------- -// ContrastDialog -//---------------------------------------------------------------------------- - -// WDR: event table for ContrastDialog - -enum { - ID_BUTTON_USECURRENTF = 10001, - ID_BUTTON_USECURRENTB, - //ID_BUTTON_GETURL, - ID_BUTTON_EXPORT, - ID_BUTTON_RESET, - //ID_BUTTON_CLOSE, - ID_FOREGROUNDSTART_T, - ID_FOREGROUNDEND_T, - ID_BACKGROUNDSTART_T, - ID_BACKGROUNDEND_T, - ID_FOREGROUNDDB_TEXT, - ID_BACKGROUNDDB_TEXT, - ID_RESULTS_TEXT, - ID_RESULTSDB_TEXT -}; - -BEGIN_EVENT_TABLE(ContrastDialog,wxDialogWrapper) - EVT_BUTTON(ID_BUTTON_USECURRENTF, ContrastDialog::OnGetForeground) - EVT_BUTTON(ID_BUTTON_USECURRENTB, ContrastDialog::OnGetBackground) - EVT_BUTTON(wxID_HELP, ContrastDialog::OnGetURL) - EVT_BUTTON(ID_BUTTON_EXPORT, ContrastDialog::OnExport) - EVT_BUTTON(ID_BUTTON_RESET, ContrastDialog::OnReset) - EVT_BUTTON(wxID_CANCEL, ContrastDialog::OnClose) -END_EVENT_TABLE() - -/* i18n-hint: WCAG2 is the 'Web Content Accessibility Guidelines (WCAG) 2.0', see http://www.w3.org/TR/WCAG20/ */ -ContrastDialog::ContrastDialog(wxWindow * parent, wxWindowID id, - const wxString & title, - const wxPoint & pos): - wxDialogWrapper(parent, id, title, pos, wxDefaultSize, - wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMAXIMIZE_BOX ) -{ - SetName(GetTitle()); - - mT0 = 0.0; - mT1 = 0.0; - foregrounddB = 0.0; - backgrounddB = 0.0; - mForegroundIsDefined = false; - mBackgroundIsDefined = false; - - // NULL out the control members until the controls are created. - mForegroundStartT = NULL; - mForegroundEndT = NULL; - mBackgroundStartT = NULL; - mBackgroundEndT = NULL; - wxTextValidator vld(wxFILTER_NUMERIC); - wxString number; - - AudacityProject *p = GetActiveProject(); - mProjectRate = p->GetRate(); - - ShuttleGui S(this, eIsCreating); - - S.SetBorder(5); - S.StartHorizontalLay(wxCENTER, false); - { - /* i18n-hint: RMS abbreviates root mean square, a certain averaging method */ - S.AddTitle(_("Contrast Analyzer, for measuring RMS volume differences between two selections of audio.")); - } - S.EndHorizontalLay(); - S.StartStatic( _("Parameters") ); - { - S.StartMultiColumn(5, wxEXPAND); - { - - // Headings - S.AddFixedText( {} ); // spacer - S.AddFixedText(_("Start")); - S.AddFixedText(_("End")); - S.AddFixedText( {} ); // spacer - S.AddFixedText(_("Volume ")); - - //Foreground - S.AddFixedText(_("&Foreground:"), false); - if (S.GetMode() == eIsCreating) - { - mForegroundStartT = safenew - NumericTextCtrl(NumericConverter::TIME, this, - ID_FOREGROUNDSTART_T, - _("hh:mm:ss + hundredths"), - 0.0, - mProjectRate, - wxDefaultPosition, - wxDefaultSize, - true); - mForegroundStartT->SetName(_("Foreground start time")); - mForegroundStartT->EnableMenu(false); - mForegroundStartT->SetReadOnly(true); - } - S.AddWindow(mForegroundStartT); - - if (S.GetMode() == eIsCreating) - { - mForegroundEndT = safenew - NumericTextCtrl(NumericConverter::TIME, this, - ID_FOREGROUNDEND_T, - _("hh:mm:ss + hundredths"), - 0.0, - mProjectRate, - wxDefaultPosition, - wxDefaultSize, - true); - mForegroundEndT->SetName(_("Foreground end time")); - mForegroundEndT->EnableMenu(false); - mForegroundEndT->SetReadOnly(true); - } - S.AddWindow(mForegroundEndT); - - m_pButton_UseCurrentF = S.Id(ID_BUTTON_USECURRENTF).AddButton(_("&Measure selection")); - mForegroundRMSText=S.Id(ID_FOREGROUNDDB_TEXT).AddTextBox( {}, wxT(""), 17); - mForegroundRMSText->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(ContrastDialog::OnChar)); - - //Background - S.AddFixedText(_("&Background:")); - if (S.GetMode() == eIsCreating) - { - mBackgroundStartT = safenew - NumericTextCtrl(NumericConverter::TIME, this, - ID_BACKGROUNDSTART_T, - _("hh:mm:ss + hundredths"), - 0.0, - mProjectRate, - wxDefaultPosition, - wxDefaultSize, - true); - mBackgroundStartT->SetName(_("Background start time")); - mBackgroundStartT->EnableMenu(false); - mBackgroundStartT->SetReadOnly(true); - } - S.AddWindow(mBackgroundStartT); - - if (S.GetMode() == eIsCreating) - { - mBackgroundEndT = safenew - NumericTextCtrl(NumericConverter::TIME, this, - ID_BACKGROUNDEND_T, - _("hh:mm:ss + hundredths"), - 0.0, - mProjectRate, - wxDefaultPosition, - wxDefaultSize, - true); - mBackgroundEndT->SetName(_("Background end time")); - mBackgroundEndT->EnableMenu(false); - mBackgroundEndT->SetReadOnly(true); - } - S.AddWindow(mBackgroundEndT); - - m_pButton_UseCurrentB = S.Id(ID_BUTTON_USECURRENTB).AddButton(_("Mea&sure selection")); - mBackgroundRMSText = S.Id(ID_BACKGROUNDDB_TEXT).AddTextBox( {}, wxT(""), 17); - mBackgroundRMSText->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(ContrastDialog::OnChar)); - } - S.EndMultiColumn(); - } - S.EndStatic(); - - //Result - S.StartStatic( _("Result") ); - { - S.StartMultiColumn(3, wxCENTER); - { - S.AddFixedText(_("Co&ntrast Result:")); - mPassFailText = S.Id(ID_RESULTS_TEXT).AddTextBox( {}, wxT(""), 50); - mPassFailText->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(ContrastDialog::OnChar)); - m_pButton_Reset = S.Id(ID_BUTTON_RESET).AddButton(_("R&eset")); - S.AddFixedText(_("&Difference:")); - mDiffText = S.Id(ID_RESULTSDB_TEXT).AddTextBox( {}, wxT(""), 50); - mDiffText->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(ContrastDialog::OnChar)); - m_pButton_Export = S.Id(ID_BUTTON_EXPORT).AddButton(_("E&xport...")); - } - S.EndMultiColumn(); - } - S.EndStatic(); - S.AddStandardButtons(eCloseButton |eHelpButton); -#if 0 - S.StartMultiColumn(3, wxEXPAND); - { - S.SetStretchyCol(1); - m_pButton_GetURL = S.Id(ID_BUTTON_GETURL).AddButton(_("&Help")); - S.AddFixedText(wxT(" ")); // spacer - m_pButton_Close = S.Id(ID_BUTTON_CLOSE).AddButton(_("&Close")); - } - S.EndMultiColumn(); -#endif - Layout(); - Fit(); - SetMinSize(GetSize()); - Center(); -} - -ContrastDialog::~ContrastDialog() -{ - mForegroundRMSText->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(ContrastDialog::OnChar)); - mBackgroundRMSText->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(ContrastDialog::OnChar)); - mPassFailText->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(ContrastDialog::OnChar)); - mDiffText->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(ContrastDialog::OnChar)); -} - -void ContrastDialog::OnGetURL(wxCommandEvent & WXUNUSED(event)) -{ - // Original help page is back on-line (March 2016), but the manual should be more reliable. - // http://www.eramp.com/WCAG_2_audio_contrast_tool_help.htm - HelpSystem::ShowHelp(this, wxT("Contrast")); -} - -void ContrastDialog::OnClose(wxCommandEvent & WXUNUSED(event)) -{ - wxCommandEvent dummyEvent; - OnReset(dummyEvent); - - Show(false); -} - -void ContrastDialog::OnGetForeground(wxCommandEvent & /*event*/) -{ - AudacityProject *p = GetActiveProject(); - SelectedTrackListOfKindIterator iter(Track::Wave, p->GetTracks()); - - for (Track *t = iter.First(); t; t = iter.Next()) { - mForegroundStartT->SetValue(p->mViewInfo.selectedRegion.t0()); - mForegroundEndT->SetValue(p->mViewInfo.selectedRegion.t1()); - } - - SetStartAndEndTime(); - mForegroundIsDefined = GetDB(foregrounddB); - m_pButton_UseCurrentF->SetFocus(); - results(); -} - -void ContrastDialog::OnGetBackground(wxCommandEvent & /*event*/) -{ - AudacityProject *p = GetActiveProject(); - SelectedTrackListOfKindIterator iter(Track::Wave, p->GetTracks()); - - for (Track *t = iter.First(); t; t = iter.Next()) { - mBackgroundStartT->SetValue(p->mViewInfo.selectedRegion.t0()); - mBackgroundEndT->SetValue(p->mViewInfo.selectedRegion.t1()); - } - - SetStartAndEndTime(); - mBackgroundIsDefined = GetDB(backgrounddB); - m_pButton_UseCurrentB->SetFocus(); - results(); -} - -namespace { - // PRL: I gathered formatting into these functions, and eliminated some - // repetitions, and removed the redundant word "Average" as applied to RMS. - // Should these variations in formats be collapsed further? - - // Pass nullptr when value is not yet defined - wxString FormatRMSMessage( float *pValue ) - { - - /* i18n-hint: RMS abbreviates root mean square, a certain averaging method */ - wxString format0{ _("RMS = %s.") }; - - /* i18n-hint: dB abbreviates decibels */ - wxString format1{ _("%s dB") }; - - wxString value; - - if ( pValue ) - if( fabs( *pValue ) != std::numeric_limits::infinity() ) { - auto number = wxString::Format( _("%.2f"), *pValue ); - value = wxString::Format( format1, number ); - } - else - value = _("zero"); - else - value = wxString::Format( format1, "" ); - - return wxString::Format( format0, value ); - } - - wxString FormatDifference( float diffdB ) - { - if( diffdB != diffdB ) // test for NaN, reliant on IEEE implementation - return _("indeterminate"); - else { - if( diffdB != std::numeric_limits::infinity() ) - /* i18n-hint: dB abbreviates decibels */ - /* i18n-hint: RMS abbreviates root mean square, a certain averaging method */ - return wxString::Format(_("%.2f dB RMS"), diffdB); - else - /* i18n-hint: dB abbreviates decibels */ - return _("Infinite dB difference"); - } - } - - wxString FormatDifferenceForExport( float diffdB ) - { - if( diffdB != diffdB ) //test for NaN, reliant on IEEE implementation - return _("Difference is indeterminate."); - else - if( fabs(diffdB) != std::numeric_limits::infinity() ) - /* i18n-hint: dB abbreviates decibels */ - /* i18n-hint: RMS abbreviates root mean square, a certain averaging method */ - return wxString::Format(_("Difference = %.2f RMS dB."), diffdB ); - else - /* i18n-hint: dB abbreviates decibels */ - /* i18n-hint: RMS abbreviates root mean square, a certain averaging method */ - return _("Difference = infinite RMS dB."); - } -} - -void ContrastDialog::results() -{ - mPassFailText->SetName(wxT("")); - mPassFailText->ChangeValue(wxT("")); - mDiffText->ChangeValue(wxT("")); - - // foreground and background defined. - if(mForegroundIsDefined && mBackgroundIsDefined) { - float diffdB = std::fabs(foregrounddB - backgrounddB); - if(foregrounddB > DB_MAX_LIMIT) { - mPassFailText->ChangeValue(_("Foreground level too high")); - } - else if (backgrounddB > DB_MAX_LIMIT) { - mPassFailText->ChangeValue(_("Background level too high")); - } - else if (backgrounddB > foregrounddB) { - mPassFailText->ChangeValue(_("Background higher than foreground")); - } - else if(diffdB > WCAG2_PASS) { - /* i18n-hint: WCAG abbreviates Web Content Accessibility Guidelines */ - mPassFailText->ChangeValue(_("WCAG2 Pass")); - } - else { - /* i18n-hint: WCAG abbreviates Web Content Accessibility Guidelines */ - mPassFailText->ChangeValue(_("WCAG2 Fail")); - } - - /* i18n-hint: i.e. difference in loudness at the moment. */ - mDiffText->SetName(_("Current difference")); - mDiffText->ChangeValue( FormatDifference( diffdB ) ); - } - - if (mForegroundIsDefined) { - mForegroundRMSText->SetName(_("Measured foreground level")); // Read by screen-readers - if(std::isinf(- foregrounddB)) - mForegroundRMSText->ChangeValue(_("zero")); - else - mForegroundRMSText->ChangeValue(wxString::Format(_("%.2f dB"), foregrounddB)); // i18n-hint: short form of 'decibels' - } - else { - mForegroundRMSText->SetName(_("No foreground measured")); // Read by screen-readers - mForegroundRMSText->ChangeValue(wxT("")); - mPassFailText->ChangeValue(_("Foreground not yet measured")); - } - - if (mBackgroundIsDefined) { - mBackgroundRMSText->SetName(_("Measured background level")); - if(std::isinf(- backgrounddB)) - mBackgroundRMSText->ChangeValue(_("zero")); - else - mBackgroundRMSText->ChangeValue(wxString::Format(_("%.2f dB"), backgrounddB)); - } - else { - mBackgroundRMSText->SetName(_("No background measured")); - mBackgroundRMSText->ChangeValue(wxT("")); - mPassFailText->ChangeValue(_("Background not yet measured")); - } -} - -void ContrastDialog::OnExport(wxCommandEvent & WXUNUSED(event)) -{ - // TODO: Handle silence checks better (-infinity dB) - AudacityProject * project = GetActiveProject(); - wxString fName = wxT("contrast.txt"); - - fName = FileNames::SelectFile(FileNames::Operation::Export, - _("Export Contrast Result As:"), - wxEmptyString, - fName, - wxT("txt"), - wxT("*.txt"), - wxFD_SAVE | wxRESIZE_BORDER, - this); - - if (fName == wxT("")) - return; - - wxTextFile f(fName); -#ifdef __WXMAC__ - wxFile{}.Create(fName); -#else - f.Create(); -#endif - f.Open(); - if (!f.IsOpened()) { - AudacityMessageBox( - wxString::Format( _("Couldn't write to file: %s"), fName) ); - return; - } - - f.AddLine(wxT("===================================")); - /* i18n-hint: WCAG abbreviates Web Content Accessibility Guidelines */ - f.AddLine(_("WCAG 2.0 Success Criteria 1.4.7 Contrast Results")); - f.AddLine(wxT("")); - f.AddLine(wxString::Format(_("Filename = %s."), project->GetFileName() )); - f.AddLine(wxT("")); - f.AddLine(_("Foreground")); - float t = (float)mForegroundStartT->GetValue(); - int h = (int)(t/3600); // there must be a standard function for this! - int m = (int)((t - h*3600)/60); - float s = t - h*3600.0 - m*60.0; - f.AddLine(wxString::Format(_("Time started = %2d hour(s), %2d minute(s), %.2f seconds."), h, m, s )); - t = (float)mForegroundEndT->GetValue(); - h = (int)(t/3600); - m = (int)((t - h*3600)/60); - s = t - h*3600.0 - m*60.0; - f.AddLine(wxString::Format(_("Time ended = %2d hour(s), %2d minute(s), %.2f seconds."), h, m, s )); - f.AddLine( FormatRMSMessage( mForegroundIsDefined ? &foregrounddB : nullptr ) ); - f.AddLine(wxT("")); - f.AddLine(_("Background")); - t = (float)mBackgroundStartT->GetValue(); - h = (int)(t/3600); - m = (int)((t - h*3600)/60); - s = t - h*3600.0 - m*60.0; - f.AddLine(wxString::Format(_("Time started = %2d hour(s), %2d minute(s), %.2f seconds."), h, m, s )); - t = (float)mBackgroundEndT->GetValue(); - h = (int)(t/3600); - m = (int)((t - h*3600)/60); - s = t - h*3600.0 - m*60.0; - f.AddLine(wxString::Format(_("Time ended = %2d hour(s), %2d minute(s), %.2f seconds."), h, m, s )); - f.AddLine( FormatRMSMessage( mBackgroundIsDefined ? &backgrounddB : nullptr ) ); - f.AddLine(wxT("")); - f.AddLine(_("Results")); - float diffdB = foregrounddB - backgrounddB; - - f.AddLine( FormatDifferenceForExport( diffdB ) ); - if( diffdB > 20. ) - f.AddLine(_("Success Criteria 1.4.7 of WCAG 2.0: Pass")); - else - f.AddLine(_("Success Criteria 1.4.7 of WCAG 2.0: Fail")); - - f.AddLine(wxT("")); - f.AddLine(_("Data gathered")); - wxString sNow; - wxDateTime now = wxDateTime::Now(); - int year = now.GetYear(); - wxDateTime::Month month = now.GetMonth(); - wxString monthName = now.GetMonthName(month); - int dom = now.GetDay(); - int hour = now.GetHour(); - int minute = now.GetMinute(); - int second = now.GetSecond(); - sNow = wxString::Format(wxT("%d %s %02d %02dh %02dm %02ds"), - dom, monthName, year, hour, minute, second); - f.AddLine(sNow); - - f.AddLine(wxT("===================================")); - f.AddLine(wxT("")); - -#ifdef __WXMAC__ - f.Write(wxTextFileType_Mac); -#else - f.Write(); -#endif - f.Close(); -} - -void ContrastDialog::OnReset(wxCommandEvent & /*event*/) -{ - mForegroundStartT->SetValue(0.0); - mForegroundEndT->SetValue(0.0); - mBackgroundStartT->SetValue(0.0); - mBackgroundEndT->SetValue(0.0); - mForegroundIsDefined = false; - mBackgroundIsDefined = false; - - mForegroundRMSText->SetName(_("No foreground measured")); // Read by screen-readers - mBackgroundRMSText->SetName(_("No background measured")); - mForegroundRMSText->ChangeValue(wxT("")); // Displayed value - mBackgroundRMSText->ChangeValue(wxT("")); - mPassFailText->ChangeValue(wxT("")); - mDiffText->ChangeValue(wxT("")); -} - -void ContrastDialog::OnChar(wxKeyEvent & event) -{ - // Is this still required? - if (event.GetKeyCode() == WXK_TAB) { - event.Skip(); - return; - } - - event.Skip(false); - return; -} +/********************************************************************** + + Audacity: A Digital Audio Editor + + Contrast.cpp + +\class ContrastDialog +\brief Dialog used for Contrast menu item + +*//*******************************************************************/ + +#include "../Audacity.h" +#include "Contrast.h" + +#include "../WaveTrack.h" +#include "../Prefs.h" +#include "../Project.h" +#include "../ShuttleGui.h" +#include "../FileNames.h" +#include "../widgets/LinkingHtmlWindow.h" +#include "../widgets/HelpSystem.h" +#include "../widgets/NumericTextCtrl.h" +#include "../widgets/ErrorDialog.h" + +#include +#include + +#if defined(__WXMSW__) && !defined(__CYGWIN__) +#include +#define finite(x) _finite(x) +#endif + +#include +#include +#include + +#include "../PlatformCompatibility.h" + +#define DB_MAX_LIMIT 0.0 // Audio is massively distorted. +#define WCAG2_PASS 20.0 // dB difference required to pass WCAG2 test. + + +bool ContrastDialog::GetDB(float &dB) +{ + float rms = float(0.0); + int numberSelecteTracks = 0; + + // For stereo tracks: sqrt((mean(L)+mean(R))/2) + bool isStereo = false; + double meanSq = 0.0; + + AudacityProject *p = GetActiveProject(); + SelectedTrackListOfKindIterator iter(Track::Wave, p->GetTracks()); + WaveTrack *t = (WaveTrack *) iter.First(); + while (t) { + numberSelecteTracks++; + if (numberSelecteTracks > 1 && !isStereo) { + AudacityMessageDialog m(NULL, _("You can only measure one track at a time."), _("Error"), wxOK); + m.ShowModal(); + return false; + } + isStereo = t->GetLinked(); + + wxASSERT(mT0 <= mT1); + + // Ignore whitespace beyond ends of track. + if(mT0 < t->GetStartTime()) + mT0 = t->GetStartTime(); + if(mT1 > t->GetEndTime()) + mT1 = t->GetEndTime(); + + auto SelT0 = t->TimeToLongSamples(mT0); + auto SelT1 = t->TimeToLongSamples(mT1); + + if(SelT0 > SelT1) + { + AudacityMessageDialog m(NULL, _("Invalid audio selection.\nPlease ensure that audio is selected."), _("Error"), wxOK); + m.ShowModal(); + return false; + } + + if(SelT0 == SelT1) + { + AudacityMessageDialog m(NULL, _("Nothing to measure.\nPlease select a section of a track."), _("Error"), wxOK); + m.ShowModal(); + return false; + } + + // Don't throw in this analysis dialog + rms = ((WaveTrack *)t)->GetRMS(mT0, mT1, false); + meanSq += rms * rms; + t = (WaveTrack *) iter.Next(); + } + // TODO: This works for stereo, provided the audio clips are in both channels. + // We should really count gaps between clips as silence. + rms = (meanSq > 0.0)? sqrt(meanSq/(double)numberSelecteTracks) : 0.0; + + if(numberSelecteTracks == 0) { + AudacityMessageDialog m(NULL, _("Please select an audio track."), _("Error"), wxOK); + m.ShowModal(); + return false; + } + // Gives warning C4056, Overflow in floating-point constant arithmetic + // -INFINITY is intentional here. + // Looks like we are stuck with this warning, as // #pragma warning( disable : 4056) // even around the whole function does not disable it successfully. + dB = (rms == 0.0)? -INFINITY : LINEAR_TO_DB(rms); + return true; +} + +void ContrastDialog::SetStartAndEndTime() +{ + AudacityProject *p = GetActiveProject(); + mT0 = p->mViewInfo.selectedRegion.t0(); + mT1 = p->mViewInfo.selectedRegion.t1(); +} + + +// WDR: class implementations + +//---------------------------------------------------------------------------- +// ContrastDialog +//---------------------------------------------------------------------------- + +// WDR: event table for ContrastDialog + +enum { + ID_BUTTON_USECURRENTF = 10001, + ID_BUTTON_USECURRENTB, + //ID_BUTTON_GETURL, + ID_BUTTON_EXPORT, + ID_BUTTON_RESET, + //ID_BUTTON_CLOSE, + ID_FOREGROUNDSTART_T, + ID_FOREGROUNDEND_T, + ID_BACKGROUNDSTART_T, + ID_BACKGROUNDEND_T, + ID_FOREGROUNDDB_TEXT, + ID_BACKGROUNDDB_TEXT, + ID_RESULTS_TEXT, + ID_RESULTSDB_TEXT +}; + +BEGIN_EVENT_TABLE(ContrastDialog,wxDialogWrapper) + EVT_BUTTON(ID_BUTTON_USECURRENTF, ContrastDialog::OnGetForeground) + EVT_BUTTON(ID_BUTTON_USECURRENTB, ContrastDialog::OnGetBackground) + EVT_BUTTON(wxID_HELP, ContrastDialog::OnGetURL) + EVT_BUTTON(ID_BUTTON_EXPORT, ContrastDialog::OnExport) + EVT_BUTTON(ID_BUTTON_RESET, ContrastDialog::OnReset) + EVT_BUTTON(wxID_CANCEL, ContrastDialog::OnClose) +END_EVENT_TABLE() + +/* i18n-hint: WCAG2 is the 'Web Content Accessibility Guidelines (WCAG) 2.0', see http://www.w3.org/TR/WCAG20/ */ +ContrastDialog::ContrastDialog(wxWindow * parent, wxWindowID id, + const wxString & title, + const wxPoint & pos): + wxDialogWrapper(parent, id, title, pos, wxDefaultSize, + wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMAXIMIZE_BOX ) +{ + SetName(GetTitle()); + + mT0 = 0.0; + mT1 = 0.0; + foregrounddB = 0.0; + backgrounddB = 0.0; + mForegroundIsDefined = false; + mBackgroundIsDefined = false; + + // NULL out the control members until the controls are created. + mForegroundStartT = NULL; + mForegroundEndT = NULL; + mBackgroundStartT = NULL; + mBackgroundEndT = NULL; + wxTextValidator vld(wxFILTER_NUMERIC); + wxString number; + + AudacityProject *p = GetActiveProject(); + mProjectRate = p->GetRate(); + + ShuttleGui S(this, eIsCreating); + + S.SetBorder(5); + S.StartHorizontalLay(wxCENTER, false); + { + /* i18n-hint: RMS abbreviates root mean square, a certain averaging method */ + S.AddTitle(_("Contrast Analyzer, for measuring RMS volume differences between two selections of audio.")); + } + S.EndHorizontalLay(); + S.StartStatic( _("Parameters") ); + { + S.StartMultiColumn(5, wxEXPAND); + { + + // Headings + S.AddFixedText( {} ); // spacer + S.AddFixedText(_("Start")); + S.AddFixedText(_("End")); + S.AddFixedText( {} ); // spacer + S.AddFixedText(_("Volume ")); + + //Foreground + S.AddFixedText(_("&Foreground:"), false); + if (S.GetMode() == eIsCreating) + { + mForegroundStartT = safenew + NumericTextCtrl(NumericConverter::TIME, this, + ID_FOREGROUNDSTART_T, + _("hh:mm:ss + hundredths"), + 0.0, + mProjectRate, + wxDefaultPosition, + wxDefaultSize, + true); + mForegroundStartT->SetName(_("Foreground start time")); + mForegroundStartT->EnableMenu(false); + mForegroundStartT->SetReadOnly(true); + } + S.AddWindow(mForegroundStartT); + + if (S.GetMode() == eIsCreating) + { + mForegroundEndT = safenew + NumericTextCtrl(NumericConverter::TIME, this, + ID_FOREGROUNDEND_T, + _("hh:mm:ss + hundredths"), + 0.0, + mProjectRate, + wxDefaultPosition, + wxDefaultSize, + true); + mForegroundEndT->SetName(_("Foreground end time")); + mForegroundEndT->EnableMenu(false); + mForegroundEndT->SetReadOnly(true); + } + S.AddWindow(mForegroundEndT); + + m_pButton_UseCurrentF = S.Id(ID_BUTTON_USECURRENTF).AddButton(_("&Measure selection")); + mForegroundRMSText=S.Id(ID_FOREGROUNDDB_TEXT).AddTextBox( {}, wxT(""), 17); + mForegroundRMSText->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(ContrastDialog::OnChar)); + + //Background + S.AddFixedText(_("&Background:")); + if (S.GetMode() == eIsCreating) + { + mBackgroundStartT = safenew + NumericTextCtrl(NumericConverter::TIME, this, + ID_BACKGROUNDSTART_T, + _("hh:mm:ss + hundredths"), + 0.0, + mProjectRate, + wxDefaultPosition, + wxDefaultSize, + true); + mBackgroundStartT->SetName(_("Background start time")); + mBackgroundStartT->EnableMenu(false); + mBackgroundStartT->SetReadOnly(true); + } + S.AddWindow(mBackgroundStartT); + + if (S.GetMode() == eIsCreating) + { + mBackgroundEndT = safenew + NumericTextCtrl(NumericConverter::TIME, this, + ID_BACKGROUNDEND_T, + _("hh:mm:ss + hundredths"), + 0.0, + mProjectRate, + wxDefaultPosition, + wxDefaultSize, + true); + mBackgroundEndT->SetName(_("Background end time")); + mBackgroundEndT->EnableMenu(false); + mBackgroundEndT->SetReadOnly(true); + } + S.AddWindow(mBackgroundEndT); + + m_pButton_UseCurrentB = S.Id(ID_BUTTON_USECURRENTB).AddButton(_("Mea&sure selection")); + mBackgroundRMSText = S.Id(ID_BACKGROUNDDB_TEXT).AddTextBox( {}, wxT(""), 17); + mBackgroundRMSText->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(ContrastDialog::OnChar)); + } + S.EndMultiColumn(); + } + S.EndStatic(); + + //Result + S.StartStatic( _("Result") ); + { + S.StartMultiColumn(3, wxCENTER); + { + S.AddFixedText(_("Co&ntrast Result:")); + mPassFailText = S.Id(ID_RESULTS_TEXT).AddTextBox( {}, wxT(""), 50); + mPassFailText->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(ContrastDialog::OnChar)); + m_pButton_Reset = S.Id(ID_BUTTON_RESET).AddButton(_("R&eset")); + S.AddFixedText(_("&Difference:")); + mDiffText = S.Id(ID_RESULTSDB_TEXT).AddTextBox( {}, wxT(""), 50); + mDiffText->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(ContrastDialog::OnChar)); + m_pButton_Export = S.Id(ID_BUTTON_EXPORT).AddButton(_("E&xport...")); + } + S.EndMultiColumn(); + } + S.EndStatic(); + S.AddStandardButtons(eCloseButton |eHelpButton); +#if 0 + S.StartMultiColumn(3, wxEXPAND); + { + S.SetStretchyCol(1); + m_pButton_GetURL = S.Id(ID_BUTTON_GETURL).AddButton(_("&Help")); + S.AddFixedText(wxT(" ")); // spacer + m_pButton_Close = S.Id(ID_BUTTON_CLOSE).AddButton(_("&Close")); + } + S.EndMultiColumn(); +#endif + Layout(); + Fit(); + SetMinSize(GetSize()); + Center(); +} + +ContrastDialog::~ContrastDialog() +{ + mForegroundRMSText->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(ContrastDialog::OnChar)); + mBackgroundRMSText->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(ContrastDialog::OnChar)); + mPassFailText->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(ContrastDialog::OnChar)); + mDiffText->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(ContrastDialog::OnChar)); +} + +void ContrastDialog::OnGetURL(wxCommandEvent & WXUNUSED(event)) +{ + // Original help page is back on-line (March 2016), but the manual should be more reliable. + // http://www.eramp.com/WCAG_2_audio_contrast_tool_help.htm + HelpSystem::ShowHelp(this, wxT("Contrast")); +} + +void ContrastDialog::OnClose(wxCommandEvent & WXUNUSED(event)) +{ + wxCommandEvent dummyEvent; + OnReset(dummyEvent); + + Show(false); +} + +void ContrastDialog::OnGetForeground(wxCommandEvent & /*event*/) +{ + AudacityProject *p = GetActiveProject(); + SelectedTrackListOfKindIterator iter(Track::Wave, p->GetTracks()); + + for (Track *t = iter.First(); t; t = iter.Next()) { + mForegroundStartT->SetValue(p->mViewInfo.selectedRegion.t0()); + mForegroundEndT->SetValue(p->mViewInfo.selectedRegion.t1()); + } + + SetStartAndEndTime(); + mForegroundIsDefined = GetDB(foregrounddB); + m_pButton_UseCurrentF->SetFocus(); + results(); +} + +void ContrastDialog::OnGetBackground(wxCommandEvent & /*event*/) +{ + AudacityProject *p = GetActiveProject(); + SelectedTrackListOfKindIterator iter(Track::Wave, p->GetTracks()); + + for (Track *t = iter.First(); t; t = iter.Next()) { + mBackgroundStartT->SetValue(p->mViewInfo.selectedRegion.t0()); + mBackgroundEndT->SetValue(p->mViewInfo.selectedRegion.t1()); + } + + SetStartAndEndTime(); + mBackgroundIsDefined = GetDB(backgrounddB); + m_pButton_UseCurrentB->SetFocus(); + results(); +} + +namespace { + // PRL: I gathered formatting into these functions, and eliminated some + // repetitions, and removed the redundant word "Average" as applied to RMS. + // Should these variations in formats be collapsed further? + + // Pass nullptr when value is not yet defined + wxString FormatRMSMessage( float *pValue ) + { + + /* i18n-hint: RMS abbreviates root mean square, a certain averaging method */ + wxString format0{ _("RMS = %s.") }; + + /* i18n-hint: dB abbreviates decibels */ + wxString format1{ _("%s dB") }; + + wxString value; + + if ( pValue ) + if( fabs( *pValue ) != std::numeric_limits::infinity() ) { + auto number = wxString::Format( _("%.2f"), *pValue ); + value = wxString::Format( format1, number ); + } + else + value = _("zero"); + else + value = wxString::Format( format1, "" ); + + return wxString::Format( format0, value ); + } + + wxString FormatDifference( float diffdB ) + { + if( diffdB != diffdB ) // test for NaN, reliant on IEEE implementation + return _("indeterminate"); + else { + if( diffdB != std::numeric_limits::infinity() ) + /* i18n-hint: dB abbreviates decibels */ + /* i18n-hint: RMS abbreviates root mean square, a certain averaging method */ + return wxString::Format(_("%.2f dB RMS"), diffdB); + else + /* i18n-hint: dB abbreviates decibels */ + return _("Infinite dB difference"); + } + } + + wxString FormatDifferenceForExport( float diffdB ) + { + if( diffdB != diffdB ) //test for NaN, reliant on IEEE implementation + return _("Difference is indeterminate."); + else + if( fabs(diffdB) != std::numeric_limits::infinity() ) + /* i18n-hint: dB abbreviates decibels */ + /* i18n-hint: RMS abbreviates root mean square, a certain averaging method */ + return wxString::Format(_("Difference = %.2f RMS dB."), diffdB ); + else + /* i18n-hint: dB abbreviates decibels */ + /* i18n-hint: RMS abbreviates root mean square, a certain averaging method */ + return _("Difference = infinite RMS dB."); + } +} + +void ContrastDialog::results() +{ + mPassFailText->SetName(wxT("")); + mPassFailText->ChangeValue(wxT("")); + mDiffText->ChangeValue(wxT("")); + + // foreground and background defined. + if(mForegroundIsDefined && mBackgroundIsDefined) { + float diffdB = std::fabs(foregrounddB - backgrounddB); + if(foregrounddB > DB_MAX_LIMIT) { + mPassFailText->ChangeValue(_("Foreground level too high")); + } + else if (backgrounddB > DB_MAX_LIMIT) { + mPassFailText->ChangeValue(_("Background level too high")); + } + else if (backgrounddB > foregrounddB) { + mPassFailText->ChangeValue(_("Background higher than foreground")); + } + else if(diffdB > WCAG2_PASS) { + /* i18n-hint: WCAG abbreviates Web Content Accessibility Guidelines */ + mPassFailText->ChangeValue(_("WCAG2 Pass")); + } + else { + /* i18n-hint: WCAG abbreviates Web Content Accessibility Guidelines */ + mPassFailText->ChangeValue(_("WCAG2 Fail")); + } + + /* i18n-hint: i.e. difference in loudness at the moment. */ + mDiffText->SetName(_("Current difference")); + mDiffText->ChangeValue( FormatDifference( diffdB ) ); + } + + if (mForegroundIsDefined) { + mForegroundRMSText->SetName(_("Measured foreground level")); // Read by screen-readers + if(std::isinf(- foregrounddB)) + mForegroundRMSText->ChangeValue(_("zero")); + else + mForegroundRMSText->ChangeValue(wxString::Format(_("%.2f dB"), foregrounddB)); // i18n-hint: short form of 'decibels' + } + else { + mForegroundRMSText->SetName(_("No foreground measured")); // Read by screen-readers + mForegroundRMSText->ChangeValue(wxT("")); + mPassFailText->ChangeValue(_("Foreground not yet measured")); + } + + if (mBackgroundIsDefined) { + mBackgroundRMSText->SetName(_("Measured background level")); + if(std::isinf(- backgrounddB)) + mBackgroundRMSText->ChangeValue(_("zero")); + else + mBackgroundRMSText->ChangeValue(wxString::Format(_("%.2f dB"), backgrounddB)); + } + else { + mBackgroundRMSText->SetName(_("No background measured")); + mBackgroundRMSText->ChangeValue(wxT("")); + mPassFailText->ChangeValue(_("Background not yet measured")); + } +} + +void ContrastDialog::OnExport(wxCommandEvent & WXUNUSED(event)) +{ + // TODO: Handle silence checks better (-infinity dB) + AudacityProject * project = GetActiveProject(); + wxString fName = wxT("contrast.txt"); + + fName = FileNames::SelectFile(FileNames::Operation::Export, + _("Export Contrast Result As:"), + wxEmptyString, + fName, + wxT("txt"), + wxT("*.txt"), + wxFD_SAVE | wxRESIZE_BORDER, + this); + + if (fName == wxT("")) + return; + + wxTextFile f(fName); +#ifdef __WXMAC__ + wxFile{}.Create(fName); +#else + f.Create(); +#endif + f.Open(); + if (!f.IsOpened()) { + AudacityMessageBox( + wxString::Format( _("Couldn't write to file: %s"), fName) ); + return; + } + + f.AddLine(wxT("===================================")); + /* i18n-hint: WCAG abbreviates Web Content Accessibility Guidelines */ + f.AddLine(_("WCAG 2.0 Success Criteria 1.4.7 Contrast Results")); + f.AddLine(wxT("")); + f.AddLine(wxString::Format(_("Filename = %s."), project->GetFileName() )); + f.AddLine(wxT("")); + f.AddLine(_("Foreground")); + float t = (float)mForegroundStartT->GetValue(); + int h = (int)(t/3600); // there must be a standard function for this! + int m = (int)((t - h*3600)/60); + float s = t - h*3600.0 - m*60.0; + f.AddLine(wxString::Format(_("Time started = %2d hour(s), %2d minute(s), %.2f seconds."), h, m, s )); + t = (float)mForegroundEndT->GetValue(); + h = (int)(t/3600); + m = (int)((t - h*3600)/60); + s = t - h*3600.0 - m*60.0; + f.AddLine(wxString::Format(_("Time ended = %2d hour(s), %2d minute(s), %.2f seconds."), h, m, s )); + f.AddLine( FormatRMSMessage( mForegroundIsDefined ? &foregrounddB : nullptr ) ); + f.AddLine(wxT("")); + f.AddLine(_("Background")); + t = (float)mBackgroundStartT->GetValue(); + h = (int)(t/3600); + m = (int)((t - h*3600)/60); + s = t - h*3600.0 - m*60.0; + f.AddLine(wxString::Format(_("Time started = %2d hour(s), %2d minute(s), %.2f seconds."), h, m, s )); + t = (float)mBackgroundEndT->GetValue(); + h = (int)(t/3600); + m = (int)((t - h*3600)/60); + s = t - h*3600.0 - m*60.0; + f.AddLine(wxString::Format(_("Time ended = %2d hour(s), %2d minute(s), %.2f seconds."), h, m, s )); + f.AddLine( FormatRMSMessage( mBackgroundIsDefined ? &backgrounddB : nullptr ) ); + f.AddLine(wxT("")); + f.AddLine(_("Results")); + float diffdB = foregrounddB - backgrounddB; + + f.AddLine( FormatDifferenceForExport( diffdB ) ); + if( diffdB > 20. ) + f.AddLine(_("Success Criteria 1.4.7 of WCAG 2.0: Pass")); + else + f.AddLine(_("Success Criteria 1.4.7 of WCAG 2.0: Fail")); + + f.AddLine(wxT("")); + f.AddLine(_("Data gathered")); + wxString sNow; + wxDateTime now = wxDateTime::Now(); + int year = now.GetYear(); + wxDateTime::Month month = now.GetMonth(); + wxString monthName = now.GetMonthName(month); + int dom = now.GetDay(); + int hour = now.GetHour(); + int minute = now.GetMinute(); + int second = now.GetSecond(); + sNow = wxString::Format(wxT("%d %s %02d %02dh %02dm %02ds"), + dom, monthName, year, hour, minute, second); + f.AddLine(sNow); + + f.AddLine(wxT("===================================")); + f.AddLine(wxT("")); + +#ifdef __WXMAC__ + f.Write(wxTextFileType_Mac); +#else + f.Write(); +#endif + f.Close(); +} + +void ContrastDialog::OnReset(wxCommandEvent & /*event*/) +{ + mForegroundStartT->SetValue(0.0); + mForegroundEndT->SetValue(0.0); + mBackgroundStartT->SetValue(0.0); + mBackgroundEndT->SetValue(0.0); + mForegroundIsDefined = false; + mBackgroundIsDefined = false; + + mForegroundRMSText->SetName(_("No foreground measured")); // Read by screen-readers + mBackgroundRMSText->SetName(_("No background measured")); + mForegroundRMSText->ChangeValue(wxT("")); // Displayed value + mBackgroundRMSText->ChangeValue(wxT("")); + mPassFailText->ChangeValue(wxT("")); + mDiffText->ChangeValue(wxT("")); +} + +void ContrastDialog::OnChar(wxKeyEvent & event) +{ + // Is this still required? + if (event.GetKeyCode() == WXK_TAB) { + event.Skip(); + return; + } + + event.Skip(false); + return; +} diff --git a/src/effects/SBSMSEffect.h b/src/effects/SBSMSEffect.h index ce29f2580..20bf758b2 100644 --- a/src/effects/SBSMSEffect.h +++ b/src/effects/SBSMSEffect.h @@ -17,8 +17,8 @@ #define __AUDACITY_EFFECT_SBSMS__ #include "Effect.h" +#include "../../../lib-src/header-substitutes/sbsms.h" -#include "sbsms.h" using namespace _sbsms_; class LabelTrack; diff --git a/src/effects/TimeScale.cpp b/src/effects/TimeScale.cpp index 7527c5f91..ecc041a53 100644 --- a/src/effects/TimeScale.cpp +++ b/src/effects/TimeScale.cpp @@ -26,8 +26,6 @@ #include "../ShuttleGui.h" #include "../widgets/valnum.h" -#include "sbsms.h" - enum { ID_RatePercentChangeStart = 10000, diff --git a/src/tracks/playabletrack/notetrack/ui/NoteTrackVZoomHandle.cpp b/src/tracks/playabletrack/notetrack/ui/NoteTrackVZoomHandle.cpp index e5bacee30..85aedbe87 100644 --- a/src/tracks/playabletrack/notetrack/ui/NoteTrackVZoomHandle.cpp +++ b/src/tracks/playabletrack/notetrack/ui/NoteTrackVZoomHandle.cpp @@ -203,7 +203,7 @@ NoteTrackVRulerMenuTable &NoteTrackVRulerMenuTable::Instance() return instance; } -void NoteTrackVRulerMenuTable::InitMenu(Menu *pMenu, void *pUserData) +void NoteTrackVRulerMenuTable::InitMenu(Menu *WXUNUSED(pMenu), void *pUserData) { mpData = static_cast(pUserData); } @@ -247,7 +247,7 @@ UIHandle::Result NoteTrackVZoomHandle::Release return RefreshNone; const wxMouseEvent &event = evt.event; - const bool shiftDown = event.ShiftDown(); + //const bool shiftDown = event.ShiftDown(); const bool rightUp = event.RightUp(); diff --git a/src/tracks/ui/Scrubbing.h b/src/tracks/ui/Scrubbing.h index cd13e925d..f67f0a501 100644 --- a/src/tracks/ui/Scrubbing.h +++ b/src/tracks/ui/Scrubbing.h @@ -131,7 +131,7 @@ public: // Convenience wrapper for the above template - void Thunk(wxCommandEvent &dummy) + void Thunk(wxCommandEvent &) { (this->*pfn)(*GetActiveProject()); } // A string to put in the leftmost part of the status bar diff --git a/src/widgets/wxPanelWrapper.h b/src/widgets/wxPanelWrapper.h index a3330a337..1495f5a98 100644 --- a/src/widgets/wxPanelWrapper.h +++ b/src/widgets/wxPanelWrapper.h @@ -16,7 +16,7 @@ void wxTabTraversalWrapperCharHook(wxKeyEvent &event); template -class wxTabTraversalWrapper : public Base +class AUDACITY_DLL_API wxTabTraversalWrapper : public Base { public: template @@ -32,7 +32,7 @@ public: } }; -class wxPanelWrapper : public wxTabTraversalWrapper +class AUDACITY_DLL_API wxPanelWrapper : public wxTabTraversalWrapper { public: // Constructors diff --git a/src/xml/XMLTagHandler.cpp b/src/xml/XMLTagHandler.cpp index db7f5b95a..9321b8b2f 100644 --- a/src/xml/XMLTagHandler.cpp +++ b/src/xml/XMLTagHandler.cpp @@ -121,7 +121,7 @@ bool XMLValueChecker::IsGoodIntForRange(const wxString & strInt, const wxString if( lenStrInt < 1 ) return false; - int offset = (strInt[0] == '-') ?1:0; + size_t offset = (strInt[0] == '-') ?1:0; if( lenStrInt <= offset ) return false;// string too short, no digits in it. @@ -134,7 +134,7 @@ bool XMLValueChecker::IsGoodIntForRange(const wxString & strInt, const wxString return false; // not a digit // All chars were digits. - if( (lenStrInt - offset) < lenMAXABS ) + if( lenStrInt < (lenMAXABS + offset) ) return true; // too few digits to overflow. // Numerical part is same length as strMAXABS