mirror of
https://github.com/cookiengineer/audacity
synced 2025-09-18 09:00:52 +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:
parent
c476110589
commit
207db5e547
@ -122,7 +122,7 @@ NoteTrack::NoteTrack(const std::shared_ptr<DirManager> &projDirManager)
|
|||||||
mVelocity = 0;
|
mVelocity = 0;
|
||||||
#endif
|
#endif
|
||||||
mBottomNote = 24;
|
mBottomNote = 24;
|
||||||
mPitchHeight = 5;
|
mPitchHeight = 5.0f;
|
||||||
|
|
||||||
mVisibleChannels = ALL_CHANNELS;
|
mVisibleChannels = ALL_CHANNELS;
|
||||||
}
|
}
|
||||||
@ -184,7 +184,7 @@ Track::Holder NoteTrack::Duplicate() const
|
|||||||
}
|
}
|
||||||
// copy some other fields here
|
// copy some other fields here
|
||||||
duplicate->SetBottomNote(mBottomNote);
|
duplicate->SetBottomNote(mBottomNote);
|
||||||
duplicate->SetPitchHeight(mPitchHeight);
|
duplicate->mPitchHeight = mPitchHeight;
|
||||||
duplicate->mVisibleChannels = mVisibleChannels;
|
duplicate->mVisibleChannels = mVisibleChannels;
|
||||||
duplicate->SetOffset(GetOffset());
|
duplicate->SetOffset(GetOffset());
|
||||||
#ifdef EXPERIMENTAL_MIDI_OUT
|
#ifdef EXPERIMENTAL_MIDI_OUT
|
||||||
@ -912,6 +912,7 @@ void NoteTrack::WriteXML(XMLWriter &xmlFile) const
|
|||||||
xmlFile.EndTag(wxT("notetrack"));
|
xmlFile.EndTag(wxT("notetrack"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
void NoteTrack::StartVScroll()
|
void NoteTrack::StartVScroll()
|
||||||
{
|
{
|
||||||
mStartBottomNote = mBottomNote;
|
mStartBottomNote = mBottomNote;
|
||||||
@ -923,17 +924,17 @@ void NoteTrack::VScroll(int start, int end)
|
|||||||
int delta = ((end - start) + ph / 2) / ph;
|
int delta = ((end - start) + ph / 2) / ph;
|
||||||
SetBottomNote(mStartBottomNote + delta);
|
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
|
// Construct track rectangle to map pitch to screen coordinates
|
||||||
// Only y and height are needed:
|
// Only y and height are needed:
|
||||||
wxRect trackRect(0, rect.GetY(), 1, rect.GetHeight());
|
wxRect trackRect(0, rect.GetY(), 1, rect.GetHeight());
|
||||||
PrepareIPitchToY(trackRect);
|
PrepareIPitchToY(trackRect);
|
||||||
int clickedPitch = YToIPitch(y);
|
int clickedPitch = YToIPitch(y);
|
||||||
// zoom out by changing the pitch height -- a small integer
|
// zoom by changing the pitch height
|
||||||
mPitchHeight += amount;
|
SetPitchHeight(rect.height, mPitchHeight * multiplier);
|
||||||
if (mPitchHeight <= 0) mPitchHeight = 1;
|
|
||||||
PrepareIPitchToY(trackRect); // update because mPitchHeight changed
|
PrepareIPitchToY(trackRect); // update because mPitchHeight changed
|
||||||
if (center) {
|
if (center) {
|
||||||
int newCenterPitch = YToIPitch(rect.GetY() + rect.GetHeight() / 2);
|
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);
|
Zoom(rect, start, 1, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int trialPitchHeight = trackRect.height / (topPitch - botPitch);
|
auto trialPitchHeight = (float)trackRect.height / (topPitch - botPitch);
|
||||||
if (trialPitchHeight > 25) { // keep mPitchHeight in bounds [1...25]
|
Zoom(rect, (start + end) / 2, trialPitchHeight / mPitchHeight, true);
|
||||||
trialPitchHeight = 25;
|
|
||||||
} else if (trialPitchHeight == 0) {
|
|
||||||
trialPitchHeight = 1;
|
|
||||||
}
|
|
||||||
Zoom(rect, (start + end) / 2, trialPitchHeight - mPitchHeight, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int NoteTrack::YToIPitch(int y)
|
int NoteTrack::YToIPitch(int y)
|
||||||
@ -976,7 +972,9 @@ int NoteTrack::YToIPitch(int y)
|
|||||||
y -= octave * GetOctaveHeight();
|
y -= octave * GetOctaveHeight();
|
||||||
// result is approximate because C and G are one pixel taller than
|
// result is approximate because C and G are one pixel taller than
|
||||||
// mPitchHeight.
|
// mPitchHeight.
|
||||||
return (y / mPitchHeight) + octave * 12;
|
return (y / GetPitchHeight()) + octave * 12;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const float NoteTrack::ZoomStep = powf( 2.0f, 0.25f );
|
||||||
|
|
||||||
#endif // USE_MIDI
|
#endif // USE_MIDI
|
||||||
|
@ -122,27 +122,38 @@ class AUDACITY_DLL_API NoteTrack final
|
|||||||
( QuantizedTimeAndBeat t0, QuantizedTimeAndBeat t1, double newDur );
|
( QuantizedTimeAndBeat t0, QuantizedTimeAndBeat t1, double newDur );
|
||||||
|
|
||||||
int GetBottomNote() const { return mBottomNote; }
|
int GetBottomNote() const { return mBottomNote; }
|
||||||
int GetPitchHeight() const { return mPitchHeight; }
|
int GetPitchHeight() const { return std::max(1, (int)mPitchHeight); }
|
||||||
void SetPitchHeight(int h) { mPitchHeight = h; }
|
void SetPitchHeight(int rectHeight, float h)
|
||||||
/// Zooms out by one unit
|
{
|
||||||
void ZoomOut(const wxRect &rect, int y) { Zoom(rect, y, -1, true); }
|
// Impose certain zoom limits
|
||||||
/// Zooms in by one unit
|
auto octavePadding = 2 * 10; // 10 octaves times 2 single-pixel seperations per pixel
|
||||||
void ZoomIn(const wxRect &rect, int y) { Zoom(rect, y, 1, true); }
|
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.
|
/// 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.
|
/// 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);
|
void ZoomTo(const wxRect &rect, int start, int end);
|
||||||
int GetNoteMargin(int height) const
|
int GetNoteMargin(int height) const
|
||||||
{ return std::min(height / 4, (mPitchHeight + 1) / 2); }
|
{ return std::min(height / 4, (GetPitchHeight() + 1) / 2); }
|
||||||
int GetOctaveHeight() const { return mPitchHeight * 12 + 2; }
|
int GetOctaveHeight() const { return GetPitchHeight() * 12 + 2; }
|
||||||
// call this once before a series of calls to IPitchToY(). It
|
// call this once before a series of calls to IPitchToY(). It
|
||||||
// sets mBottom to offset of octave 0 so that mBottomNote
|
// 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 {
|
void PrepareIPitchToY(const wxRect &r) const {
|
||||||
mBottom = r.y + r.height - GetNoteMargin(r.height) - 1 - mPitchHeight +
|
mBottom =
|
||||||
(mBottomNote / 12) * GetOctaveHeight() +
|
r.y + r.height - GetNoteMargin(r.height) - 1 - GetPitchHeight() +
|
||||||
GetNotePos(mBottomNote % 12);
|
(mBottomNote / 12) * GetOctaveHeight() +
|
||||||
|
GetNotePos(mBottomNote % 12);
|
||||||
}
|
}
|
||||||
// IPitchToY returns Y coordinate of top of pitch p
|
// IPitchToY returns Y coordinate of top of pitch p
|
||||||
int IPitchToY(int p) const {
|
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
|
// compute the window coordinate of the bottom of an octave: This is
|
||||||
// the bottom of the line separating B and C.
|
// the bottom of the line separating B and C.
|
||||||
int GetOctaveBottom(int oct) const {
|
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)
|
// Y coordinate for given floating point pitch (rounded to int)
|
||||||
int PitchToY(double p) const {
|
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
|
// 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
|
// (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).
|
// 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
|
// get pixel offset to top of ith black key note
|
||||||
int GetBlackPos(int i) const { return GetNotePos(i * 2 + 1 + (i > 1)); }
|
int GetBlackPos(int i) const { return GetNotePos(i * 2 + 1 + (i > 1)); }
|
||||||
// GetWhitePos tells where to draw lines between keys as an offset from
|
// GetWhitePos tells where to draw lines between keys as an offset from
|
||||||
@ -178,6 +190,8 @@ class AUDACITY_DLL_API NoteTrack final
|
|||||||
|
|
||||||
mBottomNote = note;
|
mBottomNote = note;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
// Vertical scrolling is performed by dragging the keyboard at
|
// Vertical scrolling is performed by dragging the keyboard at
|
||||||
// left of track. Protocol is call StartVScroll, then update by
|
// left of track. Protocol is call StartVScroll, then update by
|
||||||
// calling VScroll with original and final mouse position.
|
// 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.
|
// so I left these functions here for possible use in the future.
|
||||||
void StartVScroll();
|
void StartVScroll();
|
||||||
void VScroll(int start, int end);
|
void VScroll(int start, int end);
|
||||||
|
#endif
|
||||||
|
|
||||||
bool HandleXMLTag(const wxChar *tag, const wxChar **attrs) override;
|
bool HandleXMLTag(const wxChar *tag, const wxChar **attrs) override;
|
||||||
XMLTagHandler *HandleXMLChild(const wxChar *tag) override;
|
XMLTagHandler *HandleXMLChild(const wxChar *tag) override;
|
||||||
@ -238,7 +253,14 @@ class AUDACITY_DLL_API NoteTrack final
|
|||||||
mutable int mBottom;
|
mutable int mBottom;
|
||||||
int mBottomNote;
|
int mBottomNote;
|
||||||
int mStartBottomNote;
|
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
|
int mVisibleChannels; // bit set of visible channels
|
||||||
|
|
||||||
std::weak_ptr<StretchHandle> mStretchHandle;
|
std::weak_ptr<StretchHandle> mStretchHandle;
|
||||||
|
@ -66,7 +66,10 @@ unsigned NoteTrackVRulerControls::HandleWheelRotation
|
|||||||
|
|
||||||
const auto nt = static_cast<NoteTrack*>(pTrack.get());
|
const auto nt = static_cast<NoteTrack*>(pTrack.get());
|
||||||
if (event.CmdDown() && !event.ShiftDown()) {
|
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()) {
|
} else if (!event.CmdDown() && event.ShiftDown()) {
|
||||||
// Scroll some fixed number of notes, independent of zoom level or track height:
|
// Scroll some fixed number of notes, independent of zoom level or track height:
|
||||||
static const int movement = 6; // 6 semitones is half an octave
|
static const int movement = 6; // 6 semitones is half an octave
|
||||||
|
@ -132,11 +132,7 @@ UIHandle::Result NoteTrackVZoomHandle::Release
|
|||||||
if (event.ShiftDown() && event.RightUp()) {
|
if (event.ShiftDown() && event.RightUp()) {
|
||||||
// Zoom out completely
|
// Zoom out completely
|
||||||
pTrack->SetBottomNote(0);
|
pTrack->SetBottomNote(0);
|
||||||
auto octavePadding = 2 * 10; // 10 octaves times 2 single-pixel seperations per pixel
|
pTrack->SetPitchHeight(evt.rect.height, 1);
|
||||||
auto availableHeight = evt.rect.height - octavePadding;
|
|
||||||
auto numNotes = 128;
|
|
||||||
auto spacePerNote = availableHeight / numNotes;
|
|
||||||
pTrack->SetPitchHeight(std::max(spacePerNote, 1));
|
|
||||||
} else {
|
} else {
|
||||||
// Zoom out
|
// Zoom out
|
||||||
pTrack->ZoomOut(evt.rect, mZoomEnd);
|
pTrack->ZoomOut(evt.rect, mZoomEnd);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user