#include "ArrayArithmetic.h"
#include "DspVariableLine.h"
#include "PdGraph.h"
message::Object *DspVariableLine::new_object(pd::Message *init_message, PdGraph *graph) {
return new DspVariableLine(init_message, graph);
}
DspVariableLine::DspVariableLine(pd::Message *init_message, PdGraph *graph) : DspObject(3, 0, 0, 1, graph) {
numSamplesToTarget = 0.0f;
target = 0.0f;
slope = 0.0f;
lastOutputSample = 0.0f;
process_function = &processSignal;
process_functionNoMessage = &processSignal;
}
DspVariableLine::~DspVariableLine() {
}
void DspVariableLine::process_message(int inlet_index, pd::Message *message) {
switch (inlet_index) {
case 0: {
if (message->is_float(0)) {
float target = message->get_float(0);
float interval = message->is_float(1) ? message->get_float(1) : 0.0f;
float delay = message->is_float(2) ? message->get_float(2) : 0.0f;
pd::Message *controlMessage = PD_MESSAGE_ON_STACK(2);
controlMessage->from_timestamp(message->get_timestamp() + delay, 2);
controlMessage->set_float(0, target);
controlMessage->set_float(1, interval);
clearAllMessagesAtOrAfter(controlMessage->get_timestamp());
if (delay == 0.0f) {
updatePathWithMessage(controlMessage);
} else {
pd::Message *heapMessage = graph->schedule_message(this, 0, controlMessage);
messageList.push_back(heapMessage);
}
} else if (message->is_symbol_str(0, "stop")) {
clearAllMessagesFrom(messageList.begin());
updatePathWithMessage(NULL);
}
break;
}
case 1:
case 2:
default: {
graph->print_err("vline~ does not respond to messages on 2nd and 3rd inlets. "
"All messages must be sent to left-most inlet.");
break;
}
}
}
void DspVariableLine::clearAllMessagesAtOrAfter(double timestamp) {
for (list<pd::Message *>::iterator it = messageList.begin(); it != messageList.end(); ++it) {
pd::Message *message = *it;
if (timestamp < message->get_timestamp()) {
clearAllMessagesFrom(it);
break;
}
}
}
void DspVariableLine::clearAllMessagesFrom(list<pd::Message *>::iterator it) {
list<pd::Message *>::iterator itCopy = it;
while (it != messageList.end()) {
pd::Message *message = *it++;
graph->cancel_message(this, 0, message);
}
messageList.erase(itCopy, messageList.end());
}
void DspVariableLine::updatePathWithMessage(pd::Message *message) {
if (message == NULL) {
numSamplesToTarget = 0.0f;
slope = 0.0f;
} else {
target = message->get_float(0);
numSamplesToTarget = utils::millisecondsToSamples(message->get_float(1), graph->get_sample_rate());
if (numSamplesToTarget == 0.0f) {
lastOutputSample = target;
slope = 0.0f;
} else {
slope = (target - lastOutputSample) / numSamplesToTarget;
}
}
}
void DspVariableLine::send_message(int outlet_index, pd::Message *message) {
messageList.remove(message);
updatePathWithMessage(message);
}
void DspVariableLine::processSignal(DspObject *dspObject, int fromIndex, int toIndex) {
DspVariableLine *d = reinterpret_cast<DspVariableLine *>(dspObject);
if (d->numSamplesToTarget <= 0.0f) {
ArrayArithmetic::fill(d->dspBufferAtOutlet[0], d->lastOutputSample, fromIndex, toIndex);
} else {
int n = toIndex - fromIndex;
if (n < (int) d->numSamplesToTarget) {
#if __APPLE__
vDSP_vramp(&(d->lastOutputSample), &(d->slope), d->dspBufferAtOutlet[0]+fromIndex, 1, n);
#else
#endif
d->lastOutputSample = d->dspBufferAtOutlet[0][toIndex-1] + d->slope;
d->numSamplesToTarget -= n;
} else {
#if __APPLE__
vDSP_vramp(&(d->lastOutputSample), &(d->slope), d->dspBufferAtOutlet[0]+fromIndex, 1, (int) d->numSamplesToTarget);
#else
#endif
d->slope = 0.0f;
d->lastOutputSample = d->target;
fromIndex += (int) ceilf(d->numSamplesToTarget);
d->numSamplesToTarget = 0.0f;
ArrayArithmetic::fill(d->dspBufferAtOutlet[0], d->lastOutputSample, fromIndex, toIndex);
}
}
}