xybrid/xybrid/nodelib/instrumentcore.h

144 lines
4.2 KiB
C++

#pragma once
#include <memory>
#include <functional>
#include <unordered_map>
#ifdef WITH_BOOST
#include <boost/container/pmr/memory_resource.hpp>
#include <boost/container/pmr/polymorphic_allocator.hpp>
using boost::container::pmr::polymorphic_allocator;
template <class Key,
class T,
class Hash = std::hash<Key>,
class Pred = std::equal_to<Key>>
using unordered_map = std::unordered_map<Key, T, Hash, Pred, polymorphic_allocator<std::pair<const Key,T>>>;
template <class Key, class T,
class Hash = std::hash<Key>,
class Pred = std::equal_to<Key>>
using unordered_multimap = std::unordered_multimap<Key, T, Hash, Pred, polymorphic_allocator<std::pair<const Key,T>>>;
#else
using std::pmr::unordered_map;
using std::pmr::unordered_multimap;
#endif
#include <array>
#include "nodelib/basics.h"
#include "data/node.h"
#include "util/mem.h"
namespace Xybrid::Data {
class CommandPort;
class AudioPort;
}
namespace Xybrid::NodeLib {
class ParamReader;
/*!
* \class InstrumentCore
*
* Helper class to form the core of an instrument node.
*
* Not mandatory by any means, but handles all the "standard" commands for you.
*/
class InstrumentCore {
public:
class Note {
friend class InstrumentCore;
InstrumentCore* core = nullptr;
void* intern = nullptr;
public:
uint16_t id;
double note = 64.0; // floating point to allow smooth pitch bends
double noteAdd = 0.0;
double time = 0.0;
double volume = 1.0;
double pan = 0.0;
ADSR adsr;
double adsrTime = 0;
uint8_t adsrPhase = 0;
union {
std::array<double, 5> scratch {0.0};
std::array<void*, 5> ptr;
};
template<typename T> inline T& scratchAs() { return *(reinterpret_cast<T*>(reinterpret_cast<void*>(&scratch))); }
Note() = default;
Note(InstrumentCore*, uint16_t id);
double effectiveNote() const;
double ampMult() const;
};
friend class Note;
class Tween {
friend class InstrumentCore;
public:
uint16_t noteId;
int16_t ticksLeft = -1;
uint8_t flags = 0;
double timeStart = 0.0;
double timeEnd = 0.0;
double valStart = 0.0;
double valEnd = 0.0;
double* op;
Tween(Note&, double*, double val, double time, int16_t ticks = -1);
void startTick(Note&, double tickTime);
void process(Note&, double smpTime);
};
private:
//
double time;
double smpTime;
public:
double volume = 1.0;
double pan = 0.0;
unordered_map<uint16_t, Note> activeNotes = {16, Util::ralloc};
unordered_multimap<uint16_t, Tween> activeTweens = {16, Util::ralloc};
std::function<bool(Note*, const ParamReader&)> paramFilter;
std::unordered_map<uint8_t, std::function<bool(const ParamReader&)>> globalParam;
std::unordered_map<uint8_t, std::function<bool(Note&, const ParamReader&)>> localParam;
std::function<void(Note&, Data::AudioPort*)> processNote;
std::function<void(Note&)> onNoteOn;
std::function<void(Note&)> onNoteLegato;
std::function<void(Note&, bool)> onNoteOff;
std::function<void(Note&)> onDeleteNote;
InstrumentCore() = default;
~InstrumentCore();
inline double globalTime() const { return time; }
inline double sampleTime() const { return smpTime; }
void release();
void reset();
void process(Data::Node*);
void process(Data::CommandPort*, Data::AudioPort* = nullptr);
void advanceNote(Note&);
void deleteNote(Note&);
/// Removes tween matching the specified note and target.
void removeTween(Note&, double*);
/// Removes all tweens matching the specified note.
void removeTweens(Note&);
Tween* findTween(Note&, double*);
Tween& startTween(Note&, double*, double val, double time, int16_t ticks = -1);
};
}