#include "ArrayArithmetic.h"
#include "DspEnvelope.h"
#include "PdGraph.h"
#define DEFAULT_WINDOW_SIZE 1024
message::Object *DspEnvelope::new_object(pd::Message *init_message, PdGraph *graph) {
return new DspEnvelope(init_message, graph);
}
DspEnvelope::DspEnvelope(pd::Message *init_message, PdGraph *graph) : DspObject(0, 1, 1, 0, graph) {
if (init_message->is_float(0)) {
if (init_message->is_float(1)) {
windowSize = init_message->get_float(0);
windowInterval = init_message->get_float(1);
} else {
windowSize = (int) init_message->get_float(0);
setWindowInterval(windowSize/2);
}
} else {
windowSize = DEFAULT_WINDOW_SIZE;
windowInterval = windowSize / 2;
}
if (windowSize < graph->get_block_size()) {
graph->print_err("env~ window size must be at least as large as the block size. %i reset to %i.",
windowSize, graph->get_block_size());
windowSize = graph->get_block_size();
}
if (windowInterval < graph->get_block_size()) {
graph->print_err("env~ window interval must be at least as large as the block size. %i reset to %i.",
windowInterval, graph->get_block_size());
windowInterval = graph->get_block_size();
}
process_function = &processSignal;
initBuffers();
}
DspEnvelope::~DspEnvelope() {
free(signalBuffer);
free(hanningCoefficients);
}
string DspEnvelope::toString() {
char str[snprintf(NULL, 0, "%s %i %i", get_object_label(), windowSize, windowInterval)+1];
snprintf(str, sizeof(str), "%s %i %i", get_object_label(), windowSize, windowInterval);
return string(str);
}
void DspEnvelope::setWindowInterval(int newInterval) {
int i = newInterval % graph->get_block_size();
if (i == 0) {
this->windowInterval = newInterval;
} else if (i <= graph->get_block_size()/2) {
this->windowInterval = (newInterval/graph->get_block_size())*graph->get_block_size();
} else {
this->windowInterval = ((newInterval/graph->get_block_size())+1)*graph->get_block_size();
}
}
void DspEnvelope::initBuffers() {
numSamplesReceived = 0;
numSamplesReceivedSinceLastInterval = 0;
int numBlocksPerWindow = (windowSize % graph->get_block_size() == 0) ? (windowSize/graph->get_block_size()) : (windowSize/graph->get_block_size()) + 1;
int bufferSize = numBlocksPerWindow * graph->get_block_size();
signalBuffer = (float *) malloc(bufferSize * sizeof(float));
hanningCoefficients = (float *) malloc(bufferSize * sizeof(float));
float N_1 = (float) (windowSize - 1); float hanningSum = 0.0f;
for (int i = 0; i < windowSize; i++) {
hanningCoefficients[i] = 0.5f * (1.0f - cosf((2.0f * M_PI * (float) i) / N_1));
hanningSum += hanningCoefficients[i];
}
for (int i = 0; i < windowSize; i++) {
hanningCoefficients[i] /= hanningSum;
}
}
void DspEnvelope::processSignal(DspObject *dspObject, int fromIndex, int toIndex) {
DspEnvelope *d = reinterpret_cast<DspEnvelope *>(dspObject);
memcpy(d->signalBuffer + d->numSamplesReceived, d->dspBufferAtInlet[0], toIndex*sizeof(float));
d->numSamplesReceived += toIndex;
d->numSamplesReceivedSinceLastInterval += toIndex;
if (d->numSamplesReceived >= d->windowSize) {
d->numSamplesReceived = 0;
}
if (d->numSamplesReceivedSinceLastInterval == d->windowInterval) {
d->numSamplesReceivedSinceLastInterval -= d->windowInterval;
float rms = 0.0f;
#if __APPLE__
float rmsBuffer[d->windowSize];
vDSP_vsq(d->signalBuffer, 1, rmsBuffer, 1, d->windowSize); vDSP_vmul(rmsBuffer, 1, d->hanningCoefficients, 1, rmsBuffer, 1, d->windowSize); vDSP_sve(rmsBuffer, 1, &rms, d->windowSize); #else
for (int i = 0; i < d->windowSize; ++i) {
rms += d->signalBuffer[i] * d->signalBuffer[i] * d->hanningCoefficients[i];
}
#endif
rms = 10.0f * log10f(rms) + 100.0f;
pd::Message *outgoing_message = PD_MESSAGE_ON_STACK(1);
outgoing_message->from_timestamp_and_float(0.0, (rms < 0.0f) ? 0.0f : rms);
d->graph->schedule_message(d, 0, outgoing_message);
}
}