1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-08-01 00:19:27 +02:00

Cut and paste spectrum and waveform vruler controls into new files

This commit is contained in:
Paul Licameli 2019-07-07 16:08:32 -04:00
parent 32ae2e718f
commit 28dcf45bbe
4 changed files with 523 additions and 510 deletions

View File

@ -10,4 +10,197 @@ Paul Licameli split from WaveTrackVRulerControls.cpp
#include "SpectrumVRulerControls.h"
#include "SpectrumVZoomHandle.h"
#include "WaveTrackVRulerControls.h"
#include "../../../../NumberScale.h"
#include "../../../../ProjectHistory.h"
#include "../../../../RefreshCode.h"
#include "../../../../TrackPanelMouseEvent.h"
#include "../../../../WaveTrack.h"
#include "../../../../prefs/SpectrogramSettings.h"
#include "../../../../widgets/Ruler.h"
SpectrumVRulerControls::~SpectrumVRulerControls() = default;
std::vector<UIHandlePtr> SpectrumVRulerControls::HitTest(
const TrackPanelMouseState &st,
const AudacityProject *pProject)
{
std::vector<UIHandlePtr> results;
if ( st.state.GetX() <= st.rect.GetRight() - kGuard ) {
auto pTrack = FindTrack()->SharedPointer<WaveTrack>( );
if (pTrack) {
auto result = std::make_shared<SpectrumVZoomHandle>(
pTrack, st.rect, st.state.m_y );
result = AssignUIHandlePtr(mVZoomHandle, result);
results.push_back(result);
}
}
auto more = TrackVRulerControls::HitTest(st, pProject);
std::copy(more.begin(), more.end(), std::back_inserter(results));
return results;
}
unsigned SpectrumVRulerControls::HandleWheelRotation(
const TrackPanelMouseEvent &evt, AudacityProject *pProject)
{
using namespace RefreshCode;
const auto pTrack = FindTrack();
if (!pTrack)
return RefreshNone;
const auto wt = static_cast<WaveTrack*>(pTrack.get());
return DoHandleWheelRotation( evt, pProject, wt );
}
unsigned SpectrumVRulerControls::DoHandleWheelRotation(
const TrackPanelMouseEvent &evt, AudacityProject *pProject, WaveTrack *wt)
{
using namespace RefreshCode;
const wxMouseEvent &event = evt.event;
if (!(event.ShiftDown() || event.CmdDown()))
return RefreshNone;
// Always stop propagation even if the ruler didn't change. The ruler
// is a narrow enough target.
evt.event.Skip(false);
auto steps = evt.steps;
using namespace WaveTrackViewConstants;
if (event.CmdDown() && !event.ShiftDown()) {
const int yy = event.m_y;
SpectrumVZoomHandle::DoZoom(
pProject, wt,
(steps < 0)
? kZoomOut
: kZoomIn,
evt.rect, yy, yy, true);
}
else if (!event.CmdDown() && event.ShiftDown()) {
// Scroll some fixed number of pixels, independent of zoom level or track height:
static const float movement = 10.0f;
const int height = evt.rect.GetHeight();
{
const float delta = steps * movement / height;
SpectrogramSettings &settings = wt->GetIndependentSpectrogramSettings();
const bool isLinear = settings.scaleType == SpectrogramSettings::stLinear;
float bottom, top;
wt->GetSpectrumBounds(&bottom, &top);
const double rate = wt->GetRate();
const float bound = rate / 2;
const NumberScale numberScale(settings.GetScale(bottom, top));
float newTop =
std::min(bound, numberScale.PositionToValue(1.0f + delta));
const float newBottom =
std::max((isLinear ? 0.0f : 1.0f),
numberScale.PositionToValue(numberScale.ValueToPosition(newTop) - 1.0f));
newTop =
std::min(bound,
numberScale.PositionToValue(numberScale.ValueToPosition(newBottom) + 1.0f));
for (auto channel : TrackList::Channels(wt))
channel->SetSpectrumBounds(newBottom, newTop);
}
}
else
return RefreshNone;
ProjectHistory::Get( *pProject ).ModifyState(true);
return RefreshCell | UpdateVRuler;
}
void SpectrumVRulerControls::Draw(
TrackPanelDrawingContext &context,
const wxRect &rect_, unsigned iPass )
{
TrackVRulerControls::Draw( context, rect_, iPass );
WaveTrackVRulerControls::DoDraw( *this, context, rect_, iPass );
}
void SpectrumVRulerControls::UpdateRuler( const wxRect &rect )
{
const auto wt = std::static_pointer_cast< WaveTrack >( FindTrack() );
if (!wt)
return;
DoUpdateVRuler( rect, wt.get() );
}
void SpectrumVRulerControls::DoUpdateVRuler(
const wxRect &rect, const WaveTrack *wt )
{
auto vruler = &WaveTrackVRulerControls::ScratchRuler();
const SpectrogramSettings &settings = wt->GetSpectrogramSettings();
float minFreq, maxFreq;
wt->GetSpectrumBounds(&minFreq, &maxFreq);
vruler->SetDbMirrorValue( 0.0 );
switch (settings.scaleType) {
default:
wxASSERT(false);
case SpectrogramSettings::stLinear:
{
// Spectrum
if (rect.height < 60)
return;
/*
draw the ruler
we will use Hz if maxFreq is < 2000, otherwise we represent kHz,
and append to the numbers a "k"
*/
vruler->SetBounds(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height - 1);
vruler->SetOrientation(wxVERTICAL);
vruler->SetFormat(Ruler::RealFormat);
vruler->SetLabelEdges(true);
// use kHz in scale, if appropriate
if (maxFreq >= 2000) {
vruler->SetRange((maxFreq / 1000.), (minFreq / 1000.));
vruler->SetUnits(wxT("k"));
}
else {
// use Hz
vruler->SetRange((int)(maxFreq), (int)(minFreq));
vruler->SetUnits(wxT(""));
}
vruler->SetLog(false);
}
break;
case SpectrogramSettings::stLogarithmic:
case SpectrogramSettings::stMel:
case SpectrogramSettings::stBark:
case SpectrogramSettings::stErb:
case SpectrogramSettings::stPeriod:
{
// SpectrumLog
if (rect.height < 10)
return;
/*
draw the ruler
we will use Hz if maxFreq is < 2000, otherwise we represent kHz,
and append to the numbers a "k"
*/
vruler->SetBounds(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height - 1);
vruler->SetOrientation(wxVERTICAL);
vruler->SetFormat(Ruler::IntFormat);
vruler->SetLabelEdges(true);
vruler->SetRange(maxFreq, minFreq);
vruler->SetUnits(wxT(""));
vruler->SetLog(true);
NumberScale scale(
wt->GetSpectrogramSettings().GetScale( minFreq, maxFreq )
.Reversal() );
vruler->SetNumberScale(&scale);
}
break;
}
vruler->GetMaxSize( &wt->vrulerSize.x, &wt->vrulerSize.y );
}

