Audacity 3.2.0
NoteTrackDisplayData.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 @file NoteTrackDisplayData.cpp
6 @brief Implements NoteTrackDisplayData
7
8 Paul Licameli split from NoteTrack.cpp
9
10**********************************************************************/
11
13#include "NoteTrack.h"
14#include "WrapAllegro.h"
15
17 [](NoteTrack&){ return std::make_unique<NoteTrackRange>(); }
18};
19
21{
22 auto &mutTrack = const_cast<NoteTrack&>(track);
23 return static_cast<NoteTrackRange&>(
24 mutTrack.NoteTrackAttachments::Get(key));
25}
26
28
29std::unique_ptr<NoteTrackAttachment> NoteTrackRange::Clone() const
30{
31 return std::make_unique<NoteTrackRange>(*this);
32}
33
35{
36 xmlFile.WriteAttr(wxT("bottomnote"), mBottomNote);
37 xmlFile.WriteAttr(wxT("topnote"), mTopNote);
38}
39
41{
42 auto attr = pair.first;
43 auto value = pair.second;
44 long nValue{};
45 if (attr == "bottomnote" && value.TryGet(nValue)) {
46 SetBottomNote(nValue);
47 return true;
48 }
49 if (attr == "topnote" && value.TryGet(nValue)) {
50 SetTopNote(nValue);
51 return true;
52 }
53 return false;
54}
55
57{
58 if (note < MinPitch)
59 note = MinPitch;
60 else if (note > 96)
61 note = 96;
62
63 wxCHECK(note <= mTopNote, );
64
65 mBottomNote = note;
66}
67
69{
70 if (note > MaxPitch)
71 note = MaxPitch;
72
73 wxCHECK(note >= mBottomNote, );
74
75 mTopNote = note;
76}
77
78void NoteTrackRange::SetNoteRange(int note1, int note2)
79{
80 // Bounds check
81 if (note1 > MaxPitch)
82 note1 = MaxPitch;
83 else if (note1 < MinPitch)
84 note1 = MinPitch;
85 if (note2 > MaxPitch)
86 note2 = MaxPitch;
87 else if (note2 < MinPitch)
88 note2 = MinPitch;
89 // Swap to ensure ordering
90 if (note2 < note1) { std::swap(note1, note2); }
91
92 mBottomNote = note1;
93 mTopNote = note2;
94}
95
97{
98 // Ensure everything stays in bounds
99 if (mBottomNote + offset < MinPitch || mTopNote + offset > MaxPitch)
100 return;
101
102 mBottomNote += offset;
103 mTopNote += offset;
104}
105
106#if 0
107void NoteTrackRange::StartVScroll()
108{
109 mStartBottomNote = mBottomNote;
110}
111
112void NoteTrackRange::VScroll(int start, int end)
113{
114 int ph = GetPitchHeight();
115 int delta = ((end - start) + ph / 2) / ph;
116 ShiftNoteRange(delta);
117}
118#endif
119
121{
122 Alg_iterator iterator(pSeq, false);
123 iterator.begin();
124 Alg_event_ptr evt;
125
126 // Go through all of the notes, finding the minimum and maximum value pitches.
127 bool hasNotes = false;
128 int minPitch = MaxPitch;
129 int maxPitch = MinPitch;
130
131 while (nullptr != (evt = iterator.next())) {
132 if (evt->is_note()) {
133 int pitch = (int) evt->get_pitch();
134 hasNotes = true;
135 if (pitch < minPitch)
136 minPitch = pitch;
137 if (pitch > maxPitch)
138 maxPitch = pitch;
139 }
140 }
141
142 if (!hasNotes) {
143 // Semi-arbitrary default values:
144 minPitch = 48;
145 maxPitch = 72;
146 }
147
148 SetNoteRange(minPitch, maxPitch);
149}
150
152 const NoteTrack &track, const wxRect &rect)
153 : mTrack{ track }
154 , mRect{ rect }
155{
156 auto &data = NoteTrackRange::Get(mTrack);
157 auto span = data.GetTopNote() - data.GetBottomNote() + 1; // + 1 to make sure it includes both
158
159 mMargin = std::min((int) (rect.height / (float)(span)) / 2, rect.height / 4);
160
161 // Count the number of dividers between B/C and E/F
162 int numC = 0, numF = 0;
163 auto botOctave = data.GetBottomNote() / 12, botNote = data.GetBottomNote() % 12;
164 auto topOctave = data.GetTopNote() / 12, topNote = data.GetTopNote() % 12;
165 if (topOctave == botOctave)
166 {
167 if (botNote == 0) numC = 1;
168 if (topNote <= 5) numF = 1;
169 }
170 else
171 {
172 numC = topOctave - botOctave;
173 numF = topOctave - botOctave - 1;
174 if (botNote == 0) numC++;
175 if (botNote <= 5) numF++;
176 if (topOctave <= 5) numF++;
177 }
178 // Effective space, excluding the margins and the lines between some notes
179 auto effectiveHeight = rect.height - (2 * (mMargin + 1)) - numC - numF;
180 // Guaranteed that both the bottom and top notes will be visible
181 // (assuming that the clamping below does not happen)
182 mPitchHeight = effectiveHeight / ((float) span);
183
188
189 mBottom = rect.y + rect.height - GetNoteMargin() - 1 - GetPitchHeight(1) +
190 botOctave * GetOctaveHeight() + GetNotePos(botNote);
191}
192
194{ return mBottom - (p / 12) * GetOctaveHeight() - GetNotePos(p % 12); }
195
197{
198 y = mBottom - y; // pixels above pitch 0
199 int octave = (y / GetOctaveHeight());
200 y -= octave * GetOctaveHeight();
201 // result is approximate because C and G are one pixel taller than
202 // mPitchHeight.
203 // Poke 1-13-18: However in practice this seems not to be an issue,
204 // as long as we use mPitchHeight and not the rounded version
205 return (y / mPitchHeight) + octave * 12;
206}
207
208void NoteTrackDisplayData::Zoom(int y, float multiplier, bool center)
209{
210 auto &data = NoteTrackRange::Get(mTrack);
211 int clickedPitch = YToIPitch(y);
212 int extent = data.GetTopNote() - data.GetBottomNote() + 1;
213 int newExtent = (int) (extent / multiplier);
214 float position;
215 if (center) {
216 // center the pitch that the user clicked on
217 position = .5;
218 } else {
219 // align to keep the pitch that the user clicked on in the same place
220 position = extent / (clickedPitch - data.GetBottomNote());
221 }
222 int newBottomNote = clickedPitch - (newExtent * position);
223 int newTopNote = clickedPitch + (newExtent * (1 - position));
224 data.SetNoteRange(newBottomNote, newTopNote);
225}
226
228{
229 wxRect trackRect(0, mRect.GetY(), 1, mRect.GetHeight());
230 int pitch1 = YToIPitch(start);
231 int pitch2 = YToIPitch(end);
232 if (pitch1 == pitch2) {
233 // Just zoom in instead of zooming to show only one note
234 Zoom(start, 1, true);
235 return;
236 }
237 // It's fine for this to be in either order
238 NoteTrackRange::Get(mTrack).SetNoteRange(pitch1, pitch2);
239}
240
242{
243 return std::max(1, (int)(factor * mPitchHeight));
244
245}
246
247const float NoteTrackDisplayData::ZoomStep = powf( 2.0f, 0.25f );
wxT("CloseDown"))
int min(int a, int b)
static NoteTrackAttachments::RegisteredFactory key
std::pair< std::string_view, XMLAttributeValueView > Attribute
Definition: XMLTagHandler.h:39
Client code makes static instance from a factory of attachments; passes it to Get or Find as a retrie...
Definition: ClientData.h:275
int GetPitchHeight(int factor) const
int GetNotePos(int p) const
void Zoom(int y, float multiplier, bool center)
NoteTrackDisplayData(const NoteTrack &track, const wxRect &r)
const NoteTrack & mTrack
static const float ZoomStep
void ZoomTo(int start, int end)
A Track that is used for Midi notes. (Somewhat old code).
Definition: NoteTrack.h:78
Persistent data for display of a note track.
void WriteXML(XMLWriter &xmlFile) const override
Default implementation does nothing.
std::unique_ptr< NoteTrackAttachment > Clone() const override
static NoteTrackRange & Get(const NoteTrack &track)
Allow mutative access to attached data of a const track.
void ZoomAllNotes(Alg_seq *pSeq)
Zooms so that all notes are visible.
void SetBottomNote(int note)
Sets the bottom note (a pitch), making sure that it is never greater than the top note.
void ShiftNoteRange(int offset)
Shifts all notes vertically by the given pitch.
void SetNoteRange(int note1, int note2)
Sets the top and bottom note (both pitches) automatically, swapping them if needed.
~NoteTrackRange() override
void SetTopNote(int note)
Sets the top note (a pitch), making sure that it is never less than the bottom note.
bool HandleAttribute(const Attribute &attribute) override
Return whether the attribute was used; default returns false.
Base class for XMLFileWriter and XMLStringWriter that provides the general functionality for creating...
Definition: XMLWriter.h:25
void WriteAttr(const wxString &name, const Identifier &value)
Definition: XMLWriter.h:36
void swap(std::unique_ptr< Alg_seq > &a, std::unique_ptr< Alg_seq > &b)
Definition: NoteTrack.cpp:634
const char * end(const char *str) noexcept
Definition: StringUtils.h:106