1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-07-19 14:17:41 +02:00

Improved Note Track vertical ruler zooming...

... Make consistent zoom in and out limits for mouse picks and for scrollwheel

Make scrollwheel do in or out by a constant multiplier, analogously with wave
track

The Note track remembers a continuously varying key height, but rounds it off
whenever it draws
This commit is contained in:
Paul Licameli 2017-07-19 14:00:27 -04:00
parent c476110589
commit 207db5e547
4 changed files with 56 additions and 37 deletions

View File

@ -122,7 +122,7 @@ NoteTrack::NoteTrack(const std::shared_ptr<DirManager> &projDirManager)
mVelocity = 0;
#endif
mBottomNote = 24;
mPitchHeight = 5;
mPitchHeight = 5.0f;
mVisibleChannels = ALL_CHANNELS;
}
@ -184,7 +184,7 @@ Track::Holder NoteTrack::Duplicate() const
}
// copy some other fields here
duplicate->SetBottomNote(mBottomNote);
duplicate->SetPitchHeight(mPitchHeight);
duplicate->mPitchHeight = mPitchHeight;
duplicate->mVisibleChannels = mVisibleChannels;
duplicate->SetOffset(GetOffset());
#ifdef EXPERIMENTAL_MIDI_OUT
@ -912,6 +912,7 @@ void NoteTrack::WriteXML(XMLWriter &xmlFile) const
xmlFile.EndTag(wxT("notetrack"));
}
#if 0
void NoteTrack::StartVScroll()
{
mStartBottomNote = mBottomNote;
@ -923,17 +924,17 @@ void NoteTrack::VScroll(int start, int end)
int delta = ((end - start) + ph / 2) / ph;
SetBottomNote(mStartBottomNote + delta);
}
#endif
void NoteTrack::Zoom(const wxRect &rect, int y, int amount, bool center)
void NoteTrack::Zoom(const wxRect &rect, int y, float multiplier, bool center)
{
// Construct track rectangle to map pitch to screen coordinates
// Only y and height are needed:
wxRect trackRect(0, rect.GetY(), 1, rect.GetHeight());
PrepareIPitchToY(trackRect);
int clickedPitch = YToIPitch(y);
// zoom out by changing the pitch height -- a small integer
mPitchHeight += amount;
if (mPitchHeight <= 0) mPitchHeight = 1;
// zoom by changing the pitch height
SetPitchHeight(rect.height, mPitchHeight * multiplier);
PrepareIPitchToY(trackRect); // update because mPitchHeight changed
if (center) {
int newCenterPitch = YToIPitch(rect.GetY() + rect.GetHeight() / 2);
@ -960,13 +961,8 @@ void NoteTrack::ZoomTo(const wxRect &rect, int start, int end)
Zoom(rect, start, 1, true);
return;
}
int trialPitchHeight = trackRect.height / (topPitch - botPitch);
if (trialPitchHeight > 25) { // keep mPitchHeight in bounds [1...25]
trialPitchHeight = 25;
} else if (trialPitchHeight == 0) {
trialPitchHeight = 1;
}
Zoom(rect, (start + end) / 2, trialPitchHeight - mPitchHeight, true);
auto trialPitchHeight = (float)trackRect.height / (topPitch - botPitch);
Zoom(rect, (start + end) / 2, trialPitchHeight / mPitchHeight, true);
}
int NoteTrack::YToIPitch(int y)
@ -976,7 +972,9 @@ int NoteTrack::YToIPitch(int y)
y -= octave * GetOctaveHeight();
// result is approximate because C and G are one pixel taller than
// mPitchHeight.
return (y / mPitchHeight) + octave * 12;
return (y / GetPitchHeight()) + octave * 12;
}
const float NoteTrack::ZoomStep = powf( 2.0f, 0.25f );
#endif // USE_MIDI

View File

