You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

172 lines
3.8 KiB
C++

#pragma once
#include <set>
#include <memory>
#include <algorithm>
#include "core/time.h"
#include "core/note.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;
};
}