forked from NaiJi/project-kyoku
159 lines
3.8 KiB
C++
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> ¬es)
|
|
{
|
|
_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> ¬es)
|
|
{
|
|
_timeline.insert(notes.begin(), notes.end());
|
|
recalculate(_current_offset);
|
|
update(_current_offset);
|
|
}
|
|
|
|
inline void clear()
|
|
{
|
|
for (auto ¬e : _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 ¬e = *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 ¬e) {
|
|
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
|