InstrumentCore can do tweens now
parent
9c282a5e0e
commit
46eb1cb9b4
6
notes
6
notes
|
@ -45,15 +45,15 @@ project data {
|
|||
|
||||
TODO {
|
||||
immediate frontburner {
|
||||
- pseudoport (L) for legato (note-on for already-playing note)
|
||||
- ` for note-off
|
||||
- instrumentcore tweens (unordered_multimap...)
|
||||
actual support for commands in InstrumentCore
|
||||
node function to release unneeded old data when stopping playback?
|
||||
}
|
||||
|
||||
# make knob notches more even (currently "previous value" is twice as big as any other step at px>1)
|
||||
add standardized step values for knobs (int enum?)
|
||||
|
||||
include Arcon (rounded) for use as unified patchboard font?
|
||||
switch InstrumentCore over to central storage for notes/tweens
|
||||
|
||||
bugs to fix {
|
||||
-? graph connections sometimes spawn in duplicated :|
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "instrumentcore.h"
|
||||
using namespace Xybrid::NodeLib;
|
||||
using Note = InstrumentCore::Note;
|
||||
using Tween = InstrumentCore::Tween;
|
||||
|
||||
#include "data/porttypes.h"
|
||||
using namespace Xybrid::Data;
|
||||
|
@ -17,6 +18,8 @@ Note::Note(uint16_t id) {
|
|||
void InstrumentCore::reset() {
|
||||
activeNotes.clear();
|
||||
activeNotes.reserve(16+1);
|
||||
activeTweens.clear();
|
||||
activeTweens.reserve(16*3+1);
|
||||
|
||||
smpTime = 1.0 / audioEngine->curSampleRate();
|
||||
time = 0;
|
||||
|
@ -57,6 +60,8 @@ namespace {
|
|||
|
||||
struct NoteIntern {
|
||||
bool markedForDeletion = false;
|
||||
|
||||
decltype(InstrumentCore::activeTweens.equal_range(0)) tweenSet;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -72,6 +77,7 @@ void InstrumentCore::process(CommandPort* i, AudioPort* o) {
|
|||
auto& note = sc.first->second;
|
||||
note.note = n;
|
||||
if (!sc.second) {
|
||||
removeTweens(note, ¬e.note); // stop any note-value tweens
|
||||
if (note.adsrPhase == 2) note = Note(id); // reinstantiate on replace
|
||||
if (onNoteLegato) onNoteLegato(note);
|
||||
} else {
|
||||
|
@ -94,31 +100,44 @@ void InstrumentCore::process(CommandPort* i, AudioPort* o) {
|
|||
// then do the thing
|
||||
if (o) o->pull();
|
||||
|
||||
double tickTime = smpTime * audioEngine->curTickSize();
|
||||
|
||||
if (processNote) {
|
||||
for (auto p = activeNotes.begin(); p != activeNotes.end(); ) {
|
||||
auto& note = p->second;
|
||||
NoteIntern n;
|
||||
note.intern = &n;
|
||||
|
||||
n.tweenSet = activeTweens.equal_range(note.id);
|
||||
for (auto it = n.tweenSet.first; it != n.tweenSet.second; ++it) it->second.startTick(note, tickTime);
|
||||
|
||||
processNote(note, o);
|
||||
if (n.markedForDeletion) {
|
||||
if (onDeleteNote) onDeleteNote(note);
|
||||
auto pr = p;
|
||||
p++;
|
||||
activeNotes.erase(pr);
|
||||
activeTweens.erase(note.id);
|
||||
p = activeNotes.erase(p);
|
||||
continue;
|
||||
}
|
||||
// stuff
|
||||
for (auto it = n.tweenSet.first; it != n.tweenSet.second; ) {
|
||||
if (it->second.flags & 1) it = activeTweens.erase(it);
|
||||
else ++it;
|
||||
}
|
||||
|
||||
note.intern = nullptr;
|
||||
p++;
|
||||
++p;
|
||||
}
|
||||
}
|
||||
time += smpTime * audioEngine->curTickSize();
|
||||
time += tickTime;
|
||||
}
|
||||
|
||||
void InstrumentCore::advanceNote(Note& n) {
|
||||
//auto& ni = *reinterpret_cast<NoteIntern*>(n.intern);
|
||||
auto& ni = *reinterpret_cast<NoteIntern*>(n.intern);
|
||||
n.time += smpTime;
|
||||
n.adsrTime += smpTime;
|
||||
|
||||
for (auto it = ni.tweenSet.first; it != ni.tweenSet.second; ++it) it->second.process(n);
|
||||
|
||||
if (n.adsrPhase == 0) {
|
||||
if (n.adsrTime > n.adsr.a) {
|
||||
n.adsrPhase++;
|
||||
|
@ -137,13 +156,60 @@ void InstrumentCore::deleteNote(Note& n) {
|
|||
ni.markedForDeletion = true;
|
||||
} else {
|
||||
if (onDeleteNote) onDeleteNote(n);
|
||||
activeTweens.erase(n.id);
|
||||
activeNotes.erase(n.id);
|
||||
}
|
||||
}
|
||||
|
||||
void InstrumentCore::removeTweens(InstrumentCore::Note& n, double* op) {
|
||||
auto r = activeTweens.equal_range(n.id);
|
||||
for (auto it = r.first; it != r.second; ) {
|
||||
if (it->second.op == op) it = activeTweens.erase(it);
|
||||
else ++it;
|
||||
}
|
||||
}
|
||||
void InstrumentCore::removeTweens(InstrumentCore::Note& n) { activeTweens.erase(n.id); }
|
||||
|
||||
void InstrumentCore::startTween(InstrumentCore::Note& n, double* op, double val, double time, int16_t ticks) {
|
||||
// remove anything already operating on the same note
|
||||
removeTweens(n, op);
|
||||
activeTweens.emplace(std::make_pair(n.id, Tween(n, op, val, time, ticks)));
|
||||
}
|
||||
|
||||
double Note::ampMult() const {
|
||||
double a = adsrVol(adsr, adsrPhase, adsrTime);
|
||||
a *= a;
|
||||
a *= a;
|
||||
return a;
|
||||
}
|
||||
|
||||
Tween::Tween(InstrumentCore::Note& n, double* op, double val, double time, int16_t ticks) {
|
||||
this->noteId = n.id;
|
||||
this->op = op;
|
||||
valStart = *op;
|
||||
valEnd = val;
|
||||
if (ticks >= 0) ticksLeft = ticks;
|
||||
else {
|
||||
timeStart = n.time;
|
||||
timeEnd = timeStart + time;
|
||||
}
|
||||
}
|
||||
|
||||
void Tween::startTick(Note& n, double tickTime) {
|
||||
if (ticksLeft >= 0) {
|
||||
timeStart = n.time;
|
||||
timeEnd = timeStart + tickTime * ticksLeft;
|
||||
valStart = *op;
|
||||
ticksLeft--;
|
||||
}
|
||||
}
|
||||
|
||||
void Tween::process(Note& n) {
|
||||
if (timeEnd == timeStart) {
|
||||
flags |= 1;
|
||||
return;
|
||||
}
|
||||
double p = std::clamp((n.time - timeStart) / (timeEnd - timeStart), 0.0, 1.0);
|
||||
*op = valStart * (1.0-p) + valEnd * p;
|
||||
if (n.time >= timeEnd) flags |= 1; // mark finished
|
||||
}
|
||||
|
|
|
@ -46,7 +46,31 @@ namespace Xybrid::NodeLib {
|
|||
|
||||
double ampMult() const;
|
||||
};
|
||||
|
||||
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&);
|
||||
};
|
||||
|
||||
std::unordered_map<uint16_t, Note> activeNotes;
|
||||
std::unordered_multimap<uint16_t, Tween> activeTweens;
|
||||
|
||||
std::function<void(Note&, Data::AudioPort*)> processNote;
|
||||
std::function<void(Note&)> onNoteOn;
|
||||
|
@ -65,5 +89,11 @@ namespace Xybrid::NodeLib {
|
|||
void advanceNote(Note&);
|
||||
void deleteNote(Note&);
|
||||
|
||||
/// Removes tweens matching the specified note and target.
|
||||
void removeTweens(Note&, double*);
|
||||
/// Removes all tweens matching the specified note.
|
||||
void removeTweens(Note&);
|
||||
void startTween(Note&, double*, double val, double time, int16_t ticks = -1);
|
||||
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue