diff --git a/src/AboutDialog.cpp b/src/AboutDialog.cpp index c158302a8..7b63e60e5 100644 --- a/src/AboutDialog.cpp +++ b/src/AboutDialog.cpp @@ -117,6 +117,7 @@ void AboutDialog::CreateCreditsList() AddCredit(wxString(wxT("Mike Underwood, ")) + _("developer"), roleContributor); AddCredit(wxString(wxT("Philip Van Baren, ")) + _("developer"), roleContributor); AddCredit(wxString(wxT("Salvo Ventura, ")) + _("developer"), roleContributor); + AddCredit(wxString(wxT("Darrell Walisser, ")) + _("developer"), roleContributor); AddCredit(wxString(wxT("Jun Wan, ")) + _("developer"), roleContributor); AddCredit(wxString(wxT("Daniel Winzen, ")) + _("developer"), roleContributor); AddCredit(wxString(wxT("Tom Woodhams, ")) + _("developer"), roleContributor); diff --git a/src/AudacityApp.cpp b/src/AudacityApp.cpp index df3fd429a..c0a67b7e8 100644 --- a/src/AudacityApp.cpp +++ b/src/AudacityApp.cpp @@ -1590,7 +1590,14 @@ bool AudacityApp::IsTempDirectoryNameOK( const wxString & Name ){ // use Long Path to expand out any abbreviated long substrings. wxString BadPath = tmpFile.GetLongPath(); ::wxRemoveFile(tmpFile.GetFullPath()); + #ifdef __WXMAC__ + // This test is to fix bug 1220 on a 1.x to 2.x to 2.1.3 upgrade. + // It is less permissive than we could be as it stops a path + // with this string ANYWHERE within it rather than excluding just + // the paths that the earlier Audacities used to create. + if( Name.Contains( "/tmp/") ) + return false; BadPath = BadPath.BeforeLast( '/' ) + "/"; wxFileName cmpFile( Name ); wxString NameCanonical = cmpFile.GetLongPath( ) + "/"; diff --git a/src/Project.cpp b/src/Project.cpp index 356252520..78f9f24f7 100644 --- a/src/Project.cpp +++ b/src/Project.cpp @@ -636,16 +636,21 @@ void GetDefaultWindowRect(wxRect *defRect) } } +// true iff we have enough of the top bar to be able to reposition the window. bool IsWindowAccessible(wxRect *requestedRect) { wxDisplay display; wxRect targetTitleRect(requestedRect->GetLeftTop(), requestedRect->GetBottomRight()); + // Hackery to approximate a window top bar size from a window size. + // and exclude the open/close and borders. targetTitleRect.x += 15; targetTitleRect.width -= 100; if (targetTitleRect.width < 165) targetTitleRect.width = 165; targetTitleRect.height = 15; int targetBottom = targetTitleRect.GetBottom(); int targetRight = targetTitleRect.GetRight(); + // This looks like overkill to check each and every pixel in the ranges. + // and decide that if any is visible on screen we are OK. for (int i = targetTitleRect.GetLeft(); i < targetRight; i++) { for (int j = targetTitleRect.GetTop(); j < targetBottom; j++) { int monitor = display.GetFromPoint(wxPoint(i, j)); @@ -657,6 +662,18 @@ bool IsWindowAccessible(wxRect *requestedRect) return FALSE; } +// Returns the screen containing a rectangle, or -1 if none does. +int ScreenContaining( wxRect & r ){ + unsigned int n = wxDisplay::GetCount(); + for(unsigned int i = 0;iy += inc; } + // defaultrect is a rectangle on the first screen. It's the right fallback to + // use most of the time if things are not working out right with sizing. + // windowRect is a saved rectangle size. + // normalRect seems to be a substitute for windowRect when iconized or maximised. - //Have we hit the right side of the screen? + // Windows can say that we are off screen when actually we are not. + // On Windows 10 I am seeing miscalculation by about 6 pixels. + // To fix this we allow some sloppiness on the edge being counted as off screen. + // This matters most when restoring very carefully sized windows that are maximised + // in one dimension (height or width) but not both. + const int edgeSlop = 10; + + // Next four lines are getting the rectangle for the screen that contains the + // top left corner of nextRect (and defaulting to rect of screen 0 otherwise). + wxPoint p = nextRect->GetLeftTop(); + int scr = std::max( 0, wxDisplay::GetFromPoint( p )); + wxDisplay d( scr ); + screenRect = d.GetClientArea(); + + // Now we (possibly) start trimming our rectangle down. + // Have we hit the right side of the screen? wxPoint bottomRight = nextRect->GetBottomRight(); - if (bottomRight.x > screenRect.GetRight()) { + if (bottomRight.x > (screenRect.GetRight()+edgeSlop)) { int newWidth = screenRect.GetWidth() - nextRect->GetLeft(); if (newWidth < defaultRect.GetWidth()) { nextRect->x = windowRect.x; @@ -769,16 +806,19 @@ void GetNextWindowPlacement(wxRect *nextRect, bool *pMaximized, bool *pIconized) } } - //Have we hit the bottom of the screen? + // Have we hit the bottom of the screen? bottomRight = nextRect->GetBottomRight(); - if (bottomRight.y > screenRect.GetBottom()) { + if (bottomRight.y > (screenRect.GetBottom()+edgeSlop)) { nextRect->y -= inc; bottomRight = nextRect->GetBottomRight(); - if (bottomRight.y > screenRect.GetBottom()) { + if (bottomRight.y > (screenRect.GetBottom()+edgeSlop)) { nextRect->SetBottom(screenRect.GetBottom()); } } + // After all that we could have a window that does not have a visible + // top bar. [It is unlikely, but something might have gone wrong] + // If so, use the safe fallback size. if (!IsWindowAccessible(nextRect)) { *nextRect = defaultRect; } diff --git a/src/TrackPanel.cpp b/src/TrackPanel.cpp index cbc07845a..4825f35ec 100644 --- a/src/TrackPanel.cpp +++ b/src/TrackPanel.cpp @@ -1167,8 +1167,12 @@ void TrackPanel::HandleInterruptedDrag() WasOverCutLine, IsStretching */ - + // The bogus id isn't used anywhere, but may help with debugging. + // as this is sending a bogus mouse up. The mouse button is still actually down + // and may go up again. + const int idBogusUp = 2; wxMouseEvent evt { wxEVT_LEFT_UP }; + evt.SetId( idBogusUp ); evt.SetPosition(this->ScreenToClient(::wxGetMousePosition())); this->ProcessEvent(evt); } @@ -1888,6 +1892,44 @@ void TrackPanel::SelectRangeOfTracks(Track *sTrack, Track *eTrack) } } +void TrackPanel::ChangeSelectionOnShiftClick(Track * pTrack){ + + // Optional: Track already selected? Nothing to do. + // If we enable this, Shift-Click behaves like click in this case. + //if( pTrack->GetSelected() ) + // return; + + // Find first and last selected track. + Track* pFirst = nullptr; + Track* pLast = nullptr; + // We will either extend from the first or from the last. + Track* pExtendFrom= nullptr; + + TrackListIterator iter(GetTracks()); + for (Track *t = iter.First(); t; t = iter.Next()) { + const bool isSelected = t->GetSelected(); + // If our track is after the first, extend from the first. + if( t == pTrack ){ + pExtendFrom = pFirst; + } + // Record first and last selected. + if( isSelected ){ + if( !pFirst ) + pFirst = t; + pLast = t; + } + } + // Our track was the first or earlier. Extend from the last. + if( !pExtendFrom ) + pExtendFrom = pLast; + + SelectNone(); + if( pExtendFrom ) + SelectRangeOfTracks(pTrack, pExtendFrom); + else + SelectTrack( pTrack, true ); +} + /// This method gets called when we're handling selection /// and the mouse was just clicked. void TrackPanel::SelectionHandleClick(wxMouseEvent & event, @@ -1923,17 +1965,24 @@ void TrackPanel::SelectionHandleClick(wxMouseEvent & event, bool stretch = HitTestStretch(pTrack, rect, event); #endif - if (event.ShiftDown() + bool bShiftDown = event.ShiftDown(); + bool bCtrlDown = event.ControlDown(); + if (bShiftDown || bCtrlDown #ifdef USE_MIDI && !stretch #endif ) { - SelectNone(); - if (mLastPickedTrack && event.ShiftDown()) - SelectRangeOfTracks(pTrack, mLastPickedTrack); - else - SelectTrack(pTrack, true); + + if( bShiftDown ) + ChangeSelectionOnShiftClick( pTrack ); + if( bCtrlDown ){ + //bool bIsSelected = pTrack->GetSelected(); + bool bIsSelected = false; + // could set bIsSelected true here, but toggling is more technically correct. + // if we want to match behaviour in Track Control Panel. + SelectTrack( pTrack, !bIsSelected, false ); + } double value; // Shift-click, choose closest boundary @@ -2722,7 +2771,8 @@ void TrackPanel::SelectionHandleDrag(wxMouseEvent & event, Track *clickedTrack) if (event.CmdDown()) { // Ctrl-drag has no meaning, fuhggeddaboudit - return; + // JKC YES it has meaning. + //return; } wxRect rect = mCapturedRect; @@ -3022,7 +3072,10 @@ void TrackPanel::HandleEnvelope(wxMouseEvent & event) if (mCapturedTrack) ForwardEventToEnvelope(event); - if (event.LeftUp()) { + // We test for IsEnveloping, because we could have had our action stopped already, + // and already recorded and the second mouse up is bogus. + // e.g could be stopped by some key press. Bug 1496. + if ((mMouseCapture == IsEnveloping ) && event.LeftUp()) { SetCapturedTrack( NULL ); MakeParentPushState( /* i18n-hint: (verb) Audacity has just adjusted the envelope .*/ @@ -4977,7 +5030,7 @@ void TrackPanel::HandleListSelection(Track *t, bool shift, bool ctrl, } else { if (shift && mLastPickedTrack) - SelectRangeOfTracks(t, mLastPickedTrack); + ChangeSelectionOnShiftClick( t ); else{ SelectNone(); SelectTrack(t, true); @@ -6207,8 +6260,8 @@ bool TrackPanel::HandleTrackLocationMouseEvent(WaveTrack * track, wxRect &rect, bool bShift = event.ShiftDown(); bool bCtrlDown = event.ControlDown(); bool unsafe = IsUnsafe(); - - if( bShift || bCtrlDown ){ + bCtrlDown = false; + if( /*bShift ||*/ bCtrlDown ){ HandleListSelection(track, bShift, bCtrlDown, !unsafe); return true; @@ -6284,7 +6337,7 @@ bool TrackPanel::HandleLabelTrackClick(LabelTrack * lTrack, wxRect &rect, wxMous bool bCtrlDown = event.ControlDown(); bool unsafe = IsUnsafe(); - if( bShift || bCtrlDown ){ + if( /*bShift ||*/ bCtrlDown ){ HandleListSelection(lTrack, bShift, bCtrlDown, !unsafe); return true; diff --git a/src/TrackPanel.h b/src/TrackPanel.h index 78e19a9fa..dc010a7e2 100644 --- a/src/TrackPanel.h +++ b/src/TrackPanel.h @@ -296,6 +296,7 @@ class AUDACITY_DLL_API TrackPanel final : public OverlayPanel { protected: + virtual void ChangeSelectionOnShiftClick(Track * pTrack); virtual void SelectionHandleClick(wxMouseEvent &event, Track* pTrack, wxRect rect); virtual void StartSelection (int mouseXCoordinate, int trackLeftEdge); diff --git a/src/WaveClip.cpp b/src/WaveClip.cpp index 47913e04d..ccd1c1771 100644 --- a/src/WaveClip.cpp +++ b/src/WaveClip.cpp @@ -43,7 +43,6 @@ #include "Experimental.h" -//#undef _OPENMP #ifdef _OPENMP #include #else @@ -930,8 +929,18 @@ bool SpecCache::CalculateOneSpectrum int correctedX = (floor(0.5 + xx + timeCorrection * pixelsPerSecond / rate)); if (correctedX >= lowerBoundX && correctedX < upperBoundX) - result = true, - out[half * correctedX + bin] += power; + { + result = true; + + int index = half * correctedX + bin; +#ifdef _OPENMP + // This assignment can race if index reaches into another thread's bins. + // The probability of a race very low, so this carries little overhead, + // about 5% slower vs allowing it to race. + #pragma omp atomic update +#endif + out[index] += power; + } } } } @@ -990,29 +999,6 @@ void SpecCache::Populate if (!autocorrelation) ComputeSpectrogramGainFactors(fftLen, rate, frequencyGain, gainFactors); -#ifdef _OPENMP - // todo: query # of threads or make it a setting - const int numThreads = 8; - omp_set_num_threads(numThreads); - - // We need certain per-thread data for thread safety - // Assumes WaveTrackCache is reentrant since it takes a const* to WaveTrack - struct { - WaveTrackCache* cache; - float* scratch; - } threadLocalStorage[numThreads]; - - // May as well use existing data for one of the threads - assert(numThreads > 0); - threadLocalStorage[0].cache = &waveTrackCache; - threadLocalStorage[0].scratch = &scratch[0]; - - for (int i = 1; i < numThreads; i++) { - threadLocalStorage[i].cache = new WaveTrackCache( waveTrackCache.GetTrack() ); - threadLocalStorage[i].scratch = new float[scratchSize]; - } -#endif - // Loop over the ranges before and after the copied portion and compute anew. // One of the ranges may be empty. for (int jj = 0; jj < 2; ++jj) { @@ -1020,24 +1006,36 @@ void SpecCache::Populate const int upperBoundX = jj == 0 ? copyBegin : numPixels; #ifdef _OPENMP - #pragma omp parallel for + // Storage for mutable per-thread data. + // private clause ensures one copy per thread + struct ThreadLocalStorage { + ThreadLocalStorage() { cache = nullptr; } + ~ThreadLocalStorage() { delete cache; } + + void init(WaveTrackCache &waveTrackCache, size_t scratchSize) { + if (!cache) { + cache = new WaveTrackCache(waveTrackCache.GetTrack()); + scratch.resize(scratchSize); + } + } + WaveTrackCache* cache; + std::vector scratch; + } tls; + + #pragma omp parallel for private(tls) #endif for (auto xx = lowerBoundX; xx < upperBoundX; ++xx) { #ifdef _OPENMP - int threadNum = omp_get_thread_num(); - - assert(threadNum >=0 && threadNum < numThreads); - - WaveTrackCache* cache = threadLocalStorage[threadNum].cache; - float* buffer = threadLocalStorage[threadNum].scratch; + tls.init(waveTrackCache, scratchSize); + WaveTrackCache& cache = *tls.cache; + float* buffer = &tls.scratch[0]; #else - WaveTrackCache* cache = &waveTrackCache; + WaveTrackCache& cache = waveTrackCache; float* buffer = &scratch[0]; #endif - CalculateOneSpectrum( - settings, *cache, xx, numSamples, + settings, cache, xx, numSamples, offset, rate, pixelsPerSecond, lowerBoundX, upperBoundX, gainFactors, buffer, &freq[0]); @@ -1098,14 +1096,6 @@ void SpecCache::Populate } } } - -#ifdef _OPENMP - for (int i = 1; i < numThreads; i++) - { - delete[] threadLocalStorage[i].scratch; - delete threadLocalStorage[i].cache; - } -#endif } bool WaveClip::GetSpectrogram(WaveTrackCache &waveTrackCache,