21#include "../playabletrack/wavetrack/ui/PitchAndSpeedDialog.h"
26#include "../../ProjectSettings.h"
27#include "../../ProjectWindow.h"
28#include "../../RefreshCode.h"
29#include "../../SelectUtilities.h"
32#include "../../TrackArt.h"
33#include "../../TrackArtist.h"
35#include "../../TrackPanel.h"
36#include "../../TrackPanelDrawingContext.h"
37#include "../../TrackPanelMouseEvent.h"
43#include "../../../images/Cursors.h"
75 float minFreq, maxFreq;
79 return trackTopEdge + wxInt64((1.0 - p) * trackHeight);
86 wxInt64 mouseYCoordinate,
90 const double rate = wc.
GetRate();
101 float minFreq, maxFreq;
104 const double p = double(mouseYCoordinate - trackTopEdge) / trackHeight;
133 const double t0,
const double t1,
135 double selend,
bool onlyWithinSnapDistance,
136 wxInt64 *pPixelDist,
double *pPinValue)
140 wxInt64 pixelDist = std::abs(posS - pos0);
141 bool chooseLeft =
true;
146 chooseLeft = (selend < t0);
149 const wxInt64 rightDist = std::abs(posS - pos1);
150 if (rightDist < pixelDist)
151 chooseLeft =
false, pixelDist = rightDist;
156 if (onlyWithinSnapDistance &&
161 else if (chooseLeft) {
173 wxCoord xx, wxCoord yy,
const ChannelView &channelView,
175 bool mayDragWidth,
bool onlyWithinSnapDistance,
176 double *pPinValue = NULL)
184 wxInt64 pixelDist = 0;
190 &pixelDist, pPinValue);
199 bool chooseTime =
true;
200 bool chooseBottom =
true;
201 bool chooseCenter =
false;
205 t0 <= selend && selend < t1 &&
211 const wxInt64 bottomSel = (f0 >= 0)
213 : rect.y + rect.height;
214 const wxInt64 topSel = (f1 >= 0)
217 wxInt64 signedBottomDist = (int)(yy - bottomSel);
218 wxInt64 verticalDist = std::abs(signedBottomDist);
219 if (bottomSel == topSel)
221 chooseBottom = (signedBottomDist >= 0);
223 const wxInt64 topDist = std::abs((
int)(yy - topSel));
224 if (topDist < verticalDist)
225 chooseBottom =
false, verticalDist = topDist;
228#ifdef SPECTRAL_EDITING_ESC_KEY
232 const wxInt64 centerSel =
234 const wxInt64 centerDist = abs((
int)(yy - centerSel));
235 if (centerDist < verticalDist)
236 chooseCenter =
true, verticalDist = centerDist,
239 if (verticalDist >= 0 &&
240 verticalDist < pixelDist) {
241 pixelDist = verticalDist;
249 if (onlyWithinSnapDistance &&
254 else if (chooseCenter) {
258 else if (mayDragWidth && fc > 0) {
262 else if (chooseBottom) {
279 static auto selectCursor =
281 return &*selectCursor;
287 static auto envelopeCursor =
289 return &*envelopeCursor;
297 static auto adjustLeftSelectionCursor =
298 ::MakeCursor(wxCURSOR_POINT_LEFT, SelectionLeftXpm, 16, 16);
299 static auto adjustRightSelectionCursor =
300 ::MakeCursor(wxCURSOR_POINT_RIGHT, SelectionRightXpm, 16, 16);
301 static auto bottomFrequencyCursor =
302 ::MakeCursor(wxCURSOR_ARROW, BottomFrequencyCursorXpm, 16, 16);
303 static auto topFrequencyCursor =
304 ::MakeCursor(wxCURSOR_ARROW, TopFrequencyCursorXpm, 16, 16);
305 static auto bandWidthCursor =
306 ::MakeCursor(wxCURSOR_ARROW, BandWidthCursorXpm, 16, 16);
313 tip =
XO(
"Click and drag to move left selection boundary.");
314 pCursor = &*adjustLeftSelectionCursor;
317 tip =
XO(
"Click and drag to move right selection boundary.");
318 pCursor = &*adjustRightSelectionCursor;
321 tip =
XO(
"Click and drag to move bottom selection frequency.");
322 pCursor = &*bottomFrequencyCursor;
325 tip =
XO(
"Click and drag to move top selection frequency.");
326 pCursor = &*topFrequencyCursor;
330#ifndef SPECTRAL_EDITING_ESC_KEY
333 XO(
"Click and drag to move center selection frequency to a spectral peak.") :
334 XO(
"Click and drag to move center selection frequency.");
340 XO(
"Click and drag to move center selection frequency.");
348 tip =
XO(
"Click and drag to adjust frequency bandwidth.");
349 pCursor = &*bandWidthCursor;
359(std::weak_ptr<SelectHandle> &holder,
361 const std::shared_ptr<ChannelView> &pChannelView)
365 auto old = holder.lock();
366 bool oldUseSnap =
true;
369 if( old->mTimerHandler ) {
373 old->mTimerHandler.reset();
375 oldUseSnap = old->mUseSnap;
379 auto result = std::make_shared<SelectHandle>(
380 pChannelView, oldUseSnap,
TrackList::Get(*pProject), st, viewInfo);
385 auto pTrack =
FindTrack(pChannelView->FindChannel().get());
386 if (!pTrack->GetSelected())
392 const wxRect &rect = st.
rect;
393 wxInt64 leftSel = viewInfo.TimeToPosition(viewInfo.selectedRegion.t0(), rect.x);
394 wxInt64 rightSel = viewInfo.TimeToPosition(viewInfo.selectedRegion.t1(), rect.x);
396 wxASSERT(!(rightSel < leftSel));
397 static_cast<void>(leftSel);
398 static_cast<void>(rightSel);
409 wxASSERT( useSnap == newState.
mUseSnap );
415 if ( oldSnapState.Snapped() == newSnapState.Snapped() &&
416 (!oldSnapState.Snapped() ||
417 oldSnapState.outCoord == newSnapState.outCoord) )
424 const std::shared_ptr<ChannelView> &pChannelView,
bool useSnap,
427) : mpView{ pChannelView }
431 SnapPoint{ viewInfo.playRegion.GetLastActiveStart() },
432 SnapPoint{ viewInfo.playRegion.GetLastActiveEnd() },
435 const wxMouseState &state = st.
state;
439 auto pTrack =
FindTrack(pChannelView->FindChannel().get());
455 if (
const auto pView =
mpView.lock())
456 return pView->FindChannel();
481 template <
class A,
class B,
class DIST >
bool within(
A a, B b, DIST d)
483 return (a > b - d) && (a < b + d);
488 const double minFrequency = 1.0;
489 const double maxFrequency = (rate / 2.0);
490 const double frequency =
492 std::max(minFrequency, center));
494 std::min(frequency / minFrequency, maxFrequency / frequency);
548 const auto pView =
mpView.lock();
553 wxMouseEvent &
event = evt.
event;
564 bool selectChange = (
566 event.ControlDown() &&
570 bool bShift =
event.ShiftDown();
573 *pProject, *pTrack, bShift,
true, !unsafe);
582 if (event.LeftDClick() && !event.ShiftDown()) {
584 selectionState.SelectNone(trackList);
587 selectionState.SelectTrack(*pTrack,
true,
true);
595 if (
const auto pWc = view.FindChannel<
WaveChannel>()) {
597 auto time = viewInfo.PositionToTime(event.m_x,
mRect.x);
598 const auto selectedClip =
601 viewInfo.selectedRegion.setTimes(
602 selectedClip->GetPlayStartTime(), selectedClip->GetPlayEndTime());
611 else if (!event.LeftDown())
617 std::make_shared<SelectionStateChanger>(selectionState, trackList);
621 bool bShiftDown =
event.ShiftDown();
622 bool bCtrlDown =
event.ControlDown();
628 if (bShiftDown || bCtrlDown) {
631 selectionState.ChangeSelectionOnShiftClick(trackList, *pTrack);
637 bool bIsSelected =
false;
639 if (!bIsSelected || trackPanel.GetSelectedTrackCount() > 1)
641 selectionState.SelectTrack(*pTrack, !bIsSelected,
true);
648 view,
mRect,
false,
false, &value);
707 bool startNewSelection =
true;
708 if (pTrack && pTrack->GetSelected()) {
740 view,
mRect,
true,
true, &value);
748 startNewSelection =
false;
758 startNewSelection =
false;
779 startNewSelection =
false;
790 if (startNewSelection) {
792 selectionState.SelectNone(trackList);
797 selectionState.SelectTrack(*pTrack,
true,
true);
814 const auto pView =
mpView.lock();
820 const wxMouseEvent &
event = evt.
event;
835 if (event.CmdDown()) {
842 const auto pChannel = view.FindChannel();
851 enum { minimumSizedSelection = 5 };
857 if (wxLongLong(SelStart - x).Abs() < minimumSizedSelection)
862 if (
auto clickedTrack =
866 Track *eTrack = clickedTrack.get();
868 if ( sTrack && eTrack && !event.ControlDown() ) {
870 selectionState.SelectRangeOfTracks( trackList, *sTrack, *eTrack );
873 #ifndef SPECTRAL_EDITING_ESC_KEY
875 !viewInfo.selectedRegion.isPoint())
881 ; pWaveChannel == pChannel
910 const auto pView =
mpView.lock();
915 const auto pChannel = view.FindChannel();
932 auto &state = st.
state;
934 auto xx = viewInfo.TimeToPosition(time,
mRect.x);
936 const bool bMultiToolMode =
941 if (bMultiToolMode) {
950 keyStr =
_(
"Edit, Preferences...");
953 tip =
XO(
"Multi-Tool Mode: %s for Mouse and Keyboard Preferences.")
956 if (!pTrack->GetSelected())
959 const wxRect &rect = st.
rect;
960 const bool bShiftDown = state.ShiftDown();
961 const bool bCtrlDown = state.ControlDown();
962 const bool bModifierDown = bShiftDown || bCtrlDown;
969 view, rect, !bModifierDown, !bModifierDown);
982 tip =
XO(
"Click and drag to set frequency bandwidth.");
983 pCursor = &*envelopeCursor;
988 if (!pTrack->GetSelected())
991 const wxRect &rect = st.
rect;
992 const bool bShiftDown = state.ShiftDown();
993 const bool bCtrlDown = state.ControlDown();
994 const bool bModifierDown = bShiftDown || bCtrlDown;
996 viewInfo, xx, state.m_y,
997 view, rect, !bModifierDown, !bModifierDown);
1002 tip =
XO(
"Click and drag to select audio");
1007 XO(
"(snapping)"),
wxT(
" ")
1010 return { tip, pCursor };
1042 const wxRect &rect,
unsigned iPass )
1045 auto &dc = context.
dc;
1057 const wxRect &rect,
const wxRect &panelRect,
unsigned iPass )
1067 mTimerHandler = std::make_shared<TimerHandler>(
this, pProject );
1115 viewport.OnScrollRight();
1119 viewport.OnScrollLeft();
1127 trackPanel.ClientToScreen(&xx, &yy);
1130 viewport.OnScrollLeft();
1134 ::wxDisplaySize(&width, &height);
1135 if (xx == width - 1) {
1137 viewport.OnScrollRight();
1149 wxMouseEvent evt(wxEVT_MOTION);
1150 const auto size = trackPanel.GetSize();
1179 ViewInfo &viewInfo,
int mouseXCoordinate,
int trackLeftEdge,
1187 std::max(0.0, viewInfo.
PositionToTime(mouseXCoordinate, trackLeftEdge));
1188 double origSelend = selend;
1193 ?
dynamic_cast<Track*
>(&sChannel->GetChannelGroup())
1215 selend = origSelend;
1238 int mouseYCoordinate,
int trackTopEdge,
1252 trackTopEdge, trackHeight);
1259 int mouseYCoordinate,
int trackTopEdge,
1269 const double rate = wc.
GetRate();
1270 const double frequency =
1272 trackTopEdge, trackHeight);
1276 if (frequency == rate || frequency < 1.0)
1286 frequency / ratio, frequency * ratio);
1292 if (frequency == rate || frequency < 1.0)
1304 ratio = 1.0 / ratio;
1313 const bool bottomDefined =
1315 const bool topDefined =
1317 if (!bottomDefined || (topDefined &&
mFreqSelPin < frequency)) {
1319 if (frequency == rate)
1329 if (frequency < 1.0)
1341 bool shiftDown,
const std::shared_ptr<const WaveChannel> &pWc,
double value)
1351#ifndef SPECTRAL_EDITING_ESC_KEY
1366 static const size_t minLength = 8;
1368 const double rate = wc.
GetRate();
1371 std::vector<float> frequencySnappingData;
1377 std::min(frequencySnappingData.max_size(),
1380 const auto effectiveLength = std::max(minLength, length);
1381 frequencySnappingData.resize(effectiveLength, 0.0f);
1383 &frequencySnappingData[0],
1392 auto windowSize =
settings.GetFFTLength();
1394 while(windowSize > effectiveLength)
1396 const int windowType =
settings.windowType;
1400 &frequencySnappingData[0], length);
1424 const double rate = wc.GetRate();
1425 const double frequency =
1427 trackTopEdge, trackHeight);
1428 const double snappedFrequency =
1430 const double maxRatio =
findMaxRatio(snappedFrequency, rate);
1435 if (f1 >= f0 && f0 >= 0)
1437 ratio =
sqrt(f1 / f0);
1443 snappedFrequency / ratio, snappedFrequency * ratio);
1460 const auto windowSize =
settings.GetFFTLength();
1461 const double rate = wc.
GetRate();
1462 const double nyq = rate / 2.0;
1463 const double binFrequency = rate / windowSize;
1467 if (centerFrequency <= 0) {
1468 centerFrequency = up ? binFrequency : nyq;
1469 f1 = centerFrequency *
sqrt(2.0);
1472 double ratio = f1 / centerFrequency;
1473 const int originalBin = floor(0.5 + centerFrequency / binFrequency);
1474 const int limitingBin = up ? floor(0.5 + nyq / binFrequency) : 1;
1480 double snappedFrequency = centerFrequency;
1481 int bin = originalBin;
1483 while (snappedFrequency <= centerFrequency &&
1485 snappedFrequency = analyst.
FindPeak(++bin * binFrequency, NULL);
1488 while (snappedFrequency >= centerFrequency &&
1490 snappedFrequency = analyst.
FindPeak(--bin * binFrequency, NULL);
1494 const double maxRatio =
findMaxRatio(snappedFrequency, rate);
1498 (snappedFrequency / ratio, snappedFrequency * ratio);
1503void SelectHandle::ResetFreqSelectionPin
1504 (
const ViewInfo &viewInfo,
double hintFrequency,
bool logF)
1506 switch (mFreqSelMode) {
1521 if (f0 >= 0 && f1 >= 0)
1541 const double logf1 = log(std::max(1.0, f1));
1542 const double logf0 = log(std::max(1.0, f0));
1543 const double logHint = log(std::max(1.0, hintFrequency));
1544 if (std::abs(logHint - logf1) < std::abs(logHint - logf0))
1552 std::abs(hintFrequency - f1) < std::abs(hintFrequency - f0))
std::shared_ptr< UIHandle > UIHandlePtr
size_t limitSampleBufferSize(size_t bufferSize, sampleCount limit)
@ SELECTION_RESIZE_REGION
std::vector< SnapPoint > SnapPointArray
static Settings & settings()
std::unique_ptr< wxCursor > MakeCursor(int WXUNUSED(CursorId), const char *const pXpm[36], int HotX, int HotY)
bool within(A a, B b, DIST d)
std::shared_ptr< Subclass > AssignUIHandlePtr(std::weak_ptr< Subclass > &holder, const std::shared_ptr< Subclass > &pNew)
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
ChannelGroup & GetChannelGroup()
Channel object's lifetime is assumed to be nested in its Track's.
static ChannelView & Get(Channel &channel)
virtual bool IsSpectral() const
static CommandManager & Get(AudacityProject &project)
NormalizedKeyString GetKeyFromName(const CommandID &name) const
auto FindChannel() -> std::shared_ptr< Subtype >
May return null.
std::shared_ptr< Track > FindTrack()
A LabelTrack is a Track that holds labels (LabelStruct).
bool setTimes(double t0, double t1)
bool setF0(double f, bool maySwap=true)
bool setFrequencies(double f0, double f1)
bool setF1(double f, bool maySwap=true)
float PositionToValue(float pp) const
float ValueToPosition(float val) const
Subscription Subscribe(Callback callback)
Connect a callback to the Publisher; later-connected are called earlier.
A move-only handle representing a connection to a Publisher.
static PitchAndSpeedDialog & Get(AudacityProject &project)
void TryRetarget(const TrackPanelMouseEvent &event)
bool IsAudioActive() const
static ProjectAudioIO & Get(AudacityProject &project)
void ModifyState(bool bWantsAutoSave)
static ProjectHistory & Get(AudacityProject &project)
static ProjectSettings & Get(AudacityProject &project)
PlaybackScroller & GetPlaybackScroller()
static ProjectWindow & Get(AudacityProject &project)
void OnTimer(Observer::Message)
TimerHandler(SelectHandle *pParent, AudacityProject *pProject)
AudacityProject * mConnectedProject
Observer::Subscription mSubscription
SelectHandle(const SelectHandle &)
void StartFreqSelection(ViewInfo &viewInfo, int mouseYCoordinate, int trackTopEdge, int trackHeight, ChannelView &channelView)
std::weak_ptr< const WaveChannel > mFreqSelTrack
Result Cancel(AudacityProject *) override
void AssignSelection(ViewInfo &viewInfo, double selend)
wxRect DrawingArea(TrackPanelDrawingContext &, const wxRect &rect, const wxRect &panelRect, unsigned iPass) override
std::shared_ptr< Channel > FindChannel()
void MoveSnappingFreqSelection(AudacityProject *pProject, ViewInfo &viewInfo, int mouseYCoordinate, int trackTopEdge, int trackHeight, ChannelView &channelView, Track *pTrack)
void HandleCenterFrequencyClick(const ViewInfo &viewInfo, bool shiftDown, const std::shared_ptr< const WaveChannel > &pWc, double value)
static void StartSnappingFreqSelection(SpectrumAnalyst &analyst, const ViewInfo &viewInfo, const WaveChannel &wc)
Result Release(const TrackPanelMouseEvent &event, AudacityProject *pProject, wxWindow *pParent) override
void Enter(bool forward, AudacityProject *pProject) override
void SetUseSnap(bool use, AudacityProject *pProject)
void AdjustFreqSelection(const WaveChannel &wc, ViewInfo &viewInfo, int mouseYCoordinate, int trackTopEdge, int trackHeight)
HitTestPreview Preview(const TrackPanelMouseState &state, AudacityProject *pProject) override
bool IsDragging() const override
bool HasEscape(AudacityProject *pProject) const override
Result Click(const TrackPanelMouseEvent &event, AudacityProject *pProject) override
static void SnapCenterOnce(SpectrumAnalyst &analyst, ViewInfo &viewInfo, const WaveChannel &wc, bool up)
std::shared_ptr< TimerHandler > mTimerHandler
std::shared_ptr< SelectionStateChanger > mSelectionStateChanger
Result Drag(const TrackPanelMouseEvent &event, AudacityProject *pProject) override
std::weak_ptr< ChannelView > mpView
static UIHandlePtr HitTest(std::weak_ptr< SelectHandle > &holder, const TrackPanelMouseState &state, const AudacityProject *pProject, const std::shared_ptr< ChannelView > &pChannelView)
std::shared_ptr< SpectrumAnalyst > mFrequencySnapper
void AdjustSelection(AudacityProject *pProject, ViewInfo &viewInfo, int mouseXCoordinate, int trackLeftEdge, Track *pTrack)
Extend or contract the existing selection.
bool Escape(AudacityProject *pProject) override
std::shared_ptr< SnapManager > mSnapManager
std::shared_ptr< const Track > FindTrack() const override
enum SelectHandle::eFreqSelMode FREQ_SEL_INVALID
@ FREQ_SEL_SNAPPING_CENTER
static UIHandle::Result NeedChangeHighlight(const SelectHandle &oldState, const SelectHandle &newState)
void Draw(TrackPanelDrawingContext &context, const wxRect &rect, unsigned iPass) override
SelectedRegion mInitialSelection
void Connect(AudacityProject *pProject)
void StartSelection(AudacityProject *pProject)
Reset our selection markers.
static const int UndefinedFrequency
static void SelectTrackLength(ViewInfo &viewInfo, Track &track, bool syncLocked)
static SelectionState & Get(AudacityProject &project)
void GetBounds(const WaveChannel &wc, float &min, float &max) const
static SpectrogramBounds & Get(WaveTrack &track)
Get either the global default settings, or the track's own if previously created.
bool SpectralSelectionEnabled() const
static SpectrogramSettings & Get(const WaveTrack &track)
Used for finding the peaks, for snapping to peaks.
float FindPeak(float xPos, float *pY) const
bool Calculate(Algorithm alg, int windowFunc, size_t windowSize, double rate, const float *data, size_t dataLen, float *pYMin=NULL, float *pYMax=NULL, ProgressFn progress=NULL)
bool IsSyncLocked() const
static SyncLockState & Get(AudacityProject &project)
Abstract base class for an object holding data associated with points on a time axis.
bool GetSelected() const
Selectedness is always the same for all channels of a group.
A flat linked list of tracks supporting Add, Remove, Clear, and Contains, serialization of the list o...
static TrackList & Get(AudacityProject &project)
static wxRect MaximizeHeight(const wxRect &rect, const wxRect &panelRect)
void Refresh(bool eraseBackground=true, const wxRect *rect=(const wxRect *) NULL) override
static TrackPanel & Get(AudacityProject &project)
Holds a msgid for the translation catalog; may also bind format arguments.
TranslatableString & Join(TranslatableString arg, const wxString &separator={}) &
Append another translatable string.
static std::shared_ptr< const Track > TrackFromChannel(const std::shared_ptr< const Channel > &pChannel)
A frequent convenience in the definition of UIHandles.
NotifyingSelectedRegion selectedRegion
static ViewInfo & Get(AudacityProject &project)
static Viewport & Get(AudacityProject &project)
double GetRate() const override
bool GetFloats(float *buffer, sampleCount start, size_t len, fillFormat fill=FillFormat::fillZero, bool mayThrow=true, sampleCount *pNumWithinClips=nullptr) const
"narrow" overload fetches from the unique channel
sampleCount TimeToLongSamples(double t0) const
double PositionToTime(int64 position, int64 origin=0, bool ignoreFisheye=false) const
int64 TimeToPosition(double time, int64 origin=0, bool ignoreFisheye=false) const
STM: Converts a project time to screen x position.
Namespace containing an enum 'what to do on a refresh?'.
void DoListSelection(AudacityProject &project, Track &t, bool shift, bool ctrl, bool modifyState)
AUDACITY_DLL_API void DrawSnapLines(wxDC *dc, wxInt64 snap0, wxInt64 snap1)
WAVE_TRACK_API ClipPointer GetIntervalAtTime(WaveChannel &channel, double t)
wxCursor * EnvelopeCursor()
wxInt64 FrequencyToPosition(const WaveChannel &wc, double frequency, wxInt64 trackTopEdge, int trackHeight)
Converts a frequency to screen y position.
double findMaxRatio(double center, double rate)
void SetTipAndCursorForBoundary(SelectionBoundary boundary, bool frequencySnapping, TranslatableString &tip, wxCursor *&pCursor)
void SetIfNotNull(T *pValue, const T Value)
SelectionBoundary ChooseTimeBoundary(const double t0, const double t1, const ViewInfo &viewInfo, double selend, bool onlyWithinSnapDistance, wxInt64 *pPixelDist, double *pPinValue)
double PositionToFrequency(const WaveChannel &wc, bool maySnap, wxInt64 mouseYCoordinate, wxInt64 trackTopEdge, int trackHeight)
SelectionBoundary ChooseBoundary(const ViewInfo &viewInfo, wxCoord xx, wxCoord yy, const ChannelView &channelView, const wxRect &rect, bool mayDragWidth, bool onlyWithinSnapDistance, double *pPinValue=NULL)
wxCursor * SelectCursor()
bool isSpectralSelectionView(const ChannelView &channelView)
const char * end(const char *str) noexcept
__finl float_x4 __vecc sqrt(const float_x4 &a)
wxString Display(bool usesSpecialChars=false) const
Default message type for Publisher.
std::shared_ptr< TrackPanelCell > pCell