1 /**********************************************************************
2 
3   Audacity: A Digital Audio Editor
4 
5   MeterPanel.h
6 
7   Dominic Mazzoni
8 
9   VU Meter, for displaying recording/playback level
10 
11   This is a bunch of common code that can display many different
12   forms of VU meters and other displays.
13 
14 **********************************************************************/
15 
16 #ifndef __AUDACITY_METER_PANEL__
17 #define __AUDACITY_METER_PANEL__
18 
19 #include <wx/setup.h> // for wxUSE_* macros
20 #include <wx/brush.h> // member variable
21 #include <wx/defs.h>
22 #include <wx/timer.h> // member variable
23 
24 #include "SampleFormat.h"
25 #include "Prefs.h"
26 #include "MeterPanelBase.h" // to inherit
27 #include "Ruler.h" // member variable
28 
29 class AudacityProject;
30 
31 // Increase this when we add support for multichannel meters
32 // (most of the code is already there)
33 const int kMaxMeterBars = 2;
34 
35 struct MeterBar {
36    bool   vert;
37    wxRect b;         // Bevel around bar
38    wxRect r;         // True bar drawing area
39    float  peak;
40    float  rms;
41    float  peakHold;
42    double peakHoldTime;
43    wxRect rClip;
44    bool   clipping;
45    bool   isclipping; //ANSWER-ME: What's the diff between these bools?! "clipping" vs "isclipping" is not clear.
46    int    tailPeakCount;
47    float  peakPeakHold;
48 };
49 
50 class MeterUpdateMsg
51 {
52    public:
53    int numFrames;
54    float peak[kMaxMeterBars];
55    float rms[kMaxMeterBars];
56    bool clipping[kMaxMeterBars];
57    int headPeakCount[kMaxMeterBars];
58    int tailPeakCount[kMaxMeterBars];
59 
60    /* neither constructor nor destructor do anything */
MeterUpdateMsg()61    MeterUpdateMsg() { }
~MeterUpdateMsg()62    ~MeterUpdateMsg() { }
63    /* for debugging purposes, printing the values out is really handy */
64    /** \brief Print out all the values in the meter update message */
65    wxString toString();
66    /** \brief Only print meter updates if clipping may be happening */
67    wxString toStringIfClipped();
68 };
69 
70 // Thread-safe queue of update messages
71 class MeterUpdateQueue
72 {
73  public:
74    explicit MeterUpdateQueue(size_t maxLen);
75    ~MeterUpdateQueue();
76 
77    bool Put(MeterUpdateMsg &msg);
78    bool Get(MeterUpdateMsg &msg);
79 
80    void Clear();
81 
82  private:
83    int              mStart;
84    int              mEnd;
85    size_t           mBufferSize;
86    ArrayOf<MeterUpdateMsg> mBuffer{mBufferSize};
87 };
88 
89 class MeterAx;
90 
91 /********************************************************************//**
92 \brief MeterPanel is a panel that paints the meter used for monitoring
93 or playback.
94 ************************************************************************/
95 class AUDACITY_DLL_API MeterPanel final
96    : public MeterPanelBase, private PrefsListener
97 {
98    DECLARE_DYNAMIC_CLASS(MeterPanel)
99 
100  public:
101    // These should be kept in the same order as they appear
102    // in the menu
103    enum Style {
104       AutomaticStereo,
105       HorizontalStereo,
106       VerticalStereo,
107       MixerTrackCluster, // Doesn't show menu, icon, or L/R labels, but otherwise like VerticalStereo.
108       HorizontalStereoCompact, // Thinner.
109       VerticalStereoCompact, // Narrower.
110    };
111 
112 
113    MeterPanel(AudacityProject *,
114          wxWindow* parent, wxWindowID id,
115          bool isInput,
116          const wxPoint& pos = wxDefaultPosition,
117          const wxSize& size = wxDefaultSize,
118          Style style = HorizontalStereo,
119          float fDecayRate = 60.0f);
120 
121    void SetFocusFromKbd() override;
122 
123    void Clear() override;
124 
GetStyle()125    Style GetStyle() const { return mStyle; }
GetDesiredStyle()126    Style GetDesiredStyle() const { return mDesiredStyle; }
127    void SetStyle(Style newStyle);
128 
129    /** \brief
130     *
131     * This method is thread-safe!  Feel free to call from a
132     * different thread (like from an audio I/O callback).
133     */
134    void Reset(double sampleRate, bool resetClipping) override;
135 
136    /** \brief Update the meters with a block of audio data
137     *
138     * Process the supplied block of audio data, extracting the peak and RMS
139     * levels to send to the meter. Also record runs of clipped samples to detect
140     * clipping that lies on block boundaries.
141     * This method is thread-safe!  Feel free to call from a different thread
142     * (like from an audio I/O callback).
143     *
144     * First overload:
145     * \param numChannels The number of channels of audio being played back or
146     * recorded.
147     * \param numFrames The number of frames (samples) in this data block. It is
148     * assumed that there are the same number of frames in each channel.
149     * \param sampleData The audio data itself, as interleaved samples. So
150     * indexing through the array we get the first sample of channel, first
151     * sample of channel 2 etc up to the first sample of channel (numChannels),
152     * then the second sample of channel 1, second sample of channel 2, and so
153     * to the second sample of channel (numChannels). The last sample in the
154     * array will be the (numFrames) sample for channel (numChannels).
155     *
156     * The second overload is for ease of use in MixerBoard.
157     */
158    void UpdateDisplay(unsigned numChannels,
159                       int numFrames, const float *sampleData) override;
160 
161    // Vaughan, 2010-11-29: This not currently used. See comments in MixerTrackCluster::UpdateMeter().
162    //void UpdateDisplay(int numChannels, int numFrames,
163    //                     // Need to make these double-indexed max and min arrays if we handle more than 2 channels.
164    //                     float* maxLeft, float* rmsLeft,
165    //                     float* maxRight, float* rmsRight,
166    //                     const size_t kSampleCount);
167 
168    /** \brief Find out if the level meter is disabled or not.
169     *
170     * This method is thread-safe!  Feel free to call from a
171     * different thread (like from an audio I/O callback).
172     */
173    bool IsMeterDisabled() const override;
174 
175    float GetMaxPeak() const override;
176 
177    bool IsClipping() const override;
178 
179    void StartMonitoring();
180    void StopMonitoring();
181 
182    // These exist solely for the purpose of resetting the toolbars
183    struct State{ bool mSaved, mMonitoring, mActive; };
184    State SaveState();
185    void RestoreState(const State &state);
186 
GetDBRange()187    int GetDBRange() const override { return mDB ? mDBRange : -1; }
188 
189  private:
190    void UpdatePrefs() override;
191    void UpdateSelectedPrefs( int ) override;
192 
193  private:
194    //
195    // Event handlers
196    //
197    void OnErase(wxEraseEvent &evt);
198    void OnPaint(wxPaintEvent &evt);
199    void OnSize(wxSizeEvent &evt);
200    bool InIcon(wxMouseEvent *pEvent = nullptr) const;
201    void OnMouse(wxMouseEvent &evt);
202    void OnKeyDown(wxKeyEvent &evt);
203    void OnKeyUp(wxKeyEvent &evt);
204    void OnContext(wxContextMenuEvent &evt);
205    void OnSetFocus(wxFocusEvent &evt);
206    void OnKillFocus(wxFocusEvent &evt);
207 
208    void OnAudioIOStatus(wxCommandEvent &evt);
209 
210    void OnMeterUpdate(wxTimerEvent &evt);
211 
212    void HandleLayout(wxDC &dc);
213    void SetActiveStyle(Style style);
214    void SetBarAndClip(int iBar, bool vert);
215    void DrawMeterBar(wxDC &dc, MeterBar *meterBar);
216    void ResetBar(MeterBar *bar, bool resetClipping);
217    void RepaintBarsNow();
218    wxFont GetFont() const;
219 
220    //
221    // Pop-up menu
222    //
223    void ShowMenu(const wxPoint & pos);
224    void OnMonitor(wxCommandEvent &evt);
225    void OnPreferences(wxCommandEvent &evt);
226 
227    wxString Key(const wxString & key) const;
228 
229    AudacityProject *mProject;
230    MeterUpdateQueue mQueue;
231    wxTimer          mTimer;
232 
233    int       mWidth;
234    int       mHeight;
235 
236    int       mRulerWidth;
237    int       mRulerHeight;
238 
239    bool      mIsInput;
240 
241    Style     mStyle;
242    Style     mDesiredStyle;
243    bool      mGradient;
244    bool      mDB;
245    int       mDBRange;
246    bool      mDecay;
247    float     mDecayRate; // dB/sec
248    bool      mClip;
249    int       mNumPeakSamplesToClip;
250    double    mPeakHoldDuration;
251    double    mT;
252    double    mRate;
253    long      mMeterRefreshRate;
254    long      mMeterDisabled; //is used as a bool, needs long for easy gPrefs...
255 
256    bool      mMonitoring;
257 
258    bool      mActive;
259 
260    unsigned  mNumBars;
261    MeterBar  mBar[kMaxMeterBars];
262 
263    bool      mLayoutValid;
264 
265    std::unique_ptr<wxBitmap> mBitmap;
266    wxRect    mIconRect;
267    wxPoint   mLeftTextPos;
268    wxPoint   mRightTextPos;
269    wxSize    mLeftSize;
270    wxSize    mRightSize;
271    std::unique_ptr<wxBitmap> mIcon;
272    wxPen     mPen;
273    wxPen     mDisabledPen;
274    wxPen     mPeakPeakPen;
275    wxBrush   mBrush;
276    wxBrush   mRMSBrush;
277    wxBrush   mClipBrush;
278    wxBrush   mBkgndBrush;
279    wxBrush   mDisabledBkgndBrush;
280    Ruler     mRuler;
281    wxString  mLeftText;
282    wxString  mRightText;
283 
284    bool mIsFocused;
285    wxRect mFocusRect;
286 #if defined(__WXMSW__)
287    bool mHadKeyDown;
288 #endif
289 
290    bool mAccSilent;
291 
292    friend class MeterAx;
293 
294    bool mHighlighted {};
295 
296    DECLARE_EVENT_TABLE()
297 };
298 
299 #endif // __AUDACITY_METER_PANEL__
300