#pragma once #include #include #include #include "core/time.h" #include "core/note.h" namespace kku { template ::value>> class Timeline { public: explicit Timeline() : _current_offset(0) {} typedef typename std::set::const_iterator Iterator; void recalculate(const microsec& offset) { _current_offset = offset; expire(_top_note); if (!_timeline.empty()) { Iterator head_iterator = _timeline.begin(); while (!isExpired(head_iterator)) { if ((*head_iterator)->getPerfectOffset() >= offset) { Iterator pre_head = head_iterator; --pre_head; _top_note = !isExpired(pre_head) && (*pre_head)->isActive(offset) ? pre_head : head_iterator; break; } ++head_iterator; } if (isExpired(_top_note)) _top_note = _timeline.begin(); } } void setNotes(const std::set& notes) { _timeline = std::move(notes); recalculate(_current_offset); if (isExpired(_top_note)) return; } void insertNote(TNote* note) { _top_note = _timeline.insert(note).first; recalculate(_current_offset); update(_current_offset); } void insertNotes(const std::set& notes) { _timeline.insert(notes.begin(), notes.end()); recalculate(_current_offset); update(_current_offset); } inline void clear() { for (auto& note : _timeline) delete note; _timeline.clear(); } void update(const microsec& offset) { _current_offset = offset; updateTopNote(_current_offset); } Iterator getActiveNote(const microsec& music_offset) noexcept { Iterator return_note = _timeline.end(); auto note_iterator = _top_note; while (!isExpired(note_iterator)) { const auto& note = *note_iterator; if (note->isActive(music_offset)) { return_note = note_iterator; break; } else if (note->getPerfectOffset() > music_offset) break; ++note_iterator; } return return_note; } inline Iterator getNoteBy(const microsec& music_offset) noexcept { return std::find_if(_timeline.begin(), _timeline.end(), [music_offset](const auto& note) { return note->getPerfectOffset() == music_offset; }); } inline bool isExpired(const Iterator& iterator) const noexcept { return iterator == _timeline.end(); } inline void expire(Iterator& iterator) noexcept { iterator = _timeline.end(); } inline Iterator getTopNote() const noexcept { return _top_note; } inline Iterator begin() const noexcept { return _timeline.begin(); } inline Iterator end() const noexcept { return _timeline.end(); } private: std::set _timeline; microsec _current_offset; inline void updateTopNote(const microsec& music_offset) noexcept { if (isExpired(_top_note)) return; const auto& top_note = *_top_note; bool already_played = top_note->getPerfectOffset() < music_offset && !top_note->isActive(music_offset); if (already_played) ++_top_note; } Iterator _top_note; }; }