@ -122,27 +122,38 @@ class AUDACITY_DLL_API NoteTrack final
( QuantizedTimeAndBeat t0, QuantizedTimeAndBeat t1, double newDur );
int GetBottomNote() const { return mBottomNote; }
int GetPitchHeight() const { return mPitchHeight; }
void SetPitchHeight(int h) { mPitchHeight = h; }
/// Zooms out by one unit
void ZoomOut(const wxRect &rect, int y) { Zoom(rect, y, -1, true); }
/// Zooms in by one unit
void ZoomIn(const wxRect &rect, int y) { Zoom(rect, y, 1, true); }
int GetPitchHeight() const { return std::max(1, (int)mPitchHeight); }
void SetPitchHeight(int rectHeight, float h)
{
// Impose certain zoom limits
auto octavePadding = 2 * 10; // 10 octaves times 2 single-pixel seperations per pixel
auto availableHeight = rectHeight - octavePadding;
auto numNotes = 128.f;
auto minSpacePerNote =
std::max((float)MinPitchHeight, availableHeight / numNotes);
mPitchHeight =
std::max(minSpacePerNote,
std::min((float)MaxPitchHeight, h));
}
/// Zooms out a constant factor (subject to zoom limits)
void ZoomOut(const wxRect &rect, int y) { Zoom(rect, y, 1.0f / ZoomStep, true); }
/// Zooms in a contant factor (subject to zoom limits)
void ZoomIn(const wxRect &rect, int y) { Zoom(rect, y, ZoomStep, true); }
/// Zoom the note track around y.
/// Positive amounts zoom in; negative amounts zoom out.
/// If center is true, the result will be centered at y.
void Zoom(const wxRect &rect, int y, int amount, bool center);
void Zoom(const wxRect &rect, int y, float multiplier, bool center);
void ZoomTo(const wxRect &rect, int start, int end);
int GetNoteMargin(int height) const
{ return std::min(height / 4, (mPitchHeight + 1) / 2); }
int GetOctaveHeight() const { return mPitchHeight * 12 + 2; }
{ return std::min(height / 4, (GetPitchHeight() + 1) / 2); }
int GetOctaveHeight() const { return GetPitchHeight() * 12 + 2; }
// call this once before a series of calls to IPitchToY(). It
// sets mBottom to offset of octave 0 so that mBottomNote
// is located at r.y + r.height - (GetNoteMargin() + 1 + mPitchHeight)
// is located at r.y + r.height - (GetNoteMargin() + 1 + GetPitchHeight())
void PrepareIPitchToY(const wxRect &r) const {
mBottom = r.y + r.height - GetNoteMargin(r.height) - 1 - mPitchHeight +
(mBottomNote / 12) * GetOctaveHeight() +
GetNotePos(mBottomNote % 12);
mBottom =
r.y + r.height - GetNoteMargin(r.height) - 1 - GetPitchHeight() +
(mBottomNote / 12) * GetOctaveHeight() +
GetNotePos(mBottomNote % 12);
}
// IPitchToY returns Y coordinate of top of pitch p
int IPitchToY(int p) const {
@ -151,7 +162,7 @@ class AUDACITY_DLL_API NoteTrack final
// compute the window coordinate of the bottom of an octave: This is
// the bottom of the line separating B and C.
int GetOctaveBottom(int oct) const {
return IPitchToY(oct * 12) + mPitchHeight + 1;
return IPitchToY(oct * 12) + GetPitchHeight() + 1;
}
// Y coordinate for given floating point pitch (rounded to int)
int PitchToY(double p) const {
@ -162,7 +173,8 @@ class AUDACITY_DLL_API NoteTrack final
// map pitch class number (0-11) to pixel offset from bottom of octave
// (the bottom of the black line between B and C) to the top of the
// note. Note extra pixel separates B(11)/C(0) and E(4)/F(5).
int GetNotePos(int p) const { return 1 + mPitchHeight * (p + 1) + (p > 4); }
int GetNotePos(int p) const
{ return 1 + GetPitchHeight() * (p + 1) + (p > 4); }
// get pixel offset to top of ith black key note
int GetBlackPos(int i) const { return GetNotePos(i * 2 + 1 + (i > 1)); }
// GetWhitePos tells where to draw lines between keys as an offset from
@ -178,6 +190,8 @@ class AUDACITY_DLL_API NoteTrack final
mBottomNote = note;
}
#if 0
// Vertical scrolling is performed by dragging the keyboard at
// left of track. Protocol is call StartVScroll, then update by
// calling VScroll with original and final mouse position.
@ -186,6 +200,7 @@ class AUDACITY_DLL_API NoteTrack final
// so I left these functions here for possible use in the future.
void StartVScroll();
void VScroll(int start, int end);
#endif
bool HandleXMLTag(const wxChar *tag, const wxChar **attrs) override;
XMLTagHandler *HandleXMLChild(const wxChar *tag) override;
@ -238,7 +253,14 @@ class AUDACITY_DLL_API NoteTrack final
mutable int mBottom;
int mBottomNote;
int mStartBottomNote;
int mPitchHeight;
// Remember continuous variation for zooming,
// but it is rounded off whenever drawing:
float mPitchHeight;
enum { MinPitchHeight = 1, MaxPitchHeight = 25 };
static const float ZoomStep;
int mVisibleChannels; // bit set of visible channels
std::weak_ptr<StretchHandle> mStretchHandle;

View File

@ -66,7 +66,10 @@ unsigned NoteTrackVRulerControls::HandleWheelRotation
const auto nt = static_cast<NoteTrack*>(pTrack.get());
if (event.CmdDown() && !event.ShiftDown()) {
nt->Zoom(evt.rect, evt.event.m_y, (int) (steps), false);
if (steps > 0)
nt->ZoomIn(evt.rect, evt.event.m_y);
else
nt->ZoomOut(evt.rect, evt.event.m_y);
} else if (!event.CmdDown() && event.ShiftDown()) {
// Scroll some fixed number of notes, independent of zoom level or track height:
static const int movement = 6; // 6 semitones is half an octave

View File

@ -132,11 +132,7 @@ UIHandle::Result NoteTrackVZoomHandle::Release
if (event.ShiftDown() && event.RightUp()) {
// Zoom out completely
pTrack->SetBottomNote(0);
auto octavePadding = 2 * 10; // 10 octaves times 2 single-pixel seperations per pixel
auto availableHeight = evt.rect.height - octavePadding;
auto numNotes = 128;
auto spacePerNote = availableHeight / numNotes;
pTrack->SetPitchHeight(std::max(spacePerNote, 1));
pTrack->SetPitchHeight(evt.rect.height, 1);
} else {
// Zoom out
pTrack->ZoomOut(evt.rect, mZoomEnd);