View File

@ -11,23 +11,12 @@ Paul Licameli split from TrackPanel.cpp
#include "../../../../Audacity.h"
#include "WaveTrackVRulerControls.h"
#include "WaveTrackVZoomHandle.h"
#include "SpectrumVZoomHandle.h"
#include "WaveformVZoomHandle.h"
#include "SpectrumVRulerControls.h"
#include "WaveformVRulerControls.h"
#include "SpectrumVZoomHandle.h"
#include "WaveformVZoomHandle.h"
#include "../../../../Experimental.h"
#include "../../../../HitTestResult.h"
#include "../../../../NumberScale.h"
#include "../../../../prefs/SpectrogramSettings.h"
#include "../../../../prefs/WaveformSettings.h"
#include "../../../../ProjectHistory.h"
#include "../../../../RefreshCode.h"
#include "../../../../TrackPanelMouseEvent.h"
#include "../../../../WaveTrack.h"
@ -43,50 +32,6 @@ WaveTrackVRulerControls::~WaveTrackVRulerControls()
{
}
std::vector<UIHandlePtr> SpectrumVRulerControls::HitTest(
const TrackPanelMouseState &st,
const AudacityProject *pProject)
{
std::vector<UIHandlePtr> results;
if ( st.state.GetX() <= st.rect.GetRight() - kGuard ) {
auto pTrack = FindTrack()->SharedPointer<WaveTrack>( );
if (pTrack) {
auto result = std::make_shared<SpectrumVZoomHandle>(
pTrack, st.rect, st.state.m_y );
result = AssignUIHandlePtr(mVZoomHandle, result);
results.push_back(result);
}
}
auto more = TrackVRulerControls::HitTest(st, pProject);
std::copy(more.begin(), more.end(), std::back_inserter(results));
return results;
}
std::vector<UIHandlePtr> WaveformVRulerControls::HitTest(
const TrackPanelMouseState &st,
const AudacityProject *pProject)
{
std::vector<UIHandlePtr> results;
if ( st.state.GetX() <= st.rect.GetRight() - kGuard ) {
auto pTrack = FindTrack()->SharedPointer<WaveTrack>( );
if (pTrack) {
auto result = std::make_shared<WaveformVZoomHandle>(
pTrack, st.rect, st.state.m_y );
result = AssignUIHandlePtr(mVZoomHandle, result);
results.push_back(result);
}
}
auto more = TrackVRulerControls::HitTest(st, pProject);
std::copy(more.begin(), more.end(), std::back_inserter(results));
return results;
}
std::vector<UIHandlePtr> WaveTrackVRulerControls::HitTest
(const TrackPanelMouseState &st,
const AudacityProject *pProject)
@ -109,187 +54,6 @@ std::vector<UIHandlePtr> WaveTrackVRulerControls::HitTest
return results;
}
unsigned WaveformVRulerControls::HandleWheelRotation(
const TrackPanelMouseEvent &evt, AudacityProject *pProject)
{
using namespace RefreshCode;
const auto pTrack = FindTrack();
if (!pTrack)
return RefreshNone;
const auto wt = static_cast<WaveTrack*>(pTrack.get());
return DoHandleWheelRotation( evt, pProject, wt );
}
unsigned WaveformVRulerControls::DoHandleWheelRotation(
const TrackPanelMouseEvent &evt, AudacityProject *pProject, WaveTrack *wt)
{
using namespace RefreshCode;
const wxMouseEvent &event = evt.event;
if (!(event.ShiftDown() || event.CmdDown()))
return RefreshNone;
// Always stop propagation even if the ruler didn't change. The ruler
// is a narrow enough target.
evt.event.Skip(false);
auto steps = evt.steps;
using namespace WaveTrackViewConstants;
const bool isDB =
wt->GetWaveformSettings().scaleType == WaveformSettings::stLogarithmic;
// Special cases for Waveform dB only.
// Set the bottom of the dB scale but only if it's visible
if (isDB && event.ShiftDown() && event.CmdDown()) {
float min, max;
wt->GetDisplayBounds(&min, &max);
if (!(min < 0.0 && max > 0.0))
return RefreshNone;
WaveformSettings &settings =
wt->GetIndependentWaveformSettings();
float olddBRange = settings.dBRange;
for (auto channel : TrackList::Channels(wt)) {
WaveformSettings &channelSettings =
channel->GetIndependentWaveformSettings();
if (steps < 0)
// Zoom out
channelSettings.NextLowerDBRange();
else
channelSettings.NextHigherDBRange();
}
float newdBRange = settings.dBRange;
// Is y coordinate within the rectangle half-height centered about
// the zero level?
const auto &rect = evt.rect;
const auto zeroLevel = wt->ZeroLevelYCoordinate(rect);
const bool fixedMagnification =
(4 * std::abs(event.GetY() - zeroLevel) < rect.GetHeight());
if (fixedMagnification) {
// Vary the db limit without changing
// magnification; that is, peaks and troughs move up and down
// rigidly, as parts of the wave near zero are exposed or hidden.
const float extreme = (LINEAR_TO_DB(2) + newdBRange) / newdBRange;
max = std::min(extreme, max * olddBRange / newdBRange);
min = std::max(-extreme, min * olddBRange / newdBRange);
for (auto channel : TrackList::Channels(wt)) {
channel->SetLastdBRange();
channel->SetDisplayBounds(min, max);
}
}
}
else if (event.CmdDown() && !event.ShiftDown()) {
const int yy = event.m_y;
WaveformVZoomHandle::DoZoom(
pProject, wt,
(steps < 0)
? kZoomOut
: kZoomIn,
evt.rect, yy, yy, true);
}
else if (!event.CmdDown() && event.ShiftDown()) {
// Scroll some fixed number of pixels, independent of zoom level or track height:
static const float movement = 10.0f;
const int height = evt.rect.GetHeight();
{
float topLimit = 2.0;
if (isDB) {
const float dBRange = wt->GetWaveformSettings().dBRange;
topLimit = (LINEAR_TO_DB(topLimit) + dBRange) / dBRange;
}
const float bottomLimit = -topLimit;
float top, bottom;
wt->GetDisplayBounds(&bottom, &top);
const float range = top - bottom;
const float delta = range * steps * movement / height;
float newTop = std::min(topLimit, top + delta);
const float newBottom = std::max(bottomLimit, newTop - range);
newTop = std::min(topLimit, newBottom + range);
for (auto channel : TrackList::Channels(wt))
channel->SetDisplayBounds(newBottom, newTop);
}
}
else
return RefreshNone;
ProjectHistory::Get( *pProject ).ModifyState(true);
return RefreshCell | UpdateVRuler;
}
unsigned SpectrumVRulerControls::HandleWheelRotation(
const TrackPanelMouseEvent &evt, AudacityProject *pProject)
{
using namespace RefreshCode;
const auto pTrack = FindTrack();
if (!pTrack)
return RefreshNone;
const auto wt = static_cast<WaveTrack*>(pTrack.get());
return DoHandleWheelRotation( evt, pProject, wt );
}
unsigned SpectrumVRulerControls::DoHandleWheelRotation(
const TrackPanelMouseEvent &evt, AudacityProject *pProject, WaveTrack *wt)
{
using namespace RefreshCode;
const wxMouseEvent &event = evt.event;
if (!(event.ShiftDown() || event.CmdDown()))
return RefreshNone;
// Always stop propagation even if the ruler didn't change. The ruler
// is a narrow enough target.
evt.event.Skip(false);
auto steps = evt.steps;
using namespace WaveTrackViewConstants;
if (event.CmdDown() && !event.ShiftDown()) {
const int yy = event.m_y;
SpectrumVZoomHandle::DoZoom(
pProject, wt,
(steps < 0)
? kZoomOut
: kZoomIn,
evt.rect, yy, yy, true);
}
else if (!event.CmdDown() && event.ShiftDown()) {
// Scroll some fixed number of pixels, independent of zoom level or track height:
static const float movement = 10.0f;
const int height = evt.rect.GetHeight();
{
const float delta = steps * movement / height;
SpectrogramSettings &settings = wt->GetIndependentSpectrogramSettings();
const bool isLinear = settings.scaleType == SpectrogramSettings::stLinear;
float bottom, top;
wt->GetSpectrumBounds(&bottom, &top);
const double rate = wt->GetRate();
const float bound = rate / 2;
const NumberScale numberScale(settings.GetScale(bottom, top));
float newTop =
std::min(bound, numberScale.PositionToValue(1.0f + delta));
const float newBottom =
std::max((isLinear ? 0.0f : 1.0f),
numberScale.PositionToValue(numberScale.ValueToPosition(newTop) - 1.0f));
newTop =
std::min(bound,
numberScale.PositionToValue(numberScale.ValueToPosition(newBottom) + 1.0f));
for (auto channel : TrackList::Channels(wt))
channel->SetSpectrumBounds(newBottom, newTop);
}
}
else
return RefreshNone;
ProjectHistory::Get( *pProject ).ModifyState(true);
return RefreshCell | UpdateVRuler;
}
unsigned WaveTrackVRulerControls::HandleWheelRotation
(const TrackPanelMouseEvent &evt, AudacityProject *pProject)
{
@ -304,28 +68,10 @@ unsigned WaveTrackVRulerControls::HandleWheelRotation
return WaveformVRulerControls::DoHandleWheelRotation( evt, pProject, wt );
}
namespace {
Ruler &ruler()
{
static Ruler theRuler;
return theRuler;
}
}
void WaveformVRulerControls::Draw(
TrackPanelDrawingContext &context,
const wxRect &rect_, unsigned iPass )
Ruler &WaveTrackVRulerControls::ScratchRuler()
{
TrackVRulerControls::Draw( context, rect_, iPass );
WaveTrackVRulerControls::DoDraw( *this, context, rect_, iPass );
}
void SpectrumVRulerControls::Draw(
TrackPanelDrawingContext &context,
const wxRect &rect_, unsigned iPass )
{
TrackVRulerControls::Draw( context, rect_, iPass );
WaveTrackVRulerControls::DoDraw( *this, context, rect_, iPass );
static Ruler theRuler;
return theRuler;
}
void WaveTrackVRulerControls::Draw(
@ -340,7 +86,8 @@ void WaveTrackVRulerControls::DoDraw( TrackVRulerControls &controls,
TrackPanelDrawingContext &context,
const wxRect &rect_, unsigned iPass )
{
auto &vruler = ruler();
Ruler &vruler = ScratchRuler();
// Draw on a later pass because the bevel overpaints one pixel
// out of bounds on the bottom
@ -385,258 +132,6 @@ void WaveTrackVRulerControls::DoDraw( TrackVRulerControls &controls,
}
}
void WaveformVRulerControls::UpdateRuler( const wxRect &rect )
{
const auto wt = std::static_pointer_cast< WaveTrack >( FindTrack() );
if (!wt)
return;
DoUpdateVRuler( rect, wt.get() );
}
void WaveformVRulerControls::DoUpdateVRuler(
const wxRect &rect, const WaveTrack *wt )
{
auto vruler = &ruler();
// All waves have a ruler in the info panel
// The ruler needs a bevelled surround.
const float dBRange =
wt->GetWaveformSettings().dBRange;
WaveformSettings::ScaleType scaleType =
wt->GetWaveformSettings().scaleType;
if (scaleType == WaveformSettings::stLinear) {
// Waveform
float min, max;
wt->GetDisplayBounds(&min, &max);
if (wt->GetLastScaleType() != scaleType &&
wt->GetLastScaleType() != -1)
{
// do a translation into the linear space
wt->SetLastScaleType();
wt->SetLastdBRange();
float sign = (min >= 0 ? 1 : -1);
if (min != 0.) {
min = DB_TO_LINEAR(fabs(min) * dBRange - dBRange);
if (min < 0.0)
min = 0.0;
min *= sign;
}
sign = (max >= 0 ? 1 : -1);
if (max != 0.) {
max = DB_TO_LINEAR(fabs(max) * dBRange - dBRange);
if (max < 0.0)
max = 0.0;
max *= sign;
}
wt->SetDisplayBounds(min, max);
}
vruler->SetDbMirrorValue( 0.0 );
vruler->SetBounds(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height - 1);
vruler->SetOrientation(wxVERTICAL);
vruler->SetRange(max, min);
vruler->SetFormat(Ruler::RealFormat);
vruler->SetUnits(wxT(""));
vruler->SetLabelEdges(false);
vruler->SetLog(false);
}
else {
wxASSERT(scaleType == WaveformSettings::stLogarithmic);
scaleType = WaveformSettings::stLogarithmic;
vruler->SetUnits(wxT(""));
float min, max;
wt->GetDisplayBounds(&min, &max);
float lastdBRange;
if (wt->GetLastScaleType() != scaleType &&
wt->GetLastScaleType() != -1)
{
// do a translation into the dB space
wt->SetLastScaleType();
wt->SetLastdBRange();
float sign = (min >= 0 ? 1 : -1);
if (min != 0.) {
min = (LINEAR_TO_DB(fabs(min)) + dBRange) / dBRange;
if (min < 0.0)
min = 0.0;
min *= sign;
}
sign = (max >= 0 ? 1 : -1);
if (max != 0.) {
max = (LINEAR_TO_DB(fabs(max)) + dBRange) / dBRange;
if (max < 0.0)
max = 0.0;
max *= sign;
}
wt->SetDisplayBounds(min, max);
}
else if (dBRange != (lastdBRange = wt->GetLastdBRange())) {
wt->SetLastdBRange();
// Remap the max of the scale
float newMax = max;
// This commented out code is problematic.
// min and max may be correct, and this code cause them to change.
#ifdef ONLY_LABEL_POSITIVE
const float sign = (max >= 0 ? 1 : -1);
if (max != 0.) {
// Ugh, duplicating from TrackPanel.cpp
#define ZOOMLIMIT 0.001f
const float extreme = LINEAR_TO_DB(2);
// recover dB value of max
const float dB = std::min(extreme, (float(fabs(max)) * lastdBRange - lastdBRange));
// find NEW scale position, but old max may get trimmed if the db limit rises
// Don't trim it to zero though, but leave max and limit distinct
newMax = sign * std::max(ZOOMLIMIT, (dBRange + dB) / dBRange);
// Adjust the min of the scale if we can,
// so the db Limit remains where it was on screen, but don't violate extremes
if (min != 0.)
min = std::max(-extreme, newMax * min / max);
}
#endif
wt->SetDisplayBounds(min, newMax);
}
// Old code was if ONLY_LABEL_POSITIVE were defined.
// it uses the +1 to 0 range only.
// the enabled code uses +1 to -1, and relies on set ticks labelling knowing about
// the dB scale.
#ifdef ONLY_LABEL_POSITIVE
if (max > 0) {
#endif
int top = 0;
float topval = 0;
int bot = rect.height;
float botval = -dBRange;
#ifdef ONLY_LABEL_POSITIVE
if (min < 0) {
bot = top + (int)((max / (max - min))*(bot - top));
min = 0;
}
if (max > 1) {
top += (int)((max - 1) / (max - min) * (bot - top));
max = 1;
}
if (max < 1 && max > 0)
topval = -((1 - max) * dBRange);
if (min > 0) {
botval = -((1 - min) * dBRange);
}
#else
topval = -((1 - max) * dBRange);
botval = -((1 - min) * dBRange);
vruler->SetDbMirrorValue( dBRange );
#endif
vruler->SetBounds(rect.x, rect.y + top, rect.x + rect.width, rect.y + bot - 1);
vruler->SetOrientation(wxVERTICAL);
vruler->SetRange(topval, botval);
#ifdef ONLY_LABEL_POSITIVE
}
else
vruler->SetBounds(0.0, 0.0, 0.0, 0.0); // A.C.H I couldn't find a way to just disable it?
#endif
vruler->SetFormat(Ruler::RealLogFormat);
vruler->SetLabelEdges(true);
vruler->SetLog(false);
}
vruler->GetMaxSize( &wt->vrulerSize.x, &wt->vrulerSize.y );
}
void SpectrumVRulerControls::UpdateRuler( const wxRect &rect )
{
const auto wt = std::static_pointer_cast< WaveTrack >( FindTrack() );
if (!wt)
return;
DoUpdateVRuler( rect, wt.get() );
}
void SpectrumVRulerControls::DoUpdateVRuler(
const wxRect &rect, const WaveTrack *wt )
{
auto vruler = &ruler();
const SpectrogramSettings &settings = wt->GetSpectrogramSettings();
float minFreq, maxFreq;
wt->GetSpectrumBounds(&minFreq, &maxFreq);
vruler->SetDbMirrorValue( 0.0 );
switch (settings.scaleType) {
default:
wxASSERT(false);
case SpectrogramSettings::stLinear:
{
// Spectrum
if (rect.height < 60)
return;
/*
draw the ruler
we will use Hz if maxFreq is < 2000, otherwise we represent kHz,
and append to the numbers a "k"
*/
vruler->SetBounds(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height - 1);
vruler->SetOrientation(wxVERTICAL);
vruler->SetFormat(Ruler::RealFormat);
vruler->SetLabelEdges(true);
// use kHz in scale, if appropriate
if (maxFreq >= 2000) {
vruler->SetRange((maxFreq / 1000.), (minFreq / 1000.));
vruler->SetUnits(wxT("k"));
}
else {
// use Hz
vruler->SetRange((int)(maxFreq), (int)(minFreq));
vruler->SetUnits(wxT(""));
}
vruler->SetLog(false);
}
break;
case SpectrogramSettings::stLogarithmic:
case SpectrogramSettings::stMel:
case SpectrogramSettings::stBark:
case SpectrogramSettings::stErb:
case SpectrogramSettings::stPeriod:
{
// SpectrumLog
if (rect.height < 10)
return;
/*
draw the ruler
we will use Hz if maxFreq is < 2000, otherwise we represent kHz,
and append to the numbers a "k"
*/
vruler->SetBounds(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height - 1);
vruler->SetOrientation(wxVERTICAL);
vruler->SetFormat(Ruler::IntFormat);
vruler->SetLabelEdges(true);
vruler->SetRange(maxFreq, minFreq);
vruler->SetUnits(wxT(""));
vruler->SetLog(true);
NumberScale scale(
wt->GetSpectrogramSettings().GetScale( minFreq, maxFreq )
.Reversal() );
vruler->SetNumberScale(&scale);
}
break;
}
vruler->GetMaxSize( &wt->vrulerSize.x, &wt->vrulerSize.y );
}
void WaveTrackVRulerControls::UpdateRuler( const wxRect &rect )
{
const auto wt = std::static_pointer_cast< WaveTrack >( FindTrack() );

View File

@ -19,6 +19,8 @@ class WaveTrackVZoomHandle;
class WaveTrackVRulerControls final : public TrackVRulerControls
{
static Ruler &ScratchRuler();
WaveTrackVRulerControls(const WaveTrackVRulerControls&) = delete;
WaveTrackVRulerControls &operator=(const WaveTrackVRulerControls&) = delete;

View File

@ -10,4 +10,327 @@ Paul Licameli split from WaveTrackVRulerControls.cpp
#include "WaveformVRulerControls.h"
#include "WaveformVZoomHandle.h"
#include "WaveTrackVRulerControls.h"
#include "../../../../NumberScale.h"
#include "../../../../ProjectHistory.h"
#include "../../../../RefreshCode.h"
#include "../../../../TrackPanelMouseEvent.h"
#include "../../../../UIHandle.h"
#include "../../../../WaveTrack.h"
#include "../../../../prefs/WaveformSettings.h"
#include "../../../../widgets/Ruler.h"
WaveformVRulerControls::~WaveformVRulerControls() = default;
std::vector<UIHandlePtr> WaveformVRulerControls::HitTest(
const TrackPanelMouseState &st,
const AudacityProject *pProject)
{
std::vector<UIHandlePtr> results;
if ( st.state.GetX() <= st.rect.GetRight() - kGuard ) {
auto pTrack = FindTrack()->SharedPointer<WaveTrack>( );
if (pTrack) {
auto result = std::make_shared<WaveformVZoomHandle>(
pTrack, st.rect, st.state.m_y );
result = AssignUIHandlePtr(mVZoomHandle, result);
results.push_back(result);
}
}
auto more = TrackVRulerControls::HitTest(st, pProject);
std::copy(more.begin(), more.end(), std::back_inserter(results));
return results;
}
unsigned WaveformVRulerControls::HandleWheelRotation(
const TrackPanelMouseEvent &evt, AudacityProject *pProject)
{
using namespace RefreshCode;
const auto pTrack = FindTrack();
if (!pTrack)
return RefreshNone;
const auto wt = static_cast<WaveTrack*>(pTrack.get());
return DoHandleWheelRotation( evt, pProject, wt );
}
unsigned WaveformVRulerControls::DoHandleWheelRotation(
const TrackPanelMouseEvent &evt, AudacityProject *pProject, WaveTrack *wt)
{
using namespace RefreshCode;
const wxMouseEvent &event = evt.event;
if (!(event.ShiftDown() || event.CmdDown()))
return RefreshNone;
// Always stop propagation even if the ruler didn't change. The ruler
// is a narrow enough target.
evt.event.Skip(false);
auto steps = evt.steps;
using namespace WaveTrackViewConstants;
const bool isDB =
wt->GetWaveformSettings().scaleType == WaveformSettings::stLogarithmic;
// Special cases for Waveform dB only.
// Set the bottom of the dB scale but only if it's visible
if (isDB && event.ShiftDown() && event.CmdDown()) {
float min, max;
wt->GetDisplayBounds(&min, &max);
if (!(min < 0.0 && max > 0.0))
return RefreshNone;
WaveformSettings &settings =
wt->GetIndependentWaveformSettings();
float olddBRange = settings.dBRange;
for (auto channel : TrackList::Channels(wt)) {
WaveformSettings &channelSettings =
channel->GetIndependentWaveformSettings();
if (steps < 0)
// Zoom out
channelSettings.NextLowerDBRange();
else
channelSettings.NextHigherDBRange();
}
float newdBRange = settings.dBRange;
// Is y coordinate within the rectangle half-height centered about
// the zero level?
const auto &rect = evt.rect;
const auto zeroLevel = wt->ZeroLevelYCoordinate(rect);
const bool fixedMagnification =
(4 * std::abs(event.GetY() - zeroLevel) < rect.GetHeight());
if (fixedMagnification) {
// Vary the db limit without changing
// magnification; that is, peaks and troughs move up and down
// rigidly, as parts of the wave near zero are exposed or hidden.
const float extreme = (LINEAR_TO_DB(2) + newdBRange) / newdBRange;
max = std::min(extreme, max * olddBRange / newdBRange);
min = std::max(-extreme, min * olddBRange / newdBRange);
for (auto channel : TrackList::Channels(wt)) {
channel->SetLastdBRange();
channel->SetDisplayBounds(min, max);
}
}
}
else if (event.CmdDown() && !event.ShiftDown()) {
const int yy = event.m_y;
WaveformVZoomHandle::DoZoom(
pProject, wt,
(steps < 0)
? kZoomOut
: kZoomIn,
evt.rect, yy, yy, true);
}
else if (!event.CmdDown() && event.ShiftDown()) {
// Scroll some fixed number of pixels, independent of zoom level or track height:
static const float movement = 10.0f;
const int height = evt.rect.GetHeight();
{
float topLimit = 2.0;
if (isDB) {
const float dBRange = wt->GetWaveformSettings().dBRange;
topLimit = (LINEAR_TO_DB(topLimit) + dBRange) / dBRange;
}
const float bottomLimit = -topLimit;
float top, bottom;
wt->GetDisplayBounds(&bottom, &top);
const float range = top - bottom;
const float delta = range * steps * movement / height;
float newTop = std::min(topLimit, top + delta);
const float newBottom = std::max(bottomLimit, newTop - range);
newTop = std::min(topLimit, newBottom + range);
for (auto channel : TrackList::Channels(wt))
channel->SetDisplayBounds(newBottom, newTop);
}
}
else
return RefreshNone;
ProjectHistory::Get( *pProject ).ModifyState(true);
return RefreshCell | UpdateVRuler;
}
void WaveformVRulerControls::Draw(
TrackPanelDrawingContext &context,
const wxRect &rect_, unsigned iPass )
{
TrackVRulerControls::Draw( context, rect_, iPass );
WaveTrackVRulerControls::DoDraw( *this, context, rect_, iPass );
}
void WaveformVRulerControls::UpdateRuler( const wxRect &rect )
{
const auto wt = std::static_pointer_cast< WaveTrack >( FindTrack() );
if (!wt)
return;
DoUpdateVRuler( rect, wt.get() );
}
void WaveformVRulerControls::DoUpdateVRuler(
const wxRect &rect, const WaveTrack *wt )
{
auto vruler = &WaveTrackVRulerControls::ScratchRuler();
// All waves have a ruler in the info panel
// The ruler needs a bevelled surround.
const float dBRange =
wt->GetWaveformSettings().dBRange;
WaveformSettings::ScaleType scaleType =
wt->GetWaveformSettings().scaleType;
if (scaleType == WaveformSettings::stLinear) {
// Waveform
float min, max;
wt->GetDisplayBounds(&min, &max);
if (wt->GetLastScaleType() != scaleType &&
wt->GetLastScaleType() != -1)
{
// do a translation into the linear space
wt->SetLastScaleType();
wt->SetLastdBRange();
float sign = (min >= 0 ? 1 : -1);
if (min != 0.) {
min = DB_TO_LINEAR(fabs(min) * dBRange - dBRange);
if (min < 0.0)
min = 0.0;
min *= sign;
}
sign = (max >= 0 ? 1 : -1);
if (max != 0.) {
max = DB_TO_LINEAR(fabs(max) * dBRange - dBRange);
if (max < 0.0)
max = 0.0;
max *= sign;
}
wt->SetDisplayBounds(min, max);
}
vruler->SetDbMirrorValue( 0.0 );
vruler->SetBounds(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height - 1);
vruler->SetOrientation(wxVERTICAL);
vruler->SetRange(max, min);
vruler->SetFormat(Ruler::RealFormat);
vruler->SetUnits(wxT(""));
vruler->SetLabelEdges(false);
vruler->SetLog(false);
}
else {
wxASSERT(scaleType == WaveformSettings::stLogarithmic);
scaleType = WaveformSettings::stLogarithmic;
vruler->SetUnits(wxT(""));
float min, max;
wt->GetDisplayBounds(&min, &max);
float lastdBRange;
if (wt->GetLastScaleType() != scaleType &&
wt->GetLastScaleType() != -1)
{
// do a translation into the dB space
wt->SetLastScaleType();
wt->SetLastdBRange();
float sign = (min >= 0 ? 1 : -1);
if (min != 0.) {
min = (LINEAR_TO_DB(fabs(min)) + dBRange) / dBRange;
if (min < 0.0)
min = 0.0;
min *= sign;
}
sign = (max >= 0 ? 1 : -1);
if (max != 0.) {
max = (LINEAR_TO_DB(fabs(max)) + dBRange) / dBRange;
if (max < 0.0)
max = 0.0;
max *= sign;
}
wt->SetDisplayBounds(min, max);
}
else if (dBRange != (lastdBRange = wt->GetLastdBRange())) {
wt->SetLastdBRange();
// Remap the max of the scale
float newMax = max;
// This commented out code is problematic.
// min and max may be correct, and this code cause them to change.
#ifdef ONLY_LABEL_POSITIVE
const float sign = (max >= 0 ? 1 : -1);
if (max != 0.) {
// Ugh, duplicating from TrackPanel.cpp
#define ZOOMLIMIT 0.001f
const float extreme = LINEAR_TO_DB(2);
// recover dB value of max
const float dB = std::min(extreme, (float(fabs(max)) * lastdBRange - lastdBRange));
// find NEW scale position, but old max may get trimmed if the db limit rises
// Don't trim it to zero though, but leave max and limit distinct
newMax = sign * std::max(ZOOMLIMIT, (dBRange + dB) / dBRange);
// Adjust the min of the scale if we can,
// so the db Limit remains where it was on screen, but don't violate extremes
if (min != 0.)
min = std::max(-extreme, newMax * min / max);
}
#endif
wt->SetDisplayBounds(min, newMax);
}
// Old code was if ONLY_LABEL_POSITIVE were defined.
// it uses the +1 to 0 range only.
// the enabled code uses +1 to -1, and relies on set ticks labelling knowing about
// the dB scale.
#ifdef ONLY_LABEL_POSITIVE
if (max > 0) {
#endif
int top = 0;
float topval = 0;
int bot = rect.height;
float botval = -dBRange;
#ifdef ONLY_LABEL_POSITIVE
if (min < 0) {
bot = top + (int)((max / (max - min))*(bot - top));
min = 0;
}
if (max > 1) {
top += (int)((max - 1) / (max - min) * (bot - top));
max = 1;
}
if (max < 1 && max > 0)
topval = -((1 - max) * dBRange);
if (min > 0) {
botval = -((1 - min) * dBRange);
}
#else
topval = -((1 - max) * dBRange);
botval = -((1 - min) * dBRange);
vruler->SetDbMirrorValue( dBRange );
#endif
vruler->SetBounds(rect.x, rect.y + top, rect.x + rect.width, rect.y + bot - 1);
vruler->SetOrientation(wxVERTICAL);
vruler->SetRange(topval, botval);
#ifdef ONLY_LABEL_POSITIVE
}
else
vruler->SetBounds(0.0, 0.0, 0.0, 0.0); // A.C.H I couldn't find a way to just disable it?
#endif
vruler->SetFormat(Ruler::RealLogFormat);
vruler->SetLabelEdges(true);
vruler->SetLog(false);
}
vruler->GetMaxSize( &wt->vrulerSize.x, &wt->vrulerSize.y );
}