mirror of
https://github.com/cookiengineer/audacity
synced 2025-05-02 08:39:46 +02:00
NoteTrack channel buttons have prettier graphics in down position which now means ON; VEL_SLIDER depends on EXPERIMENTAL_MIDI_OUT (not USE_MIDI); velocity slider changes are labeled as such to Undo manager; fixed an OS X bug where backing bitmap does not get yellow highlight and then highlight on screen gets clobbered when user moves gain sliders (!); Added a high-level overview of redraw to TrackArtist.cpp.
This commit is contained in:
parent
932ca88255
commit
f3b91514d2
@ -209,36 +209,49 @@ int NoteTrack::DrawLabelControls(wxDC & dc, wxRect & r)
|
||||
// two choices: channel is enabled (to see and play) when button is in
|
||||
// "up" position (original Audacity style) or in "down" position
|
||||
//
|
||||
#define CHANNEL_ON_IS_DOWN 0
|
||||
#if !CHANNEL_ON_IS_DOWN
|
||||
#define CHANNEL_ON_IS_DOWN 1
|
||||
#if CHANNEL_ON_IS_DOWN
|
||||
AColor::DarkMIDIChannel(&dc, channel);
|
||||
#else
|
||||
AColor::LightMIDIChannel(&dc, channel);
|
||||
#endif
|
||||
AColor::Line(dc, box.x, box.y, box.x + box.width - 1, box.y);
|
||||
AColor::Line(dc, box.x, box.y, box.x, box.y + box.height - 1);
|
||||
|
||||
#if CHANNEL_ON_IS_DOWN
|
||||
AColor::LightMIDIChannel(&dc, channel);
|
||||
#else
|
||||
AColor::DarkMIDIChannel(&dc, channel);
|
||||
#endif
|
||||
AColor::Line(dc,
|
||||
box.x + box.width - 1, box.y,
|
||||
box.x + box.width - 1, box.y + box.height - 1);
|
||||
AColor::Line(dc,
|
||||
box.x, box.y + box.height - 1,
|
||||
box.x + box.width - 1, box.y + box.height - 1);
|
||||
#endif
|
||||
} else {
|
||||
AColor::MIDIChannel(&dc, 0);
|
||||
dc.DrawRectangle(box);
|
||||
#if CHANNEL_ON_IS_DOWN
|
||||
AColor::LightMIDIChannel(&dc, 0);
|
||||
#else
|
||||
AColor::DarkMIDIChannel(&dc, 0);
|
||||
#endif
|
||||
AColor::Line(dc, box.x, box.y, box.x + box.width - 1, box.y);
|
||||
AColor::Line(dc, box.x, box.y, box.x, box.y + box.height - 1);
|
||||
|
||||
#if CHANNEL_ON_IS_DOWN
|
||||
AColor::DarkMIDIChannel(&dc, 0);
|
||||
#else
|
||||
AColor::LightMIDIChannel(&dc, 0);
|
||||
#endif
|
||||
AColor::Line(dc,
|
||||
box.x + box.width - 1, box.y,
|
||||
box.x + box.width - 1, box.y + box.height - 1);
|
||||
AColor::Line(dc,
|
||||
box.x, box.y + box.height - 1,
|
||||
box.x + box.width - 1, box.y + box.height - 1);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
wxString t;
|
||||
|
@ -17,6 +17,128 @@
|
||||
waveforms at least it needs to cache the samples that are
|
||||
currently on-screen.
|
||||
|
||||
<b>How Audacity Redisplay Works \n
|
||||
Roger Dannenberg</b> \n
|
||||
Oct 2010 \n
|
||||
|
||||
This is a brief guide to Audacity redisplay -- it may not be complete. It
|
||||
is my attempt to understand the complicated graphics strategy.
|
||||
|
||||
One basic idea is that redrawing waveforms is rather slow, so Audacity
|
||||
saves waveform images in bitmaps to make redrawing faster. In particular,
|
||||
during audio playback (and recording), the vertical time indicator is
|
||||
drawn over the waveform about 20 times per second. To avoid unnecessary
|
||||
computation, the indicator is erased by copying a column of pixels from
|
||||
a bitmap image of the waveform. Notice that this implies a two-stage
|
||||
process: first, waveforms are drawn to the bitmp; then, the bitmap
|
||||
(or pieces of it) are copied to the screen, perhaps along with other
|
||||
graphics.
|
||||
|
||||
The bitmap is for the entire track panel, i.e. multiple tracks, and
|
||||
includes things like the Gain and Pan slders to the left of the
|
||||
waveform images.
|
||||
|
||||
The screen update uses a mixture of direct drawing and indirect paint
|
||||
events. The "normal" way to update a graphical display is to call
|
||||
the Refresh() method when something invalidates the screen. Later, the
|
||||
system calls OnPaint(), which the application overrides to (re)draw the
|
||||
screen. In wxWidgets, you can also draw directly to the screen without
|
||||
calling Refresh() and without waiting for OnPaint() to be called.
|
||||
|
||||
I would expect there to be a 2-level invalidation scheme: Some changes
|
||||
invalidate the bitmap, forcing a bitmap redraw *and* a screen redraw.
|
||||
Other changes merely update the screen using pre-existing bitmaps. In
|
||||
Audacity, the "2-level" invalidation works like this: Anything
|
||||
that invalidates the bitmap calls TrackPanel::Refresh(), which
|
||||
has an eraseBackground parameter. This flag says to redraw the
|
||||
bitmap when OnPaint() is called. If eraseBackground is false, the
|
||||
existing bitmap can be used for waveform imges. Audacity also
|
||||
draws directly to the screen to update the time indicator during
|
||||
playback. To move the indicator, one column of pixels is drawn to
|
||||
the screen to remove the indicator. Then the indicator is drawn at
|
||||
a new time location.
|
||||
|
||||
The track panel consists of many components. The tree of calls that
|
||||
update the bitmap looks like this:
|
||||
|
||||
\code
|
||||
TrackPanel::DrawTracks(), calls
|
||||
TrackArtist::DrawTracks();
|
||||
TrackPanel::DrawEverythingElse();
|
||||
for each track,
|
||||
TrackPanel::DrawOutside();
|
||||
TrackPanel::DrawOutsideOfTrack();
|
||||
TrackPanel::DrawBordersAroundTrack();
|
||||
TrackPanel::DrawShadow();
|
||||
TrackInfo::DrawCloseBox();
|
||||
TrackInfo::DrawTitleBar();
|
||||
TrackInfo::DrawMinimize();
|
||||
TrackInfo::DrawBordersWithin();
|
||||
various TrackInfo sliders and buttons
|
||||
TrackArtist::DrawVRuler();
|
||||
TrackPanel::DrawZooming();
|
||||
draws horizontal dashed lines during zoom-drag
|
||||
TrackPanel::HighlightFocusedTrack();
|
||||
draws yellow highlight on selected track
|
||||
draw snap guidelines if any
|
||||
\endcode
|
||||
|
||||
After drawing the bitmap and blitting the bitmap to the screen,
|
||||
the following calls are (sometimes) made. To keep track of what has
|
||||
been drawn on screen over the bitmap images,
|
||||
\li \c mLastCursor is the position of the vertical line representing sel0,
|
||||
the selected time position
|
||||
\li \c mLastIndicator is the position of the moving vertical line during
|
||||
playback
|
||||
|
||||
\code
|
||||
TrackPanel::DoDrawIndicator();
|
||||
copy pixel column from bitmap to screen to erase indicator line
|
||||
TrackPanel::DoDrawCursor(); [if mLastCursor == mLastIndicator]
|
||||
TrackPanel::DisplaySelection();
|
||||
AdornedRulerPanel::DrawIndicator(); [not part of TrackPanel graphics]
|
||||
draw indicator on each track
|
||||
TrackPanel::DoDrawCursor();
|
||||
draw cursor on each track [at mViewInfo->sel0]
|
||||
AdornedRulerPanel::DrawCursor(); [not part of TrackPanel graphics]
|
||||
TrackPanel::DisplaySelection();
|
||||
\endcode
|
||||
|
||||
To move the indicator, TrackPanel::OnTimer() calls the following, using
|
||||
a drawing context (DC) for the screen. (Refresh is not called to create
|
||||
an OnPaint event. Instead, drawing is direct to the screen.)
|
||||
\code
|
||||
TrackPanel::DrawIndicator();
|
||||
TrackPanel::DoDrawIndicator();
|
||||
\endcode
|
||||
|
||||
Notice that TrackPanel::DrawZooming(), TrackPanel::HighlightFocusedTrack(),
|
||||
and snap guidelines could be drawn directly to the screen rather than to
|
||||
the bitmap, generally eliminating redraw work.
|
||||
|
||||
One problem is slider udpates. Sliders are in the left area of the track
|
||||
panel. They are not wxWindows like wxSliders, but instead are just drawn
|
||||
on the TrackPanel. When slider state changes, *all* tracks do a full
|
||||
refresh, including recomputing the backing store. It would make more sense
|
||||
to just invalidate the region containing the slider. However, doing that
|
||||
would require either incrementally updating the bitmap (not currently done),
|
||||
or maintaining the sliders and other track info on the screen and not in
|
||||
the bitmap.
|
||||
|
||||
In my opinion, the bitmap should contain only the waveform, note, and
|
||||
label images along with gray selection highlights. The track info
|
||||
(sliders, buttons, title, etc.), track selection highlight, cursor, and
|
||||
indicator should be drawn in the normal way, and clipping regions should
|
||||
be used to avoid excessive copying of bitmaps (say, when sliders move),
|
||||
or excessive redrawing of track info widgets (say, when scrolling occurs).
|
||||
This is a fairly tricky code change since it requires careful specification
|
||||
of what and where redraw should take place when any state changes. One
|
||||
surprising finding is that NoteTrack display is slow compared to WaveTrack
|
||||
display. Each note takes some time to gather attributes and select colors,
|
||||
and while audio draws two amplitudes per horizontal pixels, large MIDI
|
||||
scores can have more notes than horizontal pixels. This can make slider
|
||||
changes very sluggish, but this can also be a problem with many
|
||||
audio tracks.
|
||||
|
||||
*//*******************************************************************/
|
||||
|
||||
|
@ -1052,7 +1052,7 @@ void TrackPanel::DoDrawIndicator(wxDC & dc)
|
||||
if (x >= w) {
|
||||
x = w - 1;
|
||||
}
|
||||
|
||||
|
||||
dc.Blit( x, 0, 1, mBacking->GetHeight(), &mBackingDC, x, 0 );
|
||||
}
|
||||
|
||||
@ -1146,7 +1146,7 @@ void TrackPanel::DoDrawIndicator(wxDC & dc)
|
||||
/// This method draws the cursor things, both in the
|
||||
/// ruler as seen at the top of the screen, but also in each of the
|
||||
/// selected tracks.
|
||||
/// These are the 'vertical lines' through waves and ruler.
|
||||
/// These are the 'vertical lines' through waves, notes, and ruler.
|
||||
void TrackPanel::DrawCursor()
|
||||
{
|
||||
wxClientDC dc( this );
|
||||
@ -3973,7 +3973,7 @@ void TrackPanel::HandleSliders(wxMouseEvent &event, bool pan)
|
||||
|
||||
float newValue = slider->Get();
|
||||
MixerBoard* pMixerBoard = this->GetMixerBoard(); // Update mixer board, too.
|
||||
#ifdef USE_MIDI
|
||||
#ifdef EXPERIMENTAL_MIDI_OUT
|
||||
if (mCapturedTrack->GetKind() == Track::Wave) {
|
||||
#endif
|
||||
WaveTrack *link = (WaveTrack *)mTracks->GetLink(mCapturedTrack);
|
||||
@ -3994,7 +3994,7 @@ void TrackPanel::HandleSliders(wxMouseEvent &event, bool pan)
|
||||
if (pMixerBoard)
|
||||
pMixerBoard->UpdateGain((WaveTrack*)mCapturedTrack);
|
||||
}
|
||||
#ifdef USE_MIDI
|
||||
#ifdef EXPERIMENTAL_MIDI_OUT
|
||||
} else {
|
||||
if (!pan) {
|
||||
((NoteTrack *) mCapturedTrack)->SetGain(newValue);
|
||||
@ -4015,9 +4015,17 @@ void TrackPanel::HandleSliders(wxMouseEvent &event, bool pan)
|
||||
}
|
||||
|
||||
if (event.ButtonUp()) {
|
||||
#ifdef EXPERIMENTAL_MIDI_OUT
|
||||
if (mCapturedTrack->GetKind() == Track::Wave) {
|
||||
#endif
|
||||
MakeParentPushState(pan ? _("Moved pan slider") : _("Moved gain slider"),
|
||||
pan ? _("Pan") : _("Gain"),
|
||||
true /* consolidate */);
|
||||
#ifdef EXPERIMENTAL_MIDI_OUT
|
||||
} else {
|
||||
MakeParentPushState(_("Moved velocity slider"), _("Velocity"), true);
|
||||
}
|
||||
#endif
|
||||
SetCapturedTrack( NULL );
|
||||
}
|
||||
}
|
||||
@ -5507,7 +5515,10 @@ void TrackPanel::DrawEverythingElse(wxDC * dc,
|
||||
clip.height - trackRect.y);
|
||||
}
|
||||
|
||||
if (GetFocusedTrack() != NULL && wxWindow::FindFocus() == this) {
|
||||
// Previous code that caused highlight NOT to be drawn on backing
|
||||
// bitmap due to wxWindow::FindFocus() not returning "this" on Mac:
|
||||
// if (GetFocusedTrack() != NULL) && wxWindow::FindFocus() == this) {
|
||||
if (GetFocusedTrack() != NULL) {
|
||||
HighlightFocusedTrack(dc, focusRect);
|
||||
}
|
||||
|
||||
@ -7997,7 +8008,7 @@ void TrackInfo::DrawSliders(wxDC *dc, WaveTrack *t, wxRect r)
|
||||
GetPanRect(r, panRect);
|
||||
|
||||
if (gainRect.y + gainRect.height < r.y + r.height - 19) {
|
||||
#ifdef USE_MIDI
|
||||
#ifdef EXPERIMENTAL_MIDI_OUT
|
||||
GainSlider(index)->SetStyle(DB_SLIDER);
|
||||
#endif
|
||||
GainSlider(index)->Move(wxPoint(gainRect.x, gainRect.y));
|
||||
|
@ -76,8 +76,8 @@ of an LWSlider or ASlider.
|
||||
#include <wx/sysopt.h>
|
||||
#endif
|
||||
|
||||
#include "ASlider.h"
|
||||
#include "../Experimental.h"
|
||||
#include "ASlider.h"
|
||||
|
||||
#include "../AColor.h"
|
||||
#include "../ImageManipulation.h"
|
||||
@ -395,7 +395,7 @@ void LWSlider::SetStyle(int style)
|
||||
mMaxValue = SPEED_MAX;
|
||||
mStepValue = STEP_CONTINUOUS;
|
||||
break;
|
||||
#ifdef USE_MIDI
|
||||
#ifdef EXPERIMENTAL_MIDI_OUT
|
||||
case VEL_SLIDER:
|
||||
mMinValue = VEL_MIN;
|
||||
mMaxValue = VEL_MAX;
|
||||
@ -541,7 +541,7 @@ void LWSlider::CreatePopWin()
|
||||
wxString maxStr = mName + wxT(": 000000");
|
||||
|
||||
if (mStyle == PAN_SLIDER || mStyle == DB_SLIDER || mStyle == SPEED_SLIDER
|
||||
#ifdef USE_MIDI
|
||||
#ifdef EXPERIMENTAL_MIDI_OUT
|
||||
|| mStyle == VEL_SLIDER
|
||||
#endif
|
||||
)
|
||||
@ -917,7 +917,7 @@ void LWSlider::FormatPopWin()
|
||||
case SPEED_SLIDER:
|
||||
label.Printf(wxT("%s: %.2fx"), mName.c_str(), mCurrentValue);
|
||||
break;
|
||||
#ifdef USE_MIDI
|
||||
#ifdef EXPERIMENTAL_MIDI_OUT
|
||||
case VEL_SLIDER:
|
||||
label.Printf(wxT("%s: %s%d"), mName.c_str(),
|
||||
(mCurrentValue > 0.0f ? _("+") : _("")),
|
||||
@ -1543,7 +1543,7 @@ void ASlider::Set(float value)
|
||||
mLWSlider->Set(value);
|
||||
}
|
||||
|
||||
#ifdef USE_MIDI
|
||||
#ifdef EXPERIMENTAL_MIDI_OUT
|
||||
void ASlider::SetStyle(int style)
|
||||
{
|
||||
mStyle = style;
|
||||
@ -1785,7 +1785,7 @@ wxAccStatus ASliderAx::GetValue(int childId, wxString* strValue)
|
||||
case SPEED_SLIDER:
|
||||
strValue->Printf( wxT("%.0f"), as->mLWSlider->mCurrentValue * 100 );
|
||||
break;
|
||||
#ifdef USE_MIDI
|
||||
#ifdef EXPERIMENTAL_MIDI_OUT
|
||||
case VEL_SLIDER:
|
||||
strValue->Printf( wxT("%.0f"), as->mLWSlider->mCurrentValue);
|
||||
break;
|
||||
|
@ -39,7 +39,7 @@ class Ruler;
|
||||
#define DB_SLIDER 2 // -36...36 dB
|
||||
#define PAN_SLIDER 3 // -1.0...1.0
|
||||
#define SPEED_SLIDER 4 // 0.01 ..3.0
|
||||
#ifdef USE_MIDI
|
||||
#ifdef EXPERIMENTAL_MIDI_OUT
|
||||
#define VEL_SLIDER 5 // -50..50
|
||||
#endif
|
||||
|
||||
@ -257,7 +257,7 @@ class ASlider :public wxPanel
|
||||
|
||||
float Get( bool convert = true );
|
||||
void Set(float value);
|
||||
#ifdef USE_MIDI
|
||||
#ifdef EXPERIMENTAL_MIDI_OUT
|
||||
void SetStyle(int style);
|
||||
#endif
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user