InstrumentCore can do tweens now

portability/boost
zetaPRIME 2019-01-27 02:23:04 -05:00
parent 9c282a5e0e
commit 46eb1cb9b4
3 changed files with 105 additions and 9 deletions

6
notes
View File

@ -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 :|

View File

@ -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, &note.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
}

View File

@ -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);
};
}