Audacity 3.2.0
PlayIndicatorOverlay.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3Audacity: A Digital Audio Editor
4
5PlayIndicatorOverlay.cpp
6
7Paul Licameli split from TrackPanel.cpp
8
9**********************************************************************/
10
11
13
14#include "AColor.h"
15#include "../../AdornedRulerPanel.h"
16#include "AllThemeResources.h"
17#include "../../AudioIO.h"
18#include "../../LabelTrack.h"
19#include "Project.h"
20#include "../../ProjectAudioIO.h"
21#include "../../ProjectAudioManager.h"
22#include "../../ProjectWindow.h"
23#include "Theme.h"
24#include "Track.h"
25#include "../../TrackPanel.h"
26#include "ViewInfo.h"
27#include "Scrubbing.h"
28#include "TrackView.h"
29
30#include <wx/dc.h>
31
32#include <algorithm>
33
34namespace {
35 template < class LOW, class MID, class HIGH >
36 bool between_incexc(LOW l, MID m, HIGH h)
37 {
38 return (m >= l && m < h);
39 }
40
41 enum { IndicatorMediumWidth = 13 };
42}
43
45: mProject(project)
46, mIsMaster(isMaster)
47{
48}
49
51{
52}
53
55{
56 return 10;
57}
58
59namespace {
60// Returns the appropriate bitmap, and panel-relative coordinates for its
61// upper left corner.
62std::pair< wxPoint, wxBitmap > GetIndicatorBitmap( AudacityProject &project,
63 wxCoord xx, bool playing)
64{
65 bool pinned = Scrubber::Get( project ).IsTransportingPinned();
66 wxBitmap & bmp = theTheme.Bitmap( pinned ?
67 (playing ? bmpPlayPointerPinned : bmpRecordPointerPinned) :
68 (playing ? bmpPlayPointer : bmpRecordPointer)
69 );
70 const int IndicatorHalfWidth = bmp.GetWidth() / 2;
71 return {
72 { xx - IndicatorHalfWidth - 1,
74 bmp
75 };
76}
77}
78
79std::pair<wxRect, bool> PlayIndicatorOverlayBase::DoGetRectangle(wxSize size)
80{
81 wxCoord width = 1, xx = mLastIndicatorX;
82
83 if ( !mIsMaster ) {
84 auto gAudioIO = AudioIO::Get();
85 bool rec = gAudioIO->IsCapturing();
86 auto pair = GetIndicatorBitmap( *mProject, xx, !rec );
87 xx = pair.first.x;
88 width = pair.second.GetWidth();
89 }
90
91 // May be excessive height, but little matter
92 wxRect rect( xx, 0, width, size.GetHeight());
93 return {
94 rect,
97 };
98}
99
100
102{
103 // Set play/record color
104 auto gAudioIO = AudioIO::Get();
105 bool rec = gAudioIO->IsCapturing();
106 AColor::IndicatorColor(&dc, !rec);
107
108 if (mIsMaster
110 // Detect transition to recording during punch and roll; make ruler
111 // change its button color too
113 ruler.UpdateButtonStates();
114 ruler.Refresh();
115 }
117
119 if (!between_incexc(0, mLastIndicatorX, dc.GetSize().GetWidth()))
120 return;
121
122 if(auto tp = dynamic_cast<TrackPanel*>(&panel)) {
123 wxASSERT(mIsMaster);
124
125 AColor::Line(dc, mLastIndicatorX, tp->GetRect().GetTop(), mLastIndicatorX, tp->GetRect().GetBottom());
126 }
127 else if(auto ruler = dynamic_cast<AdornedRulerPanel*>(&panel)) {
128 wxASSERT(!mIsMaster);
129
130 auto pair = GetIndicatorBitmap( *mProject, mLastIndicatorX, !rec );
131 dc.DrawBitmap( pair.second, pair.first.x, pair.first.y );
132 }
133 else
134 wxASSERT(false);
135}
136
138 []( AudacityProject &parent ){
139 auto result = std::make_shared< PlayIndicatorOverlay >( &parent );
140 TrackPanel::Get( parent ).AddOverlay( result );
141 return result;
142 }
143};
144
146: PlayIndicatorOverlayBase(project, true)
147{
150}
151
153{
154 // Ensure that there is an overlay attached to the ruler
155 if (!mPartner) {
157 mPartner = std::make_shared<PlayIndicatorOverlayBase>(mProject, false);
158 ruler.AddOverlay( mPartner );
159 }
160
161 const auto &viewInfo = ViewInfo::Get( *mProject );
162 auto width = viewInfo.GetTracksUsableWidth();
163
164 if (!ProjectAudioIO::Get( *mProject ).IsAudioActive()) {
165 mNewIndicatorX = -1;
166 mNewIsCapturing = false;
167 const auto &scrubber = Scrubber::Get( *mProject );
168 if (scrubber.HasMark()) {
169 auto position = scrubber.GetScrubStartPosition();
170 const auto offset = viewInfo.GetLeftOffset();
171 if(position >= viewInfo.GetLeftOffset() &&
172 position < offset + width)
173 mNewIndicatorX = position;
174 }
175 }
176 else {
177 auto &window = ProjectWindow::Get( *mProject );
178 auto &scroller = window.GetPlaybackScroller();
179 // Calculate the horizontal position of the indicator
180 const double playPos = scroller.GetRecentStreamTime();
181
183 const Mode mode = scroller.GetMode();
184 const bool pinned = ( mode == Mode::Pinned || mode == Mode::Right );
185
186 // Use a small tolerance to avoid flicker of play head pinned all the way
187 // left or right
188 const auto tolerance = pinned
189 ? 1.5 * std::chrono::duration<double>{kTimerInterval}.count()
190 : 0;
191 bool onScreen = playPos >= 0.0 &&
192 between_incexc(viewInfo.h - tolerance,
193 playPos,
194 viewInfo.GetScreenEndTime() + tolerance);
195
196 auto gAudioIO = AudioIO::Get();
197 const auto &scrubber = Scrubber::Get( *mProject );
198
199 // BG: Scroll screen if option is set
200 if( viewInfo.bUpdateTrackIndicator &&
201 playPos >= 0 && !onScreen ) {
202 // msmeyer: But only if not playing looped or in one-second mode
203 // PRL: and not scrolling with play/record head fixed
204 auto mode = ProjectAudioManager::Get( *mProject ).GetLastPlayMode();
205 if (!pinned &&
206 mode != PlayMode::oneSecondPlay &&
207 !gAudioIO->IsPaused() &&
208 // Bug 2656 allow scrolling when paused in
209 // scrubbing/play-at-speed.
210 // ONLY do this additional test if scrubbing/play-at-speed
211 // is active.
212 (!scrubber.IsScrubbing() || !scrubber.IsPaused())
213 )
214 {
215 auto newPos = playPos;
216 if (playPos < viewInfo.h) {
217 // This is possible when scrubbing backwards.
218 // We want to page leftward by (at least) a whole screen, not
219 // just a little bit equal to the scrubbing poll interval
220 // duration.
221 newPos = viewInfo.OffsetTimeByPixels( newPos, -width );
222 newPos = std::max( newPos, window.ScrollingLowerBoundTime() );
223 }
224 window.TP_ScrollWindow(newPos);
225 // Might yet be off screen, check it
226 onScreen = playPos >= 0.0 &&
227 between_incexc(viewInfo.h,
228 playPos,
229 viewInfo.GetScreenEndTime());
230 }
231 }
232
233 // Always update scrollbars even if not scrolling the window. This is
234 // important when NEW audio is recorded, because this can change the
235 // length of the project and therefore the appearance of the scrollbar.
236 window.TP_RedrawScrollbars();
237
238 if (onScreen)
240 viewInfo.TimeToPosition(playPos, viewInfo.GetLeftOffset());
241 else
242 mNewIndicatorX = -1;
243
244 mNewIsCapturing = gAudioIO->IsCapturing();
245 }
246
247 if(mPartner)
248 mPartner->Update(mNewIndicatorX);
249}
@ IndicatorMediumWidth
static const AudacityProject::AttachedObjects::RegisteredFactory sOverlayKey
THEME_API Theme theTheme
Definition: Theme.cpp:82
constexpr auto kTimerInterval
declares abstract base class Track, TrackList, and iterators over TrackList
static void IndicatorColor(wxDC *dc, bool bIsNotRecording)
Definition: AColor.cpp:449
static void Line(wxDC &dc, wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2)
Definition: AColor.cpp:187
This is an Audacity Specific ruler panel which additionally has border, selection markers,...
wxRect GetInnerRect() const
static AdornedRulerPanel & Get(AudacityProject &project)
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:89
static AudioIO * Get()
Definition: AudioIO.cpp:133
Client code makes static instance from a factory of attachments; passes it to Get or Find as a retrie...
Definition: ClientData.h:266
Subscription Subscribe(Callback callback)
Connect a callback to the Publisher; later-connected are called earlier.
Definition: Observer.h:199
void AddOverlay(const std::weak_ptr< Overlay > &pOverlay)
void Draw(OverlayPanel &panel, wxDC &dc) override
std::pair< wxRect, bool > DoGetRectangle(wxSize size) override
PlayIndicatorOverlayBase(AudacityProject *project, bool isMaster)
unsigned SequenceNumber() const override
This number determines an ordering of overlays, so that those with higher numbers overpaint those wit...
AudacityProject *const mProject
Observer::Subscription mSubscription
PlayIndicatorOverlay(AudacityProject *project)
std::shared_ptr< PlayIndicatorOverlayBase > mPartner
void OnTimer(Observer::Message)
bool IsAudioActive() const
static ProjectAudioIO & Get(AudacityProject &project)
static ProjectAudioManager & Get(AudacityProject &project)
PlayMode GetLastPlayMode() const
PlaybackScroller & GetPlaybackScroller()
static ProjectWindow & Get(AudacityProject &project)
static Scrubber & Get(AudacityProject &project)
Definition: Scrubbing.cpp:187
bool IsTransportingPinned() const
wxBitmap & Bitmap(int iIndex)
The TrackPanel class coordinates updates and operations on the main part of the screen which contains...
Definition: TrackPanel.h:65
static TrackPanel & Get(AudacityProject &project)
Definition: TrackPanel.cpp:230
static ViewInfo & Get(AudacityProject &project)
Definition: ViewInfo.cpp:235
std::pair< wxPoint, wxBitmap > GetIndicatorBitmap(AudacityProject &project, wxCoord xx, bool playing)
Default message type for Publisher.
Definition: Observer.h:26