Etterna 0.74.4
Loading...
Searching...
No Matches
NoteData.h
1#ifndef NOTE_DATA_H
2#define NOTE_DATA_H
3
4#include "NoteDataStructures.h"
5#include "Etterna/Models/Misc/NoteTypes.h"
6
7#include <iterator>
8#include <map>
9#include <set>
10#include <vector>
11
12class TimingData;
13
15#define FOREACH_NONEMPTY_ROW_IN_TRACK(nd, track, row) \
16 for (int(row) = -1; (nd).GetNextTapNoteRowForTrack(track, row);)
19#define FOREACH_NONEMPTY_ROW_IN_TRACK_RANGE(nd, track, row, start, last) \
20 for (int(row) = (start)-1; \
21 (nd).GetNextTapNoteRowForTrack(track, row) && (row) < (last);)
24#define FOREACH_NONEMPTY_ROW_IN_TRACK_RANGE_REVERSE( \
25 nd, track, row, start, last) \
26 for (int(row) = last; \
27 (nd).GetPrevTapNoteRowForTrack(track, row) && (row) >= (start);)
29#define FOREACH_NONEMPTY_ROW_ALL_TRACKS(nd, row) \
30 for (int(row) = -1; (nd).GetNextTapNoteRowForAllTracks(row);)
33#define FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE(nd, row, start, last) \
34 for (int(row) = (start)-1; \
35 (nd).GetNextTapNoteRowForAllTracks(row) && (row) < (last);)
36
37#define FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE_REVERSE(nd, row, start, last) \
38 for (int(row) = last; \
39 (nd).GetPrevTapNoteRowForAllTracks(row) && (row) >= (start);)
40
43{
44 public:
45 using TrackMap = std::map<int, TapNote>;
46 using iterator = std::map<int, TapNote>::iterator;
47 using const_iterator = std::map<int, TapNote>::const_iterator;
48 using reverse_iterator = std::map<int, TapNote>::reverse_iterator;
49 using const_reverse_iterator =
50 std::map<int, TapNote>::const_reverse_iterator;
51
52 NoteData() { m_numTracksLCD = 0; }
53
54 auto begin(int iTrack) -> iterator { return m_TapNotes[iTrack].begin(); }
55 auto begin(int iTrack) const -> const_iterator
56 {
57 return m_TapNotes[iTrack].begin();
58 }
59 auto rbegin(int iTrack) -> reverse_iterator
60 {
61 return m_TapNotes[iTrack].rbegin();
62 }
63 auto rbegin(int iTrack) const -> const_reverse_iterator
64 {
65 return m_TapNotes[iTrack].rbegin();
66 }
67 auto end(int iTrack) -> iterator { return m_TapNotes[iTrack].end(); }
68 auto end(int iTrack) const -> const_iterator
69 {
70 return m_TapNotes[iTrack].end();
71 }
72 auto rend(int iTrack) -> reverse_iterator
73 {
74 return m_TapNotes[iTrack].rend();
75 }
76 auto rend(int iTrack) const -> const_reverse_iterator
77 {
78 return m_TapNotes[iTrack].rend();
79 }
80 auto lower_bound(int iTrack, int iRow) -> iterator
81 {
82 return m_TapNotes[iTrack].lower_bound(iRow);
83 }
84 auto lower_bound(int iTrack, int iRow) const -> const_iterator
85 {
86 return m_TapNotes[iTrack].lower_bound(iRow);
87 }
88 auto upper_bound(int iTrack, int iRow) -> iterator
89 {
90 return m_TapNotes[iTrack].upper_bound(iRow);
91 }
92 auto upper_bound(int iTrack, int iRow) const -> const_iterator
93 {
94 return m_TapNotes[iTrack].upper_bound(iRow);
95 }
96 void swap(NoteData& nd)
97 {
98 m_TapNotes.swap(nd.m_TapNotes);
99 m_atis.swap(nd.m_atis);
100 m_const_atis.swap(nd.m_const_atis);
101 }
102
103 // This is ugly to make it templated but I don't want to have to write the
104 // same class twice.
105 template<typename ND, typename iter, typename TN>
107 {
108 ND* m_pNoteData;
109 std::vector<iter> m_vBeginIters;
110
111 /* There isn't a "past the beginning" iterator so this is hard to make a
112 * true bidirectional iterator. Use the "past the end" iterator in place
113 * of the "past the beginning" iterator when in reverse. */
114 std::vector<iter> m_vCurrentIters;
115
116 std::vector<iter> m_vEndIters;
117 int m_iTrack;
118 bool m_bReverse;
119
120 // These exist so that the iterator can be revalidated if the NoteData
121 // is transformed during this iterator's lifetime.
122 std::vector<int> m_PrevCurrentRows;
123 bool m_Inclusive;
124 int m_StartRow;
125 int m_EndRow;
126
127 void Find(bool bReverse);
128
129 public:
131 int iStartRow,
132 int iEndRow,
133 bool bReverse,
134 bool bInclusive);
137 auto operator++() -> _all_tracks_iterator&; // preincrement
138 auto operator++(int dummy) -> _all_tracks_iterator; // postincrement
139 //_all_tracks_iterator &operator--(); // predecrement
140 //_all_tracks_iterator operator--( int dummy ); // postdecrement
141 [[nodiscard]] auto Track() const -> int { return m_iTrack; }
142 [[nodiscard]] auto Row() const -> int
143 {
144 return m_vCurrentIters[m_iTrack]->first;
145 }
146 [[nodiscard]] auto IsAtEnd() const -> bool { return m_iTrack == -1; }
147 [[nodiscard]] auto GetIter(int iTrack) const -> iter
148 {
149 return m_vCurrentIters[iTrack];
150 }
151
152 auto operator*() -> TN&
153 {
154 DEBUG_ASSERT(!IsAtEnd());
155 return m_vCurrentIters[m_iTrack]->second;
156 }
157
158 auto operator->() -> TN*
159 {
160 DEBUG_ASSERT(!IsAtEnd());
161 return &m_vCurrentIters[m_iTrack]->second;
162 }
163
164 auto operator*() const -> const TN&
165 {
166 DEBUG_ASSERT(!IsAtEnd());
167 return m_vCurrentIters[m_iTrack]->second;
168 }
169
170 auto operator->() const -> const TN*
171 {
172 DEBUG_ASSERT(!IsAtEnd());
173 return &m_vCurrentIters[m_iTrack]->second;
174 }
175 // Use when transforming the NoteData.
176 void Revalidate(ND* notedata,
177 std::vector<int> const& added_or_removed_tracks,
178 bool added);
179 };
180 using all_tracks_iterator =
186 friend class _all_tracks_iterator<NoteData, iterator, TapNote>;
187 friend class _all_tracks_iterator<const NoteData,
188 const_iterator,
189 const TapNote>;
190
191 private:
192 // There's no point in inserting empty notes into the map.
193 // Any blank space in the map is defined to be empty.
194 std::vector<TrackMap> m_TapNotes;
195 int m_numTracksLCD;
196
197 void CalcNumTracksLCD();
198
204 static auto IsTap(const TapNote& tn, int row) -> bool;
205
211 static auto IsMine(const TapNote& tn, int row) -> bool;
212
218 static auto IsLift(const TapNote& tn, int row) -> bool;
219
225 static auto IsFake(const TapNote& tn, int row) -> bool;
226
227 // These exist so that they can be revalidated when something that
228 // transforms the NoteData occurs. -Kyz
229 mutable std::set<all_tracks_iterator*> m_atis;
230 mutable std::set<all_tracks_const_iterator*> m_const_atis;
231
232 void AddATIToList(all_tracks_iterator* iter) const;
233 void AddATIToList(all_tracks_const_iterator* iter) const;
234 void RemoveATIFromList(all_tracks_iterator* iter) const;
235 void RemoveATIFromList(all_tracks_const_iterator* iter) const;
236
237 // Mina stuf
238 std::vector<int> NonEmptyRowVector;
239 std::vector<NoteInfo> SerializedNoteData;
240
241 public:
242 void Init();
243
244 // Mina stuf
245 void LogNonEmptyRows(TimingData* ts);
246 void UnsetNerv()
247 {
248 NonEmptyRowVector.clear();
249 NonEmptyRowVector.shrink_to_fit();
250 }
251 void UnsetSerializedNoteData()
252 {
253 SerializedNoteData.clear();
254 SerializedNoteData.shrink_to_fit();
255 }
256 auto BuildAndGetNerv(TimingData* ts) -> const std::vector<int>&
257 {
258 LogNonEmptyRows(ts);
259 return NonEmptyRowVector;
260 }
261 auto WifeTotalScoreCalc(TimingData* td,
262 int iStartIndex = 0,
263 int iEndIndex = MAX_NOTE_ROW) const -> int;
264 auto GetNonEmptyRowVector() -> std::vector<int>&
265 {
266 return NonEmptyRowVector;
267 };
268 auto SerializeNoteData(const std::vector<float>& etaner)
269 -> const std::vector<NoteInfo>&;
270 // faster than the above and gives us more control over stuff like nerv
271 // generation
272 auto SerializeNoteData2(TimingData* ts,
273 bool unset_nerv_when_done = true,
274 bool unset_etaner_when_done = true)
275 -> const std::vector<NoteInfo>&;
276
277 auto GetNumTracks() const -> int
278 {
279 return static_cast<int>(m_TapNotes.size());
280 }
281 void SetNumTracks(int iNewNumTracks);
282 auto operator==(const NoteData& nd) const -> bool
283 {
284 return m_TapNotes == nd.m_TapNotes;
285 }
286 auto operator!=(const NoteData& nd) const -> bool
287 {
288 return m_TapNotes != nd.m_TapNotes;
289 }
290
291 /* Return the note at the given track and row. Row may be out of
292 * range; pretend the song goes on with TAP_EMPTYs indefinitely. */
293 auto GetTapNote(const unsigned& track, const int& row) const
294 -> const TapNote&
295 {
296 const auto& mapTrack = m_TapNotes[track];
297 const auto iter = mapTrack.find(row);
298 if (iter != mapTrack.end()) {
299 return iter->second;
300 }
301 {
302 return TAP_EMPTY;
303 }
304 }
305
306 auto FindTapNote(unsigned iTrack, int iRow) -> iterator
307 {
308 return m_TapNotes[iTrack].find(iRow);
309 }
310
311 auto FindTapNote(unsigned iTrack, int iRow) const -> const_iterator
312 {
313 return m_TapNotes[iTrack].find(iRow);
314 }
315 void RemoveTapNote(unsigned iTrack, iterator it)
316 {
317 m_TapNotes[iTrack].erase(it);
318 }
319
333 void GetTapNoteRange(int iTrack,
334 int iStartRow,
335 int iEndRow,
336 TrackMap::const_iterator& begin,
337 TrackMap::const_iterator& end) const;
345 void GetTapNoteRange(int iTrack,
346 int iStartRow,
347 int iEndRow,
348 TrackMap::iterator& begin,
349 TrackMap::iterator& end);
350 auto GetTapNoteRangeAllTracks(int iStartRow,
351 int iEndRow,
352 bool bInclusive = false)
353 -> all_tracks_iterator
354 {
355 return all_tracks_iterator(
356 *this, iStartRow, iEndRow, false, bInclusive);
357 }
358 auto GetTapNoteRangeAllTracks(int iStartRow,
359 int iEndRow,
360 bool bInclusive = false) const
361 -> all_tracks_const_iterator
362 {
363 return all_tracks_const_iterator(
364 *this, iStartRow, iEndRow, false, bInclusive);
365 }
366 auto GetTapNoteRangeAllTracksReverse(int iStartRow,
367 int iEndRow,
368 bool bInclusive = false)
369 -> all_tracks_reverse_iterator
370 {
371 return all_tracks_iterator(*this, iStartRow, iEndRow, true, bInclusive);
372 }
373 auto GetTapNoteRangeAllTracksReverse(int iStartRow,
374 int iEndRow,
375 bool bInclusive = false) const
376 -> all_tracks_const_reverse_iterator
377 {
378 return all_tracks_const_iterator(
379 *this, iStartRow, iEndRow, true, bInclusive);
380 }
381
382 // Call this after using any transform that changes the NoteData.
383 void RevalidateATIs(std::vector<int> const& added_or_removed_tracks,
384 bool added);
385
386 /* Return an iterator range include iStartRow to iEndRow. Extend the range
387 * to include hold notes overlapping the boundary. */
388 void GetTapNoteRangeInclusive(int iTrack,
389 int iStartRow,
390 int iEndRow,
391 TrackMap::const_iterator& begin,
392 TrackMap::const_iterator& end,
393 bool bIncludeAdjacent = false) const;
394 void GetTapNoteRangeInclusive(int iTrack,
395 int iStartRow,
396 int iEndRow,
397 TrackMap::iterator& begin,
398 TrackMap::iterator& end,
399 bool bIncludeAdjacent = false);
400
401 /* Return an iterator range include iStartRow to iEndRow. Shrink the range
402 * to exclude hold notes overlapping the boundary. */
403 void GetTapNoteRangeExclusive(int iTrack,
404 int iStartRow,
405 int iEndRow,
406 TrackMap::const_iterator& begin,
407 TrackMap::const_iterator& end) const;
408 void GetTapNoteRangeExclusive(int iTrack,
409 int iStartRow,
410 int iEndRow,
411 TrackMap::iterator& begin,
412 TrackMap::iterator& end);
413
414 /* Returns the row of the first TapNote on the track that has a row greater
415 * than rowInOut. */
416 auto GetNextTapNoteRowForTrack(int track,
417 int& rowInOut,
418 bool ignoreAutoKeysounds = false) const
419 -> bool;
420 auto GetNextTapNoteRowForAllTracks(int& rowInOut) const -> bool;
421 auto GetPrevTapNoteRowForTrack(int track, int& rowInOut) const -> bool;
422 auto GetPrevTapNoteRowForAllTracks(int& rowInOut) const -> bool;
423
424 void MoveTapNoteTrack(int dest, int src);
425 void SetTapNote(int track, int row, const TapNote& tn);
433 void AddHoldNote(int iTrack, int iStartRow, int iEndRow, TapNote tn);
434
435 void ClearRangeForTrack(int rowBegin, int rowEnd, int iTrack);
436 void ClearRange(int rowBegin, int rowEnd);
437 void ClearAll();
438 void CopyRange(const NoteData& from,
439 int rowFromBegin,
440 int rowFromEnd,
441 int rowToBegin = 0);
442 void CopyAll(const NoteData& from);
443
444 auto IsRowEmpty(int row) const -> bool;
445 auto IsRangeEmpty(int track, int rowBegin, int rowEnd) const -> bool;
446 auto GetNumTapNonEmptyTracks(int row) const -> int;
447 void GetTapNonEmptyTracks(int row, std::set<int>& addTo) const;
448 auto GetTapFirstNonEmptyTrack(int row, int& iNonEmptyTrackOut) const
449 -> bool; // return false if no non-empty tracks at row
450 auto GetTapFirstEmptyTrack(int row, int& iEmptyTrackOut) const
451 -> bool; // return false if no non-empty tracks at row
452 auto GetTapLastEmptyTrack(int row, int& iEmptyTrackOut) const
453 -> bool; // return false if no empty tracks at row
454 auto GetNumTracksWithTap(int row) const -> int;
455 auto GetNumTracksWithTapOrHoldHead(int row) const -> int;
456 auto GetFirstTrackWithTap(int row) const -> int;
457 auto GetFirstTrackWithTapOrHoldHead(int row) const -> int;
458 auto GetLastTrackWithTapOrHoldHead(int row) const -> int;
459
460 auto IsThereATapAtRow(int row) const -> bool
461 {
462 return GetFirstTrackWithTap(row) != -1;
463 }
464
465 auto IsThereATapOrHoldHeadAtRow(int row) const -> bool
466 {
467 return GetFirstTrackWithTapOrHoldHead(row) != -1;
468 }
469 void GetTracksHeldAtRow(int row, std::set<int>& addTo) const;
470 auto GetNumTracksHeldAtRow(int row) -> int;
471
472 auto IsHoldNoteAtRow(int iTrack, int iRow, int* pHeadRow = nullptr) const
473 -> bool;
474 auto IsHoldHeadOrBodyAtRow(int iTrack, int iRow, int* pHeadRow) const
475 -> bool;
476
477 // statistics
478 auto IsEmpty() const -> bool;
479 auto IsTrackEmpty(int iTrack) const -> bool
480 {
481 return m_TapNotes[iTrack].empty();
482 }
483 auto GetFirstRow() const -> int; // return the beat number of the first note
484 auto GetLastRow() const -> int; // return the beat number of the last note
485 auto GetFirstBeat() const -> float { return NoteRowToBeat(GetFirstRow()); }
486 auto GetLastBeat() const -> float { return NoteRowToBeat(GetLastRow()); }
487 auto GetNumTapNotes(int iStartIndex = 0, int iEndIndex = MAX_NOTE_ROW) const
488 -> int;
489 auto GetNumTapNotesNoTiming(int iStartIndex = 0,
490 int iEndIndex = MAX_NOTE_ROW) const -> int;
491 auto GetNumTapNotesInRow(int iRow) const -> int;
492 auto GetNumMines(int iStartIndex = 0, int iEndIndex = MAX_NOTE_ROW) const
493 -> int;
494 auto GetNumRowsWithTap(int iStartIndex = 0,
495 int iEndIndex = MAX_NOTE_ROW) const -> int;
496 auto GetNumRowsWithTapOrHoldHead(int iStartIndex = 0,
497 int iEndIndex = MAX_NOTE_ROW) const -> int;
498 /* Optimization: for the default of start to end, use the second (faster).
499 * XXX: Second what? -- Steve */
500 auto GetNumHoldNotes(int iStartIndex = 0,
501 int iEndIndex = MAX_NOTE_ROW) const -> int;
502 auto GetNumRolls(int iStartIndex = 0, int iEndIndex = MAX_NOTE_ROW) const
503 -> int;
504
505 // Count rows that contain iMinTaps or more taps.
506 auto GetNumRowsWithSimultaneousTaps(int iMinTaps,
507 int iStartIndex = 0,
508 int iEndIndex = MAX_NOTE_ROW) const
509 -> int;
510 auto GetNumJumps(int iStartIndex = 0, int iEndIndex = MAX_NOTE_ROW) const
511 -> int
512 {
513 return GetNumRowsWithSimultaneousTaps(2, iStartIndex, iEndIndex);
514 }
515
516 // This row needs at least iMinSimultaneousPresses either tapped or held.
517 auto RowNeedsAtLeastSimultaneousPresses(int iMinSimultaneousPresses,
518 int row) const -> bool;
519 auto RowNeedsHands(int row) const -> bool
520 {
521 return RowNeedsAtLeastSimultaneousPresses(3, row);
522 }
523
524 // Count rows that need iMinSimultaneousPresses either tapped or held.
525 auto GetNumRowsWithSimultaneousPresses(int iMinSimultaneousPresses,
526 int iStartIndex = 0,
527 int iEndIndex = MAX_NOTE_ROW) const
528 -> int;
529 auto GetNumHands(int iStartIndex = 0, int iEndIndex = MAX_NOTE_ROW) const
530 -> int
531 {
532 return GetNumRowsWithSimultaneousPresses(3, iStartIndex, iEndIndex);
533 }
534 auto GetNumQuads(int iStartIndex = 0, int iEndIndex = MAX_NOTE_ROW) const
535 -> int
536 {
537 return GetNumRowsWithSimultaneousPresses(4, iStartIndex, iEndIndex);
538 }
539
540 // and the other notetypes
541 auto GetNumLifts(int iStartIndex = 0, int iEndIndex = MAX_NOTE_ROW) const
542 -> int;
543 auto GetNumFakes(int iStartIndex = 0, int iEndIndex = MAX_NOTE_ROW) const
544 -> int;
545
546 auto GetNumTracksLCD() const -> int;
547
548 // Transformations
549 void LoadTransformed(
550 const NoteData& in,
551 int iNewNumTracks,
552 const int iOriginalTrackToTakeFrom[]); // -1 for iOriginalTracksToTakeFrom
553 // means no track
554
555 // XML
556 auto CreateNode() const -> XNode*;
557 static void LoadFromNode(const XNode* pNode);
558};
559#endif
Definition NoteData.h:107
Holds data about the notes that the player is supposed to hit.
Definition NoteData.h:43
void GetTapNoteRange(int iTrack, int iStartRow, int iEndRow, TrackMap::const_iterator &begin, TrackMap::const_iterator &end) const
Return an iterator range for [rowBegin,rowEnd).
Definition NoteData.cpp:1171
void AddHoldNote(int iTrack, int iStartRow, int iEndRow, TapNote tn)
Add a hold note, merging other overlapping holds and destroying tap notes underneath.
Definition NoteData.cpp:450
Holds data for translating beats<->seconds.
Definition TimingData.h:54
Definition XmlFile.h:95
The various properties of a tap note.
Definition NoteTypes.h:149