#include <cstdint>
#include <set>
#include <iostream>
#include <algorithm>
#include <functional>
#include <vector>
template<typename T>
using CustomComparatorSet = std::set<T, std::function<bool (T const &, T const &)>>;
#define ITERABLE_BASED_ON(container) \
auto begin () noexcept { return container.begin (); } \
auto end () noexcept { return container.end (); } \
auto begin () const noexcept { return container.cbegin(); } \
auto end () const noexcept { return container.cend (); } \
auto cbegin() const noexcept { return container.cbegin(); } \
auto cend () const noexcept { return container.cend (); }
template<typename FwdIter, typename Func>
Func for_each_overlapping_adjacent_pair(FwdIter a_ItBegin, FwdIter a_ItEnd, Func a_Func)
{
if(a_ItBegin == a_ItEnd)
{
return a_Func;
}
FwdIter itNext = a_ItBegin;
++itNext;
while (itNext != a_ItEnd)
{
a_Func(*(a_ItBegin++), *(itNext++));
}
return a_Func;
}
struct SimpleNote
{
char m_Letter;
uint32_t m_DurationTU;
};
struct SimpleNoteEvent
{
uint64_t m_StartTU;
SimpleNote m_Note;
};
class MonophonicPart final
{
friend class MonophonicPartBuilder;
MonophonicPart()
: m_Notes {}
, m_NoteSet { [] (SimpleNoteEvent const * a_LHS, SimpleNoteEvent const * a_RHS)
{ return a_LHS->m_StartTU < a_RHS->m_StartTU; } }
{}
public:
ITERABLE_BASED_ON(m_Notes)
// TODO: replace with std::optional
std::pair<bool, SimpleNoteEvent const &> getNoteAt(uint64_t a_TU)
{
// Get first note that goes after selection
SimpleNoteEvent dummy{ a_TU, SimpleNote{ } };
auto it = m_NoteSet.upper_bound(&dummy);
if (it == m_NoteSet.begin())
{
// Selection goes before the first note in the part
return std::make_pair(false, dummy);
}
SimpleNoteEvent const & event = *(*(--it));
if (event.m_StartTU + event.m_Note.m_DurationTU > a_TU)
{
// Using make_pair introduces a temp obj that mess the & up
return { true, event };
}
else
{
return std::make_pair(false, dummy);
}
}
uint64_t getLengthTU()
{
if (m_NoteSet.empty())
{
return 0;
}
SimpleNoteEvent const * event = *m_NoteSet.rbegin();
return event->m_StartTU + event->m_Note.m_DurationTU;
}
private:
// Primary storage
std::vector<SimpleNoteEvent> m_Notes;
// Used for quicker access to notes based on a timeunit value
CustomComparatorSet<SimpleNoteEvent const *> m_NoteSet;
};
class MonophonicPartBuilder final
{
public:
MonophonicPartBuilder()
: m_Notes { }
, m_CurrTU { 0 }
{}
void addNote(char a_Letter, uint32_t a_DurationTU)
{
m_Notes.emplace_back(SimpleNoteEvent{ m_CurrTU, SimpleNote{ a_Letter, a_DurationTU } });
m_CurrTU += a_DurationTU;
}
void addRest(uint32_t a_DurationTU)
{
m_CurrTU += a_DurationTU;
}
MonophonicPart build()
{
MonophonicPart out;
// Create note set for fast timeunit-based search
for (auto const & note : m_Notes)
{
out.m_NoteSet.insert(¬e);
}
// Exchange our full note vector for an empty one
out.m_Notes.swap(m_Notes);
// Start back at 0 for the next build
m_CurrTU = 0;
// TODO: Verify that the returning value is moved
return out;
}
private:
std::vector<SimpleNoteEvent> m_Notes;
uint64_t m_CurrTU;
};
int main()
{
MonophonicPartBuilder builder;
builder.addRest( 16 );
builder.addNote('A', 32 );
builder.addRest( 32 );
builder.addNote('C', 32 );
builder.addNote('G', 16 );
builder.addRest( 32 );
builder.addRest( 32 );
builder.addNote('F', 164);
MonophonicPart part = builder.build();
for (SimpleNoteEvent const & noteEvent : part)
{
std::cout << noteEvent.m_StartTU << " "
<< noteEvent.m_Note.m_Letter << " "
<< noteEvent.m_Note.m_DurationTU << std::endl;
}
std::cout << "Length: " << part.getLengthTU() << std::endl;
std::vector<uint64_t> timeunits { 3, 15, 16, 17, 52, 87, 300, 10000 };
for (uint64_t tu : timeunits)
{
auto result = part.getNoteAt(tu);
std::cout << "At " << tu << ": ";
if (result.first)
std::cout << result.second.m_Note.m_Letter;
else
std::cout << "No note";
std::cout << std::endl;
}
std::multiset<int8_t> intervalSet;
for_each_overlapping_adjacent_pair(std::cbegin(part), std::cend(part),
[&intervalSet] (SimpleNoteEvent const & a_First,
SimpleNoteEvent const & a_Second)
{
intervalSet.insert(static_cast<int8_t>(a_Second.m_Note.m_Letter - a_First.m_Note.m_Letter));
});
for (int8_t interval : intervalSet)
{
std::cout << static_cast<int32_t>(interval) << " ";
}
}