Demote vertical zooming code into WaveTrack.cpp ...

... Freeing WaveTrackVRulerControls and WaveTrackVZoomHandle from the big
s.c.c., to higher levels, though still in a cycle of two
This commit is contained in:
Paul Licameli 2019-06-07 10:10:58 -04:00
parent e969315c90
commit 1f4bf262b1
6 changed files with 317 additions and 327 deletions

View File

@ -63,10 +63,6 @@ Track classes.
#include "InconsistencyException.h"
#include "TrackPanel.h" // for TrackInfo
// Assumptions in objects separation were wrong. We need to activate
// VZooming (that lives in WaveTrackVRulerHandle) from an action on the
// TCP collapse/expand. So we need visibility here.
#include "tracks/playabletrack/wavetrack/ui/WaveTrackVRulerControls.h"
using std::max;
@ -493,15 +489,7 @@ void WaveTrack::DoSetMinimized(bool isMinimized){
gPrefs->Read(wxT("/GUI/CollapseToHalfWave"), &bHalfWave, false);
if( bHalfWave )
{
// Show half wave on collapse, full on restore.
std::shared_ptr<TrackVRulerControls> pTvc = GetVRulerControl();
// An awkward workaround for a function that lives 'in the wrong place'.
// We use magic numbers, 0 and 1, to tell it to zoom reset or zoom half-wave.
WaveTrackVRulerControls * pWtvc =
static_cast<WaveTrackVRulerControls*>(pTvc.get());
if( pWtvc )
pWtvc->DoZoomPreset( isMinimized ? 1:0);
DoZoomPreset( isMinimized ? 1:0);
}
#endif
@ -2871,3 +2859,298 @@ void WaveTrack::AllClipsIterator::push( WaveClipHolders &clips )
pClips = &(*first)->GetCutLines();
}
}
void WaveTrack::DoZoomPreset( int i)
{
// Don't do all channels, that causes problems when updating display
// during recording and there are special pending tracks.
// This function implements WaveTrack::DoSetMinimized which is always
// called in a context that loops over linked tracks too and reinvokes.
WaveTrack::DoZoom(
NULL, this, false, (i==1)?kZoomHalfWave: kZoom1to1,
wxRect(0,0,0,0), 0,0, true);
}
#include "NumberScale.h"
#include "ProjectHistory.h"
static bool IsDragZooming(int zoomStart, int zoomEnd)
{
const int DragThreshold = 3;// Anything over 3 pixels is a drag, else a click.
bool bVZoom;
gPrefs->Read(wxT("/GUI/VerticalZooming"), &bVZoom, false);
return bVZoom && (abs(zoomEnd - zoomStart) > DragThreshold);
}
// ZoomKind says how to zoom.
// If ZoomStart and ZoomEnd are not equal, this may override
// the zoomKind and cause a drag-zoom-in.
void WaveTrack::DoZoom
(AudacityProject *pProject,
WaveTrack *pTrack, bool allChannels, int ZoomKind,
const wxRect &rect, int zoomStart, int zoomEnd,
bool fixedMousePoint)
{
static const float ZOOMLIMIT = 0.001f;
int height = rect.height;
int ypos = rect.y;
// Ensure start and end are in order (swap if not).
if (zoomEnd < zoomStart)
std::swap( zoomStart, zoomEnd );
float min, max, minBand = 0;
const double rate = pTrack->GetRate();
const float halfrate = rate / 2;
float maxFreq = 8000.0;
const SpectrogramSettings &specSettings = pTrack->GetSpectrogramSettings();
NumberScale scale;
const bool spectral = (pTrack->GetDisplay() == WaveTrack::Spectrum);
const bool spectrumLinear = spectral &&
(pTrack->GetSpectrogramSettings().scaleType == SpectrogramSettings::stLinear);
bool bDragZoom = IsDragZooming(zoomStart, zoomEnd);
// Add 100 if spectral to separate the kinds of zoom.
const int kSpectral = 100;
// Possibly override the zoom kind.
if( bDragZoom )
ZoomKind = kZoomInByDrag;
// If we are actually zooming a spectrum rather than a wave.
ZoomKind += spectral ? kSpectral:0;
float top=2.0;
float half=0.5;
if (spectral) {
pTrack->GetSpectrumBounds(&min, &max);
scale = (specSettings.GetScale(min, max));
const auto fftLength = specSettings.GetFFTLength();
const float binSize = rate / fftLength;
maxFreq = gPrefs->Read(wxT("/Spectrum/MaxFreq"), 8000L);
// JKC: Following discussions of Bug 1208 I'm allowing zooming in
// down to one bin.
// const int minBins =
// std::min(10, fftLength / 2); //minimum 10 freq bins, unless there are less
const int minBins = 1;
minBand = minBins * binSize;
}
else{
pTrack->GetDisplayBounds(&min, &max);
const WaveformSettings &waveSettings = pTrack->GetWaveformSettings();
const bool linear = waveSettings.isLinear();
if( !linear ){
top = (LINEAR_TO_DB(2.0) + waveSettings.dBRange) / waveSettings.dBRange;
half = (LINEAR_TO_DB(0.5) + waveSettings.dBRange) / waveSettings.dBRange;
}
}
// Compute min and max.
switch(ZoomKind)
{
default:
// If we have covered all the cases, this won't happen.
// In release builds Audacity will ignore the zoom.
wxFAIL_MSG("Zooming Case not implemented by Audacity");
break;
case kZoomReset:
case kZoom1to1:
{
// Zoom out full
min = -1.0;
max = 1.0;
}
break;
case kZoomDiv2:
{
// Zoom out even more than full :-)
// -2.0..+2.0 (or logarithmic equivalent)
min = -top;
max = top;
}
break;
case kZoomTimes2:
{
// Zoom in to -0.5..+0.5
min = -half;
max = half;
}
break;
case kZoomHalfWave:
{
// Zoom to show fractionally more than the top half of the wave.
min = -0.01f;
max = 1.0;
}
break;
case kZoomInByDrag:
{
const float tmin = min, tmax = max;
const float p1 = (zoomStart - ypos) / (float)height;
const float p2 = (zoomEnd - ypos) / (float)height;
max = (tmax * (1.0 - p1) + tmin * p1);
min = (tmax * (1.0 - p2) + tmin * p2);
// Waveform view - allow zooming down to a range of ZOOMLIMIT
if (max - min < ZOOMLIMIT) { // if user attempts to go smaller...
float c = (min + max) / 2; // ...set centre of view to centre of dragged area and top/bottom to ZOOMLIMIT/2 above/below
min = c - ZOOMLIMIT / 2.0;
max = c + ZOOMLIMIT / 2.0;
}
}
break;
case kZoomIn:
{
// Enforce maximum vertical zoom
const float oldRange = max - min;
const float l = std::max(ZOOMLIMIT, 0.5f * oldRange);
const float ratio = l / (max - min);
const float p1 = (zoomStart - ypos) / (float)height;
float c = (max * (1.0 - p1) + min * p1);
if (fixedMousePoint)
min = c - ratio * (1.0f - p1) * oldRange,
max = c + ratio * p1 * oldRange;
else
min = c - 0.5 * l,
max = c + 0.5 * l;
}
break;
case kZoomOut:
{
// Zoom out
if (min <= -1.0 && max >= 1.0) {
min = -top;
max = top;
}
else {
// limit to +/- 1 range unless already outside that range...
float minRange = (min < -1) ? -top : -1.0;
float maxRange = (max > 1) ? top : 1.0;
// and enforce vertical zoom limits.
const float p1 = (zoomStart - ypos) / (float)height;
if (fixedMousePoint) {
const float oldRange = max - min;
const float c = (max * (1.0 - p1) + min * p1);
min = std::min(maxRange - ZOOMLIMIT,
std::max(minRange, c - 2 * (1.0f - p1) * oldRange));
max = std::max(minRange + ZOOMLIMIT,
std::min(maxRange, c + 2 * p1 * oldRange));
}
else {
const float c = p1 * min + (1 - p1) * max;
const float l = (max - min);
min = std::min(maxRange - ZOOMLIMIT,
std::max(minRange, c - l));
max = std::max(minRange + ZOOMLIMIT,
std::min(maxRange, c + l));
}
}
}
break;
// VZooming on spectral we don't implement the other zoom presets.
// They are also not in the menu.
case kZoomReset + kSpectral:
{
// Zoom out to normal level.
min = spectrumLinear ? 0.0f : 1.0f;
max = maxFreq;
}
break;
case kZoom1to1 + kSpectral:
case kZoomDiv2 + kSpectral:
case kZoomTimes2 + kSpectral:
case kZoomHalfWave + kSpectral:
{
// Zoom out full
min = spectrumLinear ? 0.0f : 1.0f;
max = halfrate;
}
break;
case kZoomInByDrag + kSpectral:
{
double xmin = 1 - (zoomEnd - ypos) / (float)height;
double xmax = 1 - (zoomStart - ypos) / (float)height;
const float middle = (xmin + xmax) / 2;
const float middleValue = scale.PositionToValue(middle);
min = std::max(spectrumLinear ? 0.0f : 1.0f,
std::min(middleValue - minBand / 2,
scale.PositionToValue(xmin)
));
max = std::min(halfrate,
std::max(middleValue + minBand / 2,
scale.PositionToValue(xmax)
));
}
break;
case kZoomIn + kSpectral:
{
// Center the zoom-in at the click
const float p1 = (zoomStart - ypos) / (float)height;
const float middle = 1.0f - p1;
const float middleValue = scale.PositionToValue(middle);
if (fixedMousePoint) {
min = std::max(spectrumLinear ? 0.0f : 1.0f,
std::min(middleValue - minBand * middle,
scale.PositionToValue(0.5f * middle)
));
max = std::min(halfrate,
std::max(middleValue + minBand * p1,
scale.PositionToValue(middle + 0.5f * p1)
));
}
else {
min = std::max(spectrumLinear ? 0.0f : 1.0f,
std::min(middleValue - minBand / 2,
scale.PositionToValue(middle - 0.25f)
));
max = std::min(halfrate,
std::max(middleValue + minBand / 2,
scale.PositionToValue(middle + 0.25f)
));
}
}
break;
case kZoomOut + kSpectral:
{
// Zoom out
const float p1 = (zoomStart - ypos) / (float)height;
// (Used to zoom out centered at midline, ignoring the click, if linear view.
// I think it is better to be consistent. PRL)
// Center zoom-out at the midline
const float middle = // spectrumLinear ? 0.5f :
1.0f - p1;
if (fixedMousePoint) {
min = std::max(spectrumLinear ? 0.0f : 1.0f, scale.PositionToValue(-middle));
max = std::min(halfrate, scale.PositionToValue(1.0f + p1));
}
else {
min = std::max(spectrumLinear ? 0.0f : 1.0f, scale.PositionToValue(middle - 1.0f));
max = std::min(halfrate, scale.PositionToValue(middle + 1.0f));
}
}
break;
}
// Now actually apply the zoom.
for (auto channel : TrackList::Channels(pTrack)) {
if (!allChannels && channel != pTrack)
continue;
if (spectral)
channel->SetSpectrumBounds(min, max);
else
channel->SetDisplayBounds(min, max);
}
zoomEnd = zoomStart = 0;
if( pProject )
ProjectHistory::Get( *pProject ).ModifyState(true);
}

