project-kyoku/include/core/timeline.h

159 lines
3.8 KiB
C++

#pragma once
#include <algorithm>
#include <memory>
#include <set>
#include "core/note.h"
#include "core/time.h"
namespace kku
{
template <class TNote,
class = std::enable_if_t<std::is_base_of<Note, TNote>::value>>
class Timeline
{
public:
explicit Timeline() : _current_offset(0) {}
typedef typename std::set<TNote *>::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<TNote *, NotePtrComparator> &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<TNote *, NotePtrComparator> &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<TNote *, NotePtrComparator> _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;
};
} // namespace kku