View File

@ -65,6 +65,17 @@ using Regions = std::vector < Region >;
class Envelope;
// Note that these can be with or without spectrum view which
// adds a constant.
const int kZoom1to1 = 1;
const int kZoomTimes2 = 2;
const int kZoomDiv2 = 3;
const int kZoomHalfWave = 4;
const int kZoomInByDrag = 5;
const int kZoomIn = 6;
const int kZoomOut = 7;
const int kZoomReset = 8;
class AUDACITY_DLL_API WaveTrack final : public PlayableTrack {
public:
@ -552,6 +563,13 @@ private:
//
static void DoZoom
(AudacityProject *pProject,
WaveTrack *pTrack, bool allChannels, int ZoomKind,
const wxRect &rect, int zoomStart, int zoomEnd,
bool fixedMousePoint);
void DoZoomPreset( int i);
typedef int WaveTrackDisplay;
enum WaveTrackDisplayValues : int {

View File

@ -49,25 +49,6 @@ std::vector<UIHandlePtr> WaveTrackVRulerControls::HitTest
return results;
}
void WaveTrackVRulerControls::DoZoomPreset( int i)
{
const auto pTrack = FindTrack();
if (!pTrack)
return;
const auto wt = static_cast<WaveTrack*>(pTrack.get());
// Don't do all channels, that causes problems when updating display
// during recording and there are special pending tracks.
// This function implements WaveTrack::DoSetMinimized which is always
// called in a context that loops over linked tracks too and reinvokes.
WaveTrackVZoomHandle::DoZoom(
NULL, wt, false, (i==1)?kZoomHalfWave: kZoom1to1,
wxRect(0,0,0,0), 0,0, true);
}
unsigned WaveTrackVRulerControls::HandleWheelRotation
(const TrackPanelMouseEvent &evt, AudacityProject *pProject)
{
@ -135,7 +116,7 @@ unsigned WaveTrackVRulerControls::HandleWheelRotation
}
else if (event.CmdDown() && !event.ShiftDown()) {
const int yy = event.m_y;
WaveTrackVZoomHandle::DoZoom(
WaveTrack::DoZoom(
pProject, wt, true, (steps < 0)?kZoomOut:kZoomIn,
evt.rect, yy, yy, true);
}

View File

@ -33,7 +33,6 @@ public:
unsigned HandleWheelRotation
(const TrackPanelMouseEvent &event,
AudacityProject *pProject) override;
void DoZoomPreset( int i);
private:
std::weak_ptr<WaveTrackVZoomHandle> mVZoomHandle;
};

View File

@ -16,7 +16,6 @@ Paul Licameli split from TrackPanel.cpp
#include "WaveTrackVRulerControls.h"
#include "../../../../HitTestResult.h"
#include "../../../../NumberScale.h"
#include "../../../../prefs/SpectrogramSettings.h"
#include "../../../../prefs/WaveformSettings.h"
#include "../../../../Project.h"
@ -63,279 +62,6 @@ void WaveTrackVZoomHandle::Enter(bool)
#endif
}
// ZoomKind says how to zoom.
// If ZoomStart and ZoomEnd are not equal, this may override
// the zoomKind and cause a drag-zoom-in.
void WaveTrackVZoomHandle::DoZoom
(AudacityProject *pProject,
WaveTrack *pTrack, bool allChannels, int ZoomKind,
const wxRect &rect, int zoomStart, int zoomEnd,
bool fixedMousePoint)
{
static const float ZOOMLIMIT = 0.001f;
int height = rect.height;
int ypos = rect.y;
// Ensure start and end are in order (swap if not).
if (zoomEnd < zoomStart)
std::swap( zoomStart, zoomEnd );
float min, max, minBand = 0;
const double rate = pTrack->GetRate();
const float halfrate = rate / 2;
float maxFreq = 8000.0;
const SpectrogramSettings &specSettings = pTrack->GetSpectrogramSettings();
NumberScale scale;
const bool spectral = (pTrack->GetDisplay() == WaveTrack::Spectrum);
const bool spectrumLinear = spectral &&
(pTrack->GetSpectrogramSettings().scaleType == SpectrogramSettings::stLinear);
bool bDragZoom = IsDragZooming(zoomStart, zoomEnd);
// Add 100 if spectral to separate the kinds of zoom.
const int kSpectral = 100;
// Possibly override the zoom kind.
if( bDragZoom )
ZoomKind = kZoomInByDrag;
// If we are actually zooming a spectrum rather than a wave.
ZoomKind += spectral ? kSpectral:0;
float top=2.0;
float half=0.5;
if (spectral) {
pTrack->GetSpectrumBounds(&min, &max);
scale = (specSettings.GetScale(min, max));
const auto fftLength = specSettings.GetFFTLength();
const float binSize = rate / fftLength;
maxFreq = gPrefs->Read(wxT("/Spectrum/MaxFreq"), 8000L);
// JKC: Following discussions of Bug 1208 I'm allowing zooming in
// down to one bin.
// const int minBins =
// std::min(10, fftLength / 2); //minimum 10 freq bins, unless there are less
const int minBins = 1;
minBand = minBins * binSize;
}
else{
pTrack->GetDisplayBounds(&min, &max);
const WaveformSettings &waveSettings = pTrack->GetWaveformSettings();
const bool linear = waveSettings.isLinear();
if( !linear ){
top = (LINEAR_TO_DB(2.0) + waveSettings.dBRange) / waveSettings.dBRange;
half = (LINEAR_TO_DB(0.5) + waveSettings.dBRange) / waveSettings.dBRange;
}
}
// Compute min and max.
switch(ZoomKind)
{
default:
// If we have covered all the cases, this won't happen.
// In release builds Audacity will ignore the zoom.
wxFAIL_MSG("Zooming Case not implemented by Audacity");
break;
case kZoomReset:
case kZoom1to1:
{
// Zoom out full
min = -1.0;
max = 1.0;
}
break;
case kZoomDiv2:
{
// Zoom out even more than full :-)
// -2.0..+2.0 (or logarithmic equivalent)
min = -top;
max = top;
}
break;
case kZoomTimes2:
{
// Zoom in to -0.5..+0.5
min = -half;
max = half;
}
break;
case kZoomHalfWave:
{
// Zoom to show fractionally more than the top half of the wave.
min = -0.01f;
max = 1.0;
}
break;
case kZoomInByDrag:
{
const float tmin = min, tmax = max;
const float p1 = (zoomStart - ypos) / (float)height;
const float p2 = (zoomEnd - ypos) / (float)height;
max = (tmax * (1.0 - p1) + tmin * p1);
min = (tmax * (1.0 - p2) + tmin * p2);
// Waveform view - allow zooming down to a range of ZOOMLIMIT
if (max - min < ZOOMLIMIT) { // if user attempts to go smaller...
float c = (min + max) / 2; // ...set centre of view to centre of dragged area and top/bottom to ZOOMLIMIT/2 above/below
min = c - ZOOMLIMIT / 2.0;
max = c + ZOOMLIMIT / 2.0;
}
}
break;
case kZoomIn:
{
// Enforce maximum vertical zoom
const float oldRange = max - min;
const float l = std::max(ZOOMLIMIT, 0.5f * oldRange);
const float ratio = l / (max - min);
const float p1 = (zoomStart - ypos) / (float)height;
float c = (max * (1.0 - p1) + min * p1);
if (fixedMousePoint)
min = c - ratio * (1.0f - p1) * oldRange,
max = c + ratio * p1 * oldRange;
else
min = c - 0.5 * l,
max = c + 0.5 * l;
}
break;
case kZoomOut:
{
// Zoom out
if (min <= -1.0 && max >= 1.0) {
min = -top;
max = top;
}
else {
// limit to +/- 1 range unless already outside that range...
float minRange = (min < -1) ? -top : -1.0;
float maxRange = (max > 1) ? top : 1.0;
// and enforce vertical zoom limits.
const float p1 = (zoomStart - ypos) / (float)height;
if (fixedMousePoint) {
const float oldRange = max - min;
const float c = (max * (1.0 - p1) + min * p1);
min = std::min(maxRange - ZOOMLIMIT,
std::max(minRange, c - 2 * (1.0f - p1) * oldRange));
max = std::max(minRange + ZOOMLIMIT,
std::min(maxRange, c + 2 * p1 * oldRange));
}
else {
const float c = p1 * min + (1 - p1) * max;
const float l = (max - min);
min = std::min(maxRange - ZOOMLIMIT,
std::max(minRange, c - l));
max = std::max(minRange + ZOOMLIMIT,
std::min(maxRange, c + l));
}
}
}
break;
// VZooming on spectral we don't implement the other zoom presets.
// They are also not in the menu.
case kZoomReset + kSpectral:
{
// Zoom out to normal level.
min = spectrumLinear ? 0.0f : 1.0f;
max = maxFreq;
}
break;
case kZoom1to1 + kSpectral:
case kZoomDiv2 + kSpectral:
case kZoomTimes2 + kSpectral:
case kZoomHalfWave + kSpectral:
{
// Zoom out full
min = spectrumLinear ? 0.0f : 1.0f;
max = halfrate;
}
break;
case kZoomInByDrag + kSpectral:
{
double xmin = 1 - (zoomEnd - ypos) / (float)height;
double xmax = 1 - (zoomStart - ypos) / (float)height;
const float middle = (xmin + xmax) / 2;
const float middleValue = scale.PositionToValue(middle);
min = std::max(spectrumLinear ? 0.0f : 1.0f,
std::min(middleValue - minBand / 2,
scale.PositionToValue(xmin)
));
max = std::min(halfrate,
std::max(middleValue + minBand / 2,
scale.PositionToValue(xmax)
));
}
break;
case kZoomIn + kSpectral:
{
// Center the zoom-in at the click
const float p1 = (zoomStart - ypos) / (float)height;
const float middle = 1.0f - p1;
const float middleValue = scale.PositionToValue(middle);
if (fixedMousePoint) {
min = std::max(spectrumLinear ? 0.0f : 1.0f,
std::min(middleValue - minBand * middle,
scale.PositionToValue(0.5f * middle)
));
max = std::min(halfrate,
std::max(middleValue + minBand * p1,
scale.PositionToValue(middle + 0.5f * p1)
));
}
else {
min = std::max(spectrumLinear ? 0.0f : 1.0f,
std::min(middleValue - minBand / 2,
scale.PositionToValue(middle - 0.25f)
));
max = std::min(halfrate,
std::max(middleValue + minBand / 2,
scale.PositionToValue(middle + 0.25f)
));
}
}
break;
case kZoomOut + kSpectral:
{
// Zoom out
const float p1 = (zoomStart - ypos) / (float)height;
// (Used to zoom out centered at midline, ignoring the click, if linear view.
// I think it is better to be consistent. PRL)
// Center zoom-out at the midline
const float middle = // spectrumLinear ? 0.5f :
1.0f - p1;
if (fixedMousePoint) {
min = std::max(spectrumLinear ? 0.0f : 1.0f, scale.PositionToValue(-middle));
max = std::min(halfrate, scale.PositionToValue(1.0f + p1));
}
else {
min = std::max(spectrumLinear ? 0.0f : 1.0f, scale.PositionToValue(middle - 1.0f));
max = std::min(halfrate, scale.PositionToValue(middle + 1.0f));
}
}
break;
}
// Now actually apply the zoom.
for (auto channel : TrackList::Channels(pTrack)) {
if (!allChannels && channel != pTrack)
continue;
if (spectral)
channel->SetSpectrumBounds(min, max);
else
channel->SetDisplayBounds(min, max);
}
zoomEnd = zoomStart = 0;
if( pProject )
ProjectHistory::Get( *pProject ).ModifyState(true);
}
enum {
OnZoomFitVerticalID = 20000,
OnZoomResetID,
@ -391,7 +117,7 @@ void WaveTrackVRulerMenuTable::InitMenu(Menu *, void *pUserData)
void WaveTrackVRulerMenuTable::OnZoom( int iZoomCode )
{
WaveTrackVZoomHandle::DoZoom
WaveTrack::DoZoom
(::GetActiveProject(), mpData->pTrack, true,
iZoomCode, mpData->rect, mpData->yy, mpData->yy, false);
@ -676,7 +402,7 @@ UIHandle::Result WaveTrackVZoomHandle::Release
if( bVZoom ){
if( shiftDown )
mZoomStart=mZoomEnd;
DoZoom(pProject, pTrack.get(), true,
WaveTrack::DoZoom(pProject, pTrack.get(), true,
shiftDown ? (rightUp ? kZoom1to1 : kZoomOut) : kZoomIn,
mRect, mZoomStart, mZoomEnd, !shiftDown);
}

View File

@ -16,17 +16,6 @@ class WaveTrack;
#include "../../../../UIHandle.h"
// Note that these can be with or without spectrum view which
// adds a constant.
const int kZoom1to1 = 1;
const int kZoomTimes2 = 2;
const int kZoomDiv2 = 3;
const int kZoomHalfWave = 4;
const int kZoomInByDrag = 5;
const int kZoomIn = 6;
const int kZoomOut = 7;
const int kZoomReset = 8;
class WaveTrackVZoomHandle : public UIHandle
{
WaveTrackVZoomHandle(const WaveTrackVZoomHandle&);
@ -38,12 +27,6 @@ public:
WaveTrackVZoomHandle &operator=(const WaveTrackVZoomHandle&) = default;
static void DoZoom
(AudacityProject *pProject,
WaveTrack *pTrack, bool allChannels, int ZoomKind,
const wxRect &rect, int zoomStart, int zoomEnd,
bool fixedMousePoint);
virtual ~WaveTrackVZoomHandle();
std::shared_ptr<WaveTrack> GetTrack() const { return mpTrack.lock(); }