snapshot
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
# Faust Examples
|
||||
|
||||
For details consult the [Wiki](https://github.com/pschatzmann/arduino-audio-tools/wiki/Faust)
|
||||
|
||||
The examples are written for the AudioKit, but you can easily just replace the AudioKitStream with an I2SStream to use a regular ESP32 module.
|
||||
@@ -0,0 +1,8 @@
|
||||
declare name "FluteMIDI";
|
||||
declare description "Simple MIDI-controllable flute physical model with physical parameters.";
|
||||
declare license "MIT";
|
||||
declare copyright "(c)Romain Michon, CCRMA (Stanford University), GRAME";
|
||||
|
||||
import("stdfaust.lib");
|
||||
|
||||
process = pm.flute_ui_MIDI <: _,_;
|
||||
@@ -0,0 +1,614 @@
|
||||
/* ------------------------------------------------------------
|
||||
copyright: "(c)Romain Michon, CCRMA (Stanford University), GRAME"
|
||||
license: "MIT"
|
||||
name: "FluteMIDI"
|
||||
Code generated with Faust 2.40.0 (https://faust.grame.fr)
|
||||
Compilation options: -lang cpp -mem -es 1 -mcd 16 -single -ftz 0
|
||||
------------------------------------------------------------ */
|
||||
|
||||
#ifndef __mydsp_H__
|
||||
#define __mydsp_H__
|
||||
|
||||
#ifndef FAUSTFLOAT
|
||||
#define FAUSTFLOAT float
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <math.h>
|
||||
|
||||
#ifndef FAUSTCLASS
|
||||
#define FAUSTCLASS mydsp
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
#define exp10f __exp10f
|
||||
#define exp10 __exp10
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define RESTRICT __restrict
|
||||
#else
|
||||
#define RESTRICT __restrict__
|
||||
#endif
|
||||
|
||||
class mydspSIG0 {
|
||||
|
||||
private:
|
||||
|
||||
int iVec0[2];
|
||||
int iRec22[2];
|
||||
|
||||
public:
|
||||
|
||||
int getNumInputsmydspSIG0() {
|
||||
return 0;
|
||||
}
|
||||
int getNumOutputsmydspSIG0() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
void instanceInitmydspSIG0(int sample_rate) {
|
||||
for (int l2 = 0; l2 < 2; l2 = l2 + 1) {
|
||||
iVec0[l2] = 0;
|
||||
}
|
||||
for (int l3 = 0; l3 < 2; l3 = l3 + 1) {
|
||||
iRec22[l3] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void fillmydspSIG0(int count, float* table) {
|
||||
for (int i1 = 0; i1 < count; i1 = i1 + 1) {
|
||||
iVec0[0] = 1;
|
||||
iRec22[0] = (iVec0[1] + iRec22[1]) % 65536;
|
||||
table[i1] = std::sin(9.58738019e-05f * float(iRec22[0]));
|
||||
iVec0[1] = iVec0[0];
|
||||
iRec22[1] = iRec22[0];
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
static mydspSIG0* newmydspSIG0(dsp_memory_manager* manager) { return (mydspSIG0*)new(manager->allocate(sizeof(mydspSIG0))) mydspSIG0(); }
|
||||
static void deletemydspSIG0(mydspSIG0* dsp, dsp_memory_manager* manager) { dsp->~mydspSIG0(); manager->destroy(dsp); }
|
||||
|
||||
static float* ftbl0mydspSIG0 = 0;
|
||||
static float mydsp_faustpower2_f(float value) {
|
||||
return value * value;
|
||||
}
|
||||
|
||||
class mydsp : public dsp {
|
||||
|
||||
private:
|
||||
|
||||
FAUSTFLOAT fHslider0;
|
||||
int* iRec15;
|
||||
float* fRec21;
|
||||
FAUSTFLOAT fHslider1;
|
||||
int fSampleRate;
|
||||
float fConst1;
|
||||
FAUSTFLOAT fHslider2;
|
||||
float* fRec23;
|
||||
FAUSTFLOAT fButton0;
|
||||
FAUSTFLOAT fHslider3;
|
||||
float* fVec1;
|
||||
FAUSTFLOAT fHslider4;
|
||||
FAUSTFLOAT fHslider5;
|
||||
float* fRec24;
|
||||
float fConst5;
|
||||
int* iRec26;
|
||||
float fConst6;
|
||||
float fConst7;
|
||||
float fConst8;
|
||||
float* fRec25;
|
||||
float* fRec27;
|
||||
int IOTA0;
|
||||
float* fRec28;
|
||||
float fConst9;
|
||||
float fConst10;
|
||||
FAUSTFLOAT fHslider6;
|
||||
FAUSTFLOAT fHslider7;
|
||||
float* fRec29;
|
||||
float fConst11;
|
||||
FAUSTFLOAT fHslider8;
|
||||
float fConst12;
|
||||
float* fRec30;
|
||||
float* fVec2;
|
||||
float* fVec3;
|
||||
float* fVec4;
|
||||
float* fRec20;
|
||||
float* fRec11;
|
||||
float* fRec7;
|
||||
float* fRec3;
|
||||
float* fRec1;
|
||||
float* fRec2;
|
||||
float* fRec0;
|
||||
|
||||
public:
|
||||
static dsp_memory_manager* fManager;
|
||||
|
||||
void metadata(Meta* m) {
|
||||
m->declare("basics.lib/name", "Faust Basic Element Library");
|
||||
m->declare("basics.lib/version", "0.5");
|
||||
m->declare("compile_options", "-lang cpp -mem -es 1 -mcd 16 -single -ftz 0");
|
||||
m->declare("copyright", "(c)Romain Michon, CCRMA (Stanford University), GRAME");
|
||||
m->declare("delays.lib/name", "Faust Delay Library");
|
||||
m->declare("delays.lib/version", "0.1");
|
||||
m->declare("description", "Simple MIDI-controllable flute physical model with physical parameters.");
|
||||
m->declare("filename", "fluteMIDI.dsp");
|
||||
m->declare("filters.lib/dcblocker:author", "Julius O. Smith III");
|
||||
m->declare("filters.lib/dcblocker:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>");
|
||||
m->declare("filters.lib/dcblocker:license", "MIT-style STK-4.3 license");
|
||||
m->declare("filters.lib/fir:author", "Julius O. Smith III");
|
||||
m->declare("filters.lib/fir:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>");
|
||||
m->declare("filters.lib/fir:license", "MIT-style STK-4.3 license");
|
||||
m->declare("filters.lib/iir:author", "Julius O. Smith III");
|
||||
m->declare("filters.lib/iir:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>");
|
||||
m->declare("filters.lib/iir:license", "MIT-style STK-4.3 license");
|
||||
m->declare("filters.lib/lowpass0_highpass1", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>");
|
||||
m->declare("filters.lib/lowpass0_highpass1:author", "Julius O. Smith III");
|
||||
m->declare("filters.lib/lowpass:author", "Julius O. Smith III");
|
||||
m->declare("filters.lib/lowpass:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>");
|
||||
m->declare("filters.lib/lowpass:license", "MIT-style STK-4.3 license");
|
||||
m->declare("filters.lib/name", "Faust Filters Library");
|
||||
m->declare("filters.lib/pole:author", "Julius O. Smith III");
|
||||
m->declare("filters.lib/pole:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>");
|
||||
m->declare("filters.lib/pole:license", "MIT-style STK-4.3 license");
|
||||
m->declare("filters.lib/tf2:author", "Julius O. Smith III");
|
||||
m->declare("filters.lib/tf2:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>");
|
||||
m->declare("filters.lib/tf2:license", "MIT-style STK-4.3 license");
|
||||
m->declare("filters.lib/tf2s:author", "Julius O. Smith III");
|
||||
m->declare("filters.lib/tf2s:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>");
|
||||
m->declare("filters.lib/tf2s:license", "MIT-style STK-4.3 license");
|
||||
m->declare("filters.lib/version", "0.3");
|
||||
m->declare("filters.lib/zero:author", "Julius O. Smith III");
|
||||
m->declare("filters.lib/zero:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>");
|
||||
m->declare("filters.lib/zero:license", "MIT-style STK-4.3 license");
|
||||
m->declare("license", "MIT");
|
||||
m->declare("maths.lib/author", "GRAME");
|
||||
m->declare("maths.lib/copyright", "GRAME");
|
||||
m->declare("maths.lib/license", "LGPL with exception");
|
||||
m->declare("maths.lib/name", "Faust Math Library");
|
||||
m->declare("maths.lib/version", "2.5");
|
||||
m->declare("name", "FluteMIDI");
|
||||
m->declare("noises.lib/name", "Faust Noise Generator Library");
|
||||
m->declare("noises.lib/version", "0.3");
|
||||
m->declare("oscillators.lib/name", "Faust Oscillator Library");
|
||||
m->declare("oscillators.lib/version", "0.3");
|
||||
m->declare("physmodels.lib/name", "Faust Physical Models Library");
|
||||
m->declare("physmodels.lib/version", "0.1");
|
||||
m->declare("platform.lib/name", "Generic Platform Library");
|
||||
m->declare("platform.lib/version", "0.2");
|
||||
m->declare("routes.lib/name", "Faust Signal Routing Library");
|
||||
m->declare("routes.lib/version", "0.2");
|
||||
m->declare("signals.lib/name", "Faust Signal Routing Library");
|
||||
m->declare("signals.lib/version", "0.1");
|
||||
}
|
||||
|
||||
virtual int getNumInputs() {
|
||||
return 0;
|
||||
}
|
||||
virtual int getNumOutputs() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
static void classInit(int sample_rate) {
|
||||
mydspSIG0* sig0 = newmydspSIG0(fManager);
|
||||
sig0->instanceInitmydspSIG0(sample_rate);
|
||||
ftbl0mydspSIG0 = static_cast<float*>(fManager->allocate(262144));
|
||||
sig0->fillmydspSIG0(65536, ftbl0mydspSIG0);
|
||||
deletemydspSIG0(sig0, fManager);
|
||||
}
|
||||
|
||||
static void classDestroy() {
|
||||
fManager->destroy(ftbl0mydspSIG0);
|
||||
}
|
||||
|
||||
virtual void instanceConstants(int sample_rate) {
|
||||
fSampleRate = sample_rate;
|
||||
float fConst0 = std::min<float>(192000.0f, std::max<float>(1.0f, float(fSampleRate)));
|
||||
fConst1 = 1.0f / fConst0;
|
||||
float fConst2 = std::tan(6283.18555f / fConst0);
|
||||
float fConst3 = 1.0f / fConst2;
|
||||
float fConst4 = (fConst3 + 1.41421354f) / fConst2 + 1.0f;
|
||||
fConst5 = 0.0500000007f / fConst4;
|
||||
fConst6 = 1.0f / fConst4;
|
||||
fConst7 = (fConst3 + -1.41421354f) / fConst2 + 1.0f;
|
||||
fConst8 = 2.0f * (1.0f - 1.0f / mydsp_faustpower2_f(fConst2));
|
||||
fConst9 = 0.00882352982f * fConst0;
|
||||
fConst10 = 0.00147058826f * fConst0;
|
||||
fConst11 = 44.0999985f / fConst0;
|
||||
fConst12 = 1.0f - fConst11;
|
||||
}
|
||||
|
||||
virtual void instanceResetUserInterface() {
|
||||
fHslider0 = FAUSTFLOAT(0.5f);
|
||||
fHslider1 = FAUSTFLOAT(0.5f);
|
||||
fHslider2 = FAUSTFLOAT(5.0f);
|
||||
fButton0 = FAUSTFLOAT(0.0f);
|
||||
fHslider3 = FAUSTFLOAT(0.0f);
|
||||
fHslider4 = FAUSTFLOAT(0.90000000000000002f);
|
||||
fHslider5 = FAUSTFLOAT(1.0f);
|
||||
fHslider6 = FAUSTFLOAT(440.0f);
|
||||
fHslider7 = FAUSTFLOAT(0.0f);
|
||||
fHslider8 = FAUSTFLOAT(0.5f);
|
||||
}
|
||||
|
||||
virtual void instanceClear() {
|
||||
for (int l0 = 0; l0 < 2; l0 = l0 + 1) {
|
||||
iRec15[l0] = 0;
|
||||
}
|
||||
for (int l1 = 0; l1 < 2; l1 = l1 + 1) {
|
||||
fRec21[l1] = 0.0f;
|
||||
}
|
||||
for (int l4 = 0; l4 < 2; l4 = l4 + 1) {
|
||||
fRec23[l4] = 0.0f;
|
||||
}
|
||||
for (int l5 = 0; l5 < 2; l5 = l5 + 1) {
|
||||
fVec1[l5] = 0.0f;
|
||||
}
|
||||
for (int l6 = 0; l6 < 2; l6 = l6 + 1) {
|
||||
fRec24[l6] = 0.0f;
|
||||
}
|
||||
for (int l7 = 0; l7 < 2; l7 = l7 + 1) {
|
||||
iRec26[l7] = 0;
|
||||
}
|
||||
for (int l8 = 0; l8 < 3; l8 = l8 + 1) {
|
||||
fRec25[l8] = 0.0f;
|
||||
}
|
||||
for (int l9 = 0; l9 < 2; l9 = l9 + 1) {
|
||||
fRec27[l9] = 0.0f;
|
||||
}
|
||||
IOTA0 = 0;
|
||||
for (int l10 = 0; l10 < 2048; l10 = l10 + 1) {
|
||||
fRec28[l10] = 0.0f;
|
||||
}
|
||||
for (int l11 = 0; l11 < 2; l11 = l11 + 1) {
|
||||
fRec29[l11] = 0.0f;
|
||||
}
|
||||
for (int l12 = 0; l12 < 2; l12 = l12 + 1) {
|
||||
fRec30[l12] = 0.0f;
|
||||
}
|
||||
for (int l13 = 0; l13 < 2; l13 = l13 + 1) {
|
||||
fVec2[l13] = 0.0f;
|
||||
}
|
||||
for (int l14 = 0; l14 < 2048; l14 = l14 + 1) {
|
||||
fVec3[l14] = 0.0f;
|
||||
}
|
||||
for (int l15 = 0; l15 < 2; l15 = l15 + 1) {
|
||||
fVec4[l15] = 0.0f;
|
||||
}
|
||||
for (int l16 = 0; l16 < 2; l16 = l16 + 1) {
|
||||
fRec20[l16] = 0.0f;
|
||||
}
|
||||
for (int l17 = 0; l17 < 2048; l17 = l17 + 1) {
|
||||
fRec11[l17] = 0.0f;
|
||||
}
|
||||
for (int l18 = 0; l18 < 2; l18 = l18 + 1) {
|
||||
fRec7[l18] = 0.0f;
|
||||
}
|
||||
for (int l19 = 0; l19 < 2048; l19 = l19 + 1) {
|
||||
fRec3[l19] = 0.0f;
|
||||
}
|
||||
for (int l20 = 0; l20 < 2; l20 = l20 + 1) {
|
||||
fRec1[l20] = 0.0f;
|
||||
}
|
||||
for (int l21 = 0; l21 < 2; l21 = l21 + 1) {
|
||||
fRec2[l21] = 0.0f;
|
||||
}
|
||||
for (int l22 = 0; l22 < 2; l22 = l22 + 1) {
|
||||
fRec0[l22] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void init(int sample_rate) {}
|
||||
virtual void instanceInit(int sample_rate) {
|
||||
instanceConstants(sample_rate);
|
||||
instanceResetUserInterface();
|
||||
instanceClear();
|
||||
}
|
||||
|
||||
virtual mydsp* clone() {
|
||||
return create();
|
||||
}
|
||||
|
||||
virtual int getSampleRate() {
|
||||
return fSampleRate;
|
||||
}
|
||||
|
||||
virtual void buildUserInterface(UI* ui_interface) {
|
||||
ui_interface->openVerticalBox("flute");
|
||||
ui_interface->declare(0, "0", "");
|
||||
ui_interface->openHorizontalBox("midi");
|
||||
ui_interface->declare(&fHslider6, "0", "");
|
||||
ui_interface->declare(&fHslider6, "style", "knob");
|
||||
ui_interface->addHorizontalSlider("freq", &fHslider6, FAUSTFLOAT(440.0f), FAUSTFLOAT(50.0f), FAUSTFLOAT(1000.0f), FAUSTFLOAT(0.00999999978f));
|
||||
ui_interface->declare(&fHslider7, "1", "");
|
||||
ui_interface->declare(&fHslider7, "hidden", "1");
|
||||
ui_interface->declare(&fHslider7, "midi", "pitchwheel");
|
||||
ui_interface->declare(&fHslider7, "style", "knob");
|
||||
ui_interface->addHorizontalSlider("bend", &fHslider7, FAUSTFLOAT(0.0f), FAUSTFLOAT(-2.0f), FAUSTFLOAT(2.0f), FAUSTFLOAT(0.00999999978f));
|
||||
ui_interface->declare(&fHslider4, "2", "");
|
||||
ui_interface->declare(&fHslider4, "style", "knob");
|
||||
ui_interface->addHorizontalSlider("gain", &fHslider4, FAUSTFLOAT(0.899999976f), FAUSTFLOAT(0.0f), FAUSTFLOAT(1.0f), FAUSTFLOAT(0.00999999978f));
|
||||
ui_interface->declare(&fHslider5, "3", "");
|
||||
ui_interface->declare(&fHslider5, "style", "knob");
|
||||
ui_interface->addHorizontalSlider("envAttack", &fHslider5, FAUSTFLOAT(1.0f), FAUSTFLOAT(0.0f), FAUSTFLOAT(30.0f), FAUSTFLOAT(0.00999999978f));
|
||||
ui_interface->declare(&fHslider3, "4", "");
|
||||
ui_interface->declare(&fHslider3, "hidden", "1");
|
||||
ui_interface->declare(&fHslider3, "midi", "ctrl 64");
|
||||
ui_interface->declare(&fHslider3, "style", "knob");
|
||||
ui_interface->addHorizontalSlider("sustain", &fHslider3, FAUSTFLOAT(0.0f), FAUSTFLOAT(0.0f), FAUSTFLOAT(1.0f), FAUSTFLOAT(1.0f));
|
||||
ui_interface->closeBox();
|
||||
ui_interface->declare(0, "1", "");
|
||||
ui_interface->openHorizontalBox("otherParams");
|
||||
ui_interface->declare(&fHslider8, "0", "");
|
||||
ui_interface->declare(&fHslider8, "midi", "ctrl 1");
|
||||
ui_interface->declare(&fHslider8, "style", "knob");
|
||||
ui_interface->addHorizontalSlider("mouthPosition", &fHslider8, FAUSTFLOAT(0.5f), FAUSTFLOAT(0.0f), FAUSTFLOAT(1.0f), FAUSTFLOAT(0.00999999978f));
|
||||
ui_interface->declare(&fHslider2, "1", "");
|
||||
ui_interface->declare(&fHslider2, "style", "knob");
|
||||
ui_interface->addHorizontalSlider("vibratoFreq", &fHslider2, FAUSTFLOAT(5.0f), FAUSTFLOAT(1.0f), FAUSTFLOAT(10.0f), FAUSTFLOAT(0.00999999978f));
|
||||
ui_interface->declare(&fHslider1, "2", "");
|
||||
ui_interface->declare(&fHslider1, "style", "knob");
|
||||
ui_interface->addHorizontalSlider("vibratoGain", &fHslider1, FAUSTFLOAT(0.5f), FAUSTFLOAT(0.0f), FAUSTFLOAT(1.0f), FAUSTFLOAT(0.00999999978f));
|
||||
ui_interface->declare(&fHslider0, "3", "");
|
||||
ui_interface->declare(&fHslider0, "style", "knob");
|
||||
ui_interface->addHorizontalSlider("outGain", &fHslider0, FAUSTFLOAT(0.5f), FAUSTFLOAT(0.0f), FAUSTFLOAT(1.0f), FAUSTFLOAT(0.00999999978f));
|
||||
ui_interface->closeBox();
|
||||
ui_interface->declare(&fButton0, "2", "");
|
||||
ui_interface->addButton("gate", &fButton0);
|
||||
ui_interface->closeBox();
|
||||
}
|
||||
|
||||
virtual void compute(int count, FAUSTFLOAT** RESTRICT inputs, FAUSTFLOAT** RESTRICT outputs) {
|
||||
FAUSTFLOAT* output0 = outputs[0];
|
||||
FAUSTFLOAT* output1 = outputs[1];
|
||||
float fSlow0 = float(fHslider0);
|
||||
float fSlow1 = 0.0399999991f * float(fHslider1);
|
||||
float fSlow2 = fConst1 * float(fHslider2);
|
||||
float fSlow3 = std::min<float>(1.0f, float(fButton0) + float(fHslider3));
|
||||
float fSlow4 = 0.00100000005f * float(fHslider5);
|
||||
int iSlow5 = std::fabs(fSlow4) < 1.1920929e-07f;
|
||||
float fThen1 = std::exp(0.0f - fConst1 / ((iSlow5) ? 1.0f : fSlow4));
|
||||
float fSlow6 = ((iSlow5) ? 0.0f : fThen1);
|
||||
float fSlow7 = fSlow3 * float(fHslider4) * (1.0f - fSlow6);
|
||||
float fSlow8 = 340.0f / float(fHslider6);
|
||||
float fSlow9 = std::pow(2.0f, 0.0833333358f * float(fHslider7));
|
||||
int iSlow10 = fSlow3 == 0.0f;
|
||||
float fSlow11 = fConst11 * float(fHslider8);
|
||||
for (int i0 = 0; i0 < count; i0 = i0 + 1) {
|
||||
iRec15[0] = 0;
|
||||
fRec21[0] = 0.284999996f * fRec20[1] + 0.699999988f * fRec21[1];
|
||||
float fRec19 = fRec21[0] + float(iRec15[1]);
|
||||
fRec23[0] = fSlow2 + fRec23[1] - std::floor(fSlow2 + fRec23[1]);
|
||||
fVec1[0] = fSlow3;
|
||||
fRec24[0] = fSlow7 + fSlow6 * fRec24[1];
|
||||
iRec26[0] = 1103515245 * iRec26[1] + 12345;
|
||||
fRec25[0] = 4.65661287e-10f * float(iRec26[0]) - fConst6 * (fConst7 * fRec25[2] + fConst8 * fRec25[1]);
|
||||
fRec27[0] = fRec1[1];
|
||||
fRec28[IOTA0 & 2047] = 0.949999988f * fRec27[1];
|
||||
float fTemp0 = float((fSlow3 == fVec1[1]) | iSlow10);
|
||||
fRec29[0] = fSlow9 * (1.0f - 0.999000013f * fTemp0) + 0.999000013f * fTemp0 * fRec29[1];
|
||||
float fTemp1 = fSlow8 / fRec29[0] + 0.270000011f;
|
||||
fRec30[0] = fSlow11 + fConst12 * fRec30[1];
|
||||
float fTemp2 = 0.400000006f * (fRec30[0] + -0.5f);
|
||||
float fTemp3 = fConst10 * fTemp1 * (fTemp2 + 0.270000011f);
|
||||
float fTemp4 = fTemp3 + -1.49999499f;
|
||||
int iTemp5 = int(fTemp4);
|
||||
int iTemp6 = int(std::min<float>(fConst9, float(std::max<int>(0, int(iTemp5))))) + 1;
|
||||
float fTemp7 = std::floor(fTemp4);
|
||||
float fTemp8 = fTemp3 + -1.0f - fTemp7;
|
||||
float fTemp9 = 0.0f - fTemp8;
|
||||
float fTemp10 = fTemp3 + -2.0f - fTemp7;
|
||||
float fTemp11 = 0.0f - 0.5f * fTemp10;
|
||||
float fTemp12 = fTemp3 + -3.0f - fTemp7;
|
||||
float fTemp13 = 0.0f - 0.333333343f * fTemp12;
|
||||
float fTemp14 = fTemp3 + -4.0f - fTemp7;
|
||||
float fTemp15 = 0.0f - 0.25f * fTemp14;
|
||||
float fTemp16 = fTemp3 - fTemp7;
|
||||
int iTemp17 = int(std::min<float>(fConst9, float(std::max<int>(0, int(iTemp5 + 1))))) + 1;
|
||||
float fTemp18 = 0.0f - fTemp10;
|
||||
float fTemp19 = 0.0f - 0.5f * fTemp12;
|
||||
float fTemp20 = 0.0f - 0.333333343f * fTemp14;
|
||||
int iTemp21 = int(std::min<float>(fConst9, float(std::max<int>(0, int(iTemp5 + 2))))) + 1;
|
||||
float fTemp22 = 0.0f - fTemp12;
|
||||
float fTemp23 = 0.0f - 0.5f * fTemp14;
|
||||
float fTemp24 = fTemp8 * fTemp10;
|
||||
int iTemp25 = int(std::min<float>(fConst9, float(std::max<int>(0, int(iTemp5 + 3))))) + 1;
|
||||
float fTemp26 = 0.0f - fTemp14;
|
||||
float fTemp27 = fTemp24 * fTemp12;
|
||||
int iTemp28 = int(std::min<float>(fConst9, float(std::max<int>(0, int(iTemp5 + 4))))) + 1;
|
||||
fVec2[0] = fRec28[(IOTA0 - iTemp6) & 2047] * fTemp9 * fTemp11 * fTemp13 * fTemp15 + fTemp16 * (fRec28[(IOTA0 - iTemp17) & 2047] * fTemp18 * fTemp19 * fTemp20 + 0.5f * fTemp8 * fRec28[(IOTA0 - iTemp21) & 2047] * fTemp22 * fTemp23 + 0.166666672f * fTemp24 * fRec28[(IOTA0 - iTemp25) & 2047] * fTemp26 + 0.0416666679f * fTemp27 * fRec28[(IOTA0 - iTemp28) & 2047]);
|
||||
float fTemp29 = (fSlow1 * ftbl0mydspSIG0[int(65536.0f * fRec23[0])] + fRec24[0] * (fConst5 * (fRec25[2] + fRec25[0] + 2.0f * fRec25[1]) + 1.0f)) - 0.5f * fVec2[1];
|
||||
float fTemp30 = 0.5f * fRec7[1] + std::max<float>(-1.0f, std::min<float>(1.0f, fTemp29 * (mydsp_faustpower2_f(fTemp29) + -1.0f)));
|
||||
fVec3[IOTA0 & 2047] = fTemp30;
|
||||
float fTemp31 = fConst10 * fTemp1 * (0.730000019f - fTemp2);
|
||||
float fTemp32 = fTemp31 + -1.49999499f;
|
||||
int iTemp33 = int(fTemp32);
|
||||
int iTemp34 = int(std::min<float>(fConst9, float(std::max<int>(0, int(iTemp33))))) + 1;
|
||||
float fTemp35 = std::floor(fTemp32);
|
||||
float fTemp36 = fTemp31 + -1.0f - fTemp35;
|
||||
float fTemp37 = 0.0f - fTemp36;
|
||||
float fTemp38 = fTemp31 + -2.0f - fTemp35;
|
||||
float fTemp39 = 0.0f - 0.5f * fTemp38;
|
||||
float fTemp40 = fTemp31 + -3.0f - fTemp35;
|
||||
float fTemp41 = 0.0f - 0.333333343f * fTemp40;
|
||||
float fTemp42 = fTemp31 + -4.0f - fTemp35;
|
||||
float fTemp43 = 0.0f - 0.25f * fTemp42;
|
||||
float fTemp44 = fTemp31 - fTemp35;
|
||||
int iTemp45 = int(std::min<float>(fConst9, float(std::max<int>(0, int(iTemp33 + 1))))) + 1;
|
||||
float fTemp46 = 0.0f - fTemp38;
|
||||
float fTemp47 = 0.0f - 0.5f * fTemp40;
|
||||
float fTemp48 = 0.0f - 0.333333343f * fTemp42;
|
||||
int iTemp49 = int(std::min<float>(fConst9, float(std::max<int>(0, int(iTemp33 + 2))))) + 1;
|
||||
float fTemp50 = 0.0f - fTemp40;
|
||||
float fTemp51 = 0.0f - 0.5f * fTemp42;
|
||||
float fTemp52 = fTemp36 * fTemp38;
|
||||
int iTemp53 = int(std::min<float>(fConst9, float(std::max<int>(0, int(iTemp33 + 3))))) + 1;
|
||||
float fTemp54 = 0.0f - fTemp42;
|
||||
float fTemp55 = fTemp52 * fTemp40;
|
||||
int iTemp56 = int(std::min<float>(fConst9, float(std::max<int>(0, int(iTemp33 + 4))))) + 1;
|
||||
fVec4[0] = fVec3[(IOTA0 - iTemp34) & 2047] * fTemp37 * fTemp39 * fTemp41 * fTemp43 + fTemp44 * (fVec3[(IOTA0 - iTemp45) & 2047] * fTemp46 * fTemp47 * fTemp48 + 0.5f * fTemp36 * fVec3[(IOTA0 - iTemp49) & 2047] * fTemp50 * fTemp51 + 0.166666672f * fTemp52 * fVec3[(IOTA0 - iTemp53) & 2047] * fTemp54 + 0.0416666679f * fTemp55 * fVec3[(IOTA0 - iTemp56) & 2047]);
|
||||
fRec20[0] = fVec4[1];
|
||||
float fRec16 = fRec19;
|
||||
float fRec17 = fRec20[0];
|
||||
float fRec18 = fRec20[0];
|
||||
fRec11[IOTA0 & 2047] = fRec16;
|
||||
float fRec12 = fTemp37 * fTemp39 * fTemp41 * fTemp43 * fRec11[(IOTA0 - iTemp34) & 2047] + fTemp44 * (fTemp46 * fTemp47 * fTemp48 * fRec11[(IOTA0 - iTemp45) & 2047] + 0.5f * fTemp36 * fTemp50 * fTemp51 * fRec11[(IOTA0 - iTemp49) & 2047] + 0.166666672f * fTemp52 * fTemp54 * fRec11[(IOTA0 - iTemp53) & 2047] + 0.0416666679f * fTemp55 * fRec11[(IOTA0 - iTemp56) & 2047]);
|
||||
float fRec13 = fRec17;
|
||||
float fRec14 = fRec18;
|
||||
fRec7[0] = fRec12;
|
||||
float fRec8 = fRec7[1];
|
||||
float fRec9 = fRec13;
|
||||
float fRec10 = fRec14;
|
||||
fRec3[IOTA0 & 2047] = fRec8;
|
||||
float fRec4 = fTemp9 * fTemp11 * fTemp13 * fTemp15 * fRec3[(IOTA0 - iTemp6) & 2047] + fTemp16 * (fTemp18 * fTemp19 * fTemp20 * fRec3[(IOTA0 - iTemp17) & 2047] + 0.5f * fTemp8 * fTemp22 * fTemp23 * fRec3[(IOTA0 - iTemp21) & 2047] + 0.166666672f * fTemp24 * fTemp26 * fRec3[(IOTA0 - iTemp25) & 2047] + 0.0416666679f * fTemp27 * fRec3[(IOTA0 - iTemp28) & 2047]);
|
||||
float fRec5 = fRec9;
|
||||
float fRec6 = fRec10;
|
||||
fRec1[0] = fRec4;
|
||||
fRec2[0] = fRec6;
|
||||
fRec0[0] = (fRec2[0] + 0.995000005f * fRec0[1]) - fRec2[1];
|
||||
float fTemp57 = fSlow0 * fRec0[0];
|
||||
output0[i0] = FAUSTFLOAT(fTemp57);
|
||||
output1[i0] = FAUSTFLOAT(fTemp57);
|
||||
iRec15[1] = iRec15[0];
|
||||
fRec21[1] = fRec21[0];
|
||||
fRec23[1] = fRec23[0];
|
||||
fVec1[1] = fVec1[0];
|
||||
fRec24[1] = fRec24[0];
|
||||
iRec26[1] = iRec26[0];
|
||||
fRec25[2] = fRec25[1];
|
||||
fRec25[1] = fRec25[0];
|
||||
fRec27[1] = fRec27[0];
|
||||
IOTA0 = IOTA0 + 1;
|
||||
fRec29[1] = fRec29[0];
|
||||
fRec30[1] = fRec30[0];
|
||||
fVec2[1] = fVec2[0];
|
||||
fVec4[1] = fVec4[0];
|
||||
fRec20[1] = fRec20[0];
|
||||
fRec7[1] = fRec7[0];
|
||||
fRec1[1] = fRec1[0];
|
||||
fRec2[1] = fRec2[0];
|
||||
fRec0[1] = fRec0[0];
|
||||
}
|
||||
}
|
||||
|
||||
static void memoryInfo() {
|
||||
fManager->begin(24);
|
||||
// mydspSIG0
|
||||
fManager->info(16, 0, 0);
|
||||
// ftbl0mydspSIG0
|
||||
fManager->info(262144, 1, 0);
|
||||
// mydsp
|
||||
fManager->info(268, 42, 1);
|
||||
// iRec15
|
||||
fManager->info(8, 2, 2);
|
||||
// fRec21
|
||||
fManager->info(8, 3, 2);
|
||||
// fRec23
|
||||
fManager->info(8, 4, 2);
|
||||
// fVec1
|
||||
fManager->info(8, 2, 2);
|
||||
// fRec24
|
||||
fManager->info(8, 3, 2);
|
||||
// iRec26
|
||||
fManager->info(8, 3, 2);
|
||||
// fRec25
|
||||
fManager->info(12, 7, 3);
|
||||
// fRec27
|
||||
fManager->info(8, 2, 2);
|
||||
// fRec28
|
||||
fManager->info(8192, 5, 1);
|
||||
// fRec29
|
||||
fManager->info(8, 3, 2);
|
||||
// fRec30
|
||||
fManager->info(8, 3, 2);
|
||||
// fVec2
|
||||
fManager->info(8, 2, 2);
|
||||
// fVec3
|
||||
fManager->info(8192, 5, 1);
|
||||
// fVec4
|
||||
fManager->info(8, 2, 2);
|
||||
// fRec20
|
||||
fManager->info(8, 4, 2);
|
||||
// fRec11
|
||||
fManager->info(8192, 5, 1);
|
||||
// fRec7
|
||||
fManager->info(8, 3, 2);
|
||||
// fRec3
|
||||
fManager->info(8192, 5, 1);
|
||||
// fRec1
|
||||
fManager->info(8, 2, 2);
|
||||
// fRec2
|
||||
fManager->info(8, 3, 2);
|
||||
// fRec0
|
||||
fManager->info(8, 3, 2);
|
||||
fManager->end();
|
||||
}
|
||||
|
||||
void memoryCreate() {
|
||||
iRec15 = static_cast<int*>(fManager->allocate(8));
|
||||
fRec21 = static_cast<float*>(fManager->allocate(8));
|
||||
fRec23 = static_cast<float*>(fManager->allocate(8));
|
||||
fVec1 = static_cast<float*>(fManager->allocate(8));
|
||||
fRec24 = static_cast<float*>(fManager->allocate(8));
|
||||
iRec26 = static_cast<int*>(fManager->allocate(8));
|
||||
fRec25 = static_cast<float*>(fManager->allocate(12));
|
||||
fRec27 = static_cast<float*>(fManager->allocate(8));
|
||||
fRec28 = static_cast<float*>(fManager->allocate(8192));
|
||||
fRec29 = static_cast<float*>(fManager->allocate(8));
|
||||
fRec30 = static_cast<float*>(fManager->allocate(8));
|
||||
fVec2 = static_cast<float*>(fManager->allocate(8));
|
||||
fVec3 = static_cast<float*>(fManager->allocate(8192));
|
||||
fVec4 = static_cast<float*>(fManager->allocate(8));
|
||||
fRec20 = static_cast<float*>(fManager->allocate(8));
|
||||
fRec11 = static_cast<float*>(fManager->allocate(8192));
|
||||
fRec7 = static_cast<float*>(fManager->allocate(8));
|
||||
fRec3 = static_cast<float*>(fManager->allocate(8192));
|
||||
fRec1 = static_cast<float*>(fManager->allocate(8));
|
||||
fRec2 = static_cast<float*>(fManager->allocate(8));
|
||||
fRec0 = static_cast<float*>(fManager->allocate(8));
|
||||
}
|
||||
|
||||
void memoryDestroy() {
|
||||
fManager->destroy(iRec15);
|
||||
fManager->destroy(fRec21);
|
||||
fManager->destroy(fRec23);
|
||||
fManager->destroy(fVec1);
|
||||
fManager->destroy(fRec24);
|
||||
fManager->destroy(iRec26);
|
||||
fManager->destroy(fRec25);
|
||||
fManager->destroy(fRec27);
|
||||
fManager->destroy(fRec28);
|
||||
fManager->destroy(fRec29);
|
||||
fManager->destroy(fRec30);
|
||||
fManager->destroy(fVec2);
|
||||
fManager->destroy(fVec3);
|
||||
fManager->destroy(fVec4);
|
||||
fManager->destroy(fRec20);
|
||||
fManager->destroy(fRec11);
|
||||
fManager->destroy(fRec7);
|
||||
fManager->destroy(fRec3);
|
||||
fManager->destroy(fRec1);
|
||||
fManager->destroy(fRec2);
|
||||
fManager->destroy(fRec0);
|
||||
}
|
||||
|
||||
static mydsp* create() {
|
||||
mydsp* dsp = new (fManager->allocate(sizeof(mydsp))) mydsp();
|
||||
dsp->memoryCreate();
|
||||
return dsp;
|
||||
}
|
||||
|
||||
static void destroy(dsp* dsp) {
|
||||
static_cast<mydsp*>(dsp)->memoryDestroy();
|
||||
fManager->destroy(dsp);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
dsp_memory_manager* mydsp::fManager = nullptr;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* @file streams-faust_noise-i2s.ino
|
||||
* @author Phil Schatzmann
|
||||
* @brief Example how to use Faust as Audio Source
|
||||
* dsp compiled with: faust -mem fluteMIDI.dsp -o fluteMIDI.h
|
||||
* @version 0.1
|
||||
* @date 2022-04-22
|
||||
*
|
||||
* @copyright Copyright (c) 2022
|
||||
*
|
||||
*/
|
||||
|
||||
#define USE_MEMORY_MANAGER
|
||||
|
||||
#include "AudioTools.h"
|
||||
#include "AudioTools/AudioLibs/AudioBoardStream.h"
|
||||
#include "AudioTools/AudioLibs/AudioFaust.h"
|
||||
#include "fluteMIDI.h"
|
||||
|
||||
FaustStream<mydsp> faust;
|
||||
AudioBoardStream out(AudioKitEs8388V1);
|
||||
StreamCopy copier(out, faust); // copy mic to tfl
|
||||
|
||||
// Arduino Setup
|
||||
void setup(void) {
|
||||
// Open Serial
|
||||
Serial.begin(115200);
|
||||
AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning);
|
||||
|
||||
// Setup Faust
|
||||
auto cfg = faust.defaultConfig();
|
||||
faust.begin(cfg);
|
||||
|
||||
// note->freq and gain
|
||||
faust.midiOn(78, 0.8);
|
||||
|
||||
// start I2S
|
||||
auto cfg_i2s = out.defaultConfig(TX_MODE);
|
||||
cfg_i2s.sample_rate = cfg.sample_rate;
|
||||
cfg_i2s.channels = cfg.channels;
|
||||
cfg_i2s.bits_per_sample = cfg.bits_per_sample;
|
||||
out.begin(cfg_i2s);
|
||||
|
||||
}
|
||||
|
||||
// Arduino loop - copy sound to out
|
||||
void loop() {
|
||||
copier.copy();
|
||||
}
|
||||
@@ -0,0 +1,344 @@
|
||||
declare compilation_options "-single -scal -I /Users/pschatzmann/.FaustLive-CurrentSession-2.2/Libs -I /Users/pschatzmann/.FaustLive-CurrentSession-2.2/Examples";
|
||||
declare author "Grame";
|
||||
declare copyright "(c)GRAME 2009";
|
||||
declare filename "Noise";
|
||||
declare license "BSD";
|
||||
declare name "Noise";
|
||||
declare version "1.1";
|
||||
ID_319 = (ID_318 : *);
|
||||
ID_321 = ID_320, 2147483647.0f;
|
||||
ID_322 = (ID_321 : /);
|
||||
ID_325 = ID_324 : *;
|
||||
ID_323 = vslider("Volume[style:knob][acc: 0 0 -10 0 10]", 0.5f, 0.0f, 1.0f, 0.10000000000000001f);
|
||||
ID_324 = ID_322, ID_323;
|
||||
ID_318 = _, 1103515245;
|
||||
ID_320 = ID_317 ~ ID_319;
|
||||
ID_197 = _, _;
|
||||
ID_313 = _, _;
|
||||
ID_45 = (1 : mem);
|
||||
ID_84 = hslider("hand[osc:/accxyz/1 0 20]", 0.0f, 0.0f, 1.0f, 0.01f);
|
||||
ID_85 = _, 0.099999999999999978f;
|
||||
ID_86 = ID_85 : *;
|
||||
ID_87 = _, 0.90000000000000002f;
|
||||
ID_88 = (ID_87 : *);
|
||||
ID_89 = + ~ ID_88;
|
||||
ID_90 = ID_86 : ID_89;
|
||||
ID_91 = ID_84 : ID_90;
|
||||
ID_200 = hslider("hand[osc:/accxyz/1 0 20]", 0.0f, 0.0f, 1.0f, 0.01f);
|
||||
ID_201 = _, 0.099999999999999978f;
|
||||
ID_202 = ID_201 : *;
|
||||
ID_203 = _, 0.90000000000000002f;
|
||||
ID_204 = (ID_203 : *);
|
||||
ID_205 = + ~ ID_204;
|
||||
ID_206 = ID_202 : ID_205;
|
||||
ID_207 = ID_200 : ID_206;
|
||||
ID_180 = (0.045454545454545414f : sqrt);
|
||||
ID_181 = _, ID_180;
|
||||
ID_182 = (ID_181 : *);
|
||||
ID_92 = (0.95454545454545459f : sqrt);
|
||||
ID_93 = _, ID_92;
|
||||
ID_94 = (ID_93 : *);
|
||||
ID_183 = ID_182, ID_94;
|
||||
ID_184 = _ <: ID_183;
|
||||
ID_296 = (0.045454545454545414f : sqrt);
|
||||
ID_297 = _, ID_296;
|
||||
ID_298 = (ID_297 : *);
|
||||
ID_208 = (0.95454545454545459f : sqrt);
|
||||
ID_209 = _, ID_208;
|
||||
ID_210 = (ID_209 : *);
|
||||
ID_299 = ID_298, ID_210;
|
||||
ID_300 = _ <: ID_299;
|
||||
ID_105 = (0.13636363636363635f : sqrt);
|
||||
ID_106 = _, ID_105;
|
||||
ID_107 = (ID_106 : *);
|
||||
ID_102 = (0.86363636363636365f : sqrt);
|
||||
ID_103 = _, ID_102;
|
||||
ID_104 = (ID_103 : *);
|
||||
ID_176 = ID_107, ID_104;
|
||||
ID_177 = _ <: ID_176;
|
||||
ID_221 = (0.13636363636363635f : sqrt);
|
||||
ID_222 = _, ID_221;
|
||||
ID_223 = (ID_222 : *);
|
||||
ID_218 = (0.86363636363636365f : sqrt);
|
||||
ID_219 = _, ID_218;
|
||||
ID_220 = (ID_219 : *);
|
||||
ID_292 = ID_223, ID_220;
|
||||
ID_293 = _ <: ID_292;
|
||||
ID_169 = (0.22727272727272729f : sqrt);
|
||||
ID_170 = _, ID_169;
|
||||
ID_171 = (ID_170 : *);
|
||||
ID_112 = (0.77272727272727271f : sqrt);
|
||||
ID_113 = _, ID_112;
|
||||
ID_114 = (ID_113 : *);
|
||||
ID_172 = ID_171, ID_114;
|
||||
ID_173 = _ <: ID_172;
|
||||
ID_285 = (0.22727272727272729f : sqrt);
|
||||
ID_286 = _, ID_285;
|
||||
ID_287 = (ID_286 : *);
|
||||
ID_228 = (0.77272727272727271f : sqrt);
|
||||
ID_229 = _, ID_228;
|
||||
ID_230 = (ID_229 : *);
|
||||
ID_288 = ID_287, ID_230;
|
||||
ID_289 = _ <: ID_288;
|
||||
ID_159 = (0.31818181818181823f : sqrt);
|
||||
ID_160 = _, ID_159;
|
||||
ID_161 = (ID_160 : *);
|
||||
ID_162 = (0.68181818181818177f : sqrt);
|
||||
ID_163 = _, ID_162;
|
||||
ID_164 = (ID_163 : *);
|
||||
ID_165 = ID_161, ID_164;
|
||||
ID_166 = _ <: ID_165;
|
||||
ID_275 = (0.31818181818181823f : sqrt);
|
||||
ID_276 = _, ID_275;
|
||||
ID_277 = (ID_276 : *);
|
||||
ID_278 = (0.68181818181818177f : sqrt);
|
||||
ID_279 = _, ID_278;
|
||||
ID_280 = (ID_279 : *);
|
||||
ID_281 = ID_277, ID_280;
|
||||
ID_282 = _ <: ID_281;
|
||||
ID_149 = (0.40909090909090906f : sqrt);
|
||||
ID_150 = _, ID_149;
|
||||
ID_151 = (ID_150 : *);
|
||||
ID_152 = (0.59090909090909094f : sqrt);
|
||||
ID_153 = _, ID_152;
|
||||
ID_154 = (ID_153 : *);
|
||||
ID_155 = ID_151, ID_154;
|
||||
ID_156 = _ <: ID_155;
|
||||
ID_265 = (0.40909090909090906f : sqrt);
|
||||
ID_266 = _, ID_265;
|
||||
ID_267 = (ID_266 : *);
|
||||
ID_268 = (0.59090909090909094f : sqrt);
|
||||
ID_269 = _, ID_268;
|
||||
ID_270 = (ID_269 : *);
|
||||
ID_271 = ID_267, ID_270;
|
||||
ID_272 = _ <: ID_271;
|
||||
ID_142 = (0.5f : sqrt);
|
||||
ID_143 = _, ID_142;
|
||||
ID_144 = (ID_143 : *);
|
||||
ID_145 = ID_144, ID_144;
|
||||
ID_146 = _ <: ID_145;
|
||||
ID_258 = (0.5f : sqrt);
|
||||
ID_259 = _, ID_258;
|
||||
ID_260 = (ID_259 : *);
|
||||
ID_261 = ID_260, ID_260;
|
||||
ID_262 = _ <: ID_261;
|
||||
ID_132 = (0.59090909090909083f : sqrt);
|
||||
ID_133 = _, ID_132;
|
||||
ID_134 = (ID_133 : *);
|
||||
ID_135 = (0.40909090909090912f : sqrt);
|
||||
ID_136 = _, ID_135;
|
||||
ID_137 = (ID_136 : *);
|
||||
ID_138 = ID_134, ID_137;
|
||||
ID_139 = _ <: ID_138;
|
||||
ID_248 = (0.59090909090909083f : sqrt);
|
||||
ID_249 = _, ID_248;
|
||||
ID_250 = (ID_249 : *);
|
||||
ID_251 = (0.40909090909090912f : sqrt);
|
||||
ID_252 = _, ID_251;
|
||||
ID_253 = (ID_252 : *);
|
||||
ID_254 = ID_250, ID_253;
|
||||
ID_255 = _ <: ID_254;
|
||||
ID_122 = (0.68181818181818188f : sqrt);
|
||||
ID_123 = _, ID_122;
|
||||
ID_124 = (ID_123 : *);
|
||||
ID_125 = (0.31818181818181818f : sqrt);
|
||||
ID_126 = _, ID_125;
|
||||
ID_127 = (ID_126 : *);
|
||||
ID_128 = ID_124, ID_127;
|
||||
ID_129 = _ <: ID_128;
|
||||
ID_238 = (0.68181818181818188f : sqrt);
|
||||
ID_239 = _, ID_238;
|
||||
ID_240 = (ID_239 : *);
|
||||
ID_241 = (0.31818181818181818f : sqrt);
|
||||
ID_242 = _, ID_241;
|
||||
ID_243 = (ID_242 : *);
|
||||
ID_244 = ID_240, ID_243;
|
||||
ID_245 = _ <: ID_244;
|
||||
ID_115 = (0.22727272727272727f : sqrt);
|
||||
ID_116 = _, ID_115;
|
||||
ID_117 = (ID_116 : *);
|
||||
ID_118 = ID_114, ID_117;
|
||||
ID_119 = _ <: ID_118;
|
||||
ID_231 = (0.22727272727272727f : sqrt);
|
||||
ID_232 = _, ID_231;
|
||||
ID_233 = (ID_232 : *);
|
||||
ID_234 = ID_230, ID_233;
|
||||
ID_235 = _ <: ID_234;
|
||||
ID_108 = ID_104, ID_107;
|
||||
ID_109 = _ <: ID_108;
|
||||
ID_224 = ID_220, ID_223;
|
||||
ID_225 = _ <: ID_224;
|
||||
ID_95 = (0.045454545454545456f : sqrt);
|
||||
ID_96 = _, ID_95;
|
||||
ID_97 = (ID_96 : *);
|
||||
ID_98 = ID_94, ID_97;
|
||||
ID_99 = _ <: ID_98;
|
||||
ID_211 = (0.045454545454545456f : sqrt);
|
||||
ID_212 = _, ID_211;
|
||||
ID_213 = (ID_212 : *);
|
||||
ID_214 = ID_210, ID_213;
|
||||
ID_215 = _ <: ID_214;
|
||||
ID_56 = hslider("freq [unit:Hz]", 1000.0f, 20.0f, 24000.0f, 1.0f);
|
||||
ID_57 = fconstant(int fSamplingFreq, <math.h>);
|
||||
ID_100 = \(x2).(((_,12345 : +)~(_,1103515245 : *),2147483647.0f : /),(hslider("level [unit:f][osc:/accxyz/0 -10 10]", 0.5f, 0.0f, 1.0f, 0.01f),2 : pow) : * : _,(x2 : \(x3).((x3,(x3 : mem) : -),0.0f : >) : +~\(x4).(x4,((x4,0.0f : >),100.22727272727273f : /) : -) : _,0.0f : >) : * : (+ : _,99.227272727272734f : @)~(\(x5).((x5,(x5 : mem) : +),2 : /) : _,(1.0f,hslider("attenuation [osc:/1/fader3]", 0.0050000000000000001f, 0.0f, 0.01f, 0.001f) : -) : *)) : ID_99;
|
||||
ID_101 = (\(x1).(((x1,(x1 : mem) : min),0.045454545454545456f : <),(0.045454545454545456f,(x1,(x1 : mem) : max) : <) : &) : ID_100);
|
||||
ID_216 = \(x2).(((_,12345 : +)~(_,1103515245 : *),2147483647.0f : /),(hslider("level [unit:f][osc:/accxyz/0 -10 10]", 0.5f, 0.0f, 1.0f, 0.01f),2 : pow) : * : _,(x2 : \(x3).((x3,(x3 : mem) : -),0.0f : >) : +~\(x4).(x4,((x4,0.0f : >),100.22727272727273f : /) : -) : _,0.0f : >) : * : (+ : _,99.227272727272734f : @)~(\(x5).((x5,(x5 : mem) : +),2 : /) : _,(1.0f,hslider("attenuation [osc:/1/fader3]", 0.0050000000000000001f, 0.0f, 0.01f, 0.001f) : -) : *)) : ID_215;
|
||||
ID_217 = (\(x1).(((x1,(x1 : mem) : min),0.045454545454545456f : <),(0.045454545454545456f,(x1,(x1 : mem) : max) : <) : &) : ID_216);
|
||||
ID_110 = \(x7).(((_,12345 : +)~(_,1103515245 : *),2147483647.0f : /),(hslider("level [unit:f][osc:/accxyz/0 -10 10]", 0.5f, 0.0f, 1.0f, 0.01f),2 : pow) : * : _,(x7 : \(x8).((x8,(x8 : mem) : -),0.0f : >) : +~\(x9).(x9,((x9,0.0f : >),87.252908730361526f : /) : -) : _,0.0f : >) : * : (+ : _,86.252908730361526f : @)~(\(x10).((x10,(x10 : mem) : +),2 : /) : _,(1.0f,hslider("attenuation [osc:/1/fader3]", 0.0050000000000000001f, 0.0f, 0.01f, 0.001f) : -) : *)) : ID_109;
|
||||
ID_111 = (\(x6).(((x6,(x6 : mem) : min),0.13636363636363635f : <),(0.13636363636363635f,(x6,(x6 : mem) : max) : <) : &) : ID_110);
|
||||
ID_226 = \(x7).(((_,12345 : +)~(_,1103515245 : *),2147483647.0f : /),(hslider("level [unit:f][osc:/accxyz/0 -10 10]", 0.5f, 0.0f, 1.0f, 0.01f),2 : pow) : * : _,(x7 : \(x8).((x8,(x8 : mem) : -),0.0f : >) : +~\(x9).(x9,((x9,0.0f : >),87.252908730361526f : /) : -) : _,0.0f : >) : * : (+ : _,86.252908730361526f : @)~(\(x10).((x10,(x10 : mem) : +),2 : /) : _,(1.0f,hslider("attenuation [osc:/1/fader3]", 0.0050000000000000001f, 0.0f, 0.01f, 0.001f) : -) : *)) : ID_225;
|
||||
ID_227 = (\(x6).(((x6,(x6 : mem) : min),0.13636363636363635f : <),(0.13636363636363635f,(x6,(x6 : mem) : max) : <) : &) : ID_226);
|
||||
ID_120 = \(x12).(((_,12345 : +)~(_,1103515245 : *),2147483647.0f : /),(hslider("level [unit:f][osc:/accxyz/0 -10 10]", 0.5f, 0.0f, 1.0f, 0.01f),2 : pow) : * : _,(x12 : \(x13).((x13,(x13 : mem) : -),0.0f : >) : +~\(x14).(x14,((x14,0.0f : >),75.958068844441542f : /) : -) : _,0.0f : >) : * : (+ : _,74.958068844441542f : @)~(\(x15).((x15,(x15 : mem) : +),2 : /) : _,(1.0f,hslider("attenuation [osc:/1/fader3]", 0.0050000000000000001f, 0.0f, 0.01f, 0.001f) : -) : *)) : ID_119;
|
||||
ID_121 = (\(x11).(((x11,(x11 : mem) : min),0.22727272727272727f : <),(0.22727272727272727f,(x11,(x11 : mem) : max) : <) : &) : ID_120);
|
||||
ID_236 = \(x12).(((_,12345 : +)~(_,1103515245 : *),2147483647.0f : /),(hslider("level [unit:f][osc:/accxyz/0 -10 10]", 0.5f, 0.0f, 1.0f, 0.01f),2 : pow) : * : _,(x12 : \(x13).((x13,(x13 : mem) : -),0.0f : >) : +~\(x14).(x14,((x14,0.0f : >),75.958068844441542f : /) : -) : _,0.0f : >) : * : (+ : _,74.958068844441542f : @)~(\(x15).((x15,(x15 : mem) : +),2 : /) : _,(1.0f,hslider("attenuation [osc:/1/fader3]", 0.0050000000000000001f, 0.0f, 0.01f, 0.001f) : -) : *)) : ID_235;
|
||||
ID_237 = (\(x11).(((x11,(x11 : mem) : min),0.22727272727272727f : <),(0.22727272727272727f,(x11,(x11 : mem) : max) : <) : &) : ID_236);
|
||||
ID_58 = 1.0f, ID_57;
|
||||
ID_59 = (ID_58 : max);
|
||||
ID_60 = 192000.0f, ID_59;
|
||||
ID_61 = (ID_60 : min);
|
||||
ID_62 = ID_56, ID_61;
|
||||
ID_63 = (ID_62 : /);
|
||||
ID_64 = _, ID_63;
|
||||
ID_65 = (ID_64 : +);
|
||||
ID_66 = ID_65, 0;
|
||||
ID_67 = 0, ID_66;
|
||||
ID_68 = ID_67 : select2;
|
||||
ID_52 = (65536 : float);
|
||||
ID_71 = _, ID_52;
|
||||
ID_72 = ID_71 : *;
|
||||
ID_69 = (ID_68 : \(x1).(x1,(x1 : floor) : -));
|
||||
ID_70 = ID_69 ~ _;
|
||||
ID_73 = ID_70 : ID_72;
|
||||
ID_130 = \(x17).(((_,12345 : +)~(_,1103515245 : *),2147483647.0f : /),(hslider("level [unit:f][osc:/accxyz/0 -10 10]", 0.5f, 0.0f, 1.0f, 0.01f),2 : pow) : * : _,(x17 : \(x18).((x18,(x18 : mem) : -),0.0f : >) : +~\(x19).(x19,((x19,0.0f : >),66.125339619414362f : /) : -) : _,0.0f : >) : * : (+ : _,65.125339619414362f : @)~(\(x20).((x20,(x20 : mem) : +),2 : /) : _,(1.0f,hslider("attenuation [osc:/1/fader3]", 0.0050000000000000001f, 0.0f, 0.01f, 0.001f) : -) : *)) : ID_129;
|
||||
ID_131 = (\(x16).(((x16,(x16 : mem) : min),0.31818181818181818f : <),(0.31818181818181818f,(x16,(x16 : mem) : max) : <) : &) : ID_130);
|
||||
ID_246 = \(x17).(((_,12345 : +)~(_,1103515245 : *),2147483647.0f : /),(hslider("level [unit:f][osc:/accxyz/0 -10 10]", 0.5f, 0.0f, 1.0f, 0.01f),2 : pow) : * : _,(x17 : \(x18).((x18,(x18 : mem) : -),0.0f : >) : +~\(x19).(x19,((x19,0.0f : >),66.125339619414362f : /) : -) : _,0.0f : >) : * : (+ : _,65.125339619414362f : @)~(\(x20).((x20,(x20 : mem) : +),2 : /) : _,(1.0f,hslider("attenuation [osc:/1/fader3]", 0.0050000000000000001f, 0.0f, 0.01f, 0.001f) : -) : *)) : ID_245;
|
||||
ID_247 = (\(x16).(((x16,(x16 : mem) : min),0.31818181818181818f : <),(0.31818181818181818f,(x16,(x16 : mem) : max) : <) : &) : ID_246);
|
||||
ID_140 = \(x22).(((_,12345 : +)~(_,1103515245 : *),2147483647.0f : /),(hslider("level [unit:f][osc:/accxyz/0 -10 10]", 0.5f, 0.0f, 1.0f, 0.01f),2 : pow) : * : _,(x22 : \(x23).((x23,(x23 : mem) : -),0.0f : >) : +~\(x24).(x24,((x24,0.0f : >),57.565451653828681f : /) : -) : _,0.0f : >) : * : (+ : _,56.565451653828681f : @)~(\(x25).((x25,(x25 : mem) : +),2 : /) : _,(1.0f,hslider("attenuation [osc:/1/fader3]", 0.0050000000000000001f, 0.0f, 0.01f, 0.001f) : -) : *)) : ID_139;
|
||||
ID_141 = (\(x21).(((x21,(x21 : mem) : min),0.40909090909090912f : <),(0.40909090909090912f,(x21,(x21 : mem) : max) : <) : &) : ID_140);
|
||||
ID_256 = \(x22).(((_,12345 : +)~(_,1103515245 : *),2147483647.0f : /),(hslider("level [unit:f][osc:/accxyz/0 -10 10]", 0.5f, 0.0f, 1.0f, 0.01f),2 : pow) : * : _,(x22 : \(x23).((x23,(x23 : mem) : -),0.0f : >) : +~\(x24).(x24,((x24,0.0f : >),57.565451653828681f : /) : -) : _,0.0f : >) : * : (+ : _,56.565451653828681f : @)~(\(x25).((x25,(x25 : mem) : +),2 : /) : _,(1.0f,hslider("attenuation [osc:/1/fader3]", 0.0050000000000000001f, 0.0f, 0.01f, 0.001f) : -) : *)) : ID_255;
|
||||
ID_257 = (\(x21).(((x21,(x21 : mem) : min),0.40909090909090912f : <),(0.40909090909090912f,(x21,(x21 : mem) : max) : <) : &) : ID_256);
|
||||
ID_147 = \(x27).(((_,12345 : +)~(_,1103515245 : *),2147483647.0f : /),(hslider("level [unit:f][osc:/accxyz/0 -10 10]", 0.5f, 0.0f, 1.0f, 0.01f),2 : pow) : * : _,(x27 : \(x28).((x28,(x28 : mem) : -),0.0f : >) : +~\(x29).(x29,((x29,0.0f : >),50.113636363636367f : /) : -) : _,0.0f : >) : * : (+ : _,49.113636363636367f : @)~(\(x30).((x30,(x30 : mem) : +),2 : /) : _,(1.0f,hslider("attenuation [osc:/1/fader3]", 0.0050000000000000001f, 0.0f, 0.01f, 0.001f) : -) : *)) : ID_146;
|
||||
ID_148 = (\(x26).(((x26,(x26 : mem) : min),0.5f : <),(0.5f,(x26,(x26 : mem) : max) : <) : &) : ID_147);
|
||||
ID_263 = \(x27).(((_,12345 : +)~(_,1103515245 : *),2147483647.0f : /),(hslider("level [unit:f][osc:/accxyz/0 -10 10]", 0.5f, 0.0f, 1.0f, 0.01f),2 : pow) : * : _,(x27 : \(x28).((x28,(x28 : mem) : -),0.0f : >) : +~\(x29).(x29,((x29,0.0f : >),50.113636363636367f : /) : -) : _,0.0f : >) : * : (+ : _,49.113636363636367f : @)~(\(x30).((x30,(x30 : mem) : +),2 : /) : _,(1.0f,hslider("attenuation [osc:/1/fader3]", 0.0050000000000000001f, 0.0f, 0.01f, 0.001f) : -) : *)) : ID_262;
|
||||
ID_264 = (\(x26).(((x26,(x26 : mem) : min),0.5f : <),(0.5f,(x26,(x26 : mem) : max) : <) : &) : ID_263);
|
||||
ID_157 = \(x32).(((_,12345 : +)~(_,1103515245 : *),2147483647.0f : /),(hslider("level [unit:f][osc:/accxyz/0 -10 10]", 0.5f, 0.0f, 1.0f, 0.01f),2 : pow) : * : _,(x32 : \(x33).((x33,(x33 : mem) : -),0.0f : >) : +~\(x34).(x34,((x34,0.0f : >),43.62645436518077f : /) : -) : _,0.0f : >) : * : (+ : _,42.62645436518077f : @)~(\(x35).((x35,(x35 : mem) : +),2 : /) : _,(1.0f,hslider("attenuation [osc:/1/fader3]", 0.0050000000000000001f, 0.0f, 0.01f, 0.001f) : -) : *)) : ID_156;
|
||||
ID_158 = (\(x31).(((x31,(x31 : mem) : min),0.59090909090909094f : <),(0.59090909090909094f,(x31,(x31 : mem) : max) : <) : &) : ID_157);
|
||||
ID_273 = \(x32).(((_,12345 : +)~(_,1103515245 : *),2147483647.0f : /),(hslider("level [unit:f][osc:/accxyz/0 -10 10]", 0.5f, 0.0f, 1.0f, 0.01f),2 : pow) : * : _,(x32 : \(x33).((x33,(x33 : mem) : -),0.0f : >) : +~\(x34).(x34,((x34,0.0f : >),43.62645436518077f : /) : -) : _,0.0f : >) : * : (+ : _,42.62645436518077f : @)~(\(x35).((x35,(x35 : mem) : +),2 : /) : _,(1.0f,hslider("attenuation [osc:/1/fader3]", 0.0050000000000000001f, 0.0f, 0.01f, 0.001f) : -) : *)) : ID_272;
|
||||
ID_274 = (\(x31).(((x31,(x31 : mem) : min),0.59090909090909094f : <),(0.59090909090909094f,(x31,(x31 : mem) : max) : <) : &) : ID_273);
|
||||
ID_167 = \(x37).(((_,12345 : +)~(_,1103515245 : *),2147483647.0f : /),(hslider("level [unit:f][osc:/accxyz/0 -10 10]", 0.5f, 0.0f, 1.0f, 0.01f),2 : pow) : * : _,(x37 : \(x38).((x38,(x38 : mem) : -),0.0f : >) : +~\(x39).(x39,((x39,0.0f : >),37.979034422220771f : /) : -) : _,0.0f : >) : * : (+ : _,36.979034422220771f : @)~(\(x40).((x40,(x40 : mem) : +),2 : /) : _,(1.0f,hslider("attenuation [osc:/1/fader3]", 0.0050000000000000001f, 0.0f, 0.01f, 0.001f) : -) : *)) : ID_166;
|
||||
ID_168 = (\(x36).(((x36,(x36 : mem) : min),0.68181818181818177f : <),(0.68181818181818177f,(x36,(x36 : mem) : max) : <) : &) : ID_167);
|
||||
ID_283 = \(x37).(((_,12345 : +)~(_,1103515245 : *),2147483647.0f : /),(hslider("level [unit:f][osc:/accxyz/0 -10 10]", 0.5f, 0.0f, 1.0f, 0.01f),2 : pow) : * : _,(x37 : \(x38).((x38,(x38 : mem) : -),0.0f : >) : +~\(x39).(x39,((x39,0.0f : >),37.979034422220771f : /) : -) : _,0.0f : >) : * : (+ : _,36.979034422220771f : @)~(\(x40).((x40,(x40 : mem) : +),2 : /) : _,(1.0f,hslider("attenuation [osc:/1/fader3]", 0.0050000000000000001f, 0.0f, 0.01f, 0.001f) : -) : *)) : ID_282;
|
||||
ID_284 = (\(x36).(((x36,(x36 : mem) : min),0.68181818181818177f : <),(0.68181818181818177f,(x36,(x36 : mem) : max) : <) : &) : ID_283);
|
||||
ID_174 = \(x42).(((_,12345 : +)~(_,1103515245 : *),2147483647.0f : /),(hslider("level [unit:f][osc:/accxyz/0 -10 10]", 0.5f, 0.0f, 1.0f, 0.01f),2 : pow) : * : _,(x42 : \(x43).((x43,(x43 : mem) : -),0.0f : >) : +~\(x44).(x44,((x44,0.0f : >),33.062669809707174f : /) : -) : _,0.0f : >) : * : (+ : _,32.062669809707174f : @)~(\(x45).((x45,(x45 : mem) : +),2 : /) : _,(1.0f,hslider("attenuation [osc:/1/fader3]", 0.0050000000000000001f, 0.0f, 0.01f, 0.001f) : -) : *)) : ID_173;
|
||||
ID_175 = (\(x41).(((x41,(x41 : mem) : min),0.77272727272727271f : <),(0.77272727272727271f,(x41,(x41 : mem) : max) : <) : &) : ID_174);
|
||||
ID_290 = \(x42).(((_,12345 : +)~(_,1103515245 : *),2147483647.0f : /),(hslider("level [unit:f][osc:/accxyz/0 -10 10]", 0.5f, 0.0f, 1.0f, 0.01f),2 : pow) : * : _,(x42 : \(x43).((x43,(x43 : mem) : -),0.0f : >) : +~\(x44).(x44,((x44,0.0f : >),33.062669809707174f : /) : -) : _,0.0f : >) : * : (+ : _,32.062669809707174f : @)~(\(x45).((x45,(x45 : mem) : +),2 : /) : _,(1.0f,hslider("attenuation [osc:/1/fader3]", 0.0050000000000000001f, 0.0f, 0.01f, 0.001f) : -) : *)) : ID_289;
|
||||
ID_291 = (\(x41).(((x41,(x41 : mem) : min),0.77272727272727271f : <),(0.77272727272727271f,(x41,(x41 : mem) : max) : <) : &) : ID_290);
|
||||
ID_178 = \(x47).(((_,12345 : +)~(_,1103515245 : *),2147483647.0f : /),(hslider("level [unit:f][osc:/accxyz/0 -10 10]", 0.5f, 0.0f, 1.0f, 0.01f),2 : pow) : * : _,(x47 : \(x48).((x48,(x48 : mem) : -),0.0f : >) : +~\(x49).(x49,((x49,0.0f : >),28.782725826914341f : /) : -) : _,0.0f : >) : * : (+ : _,27.782725826914341f : @)~(\(x50).((x50,(x50 : mem) : +),2 : /) : _,(1.0f,hslider("attenuation [osc:/1/fader3]", 0.0050000000000000001f, 0.0f, 0.01f, 0.001f) : -) : *)) : ID_177;
|
||||
ID_179 = (\(x46).(((x46,(x46 : mem) : min),0.86363636363636365f : <),(0.86363636363636365f,(x46,(x46 : mem) : max) : <) : &) : ID_178);
|
||||
ID_294 = \(x47).(((_,12345 : +)~(_,1103515245 : *),2147483647.0f : /),(hslider("level [unit:f][osc:/accxyz/0 -10 10]", 0.5f, 0.0f, 1.0f, 0.01f),2 : pow) : * : _,(x47 : \(x48).((x48,(x48 : mem) : -),0.0f : >) : +~\(x49).(x49,((x49,0.0f : >),28.782725826914341f : /) : -) : _,0.0f : >) : * : (+ : _,27.782725826914341f : @)~(\(x50).((x50,(x50 : mem) : +),2 : /) : _,(1.0f,hslider("attenuation [osc:/1/fader3]", 0.0050000000000000001f, 0.0f, 0.01f, 0.001f) : -) : *)) : ID_293;
|
||||
ID_295 = (\(x46).(((x46,(x46 : mem) : min),0.86363636363636365f : <),(0.86363636363636365f,(x46,(x46 : mem) : max) : <) : &) : ID_294);
|
||||
ID_185 = \(x52).(((_,12345 : +)~(_,1103515245 : *),2147483647.0f : /),(hslider("level [unit:f][osc:/accxyz/0 -10 10]", 0.5f, 0.0f, 1.0f, 0.01f),2 : pow) : * : _,(x52 : \(x53).((x53,(x53 : mem) : -),0.0f : >) : +~\(x54).(x54,((x54,0.0f : >),25.056818181818183f : /) : -) : _,0.0f : >) : * : (+ : _,24.056818181818183f : @)~(\(x55).((x55,(x55 : mem) : +),2 : /) : _,(1.0f,hslider("attenuation [osc:/1/fader3]", 0.0050000000000000001f, 0.0f, 0.01f, 0.001f) : -) : *)) : ID_184;
|
||||
ID_186 = (\(x51).(((x51,(x51 : mem) : min),0.95454545454545459f : <),(0.95454545454545459f,(x51,(x51 : mem) : max) : <) : &) : ID_185);
|
||||
ID_187 = ID_179, ID_186;
|
||||
ID_188 = ID_175, ID_187;
|
||||
ID_189 = ID_168, ID_188;
|
||||
ID_190 = ID_158, ID_189;
|
||||
ID_191 = ID_148, ID_190;
|
||||
ID_192 = ID_141, ID_191;
|
||||
ID_193 = ID_131, ID_192;
|
||||
ID_194 = ID_121, ID_193;
|
||||
ID_195 = ID_111, ID_194;
|
||||
ID_196 = ID_101, ID_195;
|
||||
ID_198 = ID_196 :> ID_197;
|
||||
ID_199 = ID_91 <: ID_198;
|
||||
ID_301 = \(x52).(((_,12345 : +)~(_,1103515245 : *),2147483647.0f : /),(hslider("level [unit:f][osc:/accxyz/0 -10 10]", 0.5f, 0.0f, 1.0f, 0.01f),2 : pow) : * : _,(x52 : \(x53).((x53,(x53 : mem) : -),0.0f : >) : +~\(x54).(x54,((x54,0.0f : >),25.056818181818183f : /) : -) : _,0.0f : >) : * : (+ : _,24.056818181818183f : @)~(\(x55).((x55,(x55 : mem) : +),2 : /) : _,(1.0f,hslider("attenuation [osc:/1/fader3]", 0.0050000000000000001f, 0.0f, 0.01f, 0.001f) : -) : *)) : ID_300;
|
||||
ID_302 = (\(x51).(((x51,(x51 : mem) : min),0.95454545454545459f : <),(0.95454545454545459f,(x51,(x51 : mem) : max) : <) : &) : ID_301);
|
||||
ID_303 = ID_295, ID_302;
|
||||
ID_304 = ID_291, ID_303;
|
||||
ID_305 = ID_284, ID_304;
|
||||
ID_306 = ID_274, ID_305;
|
||||
ID_307 = ID_264, ID_306;
|
||||
ID_308 = ID_257, ID_307;
|
||||
ID_309 = ID_247, ID_308;
|
||||
ID_310 = ID_237, ID_309;
|
||||
ID_311 = ID_227, ID_310;
|
||||
ID_312 = ID_217, ID_311;
|
||||
ID_314 = ID_312 :> ID_313;
|
||||
ID_315 = ID_207 <: ID_314;
|
||||
ID_42 = (65536 : int);
|
||||
ID_43 = _, ID_42;
|
||||
ID_44 = (ID_43 : %);
|
||||
ID_46 = _, ID_45;
|
||||
ID_47 = (ID_46 : +);
|
||||
ID_48 = ID_44 ~ ID_47;
|
||||
ID_49 = (ID_48 : float);
|
||||
ID_50 = ID_49, 6.2831853071795862f;
|
||||
ID_51 = (ID_50 : *);
|
||||
ID_53 = ID_51, ID_52;
|
||||
ID_54 = ID_53 : /;
|
||||
ID_55 = (ID_54 : sin);
|
||||
ID_74 = (ID_73 : int);
|
||||
ID_78 = hslider("volume [unit:dB]", 0.0f, -96.0f, 0.0f, 0.10000000000000001f);
|
||||
ID_75 = ID_55, ID_74;
|
||||
ID_76 = 65536, ID_75;
|
||||
ID_77 = (ID_76 : rdtable);
|
||||
ID_79 = \(x2).(10.0f,(x2,20.0f : /) : pow) : \(x3).(\(x4).(((1.0f,(1,(44.100000000000001f,(192000.0f,(1.0f,fconstant(int fSamplingFreq, <math.h>) : max) : min) : /) : -) : -),x3 : *),((1,(44.100000000000001f,(192000.0f,(1.0f,fconstant(int fSamplingFreq, <math.h>) : max) : min) : /) : -),x4 : *) : +)~_);
|
||||
ID_80 = (ID_78 : ID_79);
|
||||
ID_81 = ID_77, ID_80;
|
||||
ID_82 = ID_81 : *;
|
||||
ID_83 = vgroup("Oscillator", ID_82);
|
||||
ID_317 = (ID_316 : +);
|
||||
ID_316 = _, 12345;
|
||||
ID_3 = (1 : mem);
|
||||
ID_14 = hslider("freq [unit:Hz]", 1000.0f, 20.0f, 24000.0f, 1.0f);
|
||||
ID_15 = fconstant(int fSamplingFreq, <math.h>);
|
||||
ID_333 = vslider("Volume[style:knob][acc: 0 0 -10 0 10]", 0.5f, 0.0f, 1.0f, 0.10000000000000001f);
|
||||
ID_334 = ID_332, ID_333;
|
||||
ID_331 = ID_330, 2147483647.0f;
|
||||
ID_332 = (ID_331 : /);
|
||||
ID_330 = ID_327 ~ ID_329;
|
||||
ID_326 = _, 12345;
|
||||
ID_327 = (ID_326 : +);
|
||||
ID_328 = _, 1103515245;
|
||||
ID_329 = (ID_328 : *);
|
||||
ID_335 = ID_334 : *;
|
||||
ID_16 = 1.0f, ID_15;
|
||||
ID_17 = (ID_16 : max);
|
||||
ID_18 = 192000.0f, ID_17;
|
||||
ID_19 = (ID_18 : min);
|
||||
ID_20 = ID_14, ID_19;
|
||||
ID_21 = (ID_20 : /);
|
||||
ID_22 = _, ID_21;
|
||||
ID_23 = (ID_22 : +);
|
||||
ID_24 = ID_23, 0;
|
||||
ID_25 = 0, ID_24;
|
||||
ID_26 = ID_25 : select2;
|
||||
ID_10 = (65536 : float);
|
||||
ID_29 = _, ID_10;
|
||||
ID_30 = ID_29 : *;
|
||||
ID_27 = (ID_26 : \(x1).(x1,(x1 : floor) : -));
|
||||
ID_28 = ID_27 ~ _;
|
||||
ID_31 = ID_28 : ID_30;
|
||||
ID_0 = (65536 : int);
|
||||
ID_1 = _, ID_0;
|
||||
ID_2 = (ID_1 : %);
|
||||
ID_4 = _, ID_3;
|
||||
ID_5 = (ID_4 : +);
|
||||
ID_6 = ID_2 ~ ID_5;
|
||||
ID_7 = (ID_6 : float);
|
||||
ID_8 = ID_7, 6.2831853071795862f;
|
||||
ID_9 = (ID_8 : *);
|
||||
ID_11 = ID_9, ID_10;
|
||||
ID_12 = ID_11 : /;
|
||||
ID_13 = (ID_12 : sin);
|
||||
ID_32 = (ID_31 : int);
|
||||
ID_36 = hslider("volume [unit:dB]", 0.0f, -96.0f, 0.0f, 0.10000000000000001f);
|
||||
ID_33 = ID_13, ID_32;
|
||||
ID_34 = 65536, ID_33;
|
||||
ID_35 = (ID_34 : rdtable);
|
||||
ID_37 = \(x2).(10.0f,(x2,20.0f : /) : pow) : \(x3).(\(x4).(((1.0f,(1,(44.100000000000001f,(192000.0f,(1.0f,fconstant(int fSamplingFreq, <math.h>) : max) : min) : /) : -) : -),x3 : *),((1,(44.100000000000001f,(192000.0f,(1.0f,fconstant(int fSamplingFreq, <math.h>) : max) : min) : /) : -),x4 : *) : +)~_);
|
||||
ID_38 = (ID_36 : ID_37);
|
||||
ID_39 = ID_35, ID_38;
|
||||
ID_40 = ID_39 : *;
|
||||
ID_41 = vgroup("Oscillator", ID_40);
|
||||
process = ID_335;
|
||||
@@ -0,0 +1,122 @@
|
||||
/* ------------------------------------------------------------
|
||||
author: "Grame"
|
||||
copyright: "(c)GRAME 2009"
|
||||
license: "BSD"
|
||||
name: "Noise"
|
||||
version: "1.1"
|
||||
Code generated with Faust 2.38.16 (https://faust.grame.fr)
|
||||
Compilation options: -lang cpp -es 1 -mcd 16 -single -ftz 0
|
||||
------------------------------------------------------------ */
|
||||
|
||||
#ifndef __mydsp_H__
|
||||
#define __mydsp_H__
|
||||
|
||||
#ifndef FAUSTFLOAT
|
||||
#define FAUSTFLOAT float
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include "AudioTools/AudioLibs/AudioFaustDSP.h" // used to define dsp class
|
||||
|
||||
#ifndef FAUSTCLASS
|
||||
#define FAUSTCLASS mydsp
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
#define exp10f __exp10f
|
||||
#define exp10 __exp10
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define RESTRICT __restrict
|
||||
#else
|
||||
#define RESTRICT __restrict__
|
||||
#endif
|
||||
|
||||
|
||||
class mydsp : public dsp {
|
||||
|
||||
private:
|
||||
|
||||
FAUSTFLOAT fVslider0;
|
||||
int iRec0[2];
|
||||
int fSampleRate;
|
||||
|
||||
public:
|
||||
|
||||
void metadata(Meta* m) {
|
||||
m->declare("author", "Grame");
|
||||
m->declare("compilation_options", "-single -scal -I /Users/pschatzmann/.FaustLive-CurrentSession-2.2/Libs -I /Users/pschatzmann/.FaustLive-CurrentSession-2.2/Examples");
|
||||
m->declare("compile_options", "-lang cpp -es 1 -mcd 16 -single -ftz 0 ");
|
||||
m->declare("copyright", "(c)GRAME 2009");
|
||||
m->declare("filename", "Noise.dsp");
|
||||
m->declare("license", "BSD");
|
||||
m->declare("name", "Noise");
|
||||
m->declare("version", "1.1");
|
||||
}
|
||||
|
||||
virtual int getNumInputs() {
|
||||
return 0;
|
||||
}
|
||||
virtual int getNumOutputs() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void classInit(int sample_rate) {
|
||||
}
|
||||
|
||||
virtual void instanceConstants(int sample_rate) {
|
||||
fSampleRate = sample_rate;
|
||||
}
|
||||
|
||||
virtual void instanceResetUserInterface() {
|
||||
fVslider0 = FAUSTFLOAT(0.5f);
|
||||
}
|
||||
|
||||
virtual void instanceClear() {
|
||||
for (int l0 = 0; (l0 < 2); l0 = (l0 + 1)) {
|
||||
iRec0[l0] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void init(int sample_rate) {
|
||||
classInit(sample_rate);
|
||||
instanceInit(sample_rate);
|
||||
}
|
||||
virtual void instanceInit(int sample_rate) {
|
||||
instanceConstants(sample_rate);
|
||||
instanceResetUserInterface();
|
||||
instanceClear();
|
||||
}
|
||||
|
||||
virtual mydsp* clone() {
|
||||
return new mydsp();
|
||||
}
|
||||
|
||||
virtual int getSampleRate() {
|
||||
return fSampleRate;
|
||||
}
|
||||
|
||||
virtual void buildUserInterface(UI* ui_interface) {
|
||||
ui_interface->openVerticalBox("Noise");
|
||||
ui_interface->declare(&fVslider0, "acc", "0 0 -10 0 10");
|
||||
ui_interface->declare(&fVslider0, "style", "knob");
|
||||
ui_interface->addVerticalSlider("Volume", &fVslider0, FAUSTFLOAT(0.5f), FAUSTFLOAT(0.0f), FAUSTFLOAT(1.0f), FAUSTFLOAT(0.100000001f));
|
||||
ui_interface->closeBox();
|
||||
}
|
||||
|
||||
virtual void compute(int count, FAUSTFLOAT** RESTRICT inputs, FAUSTFLOAT** RESTRICT outputs) {
|
||||
FAUSTFLOAT* output0 = outputs[0];
|
||||
float fSlow0 = (4.65661287e-10f * float(fVslider0));
|
||||
for (int i0 = 0; (i0 < count); i0 = (i0 + 1)) {
|
||||
iRec0[0] = ((1103515245 * iRec0[1]) + 12345);
|
||||
output0[i0] = FAUSTFLOAT((fSlow0 * float(iRec0[0])));
|
||||
iRec0[1] = iRec0[0];
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* @file streams-faust_noise-i2s.ino
|
||||
* @author Phil Schatzmann
|
||||
* @brief Example how to use Faust as Audio Source
|
||||
* @version 0.1
|
||||
* @date 2022-04-22
|
||||
*
|
||||
* @copyright Copyright (c) 2022
|
||||
*
|
||||
*/
|
||||
|
||||
#include "AudioTools.h"
|
||||
#include "AudioTools/AudioLibs/AudioBoardStream.h"
|
||||
#include "AudioTools/AudioLibs/AudioFaust.h"
|
||||
#include "Noise.h"
|
||||
|
||||
FaustStream<mydsp> faust;
|
||||
AudioBoardStream out(AudioKitEs8388V1);
|
||||
StreamCopy copier(out, faust); // copy mic to tfl
|
||||
|
||||
// Arduino Setup
|
||||
void setup(void) {
|
||||
// Open Serial
|
||||
Serial.begin(115200);
|
||||
AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning);
|
||||
|
||||
// Setup Faust
|
||||
auto cfg = faust.defaultConfig();
|
||||
faust.begin(cfg);
|
||||
|
||||
faust.setLabelValue("Volume", 0.1);
|
||||
|
||||
|
||||
// start I2S
|
||||
auto cfg_i2s = out.defaultConfig(TX_MODE);
|
||||
cfg_i2s.sample_rate = cfg.sample_rate;
|
||||
cfg_i2s.channels = cfg.channels;
|
||||
cfg_i2s.bits_per_sample = cfg.bits_per_sample;
|
||||
out.begin(cfg_i2s);
|
||||
|
||||
}
|
||||
|
||||
// Arduino loop - copy sound to out
|
||||
void loop() {
|
||||
copier.copy();
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* @file streams-faust-i2s.ino
|
||||
* @author Phil Schatzmann
|
||||
* @brief Example how to use Faust as "Singal Processor"
|
||||
* @version 0.1
|
||||
* @date 2022-04-22
|
||||
*
|
||||
* @copyright Copyright (c) 2022
|
||||
*
|
||||
*/
|
||||
|
||||
#include "AudioTools.h"
|
||||
#include "AudioTools/AudioLibs/AudioBoardStream.h"
|
||||
#include "AudioTools/AudioLibs/AudioFaust.h"
|
||||
#include "volume.h"
|
||||
|
||||
SineWaveGenerator<int16_t> sineWave(32000); // subclass of SoundGenerator with max amplitude of 32000
|
||||
GeneratedSoundStream<int16_t> sound(sineWave); // Stream generated from sine wave
|
||||
mydsp dsp;
|
||||
AudioBoardStream out(AudioKitEs8388V1);
|
||||
FaustStream<mydsp> faust(out);
|
||||
StreamCopy copier(faust, sound); // copy mic to tfl
|
||||
|
||||
// Arduino Setup
|
||||
void setup(void) {
|
||||
// Open Serial
|
||||
Serial.begin(115200);
|
||||
AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning);
|
||||
|
||||
// Setup Faust volume control
|
||||
auto cfg = faust.defaultConfig();
|
||||
faust.begin(cfg);
|
||||
faust.setLabelValue("0x00", 0.5);
|
||||
|
||||
// Setup sine wave generator
|
||||
sineWave.begin(cfg.channels, cfg.sample_rate, N_B4);
|
||||
|
||||
// start I2S
|
||||
auto cfg_i2s = out.defaultConfig(TX_MODE);
|
||||
cfg_i2s.sample_rate = cfg.sample_rate;
|
||||
cfg_i2s.channels = cfg.channels;
|
||||
cfg_i2s.bits_per_sample = cfg.bits_per_sample;
|
||||
out.begin(cfg_i2s);
|
||||
|
||||
}
|
||||
|
||||
// Arduino loop - copy sound to out
|
||||
void loop() {
|
||||
copier.copy();
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
declare compilation_options "-single -scal -inpl -fx -I /Users/pschatzmann/.FaustLive-CurrentSession-2.0/Libs -I /Users/pschatzmann/.FaustLive-CurrentSession-2.0/Examples";
|
||||
declare library_path "volume";
|
||||
declare library_path "/Users/pschatzmann/.FaustLive-CurrentSession-2.0/Libs/stdfaust.lib";
|
||||
declare library_path "/Users/pschatzmann/.FaustLive-CurrentSession-2.0/Libs/basics.lib";
|
||||
declare library_path "/Users/pschatzmann/.FaustLive-CurrentSession-2.0/Libs/signals.lib";
|
||||
declare author "Grame";
|
||||
declare basics_lib_name "Faust Basic Element Library";
|
||||
declare basics_lib_version "0.1";
|
||||
declare copyright "(c)GRAME 2006";
|
||||
declare filename "volume";
|
||||
declare license "BSD";
|
||||
declare name "volume";
|
||||
declare signals_lib_name "Faust Signal Routing Library";
|
||||
declare signals_lib_version "0.0";
|
||||
declare version "1.0";
|
||||
process = _,(vslider("[1]", 0.0f, -70.0f, 4.0f, 0.10000000000000001f) : \(x1).(10.0f,(x1,20.0f : /) : pow) : _,0.0010000000000000009f : * : +~(_,0.999f : *)) : *;
|
||||
@@ -0,0 +1,126 @@
|
||||
/* ------------------------------------------------------------
|
||||
author: "Grame"
|
||||
copyright: "(c)GRAME 2006"
|
||||
license: "BSD"
|
||||
name: "volume"
|
||||
version: "1.0"
|
||||
Code generated with Faust 2.38.16 (https://faust.grame.fr)
|
||||
Compilation options: -lang cpp -es 1 -mcd 16 -single -ftz 0
|
||||
------------------------------------------------------------ */
|
||||
|
||||
#ifndef __mydsp_H__
|
||||
#define __mydsp_H__
|
||||
|
||||
#ifndef FAUSTFLOAT
|
||||
#define FAUSTFLOAT float
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
|
||||
#ifndef FAUSTCLASS
|
||||
#define FAUSTCLASS mydsp
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
#define exp10f __exp10f
|
||||
#define exp10 __exp10
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define RESTRICT __restrict
|
||||
#else
|
||||
#define RESTRICT __restrict__
|
||||
#endif
|
||||
|
||||
|
||||
class mydsp : public dsp {
|
||||
|
||||
private:
|
||||
|
||||
FAUSTFLOAT fVslider0;
|
||||
float fRec0[2];
|
||||
int fSampleRate;
|
||||
|
||||
public:
|
||||
|
||||
void metadata(Meta* m) {
|
||||
m->declare("author", "Grame");
|
||||
m->declare("basics_lib_name", "Faust Basic Element Library");
|
||||
m->declare("basics_lib_version", "0.1");
|
||||
m->declare("compilation_options", "-single -scal -inpl -fx -I /Users/pschatzmann/.FaustLive-CurrentSession-2.0/Libs -I /Users/pschatzmann/.FaustLive-CurrentSession-2.0/Examples");
|
||||
m->declare("compile_options", "-lang cpp -es 1 -mcd 16 -single -ftz 0 ");
|
||||
m->declare("copyright", "(c)GRAME 2006");
|
||||
m->declare("filename", "volume.dsp");
|
||||
m->declare("library_path", "volume");
|
||||
m->declare("license", "BSD");
|
||||
m->declare("name", "volume");
|
||||
m->declare("signals_lib_name", "Faust Signal Routing Library");
|
||||
m->declare("signals_lib_version", "0.0");
|
||||
m->declare("version", "1.0");
|
||||
}
|
||||
|
||||
virtual int getNumInputs() {
|
||||
return 1;
|
||||
}
|
||||
virtual int getNumOutputs() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void classInit(int sample_rate) {
|
||||
}
|
||||
|
||||
virtual void instanceConstants(int sample_rate) {
|
||||
fSampleRate = sample_rate;
|
||||
}
|
||||
|
||||
virtual void instanceResetUserInterface() {
|
||||
fVslider0 = FAUSTFLOAT(0.0f);
|
||||
}
|
||||
|
||||
virtual void instanceClear() {
|
||||
for (int l0 = 0; (l0 < 2); l0 = (l0 + 1)) {
|
||||
fRec0[l0] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void init(int sample_rate) {
|
||||
classInit(sample_rate);
|
||||
instanceInit(sample_rate);
|
||||
}
|
||||
virtual void instanceInit(int sample_rate) {
|
||||
instanceConstants(sample_rate);
|
||||
instanceResetUserInterface();
|
||||
instanceClear();
|
||||
}
|
||||
|
||||
virtual mydsp* clone() {
|
||||
return new mydsp();
|
||||
}
|
||||
|
||||
virtual int getSampleRate() {
|
||||
return fSampleRate;
|
||||
}
|
||||
|
||||
virtual void buildUserInterface(UI* ui_interface) {
|
||||
ui_interface->openVerticalBox("volume");
|
||||
ui_interface->declare(&fVslider0, "1", "");
|
||||
ui_interface->addVerticalSlider("0x00", &fVslider0, FAUSTFLOAT(0.0f), FAUSTFLOAT(-70.0f), FAUSTFLOAT(4.0f), FAUSTFLOAT(0.100000001f));
|
||||
ui_interface->closeBox();
|
||||
}
|
||||
|
||||
virtual void compute(int count, FAUSTFLOAT** RESTRICT inputs, FAUSTFLOAT** RESTRICT outputs) {
|
||||
FAUSTFLOAT* input0 = inputs[0];
|
||||
FAUSTFLOAT* output0 = outputs[0];
|
||||
float fSlow0 = (0.00100000005f * std::pow(10.0f, (0.0500000007f * float(fVslider0))));
|
||||
for (int i0 = 0; (i0 < count); i0 = (i0 + 1)) {
|
||||
fRec0[0] = (fSlow0 + (0.999000013f * fRec0[1]));
|
||||
output0[i0] = FAUSTFLOAT((float(input0[i0]) * fRec0[0]));
|
||||
fRec0[1] = fRec0[0];
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,3 @@
|
||||
declare filename "copy.dsp";
|
||||
declare name "copy";
|
||||
process = _,_;
|
||||
@@ -0,0 +1,106 @@
|
||||
/* ------------------------------------------------------------
|
||||
name: "Copy"
|
||||
Code generated with Faust 2.68.1 (https://faust.grame.fr)
|
||||
Compilation options: -lang cpp -ct 1 -es 1 -mcd 16 -single -ftz 0
|
||||
------------------------------------------------------------ */
|
||||
|
||||
#ifndef __mydsp_H__
|
||||
#define __mydsp_H__
|
||||
|
||||
#ifndef FAUSTFLOAT
|
||||
#define FAUSTFLOAT float
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
|
||||
#ifndef FAUSTCLASS
|
||||
#define FAUSTCLASS mydsp
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
#define exp10f __exp10f
|
||||
#define exp10 __exp10
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define RESTRICT __restrict
|
||||
#else
|
||||
#define RESTRICT __restrict__
|
||||
#endif
|
||||
|
||||
|
||||
class mydsp : public dsp {
|
||||
|
||||
private:
|
||||
|
||||
int fSampleRate;
|
||||
|
||||
public:
|
||||
mydsp() {}
|
||||
|
||||
void metadata(Meta* m) {
|
||||
m->declare("compile_options", "-lang cpp -ct 1 -es 1 -mcd 16 -single -ftz 0");
|
||||
m->declare("filename", "Copy.dsp");
|
||||
m->declare("name", "Copy");
|
||||
}
|
||||
|
||||
virtual int getNumInputs() {
|
||||
return 2;
|
||||
}
|
||||
virtual int getNumOutputs() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
static void classInit(int sample_rate) {
|
||||
}
|
||||
|
||||
virtual void instanceConstants(int sample_rate) {
|
||||
fSampleRate = sample_rate;
|
||||
}
|
||||
|
||||
virtual void instanceResetUserInterface() {
|
||||
}
|
||||
|
||||
virtual void instanceClear() {
|
||||
}
|
||||
|
||||
virtual void init(int sample_rate) {
|
||||
classInit(sample_rate);
|
||||
instanceInit(sample_rate);
|
||||
}
|
||||
|
||||
virtual void instanceInit(int sample_rate) {
|
||||
instanceConstants(sample_rate);
|
||||
instanceResetUserInterface();
|
||||
instanceClear();
|
||||
}
|
||||
|
||||
virtual mydsp* clone() {
|
||||
return new mydsp();
|
||||
}
|
||||
|
||||
virtual int getSampleRate() {
|
||||
return fSampleRate;
|
||||
}
|
||||
|
||||
virtual void buildUserInterface(UI* ui_interface) {
|
||||
ui_interface->openVerticalBox("Copy");
|
||||
ui_interface->closeBox();
|
||||
}
|
||||
|
||||
virtual void compute(int count, FAUSTFLOAT** RESTRICT inputs, FAUSTFLOAT** RESTRICT outputs) {
|
||||
FAUSTFLOAT* input0 = inputs[0];
|
||||
FAUSTFLOAT* input1 = inputs[1];
|
||||
FAUSTFLOAT* output0 = outputs[0];
|
||||
FAUSTFLOAT* output1 = outputs[1];
|
||||
for (int i0 = 0; i0 < count; i0 = i0 + 1) {
|
||||
output0[i0] = FAUSTFLOAT(float(input0[i0]));
|
||||
output1[i0] = FAUSTFLOAT(float(input1[i0]));
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* @file streams-i2s-faust_copy-i2s.ino
|
||||
* @author Phil Schatzmann
|
||||
* @brief Example how to use Faust to write and process a stereo signal. The data
|
||||
* is just copied, keeping the left and right signal separate.
|
||||
* @version 0.1
|
||||
* @date 2022-04-22
|
||||
*
|
||||
* @copyright Copyright (c) 2022
|
||||
*
|
||||
*/
|
||||
|
||||
#include "AudioTools.h"
|
||||
#include "AudioTools/AudioLibs/AudioBoardStream.h"
|
||||
#include "AudioTools/AudioLibs/AudioFaust.h"
|
||||
#include "Copy.h"
|
||||
|
||||
AudioBoardStream io(AudioKitEs8388V1);
|
||||
FaustStream<mydsp> faust(io); // final output to io
|
||||
StreamCopy copier(faust, io); // copy mic to faust
|
||||
|
||||
// Arduino Setup
|
||||
void setup(void) {
|
||||
// Open Serial
|
||||
Serial.begin(115200);
|
||||
AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning);
|
||||
|
||||
// Setup Faust
|
||||
auto cfg = faust.defaultConfig();
|
||||
faust.begin(cfg);
|
||||
|
||||
|
||||
// start I2S
|
||||
auto cfg_i2s = io.defaultConfig(RXTX_MODE);
|
||||
cfg_i2s.copyFrom(cfg);
|
||||
io.begin(cfg_i2s);
|
||||
|
||||
}
|
||||
|
||||
// Arduino loop - copy sound to out
|
||||
void loop() {
|
||||
copier.copy();
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* @file streams-i2s-faust_guitarix-i2s.ino
|
||||
* @author Phil Schatzmann
|
||||
* @brief Example how to use Faust when Faust expects input and provides kitput
|
||||
* @version 0.1
|
||||
* @date 2022-04-22
|
||||
*
|
||||
* @copyright Copyright (c) 2022
|
||||
*
|
||||
*/
|
||||
|
||||
#include "AudioTools.h"
|
||||
#include "AudioTools/AudioLibs/AudioBoardStream.h"
|
||||
#include "AudioTools/AudioLibs/AudioFaust.h"
|
||||
#include "guitarix.h"
|
||||
|
||||
AudioBoardStream kit(AudioKitEs8388V1);
|
||||
FaustStream<mydsp> faust(kit); // final output of Faust is kit
|
||||
StreamCopy copier(faust, kit); // copy data from kit to faust
|
||||
|
||||
// Arduino Setup
|
||||
void setup(void) {
|
||||
// Open Serial
|
||||
Serial.begin(115200);
|
||||
AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning);
|
||||
|
||||
// Setup Faust
|
||||
auto cfg = faust.defaultConfig();
|
||||
faust.begin(cfg);
|
||||
|
||||
// Tube Screemer
|
||||
faust.setLabelValue("drive", 0.23);
|
||||
faust.setLabelValue("level", -12.6);
|
||||
faust.setLabelValue("tone", 489);
|
||||
// preamp
|
||||
faust.setLabelValue("Pregain", 0.4);
|
||||
faust.setLabelValue("Gain", 0.4);
|
||||
//jcm 2000
|
||||
faust.setLabelValue("Treble", 0.9);
|
||||
faust.setLabelValue("Middle", 0.76);
|
||||
faust.setLabelValue("Bass", 0.62);
|
||||
//Cabinet
|
||||
faust.setLabelValue("amount", 100);
|
||||
|
||||
|
||||
// start I2S
|
||||
auto cfg_i2s = kit.defaultConfig(RXTX_MODE);
|
||||
cfg_i2s.sample_rate = cfg.sample_rate;
|
||||
cfg_i2s.channels = cfg.channels;
|
||||
cfg_i2s.bits_per_sample = cfg.bits_per_sample;
|
||||
cfg_i2s.input_device = ADC_INPUT_LINE1;
|
||||
kit.begin(cfg_i2s);
|
||||
|
||||
}
|
||||
|
||||
// Arduino loop - copy sound to kit
|
||||
void loop() {
|
||||
copier.copy();
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
declare filename "pitchShifter.dsp";
|
||||
declare name "pitchShifter";
|
||||
declare name "pitchShifter";
|
||||
declare version "1.0";
|
||||
declare author "Grame";
|
||||
declare license "BSD";
|
||||
declare copyright "(c)GRAME 2006";
|
||||
|
||||
//--------------------------------------
|
||||
// very simple real time pitch shifter
|
||||
//--------------------------------------
|
||||
|
||||
import("stdfaust.lib");
|
||||
|
||||
pitchshifter = vgroup("Pitch Shifter", ef.transpose(
|
||||
hslider("window (samples)", 1000, 50, 10000, 1),
|
||||
hslider("xfade (samples)", 10, 1, 10000, 1),
|
||||
hslider("shift (semitones) ", 0, -12, +12, 0.1)
|
||||
)
|
||||
);
|
||||
|
||||
process = (pitchshifter, pitchshifter);
|
||||
@@ -0,0 +1,172 @@
|
||||
/* ------------------------------------------------------------
|
||||
author: "Grame"
|
||||
copyright: "(c)GRAME 2006"
|
||||
license: "BSD"
|
||||
name: "pitchShifter"
|
||||
version: "1.0"
|
||||
Code generated with Faust 2.68.1 (https://faust.grame.fr)
|
||||
Compilation options: -lang cpp -ct 1 -es 1 -mcd 16 -single -ftz 0
|
||||
------------------------------------------------------------ */
|
||||
|
||||
#ifndef __mydsp_H__
|
||||
#define __mydsp_H__
|
||||
|
||||
#ifndef FAUSTFLOAT
|
||||
#define FAUSTFLOAT float
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
|
||||
#ifndef FAUSTCLASS
|
||||
#define FAUSTCLASS mydsp
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
#define exp10f __exp10f
|
||||
#define exp10 __exp10
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define RESTRICT __restrict
|
||||
#else
|
||||
#define RESTRICT __restrict__
|
||||
#endif
|
||||
|
||||
|
||||
class mydsp : public dsp {
|
||||
|
||||
private:
|
||||
|
||||
FAUSTFLOAT fHslider0;
|
||||
FAUSTFLOAT fHslider1;
|
||||
float fRec0[2];
|
||||
FAUSTFLOAT fHslider2;
|
||||
int IOTA0;
|
||||
float fVec0[131072];
|
||||
float fVec1[131072];
|
||||
int fSampleRate;
|
||||
|
||||
public:
|
||||
mydsp() {}
|
||||
|
||||
void metadata(Meta* m) {
|
||||
m->declare("author", "Grame");
|
||||
m->declare("compile_options", "-lang cpp -ct 1 -es 1 -mcd 16 -single -ftz 0");
|
||||
m->declare("copyright", "(c)GRAME 2006");
|
||||
m->declare("delays.lib/name", "Faust Delay Library");
|
||||
m->declare("delays.lib/version", "1.1.0");
|
||||
m->declare("filename", "pitchShifter.dsp");
|
||||
m->declare("license", "BSD");
|
||||
m->declare("maths.lib/author", "GRAME");
|
||||
m->declare("maths.lib/copyright", "GRAME");
|
||||
m->declare("maths.lib/license", "LGPL with exception");
|
||||
m->declare("maths.lib/name", "Faust Math Library");
|
||||
m->declare("maths.lib/version", "2.6.0");
|
||||
m->declare("misceffects.lib/name", "Misc Effects Library");
|
||||
m->declare("misceffects.lib/version", "2.1.0");
|
||||
m->declare("name", "pitchShifter");
|
||||
m->declare("version", "1.0");
|
||||
}
|
||||
|
||||
virtual int getNumInputs() {
|
||||
return 2;
|
||||
}
|
||||
virtual int getNumOutputs() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
static void classInit(int sample_rate) {
|
||||
}
|
||||
|
||||
virtual void instanceConstants(int sample_rate) {
|
||||
fSampleRate = sample_rate;
|
||||
}
|
||||
|
||||
virtual void instanceResetUserInterface() {
|
||||
fHslider0 = FAUSTFLOAT(0.0f);
|
||||
fHslider1 = FAUSTFLOAT(1e+03f);
|
||||
fHslider2 = FAUSTFLOAT(1e+01f);
|
||||
}
|
||||
|
||||
virtual void instanceClear() {
|
||||
for (int l0 = 0; l0 < 2; l0 = l0 + 1) {
|
||||
fRec0[l0] = 0.0f;
|
||||
}
|
||||
IOTA0 = 0;
|
||||
for (int l1 = 0; l1 < 131072; l1 = l1 + 1) {
|
||||
fVec0[l1] = 0.0f;
|
||||
}
|
||||
for (int l2 = 0; l2 < 131072; l2 = l2 + 1) {
|
||||
fVec1[l2] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void init(int sample_rate) {
|
||||
classInit(sample_rate);
|
||||
instanceInit(sample_rate);
|
||||
}
|
||||
|
||||
virtual void instanceInit(int sample_rate) {
|
||||
instanceConstants(sample_rate);
|
||||
instanceResetUserInterface();
|
||||
instanceClear();
|
||||
}
|
||||
|
||||
virtual mydsp* clone() {
|
||||
return new mydsp();
|
||||
}
|
||||
|
||||
virtual int getSampleRate() {
|
||||
return fSampleRate;
|
||||
}
|
||||
|
||||
virtual void buildUserInterface(UI* ui_interface) {
|
||||
ui_interface->openVerticalBox("Pitch Shifter");
|
||||
ui_interface->addHorizontalSlider("shift (semitones)", &fHslider0, FAUSTFLOAT(0.0f), FAUSTFLOAT(-12.0f), FAUSTFLOAT(12.0f), FAUSTFLOAT(0.1f));
|
||||
ui_interface->addHorizontalSlider("window (samples)", &fHslider1, FAUSTFLOAT(1e+03f), FAUSTFLOAT(5e+01f), FAUSTFLOAT(1e+04f), FAUSTFLOAT(1.0f));
|
||||
ui_interface->addHorizontalSlider("xfade (samples)", &fHslider2, FAUSTFLOAT(1e+01f), FAUSTFLOAT(1.0f), FAUSTFLOAT(1e+04f), FAUSTFLOAT(1.0f));
|
||||
ui_interface->closeBox();
|
||||
}
|
||||
|
||||
virtual void compute(int count, FAUSTFLOAT** RESTRICT inputs, FAUSTFLOAT** RESTRICT outputs) {
|
||||
FAUSTFLOAT* input0 = inputs[0];
|
||||
FAUSTFLOAT* input1 = inputs[1];
|
||||
FAUSTFLOAT* output0 = outputs[0];
|
||||
FAUSTFLOAT* output1 = outputs[1];
|
||||
float fSlow0 = std::pow(2.0f, 0.083333336f * float(fHslider0));
|
||||
float fSlow1 = float(fHslider1);
|
||||
float fSlow2 = 1.0f / float(fHslider2);
|
||||
for (int i0 = 0; i0 < count; i0 = i0 + 1) {
|
||||
fRec0[0] = std::fmod(fSlow1 + (fRec0[1] + 1.0f - fSlow0), fSlow1);
|
||||
float fTemp0 = std::min<float>(fSlow2 * fRec0[0], 1.0f);
|
||||
float fTemp1 = 1.0f - fTemp0;
|
||||
float fTemp2 = float(input0[i0]);
|
||||
fVec0[IOTA0 & 131071] = fTemp2;
|
||||
float fTemp3 = fSlow1 + fRec0[0];
|
||||
int iTemp4 = int(fTemp3);
|
||||
int iTemp5 = std::min<int>(65537, std::max<int>(0, iTemp4 + 1));
|
||||
float fTemp6 = std::floor(fTemp3);
|
||||
float fTemp7 = fSlow1 + (fRec0[0] - fTemp6);
|
||||
float fTemp8 = 1.0f - fRec0[0];
|
||||
float fTemp9 = fTemp6 + fTemp8 - fSlow1;
|
||||
int iTemp10 = std::min<int>(65537, std::max<int>(0, iTemp4));
|
||||
int iTemp11 = int(fRec0[0]);
|
||||
int iTemp12 = std::min<int>(65537, std::max<int>(0, iTemp11 + 1));
|
||||
float fTemp13 = std::floor(fRec0[0]);
|
||||
float fTemp14 = fRec0[0] - fTemp13;
|
||||
float fTemp15 = fTemp13 + fTemp8;
|
||||
int iTemp16 = std::min<int>(65537, std::max<int>(0, iTemp11));
|
||||
output0[i0] = FAUSTFLOAT((fVec0[(IOTA0 - iTemp16) & 131071] * fTemp15 + fTemp14 * fVec0[(IOTA0 - iTemp12) & 131071]) * fTemp0 + (fVec0[(IOTA0 - iTemp10) & 131071] * fTemp9 + fTemp7 * fVec0[(IOTA0 - iTemp5) & 131071]) * fTemp1);
|
||||
float fTemp17 = float(input1[i0]);
|
||||
fVec1[IOTA0 & 131071] = fTemp17;
|
||||
output1[i0] = FAUSTFLOAT(fTemp0 * (fVec1[(IOTA0 - iTemp16) & 131071] * fTemp15 + fTemp14 * fVec1[(IOTA0 - iTemp12) & 131071]) + fTemp1 * (fTemp9 * fVec1[(IOTA0 - iTemp10) & 131071] + fTemp7 * fVec1[(IOTA0 - iTemp5) & 131071]));
|
||||
fRec0[1] = fRec0[0];
|
||||
IOTA0 = IOTA0 + 1;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* @file streams-i2s-faust_pitchshift-i2s.ino
|
||||
* @author Phil Schatzmann
|
||||
* @brief Example how to use Faust for pitch shifting in stereo
|
||||
* @version 0.1
|
||||
* @date 2022-04-22
|
||||
*
|
||||
* @copyright Copyright (c) 2022
|
||||
*
|
||||
*/
|
||||
|
||||
#include "AudioTools.h"
|
||||
#include "AudioTools/AudioLibs/AudioBoardStream.h"
|
||||
#include "AudioTools/AudioLibs/AudioFaust.h"
|
||||
#include "pitchShifter.h"
|
||||
|
||||
AudioBoardStream io(AudioKitEs8388V1);
|
||||
FaustStream<mydsp> faust(io); // final output to io
|
||||
StreamCopy copier(faust, io); // copy mic to faust
|
||||
|
||||
// Arduino Setup
|
||||
void setup(void) {
|
||||
// Open Serial
|
||||
Serial.begin(115200);
|
||||
AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning);
|
||||
|
||||
// Setup Faust
|
||||
auto cfg = faust.defaultConfig();
|
||||
faust.begin(cfg);
|
||||
|
||||
|
||||
// start I2S
|
||||
auto cfg_i2s = io.defaultConfig(RXTX_MODE);
|
||||
cfg_i2s.copyFrom(cfg);
|
||||
io.begin(cfg_i2s);
|
||||
|
||||
}
|
||||
|
||||
// Arduino loop - copy sound to out
|
||||
void loop() {
|
||||
copier.copy();
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#include "AudioTools.h"
|
||||
#include "AudioTools/AudioLibs/MaximilianDSP.h"
|
||||
|
||||
// Define output
|
||||
I2SStream out;
|
||||
Maximilian maximilian(out);
|
||||
|
||||
//This shows how the fundamental building block of digital audio - the sine wave.
|
||||
maxiOsc mySine;//One oscillator - can be called anything. Can be any of the available waveforms.
|
||||
|
||||
void setup() {//some inits
|
||||
// setup logging
|
||||
Serial.begin(115200);
|
||||
AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info);
|
||||
// setup Aduio output
|
||||
auto cfg = out.defaultConfig(TX_MODE);
|
||||
out.begin(cfg);
|
||||
maximilian.begin(cfg);
|
||||
}
|
||||
|
||||
void play(float *output) {
|
||||
output[0]=mySine.sinewave(440);
|
||||
output[1]=output[0];
|
||||
}
|
||||
|
||||
// Arduino loop
|
||||
void loop() {
|
||||
maximilian.copy();
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
//This examples shows another fundamental building block of digital audio - adding two sine waves together. When you add waves together they create a new wave whose amplitude at any time is computed by adding the current amplitudes of each wave together. So, if one wave has an amplitude of 1, and the other has an amplitude of 1, the new wave will be equal to 2 at that point in time. Whereas, later, if one wave has an amplitude of -1, and the other has an amplitude of 1, the new wave - the one you hear - will equal 0. This can create some interesting effects, including 'beating', when the waves interact to create a single wave that fades up and down based on the frequencies of the two interacting waves. The frequency of the 'beating' i.e. the fading in and out, is equal to the difference in frequency between the two waves.
|
||||
|
||||
#include "AudioTools.h"
|
||||
#include "AudioTools/AudioLibs/MaximilianDSP.h"
|
||||
|
||||
// Define output
|
||||
I2SStream out;
|
||||
Maximilian maximilian(out);
|
||||
|
||||
maxiOsc mySine,myOtherSine;//Two oscillators with names.
|
||||
|
||||
void setup() {//some inits
|
||||
// setup logging
|
||||
Serial.begin(115200);
|
||||
AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info);
|
||||
|
||||
// setup Aduio output
|
||||
auto cfg = out.defaultConfig(TX_MODE);
|
||||
out.begin(cfg);
|
||||
maximilian.begin(cfg);
|
||||
}
|
||||
|
||||
void play(float *output) {//this is where the magic happens. Very slow magic.
|
||||
//output[0] is the left output. output[1] is the right output
|
||||
output[0]=mySine.sinewave(440)+myOtherSine.sinewave(441);//these two sines will beat together. They're now a bit too loud though..
|
||||
output[1]=output[0];
|
||||
}
|
||||
|
||||
|
||||
// Arduino loop
|
||||
void loop() {
|
||||
maximilian.copy();
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#include "AudioTools.h"
|
||||
#include "AudioTools/AudioLibs/MaximilianDSP.h"
|
||||
|
||||
// Define Arduino output
|
||||
I2SStream out;
|
||||
Maximilian maximilian(out);
|
||||
|
||||
//This shows how to use maximilian to do basic amplitude modulation. Amplitude modulation is when you multiply waves together. In maximilian you just use the * inbetween the two waveforms.
|
||||
|
||||
maxiOsc mySine,myOtherSine;//Two oscillators. They can be called anything. They can be any of the available waveforms. These ones will be sinewaves
|
||||
|
||||
void setup() {//some inits
|
||||
// setup logging
|
||||
Serial.begin(115200);
|
||||
AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info);
|
||||
|
||||
// setup Aduio output
|
||||
auto cfg = out.defaultConfig(TX_MODE);
|
||||
out.begin(cfg);
|
||||
maximilian.begin(cfg);
|
||||
}
|
||||
|
||||
void play(float *output) {
|
||||
|
||||
// This form of amplitude modulation is straightforward multiplication of two waveforms.
|
||||
// Notice that the maths is different to when you add waves.
|
||||
// The waves aren't 'beating'. Instead, the amplitude of one is modulating the amplitude of the other
|
||||
// Remember that the sine wave has positive and negative sections as it oscillates.
|
||||
// When you multiply something by -1, its phase is inverted but it retains its amplitude.
|
||||
// So you hear 2 waves per second, not 1, even though the frequency is 1.
|
||||
output[0]=mySine.sinewave(440)*myOtherSine.sinewave(1);
|
||||
output[1]=output[0];
|
||||
}
|
||||
|
||||
// Arduino loop
|
||||
void loop() {
|
||||
maximilian.copy();
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
#include "AudioTools.h"
|
||||
#include "AudioTools/AudioLibs/MaximilianDSP.h"
|
||||
|
||||
// Define Arduino output
|
||||
I2SStream out;
|
||||
Maximilian maximilian(out);
|
||||
|
||||
//This shows how to use maximilian to do basic amplitude modulation.
|
||||
//It also shows what happens when you modulate waves with waves that have frequencies over 20 hz.
|
||||
//You start to get interesting effects.
|
||||
|
||||
maxiOsc mySine,myOtherSine,myPhasor;//Three oscillators. They can be called anything. They can be any of the available waveforms.
|
||||
|
||||
|
||||
void setup() {//some inits
|
||||
// setup logging
|
||||
Serial.begin(115200);
|
||||
AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info);
|
||||
|
||||
// setup Aduio output
|
||||
auto cfg = out.defaultConfig(TX_MODE);
|
||||
out.begin(cfg);
|
||||
maximilian.begin(cfg);
|
||||
}
|
||||
|
||||
void play(float *output) {
|
||||
|
||||
//Using the phasor we can create a ramp, and use this ramp to set the frequency of one of the waves.
|
||||
//When the frequency of the lower waveform passes over the threshold of 20hz, we start to hear two new waveforms.
|
||||
//The frequency of the first new wave is the sum of the two original waves.
|
||||
//The frequency of the second new wave is the difference of the two original waves.
|
||||
//So you hear two new waves, one going up, one going down.
|
||||
|
||||
output[0]=mySine.sinewave(440)*myOtherSine.sinewave(myPhasor.phasorBetween(0.01,0,440));
|
||||
output[1]=output[0];
|
||||
|
||||
}
|
||||
|
||||
// Arduino loop
|
||||
void loop() {
|
||||
maximilian.copy();
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// One way of thinking about FM synthesis is to see it as vibrato.
|
||||
// You make a pitch, then vary it up and down at some rate.
|
||||
// You can change the speed of the pitch variation (modulation frequency), and also the amount of variation (modulation index).
|
||||
// In FM, usually only one of the waveforms - the carrier that provides the initial pitch - is sent to the output.
|
||||
// The frequency of the the carrier wave is continually adjusted at a rate equal to the frequency of the second wave (the modulator).
|
||||
// So at any given point in time, the frequency of the carrier can increase by an amount equal to the current amp of the modulator.
|
||||
// This has some interesting effects.
|
||||
|
||||
#include "AudioTools.h"
|
||||
#include "AudioTools/AudioLibs/MaximilianDSP.h"
|
||||
|
||||
// Define Arduino output
|
||||
I2SStream out;
|
||||
Maximilian maximilian(out);
|
||||
// Maximilian
|
||||
maxiOsc mySine,myOtherSine;//Two oscillators
|
||||
|
||||
|
||||
void setup() {//some inits
|
||||
// setup logging
|
||||
Serial.begin(115200);
|
||||
AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info);
|
||||
|
||||
// setup Aduio output
|
||||
auto cfg = out.defaultConfig(TX_MODE);
|
||||
out.begin(cfg);
|
||||
maximilian.begin(cfg);
|
||||
}
|
||||
|
||||
void play(float *output) {
|
||||
|
||||
// In this example, the 'myOtherSine.sinewave' is at an amplitude of 1, it's original amplitude.
|
||||
// This is pretty simple and not too useful.
|
||||
//output[0]=mySine.sinewave(440*myOtherSine.sinewave(1));
|
||||
|
||||
// Perhaps you should comment out the above line and uncomment the below one instead
|
||||
// It shows how the frequency of the carrier is altered by ADDING a second waveform to its frequency value.
|
||||
// The carrier frequency is 440, and the modulation frequency is 1.
|
||||
// It also shows how the modulation index works. In this case the modulation index is 100
|
||||
// Try adjusting the modolation index. Also, try altering the modulation frequency.
|
||||
output[0]=mySine.sinewave(440+(myOtherSine.sinewave(1)*100));
|
||||
output[1]=output[0];
|
||||
|
||||
}
|
||||
|
||||
// In complex FM systems you can have lots of modulators stacked together in interesting ways, and theoretically this can make any sound.
|
||||
// John Chowning is the guy you probably want to talk to about that.
|
||||
|
||||
// Arduino loop
|
||||
void loop() {
|
||||
maximilian.copy();
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// Nothing much to say about this other than I like it.
|
||||
|
||||
#include "AudioTools.h"
|
||||
#include "AudioTools/AudioLibs/MaximilianDSP.h"
|
||||
|
||||
// Define Arduino output
|
||||
I2SStream out;
|
||||
Maximilian maximilian(out);
|
||||
// Maximilian
|
||||
maxiOsc mySine,myOtherSine,myLastSine,myPhasor;//Three oscillators
|
||||
|
||||
|
||||
void setup() {//some inits
|
||||
// setup logging
|
||||
Serial.begin(115200);
|
||||
AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info);
|
||||
|
||||
// setup Aduio output
|
||||
auto cfg = out.defaultConfig(TX_MODE);
|
||||
out.begin(cfg);
|
||||
maximilian.begin(cfg);
|
||||
}
|
||||
|
||||
void play(float *output) {
|
||||
output[0]=mySine.sinewave(myOtherSine.sinewave(myLastSine.sinewave(0.1)*30)*440);//awesome bassline
|
||||
output[1]=output[0];
|
||||
}
|
||||
|
||||
// Arduino loop
|
||||
void loop() {
|
||||
maximilian.copy();
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
|
||||
#include "AudioTools.h"
|
||||
#include "AudioTools/AudioLibs/MaximilianDSP.h"
|
||||
|
||||
// Define Arduino output
|
||||
I2SStream out;
|
||||
Maximilian maximilian(out);
|
||||
|
||||
// Maximilian
|
||||
maxiOsc mySine; // This is the oscillator we will use to generate the test tone
|
||||
maxiClock myClock; // This will allow us to generate a clock signal and do things at specific times
|
||||
double freq; // This is a variable that we will use to hold and set the current frequency of the oscillator
|
||||
|
||||
void setup() {
|
||||
// setup logging
|
||||
Serial.begin(115200);
|
||||
AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info);
|
||||
|
||||
// setup audio output
|
||||
auto cfg = out.defaultConfig(TX_MODE);
|
||||
out.begin(cfg);
|
||||
maximilian.begin(cfg);
|
||||
|
||||
// setup maximilian
|
||||
myClock.setTicksPerBeat(1);//This sets the number of ticks per beat
|
||||
myClock.setTempo(120);// This sets the tempo in Beats Per Minute
|
||||
freq=20; // Here we initialise the variable
|
||||
}
|
||||
|
||||
void play(float *output) {
|
||||
|
||||
myClock.ticker(); // This makes the clock object count at the current samplerate
|
||||
|
||||
//This is a 'conditional'. It does a test and then does something if the test is true
|
||||
|
||||
if (myClock.tick) { // If there is an actual tick at this time, this will be true.
|
||||
|
||||
freq+=100; // DO SOMETHING
|
||||
|
||||
} // The curly braces close the conditional
|
||||
|
||||
//output[0] is the left output. output[1] is the right output
|
||||
|
||||
output[0]=mySine.sinewave(freq);//simple as that!
|
||||
output[1]=output[0];
|
||||
|
||||
}
|
||||
|
||||
// Arduino loop
|
||||
void loop() {
|
||||
maximilian.copy();
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// This example shows how you can create a basic counter with a phasor.
|
||||
// A phasor oscillator can create a ramp between any two values.
|
||||
// It takes three inputs - frequency, start value and stop value.
|
||||
// These are all double precision floats, so it's a continuous slide.
|
||||
// If you write it into an integer, it will round it off for you.
|
||||
// This creates a bunch of steps.
|
||||
|
||||
#include "AudioTools.h"
|
||||
#include "AudioTools/AudioLibs/MaximilianDSP.h"
|
||||
|
||||
// Define Arduino output
|
||||
I2SStream out;
|
||||
Maximilian maximilian(out);
|
||||
|
||||
// Maximilian
|
||||
maxiOsc myCounter,mySquare;//these oscillators will help us count and play sound
|
||||
int CurrentCount;//we're going to put the current count in this variable so that we can use it more easily.
|
||||
|
||||
|
||||
void setup() {//some inits
|
||||
// setup logging
|
||||
Serial.begin(115200);
|
||||
AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info);
|
||||
|
||||
// setup audio output
|
||||
auto cfg = out.defaultConfig(TX_MODE);
|
||||
out.begin(cfg);
|
||||
maximilian.begin(cfg);
|
||||
}
|
||||
|
||||
void play(float *output) {
|
||||
|
||||
// Here you can see that CurrentCount is an int. It's taking the continuous output of the phasor and convering it.
|
||||
// You don't need to explicityly 'cast' (i.e. change) the value from a float to an int.
|
||||
// It happens automagically in these cases.
|
||||
|
||||
// Once every second, CurrentCount counts from 1 until it gets to 9, then resets itself.
|
||||
// When it reaches 9 it resets, so the values you get are 1-8.
|
||||
|
||||
CurrentCount=myCounter.phasorBetween(1.0, 1.0, 9.0);//phasor can take three arguments; frequency, start value and end value.
|
||||
|
||||
// If we multiply the output of CurrentCount by 100, we get 100,200,300,400,500,600,700,800 in that order.
|
||||
// These become the frequency of the oscillator.
|
||||
// In this case, the oscillator is an antialiased sawtooth wave. Yum.
|
||||
output[0]=mySquare.sawn(CurrentCount*100);
|
||||
output[1]=output[0];
|
||||
|
||||
}
|
||||
|
||||
// Arduino loop
|
||||
void loop() {
|
||||
maximilian.copy();
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
#include "AudioTools.h"
|
||||
#include "AudioTools/AudioLibs/MaximilianDSP.h"
|
||||
|
||||
// Define Arduino output
|
||||
I2SStream out;
|
||||
Maximilian maximilian(out);
|
||||
|
||||
// Maximilian
|
||||
maxiOsc myCounter,mySwitchableOsc;//these oscillators will help us count and make sound.
|
||||
int CurrentCount;//we're going to put the current count in this variable so that we can use it more easily.
|
||||
double myOscOutput;//we're going to stick the output here to make it easier to mess with stuff.
|
||||
|
||||
void setup() {//some inits
|
||||
// setup logging
|
||||
Serial.begin(115200);
|
||||
AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info);
|
||||
|
||||
// setup audio output
|
||||
auto cfg = out.defaultConfig(TX_MODE);
|
||||
out.begin(cfg);
|
||||
maximilian.begin(cfg);
|
||||
}
|
||||
|
||||
void play(float *output) {
|
||||
|
||||
CurrentCount=myCounter.phasorBetween(1, 1, 9);//phasor can take three arguments; frequency, start value and end value.
|
||||
|
||||
// here we use a conditional to make something happen at a specific time.
|
||||
|
||||
if (CurrentCount<5)//simple if statement
|
||||
|
||||
myOscOutput=mySwitchableOsc.square(CurrentCount*100);
|
||||
|
||||
else if (CurrentCount>=5)//and the 'else' bit.
|
||||
|
||||
myOscOutput=mySwitchableOsc.sinewave(CurrentCount*50);//one osc object can produce whichever waveform you want.
|
||||
|
||||
output[0]=myOscOutput;
|
||||
output[1]=output[0];
|
||||
|
||||
}
|
||||
|
||||
// Arduino loop
|
||||
void loop() {
|
||||
maximilian.copy();
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
#include "AudioTools.h"
|
||||
#include "AudioTools/AudioLibs/MaximilianDSP.h"
|
||||
|
||||
// Define Arduino output
|
||||
I2SStream out;
|
||||
Maximilian maximilian(out);
|
||||
|
||||
// Maximilian
|
||||
maxiOsc myCounter,mySwitchableOsc,another;//these oscillators will help us count and make sound.
|
||||
int CurrentCount;//we're going to put the current count in this variable so that we can use it more easily.
|
||||
double myOscOutput;//we're going to stick the output here to make it easier to mess with stuff.
|
||||
int myArray[10]={100,200,300,400,300,200,100,240,640,360};
|
||||
|
||||
void setup() {//some inits
|
||||
// setup logging
|
||||
Serial.begin(115200);
|
||||
AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info);
|
||||
|
||||
// setup audio output
|
||||
auto cfg = out.defaultConfig(TX_MODE);
|
||||
out.begin(cfg);
|
||||
maximilian.begin(cfg);
|
||||
}
|
||||
|
||||
void play(float *output) {
|
||||
|
||||
CurrentCount=myCounter.phasorBetween(1*((another.sawn(0.1)+1)/2), 1, 9);//phasor can take three arguments; frequency, start value and end value.
|
||||
|
||||
if (CurrentCount<5) {//simple if statement
|
||||
|
||||
myOscOutput=mySwitchableOsc.square(myArray[CurrentCount]);
|
||||
}
|
||||
|
||||
else if (CurrentCount>=5) {//and the 'else' bit.
|
||||
|
||||
myOscOutput=mySwitchableOsc.sawn(myArray[CurrentCount]);//one osc object can produce whichever waveform you want.
|
||||
}
|
||||
output[0]=myOscOutput;//point me at your speakers and fire.
|
||||
output[1]=output[0];
|
||||
|
||||
}
|
||||
|
||||
// Arduino loop
|
||||
void loop() {
|
||||
maximilian.copy();
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
//Envelopes allow you to shape the sound. The basic idea is that a sound has the following shape
|
||||
// Attack: This is how long it takes to fade up to maximum volume
|
||||
// Decay: This is how long it takes to reach the sustain level.
|
||||
// Sustain: This is the sustain level
|
||||
// Release: This is how long it takes to fade out.
|
||||
|
||||
#include "AudioTools.h"
|
||||
#include "AudioTools/AudioLibs/MaximilianDSP.h"
|
||||
|
||||
// Define Arduino output
|
||||
I2SStream out;
|
||||
Maximilian maximilian(out);
|
||||
|
||||
// Maximilian
|
||||
maxiOsc myCounter,mySwitchableOsc;//
|
||||
int CurrentCount;//
|
||||
double myOscOutput,myCurrentVolume;//
|
||||
maxiEnv myEnvelope;
|
||||
|
||||
|
||||
void setup() {//some inits
|
||||
// setup logging
|
||||
Serial.begin(115200);
|
||||
AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info);
|
||||
|
||||
// setup audio output
|
||||
auto cfg = out.defaultConfig(TX_MODE);
|
||||
out.begin(cfg);
|
||||
maximilian.begin(cfg);
|
||||
|
||||
|
||||
//Timing is in ms
|
||||
myEnvelope.setAttack(0);
|
||||
myEnvelope.setDecay(1); // Needs to be at least 1
|
||||
myEnvelope.setSustain(1);
|
||||
myEnvelope.setRelease(1000);
|
||||
|
||||
}
|
||||
|
||||
void play(float *output) {
|
||||
|
||||
//notice that we feed in a value of 1. to create an envelope shape we can apply later.
|
||||
myCurrentVolume=myEnvelope.adsr(1.,myEnvelope.trigger);
|
||||
|
||||
CurrentCount=myCounter.phasorBetween(1, 1, 9);//phasor can take three arguments; frequency, start value and end value.
|
||||
|
||||
// You'll notice that these 'if' statements don't require curly braces "{}".
|
||||
// This is because there is only one outcome if the statement is true.
|
||||
|
||||
if (CurrentCount==1) myEnvelope.trigger=1; //trigger the envelope
|
||||
|
||||
else myEnvelope.trigger=0;//release the envelope to make it fade out only if it's been triggered
|
||||
|
||||
if (CurrentCount<5)
|
||||
|
||||
myOscOutput=mySwitchableOsc.sawn(CurrentCount*100);
|
||||
|
||||
else if (CurrentCount>=5)//and the 'else' bit.
|
||||
|
||||
myOscOutput=mySwitchableOsc.sinewave(CurrentCount*50);//one osc object can produce whichever waveform you want.
|
||||
|
||||
|
||||
output[0]=myOscOutput*myCurrentVolume;//left speaker
|
||||
output[1]=output[0];
|
||||
|
||||
}
|
||||
|
||||
// Arduino loop
|
||||
void loop() {
|
||||
maximilian.copy();
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
// Here is an example of a Maximilian filter being used.
|
||||
// There are a number of filters in Maximilian, including low and high pass filters.
|
||||
// There are also resonant filters and a state variable filter.
|
||||
|
||||
|
||||
#include "AudioTools.h"
|
||||
#include "AudioTools/AudioLibs/MaximilianDSP.h"
|
||||
|
||||
// Define Arduino output
|
||||
I2SStream out;
|
||||
Maximilian maximilian(out);
|
||||
|
||||
// Maximilian
|
||||
maxiOsc myCounter,mySwitchableOsc;//
|
||||
int CurrentCount;//
|
||||
double myOscOutput,myCurrentVolume, myFilteredOutput;//
|
||||
maxiEnv myEnvelope;
|
||||
maxiFilter myFilter;
|
||||
|
||||
void setup() {//some inits
|
||||
// setup logging
|
||||
Serial.begin(115200);
|
||||
AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info);
|
||||
|
||||
// setup audio output
|
||||
auto cfg = out.defaultConfig(TX_MODE);
|
||||
out.begin(cfg);
|
||||
maximilian.begin(cfg);
|
||||
|
||||
//Timing is in ms
|
||||
myEnvelope.setAttack(0);
|
||||
myEnvelope.setDecay(1); // Needs to be at least 1
|
||||
myEnvelope.setSustain(1);
|
||||
myEnvelope.setRelease(1000);
|
||||
|
||||
}
|
||||
|
||||
void play(float *output) {
|
||||
|
||||
myCurrentVolume=myEnvelope.adsr(1.,myEnvelope.trigger);
|
||||
|
||||
CurrentCount=myCounter.phasorBetween(1.0, 1.0, 9.0);//phasor can take three arguments; frequency, start value and end value.
|
||||
|
||||
// You'll notice that these 'if' statements don't require curly braces "{}".
|
||||
// This is because there is only one outcome if the statement is true.
|
||||
|
||||
if (CurrentCount==1) myEnvelope.trigger=1; //trigger the envelope
|
||||
|
||||
else myEnvelope.trigger=0;//release the envelope to make it fade out only if it's been triggered
|
||||
|
||||
myOscOutput=mySwitchableOsc.sawn(100);
|
||||
|
||||
// Below, the oscilator signals are being passed through a low pass filter.
|
||||
// The middle input is the filter cutoff. It is being controlled by the envelope.
|
||||
// Notice that the envelope is being amplified so that it scales between 0 and 1000.
|
||||
// The last input is the resonance.
|
||||
myFilteredOutput=myFilter.lores(myOscOutput,myCurrentVolume*1000,10);
|
||||
|
||||
output[0]=myFilteredOutput;//left speaker
|
||||
output[1]=output[0];
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Arduino loop
|
||||
void loop() {
|
||||
maximilian.copy();
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#include "AudioTools.h"
|
||||
#include "AudioTools/AudioLibs/MaximilianDSP.h"
|
||||
|
||||
// Define Arduino output
|
||||
I2SStream out;
|
||||
Maximilian maximilian(out);
|
||||
|
||||
// Maximilian
|
||||
maxiOsc myOsc,myAutoPanner;//
|
||||
vector<float> myStereoOutput(2,0);
|
||||
maxiMix myOutputs;//this is the stereo mixer channel.
|
||||
|
||||
void setup() {//some inits
|
||||
// setup logging
|
||||
Serial.begin(115200);
|
||||
AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info);
|
||||
|
||||
// setup audio output
|
||||
auto cfg = out.defaultConfig(TX_MODE);
|
||||
out.begin(cfg);
|
||||
maximilian.begin(cfg);
|
||||
}
|
||||
|
||||
void play(float *output) {
|
||||
myOutputs.stereo(myOsc.noise(),myStereoOutput,(myAutoPanner.sinewave(1)+1)/2);//Stereo, Quad or 8 Channel. Specify the input to be mixed, the output[numberofchannels], and the pan (0-1,equal power).
|
||||
output[0]=myStereoOutput[0];//When working with mixing, you need to specify the outputs explicitly
|
||||
output[1]=myStereoOutput[1];//
|
||||
}
|
||||
|
||||
// Arduino loop
|
||||
void loop() {
|
||||
maximilian.copy();
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
#include "AudioTools.h"
|
||||
#include "AudioTools/AudioLibs/MaximilianDSP.h"
|
||||
#include "AudioTools/AudioLibs/AudioBoardStream.h"
|
||||
#include "maximilian.h"
|
||||
#include <FS.h>
|
||||
#include <SD_MMC.h>
|
||||
|
||||
|
||||
// Define Arduino output
|
||||
AudioBoardStream out(AudioKitEs8388V1);
|
||||
Maximilian maximilian(out);
|
||||
|
||||
// MAXIMILIAN
|
||||
maxiSample beats; //We give our sample a name. It's called beats this time. We could have loads of them, but they have to have different names.
|
||||
|
||||
void setup() {//some inits
|
||||
// setup logging
|
||||
Serial.begin(115200);
|
||||
AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info);
|
||||
|
||||
// setup audio output
|
||||
auto cfg = out.defaultConfig(TX_MODE);
|
||||
out.begin(cfg);
|
||||
maximilian.begin(cfg);
|
||||
|
||||
// setup SD to allow file operations
|
||||
if(!SD_MMC.begin()){
|
||||
Serial.println("Card Mount Failed");
|
||||
return;
|
||||
}
|
||||
|
||||
//load in your samples: beat2.wav is too big - but snare.wav will work
|
||||
//beats.load("/sdcard/Maximilian/beat2.wav");
|
||||
beats.load("/sdcard/Maximilian/snare.wav");
|
||||
//get info on samples if you like.
|
||||
Serial.println(beats.getSummary().c_str());
|
||||
|
||||
}
|
||||
|
||||
//this is where the magic happens. Very slow magic.
|
||||
void play(float *output) {
|
||||
//output[0]=beats.play();//just play the file. Looping is default for all play functions.
|
||||
output[0]=beats.playAtSpeed(0.68);//play the file with a speed setting. 1. is normal speed.
|
||||
//output[0]=beats.playAtSpeedBetweenPoints(0.5,0,13300);//linear interpolationplay with a frequency input, start point and end point. Useful for syncing.
|
||||
//output[0]=beats.play4(0.5,0,13300);//cubic interpolation play with a frequency input, start point and end point. Useful for syncing.
|
||||
|
||||
output[1]=output[0];
|
||||
}
|
||||
|
||||
// Arduino loop
|
||||
void loop() {
|
||||
maximilian.copy();
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
//Using BPF equation from http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
|
||||
//Example contributed by Rebecca Fiebrink
|
||||
|
||||
#include "AudioTools.h"
|
||||
#include "AudioTools/AudioLibs/MaximilianDSP.h"
|
||||
|
||||
// Define Arduino output
|
||||
I2SStream out;
|
||||
Maximilian maximilian(out);
|
||||
|
||||
// Maximilian
|
||||
float xs[3], ys[3];
|
||||
float a0, a1, a2, b0, b1, b2;
|
||||
float f0 = 400; //THE FREQUENCY
|
||||
float Q = 1.0;
|
||||
|
||||
maxiOsc mySwitchableOsc;
|
||||
|
||||
void setup() {//some inits
|
||||
// setup logging
|
||||
Serial.begin(115200);
|
||||
AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info);
|
||||
|
||||
// setup audio output
|
||||
auto cfg = out.defaultConfig(TX_MODE);
|
||||
out.begin(cfg);
|
||||
maximilian.begin(cfg);
|
||||
|
||||
// setup maximilian
|
||||
float w0 = 2*PI*f0/44100;
|
||||
float alpha = sin(w0)/(2*Q);
|
||||
//Band-pass reson:
|
||||
// b0 = alpha;
|
||||
// b1 = 0;
|
||||
// b2 = -1 * alpha;
|
||||
// a0 = 1 + alpha;
|
||||
// a1 = -2*cos(w0);
|
||||
// a2 = 1 - alpha;
|
||||
|
||||
//Notch:
|
||||
b0 = 1;
|
||||
b1 = -2*cos(w0);
|
||||
b2 = 1;
|
||||
a0 = 1 + alpha;
|
||||
a1 = -2*cos(w0);
|
||||
a2 = 1 - alpha;
|
||||
|
||||
//LPF:
|
||||
// b0 = (1 - cos(w0))/2;
|
||||
// b1 = 1 - cos(w0);
|
||||
// b2 = (1 - cos(w0))/2;
|
||||
// a0 = 1 + alpha;
|
||||
// a1 = -2*cos(w0);
|
||||
// a2 = 1 - alpha;
|
||||
}
|
||||
|
||||
void play(float *output) {
|
||||
xs[0] = mySwitchableOsc.sawn(400);
|
||||
ys[0] = (b0/a0)*xs[0] + (b1/a0)*xs[1] + (b2/a0)*xs[2]
|
||||
- (a1/a0)*ys[1] - (a2/a0)*ys[2];
|
||||
|
||||
*output = ys[0];
|
||||
|
||||
ys[2] = ys[1]; ys[1] = ys[0];
|
||||
xs[2] = xs[1]; xs[1] = xs[0];
|
||||
}
|
||||
|
||||
// Arduino loop
|
||||
void loop() {
|
||||
maximilian.copy();
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
//This shows how to use maximilian to build a monophonic synth
|
||||
|
||||
#include "AudioTools.h"
|
||||
#include "AudioTools/AudioLibs/MaximilianDSP.h"
|
||||
|
||||
// Define Arduino output
|
||||
I2SStream out;
|
||||
Maximilian maximilian(out);
|
||||
|
||||
//These are the synthesiser bits
|
||||
maxiOsc VCO1,VCO2,LFO1,LFO2;
|
||||
maxiFilter VCF;
|
||||
maxiEnv V_ADSR;
|
||||
|
||||
//This is a bunch of control signals so that we can hear something
|
||||
maxiOsc timer;//this is the metronome
|
||||
int currentCount,lastCount;//these values are used to check if we have a new beat this sample
|
||||
|
||||
//and these are some variables we can use to pass stuff around
|
||||
|
||||
double VCO1out,VCO2out,LFO1out,LFO2out,VCFout,ADSRout;
|
||||
|
||||
|
||||
void setup() {//some inits
|
||||
// setup logging
|
||||
Serial.begin(115200);
|
||||
AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info);
|
||||
|
||||
// setup audio output
|
||||
auto cfg = out.defaultConfig(TX_MODE);
|
||||
out.begin(cfg);
|
||||
maximilian.begin(cfg);
|
||||
|
||||
// setup maximilian
|
||||
|
||||
V_ADSR.setAttack(1000);
|
||||
V_ADSR.setDecay(1);
|
||||
V_ADSR.setSustain(1);
|
||||
V_ADSR.setRelease(1000);
|
||||
}
|
||||
|
||||
void play(float *output) {
|
||||
|
||||
//so this first bit is just a basic metronome so we can hear what we're doing.
|
||||
|
||||
currentCount=(int)timer.phasor(0.5);//this sets up a metronome that ticks every 2 seconds
|
||||
|
||||
|
||||
if (lastCount!=currentCount) {//if we have a new timer int this sample, play the sound
|
||||
V_ADSR.trigger=1;
|
||||
|
||||
cout << "tick\n";//the clock ticks
|
||||
|
||||
lastCount=0;//set lastCount to 0
|
||||
}
|
||||
|
||||
//and this is where we build the synth
|
||||
|
||||
ADSRout=V_ADSR.adsr(1.0,V_ADSR.trigger);
|
||||
|
||||
LFO1out=LFO1.sinebuf(0.2);//this lfo is a sinewave at 0.2 hz
|
||||
|
||||
VCO1out=VCO1.pulse(55,0.6);//here's VCO1. it's a pulse wave at 55 hz, with a pulse width of 0.6
|
||||
VCO2out=VCO2.pulse(110+LFO1out,0.2);//here's VCO2. it's a pulse wave at 110hz with LFO modulation on the frequency, and width of 0.2
|
||||
|
||||
|
||||
VCFout=VCF.lores((VCO1out+VCO2out)*0.5, ADSRout*10000, 10);//now we stick the VCO's into the VCF, using the V_ADSR as the filter cutoff
|
||||
|
||||
double finalSound=VCFout*ADSRout;//finally we add the V_ADSR as an amplitude modulator
|
||||
V_ADSR.trigger=0;
|
||||
output[0]=finalSound;
|
||||
output[1]=finalSound;
|
||||
}
|
||||
|
||||
// Arduino loop
|
||||
void loop() {
|
||||
maximilian.copy();
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
|
||||
//This shows how to use maximilian to build a polyphonic synth.
|
||||
|
||||
#include "AudioTools.h"
|
||||
#include "AudioTools/AudioLibs/MaximilianDSP.h"
|
||||
|
||||
// Define Arduino output
|
||||
I2SStream out;
|
||||
Maximilian maximilian(out);
|
||||
|
||||
//These are the synthesiser bits
|
||||
maxiOsc VCO1[6],VCO2[6],LFO1[6],LFO2[6];
|
||||
maxiFilter VCF[6];
|
||||
maxiEnv V_ADSR[6];
|
||||
|
||||
//This is a bunch of control signals so that we can hear something
|
||||
maxiOsc timer;//this is the metronome
|
||||
int currentCount,lastCount,voice=0;//these values are used to check if we have a new beat this sample
|
||||
|
||||
//and these are some variables we can use to pass stuff around
|
||||
double VCO1out[6],VCO2out[6],LFO1out[6],LFO2out[6],VCFout[6],ADSRout[6],mix,pitch[6];
|
||||
|
||||
|
||||
void setup() {//some inits
|
||||
// setup logging
|
||||
Serial.begin(115200);
|
||||
AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info);
|
||||
|
||||
// setup audio output
|
||||
auto cfg = out.defaultConfig(TX_MODE);
|
||||
out.begin(cfg);
|
||||
maximilian.begin(cfg);
|
||||
|
||||
// setup maximilian
|
||||
for (int i=0;i<6;i++) {
|
||||
V_ADSR[i].setAttack(0);
|
||||
V_ADSR[i].setDecay(200);
|
||||
V_ADSR[i].setSustain(0.2);
|
||||
V_ADSR[i].setRelease(2000);
|
||||
}
|
||||
}
|
||||
|
||||
void play(float *output) {
|
||||
|
||||
mix=0;//we're adding up the samples each update and it makes sense to clear them each time first.
|
||||
|
||||
//so this first bit is just a basic metronome so we can hear what we're doing.
|
||||
|
||||
currentCount=(int)timer.phasor(8);//this sets up a metronome that ticks 8 times a second
|
||||
|
||||
if (lastCount!=currentCount) {//if we have a new timer int this sample, play the sound
|
||||
|
||||
if (voice==6) {
|
||||
voice=0;
|
||||
}
|
||||
|
||||
V_ADSR[voice].trigger=1;//trigger the envelope from the start
|
||||
pitch[voice]=voice+1;
|
||||
voice++;
|
||||
|
||||
}
|
||||
|
||||
//and this is where we build the synth
|
||||
|
||||
for (int i=0; i<6; i++) {
|
||||
|
||||
|
||||
ADSRout[i]=V_ADSR[i].adsr(1.,V_ADSR[i].trigger);//our V_ADSR env is passed a constant signal of 1 to generate the transient.
|
||||
|
||||
LFO1out[i]=LFO1[i].sinebuf(0.2);//this lfo is a sinewave at 0.2 hz
|
||||
|
||||
VCO1out[i]=VCO1[i].pulse(55*pitch[i],0.6);//here's VCO1. it's a pulse wave at 55 hz, with a pulse width of 0.6
|
||||
VCO2out[i]=VCO2[i].pulse((110*pitch[i])+LFO1out[i],0.2);//here's VCO2. it's a pulse wave at 110hz with LFO modulation on the frequency, and width of 0.2
|
||||
|
||||
|
||||
VCFout[i]=VCF[i].lores((VCO1out[i]+VCO2out[i])*0.5, 250+((pitch[i]+LFO1out[i])*1000), 10);//now we stick the VCO's into the VCF, using the V_ADSR as the filter cutoff
|
||||
|
||||
mix+=VCFout[i]*ADSRout[i]/6;//finally we add the V_ADSR as an amplitude modulator
|
||||
|
||||
|
||||
}
|
||||
|
||||
output[0]=mix*0.5;//left channel
|
||||
output[1]=mix*0.5;//right channel
|
||||
|
||||
|
||||
// This just sends note-off messages.
|
||||
for (int i=0; i<6; i++) {
|
||||
V_ADSR[i].trigger=0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Arduino loop
|
||||
void loop() {
|
||||
maximilian.copy();
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
|
||||
// WARNNG: This sketch is too big to fit on an ESP32 w/o PSRAM !
|
||||
|
||||
//Bizarelly, this sounds a little bit like Kraftwerk's 'Metropolis', although it isn't. Funny that.
|
||||
|
||||
#include "AudioTools.h"
|
||||
#include "AudioTools/AudioLibs/MaximilianDSP.h"
|
||||
|
||||
// Define Arduino output
|
||||
I2SStream out;
|
||||
Maximilian maximilian(out);
|
||||
|
||||
// Maximilian
|
||||
maxiOsc sound,bass,timer,mod,lead,lead2,leadmod;//here are the synth bits
|
||||
maxiEnv envelope, leadenvelope;//some envelopes
|
||||
maxiFilter filter, filter2;//some filters
|
||||
maxiDelayline delay_value;//a delay_value
|
||||
convert mtof;//a method for converting midi notes to frequency
|
||||
double bassout,leadout, delayout;//some variables to hold the data and pass it around
|
||||
int trigger, trigger2, newnote;//some control variables
|
||||
int currentCount,lastCount,playHead=0, currentChord=0;//some other control variables
|
||||
int pitch[8]={57,57,59,60};//the bassline for the arpeggio
|
||||
int chord[8]={0,0,7,2,5,5,0,0};//the root chords for the arpeggio
|
||||
float currentPitch,leadPitch;//the final pitch variables
|
||||
|
||||
//here's the lead line trigger array, followed by the pitches
|
||||
int leadLineTrigger[256]={1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
|
||||
int leadLinePitch[15]={69,67,65,64,67,66,64,62,65,64,62,57,55,60,57};
|
||||
|
||||
|
||||
|
||||
void setup() {//some inits
|
||||
// setup logging
|
||||
Serial.begin(115200);
|
||||
AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info);
|
||||
|
||||
// setup audio output
|
||||
auto cfg = out.defaultConfig(TX_MODE);
|
||||
out.begin(cfg);
|
||||
maximilian.begin(cfg);
|
||||
|
||||
}
|
||||
|
||||
void play(float *output) {//this is where the magic happens. Very slow magic.
|
||||
|
||||
currentCount=(int)timer.phasor(9);//this sets up a metronome that ticks every so often
|
||||
|
||||
if (lastCount!=currentCount) {//if we have a new timer int this sample, play the sound
|
||||
trigger=1;//play the arpeggiator line
|
||||
trigger2=leadLineTrigger[playHead%256];//play the lead line
|
||||
if (trigger2==1) {//if we are going to play a note
|
||||
leadPitch=mtof.mtof(leadLinePitch[newnote]);//get the next pitch val
|
||||
newnote++;//and iterate
|
||||
if (newnote>14) {
|
||||
newnote=0;//make sure we don't go over the edge of the array
|
||||
}
|
||||
}
|
||||
currentPitch=mtof.mtof(pitch[(playHead%4)]+chord[currentChord%8]);//write the frequency val into currentPitch
|
||||
playHead++;//iterate the playhead
|
||||
if (playHead%32==0) {//wrap every 4 bars
|
||||
currentChord++;//change the chord
|
||||
}
|
||||
//cout << "tick\n";//the clock ticks
|
||||
lastCount=0;//set lastCount to 0
|
||||
}
|
||||
|
||||
bassout=filter2.lores(envelope.adsr(bass.saw(currentPitch*0.5)+sound.pulse(currentPitch*0.5,mod.phasor(1)),1,0.9995, 0.25, 0.9995, 1, trigger),9250,2);//new, simple ADSR.
|
||||
leadout=filter.lores(leadenvelope.ar(lead2.saw(leadPitch*4)+lead.pulse(leadPitch+(leadmod.sinebuf(1.9)*1.5), 0.6), 0.00005, 0.999975, 50000, trigger2),5900,10);//leadline
|
||||
|
||||
delayout=(leadout+(delay_value.dl(leadout, 14000, 0.8)*0.5))/2;//add some delay_value
|
||||
|
||||
if(trigger!=0)trigger=0;//set the trigger to off if you want it to trigger immediately next time.
|
||||
|
||||
|
||||
output[0]=(bassout)/2;//sum output
|
||||
output[1]=(bassout)/2;
|
||||
|
||||
}
|
||||
|
||||
// Arduino loop
|
||||
void loop() {
|
||||
maximilian.copy();
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
|
||||
#include "AudioTools.h"
|
||||
#include "AudioTools/AudioLibs/MaximilianDSP.h"
|
||||
#include <FS.h>
|
||||
#include <SD_MMC.h>
|
||||
|
||||
|
||||
// Arduino output
|
||||
I2SStream out;
|
||||
Maximilian maximilian(out);
|
||||
|
||||
// Maximilian
|
||||
maxiSample beats; //We give our sample a name. It's called beats this time. We could have loads of them, but they have to have different names.
|
||||
maxiDyn compressor; //this is a compressor
|
||||
float fout;
|
||||
|
||||
void setup() {//some inits
|
||||
// setup logging
|
||||
Serial.begin(115200);
|
||||
AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info);
|
||||
|
||||
// setup audio output
|
||||
auto cfg = out.defaultConfig(TX_MODE);
|
||||
out.begin(cfg);
|
||||
maximilian.begin(cfg);
|
||||
|
||||
// setup SD to allow file operations
|
||||
if(!SD_MMC.begin()){
|
||||
Serial.println("Card Mount Failed");
|
||||
return;
|
||||
}
|
||||
|
||||
//load in your samples. Provide the full path to a wav file: beat2 is too big
|
||||
//beats.load("/sdcard/Maximilian/beat2.wav");
|
||||
beats.load("/sdcard/Maximilian/snare.wav");
|
||||
Serial.println(beats.getSummary().c_str());//get info on samples if you like.
|
||||
|
||||
compressor.setAttack(100);
|
||||
compressor.setRelease(300);
|
||||
compressor.setThreshold(0.25);
|
||||
compressor.setRatio(5);
|
||||
|
||||
//you can set these any time you like.
|
||||
|
||||
}
|
||||
|
||||
void play(float *output) {//this is where the magic happens. Very slow magic.
|
||||
//here, we're just compressing the file in real-time
|
||||
//arguments are input,ratio,threshold,attack,release
|
||||
fout=compressor.compress(beats.play());
|
||||
|
||||
output[0]=fout;
|
||||
output[1]=fout;
|
||||
}
|
||||
|
||||
// Arduino loop
|
||||
void loop() {
|
||||
maximilian.copy();
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
#include "AudioTools.h"
|
||||
#include "AudioTools/AudioLibs/MaximilianDSP.h"
|
||||
#include "audio/kick.h"
|
||||
#include "audio/snare.h"
|
||||
|
||||
// Arduino output
|
||||
I2SStream out;
|
||||
Maximilian maximilian(out);
|
||||
|
||||
// Maximilian def
|
||||
maxiSample kick,snare; //we've got two sampleplayers
|
||||
maxiOsc timer; //and a timer
|
||||
|
||||
int currentCount,lastCount,playHead,hit[16]={1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1}; //This is the sequence for the kick
|
||||
int snarehit[16]={0,0,0,0,1,0,0,0,0,0,0,0,1,1,0,0};//This is the sequence for the snare
|
||||
|
||||
int kicktrigger,snaretrigger;
|
||||
|
||||
double sampleOut;
|
||||
|
||||
void setup() {//some inits
|
||||
// setup logging
|
||||
Serial.begin(115200);
|
||||
AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info);
|
||||
|
||||
// setup audio output
|
||||
auto cfg = out.defaultConfig(TX_MODE);
|
||||
out.begin(cfg);
|
||||
maximilian.begin(cfg);
|
||||
|
||||
//YOU HAVE TO PROVIDE THE SAMPLES: we assign data in progmem
|
||||
//kick.load("/sdcard/Maximilian/kick.wav");//load in your samples. Provide the full path to a wav file.
|
||||
//snare.load("/sdcard/Maximilian/snare.wav");
|
||||
kick.setSample(kick_vector);
|
||||
snare.setSample(snare_vector);
|
||||
|
||||
|
||||
printf("Summary:\n%s", kick.getSummary());//get info on samples if you like.
|
||||
//beats.getLength();
|
||||
}
|
||||
|
||||
void play(float *output) {//this is where the magic happens. Very slow magic.
|
||||
|
||||
currentCount=(int)timer.phasor(8);//this sets up a metronome that ticks 8 times a second
|
||||
|
||||
if (lastCount!=currentCount) {//if we have a new timer int this sample, play the sound
|
||||
|
||||
kicktrigger=hit[playHead%16];//get the value out of the array for the kick
|
||||
snaretrigger=snarehit[playHead%16];//same for the snare
|
||||
playHead++;//iterate the playhead
|
||||
lastCount=0;//reset the metrotest
|
||||
}
|
||||
|
||||
if (kicktrigger==1) {//if the sequence has a 1 in it
|
||||
kick.trigger();//reset the playback position of the sample to 0 (the beginning)
|
||||
}
|
||||
|
||||
if (snaretrigger==1) {
|
||||
snare.trigger();//likewise for the snare
|
||||
}
|
||||
|
||||
sampleOut=kick.playOnce()+snare.playOnce();//just play the file. No looping.
|
||||
|
||||
output[0]=sampleOut;//left channel
|
||||
output[1]=sampleOut;//right channel
|
||||
|
||||
kicktrigger = 0;//set trigger to 0 at the end of each sample to guarantee retriggering.
|
||||
snaretrigger = 0;
|
||||
}
|
||||
|
||||
// Arduino loop
|
||||
void loop() {
|
||||
maximilian.copy();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
|
||||
//this tutorial explains how to use the maxiEnv
|
||||
|
||||
#include "AudioTools.h"
|
||||
#include "AudioTools/AudioLibs/MaximilianDSP.h"
|
||||
#include "audio/cello_open_string_bowed.h"
|
||||
|
||||
// Arduino output
|
||||
I2SStream out;
|
||||
Maximilian maximilian(out);
|
||||
|
||||
// Maximilian
|
||||
maxiSample sound1;
|
||||
maxiOsc timer,snarePhase; //and a timer
|
||||
maxiEnv envelope;//this is going to be an envelope
|
||||
int currentCount,lastCount,playHead,
|
||||
sequence[16]={1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0}; //This is the sequence for the kick
|
||||
int sampleTrigger;
|
||||
float sampleOut;
|
||||
|
||||
void setup() {//some inits
|
||||
// setup logging
|
||||
Serial.begin(115200);
|
||||
AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info);
|
||||
|
||||
// setup audio output
|
||||
auto cfg = out.defaultConfig(TX_MODE);
|
||||
out.begin(cfg);
|
||||
maximilian.begin(cfg);
|
||||
|
||||
//YOU HAVE TO PROVIDE THE SAMPLES....
|
||||
//sound1.load("/Users/mickgrierson/Documents/audio/68373__juskiddink__Cello_open_string_bowed.wav");//load in your samples. Provide the full path to a wav file.
|
||||
sound1.setSample(cello_open_string_bowed_vector);
|
||||
|
||||
Serial.println(sound1.getSummary().c_str());//get info on samples if you like.
|
||||
}
|
||||
|
||||
void play(float *output) {//this is where the magic happens. Very slow magic.
|
||||
|
||||
currentCount=(int)timer.phasor(8);//this sets up a metronome that ticks 8 times a second
|
||||
|
||||
if (lastCount!=currentCount) {//if we have a new timer int this sample, play the sound
|
||||
sampleTrigger=sequence[playHead%16];
|
||||
playHead++;//iterate the playhead
|
||||
lastCount=0;//reset the metrotest
|
||||
}
|
||||
|
||||
//the envelope we're using here is an AR envelope.
|
||||
//It has an input (which in this case is a sound)
|
||||
//It has an attack coefficient, a hold val (in samples)
|
||||
//and a release coefficient. Finally, it has a trigger input.
|
||||
//If you stick a 1 in the trigger input, it retriggers the envelope
|
||||
sampleOut=envelope.ar(sound1.playAtSpeed(1.0), 0.1, 0.9999, 1, sampleTrigger); //
|
||||
|
||||
output[0]=sampleOut;//left channel
|
||||
output[1]=sampleOut;//right channel
|
||||
|
||||
sampleTrigger = 0;//set trigger to 0 at the end of each sample to guarantee retriggering.
|
||||
|
||||
}
|
||||
|
||||
// Arduino loop
|
||||
void loop() {
|
||||
maximilian.copy();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
|
||||
// WARNNG: This sketch is too big to fit on an ESP32 w/o PSRAM !
|
||||
|
||||
|
||||
#include "AudioTools.h"
|
||||
#include "AudioTools/AudioLibs/MaximilianDSP.h"
|
||||
#include "libs/maxim.h"
|
||||
|
||||
// Define Arduino output
|
||||
I2SStream out;
|
||||
Maximilian maximilian(out);
|
||||
|
||||
// Maximilian
|
||||
maxiOsc mySine, myPhasor; // This is the oscillator we will use to generate the test tone
|
||||
maxiFFT myFFT;
|
||||
|
||||
void setup() {
|
||||
// setup logging
|
||||
Serial.begin(115200);
|
||||
AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info);
|
||||
|
||||
// setup audio output
|
||||
auto cfg = out.defaultConfig(TX_MODE);
|
||||
out.begin(cfg);
|
||||
maximilian.begin(cfg);
|
||||
|
||||
// setup maximilian
|
||||
myFFT.setup(512, 512, 256);
|
||||
|
||||
}
|
||||
|
||||
void play(float *output) {
|
||||
float myOut=mySine.sinewave(myPhasor.phasorBetween(0.2,100,5000));
|
||||
//output[0] is the left output. output[1] is the right output
|
||||
|
||||
if (myFFT.process(myOut)) {
|
||||
//if you want you can mess with FFT frame values in here
|
||||
|
||||
}
|
||||
|
||||
output[0]=myOut;//simple as that!
|
||||
output[1]=output[0];
|
||||
}
|
||||
|
||||
|
||||
// Arduino loop
|
||||
void loop() {
|
||||
maximilian.copy();
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
# Maximilian Examples
|
||||
|
||||
see [Maximilian](https://github.com/pschatzmann/Maximilian)
|
||||
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Mozzi Input example: we get the input from a generator and
|
||||
* output the audio via i2s again. In this example we use the
|
||||
* default value range of 0-1023 for the getAudioInput() method.
|
||||
* However usually it is easier to define the desired range in
|
||||
* the Mozzi configuration object: e.g. cfg.input_range_from = -244;
|
||||
* cfg.input_range_to = 243;
|
||||
*/
|
||||
#include "AudioTools.h"
|
||||
#include "AudioTools/AudioLibs/AudioBoardStream.h"
|
||||
#include "AudioTools/AudioLibs/MozziStream.h"
|
||||
|
||||
const int sample_rate = 16000;
|
||||
AudioInfo info(sample_rate, 1, 16);
|
||||
AudioBoardStream i2s(AudioKitEs8388V1); // final output of decoded stream
|
||||
MozziStream mozzi;
|
||||
SineWaveGenerator<int16_t> sineWave; // subclass of SoundGenerator with max amplitude of 32000
|
||||
GeneratedSoundStream<int16_t> sound(sineWave); // Stream generated from sine wave
|
||||
|
||||
StreamCopy copier(i2s, mozzi);
|
||||
// use: Oscil <table_size, update_rate> oscilName (wavetable), look in .h file
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning);
|
||||
|
||||
// setup mozzi
|
||||
auto cfg = mozzi.defaultConfig();
|
||||
cfg.control_rate = CONTROL_RATE;
|
||||
cfg.copyFrom(info);
|
||||
mozzi.begin(cfg);
|
||||
|
||||
// setup data source for mozzi
|
||||
mozzi.setInput(sound);
|
||||
sineWave.begin(info, N_B4);
|
||||
|
||||
// setup output
|
||||
auto out_cfg = i2s.defaultConfig(RXTX_MODE);
|
||||
out_cfg.copyFrom(info);
|
||||
i2s.begin(out_cfg);
|
||||
i2s.setVolume(1.0);
|
||||
}
|
||||
|
||||
void updateControl() {}
|
||||
|
||||
AudioOutputMozzi updateAudio() {
|
||||
int asig = mozzi.getAudioInput(); // range 0-1023
|
||||
asig = asig - 512; // now range is -512 to 511
|
||||
// output range in STANDARD mode is -244 to 243,
|
||||
// so you might need to adjust your signal to suit
|
||||
return asig;
|
||||
}
|
||||
|
||||
void loop() { copier.copy(); }
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Demo how to use the Mozzi API to provide a stream of int16_t data.
|
||||
* Inspired by https://sensorium.github.io/Mozzi/examples/#01.Basics
|
||||
* The result is published to a bluetooth speaker. We use the more efficient
|
||||
* A2DP base API with a callback
|
||||
*/
|
||||
#include "AudioTools.h"
|
||||
#include "AudioTools/Communication/A2DPStream.h"
|
||||
#include "AudioTools/AudioLibs/MozziStream.h"
|
||||
#include <Oscil.h> // oscillator template
|
||||
#include <tables/sin2048_int8.h> // sine table for oscillator
|
||||
|
||||
const int sample_rate = 44100;
|
||||
AudioInfo info(sample_rate, 2, 16); // bluetooth requires 44100, stereo, 16 bits
|
||||
BluetoothA2DPSource a2dp_source;
|
||||
MozziStream mozzi; // audio source
|
||||
const int16_t BYTES_PER_FRAME = 4;
|
||||
// use: Oscil <table_size, update_rate> oscilName (wavetable), look in .h file
|
||||
// of table #included above
|
||||
Oscil<SIN2048_NUM_CELLS, sample_rate> aSin(SIN2048_DATA);
|
||||
// control variable, use the smallest data size you can for anything used in
|
||||
// audio
|
||||
byte gain = 255;
|
||||
|
||||
// callback used by A2DP to provide the sound data - usually len is 128 2 channel int16 frames
|
||||
int32_t get_sound_data(uint8_t* data, int32_t size) {
|
||||
int32_t result = mozzi.readBytes(data, size);
|
||||
//LOGI("get_sound_data %d->%d",size, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Arduino Setup
|
||||
void setup(void) {
|
||||
Serial.begin(115200);
|
||||
AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info);
|
||||
|
||||
// setup mozzi
|
||||
auto cfg = mozzi.defaultConfig();
|
||||
cfg.control_rate = CONTROL_RATE;
|
||||
cfg.copyFrom(info);
|
||||
mozzi.begin(cfg);
|
||||
|
||||
aSin.setFreq(3320); // set the frequency
|
||||
|
||||
// start the bluetooth
|
||||
Serial.println("starting A2DP...");
|
||||
a2dp_source.set_data_callback(get_sound_data);
|
||||
a2dp_source.start("LEXON MINO L");
|
||||
//a2dp_source.set_volume(100);
|
||||
}
|
||||
|
||||
void updateControl() {
|
||||
// as byte, this will automatically roll around to 255 when it passes 0
|
||||
gain = gain - 3;
|
||||
}
|
||||
|
||||
AudioOutputMozzi updateAudio() {
|
||||
// shift back to STANDARD audio range, like /256 but faster
|
||||
return (aSin.next() * gain) >> 8;
|
||||
}
|
||||
|
||||
// Arduino loop - repeated processing
|
||||
void loop() {
|
||||
delay(1000);
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Demo how to use the Mozzi API to provide a stream of int16_t data.
|
||||
* Inspired by https://sensorium.github.io/Mozzi/examples/#01.Basics
|
||||
*/
|
||||
#include "AudioTools.h"
|
||||
#include "AudioTools/AudioLibs/AudioBoardStream.h"
|
||||
#include "AudioTools/AudioLibs/MozziStream.h"
|
||||
#include <Oscil.h> // oscillator template
|
||||
#include <tables/sin2048_int8.h> // sine table for oscillator
|
||||
|
||||
const int sample_rate = 16000;
|
||||
AudioInfo info(sample_rate, 1, 16);
|
||||
AudioBoardStream i2s(AudioKitEs8388V1); // audio sink
|
||||
MozziStream mozzi; // audio source
|
||||
StreamCopy copier(i2s, mozzi); // copy source to sink
|
||||
// use: Oscil <table_size, update_rate> oscilName (wavetable), look in .h file
|
||||
// of table #included above
|
||||
Oscil<SIN2048_NUM_CELLS, sample_rate> aSin(SIN2048_DATA);
|
||||
// control variable, use the smallest data size you can for anything used in
|
||||
// audio
|
||||
byte gain = 255;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info);
|
||||
|
||||
// setup mozzi
|
||||
auto cfg = mozzi.defaultConfig();
|
||||
cfg.control_rate = CONTROL_RATE;
|
||||
cfg.copyFrom(info);
|
||||
mozzi.begin(cfg);
|
||||
|
||||
// setup output
|
||||
auto out_cfg = i2s.defaultConfig();
|
||||
out_cfg.copyFrom(info);
|
||||
i2s.begin(out_cfg);
|
||||
|
||||
// setup mozzi sine
|
||||
aSin.setFreq(3320); // set the frequency
|
||||
}
|
||||
|
||||
void loop() { copier.copy(); }
|
||||
|
||||
void updateControl() {
|
||||
// as byte, this will automatically roll around to 255 when it passes 0
|
||||
gain = gain - 3;
|
||||
}
|
||||
|
||||
AudioOutputMozzi updateAudio() {
|
||||
return (aSin.next() * gain) >>
|
||||
8; // shift back to STANDARD audio range, like /256 but faster
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
Pure Data (or just "Pd") is an open source visual programming language for multimedia. Further information can be found in the [Wiki](https://github.com/pschatzmann/arduino-audio-tools/wiki/Pure-Data)
|
||||
@@ -0,0 +1,264 @@
|
||||
/**
|
||||
* Copyright (c) 2014-2018 Enzien Audio Ltd.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "HeavyContext.hpp"
|
||||
#include "HvTable.h"
|
||||
|
||||
void defaultSendHook(HeavyContextInterface *context,
|
||||
const char *sendName, hv_uint32_t sendHash, const HvMessage *msg) {
|
||||
HeavyContext *thisContext = reinterpret_cast<HeavyContext *>(context);
|
||||
const hv_uint32_t numBytes = sizeof(ReceiverMessagePair) + msg_getSize(msg) - sizeof(HvMessage);
|
||||
ReceiverMessagePair *p = reinterpret_cast<ReceiverMessagePair *>(hLp_getWriteBuffer(&thisContext->outQueue, numBytes));
|
||||
if (p != nullptr) {
|
||||
p->receiverHash = sendHash;
|
||||
msg_copyToBuffer(msg, (char *) &p->msg, msg_getSize(msg));
|
||||
hLp_produce(&thisContext->outQueue, numBytes);
|
||||
} else {
|
||||
hv_assert(false &&
|
||||
"::defaultSendHook - The out message queue is full and cannot accept more messages until they "
|
||||
"have been processed. Try increasing the outQueueKb size in the new_with_options() constructor.");
|
||||
}
|
||||
}
|
||||
|
||||
HeavyContext::HeavyContext(double sampleRate, int poolKb, int inQueueKb, int outQueueKb) :
|
||||
sampleRate(sampleRate) {
|
||||
|
||||
hv_assert(sampleRate > 0.0); // sample rate must be positive
|
||||
hv_assert(poolKb > 0);
|
||||
hv_assert(inQueueKb > 0);
|
||||
hv_assert(outQueueKb >= 0);
|
||||
|
||||
blockStartTimestamp = 0;
|
||||
printHook = nullptr;
|
||||
userData = nullptr;
|
||||
|
||||
// if outQueueKb is positive, then the outQueue is allocated and the default sendhook is set.
|
||||
// Otherwise outQueue and the sendhook are set to NULL.
|
||||
sendHook = (outQueueKb > 0) ? &defaultSendHook : nullptr;
|
||||
|
||||
HV_SPINLOCK_RELEASE(inQueueLock);
|
||||
HV_SPINLOCK_RELEASE(outQueueLock);
|
||||
|
||||
numBytes = sizeof(HeavyContext);
|
||||
|
||||
numBytes += mq_initWithPoolSize(&mq, poolKb);
|
||||
numBytes += hLp_init(&inQueue, inQueueKb * 1024);
|
||||
numBytes += hLp_init(&outQueue, outQueueKb * 1024); // outQueueKb value of 0 sets everything to NULL
|
||||
}
|
||||
|
||||
HeavyContext::~HeavyContext() {
|
||||
mq_free(&mq);
|
||||
hLp_free(&inQueue);
|
||||
hLp_free(&outQueue);
|
||||
}
|
||||
|
||||
bool HeavyContext::sendBangToReceiver(hv_uint32_t receiverHash) {
|
||||
HvMessage *m = HV_MESSAGE_ON_STACK(1);
|
||||
msg_initWithBang(m, 0);
|
||||
bool success = sendMessageToReceiver(receiverHash, 0.0, m);
|
||||
return success;
|
||||
}
|
||||
|
||||
bool HeavyContext::sendFloatToReceiver(hv_uint32_t receiverHash, float f) {
|
||||
HvMessage *m = HV_MESSAGE_ON_STACK(1);
|
||||
msg_initWithFloat(m, 0, f);
|
||||
bool success = sendMessageToReceiver(receiverHash, 0.0, m);
|
||||
return success;
|
||||
}
|
||||
|
||||
bool HeavyContext::sendSymbolToReceiver(hv_uint32_t receiverHash, const char *s) {
|
||||
hv_assert(s != nullptr);
|
||||
HvMessage *m = HV_MESSAGE_ON_STACK(1);
|
||||
msg_initWithSymbol(m, 0, (char *) s);
|
||||
bool success = sendMessageToReceiver(receiverHash, 0.0, m);
|
||||
return success;
|
||||
}
|
||||
|
||||
bool HeavyContext::sendMessageToReceiverV(hv_uint32_t receiverHash, double delayMs, const char *format, ...) {
|
||||
hv_assert(delayMs >= 0.0);
|
||||
hv_assert(format != nullptr);
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
const int numElem = (int) hv_strlen(format);
|
||||
HvMessage *m = HV_MESSAGE_ON_STACK(numElem);
|
||||
msg_init(m, numElem, blockStartTimestamp + (hv_uint32_t) (hv_max_d(0.0, delayMs)*getSampleRate()/1000.0));
|
||||
for (int i = 0; i < numElem; i++) {
|
||||
switch (format[i]) {
|
||||
case 'b': msg_setBang(m, i); break;
|
||||
case 'f': msg_setFloat(m, i, (float) va_arg(ap, double)); break;
|
||||
case 'h': msg_setHash(m, i, (int) va_arg(ap, int)); break;
|
||||
case 's': msg_setSymbol(m, i, (char *) va_arg(ap, char *)); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
bool success = sendMessageToReceiver(receiverHash, delayMs, m);
|
||||
return success;
|
||||
}
|
||||
|
||||
bool HeavyContext::sendMessageToReceiver(hv_uint32_t receiverHash, double delayMs, HvMessage *m) {
|
||||
hv_assert(delayMs >= 0.0);
|
||||
hv_assert(m != nullptr);
|
||||
|
||||
const hv_uint32_t timestamp = blockStartTimestamp +
|
||||
(hv_uint32_t) (hv_max_d(0.0, delayMs)*(getSampleRate()/1000.0));
|
||||
|
||||
ReceiverMessagePair *p = nullptr;
|
||||
HV_SPINLOCK_ACQUIRE(inQueueLock);
|
||||
const hv_uint32_t numBytes = sizeof(ReceiverMessagePair) + msg_getSize(m) - sizeof(HvMessage);
|
||||
p = (ReceiverMessagePair *) hLp_getWriteBuffer(&inQueue, numBytes);
|
||||
if (p != nullptr) {
|
||||
p->receiverHash = receiverHash;
|
||||
msg_copyToBuffer(m, (char *) &p->msg, msg_getSize(m));
|
||||
msg_setTimestamp(&p->msg, timestamp);
|
||||
hLp_produce(&inQueue, numBytes);
|
||||
} else {
|
||||
hv_assert(false &&
|
||||
"::sendMessageToReceiver - The input message queue is full and cannot accept more messages until they "
|
||||
"have been processed. Try increasing the inQueueKb size in the new_with_options() constructor.");
|
||||
}
|
||||
HV_SPINLOCK_RELEASE(inQueueLock);
|
||||
return (p != nullptr);
|
||||
}
|
||||
|
||||
bool HeavyContext::cancelMessage(HvMessage *m, void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) {
|
||||
return mq_removeMessage(&mq, m, sendMessage);
|
||||
}
|
||||
|
||||
HvMessage *HeavyContext::scheduleMessageForObject(const HvMessage *m,
|
||||
void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *),
|
||||
int letIndex) {
|
||||
HvMessage *n = mq_addMessageByTimestamp(&mq, m, letIndex, sendMessage);
|
||||
return n;
|
||||
}
|
||||
|
||||
float *HeavyContext::getBufferForTable(hv_uint32_t tableHash) {
|
||||
HvTable *t = getTableForHash(tableHash);
|
||||
if (t != nullptr) {
|
||||
return hTable_getBuffer(t);
|
||||
} else return nullptr;
|
||||
}
|
||||
|
||||
int HeavyContext::getLengthForTable(hv_uint32_t tableHash) {
|
||||
HvTable *t = getTableForHash(tableHash);
|
||||
if (t != nullptr) {
|
||||
return hTable_getLength(t);
|
||||
} else return 0;
|
||||
}
|
||||
|
||||
bool HeavyContext::setLengthForTable(hv_uint32_t tableHash, hv_uint32_t newSampleLength) {
|
||||
HvTable *t = getTableForHash(tableHash);
|
||||
if (t != nullptr) {
|
||||
hTable_resize(t, newSampleLength);
|
||||
return true;
|
||||
} else return false;
|
||||
}
|
||||
|
||||
void HeavyContext::lockAcquire() {
|
||||
HV_SPINLOCK_ACQUIRE(inQueueLock);
|
||||
}
|
||||
|
||||
bool HeavyContext::lockTry() {
|
||||
HV_SPINLOCK_TRY(inQueueLock);
|
||||
}
|
||||
|
||||
void HeavyContext::lockRelease() {
|
||||
HV_SPINLOCK_RELEASE(inQueueLock);
|
||||
}
|
||||
|
||||
void HeavyContext::setInputMessageQueueSize(int inQueueKb) {
|
||||
hv_assert(inQueueKb > 0);
|
||||
hLp_free(&inQueue);
|
||||
hLp_init(&inQueue, inQueueKb*1024);
|
||||
}
|
||||
|
||||
void HeavyContext::setOutputMessageQueueSize(int outQueueKb) {
|
||||
hv_assert(outQueueKb > 0);
|
||||
hLp_free(&outQueue);
|
||||
hLp_init(&outQueue, outQueueKb*1024);
|
||||
}
|
||||
|
||||
bool HeavyContext::getNextSentMessage(hv_uint32_t *destinationHash, HvMessage *outMsg, hv_size_t msgLengthBytes) {
|
||||
*destinationHash = 0;
|
||||
ReceiverMessagePair *p = nullptr;
|
||||
hv_assert((sendHook == &defaultSendHook) &&
|
||||
"::getNextSentMessage - this function won't do anything if the msg outQueue "
|
||||
"size is 0, or you've overriden the default sendhook.");
|
||||
if (sendHook == &defaultSendHook) {
|
||||
HV_SPINLOCK_ACQUIRE(outQueueLock);
|
||||
if (hLp_hasData(&outQueue)) {
|
||||
hv_uint32_t numBytes = 0;
|
||||
p = reinterpret_cast<ReceiverMessagePair *>(hLp_getReadBuffer(&outQueue, &numBytes));
|
||||
hv_assert((p != nullptr) && "::getNextSentMessage - something bad happened.");
|
||||
hv_assert(numBytes >= sizeof(ReceiverMessagePair));
|
||||
hv_assert((numBytes <= msgLengthBytes) &&
|
||||
"::getNextSentMessage - the sent message is bigger than the message "
|
||||
"passed to handle it.");
|
||||
*destinationHash = p->receiverHash;
|
||||
hv_memcpy(outMsg, &p->msg, numBytes);
|
||||
hLp_consume(&outQueue);
|
||||
}
|
||||
HV_SPINLOCK_RELEASE(outQueueLock);
|
||||
}
|
||||
return (p != nullptr);
|
||||
}
|
||||
|
||||
hv_uint32_t HeavyContext::getHashForString(const char *str) {
|
||||
return hv_string_to_hash(str);
|
||||
}
|
||||
|
||||
HvTable *_hv_table_get(HeavyContextInterface *c, hv_uint32_t tableHash) {
|
||||
hv_assert(c != nullptr);
|
||||
return reinterpret_cast<HeavyContext *>(c)->getTableForHash(tableHash);
|
||||
}
|
||||
|
||||
void _hv_scheduleMessageForReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, HvMessage *m) {
|
||||
hv_assert(c != nullptr);
|
||||
reinterpret_cast<HeavyContext *>(c)->scheduleMessageForReceiver(receiverHash, m);
|
||||
}
|
||||
|
||||
HvMessage *_hv_scheduleMessageForObject(HeavyContextInterface *c, const HvMessage *m,
|
||||
void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *),
|
||||
int letIndex) {
|
||||
hv_assert(c != nullptr);
|
||||
HvMessage *n = reinterpret_cast<HeavyContext *>(c)->scheduleMessageForObject(
|
||||
m, sendMessage, letIndex);
|
||||
return n;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
HvTable *hv_table_get(HeavyContextInterface *c, hv_uint32_t tableHash) {
|
||||
return _hv_table_get(c, tableHash);
|
||||
}
|
||||
|
||||
void hv_scheduleMessageForReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, HvMessage *m) {
|
||||
_hv_scheduleMessageForReceiver(c, receiverHash, m);
|
||||
}
|
||||
|
||||
HvMessage *hv_scheduleMessageForObject(HeavyContextInterface *c, const HvMessage *m,
|
||||
void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *),
|
||||
int letIndex) {
|
||||
return _hv_scheduleMessageForObject(c, m, sendMessage, letIndex);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
* Copyright (c) 2014-2018 Enzien Audio Ltd.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _HEAVY_CONTEXT_H_
|
||||
#define _HEAVY_CONTEXT_H_
|
||||
|
||||
#include "HeavyContextInterface.hpp"
|
||||
#include "HvLightPipe.h"
|
||||
#include "HvMessageQueue.h"
|
||||
#include "HvMath.h"
|
||||
|
||||
struct HvTable;
|
||||
|
||||
class HeavyContext : public HeavyContextInterface {
|
||||
|
||||
public:
|
||||
HeavyContext(double sampleRate, int poolKb=10, int inQueueKb=2, int outQueueKb=0);
|
||||
virtual ~HeavyContext();
|
||||
|
||||
int getSize() override { return (int) numBytes; }
|
||||
|
||||
double getSampleRate() override { return sampleRate; }
|
||||
|
||||
hv_uint32_t getCurrentSample() override { return blockStartTimestamp; }
|
||||
float samplesToMilliseconds(hv_uint32_t numSamples) override { return (float) (1000.0*numSamples/sampleRate); }
|
||||
hv_uint32_t millisecondsToSamples(float ms) override { return (hv_uint32_t) (hv_max_f(0.0f,ms)*sampleRate/1000.0); }
|
||||
|
||||
void setUserData(void *x) override { userData = x; }
|
||||
void *getUserData() override { return userData; }
|
||||
|
||||
// hook management
|
||||
void setSendHook(HvSendHook_t *f) override { sendHook = f; }
|
||||
HvSendHook_t *getSendHook() override { return sendHook; }
|
||||
|
||||
void setPrintHook(HvPrintHook_t *f) override { printHook = f; }
|
||||
HvPrintHook_t *getPrintHook() override { return printHook; }
|
||||
|
||||
// message scheduling
|
||||
bool sendMessageToReceiver(hv_uint32_t receiverHash, double delayMs, HvMessage *m) override;
|
||||
bool sendMessageToReceiverV(hv_uint32_t receiverHash, double delayMs, const char *fmt, ...) override;
|
||||
bool sendFloatToReceiver(hv_uint32_t receiverHash, float f) override;
|
||||
bool sendBangToReceiver(hv_uint32_t receiverHash) override;
|
||||
bool sendSymbolToReceiver(hv_uint32_t receiverHash, const char *symbol) override;
|
||||
bool cancelMessage(HvMessage *m, void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) override;
|
||||
|
||||
// table manipulation
|
||||
float *getBufferForTable(hv_uint32_t tableHash) override;
|
||||
int getLengthForTable(hv_uint32_t tableHash) override;
|
||||
bool setLengthForTable(hv_uint32_t tableHash, hv_uint32_t newSampleLength) override;
|
||||
|
||||
// lock control
|
||||
void lockAcquire() override;
|
||||
bool lockTry() override;
|
||||
void lockRelease() override;
|
||||
|
||||
// message queue management
|
||||
void setInputMessageQueueSize(int inQueueKb) override;
|
||||
void setOutputMessageQueueSize(int outQueueKb) override;
|
||||
bool getNextSentMessage(hv_uint32_t *destinationHash, HvMessage *outMsg, hv_size_t msgLength) override;
|
||||
|
||||
// utility functions
|
||||
static hv_uint32_t getHashForString(const char *str);
|
||||
|
||||
protected:
|
||||
virtual HvTable *getTableForHash(hv_uint32_t tableHash) = 0;
|
||||
friend HvTable *_hv_table_get(HeavyContextInterface *, hv_uint32_t);
|
||||
|
||||
virtual void scheduleMessageForReceiver(hv_uint32_t receiverHash, HvMessage *m) = 0;
|
||||
friend void _hv_scheduleMessageForReceiver(HeavyContextInterface *, hv_uint32_t, HvMessage *);
|
||||
|
||||
HvMessage *scheduleMessageForObject(const HvMessage *,
|
||||
void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *),
|
||||
int);
|
||||
friend HvMessage *_hv_scheduleMessageForObject(HeavyContextInterface *, const HvMessage *,
|
||||
void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *),
|
||||
int);
|
||||
|
||||
friend void defaultSendHook(HeavyContextInterface *, const char *, hv_uint32_t, const HvMessage *);
|
||||
|
||||
// object state
|
||||
double sampleRate;
|
||||
hv_uint32_t blockStartTimestamp;
|
||||
hv_size_t numBytes;
|
||||
HvMessageQueue mq;
|
||||
HvSendHook_t *sendHook;
|
||||
HvPrintHook_t *printHook;
|
||||
void *userData;
|
||||
HvLightPipe inQueue;
|
||||
HvLightPipe outQueue;
|
||||
hv_atomic_bool inQueueLock;
|
||||
hv_atomic_bool outQueueLock;
|
||||
};
|
||||
|
||||
#endif // _HEAVY_CONTEXT_H_
|
||||
@@ -0,0 +1,291 @@
|
||||
/**
|
||||
* Copyright (c) 2014-2018 Enzien Audio Ltd.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _HEAVY_CONTEXT_INTERFACE_H_
|
||||
#define _HEAVY_CONTEXT_INTERFACE_H_
|
||||
|
||||
#include "HvUtils.h"
|
||||
|
||||
#ifndef _HEAVY_DECLARATIONS_
|
||||
#define _HEAVY_DECLARATIONS_
|
||||
|
||||
class HeavyContextInterface;
|
||||
struct HvMessage;
|
||||
|
||||
typedef enum {
|
||||
HV_PARAM_TYPE_PARAMETER_IN,
|
||||
HV_PARAM_TYPE_PARAMETER_OUT,
|
||||
HV_PARAM_TYPE_EVENT_IN,
|
||||
HV_PARAM_TYPE_EVENT_OUT
|
||||
} HvParameterType;
|
||||
|
||||
typedef struct HvParameterInfo {
|
||||
const char *name; // the human readable parameter name
|
||||
hv_uint32_t hash; // an integer identified used by heavy for this parameter
|
||||
HvParameterType type; // type of this parameter
|
||||
float minVal; // the minimum value of this parameter
|
||||
float maxVal; // the maximum value of this parameter
|
||||
float defaultVal; // the default value of this parameter
|
||||
} HvParameterInfo;
|
||||
|
||||
typedef void (HvSendHook_t) (HeavyContextInterface *context, const char *sendName, hv_uint32_t sendHash, const HvMessage *msg);
|
||||
typedef void (HvPrintHook_t) (HeavyContextInterface *context, const char *printName, const char *str, const HvMessage *msg);
|
||||
|
||||
#endif // _HEAVY_DECLARATIONS_
|
||||
|
||||
|
||||
|
||||
class HeavyContextInterface {
|
||||
|
||||
public:
|
||||
HeavyContextInterface() {}
|
||||
virtual ~HeavyContextInterface() {};
|
||||
|
||||
/** Returns the read-only user-assigned name of this patch. */
|
||||
virtual const char *getName() = 0;
|
||||
|
||||
/** Returns the number of input channels with which this context has been configured. */
|
||||
virtual int getNumInputChannels() = 0;
|
||||
|
||||
/** Returns the number of output channels with which this context has been configured. */
|
||||
virtual int getNumOutputChannels() = 0;
|
||||
|
||||
/**
|
||||
* Returns the total size in bytes of the context.
|
||||
* This value may change if tables are resized.
|
||||
*/
|
||||
virtual int getSize() = 0;
|
||||
|
||||
/** Returns the sample rate with which this context has been configured. */
|
||||
virtual double getSampleRate() = 0;
|
||||
|
||||
/** Returns the current patch time in samples. This value is always exact. */
|
||||
virtual hv_uint32_t getCurrentSample() = 0;
|
||||
virtual float samplesToMilliseconds(hv_uint32_t numSamples) = 0;
|
||||
|
||||
/** Converts milliseconds to samples. Input is limited to non-negative range. */
|
||||
virtual hv_uint32_t millisecondsToSamples(float ms) = 0;
|
||||
|
||||
/** Sets a user-definable value. This value is never manipulated by Heavy. */
|
||||
virtual void setUserData(void *x) = 0;
|
||||
|
||||
/** Returns the user-defined data. */
|
||||
virtual void *getUserData() = 0;
|
||||
|
||||
/**
|
||||
* Set the send hook. The function is called whenever a message is sent to any send object.
|
||||
* Messages returned by this function should NEVER be freed. If the message must persist, call
|
||||
* hv_msg_copy() first.
|
||||
*/
|
||||
virtual void setSendHook(HvSendHook_t *f) = 0;
|
||||
|
||||
/** Returns the send hook, or NULL if unset. */
|
||||
virtual HvSendHook_t *getSendHook() = 0;
|
||||
|
||||
/** Set the print hook. The function is called whenever a message is sent to a print object. */
|
||||
virtual void setPrintHook(HvPrintHook_t *f) = 0;
|
||||
|
||||
/** Returns the print hook, or NULL if unset. */
|
||||
virtual HvPrintHook_t *getPrintHook() = 0;
|
||||
|
||||
/**
|
||||
* Processes one block of samples for a patch instance. The buffer format is an array of float channel arrays.
|
||||
* If the context has not input or output channels, the respective argument may be NULL.
|
||||
* The number of samples to to tbe processed should be a multiple of 1, 4, or 8, depending on if
|
||||
* no, SSE or NEON, or AVX optimisation is being used, respectively.
|
||||
* e.g. [[LLLL][RRRR]]
|
||||
*
|
||||
* @return The number of samples processed.
|
||||
*
|
||||
* This function is NOT thread-safe. It is assumed that only the audio thread will execute this function.
|
||||
*/
|
||||
virtual int process(float **inputBuffers, float **outputBuffer, int n) = 0;
|
||||
|
||||
/**
|
||||
* Processes one block of samples for a patch instance. The buffer format is an uninterleaved float array of channels.
|
||||
* If the context has not input or output channels, the respective argument may be NULL.
|
||||
* The number of samples to to tbe processed should be a multiple of 1, 4, or 8, depending on if
|
||||
* no, SSE or NEON, or AVX optimisation is being used, respectively.
|
||||
* e.g. [LLLLRRRR]
|
||||
*
|
||||
* @return The number of samples processed.
|
||||
*
|
||||
* This function is NOT thread-safe. It is assumed that only the audio thread will execute this function.
|
||||
*/
|
||||
virtual int processInline(float *inputBuffers, float *outputBuffer, int n) = 0;
|
||||
|
||||
/**
|
||||
* Processes one block of samples for a patch instance. The buffer format is an interleaved float array of channels.
|
||||
* If the context has not input or output channels, the respective argument may be NULL.
|
||||
* The number of samples to to tbe processed should be a multiple of 1, 4, or 8, depending on if
|
||||
* no, SSE or NEON, or AVX optimisation is being used, respectively.
|
||||
* e.g. [LRLRLRLR]
|
||||
*
|
||||
* @return The number of samples processed.
|
||||
*
|
||||
* This function is NOT thread-safe. It is assumed that only the audio thread will execute this function.
|
||||
*/
|
||||
virtual int processInlineInterleaved(float *inputBuffers, float *outputBuffer, int n) = 0;
|
||||
|
||||
/**
|
||||
* Sends a formatted message to a receiver that can be scheduled for the future.
|
||||
* The receiver is addressed with its hash, which can also be determined using hv_stringToHash().
|
||||
* This function is thread-safe.
|
||||
*
|
||||
* @return True if the message was accepted. False if the message could not fit onto
|
||||
* the message queue to be processed this block.
|
||||
*/
|
||||
virtual bool sendMessageToReceiver(hv_uint32_t receiverHash, double delayMs, HvMessage *m) = 0;
|
||||
|
||||
/**
|
||||
* Sends a formatted message to a receiver that can be scheduled for the future.
|
||||
* The receiver is addressed with its hash, which can also be determined using hv_stringToHash().
|
||||
* This function is thread-safe.
|
||||
*
|
||||
* @return True if the message was accepted. False if the message could not fit onto
|
||||
* the message queue to be processed this block.
|
||||
*/
|
||||
virtual bool sendMessageToReceiverV(hv_uint32_t receiverHash, double delayMs, const char *fmt, ...) = 0;
|
||||
|
||||
/**
|
||||
* A convenience function to send a float to a receiver to be processed immediately.
|
||||
* The receiver is addressed with its hash, which can also be determined using hv_stringToHash().
|
||||
* This function is thread-safe.
|
||||
*
|
||||
* @return True if the message was accepted. False if the message could not fit onto
|
||||
* the message queue to be processed this block.
|
||||
*/
|
||||
virtual bool sendFloatToReceiver(hv_uint32_t receiverHash, float f) = 0;
|
||||
|
||||
/**
|
||||
* A convenience function to send a bang to a receiver to be processed immediately.
|
||||
* The receiver is addressed with its hash, which can also be determined using hv_stringToHash().
|
||||
* This function is thread-safe.
|
||||
*
|
||||
* @return True if the message was accepted. False if the message could not fit onto
|
||||
* the message queue to be processed this block.
|
||||
*/
|
||||
virtual bool sendBangToReceiver(hv_uint32_t receiverHash) = 0;
|
||||
|
||||
/**
|
||||
* A convenience function to send a symbol to a receiver to be processed immediately.
|
||||
* The receiver is addressed with its hash, which can also be determined using hv_stringToHash().
|
||||
* This function is thread-safe.
|
||||
*
|
||||
* @return True if the message was accepted. False if the message could not fit onto
|
||||
* the message queue to be processed this block.
|
||||
*/
|
||||
virtual bool sendSymbolToReceiver(hv_uint32_t receiverHash, const char *symbol) = 0;
|
||||
|
||||
/**
|
||||
* Cancels a previously scheduled message.
|
||||
*
|
||||
* @param sendMessage May be NULL.
|
||||
*/
|
||||
virtual bool cancelMessage(HvMessage *m, void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)=nullptr) = 0;
|
||||
|
||||
/**
|
||||
* Returns information about each parameter such as name, hash, and range.
|
||||
* The total number of parameters is always returned.
|
||||
*
|
||||
* @param index The parameter index.
|
||||
* @param info A pointer to a HvParameterInfo struct. May be null.
|
||||
*
|
||||
* @return The total number of parameters.
|
||||
*/
|
||||
virtual int getParameterInfo(int index, HvParameterInfo *info) = 0;
|
||||
|
||||
/** Returns a pointer to the raw buffer backing this table. DO NOT free it. */
|
||||
virtual float *getBufferForTable(hv_uint32_t tableHash) = 0;
|
||||
|
||||
/** Returns the length of this table in samples. */
|
||||
virtual int getLengthForTable(hv_uint32_t tableHash) = 0;
|
||||
|
||||
/**
|
||||
* Resizes the table to the given length.
|
||||
*
|
||||
* Existing contents are copied to the new table. Remaining space is cleared
|
||||
* if the table is longer than the original, truncated otherwise.
|
||||
*
|
||||
* @param tableHash The table identifier.
|
||||
* @param newSampleLength The new length of the table, in samples.
|
||||
*
|
||||
* @return False if the table could not be found. True otherwise.
|
||||
*/
|
||||
virtual bool setLengthForTable(hv_uint32_t tableHash, hv_uint32_t newSampleLength) = 0;
|
||||
|
||||
/**
|
||||
* Acquire the input message queue lock.
|
||||
*
|
||||
* This function will block until the message lock as been acquired.
|
||||
* Typical applications will not require the use of this function.
|
||||
*/
|
||||
virtual void lockAcquire() = 0;
|
||||
|
||||
/**
|
||||
* Try to acquire the input message queue lock.
|
||||
*
|
||||
* If the lock has been acquired, hv_lock_release() must be called to release it.
|
||||
* Typical applications will not require the use of this function.
|
||||
*
|
||||
* @return Returns true if the lock has been acquired, false otherwise.
|
||||
*/
|
||||
virtual bool lockTry() = 0;
|
||||
|
||||
/**
|
||||
* Release the input message queue lock.
|
||||
*
|
||||
* Typical applications will not require the use of this function.
|
||||
*/
|
||||
virtual void lockRelease() = 0;
|
||||
|
||||
/**
|
||||
* Set the size of the input message queue in kilobytes.
|
||||
*
|
||||
* The buffer is reset and all existing contents are lost on resize.
|
||||
*
|
||||
* @param inQueueKb Must be positive i.e. at least one.
|
||||
*/
|
||||
virtual void setInputMessageQueueSize(int inQueueKb) = 0;
|
||||
|
||||
/**
|
||||
* Set the size of the output message queue in kilobytes.
|
||||
*
|
||||
* The buffer is reset and all existing contents are lost on resize.
|
||||
* Only the default sendhook uses the outgoing message queue. If the default
|
||||
* sendhook is not being used, then this function is not useful.
|
||||
*
|
||||
* @param outQueueKb Must be postive i.e. at least one.
|
||||
*/
|
||||
virtual void setOutputMessageQueueSize(int outQueueKb) = 0;
|
||||
|
||||
/**
|
||||
* Get the next message in the outgoing queue, will also consume the message.
|
||||
* Returns false if there are no messages.
|
||||
*
|
||||
* @param destinationHash a hash of the name of the receiver the message was sent to.
|
||||
* @param outMsg message pointer that is filled by the next message contents.
|
||||
* @param msgLengthBytes max length of outMsg in bytes.
|
||||
*
|
||||
* @return True if there is a message in the outgoing queue.
|
||||
*/
|
||||
virtual bool getNextSentMessage(hv_uint32_t *destinationHash, HvMessage *outMsg, hv_size_t msgLengthBytes) = 0;
|
||||
|
||||
/** Returns a 32-bit hash of any string. Returns 0 if string is NULL. */
|
||||
static hv_uint32_t getHashForString(const char *str);
|
||||
};
|
||||
|
||||
#endif // _HEAVY_CONTEXT_INTERFACE_H_
|
||||
@@ -0,0 +1,274 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Enzien Audio, Ltd.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions, and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the phrase "powered by heavy",
|
||||
* the heavy logo, and a hyperlink to https://enzienaudio.com, all in a visible
|
||||
* form.
|
||||
*
|
||||
* 2.1 If the Application is distributed in a store system (for example,
|
||||
* the Apple "App Store" or "Google Play"), the phrase "powered by heavy"
|
||||
* shall be included in the app description or the copyright text as well as
|
||||
* the in the app itself. The heavy logo will shall be visible in the app
|
||||
* itself as well.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "Heavy_Adc2Dac.hpp"
|
||||
|
||||
#include <new>
|
||||
|
||||
#define Context(_c) static_cast<Heavy_Adc2Dac *>(_c)
|
||||
|
||||
|
||||
/*
|
||||
* C Functions
|
||||
*/
|
||||
|
||||
extern "C" {
|
||||
HV_EXPORT HeavyContextInterface *hv_Adc2Dac_new(double sampleRate) {
|
||||
// allocate aligned memory
|
||||
void *ptr = hv_malloc(sizeof(Heavy_Adc2Dac));
|
||||
// ensure non-null
|
||||
if (!ptr) return nullptr;
|
||||
// call constructor
|
||||
new(ptr) Heavy_Adc2Dac(sampleRate);
|
||||
return Context(ptr);
|
||||
}
|
||||
|
||||
HV_EXPORT HeavyContextInterface *hv_Adc2Dac_new_with_options(double sampleRate,
|
||||
int poolKb, int inQueueKb, int outQueueKb) {
|
||||
// allocate aligned memory
|
||||
void *ptr = hv_malloc(sizeof(Heavy_Adc2Dac));
|
||||
// ensure non-null
|
||||
if (!ptr) return nullptr;
|
||||
// call constructor
|
||||
new(ptr) Heavy_Adc2Dac(sampleRate, poolKb, inQueueKb, outQueueKb);
|
||||
return Context(ptr);
|
||||
}
|
||||
|
||||
HV_EXPORT void hv_Adc2Dac_free(HeavyContextInterface *instance) {
|
||||
// call destructor
|
||||
Context(instance)->~Heavy_Adc2Dac();
|
||||
// free memory
|
||||
hv_free(instance);
|
||||
}
|
||||
} // extern "C"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Class Functions
|
||||
*/
|
||||
|
||||
Heavy_Adc2Dac::Heavy_Adc2Dac(double sampleRate, int poolKb, int inQueueKb, int outQueueKb)
|
||||
: HeavyContext(sampleRate, poolKb, inQueueKb, outQueueKb) {
|
||||
|
||||
}
|
||||
|
||||
Heavy_Adc2Dac::~Heavy_Adc2Dac() {
|
||||
// nothing to free
|
||||
}
|
||||
|
||||
HvTable *Heavy_Adc2Dac::getTableForHash(hv_uint32_t tableHash) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Heavy_Adc2Dac::scheduleMessageForReceiver(hv_uint32_t receiverHash, HvMessage *m) {
|
||||
switch (receiverHash) {
|
||||
default: return;
|
||||
}
|
||||
}
|
||||
|
||||
int Heavy_Adc2Dac::getParameterInfo(int index, HvParameterInfo *info) {
|
||||
if (info != nullptr) {
|
||||
switch (index) {
|
||||
default: {
|
||||
info->name = "invalid parameter index";
|
||||
info->hash = 0;
|
||||
info->type = HvParameterType::HV_PARAM_TYPE_PARAMETER_IN;
|
||||
info->minVal = 0.0f;
|
||||
info->maxVal = 0.0f;
|
||||
info->defaultVal = 0.0f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Send Function Implementations
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Context Process Implementation
|
||||
*/
|
||||
|
||||
int Heavy_Adc2Dac::process(float **inputBuffers, float **outputBuffers, int n) {
|
||||
while (hLp_hasData(&inQueue)) {
|
||||
hv_uint32_t numBytes = 0;
|
||||
ReceiverMessagePair *p = reinterpret_cast<ReceiverMessagePair *>(hLp_getReadBuffer(&inQueue, &numBytes));
|
||||
hv_assert(numBytes >= sizeof(ReceiverMessagePair));
|
||||
scheduleMessageForReceiver(p->receiverHash, &p->msg);
|
||||
hLp_consume(&inQueue);
|
||||
}
|
||||
|
||||
sendBangToReceiver(0xDD21C0EB); // send to __hv_bang~ on next cycle
|
||||
const int n4 = n & ~HV_N_SIMD_MASK; // ensure that the block size is a multiple of HV_N_SIMD
|
||||
|
||||
// temporary signal vars
|
||||
|
||||
// input and output vars
|
||||
hv_bufferf_t O0, O1;
|
||||
hv_bufferf_t I0, I1;
|
||||
|
||||
// declare and init the zero buffer
|
||||
hv_bufferf_t ZERO; __hv_zero_f(VOf(ZERO));
|
||||
|
||||
hv_uint32_t nextBlock = blockStartTimestamp;
|
||||
for (int n = 0; n < n4; n += HV_N_SIMD) {
|
||||
|
||||
// process all of the messages for this block
|
||||
nextBlock += HV_N_SIMD;
|
||||
while (mq_hasMessageBefore(&mq, nextBlock)) {
|
||||
MessageNode *const node = mq_peek(&mq);
|
||||
node->sendMessage(this, node->let, node->m);
|
||||
mq_pop(&mq);
|
||||
}
|
||||
|
||||
// load input buffers
|
||||
__hv_load_f(inputBuffers[0]+n, VOf(I0));
|
||||
__hv_load_f(inputBuffers[1]+n, VOf(I1));
|
||||
|
||||
// zero output buffers
|
||||
__hv_zero_f(VOf(O0));
|
||||
__hv_zero_f(VOf(O1));
|
||||
|
||||
// process all signal functions
|
||||
__hv_add_f(VIf(I0), VIf(O0), VOf(O0));
|
||||
__hv_add_f(VIf(I1), VIf(O1), VOf(O1));
|
||||
|
||||
// save output vars to output buffer
|
||||
__hv_store_f(outputBuffers[0]+n, VIf(O0));
|
||||
__hv_store_f(outputBuffers[1]+n, VIf(O1));
|
||||
}
|
||||
|
||||
blockStartTimestamp = nextBlock;
|
||||
|
||||
return n4; // return the number of frames processed
|
||||
|
||||
}
|
||||
|
||||
int Heavy_Adc2Dac::processInline(float *inputBuffers, float *outputBuffers, int n4) {
|
||||
hv_assert(!(n4 & HV_N_SIMD_MASK)); // ensure that n4 is a multiple of HV_N_SIMD
|
||||
|
||||
// define the heavy input buffer for 2 channel(s)
|
||||
float **const bIn = reinterpret_cast<float **>(hv_alloca(2*sizeof(float *)));
|
||||
bIn[0] = inputBuffers+(0*n4);
|
||||
bIn[1] = inputBuffers+(1*n4);
|
||||
|
||||
// define the heavy output buffer for 2 channel(s)
|
||||
float **const bOut = reinterpret_cast<float **>(hv_alloca(2*sizeof(float *)));
|
||||
bOut[0] = outputBuffers+(0*n4);
|
||||
bOut[1] = outputBuffers+(1*n4);
|
||||
|
||||
int n = process(bIn, bOut, n4);
|
||||
return n;
|
||||
}
|
||||
|
||||
int Heavy_Adc2Dac::processInlineInterleaved(float *inputBuffers, float *outputBuffers, int n4) {
|
||||
hv_assert(n4 & ~HV_N_SIMD_MASK); // ensure that n4 is a multiple of HV_N_SIMD
|
||||
|
||||
// define the heavy input buffer for 2 channel(s), uninterleave
|
||||
float *const bIn = reinterpret_cast<float *>(hv_alloca(2*n4*sizeof(float)));
|
||||
#if HV_SIMD_SSE || HV_SIMD_AVX
|
||||
for (int i = 0, j = 0; j < n4; j += 4, i += 8) {
|
||||
__m128 a = _mm_load_ps(inputBuffers+i); // LRLR
|
||||
__m128 b = _mm_load_ps(inputBuffers+4+i); // LRLR
|
||||
__m128 x = _mm_shuffle_ps(a, b, _MM_SHUFFLE(2,0,2,0)); // LLLL
|
||||
__m128 y = _mm_shuffle_ps(a, b, _MM_SHUFFLE(3,1,3,1)); // RRRR
|
||||
_mm_store_ps(bIn+j, x);
|
||||
_mm_store_ps(bIn+n4+j, y);
|
||||
}
|
||||
#elif HV_SIMD_NEON
|
||||
for (int i = 0, j = 0; j < n4; j += 4, i += 8) {
|
||||
float32x4x2_t a = vld2q_f32(inputBuffers+i); // load and uninterleave
|
||||
vst1q_f32(bIn+j, a.val[0]);
|
||||
vst1q_f32(bIn+n4+j, a.val[1]);
|
||||
}
|
||||
#else // HV_SIMD_NONE
|
||||
for (int j = 0; j < n4; ++j) {
|
||||
bIn[0*n4+j] = inputBuffers[0+2*j];
|
||||
bIn[1*n4+j] = inputBuffers[1+2*j];
|
||||
}
|
||||
#endif
|
||||
|
||||
// define the heavy output buffer for 2 channel(s)
|
||||
float *const bOut = reinterpret_cast<float *>(hv_alloca(2*n4*sizeof(float)));
|
||||
|
||||
int n = processInline(bIn, bOut, n4);
|
||||
|
||||
// interleave the heavy output into the output buffer
|
||||
#if HV_SIMD_AVX
|
||||
for (int i = 0, j = 0; j < n4; j += 8, i += 16) {
|
||||
__m256 x = _mm256_load_ps(bOut+j); // LLLLLLLL
|
||||
__m256 y = _mm256_load_ps(bOut+n4+j); // RRRRRRRR
|
||||
__m256 a = _mm256_unpacklo_ps(x, y); // LRLRLRLR
|
||||
__m256 b = _mm256_unpackhi_ps(x, y); // LRLRLRLR
|
||||
_mm256_store_ps(outputBuffers+i, a);
|
||||
_mm256_store_ps(outputBuffers+8+i, b);
|
||||
}
|
||||
#elif HV_SIMD_SSE
|
||||
for (int i = 0, j = 0; j < n4; j += 4, i += 8) {
|
||||
__m128 x = _mm_load_ps(bOut+j); // LLLL
|
||||
__m128 y = _mm_load_ps(bOut+n4+j); // RRRR
|
||||
__m128 a = _mm_unpacklo_ps(x, y); // LRLR
|
||||
__m128 b = _mm_unpackhi_ps(x, y); // LRLR
|
||||
_mm_store_ps(outputBuffers+i, a);
|
||||
_mm_store_ps(outputBuffers+4+i, b);
|
||||
}
|
||||
#elif HV_SIMD_NEON
|
||||
// https://community.arm.com/groups/processors/blog/2012/03/13/coding-for-neon--part-5-rearranging-vectors
|
||||
for (int i = 0, j = 0; j < n4; j += 4, i += 8) {
|
||||
float32x4_t x = vld1q_f32(bOut+j);
|
||||
float32x4_t y = vld1q_f32(bOut+n4+j);
|
||||
float32x4x2_t z = {x, y};
|
||||
vst2q_f32(outputBuffers+i, z); // interleave and store
|
||||
}
|
||||
#else // HV_SIMD_NONE
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
for (int j = 0; j < n4; ++j) {
|
||||
outputBuffers[i+2*j] = bOut[i*n4+j];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return n;
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Enzien Audio, Ltd.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions, and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the phrase "powered by heavy",
|
||||
* the heavy logo, and a hyperlink to https://enzienaudio.com, all in a visible
|
||||
* form.
|
||||
*
|
||||
* 2.1 If the Application is distributed in a store system (for example,
|
||||
* the Apple "App Store" or "Google Play"), the phrase "powered by heavy"
|
||||
* shall be included in the app description or the copyright text as well as
|
||||
* the in the app itself. The heavy logo will shall be visible in the app
|
||||
* itself as well.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _HEAVY_ADC2DAC_H_
|
||||
#define _HEAVY_ADC2DAC_H_
|
||||
|
||||
#include "HvHeavy.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if HV_APPLE
|
||||
#pragma mark - Heavy Context
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new patch instance.
|
||||
* Sample rate should be positive and in Hertz, e.g. 44100.0.
|
||||
*/
|
||||
HeavyContextInterface *hv_Adc2Dac_new(double sampleRate);
|
||||
|
||||
/**
|
||||
* Creates a new patch instance.
|
||||
* @param sampleRate Sample rate should be positive (> 0) and in Hertz, e.g. 48000.0.
|
||||
* @param poolKb Pool size is in kilobytes, and determines the maximum amount of memory
|
||||
* allocated to messages at any time. By default this is 10 KB.
|
||||
* @param inQueueKb The size of the input message queue in kilobytes. It determines the
|
||||
* amount of memory dedicated to holding scheduled messages between calls to
|
||||
* process(). Default is 2 KB.
|
||||
* @param outQueueKb The size of the output message queue in kilobytes. It determines the
|
||||
* amount of memory dedicated to holding scheduled messages to the default sendHook.
|
||||
* See getNextSentMessage() for info on accessing these messages. Default is 0 KB.
|
||||
*/
|
||||
HeavyContextInterface *hv_Adc2Dac_new_with_options(double sampleRate, int poolKb, int inQueueKb, int outQueueKb);
|
||||
|
||||
/**
|
||||
* Free the patch instance.
|
||||
*/
|
||||
void hv_Adc2Dac_free(HeavyContextInterface *instance);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // _HEAVY_ADC2DAC_H_
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Enzien Audio, Ltd.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions, and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the phrase "powered by heavy",
|
||||
* the heavy logo, and a hyperlink to https://enzienaudio.com, all in a visible
|
||||
* form.
|
||||
*
|
||||
* 2.1 If the Application is distributed in a store system (for example,
|
||||
* the Apple "App Store" or "Google Play"), the phrase "powered by heavy"
|
||||
* shall be included in the app description or the copyright text as well as
|
||||
* the in the app itself. The heavy logo will shall be visible in the app
|
||||
* itself as well.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _HEAVY_CONTEXT_ADC2DAC_HPP_
|
||||
#define _HEAVY_CONTEXT_ADC2DAC_HPP_
|
||||
|
||||
// object includes
|
||||
#include "HeavyContext.hpp"
|
||||
#include "HvMath.h"
|
||||
|
||||
class Heavy_Adc2Dac : public HeavyContext {
|
||||
|
||||
public:
|
||||
Heavy_Adc2Dac(double sampleRate, int poolKb=10, int inQueueKb=2, int outQueueKb=0);
|
||||
~Heavy_Adc2Dac();
|
||||
|
||||
const char *getName() override { return "Adc2Dac"; }
|
||||
int getNumInputChannels() override { return 2; }
|
||||
int getNumOutputChannels() override { return 2; }
|
||||
|
||||
int process(float **inputBuffers, float **outputBuffer, int n) override;
|
||||
int processInline(float *inputBuffers, float *outputBuffer, int n) override;
|
||||
int processInlineInterleaved(float *inputBuffers, float *outputBuffer, int n) override;
|
||||
|
||||
int getParameterInfo(int index, HvParameterInfo *info) override;
|
||||
|
||||
private:
|
||||
HvTable *getTableForHash(hv_uint32_t tableHash) override;
|
||||
void scheduleMessageForReceiver(hv_uint32_t receiverHash, HvMessage *m) override;
|
||||
|
||||
// static sendMessage functions
|
||||
|
||||
// objects
|
||||
};
|
||||
|
||||
#endif // _HEAVY_CONTEXT_ADC2DAC_HPP_
|
||||
@@ -0,0 +1,318 @@
|
||||
/**
|
||||
* Copyright (c) 2014-2018 Enzien Audio Ltd.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "HeavyContext.hpp"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if HV_APPLE
|
||||
#pragma mark - Heavy Table
|
||||
#endif
|
||||
|
||||
HV_EXPORT bool hv_table_setLength(HeavyContextInterface *c, hv_uint32_t tableHash, hv_uint32_t newSampleLength) {
|
||||
hv_assert(c != nullptr);
|
||||
return c->setLengthForTable(tableHash, newSampleLength);
|
||||
}
|
||||
|
||||
HV_EXPORT float *hv_table_getBuffer(HeavyContextInterface *c, hv_uint32_t tableHash) {
|
||||
hv_assert(c != nullptr);
|
||||
return c->getBufferForTable(tableHash);
|
||||
}
|
||||
|
||||
HV_EXPORT hv_uint32_t hv_table_getLength(HeavyContextInterface *c, hv_uint32_t tableHash) {
|
||||
hv_assert(c != nullptr);
|
||||
return c->getLengthForTable(tableHash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#if HV_APPLE
|
||||
#pragma mark - Heavy Message
|
||||
#endif
|
||||
|
||||
HV_EXPORT hv_size_t hv_msg_getByteSize(hv_uint32_t numElements) {
|
||||
return msg_getCoreSize(numElements);
|
||||
}
|
||||
|
||||
HV_EXPORT void hv_msg_init(HvMessage *m, int numElements, hv_uint32_t timestamp) {
|
||||
msg_init(m, numElements, timestamp);
|
||||
}
|
||||
|
||||
HV_EXPORT hv_size_t hv_msg_getNumElements(const HvMessage *m) {
|
||||
return msg_getNumElements(m);
|
||||
}
|
||||
|
||||
HV_EXPORT hv_uint32_t hv_msg_getTimestamp(const HvMessage *m) {
|
||||
return msg_getTimestamp(m);
|
||||
}
|
||||
|
||||
HV_EXPORT void hv_msg_setTimestamp(HvMessage *m, hv_uint32_t timestamp) {
|
||||
msg_setTimestamp(m, timestamp);
|
||||
}
|
||||
|
||||
HV_EXPORT bool hv_msg_isBang(const HvMessage *const m, int i) {
|
||||
return msg_isBang(m,i);
|
||||
}
|
||||
|
||||
HV_EXPORT void hv_msg_setBang(HvMessage *m, int i) {
|
||||
msg_setBang(m,i);
|
||||
}
|
||||
|
||||
HV_EXPORT bool hv_msg_isFloat(const HvMessage *const m, int i) {
|
||||
return msg_isFloat(m, i);
|
||||
}
|
||||
|
||||
HV_EXPORT float hv_msg_getFloat(const HvMessage *const m, int i) {
|
||||
return msg_getFloat(m,i);
|
||||
}
|
||||
|
||||
HV_EXPORT void hv_msg_setFloat(HvMessage *m, int i, float f) {
|
||||
msg_setFloat(m,i,f);
|
||||
}
|
||||
|
||||
HV_EXPORT bool hv_msg_isSymbol(const HvMessage *const m, int i) {
|
||||
return msg_isSymbol(m,i);
|
||||
}
|
||||
|
||||
HV_EXPORT const char *hv_msg_getSymbol(const HvMessage *const m, int i) {
|
||||
return msg_getSymbol(m,i);
|
||||
}
|
||||
|
||||
HV_EXPORT void hv_msg_setSymbol(HvMessage *m, int i, const char *s) {
|
||||
msg_setSymbol(m,i,s);
|
||||
}
|
||||
|
||||
HV_EXPORT bool hv_msg_isHash(const HvMessage *const m, int i) {
|
||||
return msg_isHash(m, i);
|
||||
}
|
||||
|
||||
HV_EXPORT hv_uint32_t hv_msg_getHash(const HvMessage *const m, int i) {
|
||||
return msg_getHash(m, i);
|
||||
}
|
||||
|
||||
HV_EXPORT bool hv_msg_hasFormat(const HvMessage *const m, const char *fmt) {
|
||||
return msg_hasFormat(m, fmt);
|
||||
}
|
||||
|
||||
HV_EXPORT char *hv_msg_toString(const HvMessage *const m) {
|
||||
return msg_toString(m);
|
||||
}
|
||||
|
||||
HV_EXPORT HvMessage *hv_msg_copy(const HvMessage *const m) {
|
||||
return msg_copy(m);
|
||||
}
|
||||
|
||||
HV_EXPORT void hv_msg_free(HvMessage *m) {
|
||||
msg_free(m);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#if HV_APPLE
|
||||
#pragma mark - Heavy Common
|
||||
#endif
|
||||
|
||||
HV_EXPORT int hv_getSize(HeavyContextInterface *c) {
|
||||
hv_assert(c != nullptr);
|
||||
return (int) c->getSize();
|
||||
}
|
||||
|
||||
HV_EXPORT double hv_getSampleRate(HeavyContextInterface *c) {
|
||||
hv_assert(c != nullptr);
|
||||
return c->getSampleRate();
|
||||
}
|
||||
|
||||
HV_EXPORT int hv_getNumInputChannels(HeavyContextInterface *c) {
|
||||
hv_assert(c != nullptr);
|
||||
return c->getNumInputChannels();
|
||||
}
|
||||
|
||||
HV_EXPORT int hv_getNumOutputChannels(HeavyContextInterface *c) {
|
||||
hv_assert(c != nullptr);
|
||||
return c->getNumOutputChannels();
|
||||
}
|
||||
|
||||
HV_EXPORT void hv_setPrintHook(HeavyContextInterface *c, HvPrintHook_t *f) {
|
||||
hv_assert(c != nullptr);
|
||||
c->setPrintHook(f);
|
||||
}
|
||||
|
||||
HV_EXPORT HvPrintHook_t *hv_getPrintHook(HeavyContextInterface *c) {
|
||||
hv_assert(c != nullptr);
|
||||
return c->getPrintHook();
|
||||
}
|
||||
|
||||
HV_EXPORT void hv_setSendHook(HeavyContextInterface *c, HvSendHook_t *f) {
|
||||
hv_assert(c != nullptr);
|
||||
c->setSendHook(f);
|
||||
}
|
||||
|
||||
HV_EXPORT hv_uint32_t hv_stringToHash(const char *s) {
|
||||
return hv_string_to_hash(s);
|
||||
}
|
||||
|
||||
HV_EXPORT bool hv_sendBangToReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash) {
|
||||
hv_assert(c != nullptr);
|
||||
return c->sendBangToReceiver(receiverHash);
|
||||
}
|
||||
|
||||
HV_EXPORT bool hv_sendFloatToReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, float x) {
|
||||
hv_assert(c != nullptr);
|
||||
return c->sendFloatToReceiver(receiverHash, x);
|
||||
}
|
||||
|
||||
HV_EXPORT bool hv_sendSymbolToReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, char *s) {
|
||||
hv_assert(c != nullptr);
|
||||
return c->sendSymbolToReceiver(receiverHash, s);
|
||||
}
|
||||
|
||||
HV_EXPORT bool hv_sendMessageToReceiverV(
|
||||
HeavyContextInterface *c, hv_uint32_t receiverHash, double delayMs, const char *format, ...) {
|
||||
hv_assert(c != nullptr);
|
||||
hv_assert(delayMs >= 0.0);
|
||||
hv_assert(format != nullptr);
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
const int numElem = (int) hv_strlen(format);
|
||||
HvMessage *m = HV_MESSAGE_ON_STACK(numElem);
|
||||
msg_init(m, numElem, c->getCurrentSample() + (hv_uint32_t) (hv_max_d(0.0, delayMs)*c->getSampleRate()/1000.0));
|
||||
for (int i = 0; i < numElem; i++) {
|
||||
switch (format[i]) {
|
||||
case 'b': msg_setBang(m, i); break;
|
||||
case 'f': msg_setFloat(m, i, (float) va_arg(ap, double)); break;
|
||||
case 'h': msg_setHash(m, i, (int) va_arg(ap, int)); break;
|
||||
case 's': msg_setSymbol(m, i, (char *) va_arg(ap, char *)); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
return c->sendMessageToReceiver(receiverHash, delayMs, m);
|
||||
}
|
||||
|
||||
HV_EXPORT bool hv_sendMessageToReceiver(
|
||||
HeavyContextInterface *c, hv_uint32_t receiverHash, double delayMs, HvMessage *m) {
|
||||
hv_assert(c != nullptr);
|
||||
return c->sendMessageToReceiver(receiverHash, delayMs, m);
|
||||
}
|
||||
|
||||
HV_EXPORT void hv_cancelMessage(HeavyContextInterface *c, HvMessage *m, void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) {
|
||||
hv_assert(c != nullptr);
|
||||
c->cancelMessage(m, sendMessage);
|
||||
}
|
||||
|
||||
HV_EXPORT const char *hv_getName(HeavyContextInterface *c) {
|
||||
hv_assert(c != nullptr);
|
||||
return c->getName();
|
||||
}
|
||||
|
||||
HV_EXPORT void hv_setUserData(HeavyContextInterface *c, void *userData) {
|
||||
hv_assert(c != nullptr);
|
||||
c->setUserData(userData);
|
||||
}
|
||||
|
||||
HV_EXPORT void *hv_getUserData(HeavyContextInterface *c) {
|
||||
hv_assert(c != nullptr);
|
||||
return c->getUserData();
|
||||
}
|
||||
|
||||
HV_EXPORT double hv_getCurrentTime(HeavyContextInterface *c) {
|
||||
hv_assert(c != nullptr);
|
||||
return (double) c->samplesToMilliseconds(c->getCurrentSample());
|
||||
}
|
||||
|
||||
HV_EXPORT hv_uint32_t hv_getCurrentSample(HeavyContextInterface *c) {
|
||||
hv_assert(c != nullptr);
|
||||
return c->getCurrentSample();
|
||||
}
|
||||
|
||||
HV_EXPORT float hv_samplesToMilliseconds(HeavyContextInterface *c, hv_uint32_t numSamples) {
|
||||
hv_assert(c != nullptr);
|
||||
return c->samplesToMilliseconds(numSamples);
|
||||
}
|
||||
|
||||
HV_EXPORT hv_uint32_t hv_millisecondsToSamples(HeavyContextInterface *c, float ms) {
|
||||
hv_assert(c != nullptr);
|
||||
return c->millisecondsToSamples(ms);
|
||||
}
|
||||
|
||||
HV_EXPORT int hv_getParameterInfo(HeavyContextInterface *c, int index, HvParameterInfo *info) {
|
||||
hv_assert(c != nullptr);
|
||||
return c->getParameterInfo(index, info);
|
||||
}
|
||||
|
||||
HV_EXPORT void hv_lock_acquire(HeavyContextInterface *c) {
|
||||
hv_assert(c != nullptr);
|
||||
c->lockAcquire();
|
||||
}
|
||||
|
||||
HV_EXPORT bool hv_lock_try(HeavyContextInterface *c) {
|
||||
hv_assert(c != nullptr);
|
||||
return c->lockTry();
|
||||
}
|
||||
|
||||
HV_EXPORT void hv_lock_release(HeavyContextInterface *c) {
|
||||
hv_assert(c != nullptr);
|
||||
c->lockRelease();
|
||||
}
|
||||
|
||||
HV_EXPORT void hv_setInputMessageQueueSize(HeavyContextInterface *c, hv_uint32_t inQueueKb) {
|
||||
hv_assert(c != nullptr);
|
||||
c->setInputMessageQueueSize(inQueueKb);
|
||||
}
|
||||
|
||||
HV_EXPORT void hv_setOutputMessageQueueSize(HeavyContextInterface *c, hv_uint32_t outQueueKb) {
|
||||
hv_assert(c != nullptr);
|
||||
c->setOutputMessageQueueSize(outQueueKb);
|
||||
}
|
||||
|
||||
HV_EXPORT bool hv_getNextSentMessage(HeavyContextInterface *c, hv_uint32_t *destinationHash, HvMessage *outMsg, hv_uint32_t msgLength) {
|
||||
hv_assert(c != nullptr);
|
||||
hv_assert(destinationHash != nullptr);
|
||||
hv_assert(outMsg != nullptr);
|
||||
return c->getNextSentMessage(destinationHash, outMsg, msgLength);
|
||||
}
|
||||
|
||||
|
||||
#if HV_APPLE
|
||||
#pragma mark - Heavy Common
|
||||
#endif
|
||||
|
||||
HV_EXPORT int hv_process(HeavyContextInterface *c, float **inputBuffers, float **outputBuffers, int n) {
|
||||
hv_assert(c != nullptr);
|
||||
return c->process(inputBuffers, outputBuffers, n);
|
||||
}
|
||||
|
||||
HV_EXPORT int hv_processInline(HeavyContextInterface *c, float *inputBuffers, float *outputBuffers, int n) {
|
||||
hv_assert(c != nullptr);
|
||||
return c->processInline(inputBuffers, outputBuffers, n);
|
||||
}
|
||||
|
||||
HV_EXPORT int hv_processInlineInterleaved(HeavyContextInterface *c, float *inputBuffers, float *outputBuffers, int n) {
|
||||
hv_assert(c != nullptr);
|
||||
return c->processInlineInterleaved(inputBuffers, outputBuffers, n);
|
||||
}
|
||||
|
||||
HV_EXPORT void hv_delete(HeavyContextInterface *c) {
|
||||
delete c;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,413 @@
|
||||
/**
|
||||
* Copyright (c) 2014-2018 Enzien Audio Ltd.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _HEAVY_H_
|
||||
#define _HEAVY_H_
|
||||
|
||||
#include "HvUtils.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef _HEAVY_DECLARATIONS_
|
||||
#define _HEAVY_DECLARATIONS_
|
||||
|
||||
#ifdef __cplusplus
|
||||
class HeavyContextInterface;
|
||||
#else
|
||||
typedef struct HeavyContextInterface HeavyContextInterface;
|
||||
#endif
|
||||
|
||||
typedef struct HvMessage HvMessage;
|
||||
|
||||
typedef enum {
|
||||
HV_PARAM_TYPE_PARAMETER_IN,
|
||||
HV_PARAM_TYPE_PARAMETER_OUT,
|
||||
HV_PARAM_TYPE_EVENT_IN,
|
||||
HV_PARAM_TYPE_EVENT_OUT
|
||||
} HvParameterType;
|
||||
|
||||
typedef struct HvParameterInfo {
|
||||
const char *name; // the human readable parameter name
|
||||
hv_uint32_t hash; // an integer identified used by heavy for this parameter
|
||||
HvParameterType type; // type of this parameter
|
||||
float minVal; // the minimum value of this parameter
|
||||
float maxVal; // the maximum value of this parameter
|
||||
float defaultVal; // the default value of this parameter
|
||||
} HvParameterInfo;
|
||||
|
||||
typedef void (HvSendHook_t) (HeavyContextInterface *context, const char *sendName, hv_uint32_t sendHash, const HvMessage *msg);
|
||||
typedef void (HvPrintHook_t) (HeavyContextInterface *context, const char *printName, const char *str, const HvMessage *msg);
|
||||
|
||||
#endif // _HEAVY_DECLARATIONS_
|
||||
|
||||
|
||||
|
||||
#if HV_APPLE
|
||||
#pragma mark - Heavy Context
|
||||
#endif
|
||||
|
||||
/** Deletes a patch instance. */
|
||||
void hv_delete(HeavyContextInterface *c);
|
||||
|
||||
|
||||
|
||||
#if HV_APPLE
|
||||
#pragma mark - Heavy Process
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Processes one block of samples for a patch instance. The buffer format is an array of float channel arrays.
|
||||
* If the context has not input or output channels, the respective argument may be NULL.
|
||||
* The number of samples to to tbe processed should be a multiple of 1, 4, or 8, depending on if
|
||||
* no, SSE or NEON, or AVX optimisation is being used, respectively.
|
||||
* e.g. [[LLLL][RRRR]]
|
||||
* This function support in-place processing.
|
||||
*
|
||||
* @return The number of samples processed.
|
||||
*
|
||||
* This function is NOT thread-safe. It is assumed that only the audio thread will execute this function.
|
||||
*/
|
||||
int hv_process(HeavyContextInterface *c, float **inputBuffers, float **outputBuffers, int n);
|
||||
|
||||
/**
|
||||
* Processes one block of samples for a patch instance. The buffer format is an uninterleaved float array of channels.
|
||||
* If the context has not input or output channels, the respective argument may be NULL.
|
||||
* The number of samples to to tbe processed should be a multiple of 1, 4, or 8, depending on if
|
||||
* no, SSE or NEON, or AVX optimisation is being used, respectively.
|
||||
* e.g. [LLLLRRRR]
|
||||
* This function support in-place processing.
|
||||
*
|
||||
* @return The number of samples processed.
|
||||
*
|
||||
* This function is NOT thread-safe. It is assumed that only the audio thread will execute this function.
|
||||
*/
|
||||
int hv_processInline(HeavyContextInterface *c, float *inputBuffers, float *outputBuffers, int n);
|
||||
|
||||
/**
|
||||
* Processes one block of samples for a patch instance. The buffer format is an interleaved float array of channels.
|
||||
* If the context has not input or output channels, the respective argument may be NULL.
|
||||
* The number of samples to to tbe processed should be a multiple of 1, 4, or 8, depending on if
|
||||
* no, SSE or NEON, or AVX optimisation is being used, respectively.
|
||||
* e.g. [LRLRLRLR]
|
||||
* This function support in-place processing.
|
||||
*
|
||||
* @return The number of samples processed.
|
||||
*
|
||||
* This function is NOT thread-safe. It is assumed that only the audio thread will execute this function.
|
||||
*/
|
||||
int hv_processInlineInterleaved(HeavyContextInterface *c, float *inputBuffers, float *outputBuffers, int n);
|
||||
|
||||
|
||||
|
||||
#if HV_APPLE
|
||||
#pragma mark - Heavy Common
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Returns the total size in bytes of the context.
|
||||
* This value may change if tables are resized.
|
||||
*/
|
||||
int hv_getSize(HeavyContextInterface *c);
|
||||
|
||||
/** Returns the sample rate with which this context has been configured. */
|
||||
double hv_getSampleRate(HeavyContextInterface *c);
|
||||
|
||||
/** Returns the number of input channels with which this context has been configured. */
|
||||
int hv_getNumInputChannels(HeavyContextInterface *c);
|
||||
|
||||
/** Returns the number of output channels with which this context has been configured. */
|
||||
int hv_getNumOutputChannels(HeavyContextInterface *c);
|
||||
|
||||
/** Set the print hook. The function is called whenever a message is sent to a print object. */
|
||||
void hv_setPrintHook(HeavyContextInterface *c, HvPrintHook_t *f);
|
||||
|
||||
/** Returns the print hook, or NULL. */
|
||||
HvPrintHook_t *hv_getPrintHook(HeavyContextInterface *c);
|
||||
|
||||
/**
|
||||
* Set the send hook. The function is called whenever a message is sent to any send object.
|
||||
* Messages returned by this function should NEVER be freed. If the message must persist, call
|
||||
* hv_msg_copy() first.
|
||||
*/
|
||||
void hv_setSendHook(HeavyContextInterface *c, HvSendHook_t *f);
|
||||
|
||||
/** Returns a 32-bit hash of any string. Returns 0 if string is NULL. */
|
||||
hv_uint32_t hv_stringToHash(const char *s);
|
||||
|
||||
/**
|
||||
* A convenience function to send a bang to a receiver to be processed immediately.
|
||||
* The receiver is addressed with its hash, which can also be determined using hv_stringToHash().
|
||||
* This function is thread-safe.
|
||||
*
|
||||
* @return True if the message was accepted. False if the message could not fit onto
|
||||
* the message queue to be processed this block.
|
||||
*/
|
||||
bool hv_sendBangToReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash);
|
||||
|
||||
/**
|
||||
* A convenience function to send a float to a receiver to be processed immediately.
|
||||
* The receiver is addressed with its hash, which can also be determined using hv_stringToHash().
|
||||
* This function is thread-safe.
|
||||
*
|
||||
* @return True if the message was accepted. False if the message could not fit onto
|
||||
* the message queue to be processed this block.
|
||||
*/
|
||||
bool hv_sendFloatToReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, const float x);
|
||||
|
||||
/**
|
||||
* A convenience function to send a symbol to a receiver to be processed immediately.
|
||||
* The receiver is addressed with its hash, which can also be determined using hv_stringToHash().
|
||||
* This function is thread-safe.
|
||||
*
|
||||
* @return True if the message was accepted. False if the message could not fit onto
|
||||
* the message queue to be processed this block.
|
||||
*/
|
||||
bool hv_sendSymbolToReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, char *s);
|
||||
|
||||
/**
|
||||
* Sends a formatted message to a receiver that can be scheduled for the future.
|
||||
* The receiver is addressed with its hash, which can also be determined using hv_stringToHash().
|
||||
* This function is thread-safe.
|
||||
*
|
||||
* @return True if the message was accepted. False if the message could not fit onto
|
||||
* the message queue to be processed this block.
|
||||
*/
|
||||
bool hv_sendMessageToReceiverV(HeavyContextInterface *c, hv_uint32_t receiverHash, double delayMs, const char *format, ...);
|
||||
|
||||
/**
|
||||
* Sends a message to a receiver that can be scheduled for the future.
|
||||
* The receiver is addressed with its hash, which can also be determined using hv_stringToHash().
|
||||
* This function is thread-safe.
|
||||
*
|
||||
* @return True if the message was accepted. False if the message could not fit onto
|
||||
* the message queue to be processed this block.
|
||||
*/
|
||||
bool hv_sendMessageToReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, double delayMs, HvMessage *m);
|
||||
|
||||
/**
|
||||
* Cancels a previously scheduled message.
|
||||
*
|
||||
* @param sendMessage May be NULL.
|
||||
*/
|
||||
void hv_cancelMessage(HeavyContextInterface *c, HvMessage *m, void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *));
|
||||
|
||||
/** Returns the read-only user-assigned name of this patch. */
|
||||
const char *hv_getName(HeavyContextInterface *c);
|
||||
|
||||
/** Sets a user-definable value. This value is never manipulated by Heavy. */
|
||||
void hv_setUserData(HeavyContextInterface *c, void *userData);
|
||||
|
||||
/** Returns the user-defined data. */
|
||||
void *hv_getUserData(HeavyContextInterface *c);
|
||||
|
||||
/** Returns the current patch time in milliseconds. This value may have rounding errors. */
|
||||
double hv_getCurrentTime(HeavyContextInterface *c);
|
||||
|
||||
/** Returns the current patch time in samples. This value is always exact. */
|
||||
hv_uint32_t hv_getCurrentSample(HeavyContextInterface *c);
|
||||
|
||||
/**
|
||||
* Returns information about each parameter such as name, hash, and range.
|
||||
* The total number of parameters is always returned.
|
||||
*
|
||||
* @param index The parameter index.
|
||||
* @param info A pointer to a HvParameterInfo struct. May be null.
|
||||
*
|
||||
* @return The total number of parameters.
|
||||
*/
|
||||
int hv_getParameterInfo(HeavyContextInterface *c, int index, HvParameterInfo *info);
|
||||
|
||||
/** */
|
||||
float hv_samplesToMilliseconds(HeavyContextInterface *c, hv_uint32_t numSamples);
|
||||
|
||||
/** Converts milliseconds to samples. Input is limited to non-negative range. */
|
||||
hv_uint32_t hv_millisecondsToSamples(HeavyContextInterface *c, float ms);
|
||||
|
||||
/**
|
||||
* Acquire the input message queue lock.
|
||||
*
|
||||
* This function will block until the message lock as been acquired.
|
||||
* Typical applications will not require the use of this function.
|
||||
*
|
||||
* @param c A Heavy context.
|
||||
*/
|
||||
void hv_lock_acquire(HeavyContextInterface *c);
|
||||
|
||||
/**
|
||||
* Try to acquire the input message queue lock.
|
||||
*
|
||||
* If the lock has been acquired, hv_lock_release() must be called to release it.
|
||||
* Typical applications will not require the use of this function.
|
||||
*
|
||||
* @param c A Heavy context.
|
||||
*
|
||||
* @return Returns true if the lock has been acquired, false otherwise.
|
||||
*/
|
||||
bool hv_lock_try(HeavyContextInterface *c);
|
||||
|
||||
/**
|
||||
* Release the input message queue lock.
|
||||
*
|
||||
* Typical applications will not require the use of this function.
|
||||
*
|
||||
* @param c A Heavy context.
|
||||
*/
|
||||
void hv_lock_release(HeavyContextInterface *c);
|
||||
|
||||
/**
|
||||
* Set the size of the input message queue in kilobytes.
|
||||
*
|
||||
* The buffer is reset and all existing contents are lost on resize.
|
||||
*
|
||||
* @param c A Heavy context.
|
||||
* @param inQueueKb Must be positive i.e. at least one.
|
||||
*/
|
||||
void hv_setInputMessageQueueSize(HeavyContextInterface *c, hv_uint32_t inQueueKb);
|
||||
|
||||
/**
|
||||
* Set the size of the output message queue in kilobytes.
|
||||
*
|
||||
* The buffer is reset and all existing contents are lost on resize.
|
||||
* Only the default sendhook uses the outgoing message queue. If the default
|
||||
* sendhook is not being used, then this function is not useful.
|
||||
*
|
||||
* @param c A Heavy context.
|
||||
* @param outQueueKb Must be postive i.e. at least one.
|
||||
*/
|
||||
void hv_setOutputMessageQueueSize(HeavyContextInterface *c, hv_uint32_t outQueueKb);
|
||||
|
||||
/**
|
||||
* Get the next message in the outgoing queue, will also consume the message.
|
||||
* Returns false if there are no messages.
|
||||
*
|
||||
* @param c A Heavy context.
|
||||
* @param destinationHash a hash of the name of the receiver the message was sent to.
|
||||
* @param outMsg message pointer that is filled by the next message contents.
|
||||
* @param msgLength length of outMsg in bytes.
|
||||
*
|
||||
* @return True if there is a message in the outgoing queue.
|
||||
*/
|
||||
bool hv_getNextSentMessage(HeavyContextInterface *c, hv_uint32_t *destinationHash, HvMessage *outMsg, hv_uint32_t msgLength);
|
||||
|
||||
|
||||
|
||||
#if HV_APPLE
|
||||
#pragma mark - Heavy Message
|
||||
#endif
|
||||
|
||||
typedef struct HvMessage HvMessage;
|
||||
|
||||
/** Returns the total size in bytes of a HvMessage with a number of elements on the heap. */
|
||||
unsigned long hv_msg_getByteSize(hv_uint32_t numElements);
|
||||
|
||||
/** Initialise a HvMessage structure with the number of elements and a timestamp (in samples). */
|
||||
void hv_msg_init(HvMessage *m, int numElements, hv_uint32_t timestamp);
|
||||
|
||||
/** Returns the number of elements in this message. */
|
||||
unsigned long hv_msg_getNumElements(const HvMessage *m);
|
||||
|
||||
/** Returns the time at which this message exists (in samples). */
|
||||
hv_uint32_t hv_msg_getTimestamp(const HvMessage *m);
|
||||
|
||||
/** Set the time at which this message should be executed (in samples). */
|
||||
void hv_msg_setTimestamp(HvMessage *m, hv_uint32_t timestamp);
|
||||
|
||||
/** Returns true of the indexed element is a bang. False otherwise. Index is not bounds checked. */
|
||||
bool hv_msg_isBang(const HvMessage *const m, int i);
|
||||
|
||||
/** Sets the indexed element to a bang. Index is not bounds checked. */
|
||||
void hv_msg_setBang(HvMessage *m, int i);
|
||||
|
||||
/** Returns true of the indexed element is a float. False otherwise. Index is not bounds checked. */
|
||||
bool hv_msg_isFloat(const HvMessage *const m, int i);
|
||||
|
||||
/** Returns the indexed element as a float value. Index is not bounds checked. */
|
||||
float hv_msg_getFloat(const HvMessage *const m, int i);
|
||||
|
||||
/** Sets the indexed element to float value. Index is not bounds checked. */
|
||||
void hv_msg_setFloat(HvMessage *m, int i, float f);
|
||||
|
||||
/** Returns true of the indexed element is a symbol. False otherwise. Index is not bounds checked. */
|
||||
bool hv_msg_isSymbol(const HvMessage *const m, int i);
|
||||
|
||||
/** Returns the indexed element as a symbol value. Index is not bounds checked. */
|
||||
const char *hv_msg_getSymbol(const HvMessage *const m, int i);
|
||||
|
||||
/** Returns true of the indexed element is a hash. False otherwise. Index is not bounds checked. */
|
||||
bool hv_msg_isHash(const HvMessage *const m, int i);
|
||||
|
||||
/** Returns the indexed element as a hash value. Index is not bounds checked. */
|
||||
hv_uint32_t hv_msg_getHash(const HvMessage *const m, int i);
|
||||
|
||||
/** Sets the indexed element to symbol value. Index is not bounds checked. */
|
||||
void hv_msg_setSymbol(HvMessage *m, int i, const char *s);
|
||||
|
||||
/**
|
||||
* Returns true if the message has the given format, in number of elements and type. False otherwise.
|
||||
* Valid element types are:
|
||||
* 'b': bang
|
||||
* 'f': float
|
||||
* 's': symbol
|
||||
*
|
||||
* For example, a message with three floats would have a format of "fff". A single bang is "b".
|
||||
* A message with two symbols is "ss". These types can be mixed and matched in any way.
|
||||
*/
|
||||
bool hv_msg_hasFormat(const HvMessage *const m, const char *fmt);
|
||||
|
||||
/**
|
||||
* Returns a basic string representation of the message.
|
||||
* The character array MUST be deallocated by the caller.
|
||||
*/
|
||||
char *hv_msg_toString(const HvMessage *const m);
|
||||
|
||||
/** Copy a message onto the stack. The message persists. */
|
||||
HvMessage *hv_msg_copy(const HvMessage *const m);
|
||||
|
||||
/** Free a copied message. */
|
||||
void hv_msg_free(HvMessage *m);
|
||||
|
||||
|
||||
|
||||
#if HV_APPLE
|
||||
#pragma mark - Heavy Table
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Resizes the table to the given length.
|
||||
*
|
||||
* Existing contents are copied to the new table. Remaining space is cleared
|
||||
* if the table is longer than the original, truncated otherwise.
|
||||
*
|
||||
* @param tableHash The table identifier.
|
||||
* @param newSampleLength The new length of the table, in samples. Must be positive.
|
||||
*
|
||||
* @return False if the table could not be found. True otherwise.
|
||||
*/
|
||||
bool hv_table_setLength(HeavyContextInterface *c, hv_uint32_t tableHash, hv_uint32_t newSampleLength);
|
||||
|
||||
/** Returns a pointer to the raw buffer backing this table. DO NOT free it. */
|
||||
float *hv_table_getBuffer(HeavyContextInterface *c, hv_uint32_t tableHash);
|
||||
|
||||
/** Returns the length of this table in samples. */
|
||||
hv_uint32_t hv_table_getLength(HeavyContextInterface *c, hv_uint32_t tableHash);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // _HEAVY_H_
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Copyright (c) 2014-2018 Enzien Audio Ltd.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _HEAVY_INTERNAL_H_
|
||||
#define _HEAVY_INTERNAL_H_
|
||||
|
||||
#include "HvHeavy.h"
|
||||
#include "HvUtils.h"
|
||||
#include "HvTable.h"
|
||||
#include "HvMessage.h"
|
||||
#include "HvMath.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
HvTable *hv_table_get(HeavyContextInterface *c, hv_uint32_t tableHash);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void hv_scheduleMessageForReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, HvMessage *m);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
HvMessage *hv_scheduleMessageForObject(HeavyContextInterface *c, const HvMessage *m,
|
||||
void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *),
|
||||
int letIndex);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,136 @@
|
||||
/**
|
||||
* Copyright (c) 2014-2018 Enzien Audio Ltd.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "HvLightPipe.h"
|
||||
|
||||
#if __SSE__ || HV_SIMD_SSE
|
||||
#include <xmmintrin.h>
|
||||
#define hv_sfence() _mm_sfence()
|
||||
#elif __arm__ || HV_SIMD_NEON
|
||||
#if __ARM_ACLE
|
||||
#include <arm_acle.h>
|
||||
// https://msdn.microsoft.com/en-us/library/hh875058.aspx#BarrierRestrictions
|
||||
// http://doxygen.reactos.org/d8/d47/armintr_8h_a02be7ec76ca51842bc90d9b466b54752.html
|
||||
#define hv_sfence() __dmb(0xE) /* _ARM_BARRIER_ST */
|
||||
#elif defined(__GNUC__)
|
||||
#define hv_sfence() __asm__ volatile ("dmb 0xE":::"memory")
|
||||
#else
|
||||
// http://stackoverflow.com/questions/19965076/gcc-memory-barrier-sync-synchronize-vs-asm-volatile-memory
|
||||
#define hv_sfence() __sync_synchronize()
|
||||
#endif
|
||||
#elif HV_WIN
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms684208(v=vs.85).aspx
|
||||
#define hv_sfence() _WriteBarrier()
|
||||
#else
|
||||
#define hv_sfence() __asm__ volatile("" : : : "memory")
|
||||
#endif
|
||||
|
||||
#define HLP_STOP 0
|
||||
#define HLP_LOOP 0xFFFFFFFF
|
||||
#define HLP_SET_UINT32_AT_BUFFER(a, b) (*((hv_uint32_t *) (a)) = (b))
|
||||
#define HLP_GET_UINT32_AT_BUFFER(a) (*((hv_uint32_t *) (a)))
|
||||
|
||||
hv_uint32_t hLp_init(HvLightPipe *q, hv_uint32_t numBytes) {
|
||||
if (numBytes > 0) {
|
||||
q->buffer = (char *) hv_malloc(numBytes);
|
||||
hv_assert(q->buffer != NULL);
|
||||
HLP_SET_UINT32_AT_BUFFER(q->buffer, HLP_STOP);
|
||||
} else {
|
||||
q->buffer = NULL;
|
||||
}
|
||||
q->writeHead = q->buffer;
|
||||
q->readHead = q->buffer;
|
||||
q->len = numBytes;
|
||||
q->remainingBytes = numBytes;
|
||||
return numBytes;
|
||||
}
|
||||
|
||||
void hLp_free(HvLightPipe *q) {
|
||||
hv_free(q->buffer);
|
||||
}
|
||||
|
||||
hv_uint32_t hLp_hasData(HvLightPipe *q) {
|
||||
hv_uint32_t x = HLP_GET_UINT32_AT_BUFFER(q->readHead);
|
||||
if (x == HLP_LOOP) {
|
||||
q->readHead = q->buffer;
|
||||
x = HLP_GET_UINT32_AT_BUFFER(q->readHead);
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
char *hLp_getWriteBuffer(HvLightPipe *q, hv_uint32_t bytesToWrite) {
|
||||
char *const readHead = q->readHead;
|
||||
char *const oldWriteHead = q->writeHead;
|
||||
const hv_uint32_t totalByteRequirement = bytesToWrite + 2*sizeof(hv_uint32_t);
|
||||
|
||||
// check if there is enough space to write the data in the remaining
|
||||
// length of the buffer
|
||||
if (totalByteRequirement <= q->remainingBytes) {
|
||||
char *const newWriteHead = oldWriteHead + sizeof(hv_uint32_t) + bytesToWrite;
|
||||
|
||||
// check if writing would overwrite existing data in the pipe (return NULL if so)
|
||||
if ((oldWriteHead < readHead) && (newWriteHead >= readHead)) return NULL;
|
||||
else return (oldWriteHead + sizeof(hv_uint32_t));
|
||||
} else {
|
||||
// there isn't enough space, try looping around to the start
|
||||
if (totalByteRequirement <= q->len) {
|
||||
if ((oldWriteHead < readHead) || ((q->buffer + totalByteRequirement) > readHead)) {
|
||||
return NULL; // overwrite condition
|
||||
} else {
|
||||
q->writeHead = q->buffer;
|
||||
q->remainingBytes = q->len;
|
||||
HLP_SET_UINT32_AT_BUFFER(q->buffer, HLP_STOP);
|
||||
hv_sfence();
|
||||
HLP_SET_UINT32_AT_BUFFER(oldWriteHead, HLP_LOOP);
|
||||
return q->buffer + sizeof(hv_uint32_t);
|
||||
}
|
||||
} else {
|
||||
return NULL; // there isn't enough space to write the data
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void hLp_produce(HvLightPipe *q, hv_uint32_t numBytes) {
|
||||
hv_assert(q->remainingBytes >= (numBytes + 2*sizeof(hv_uint32_t)));
|
||||
q->remainingBytes -= (sizeof(hv_uint32_t) + numBytes);
|
||||
char *const oldWriteHead = q->writeHead;
|
||||
q->writeHead += (sizeof(hv_uint32_t) + numBytes);
|
||||
HLP_SET_UINT32_AT_BUFFER(q->writeHead, HLP_STOP);
|
||||
|
||||
// save everything before this point to memory
|
||||
hv_sfence();
|
||||
|
||||
// then save this
|
||||
HLP_SET_UINT32_AT_BUFFER(oldWriteHead, numBytes);
|
||||
}
|
||||
|
||||
char *hLp_getReadBuffer(HvLightPipe *q, hv_uint32_t *numBytes) {
|
||||
*numBytes = HLP_GET_UINT32_AT_BUFFER(q->readHead);
|
||||
char *const readBuffer = q->readHead + sizeof(hv_uint32_t);
|
||||
return readBuffer;
|
||||
}
|
||||
|
||||
void hLp_consume(HvLightPipe *q) {
|
||||
hv_assert(HLP_GET_UINT32_AT_BUFFER(q->readHead) != HLP_STOP);
|
||||
q->readHead += sizeof(hv_uint32_t) + HLP_GET_UINT32_AT_BUFFER(q->readHead);
|
||||
}
|
||||
|
||||
void hLp_reset(HvLightPipe *q) {
|
||||
q->writeHead = q->buffer;
|
||||
q->readHead = q->buffer;
|
||||
q->remainingBytes = q->len;
|
||||
memset(q->buffer, 0, q->len);
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/**
|
||||
* Copyright (c) 2014-2018 Enzien Audio Ltd.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _HEAVY_LIGHTPIPE_H_
|
||||
#define _HEAVY_LIGHTPIPE_H_
|
||||
|
||||
#include "HvUtils.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This pipe assumes that there is only one producer thread and one consumer
|
||||
* thread. This data structure does not support any other configuration.
|
||||
*/
|
||||
typedef struct HvLightPipe {
|
||||
char *buffer;
|
||||
char *writeHead;
|
||||
char *readHead;
|
||||
hv_uint32_t len;
|
||||
hv_uint32_t remainingBytes; // total bytes from write head to end
|
||||
} HvLightPipe;
|
||||
|
||||
/**
|
||||
* Initialise the pipe with a given length, in bytes.
|
||||
* @return Returns the size of the pipe in bytes.
|
||||
*/
|
||||
hv_uint32_t hLp_init(HvLightPipe *q, hv_uint32_t numBytes);
|
||||
|
||||
/**
|
||||
* Frees the internal buffer.
|
||||
* @param q The light pipe.
|
||||
*/
|
||||
void hLp_free(HvLightPipe *q);
|
||||
|
||||
/**
|
||||
* Indicates if data is available for reading.
|
||||
* @param q The light pipe.
|
||||
*
|
||||
* @return Returns the number of bytes available for reading. Zero if no bytes
|
||||
* are available.
|
||||
*/
|
||||
hv_uint32_t hLp_hasData(HvLightPipe *q);
|
||||
|
||||
/**
|
||||
* Returns a pointer to a location in the pipe where numBytes can be written.
|
||||
*
|
||||
* @param numBytes The number of bytes to be written.
|
||||
* @return A pointer to a location where those bytes can be written. Returns
|
||||
* NULL if no more space is available. Successive calls to this
|
||||
* function may eventually return a valid pointer because the readhead
|
||||
* has been advanced on another thread.
|
||||
*/
|
||||
char *hLp_getWriteBuffer(HvLightPipe *q, hv_uint32_t numBytes);
|
||||
|
||||
/**
|
||||
* Indicates to the pipe how many bytes have been written.
|
||||
*
|
||||
* @param numBytes The number of bytes written. In general this should be the
|
||||
* same value as was passed to the preceeding call to
|
||||
* hLp_getWriteBuffer().
|
||||
*/
|
||||
void hLp_produce(HvLightPipe *q, hv_uint32_t numBytes);
|
||||
|
||||
/**
|
||||
* Returns the current read buffer, indicating the number of bytes available
|
||||
* for reading.
|
||||
* @param q The light pipe.
|
||||
* @param numBytes This value will be filled with the number of bytes available
|
||||
* for reading.
|
||||
*
|
||||
* @return A pointer to the read buffer.
|
||||
*/
|
||||
char *hLp_getReadBuffer(HvLightPipe *q, hv_uint32_t *numBytes);
|
||||
|
||||
/**
|
||||
* Indicates that the next set of bytes have been read and are no longer needed.
|
||||
* @param q The light pipe.
|
||||
*/
|
||||
void hLp_consume(HvLightPipe *q);
|
||||
|
||||
// resets the queue to it's initialised state
|
||||
// This should be done when only one thread is accessing the pipe.
|
||||
void hLp_reset(HvLightPipe *q);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _HEAVY_LIGHTPIPE_H_
|
||||
@@ -0,0 +1,736 @@
|
||||
/**
|
||||
* Copyright (c) 2014-2018 Enzien Audio Ltd.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _HEAVY_MATH_H_
|
||||
#define _HEAVY_MATH_H_
|
||||
|
||||
#include "HvUtils.h"
|
||||
|
||||
// https://software.intel.com/sites/landingpage/IntrinsicsGuide/
|
||||
// https://gcc.gnu.org/onlinedocs/gcc-4.8.1/gcc/ARM-NEON-Intrinsics.html
|
||||
// http://codesuppository.blogspot.co.uk/2015/02/sse2neonh-porting-guide-and-header-file.html
|
||||
|
||||
static inline void __hv_zero_f(hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_setzero_ps();
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_setzero_ps();
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vdupq_n_f32(0.0f);
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = 0.0f;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_zero_i(hv_bOuti_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_setzero_si256();
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_setzero_si128();
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vdupq_n_s32(0);
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_load_f(float *bIn, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_load_ps(bIn);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_load_ps(bIn);
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vld1q_f32(bIn);
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = *bIn;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_store_f(float *bOut, hv_bInf_t bIn) {
|
||||
#if HV_SIMD_AVX
|
||||
_mm256_store_ps(bOut, bIn);
|
||||
#elif HV_SIMD_SSE
|
||||
_mm_store_ps(bOut, bIn);
|
||||
#elif HV_SIMD_NEON
|
||||
vst1q_f32(bOut, bIn);
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = bIn;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_log2_f(hv_bInf_t bIn, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
hv_assert(0); // __hv_log2_f() not implemented
|
||||
#elif HV_SIMD_SSE
|
||||
// https://en.wikipedia.org/wiki/Fast_inverse_square_root
|
||||
__m128i a = _mm_castps_si128(bIn);
|
||||
__m128i b = _mm_srli_epi32(a, 23);
|
||||
__m128i c = _mm_sub_epi32(b, _mm_set1_epi32(127)); // exponent (int)
|
||||
__m128 d = _mm_cvtepi32_ps(c); // exponent (float)
|
||||
__m128i e = _mm_or_si128(_mm_andnot_si128(_mm_set1_epi32(0xFF800000), a), _mm_set1_epi32(0x3F800000));
|
||||
__m128 f = _mm_castsi128_ps(e); // 1+m (float)
|
||||
__m128 g = _mm_add_ps(d, f); // e + 1 + m
|
||||
__m128 h = _mm_add_ps(g, _mm_set1_ps(-0.9569643f)); // e + 1 + m + (sigma-1)
|
||||
*bOut = h;
|
||||
#elif HV_SIMD_NEON
|
||||
int32x4_t a = vreinterpretq_s32_f32(bIn);
|
||||
int32x4_t b = vshrq_n_s32(a, 23);
|
||||
int32x4_t c = vsubq_s32(b, vdupq_n_s32(127));
|
||||
float32x4_t d = vcvtq_f32_s32(c);
|
||||
int32x4_t e = vorrq_s32(vbicq_s32(a, vdupq_n_s32(0xFF800000)), vdupq_n_s32(0x3F800000));
|
||||
float32x4_t f = vreinterpretq_f32_s32(e);
|
||||
float32x4_t g = vaddq_f32(d, f);
|
||||
float32x4_t h = vaddq_f32(g, vdupq_n_f32(-0.9569643f));
|
||||
*bOut = h;
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = 1.442695040888963f * hv_log_f(bIn);
|
||||
#endif
|
||||
}
|
||||
|
||||
// NOTE(mhroth): this is a pretty ghetto implementation
|
||||
static inline void __hv_cos_f(hv_bInf_t bIn, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_set_ps(
|
||||
hv_cos_f(bIn[7]), hv_cos_f(bIn[6]), hv_cos_f(bIn[5]), hv_cos_f(bIn[4]),
|
||||
hv_cos_f(bIn[3]), hv_cos_f(bIn[2]), hv_cos_f(bIn[1]), hv_cos_f(bIn[0]));
|
||||
#elif HV_SIMD_SSE
|
||||
const float *const b = (float *) &bIn;
|
||||
*bOut = _mm_set_ps(hv_cos_f(b[3]), hv_cos_f(b[2]), hv_cos_f(b[1]), hv_cos_f(b[0]));
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = (float32x4_t) {hv_cos_f(bIn[0]), hv_cos_f(bIn[1]), hv_cos_f(bIn[2]), hv_cos_f(bIn[3])};
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = hv_cos_f(bIn);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_acos_f(hv_bInf_t bIn, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
hv_assert(0); // __hv_acos_f() not implemented
|
||||
#elif HV_SIMD_SSE
|
||||
hv_assert(0); // __hv_acos_f() not implemented
|
||||
#elif HV_SIMD_NEON
|
||||
hv_assert(0); // __hv_acos_f() not implemented
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = hv_acos_f(bIn);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_cosh_f(hv_bInf_t bIn, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
hv_assert(0); // __hv_cosh_f() not implemented
|
||||
#elif HV_SIMD_SSE
|
||||
hv_assert(0); // __hv_cosh_f() not implemented
|
||||
#elif HV_SIMD_NEON
|
||||
hv_assert(0); // __hv_cosh_f() not implemented
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = hv_cosh_f(bIn);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_acosh_f(hv_bInf_t bIn, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
hv_assert(0); // __hv_acosh_f() not implemented
|
||||
#elif HV_SIMD_SSE
|
||||
hv_assert(0); // __hv_acosh_f() not implemented
|
||||
#elif HV_SIMD_NEON
|
||||
hv_assert(0); // __hv_acosh_f() not implemented
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = hv_acosh_f(bIn);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_sin_f(hv_bInf_t bIn, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
hv_assert(0); // __hv_sin_f() not implemented
|
||||
#elif HV_SIMD_SSE
|
||||
hv_assert(0); // __hv_sin_f() not implemented
|
||||
#elif HV_SIMD_NEON
|
||||
hv_assert(0); // __hv_sin_f() not implemented
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = hv_sin_f(bIn);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_asin_f(hv_bInf_t bIn, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
hv_assert(0); // __hv_asin_f() not implemented
|
||||
#elif HV_SIMD_SSE
|
||||
hv_assert(0); // __hv_asin_f() not implemented
|
||||
#elif HV_SIMD_NEON
|
||||
hv_assert(0); // __hv_asin_f() not implemented
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = hv_asin_f(bIn);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_sinh_f(hv_bInf_t bIn, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
hv_assert(0); // __hv_sinh_f() not implemented
|
||||
#elif HV_SIMD_SSE
|
||||
hv_assert(0); // __hv_sinh_f() not implemented
|
||||
#elif HV_SIMD_NEON
|
||||
hv_assert(0); // __hv_sinh_f() not implemented
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = hv_sinh_f(bIn);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_asinh_f(hv_bInf_t bIn, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
hv_assert(0); // __hv_asinh_f() not implemented
|
||||
#elif HV_SIMD_SSE
|
||||
hv_assert(0); // __hv_asinh_f() not implemented
|
||||
#elif HV_SIMD_NEON
|
||||
hv_assert(0); // __hv_asinh_f() not implemented
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = hv_asinh_f(bIn);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_tan_f(hv_bInf_t bIn, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
hv_assert(0); // __hv_tan_f() not implemented
|
||||
#elif HV_SIMD_SSE
|
||||
hv_assert(0); // __hv_tan_f() not implemented
|
||||
#elif HV_SIMD_NEON
|
||||
hv_assert(0); // __hv_tan_f() not implemented
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = hv_tan_f(bIn);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_atan_f(hv_bInf_t bIn, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
hv_assert(0); // __hv_atan_f() not implemented
|
||||
#elif HV_SIMD_SSE
|
||||
hv_assert(0); // __hv_atan_f() not implemented
|
||||
#elif HV_SIMD_NEON
|
||||
hv_assert(0); // __hv_atan_f() not implemented
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = hv_atan_f(bIn);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_atan2_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
hv_assert(0); // __hv_atan2_f() not implemented
|
||||
#elif HV_SIMD_SSE
|
||||
hv_assert(0); // __hv_atan2_f() not implemented
|
||||
#elif HV_SIMD_NEON
|
||||
hv_assert(0); // __hv_atan2_f() not implemented
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = hv_atan2_f(bIn0, bIn1);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_tanh_f(hv_bInf_t bIn, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
hv_assert(0); // __hv_tanh_f() not implemented
|
||||
#elif HV_SIMD_SSE
|
||||
hv_assert(0); // __hv_tanh_f() not implemented
|
||||
#elif HV_SIMD_NEON
|
||||
hv_assert(0); // __hv_tanh_f() not implemented
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = hv_tanh_f(bIn);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_atanh_f(hv_bInf_t bIn, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
hv_assert(0); // __hv_atanh_f() not implemented
|
||||
#elif HV_SIMD_SSE
|
||||
hv_assert(0); // __hv_atanh_f() not implemented
|
||||
#elif HV_SIMD_NEON
|
||||
hv_assert(0); // __hv_atanh_f() not implemented
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = hv_atanh_f(bIn);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_sqrt_f(hv_bInf_t bIn, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_sqrt_ps(bIn);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_sqrt_ps(bIn);
|
||||
#elif HV_SIMD_NEON
|
||||
const float32x4_t y = vrsqrteq_f32(bIn);
|
||||
*bOut = vmulq_f32(bIn, vmulq_f32(vrsqrtsq_f32(vmulq_f32(bIn, y), y), y)); // numerical results may be inexact
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = hv_sqrt_f(bIn);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_rsqrt_f(hv_bInf_t bIn, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_rsqrt_ps(bIn);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_rsqrt_ps(bIn);
|
||||
#elif HV_SIMD_NEON
|
||||
const float32x4_t y = vrsqrteq_f32(bIn);
|
||||
*bOut = vmulq_f32(vrsqrtsq_f32(vmulq_f32(bIn, y), y), y); // numerical results may be inexact
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = 1.0f/hv_sqrt_f(bIn);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_abs_f(hv_bInf_t bIn, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_andnot_ps(_mm256_set1_ps(-0.0f), bIn);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_andnot_ps(_mm_set1_ps(-0.0f), bIn); // == 1 << 31
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vabsq_f32(bIn);
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = hv_abs_f(bIn);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_neg_f(hv_bInf_t bIn, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_xor_ps(bIn, _mm256_set1_ps(-0.0f));
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_xor_ps(bIn, _mm_set1_ps(-0.0f));
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vnegq_f32(bIn);
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = bIn * -1.0f;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_exp_f(hv_bInf_t bIn, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
float *const b = (float *) hv_alloca(HV_N_SIMD*sizeof(float));
|
||||
_mm256_store_ps(b, bIn);
|
||||
*bOut = _mm256_set_ps(
|
||||
hv_exp_f(b[7]), hv_exp_f(b[6]), hv_exp_f(b[5]), hv_exp_f(b[4]),
|
||||
hv_exp_f(b[3]), hv_exp_f(b[2]), hv_exp_f(b[1]), hv_exp_f(b[0]));
|
||||
#elif HV_SIMD_SSE
|
||||
float *const b = (float *) hv_alloca(HV_N_SIMD*sizeof(float));
|
||||
_mm_store_ps(b, bIn);
|
||||
*bOut = _mm_set_ps(hv_exp_f(b[3]), hv_exp_f(b[2]), hv_exp_f(b[1]), hv_exp_f(b[0]));
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = (float32x4_t) {
|
||||
hv_exp_f(bIn[0]),
|
||||
hv_exp_f(bIn[1]),
|
||||
hv_exp_f(bIn[2]),
|
||||
hv_exp_f(bIn[3])};
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = hv_exp_f(bIn);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_ceil_f(hv_bInf_t bIn, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_ceil_ps(bIn);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_ceil_ps(bIn);
|
||||
#elif HV_SIMD_NEON
|
||||
#if __ARM_ARCH >= 8
|
||||
*bOut = vrndpq_f32(bIn);
|
||||
#else
|
||||
// A slow NEON implementation of __hv_ceil_f() is being used because
|
||||
// the necessary intrinsic cannot be found. It is only available in ARMv8.
|
||||
*bOut = (float32x4_t) {hv_ceil_f(bIn[0]), hv_ceil_f(bIn[1]), hv_ceil_f(bIn[2]), hv_ceil_f(bIn[3])};
|
||||
#endif // vrndpq_f32
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = hv_ceil_f(bIn);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_floor_f(hv_bInf_t bIn, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_floor_ps(bIn);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_floor_ps(bIn);
|
||||
#elif HV_SIMD_NEON
|
||||
#if __ARM_ARCH >= 8
|
||||
*bOut = vrndmq_f32(bIn);
|
||||
#else
|
||||
// A slow implementation of __hv_floor_f() is being used because
|
||||
// the necessary intrinsic cannot be found. It is only available from ARMv8.
|
||||
*bOut = (float32x4_t) {hv_floor_f(bIn[0]), hv_floor_f(bIn[1]), hv_floor_f(bIn[2]), hv_floor_f(bIn[3])};
|
||||
#endif // vrndmq_f32
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = hv_floor_f(bIn);
|
||||
#endif
|
||||
}
|
||||
|
||||
// __add~f
|
||||
static inline void __hv_add_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_add_ps(bIn0, bIn1);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_add_ps(bIn0, bIn1);
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vaddq_f32(bIn0, bIn1);
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = bIn0 + bIn1;
|
||||
#endif
|
||||
}
|
||||
|
||||
// __add~i
|
||||
static inline void __hv_add_i(hv_bIni_t bIn0, hv_bIni_t bIn1, hv_bOuti_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
__m128i x = _mm_add_epi32(_mm256_castsi256_si128(bIn0), _mm256_castsi256_si128(bIn1));
|
||||
__m128i y = _mm_add_epi32(_mm256_extractf128_si256(bIn0, 1), _mm256_extractf128_si256(bIn1, 1));
|
||||
*bOut = _mm256_insertf128_si256(_mm256_castsi128_si256(x), y, 1);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_add_epi32(bIn0, bIn1);
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vaddq_s32(bIn0, bIn1);
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = bIn0 + bIn1;
|
||||
#endif
|
||||
}
|
||||
|
||||
// __sub~f
|
||||
static inline void __hv_sub_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_sub_ps(bIn0, bIn1);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_sub_ps(bIn0, bIn1);
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vsubq_f32(bIn0, bIn1);
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = bIn0 - bIn1;
|
||||
#endif
|
||||
}
|
||||
|
||||
// __mul~f
|
||||
static inline void __hv_mul_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_mul_ps(bIn0, bIn1);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_mul_ps(bIn0, bIn1);
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vmulq_f32(bIn0, bIn1);
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = bIn0 * bIn1;
|
||||
#endif
|
||||
}
|
||||
|
||||
// __*~i
|
||||
static inline void __hv_mul_i(hv_bIni_t bIn0, hv_bIni_t bIn1, hv_bOuti_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
__m128i x = _mm_mullo_epi32(_mm256_castsi256_si128(bIn0), _mm256_castsi256_si128(bIn1));
|
||||
__m128i y = _mm_mullo_epi32(_mm256_extractf128_si256(bIn0, 1), _mm256_extractf128_si256(bIn1, 1));
|
||||
*bOut = _mm256_insertf128_si256(_mm256_castsi128_si256(x), y, 1);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_mullo_epi32(bIn0, bIn1);
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vmulq_s32(bIn0, bIn1);
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = bIn0 * bIn1;
|
||||
#endif
|
||||
}
|
||||
|
||||
// __cast~if
|
||||
static inline void __hv_cast_if(hv_bIni_t bIn, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_cvtepi32_ps(bIn);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_cvtepi32_ps(bIn);
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vcvtq_f32_s32(bIn);
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = (float) bIn;
|
||||
#endif
|
||||
}
|
||||
|
||||
// __cast~fi
|
||||
static inline void __hv_cast_fi(hv_bInf_t bIn, hv_bOuti_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_cvtps_epi32(bIn);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_cvtps_epi32(bIn);
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vcvtq_s32_f32(bIn);
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = (int) bIn;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_div_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
__m256 a = _mm256_cmp_ps(bIn1, _mm256_setzero_ps(), _CMP_EQ_OQ);
|
||||
__m256 b = _mm256_div_ps(bIn0, bIn1);
|
||||
*bOut = _mm256_andnot_ps(a, b);
|
||||
#elif HV_SIMD_SSE
|
||||
__m128 a = _mm_cmpeq_ps(bIn1, _mm_setzero_ps());
|
||||
__m128 b = _mm_div_ps(bIn0, bIn1);
|
||||
*bOut = _mm_andnot_ps(a, b);
|
||||
#elif HV_SIMD_NEON
|
||||
uint32x4_t a = vceqq_f32(bIn1, vdupq_n_f32(0.0f));
|
||||
float32x4_t b = vmulq_f32(bIn0, vrecpeq_f32(bIn1)); // NOTE(mhroth): numerical results may be inexact
|
||||
*bOut = vreinterpretq_f32_u32(vbicq_u32(vreinterpretq_u32_f32(b), a));
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = (bIn1 != 0.0f) ? (bIn0 / bIn1) : 0.0f;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_min_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_min_ps(bIn0, bIn1);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_min_ps(bIn0, bIn1);
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vminq_f32(bIn0, bIn1);
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = hv_min_f(bIn0, bIn1);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_min_i(hv_bIni_t bIn0, hv_bIni_t bIn1, hv_bOuti_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
__m128i x = _mm_min_epi32(_mm256_castsi256_si128(bIn0), _mm256_castsi256_si128(bIn1));
|
||||
__m128i y = _mm_min_epi32(_mm256_extractf128_si256(bIn0, 1), _mm256_extractf128_si256(bIn1, 1));
|
||||
*bOut = _mm256_insertf128_si256(_mm256_castsi128_si256(x), y, 1);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_min_epi32(bIn0, bIn1);
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vminq_s32(bIn0, bIn1);
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = hv_min_i(bIn0, bIn1);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_max_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_max_ps(bIn0, bIn1);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_max_ps(bIn0, bIn1);
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vmaxq_f32(bIn0, bIn1);
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = hv_max_f(bIn0, bIn1);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_max_i(hv_bIni_t bIn0, hv_bIni_t bIn1, hv_bOuti_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
__m128i x = _mm_max_epi32(_mm256_castsi256_si128(bIn0), _mm256_castsi256_si128(bIn1));
|
||||
__m128i y = _mm_max_epi32(_mm256_extractf128_si256(bIn0, 1), _mm256_extractf128_si256(bIn1, 1));
|
||||
*bOut = _mm256_insertf128_si256(_mm256_castsi128_si256(x), y, 1);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_max_epi32(bIn0, bIn1);
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vmaxq_s32(bIn0, bIn1);
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = hv_max_i(bIn0, bIn1);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_pow_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
float *b = (float *) hv_alloca(16*sizeof(float));
|
||||
_mm256_store_ps(b, bIn0);
|
||||
_mm256_store_ps(b+8, bIn1);
|
||||
*bOut = _mm256_set_ps(
|
||||
hv_pow_f(b[7], b[15]),
|
||||
hv_pow_f(b[6], b[14]),
|
||||
hv_pow_f(b[5], b[13]),
|
||||
hv_pow_f(b[4], b[12]),
|
||||
hv_pow_f(b[3], b[11]),
|
||||
hv_pow_f(b[2], b[10]),
|
||||
hv_pow_f(b[1], b[9]),
|
||||
hv_pow_f(b[0], b[8]));
|
||||
#elif HV_SIMD_SSE
|
||||
float *b = (float *) hv_alloca(8*sizeof(float));
|
||||
_mm_store_ps(b, bIn0);
|
||||
_mm_store_ps(b+4, bIn1);
|
||||
*bOut = _mm_set_ps(
|
||||
hv_pow_f(b[3], b[7]),
|
||||
hv_pow_f(b[2], b[6]),
|
||||
hv_pow_f(b[1], b[5]),
|
||||
hv_pow_f(b[0], b[4]));
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = (float32x4_t) {
|
||||
hv_pow_f(bIn0[0], bIn1[0]),
|
||||
hv_pow_f(bIn0[1], bIn1[1]),
|
||||
hv_pow_f(bIn0[2], bIn1[2]),
|
||||
hv_pow_f(bIn0[3], bIn1[3])};
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = hv_pow_f(bIn0, bIn1);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_gt_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_cmp_ps(bIn0, bIn1, _CMP_GT_OQ);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_cmpgt_ps(bIn0, bIn1);
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vreinterpretq_f32_u32(vcgtq_f32(bIn0, bIn1));
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = (bIn0 > bIn1) ? 1.0f : 0.0f;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_gte_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_cmp_ps(bIn0, bIn1, _CMP_GE_OQ);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_cmpge_ps(bIn0, bIn1);
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vreinterpretq_f32_u32(vcgeq_f32(bIn0, bIn1));
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = (bIn0 >= bIn1) ? 1.0f : 0.0f;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_lt_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_cmp_ps(bIn0, bIn1, _CMP_LT_OQ);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_cmplt_ps(bIn0, bIn1);
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vreinterpretq_f32_u32(vcltq_f32(bIn0, bIn1));
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = (bIn0 < bIn1) ? 1.0f : 0.0f;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_lte_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_cmp_ps(bIn0, bIn1, _CMP_LE_OQ);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_cmple_ps(bIn0, bIn1);
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vreinterpretq_f32_u32(vcleq_f32(bIn0, bIn1));
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = (bIn0 <= bIn1) ? 1.0f : 0.0f;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_eq_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_cmp_ps(bIn0, bIn1, _CMP_EQ_OQ);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_cmpeq_ps(bIn0, bIn1);
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vreinterpretq_f32_u32(vceqq_f32(bIn0, bIn1));
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = (bIn0 == bIn1) ? 1.0f : 0.0f;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_neq_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_cmp_ps(bIn0, bIn1, _CMP_NEQ_OQ);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_cmpneq_ps(bIn0, bIn1);
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vreinterpretq_f32_u32(vmvnq_u32(vceqq_f32(bIn0, bIn1)));
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = (bIn0 != bIn1) ? 1.0f : 0.0f;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_or_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_or_ps(bIn1, bIn0);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_or_ps(bIn1, bIn0);
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vreinterpretq_f32_u32(vorrq_u32(vreinterpretq_u32_f32(bIn1), vreinterpretq_u32_f32(bIn0)));
|
||||
#else // HV_SIMD_NONE
|
||||
if (bIn0 == 0.0f && bIn1 == 0.0f) *bOut = 0.0f;
|
||||
else if (bIn0 == 0.0f) *bOut = bIn1;
|
||||
else if (bIn1 == 0.0f) *bOut = bIn0;
|
||||
else hv_assert(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_and_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_and_ps(bIn1, bIn0);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_and_ps(bIn1, bIn0);
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(bIn1), vreinterpretq_u32_f32(bIn0)));
|
||||
#else // HV_SIMD_NONE
|
||||
if (bIn0 == 0.0f || bIn1 == 0.0f) *bOut = 0.0f;
|
||||
else if (bIn0 == 1.0f) *bOut = bIn1;
|
||||
else if (bIn1 == 1.0f) *bOut = bIn0;
|
||||
else hv_assert(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_andnot_f(hv_bInf_t bIn0_mask, hv_bInf_t bIn1, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_andnot_ps(bIn0_mask, bIn1);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_andnot_ps(bIn0_mask, bIn1);
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vreinterpretq_f32_s32(vbicq_s32(vreinterpretq_s32_f32(bIn1), vreinterpretq_s32_f32(bIn0_mask)));
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = (bIn0_mask == 0.0f) ? bIn1 : 0.0f;
|
||||
#endif
|
||||
}
|
||||
|
||||
// bOut = (bIn0 * bIn1) + bIn2
|
||||
static inline void __hv_fma_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bInf_t bIn2, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
#if HV_SIMD_FMA
|
||||
*bOut = _mm256_fmadd_ps(bIn0, bIn1, bIn2);
|
||||
#else
|
||||
*bOut = _mm256_add_ps(_mm256_mul_ps(bIn0, bIn1), bIn2);
|
||||
#endif // HV_SIMD_FMA
|
||||
#elif HV_SIMD_SSE
|
||||
#if HV_SIMD_FMA
|
||||
*bOut = _mm_fmadd_ps(bIn0, bIn1, bIn2);
|
||||
#else
|
||||
*bOut = _mm_add_ps(_mm_mul_ps(bIn0, bIn1), bIn2);
|
||||
#endif // HV_SIMD_FMA
|
||||
#elif HV_SIMD_NEON
|
||||
#if __ARM_ARCH >= 8
|
||||
*bOut = vfmaq_f32(bIn2, bIn0, bIn1);
|
||||
#else
|
||||
// NOTE(mhroth): it turns out, fma SUUUUCKS on lesser ARM architectures
|
||||
*bOut = vaddq_f32(vmulq_f32(bIn0, bIn1), bIn2);
|
||||
#endif
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = hv_fma_f(bIn0, bIn1, bIn2);
|
||||
#endif
|
||||
}
|
||||
|
||||
// bOut = (bIn0 * bIn1) - bIn2
|
||||
static inline void __hv_fms_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bInf_t bIn2, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
#if HV_SIMD_FMA
|
||||
*bOut = _mm256_fmsub_ps(bIn0, bIn1, bIn2);
|
||||
#else
|
||||
*bOut = _mm256_sub_ps(_mm256_mul_ps(bIn0, bIn1), bIn2);
|
||||
#endif // HV_SIMD_FMA
|
||||
#elif HV_SIMD_SSE
|
||||
#if HV_SIMD_FMA
|
||||
*bOut = _mm_fmsub_ps(bIn0, bIn1, bIn2);
|
||||
#else
|
||||
*bOut = _mm_sub_ps(_mm_mul_ps(bIn0, bIn1), bIn2);
|
||||
#endif // HV_SIMD_FMA
|
||||
#elif HV_SIMD_NEON
|
||||
#if __ARM_ARCH >= 8
|
||||
*bOut = vfmsq_f32(bIn2, bIn0, bIn1);
|
||||
#else
|
||||
// NOTE(mhroth): it turns out, fma SUUUUCKS on lesser ARM architectures
|
||||
*bOut = vsubq_f32(vmulq_f32(bIn0, bIn1), bIn2);
|
||||
#endif
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = (bIn0 * bIn1) - bIn2;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // _HEAVY_MATH_H_
|
||||
@@ -0,0 +1,199 @@
|
||||
/**
|
||||
* Copyright (c) 2014-2018 Enzien Audio Ltd.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "HvMessage.h"
|
||||
|
||||
HvMessage *msg_init(HvMessage *m, hv_size_t numElements, hv_uint32_t timestamp) {
|
||||
m->timestamp = timestamp;
|
||||
m->numElements = (hv_uint16_t) numElements;
|
||||
m->numBytes = (hv_uint16_t) msg_getCoreSize(numElements);
|
||||
return m;
|
||||
}
|
||||
|
||||
HvMessage *msg_initWithFloat(HvMessage *m, hv_uint32_t timestamp, float f) {
|
||||
m->timestamp = timestamp;
|
||||
m->numElements = 1;
|
||||
m->numBytes = sizeof(HvMessage);
|
||||
msg_setFloat(m, 0, f);
|
||||
return m;
|
||||
}
|
||||
|
||||
HvMessage *msg_initWithBang(HvMessage *m, hv_uint32_t timestamp) {
|
||||
m->timestamp = timestamp;
|
||||
m->numElements = 1;
|
||||
m->numBytes = sizeof(HvMessage);
|
||||
msg_setBang(m, 0);
|
||||
return m;
|
||||
}
|
||||
|
||||
HvMessage *msg_initWithSymbol(HvMessage *m, hv_uint32_t timestamp, const char *s) {
|
||||
m->timestamp = timestamp;
|
||||
m->numElements = 1;
|
||||
m->numBytes = sizeof(HvMessage) + (hv_uint16_t) hv_strlen(s);
|
||||
msg_setSymbol(m, 0, s);
|
||||
return m;
|
||||
}
|
||||
|
||||
HvMessage *msg_initWithHash(HvMessage *m, hv_uint32_t timestamp, hv_uint32_t h) {
|
||||
m->timestamp = timestamp;
|
||||
m->numElements = 1;
|
||||
m->numBytes = sizeof(HvMessage);
|
||||
msg_setHash(m, 0, h);
|
||||
return m;
|
||||
}
|
||||
|
||||
void msg_copyToBuffer(const HvMessage *m, char *buffer, hv_size_t len) {
|
||||
HvMessage *r = (HvMessage *) buffer;
|
||||
|
||||
hv_size_t len_r = msg_getCoreSize(msg_getNumElements(m));
|
||||
|
||||
// assert that the message is not already larger than the length of the buffer
|
||||
hv_assert(len_r <= len);
|
||||
|
||||
// copy the basic message to the buffer
|
||||
hv_memcpy(r, m, len_r);
|
||||
|
||||
char *p = buffer + len_r; // points to the end of the base message
|
||||
for (int i = 0; i < msg_getNumElements(m); ++i) {
|
||||
if (msg_isSymbol(m,i)) {
|
||||
const hv_size_t symLen = (hv_size_t) hv_strlen(msg_getSymbol(m,i)) + 1; // include the trailing null char
|
||||
hv_assert(len_r + symLen <= len); // stay safe!
|
||||
hv_strncpy(p, msg_getSymbol(m,i), symLen);
|
||||
msg_setSymbol(r, i, p);
|
||||
p += symLen;
|
||||
len_r += symLen;
|
||||
}
|
||||
}
|
||||
|
||||
r->numBytes = (hv_uint16_t) len_r; // update the message size in memory
|
||||
}
|
||||
|
||||
// the message is serialised such that all symbol elements are placed in order at the end of the buffer
|
||||
HvMessage *msg_copy(const HvMessage *m) {
|
||||
const hv_uint32_t heapSize = msg_getSize(m);
|
||||
char *r = (char *) hv_malloc(heapSize);
|
||||
hv_assert(r != NULL);
|
||||
msg_copyToBuffer(m, r, heapSize);
|
||||
return (HvMessage *) r;
|
||||
}
|
||||
|
||||
void msg_free(HvMessage *m) {
|
||||
hv_free(m); // because heap messages are serialised in memory, a simple call to free releases the message
|
||||
}
|
||||
|
||||
bool msg_hasFormat(const HvMessage *m, const char *fmt) {
|
||||
hv_assert(fmt != NULL);
|
||||
const int n = msg_getNumElements(m);
|
||||
for (int i = 0; i < n; ++i) {
|
||||
switch (fmt[i]) {
|
||||
case 'b': if (!msg_isBang(m, i)) return false; break;
|
||||
case 'f': if (!msg_isFloat(m, i)) return false; break;
|
||||
case 'h': if (!msg_isHash(m, i)) return false; break;
|
||||
case 's': if (!msg_isSymbol(m, i)) return false; break;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
return (fmt[n] == '\0');
|
||||
}
|
||||
|
||||
bool msg_compareSymbol(const HvMessage *m, int i, const char *s) {
|
||||
switch (msg_getType(m,i)) {
|
||||
case HV_MSG_SYMBOL: return !hv_strcmp(msg_getSymbol(m, i), s);
|
||||
case HV_MSG_HASH: return (msg_getHash(m,i) == hv_string_to_hash(s));
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool msg_equalsElement(const HvMessage *m, int i_m, const HvMessage *n, int i_n) {
|
||||
if (i_m < msg_getNumElements(m) && i_n < msg_getNumElements(n)) {
|
||||
if (msg_getType(m, i_m) == msg_getType(n, i_n)) {
|
||||
switch (msg_getType(m, i_m)) {
|
||||
case HV_MSG_BANG: return true;
|
||||
case HV_MSG_FLOAT: return (msg_getFloat(m, i_m) == msg_getFloat(n, i_n));
|
||||
case HV_MSG_SYMBOL: return msg_compareSymbol(m, i_m, msg_getSymbol(n, i_n));
|
||||
case HV_MSG_HASH: return msg_getHash(m,i_m) == msg_getHash(n,i_n);
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void msg_setElementToFrom(HvMessage *n, int i_n, const HvMessage *const m, int i_m) {
|
||||
switch (msg_getType(m, i_m)) {
|
||||
case HV_MSG_BANG: msg_setBang(n, i_n); break;
|
||||
case HV_MSG_FLOAT: msg_setFloat(n, i_n, msg_getFloat(m, i_m)); break;
|
||||
case HV_MSG_SYMBOL: msg_setSymbol(n, i_n, msg_getSymbol(m, i_m)); break;
|
||||
case HV_MSG_HASH: msg_setHash(n, i_n, msg_getHash(m, i_m));
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
hv_uint32_t msg_getHash(const HvMessage *const m, int i) {
|
||||
hv_assert(i < msg_getNumElements(m)); // invalid index
|
||||
switch (msg_getType(m,i)) {
|
||||
case HV_MSG_BANG: return 0xFFFFFFFF;
|
||||
case HV_MSG_FLOAT: {
|
||||
float f = msg_getFloat(m,i);
|
||||
return *((hv_uint32_t *) &f);
|
||||
}
|
||||
case HV_MSG_SYMBOL: return hv_string_to_hash(msg_getSymbol(m,i));
|
||||
case HV_MSG_HASH: return (&(m->elem)+i)->data.h;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
char *msg_toString(const HvMessage *m) {
|
||||
hv_assert(msg_getNumElements(m) > 0);
|
||||
int *len = (int *) hv_alloca(msg_getNumElements(m)*sizeof(int));
|
||||
int size = 0; // the total length of our final buffer
|
||||
|
||||
// loop through every element in our list of atoms
|
||||
// first loop figures out how long our buffer should be
|
||||
for (int i = 0; i < msg_getNumElements(m); i++) {
|
||||
// length of our string is each atom plus a space, or \0 on the end
|
||||
switch (msg_getType(m, i)) {
|
||||
case HV_MSG_BANG: len[i] = hv_snprintf(NULL, 0, "%s", "bang") + 1; break;
|
||||
case HV_MSG_FLOAT: len[i] = hv_snprintf(NULL, 0, "%g", msg_getFloat(m, i)) + 1; break;
|
||||
case HV_MSG_SYMBOL: len[i] = hv_snprintf(NULL, 0, "%s", msg_getSymbol(m, i)) + 1; break;
|
||||
case HV_MSG_HASH: len[i] = hv_snprintf(NULL, 0, "0x%X", msg_getHash(m, i)) + 1; break;
|
||||
default: break;
|
||||
}
|
||||
size += len[i];
|
||||
}
|
||||
|
||||
hv_assert(size > 0);
|
||||
|
||||
// now we do the piecewise concatenation into our final string
|
||||
// the final buffer we will pass back after concatenating all strings - user should free it
|
||||
char *finalString = (char *) hv_malloc(size*sizeof(char));
|
||||
hv_assert(finalString != NULL);
|
||||
int pos = 0;
|
||||
for (int i = 0; i < msg_getNumElements(m); i++) {
|
||||
// put a string representation of each atom into the final string
|
||||
switch (msg_getType(m, i)) {
|
||||
case HV_MSG_BANG: hv_snprintf(finalString+pos, len[i], "%s", "bang"); break;
|
||||
case HV_MSG_FLOAT: hv_snprintf(finalString+pos, len[i], "%g", msg_getFloat(m, i)); break;
|
||||
case HV_MSG_SYMBOL: hv_snprintf(finalString+pos, len[i], "%s", msg_getSymbol(m, i)); break;
|
||||
case HV_MSG_HASH: hv_snprintf(finalString+pos, len[i], "0x%X", msg_getHash(m, i)); break;
|
||||
default: break;
|
||||
}
|
||||
pos += len[i];
|
||||
finalString[pos-1] = 32; // ASCII space
|
||||
}
|
||||
finalString[size-1] = '\0'; // ensure that the string is null terminated
|
||||
return finalString;
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
/**
|
||||
* Copyright (c) 2014-2018 Enzien Audio Ltd.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _HEAVY_MESSAGE_H_
|
||||
#define _HEAVY_MESSAGE_H_
|
||||
|
||||
#include "HvUtils.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum ElementType {
|
||||
HV_MSG_BANG = 0,
|
||||
HV_MSG_FLOAT = 1,
|
||||
HV_MSG_SYMBOL = 2,
|
||||
HV_MSG_HASH = 3
|
||||
} ElementType;
|
||||
|
||||
typedef struct Element {
|
||||
ElementType type;
|
||||
union {
|
||||
float f; // float
|
||||
const char *s; // symbol
|
||||
hv_uint32_t h; // hash
|
||||
} data;
|
||||
} Element;
|
||||
|
||||
typedef struct HvMessage {
|
||||
hv_uint32_t timestamp; // the sample at which this message should be processed
|
||||
hv_uint16_t numElements;
|
||||
hv_uint16_t numBytes; // the total number of bytes that this message occupies in memory, including strings
|
||||
Element elem;
|
||||
} HvMessage;
|
||||
|
||||
typedef struct ReceiverMessagePair {
|
||||
hv_uint32_t receiverHash;
|
||||
HvMessage msg;
|
||||
} ReceiverMessagePair;
|
||||
|
||||
#define HV_MESSAGE_ON_STACK(_x) (HvMessage *) hv_alloca(msg_getCoreSize(_x))
|
||||
|
||||
/** Returns the number of bytes that this message consumes in memory, not including strings. */
|
||||
static inline hv_size_t msg_getCoreSize(hv_size_t numElements) {
|
||||
hv_assert(numElements > 0);
|
||||
return sizeof(HvMessage) + ((numElements-1) * sizeof(Element));
|
||||
}
|
||||
|
||||
HvMessage *msg_copy(const HvMessage *m);
|
||||
|
||||
/** Copies the message into the given buffer. The buffer must be at least as large as msg_getNumHeapBytes(). */
|
||||
void msg_copyToBuffer(const HvMessage *m, char *buffer, hv_size_t len);
|
||||
|
||||
void msg_setElementToFrom(HvMessage *n, int indexN, const HvMessage *const m, int indexM);
|
||||
|
||||
/** Frees a message on the heap. Does nothing if argument is NULL. */
|
||||
void msg_free(HvMessage *m);
|
||||
|
||||
HvMessage *msg_init(HvMessage *m, hv_size_t numElements, hv_uint32_t timestamp);
|
||||
|
||||
HvMessage *msg_initWithFloat(HvMessage *m, hv_uint32_t timestamp, float f);
|
||||
|
||||
HvMessage *msg_initWithBang(HvMessage *m, hv_uint32_t timestamp);
|
||||
|
||||
HvMessage *msg_initWithSymbol(HvMessage *m, hv_uint32_t timestamp, const char *s);
|
||||
|
||||
HvMessage *msg_initWithHash(HvMessage *m, hv_uint32_t timestamp, hv_uint32_t h);
|
||||
|
||||
static inline hv_uint32_t msg_getTimestamp(const HvMessage *m) {
|
||||
return m->timestamp;
|
||||
}
|
||||
|
||||
static inline void msg_setTimestamp(HvMessage *m, hv_uint32_t timestamp) {
|
||||
m->timestamp = timestamp;
|
||||
}
|
||||
|
||||
static inline int msg_getNumElements(const HvMessage *m) {
|
||||
return (int) m->numElements;
|
||||
}
|
||||
|
||||
/** Returns the total number of bytes this message consumes in memory. */
|
||||
static inline hv_uint32_t msg_getSize(const HvMessage *m) {
|
||||
return m->numBytes;
|
||||
}
|
||||
|
||||
static inline ElementType msg_getType(const HvMessage *m, int index) {
|
||||
hv_assert(index < msg_getNumElements(m)); // invalid index
|
||||
return (&(m->elem)+index)->type;
|
||||
}
|
||||
|
||||
static inline void msg_setBang(HvMessage *m, int index) {
|
||||
hv_assert(index < msg_getNumElements(m)); // invalid index
|
||||
(&(m->elem)+index)->type = HV_MSG_BANG;
|
||||
(&(m->elem)+index)->data.s = NULL;
|
||||
}
|
||||
|
||||
static inline bool msg_isBang(const HvMessage *m, int index) {
|
||||
return (index < msg_getNumElements(m)) ? (msg_getType(m,index) == HV_MSG_BANG) : false;
|
||||
}
|
||||
|
||||
static inline void msg_setFloat(HvMessage *m, int index, float f) {
|
||||
hv_assert(index < msg_getNumElements(m)); // invalid index
|
||||
(&(m->elem)+index)->type = HV_MSG_FLOAT;
|
||||
(&(m->elem)+index)->data.f = f;
|
||||
}
|
||||
|
||||
static inline float msg_getFloat(const HvMessage *const m, int index) {
|
||||
hv_assert(index < msg_getNumElements(m)); // invalid index
|
||||
return (&(m->elem)+index)->data.f;
|
||||
}
|
||||
|
||||
static inline bool msg_isFloat(const HvMessage *const m, int index) {
|
||||
return (index < msg_getNumElements(m)) ? (msg_getType(m,index) == HV_MSG_FLOAT) : false;
|
||||
}
|
||||
|
||||
static inline void msg_setHash(HvMessage *m, int index, hv_uint32_t h) {
|
||||
hv_assert(index < msg_getNumElements(m)); // invalid index
|
||||
(&(m->elem)+index)->type = HV_MSG_HASH;
|
||||
(&(m->elem)+index)->data.h = h;
|
||||
}
|
||||
|
||||
static inline bool msg_isHash(const HvMessage *m, int index) {
|
||||
return (index < msg_getNumElements(m)) ? (msg_getType(m, index) == HV_MSG_HASH) : false;
|
||||
}
|
||||
|
||||
/** Returns true if the element is a hash or symbol. False otherwise. */
|
||||
static inline bool msg_isHashLike(const HvMessage *m, int index) {
|
||||
return (index < msg_getNumElements(m)) ? ((msg_getType(m, index) == HV_MSG_HASH) || (msg_getType(m, index) == HV_MSG_SYMBOL)) : false;
|
||||
}
|
||||
|
||||
/** Returns a 32-bit hash of the given element. */
|
||||
hv_uint32_t msg_getHash(const HvMessage *const m, int i);
|
||||
|
||||
static inline void msg_setSymbol(HvMessage *m, int index, const char *s) {
|
||||
hv_assert(index < msg_getNumElements(m)); // invalid index
|
||||
hv_assert(s != NULL);
|
||||
(&(m->elem)+index)->type = HV_MSG_SYMBOL;
|
||||
(&(m->elem)+index)->data.s = s;
|
||||
// NOTE(mhroth): if the same message container is reused and string reset,
|
||||
// then the message size will be overcounted
|
||||
m->numBytes += (hv_uint16_t) (hv_strlen(s) + 1); // also count '\0'
|
||||
}
|
||||
|
||||
static inline const char *msg_getSymbol(const HvMessage *m, int index) {
|
||||
hv_assert(index < msg_getNumElements(m)); // invalid index
|
||||
return (&(m->elem)+index)->data.s;
|
||||
}
|
||||
|
||||
static inline bool msg_isSymbol(const HvMessage *m, int index) {
|
||||
return (index < msg_getNumElements(m)) ? (msg_getType(m, index) == HV_MSG_SYMBOL) : false;
|
||||
}
|
||||
|
||||
bool msg_compareSymbol(const HvMessage *m, int i, const char *s);
|
||||
|
||||
/** Returns 1 if the element i_m of message m is equal to element i_n of message n. */
|
||||
bool msg_equalsElement(const HvMessage *m, int i_m, const HvMessage *n, int i_n);
|
||||
|
||||
bool msg_hasFormat(const HvMessage *m, const char *fmt);
|
||||
|
||||
/**
|
||||
* Create a string representation of the message. Suitable for use by the print object.
|
||||
* The resulting string must be freed by the caller.
|
||||
*/
|
||||
char *msg_toString(const HvMessage *msg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _HEAVY_MESSAGE_H_
|
||||
@@ -0,0 +1,144 @@
|
||||
/**
|
||||
* Copyright (c) 2014-2018 Enzien Audio Ltd.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "HvMessagePool.h"
|
||||
#include "HvMessage.h"
|
||||
|
||||
// the number of bytes reserved at a time from the pool
|
||||
#define MP_BLOCK_SIZE_BYTES 512
|
||||
|
||||
#if HV_APPLE
|
||||
#pragma mark - MessageList
|
||||
#endif
|
||||
|
||||
typedef struct MessageListNode {
|
||||
char *p;
|
||||
struct MessageListNode *next;
|
||||
} MessageListNode;
|
||||
|
||||
static inline bool ml_hasAvailable(HvMessagePoolList *ml) {
|
||||
return (ml->head != NULL);
|
||||
}
|
||||
|
||||
static char *ml_pop(HvMessagePoolList *ml) {
|
||||
MessageListNode *n = ml->head;
|
||||
ml->head = n->next;
|
||||
n->next = ml->pool;
|
||||
ml->pool = n;
|
||||
char *const p = n->p;
|
||||
n->p = NULL; // set to NULL to make it clear that this node does not have a valid buffer
|
||||
return p;
|
||||
}
|
||||
|
||||
/** Push a MessageListNode with the given pointer onto the head of the queue. */
|
||||
static void ml_push(HvMessagePoolList *ml, void *p) {
|
||||
MessageListNode *n = NULL;
|
||||
if (ml->pool != NULL) {
|
||||
// take an empty MessageListNode from the pool
|
||||
n = ml->pool;
|
||||
ml->pool = n->next;
|
||||
} else {
|
||||
// a MessageListNode is not available, allocate one
|
||||
n = (MessageListNode *) hv_malloc(sizeof(MessageListNode));
|
||||
hv_assert(n != NULL);
|
||||
}
|
||||
n->p = (char *) p;
|
||||
n->next = ml->head;
|
||||
ml->head = n; // push to the front of the queue
|
||||
}
|
||||
|
||||
static void ml_free(HvMessagePoolList *ml) {
|
||||
if (ml != NULL) {
|
||||
while (ml_hasAvailable(ml)) {
|
||||
ml_pop(ml);
|
||||
}
|
||||
while (ml->pool != NULL) {
|
||||
MessageListNode *n = ml->pool;
|
||||
ml->pool = n->next;
|
||||
hv_free(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if HV_APPLE
|
||||
#pragma mark - HvMessagePool
|
||||
#endif
|
||||
|
||||
static hv_size_t mp_messagelistIndexForSize(hv_size_t byteSize) {
|
||||
return (hv_size_t) hv_max_i((hv_min_max_log2((hv_uint32_t) byteSize) - 5), 0);
|
||||
}
|
||||
|
||||
hv_size_t mp_init(HvMessagePool *mp, hv_size_t numKB) {
|
||||
mp->bufferSize = numKB * 1024;
|
||||
mp->buffer = (char *) hv_malloc(mp->bufferSize);
|
||||
hv_assert(mp->buffer != NULL);
|
||||
mp->bufferIndex = 0;
|
||||
|
||||
// initialise all message lists
|
||||
for (int i = 0; i < MP_NUM_MESSAGE_LISTS; i++) {
|
||||
mp->lists[i].head = NULL;
|
||||
mp->lists[i].pool = NULL;
|
||||
}
|
||||
|
||||
return mp->bufferSize;
|
||||
}
|
||||
|
||||
void mp_free(HvMessagePool *mp) {
|
||||
hv_free(mp->buffer);
|
||||
for (int i = 0; i < MP_NUM_MESSAGE_LISTS; i++) {
|
||||
ml_free(&mp->lists[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void mp_freeMessage(HvMessagePool *mp, HvMessage *m) {
|
||||
const hv_size_t b = msg_getSize(m); // the number of bytes that a message occupies in memory
|
||||
const hv_size_t i = mp_messagelistIndexForSize(b); // the HvMessagePoolList index in the pool
|
||||
HvMessagePoolList *ml = &mp->lists[i];
|
||||
const hv_size_t chunkSize = 32 << i;
|
||||
hv_memclear(m, chunkSize); // clear the chunk, just in case
|
||||
ml_push(ml, m);
|
||||
}
|
||||
|
||||
HvMessage *mp_addMessage(HvMessagePool *mp, const HvMessage *m) {
|
||||
const hv_size_t b = msg_getSize(m);
|
||||
// determine the message list index to allocate data from based on the msg size
|
||||
// smallest chunk size is 32 bytes
|
||||
const hv_size_t i = mp_messagelistIndexForSize(b);
|
||||
|
||||
hv_assert(i < MP_NUM_MESSAGE_LISTS); // how many chunk sizes do we want to support? 32, 64, 128, 256 at the moment
|
||||
HvMessagePoolList *ml = &mp->lists[i];
|
||||
const hv_size_t chunkSize = 32 << i;
|
||||
|
||||
if (ml_hasAvailable(ml)) {
|
||||
char *buf = ml_pop(ml);
|
||||
msg_copyToBuffer(m, buf, chunkSize);
|
||||
return (HvMessage *) buf;
|
||||
} else {
|
||||
// if no appropriately sized buffer is immediately available, increase the size of the used buffer
|
||||
const hv_size_t newIndex = mp->bufferIndex + MP_BLOCK_SIZE_BYTES;
|
||||
hv_assert((newIndex <= mp->bufferSize) &&
|
||||
"The message pool buffer size has been exceeded. The context cannot store more messages. "
|
||||
"Try using the new_with_options() initialiser with a larger pool size (default is 10KB).");
|
||||
|
||||
for (hv_size_t j = mp->bufferIndex; j < newIndex; j += chunkSize) {
|
||||
ml_push(ml, mp->buffer + j); // push new nodes onto the list with chunk pointers
|
||||
}
|
||||
mp->bufferIndex = newIndex;
|
||||
char *buf = ml_pop(ml);
|
||||
msg_copyToBuffer(m, buf, chunkSize);
|
||||
return (HvMessage *) buf;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* Copyright (c) 2014-2018 Enzien Audio Ltd.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _MESSAGE_POOL_H_
|
||||
#define _MESSAGE_POOL_H_
|
||||
|
||||
#include "HvUtils.h"
|
||||
|
||||
#ifdef HV_MP_NUM_MESSAGE_LISTS
|
||||
#define MP_NUM_MESSAGE_LISTS HV_MP_NUM_MESSAGE_LISTS
|
||||
#else // HV_MP_NUM_MESSAGE_LISTS
|
||||
#define MP_NUM_MESSAGE_LISTS 4
|
||||
#endif // HV_MP_NUM_MESSAGE_LISTS
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct HvMessagePoolList {
|
||||
struct MessageListNode *head; // list of currently available blocks
|
||||
struct MessageListNode *pool; // list of currently used blocks
|
||||
} HvMessagePoolList;
|
||||
|
||||
typedef struct HvMessagePool {
|
||||
char *buffer; // the buffer of all messages
|
||||
hv_size_t bufferSize; // in bytes
|
||||
hv_size_t bufferIndex; // the number of total reserved bytes
|
||||
|
||||
HvMessagePoolList lists[MP_NUM_MESSAGE_LISTS];
|
||||
} HvMessagePool;
|
||||
|
||||
/**
|
||||
* The HvMessagePool is a basic memory management system. It reserves a large block of memory at initialisation
|
||||
* and proceeds to divide this block into smaller chunks (usually 512 bytes) as they are needed. These chunks are
|
||||
* further divided into 32, 64, 128, or 256 sections. Each of these sections is managed by a HvMessagePoolList (MPL).
|
||||
* An MPL is a linked-list data structure which is initialised such that its own pool of listnodes is filled with nodes
|
||||
* that point at each subblock (e.g. each 32-byte block of a 512-block chunk).
|
||||
*
|
||||
* HvMessagePool is loosely inspired by TCMalloc. http://goog-perftools.sourceforge.net/doc/tcmalloc.html
|
||||
*/
|
||||
|
||||
hv_size_t mp_init(struct HvMessagePool *mp, hv_size_t numKB);
|
||||
|
||||
void mp_free(struct HvMessagePool *mp);
|
||||
|
||||
/**
|
||||
* Adds a message to the pool and returns a pointer to the copy. Returns NULL
|
||||
* if no space was available in the pool.
|
||||
*/
|
||||
struct HvMessage *mp_addMessage(struct HvMessagePool *mp, const struct HvMessage *m);
|
||||
|
||||
void mp_freeMessage(struct HvMessagePool *mp, struct HvMessage *m);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _MESSAGE_POOL_H_
|
||||
@@ -0,0 +1,215 @@
|
||||
/**
|
||||
* Copyright (c) 2014-2018 Enzien Audio Ltd.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "HvMessageQueue.h"
|
||||
|
||||
hv_size_t mq_initWithPoolSize(HvMessageQueue *q, hv_size_t poolSizeKB) {
|
||||
hv_assert(poolSizeKB > 0);
|
||||
q->head = NULL;
|
||||
q->tail = NULL;
|
||||
q->pool = NULL;
|
||||
return mp_init(&q->mp, poolSizeKB);
|
||||
}
|
||||
|
||||
void mq_free(HvMessageQueue *q) {
|
||||
mq_clear(q);
|
||||
while (q->pool != NULL) {
|
||||
MessageNode *n = q->pool;
|
||||
q->pool = q->pool->next;
|
||||
hv_free(n);
|
||||
}
|
||||
mp_free(&q->mp);
|
||||
}
|
||||
|
||||
static MessageNode *mq_getOrCreateNodeFromPool(HvMessageQueue *q) {
|
||||
if (q->pool == NULL) {
|
||||
// if necessary, create a new empty node
|
||||
q->pool = (MessageNode *) hv_malloc(sizeof(MessageNode));
|
||||
hv_assert(q->pool != NULL);
|
||||
q->pool->next = NULL;
|
||||
}
|
||||
MessageNode *node = q->pool;
|
||||
q->pool = q->pool->next;
|
||||
return node;
|
||||
}
|
||||
|
||||
int mq_size(HvMessageQueue *q) {
|
||||
int size = 0;
|
||||
MessageNode *n = q->head;
|
||||
while (n != NULL) {
|
||||
++size;
|
||||
n = n->next;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
HvMessage *mq_addMessage(HvMessageQueue *q, const HvMessage *m, int let,
|
||||
void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) {
|
||||
MessageNode *node = mq_getOrCreateNodeFromPool(q);
|
||||
node->m = mp_addMessage(&q->mp, m);
|
||||
node->let = let;
|
||||
node->sendMessage = sendMessage;
|
||||
node->prev = NULL;
|
||||
node->next = NULL;
|
||||
|
||||
if (q->tail != NULL) {
|
||||
// the list already contains elements
|
||||
q->tail->next = node;
|
||||
node->prev = q->tail;
|
||||
q->tail = node;
|
||||
} else {
|
||||
// the list is empty
|
||||
node->prev = NULL;
|
||||
q->head = node;
|
||||
q->tail = node;
|
||||
}
|
||||
return mq_node_getMessage(node);
|
||||
}
|
||||
|
||||
HvMessage *mq_addMessageByTimestamp(HvMessageQueue *q, const HvMessage *m, int let,
|
||||
void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) {
|
||||
if (mq_hasMessage(q)) {
|
||||
MessageNode *n = mq_getOrCreateNodeFromPool(q);
|
||||
n->m = mp_addMessage(&q->mp, m);
|
||||
n->let = let;
|
||||
n->sendMessage = sendMessage;
|
||||
|
||||
if (msg_getTimestamp(m) < msg_getTimestamp(q->head->m)) {
|
||||
// the message occurs before the current head
|
||||
n->next = q->head;
|
||||
q->head->prev = n;
|
||||
n->prev = NULL;
|
||||
q->head = n;
|
||||
} else if (msg_getTimestamp(m) >= msg_getTimestamp(q->tail->m)) {
|
||||
// the message occurs after the current tail
|
||||
n->next = NULL;
|
||||
n->prev = q->tail;
|
||||
q->tail->next = n;
|
||||
q->tail = n;
|
||||
} else {
|
||||
// the message occurs somewhere between the head and tail
|
||||
MessageNode *node = q->head;
|
||||
while (node != NULL) {
|
||||
if (msg_getTimestamp(m) < msg_getTimestamp(node->next->m)) {
|
||||
MessageNode *r = node->next;
|
||||
node->next = n;
|
||||
n->next = r;
|
||||
n->prev = node;
|
||||
r->prev = n;
|
||||
break;
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
}
|
||||
return n->m;
|
||||
} else {
|
||||
// add a message to the head
|
||||
return mq_addMessage(q, m, let, sendMessage);
|
||||
}
|
||||
}
|
||||
|
||||
void mq_pop(HvMessageQueue *q) {
|
||||
if (mq_hasMessage(q)) {
|
||||
MessageNode *n = q->head;
|
||||
|
||||
mp_freeMessage(&q->mp, n->m);
|
||||
n->m = NULL;
|
||||
|
||||
n->let = 0;
|
||||
n->sendMessage = NULL;
|
||||
|
||||
q->head = n->next;
|
||||
if (q->head == NULL) {
|
||||
q->tail = NULL;
|
||||
} else {
|
||||
q->head->prev = NULL;
|
||||
}
|
||||
n->next = q->pool;
|
||||
n->prev = NULL;
|
||||
q->pool = n;
|
||||
}
|
||||
}
|
||||
|
||||
bool mq_removeMessage(HvMessageQueue *q, HvMessage *m, void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) {
|
||||
if (mq_hasMessage(q)) {
|
||||
if (mq_node_getMessage(q->head) == m) { // msg in head node
|
||||
// only remove the message if sendMessage is the same as the stored one,
|
||||
// if the sendMessage argument is NULL, it is not checked and will remove any matching message pointer
|
||||
if (sendMessage == NULL || q->head->sendMessage == sendMessage) {
|
||||
mq_pop(q);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
MessageNode *prevNode = q->head;
|
||||
MessageNode *currNode = q->head->next;
|
||||
while ((currNode != NULL) && (currNode->m != m)) {
|
||||
prevNode = currNode;
|
||||
currNode = currNode->next;
|
||||
}
|
||||
if (currNode != NULL) {
|
||||
if (sendMessage == NULL || currNode->sendMessage == sendMessage) {
|
||||
mp_freeMessage(&q->mp, m);
|
||||
currNode->m = NULL;
|
||||
currNode->let = 0;
|
||||
currNode->sendMessage = NULL;
|
||||
if (currNode == q->tail) { // msg in tail node
|
||||
prevNode->next = NULL;
|
||||
q->tail = prevNode;
|
||||
} else { // msg in middle node
|
||||
prevNode->next = currNode->next;
|
||||
currNode->next->prev = prevNode;
|
||||
}
|
||||
currNode->next = (q->pool == NULL) ? NULL : q->pool;
|
||||
currNode->prev = NULL;
|
||||
q->pool = currNode;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void mq_clear(HvMessageQueue *q) {
|
||||
while (mq_hasMessage(q)) {
|
||||
mq_pop(q);
|
||||
}
|
||||
}
|
||||
|
||||
void mq_clearAfter(HvMessageQueue *q, const hv_uint32_t timestamp) {
|
||||
MessageNode *n = q->tail;
|
||||
while (n != NULL && timestamp <= msg_getTimestamp(n->m)) {
|
||||
// free the node's message
|
||||
mp_freeMessage(&q->mp, n->m);
|
||||
n->m = NULL;
|
||||
n->let = 0;
|
||||
n->sendMessage = NULL;
|
||||
|
||||
// the tail points at the previous node
|
||||
q->tail = n->prev;
|
||||
|
||||
// put the node back in the pool
|
||||
n->next = q->pool;
|
||||
n->prev = NULL;
|
||||
if (q->pool != NULL) q->pool->prev = n;
|
||||
q->pool = n;
|
||||
|
||||
// update the tail node
|
||||
n = q->tail;
|
||||
}
|
||||
|
||||
if (q->tail == NULL) q->head = NULL;
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* Copyright (c) 2014-2018 Enzien Audio Ltd.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _MESSAGE_QUEUE_H_
|
||||
#define _MESSAGE_QUEUE_H_
|
||||
|
||||
#include "HvMessage.h"
|
||||
#include "HvMessagePool.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
class HeavyContextInterface;
|
||||
#else
|
||||
typedef struct HeavyContextInterface HeavyContextInterface;
|
||||
#endif
|
||||
|
||||
typedef struct MessageNode {
|
||||
struct MessageNode *prev; // doubly linked list
|
||||
struct MessageNode *next;
|
||||
HvMessage *m;
|
||||
void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *);
|
||||
int let;
|
||||
} MessageNode;
|
||||
|
||||
/** A doubly linked list containing scheduled messages. */
|
||||
typedef struct HvMessageQueue {
|
||||
MessageNode *head; // the head of the queue
|
||||
MessageNode *tail; // the tail of the queue
|
||||
MessageNode *pool; // the head of the reserve pool
|
||||
HvMessagePool mp;
|
||||
} HvMessageQueue;
|
||||
|
||||
hv_size_t mq_initWithPoolSize(HvMessageQueue *q, hv_size_t poolSizeKB);
|
||||
|
||||
void mq_free(HvMessageQueue *q);
|
||||
|
||||
int mq_size(HvMessageQueue *q);
|
||||
|
||||
static inline HvMessage *mq_node_getMessage(MessageNode *n) {
|
||||
return n->m;
|
||||
}
|
||||
|
||||
static inline int mq_node_getLet(MessageNode *n) {
|
||||
return n->let;
|
||||
}
|
||||
|
||||
static inline bool mq_hasMessage(HvMessageQueue *q) {
|
||||
return (q->head != NULL);
|
||||
}
|
||||
|
||||
// true if there is a message and it occurs before (<) timestamp
|
||||
static inline bool mq_hasMessageBefore(HvMessageQueue *const q, const hv_uint32_t timestamp) {
|
||||
return mq_hasMessage(q) && (msg_getTimestamp(mq_node_getMessage(q->head)) < timestamp);
|
||||
}
|
||||
|
||||
static inline MessageNode *mq_peek(HvMessageQueue *q) {
|
||||
return q->head;
|
||||
}
|
||||
|
||||
/** Appends the message to the end of the queue. */
|
||||
HvMessage *mq_addMessage(HvMessageQueue *q, const HvMessage *m, int let,
|
||||
void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *));
|
||||
|
||||
/** Insert in ascending order the message acccording to its timestamp. */
|
||||
HvMessage *mq_addMessageByTimestamp(HvMessageQueue *q, const HvMessage *m, int let,
|
||||
void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *));
|
||||
|
||||
/** Pop the message at the head of the queue (and free its memory). */
|
||||
void mq_pop(HvMessageQueue *q);
|
||||
|
||||
/** Remove a message from the queue (and free its memory) */
|
||||
bool mq_removeMessage(HvMessageQueue *q, HvMessage *m,
|
||||
void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *));
|
||||
|
||||
/** Clears (and frees) all messages in the queue. */
|
||||
void mq_clear(HvMessageQueue *q);
|
||||
|
||||
/** Removes all messages occuring at or after the given timestamp. */
|
||||
void mq_clearAfter(HvMessageQueue *q, const hv_uint32_t timestamp);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _MESSAGE_QUEUE_H_
|
||||
@@ -0,0 +1,110 @@
|
||||
/**
|
||||
* Copyright (c) 2014-2018 Enzien Audio Ltd.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "HvTable.h"
|
||||
#include "HvMessage.h"
|
||||
|
||||
hv_size_t hTable_init(HvTable *o, int length) {
|
||||
o->length = length;
|
||||
// true size of the table is always an integer multple of HV_N_SIMD
|
||||
o->size = (length + HV_N_SIMD_MASK) & ~HV_N_SIMD_MASK;
|
||||
// add an extra length for mirroring
|
||||
o->allocated = o->size + HV_N_SIMD;
|
||||
o->head = 0;
|
||||
hv_size_t numBytes = o->allocated * sizeof(float);
|
||||
o->buffer = (float *) hv_malloc(numBytes);
|
||||
hv_assert(o->buffer != NULL);
|
||||
hv_memclear(o->buffer, numBytes);
|
||||
return numBytes;
|
||||
}
|
||||
|
||||
hv_size_t hTable_initWithData(HvTable *o, int length, const float *data) {
|
||||
o->length = length;
|
||||
o->size = (length + HV_N_SIMD_MASK) & ~HV_N_SIMD_MASK;
|
||||
o->allocated = o->size + HV_N_SIMD;
|
||||
o->head = 0;
|
||||
hv_size_t numBytes = o->size * sizeof(float);
|
||||
o->buffer = (float *) hv_malloc(numBytes);
|
||||
hv_assert(o->buffer != NULL);
|
||||
hv_memclear(o->buffer, numBytes);
|
||||
hv_memcpy(o->buffer, data, length*sizeof(float));
|
||||
return numBytes;
|
||||
}
|
||||
|
||||
hv_size_t hTable_initWithFinalData(HvTable *o, int length, float *data) {
|
||||
o->length = length;
|
||||
o->size = length;
|
||||
o->allocated = length;
|
||||
o->buffer = data;
|
||||
o->head = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void hTable_free(HvTable *o) {
|
||||
hv_free(o->buffer);
|
||||
}
|
||||
|
||||
int hTable_resize(HvTable *o, hv_uint32_t newLength) {
|
||||
// TODO(mhroth): update context with memory allocated by table
|
||||
// NOTE(mhroth): mirrored bytes are not necessarily carried over
|
||||
const hv_uint32_t newSize = (newLength + HV_N_SIMD_MASK) & ~HV_N_SIMD_MASK;
|
||||
if (newSize == o->size) return 0; // early exit if no change in size
|
||||
const hv_uint32_t oldSizeBytes = (hv_uint32_t) (o->size * sizeof(float));
|
||||
const hv_uint32_t newAllocated = newSize + HV_N_SIMD;
|
||||
const hv_uint32_t newAllocatedBytes = (hv_uint32_t) (newAllocated * sizeof(float));
|
||||
|
||||
float *b = (float *) hv_realloc(o->buffer, newAllocatedBytes);
|
||||
hv_assert(b != NULL); // error while reallocing!
|
||||
// ensure that hv_realloc has given us a correctly aligned buffer
|
||||
if ((((hv_uintptr_t) (const void *) b) & ((0x1<<HV_N_SIMD)-1)) == 0) {
|
||||
if (newSize > o->size) {
|
||||
hv_memclear(b + o->size, (newAllocated - o->size) * sizeof(float)); // clear new parts of the buffer
|
||||
}
|
||||
o->buffer = b;
|
||||
} else {
|
||||
// if not, we have to re-malloc ourselves
|
||||
char *c = (char *) hv_malloc(newAllocatedBytes);
|
||||
hv_assert(c != NULL); // error while allocating new buffer!
|
||||
if (newAllocatedBytes > oldSizeBytes) {
|
||||
hv_memcpy(c, b, oldSizeBytes);
|
||||
hv_memclear(c + oldSizeBytes, newAllocatedBytes - oldSizeBytes);
|
||||
} else {
|
||||
hv_memcpy(c, b, newAllocatedBytes);
|
||||
}
|
||||
hv_free(b);
|
||||
o->buffer = (float *) c;
|
||||
}
|
||||
o->length = newLength;
|
||||
o->size = newSize;
|
||||
o->allocated = newAllocated;
|
||||
return (int) (newAllocated - oldSizeBytes - (HV_N_SIMD*sizeof(float)));
|
||||
}
|
||||
|
||||
void hTable_onMessage(HeavyContextInterface *_c, HvTable *o, int letIn, const HvMessage *m,
|
||||
void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) {
|
||||
if (msg_compareSymbol(m,0,"resize") && msg_isFloat(m,1) && msg_getFloat(m,1) >= 0.0f) {
|
||||
hTable_resize(o, (int) hv_ceil_f(msg_getFloat(m,1))); // apply ceil to ensure that tables always have enough space
|
||||
|
||||
// send out the new size of the table
|
||||
HvMessage *n = HV_MESSAGE_ON_STACK(1);
|
||||
msg_initWithFloat(n, msg_getTimestamp(m), (float) hTable_getSize(o));
|
||||
sendMessage(_c, 0, n);
|
||||
}
|
||||
|
||||
else if (msg_compareSymbol(m,0,"mirror")) {
|
||||
hv_memcpy(o->buffer+o->size, o->buffer, HV_N_SIMD*sizeof(float));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* Copyright (c) 2014-2018 Enzien Audio Ltd.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _HEAVY_TABLE_H_
|
||||
#define _HEAVY_TABLE_H_
|
||||
|
||||
#include "HvHeavy.h"
|
||||
#include "HvUtils.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct HvTable {
|
||||
float *buffer;
|
||||
// the number of values that the table is requested to have
|
||||
hv_uint32_t length;
|
||||
|
||||
// the number of usable values that the table actually has
|
||||
// this is always an even multiple of HV_N_SIMD
|
||||
hv_uint32_t size;
|
||||
|
||||
// Note that the true size of the table is (size + HV_N_SIMD),
|
||||
// with the trailing values used by the system, e.g. to create a circular
|
||||
// buffer
|
||||
hv_uint32_t allocated;
|
||||
|
||||
hv_uint32_t head; // the most recently written point
|
||||
} HvTable;
|
||||
|
||||
hv_size_t hTable_init(HvTable *o, int length);
|
||||
|
||||
hv_size_t hTable_initWithData(HvTable *o, int length, const float *data);
|
||||
|
||||
hv_size_t hTable_initWithFinalData(HvTable *o, int length, float *data);
|
||||
|
||||
void hTable_free(HvTable *o);
|
||||
|
||||
int hTable_resize(HvTable *o, hv_uint32_t newLength);
|
||||
|
||||
void hTable_onMessage(HeavyContextInterface *_c, HvTable *o, int letIn, const HvMessage *m,
|
||||
void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *));
|
||||
|
||||
static inline float *hTable_getBuffer(HvTable *o) {
|
||||
return o->buffer;
|
||||
}
|
||||
|
||||
// the user-requested length of the table (number of floats)
|
||||
static inline hv_uint32_t hTable_getLength(HvTable *o) {
|
||||
return o->length;
|
||||
}
|
||||
|
||||
// the usable length of the table (an even multiple of HV_N_SIMD)
|
||||
static inline hv_uint32_t hTable_getSize(HvTable *o) {
|
||||
return o->size;
|
||||
}
|
||||
|
||||
// the number of floats allocated to this table (usually size + HV_N_SIMD)
|
||||
static inline hv_uint32_t hTable_getAllocated(HvTable *o) {
|
||||
return o->allocated;
|
||||
}
|
||||
|
||||
static inline hv_uint32_t hTable_getHead(HvTable *o) {
|
||||
return o->head;
|
||||
}
|
||||
|
||||
static inline void hTable_setHead(HvTable *o, hv_uint32_t head) {
|
||||
o->head = head;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // _HEAVY_TABLE_H_
|
||||
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Copyright (c) 2014-2018 Enzien Audio Ltd.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "HvUtils.h"
|
||||
|
||||
hv_uint32_t hv_string_to_hash(const char *str) {
|
||||
// this hash is based MurmurHash2
|
||||
// http://en.wikipedia.org/wiki/MurmurHash
|
||||
// https://sites.google.com/site/murmurhash/
|
||||
static const hv_uint32_t n = 0x5bd1e995;
|
||||
static const hv_int32_t r = 24;
|
||||
|
||||
if (str == NULL) return 0;
|
||||
|
||||
hv_uint32_t len = (hv_uint32_t) hv_strlen(str);
|
||||
hv_uint32_t x = len; // seed (0) ^ len
|
||||
|
||||
while (len >= 4) {
|
||||
#if HV_EMSCRIPTEN
|
||||
hv_uint32_t k = str[0] | (str[1] << 8) | (str[2] << 16) | (str[3] << 24);
|
||||
#else
|
||||
hv_uint32_t k = *((hv_uint32_t *) str);
|
||||
#endif
|
||||
k *= n;
|
||||
k ^= (k >> r);
|
||||
k *= n;
|
||||
x *= n;
|
||||
x ^= k;
|
||||
str += 4; len -= 4;
|
||||
}
|
||||
switch (len) {
|
||||
case 3: x ^= (str[2] << 16);
|
||||
case 2: x ^= (str[1] << 8);
|
||||
case 1: x ^= str[0]; x *= n;
|
||||
default: break;
|
||||
}
|
||||
x ^= (x >> 13);
|
||||
x *= n;
|
||||
x ^= (x >> 15);
|
||||
return x;
|
||||
}
|
||||
@@ -0,0 +1,319 @@
|
||||
/**
|
||||
* Copyright (c) 2014-2018 Enzien Audio Ltd.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _HEAVY_UTILS_H_
|
||||
#define _HEAVY_UTILS_H_
|
||||
|
||||
// platform definitions
|
||||
#if _WIN32 || _WIN64 || _MSC_VER
|
||||
#define HV_WIN 1
|
||||
#elif __APPLE__
|
||||
#define HV_APPLE 1
|
||||
#elif __ANDROID__
|
||||
#define HV_ANDROID 1
|
||||
#elif __unix__ || __unix
|
||||
#define HV_UNIX 1
|
||||
#else
|
||||
#warning Could not detect platform. Assuming Unix-like.
|
||||
#endif
|
||||
|
||||
#ifdef EMSCRIPTEN
|
||||
#define HV_EMSCRIPTEN 1
|
||||
#endif
|
||||
|
||||
// basic includes
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// type definitions
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#define hv_uint8_t uint8_t
|
||||
#define hv_int16_t int16_t
|
||||
#define hv_uint16_t uint16_t
|
||||
#define hv_int32_t int32_t
|
||||
#define hv_uint32_t uint32_t
|
||||
#define hv_uint64_t uint64_t
|
||||
#define hv_size_t size_t
|
||||
#define hv_uintptr_t uintptr_t
|
||||
|
||||
// SIMD-specific includes
|
||||
#if !(HV_SIMD_NONE || HV_SIMD_NEON || HV_SIMD_SSE || HV_SIMD_AVX)
|
||||
#define HV_SIMD_NEON __ARM_NEON__
|
||||
#define HV_SIMD_SSE (__SSE__ && __SSE2__ && __SSE3__ && __SSSE3__ && __SSE4_1__)
|
||||
#define HV_SIMD_AVX (__AVX__ && HV_SIMD_SSE)
|
||||
#endif
|
||||
#ifndef HV_SIMD_FMA
|
||||
#define HV_SIMD_FMA __FMA__
|
||||
#endif
|
||||
|
||||
#if HV_SIMD_AVX || HV_SIMD_SSE
|
||||
#include <immintrin.h>
|
||||
#elif HV_SIMD_NEON
|
||||
#include <arm_neon.h>
|
||||
#endif
|
||||
|
||||
#if HV_SIMD_NEON // NEON
|
||||
#define HV_N_SIMD 4
|
||||
#define hv_bufferf_t float32x4_t
|
||||
#define hv_bufferi_t int32x4_t
|
||||
#define hv_bInf_t float32x4_t
|
||||
#define hv_bOutf_t float32x4_t*
|
||||
#define hv_bIni_t int32x4_t
|
||||
#define hv_bOuti_t int32x4_t*
|
||||
#define VIf(_x) (_x)
|
||||
#define VOf(_x) (&_x)
|
||||
#define VIi(_x) (_x)
|
||||
#define VOi(_x) (&_x)
|
||||
#elif HV_SIMD_AVX // AVX
|
||||
#define HV_N_SIMD 8
|
||||
#define hv_bufferf_t __m256
|
||||
#define hv_bufferi_t __m256i
|
||||
#define hv_bInf_t __m256
|
||||
#define hv_bOutf_t __m256*
|
||||
#define hv_bIni_t __m256i
|
||||
#define hv_bOuti_t __m256i*
|
||||
#define VIf(_x) (_x)
|
||||
#define VOf(_x) (&_x)
|
||||
#define VIi(_x) (_x)
|
||||
#define VOi(_x) (&_x)
|
||||
#elif HV_SIMD_SSE // SSE
|
||||
#define HV_N_SIMD 4
|
||||
#define hv_bufferf_t __m128
|
||||
#define hv_bufferi_t __m128i
|
||||
#define hv_bInf_t __m128
|
||||
#define hv_bOutf_t __m128*
|
||||
#define hv_bIni_t __m128i
|
||||
#define hv_bOuti_t __m128i*
|
||||
#define VIf(_x) (_x)
|
||||
#define VOf(_x) (&_x)
|
||||
#define VIi(_x) (_x)
|
||||
#define VOi(_x) (&_x)
|
||||
#else // DEFAULT
|
||||
#define HV_N_SIMD 1
|
||||
#undef HV_SIMD_NONE
|
||||
#define HV_SIMD_NONE 1
|
||||
#define hv_bufferf_t float
|
||||
#define hv_bufferi_t int
|
||||
#define hv_bInf_t float
|
||||
#define hv_bOutf_t float*
|
||||
#define hv_bIni_t int
|
||||
#define hv_bOuti_t int*
|
||||
#define VIf(_x) (_x)
|
||||
#define VOf(_x) (&_x)
|
||||
#define VIi(_x) (_x)
|
||||
#define VOi(_x) (&_x)
|
||||
#endif
|
||||
|
||||
#define HV_N_SIMD_MASK (HV_N_SIMD-1)
|
||||
|
||||
// Strings
|
||||
#include <string.h>
|
||||
#define hv_strlen(a) strlen(a)
|
||||
#define hv_strcmp(a, b) strcmp(a, b)
|
||||
#define hv_snprintf(a, b, c, ...) snprintf(a, b, c, __VA_ARGS__)
|
||||
#if HV_WIN
|
||||
#define hv_strncpy(_dst, _src, _len) strncpy_s(_dst, _len, _src, _TRUNCATE)
|
||||
#else
|
||||
#define hv_strncpy(_dst, _src, _len) strncpy(_dst, _src, _len)
|
||||
#endif
|
||||
|
||||
// Memory management
|
||||
#define hv_memcpy(a, b, c) memcpy(a, b, c)
|
||||
#define hv_memclear(a, b) memset(a, 0, b)
|
||||
#if HV_WIN
|
||||
#include <malloc.h>
|
||||
#define hv_alloca(_n) _alloca(_n)
|
||||
#if HV_SIMD_AVX
|
||||
#define hv_malloc(_n) _aligned_malloc(_n, 32)
|
||||
#define hv_realloc(a, b) _aligned_realloc(a, b, 32)
|
||||
#define hv_free(x) _aligned_free(x)
|
||||
#elif HV_SIMD_SSE || HV_SIMD_NEON
|
||||
#define hv_malloc(_n) _aligned_malloc(_n, 16)
|
||||
#define hv_realloc(a, b) _aligned_realloc(a, b, 16)
|
||||
#define hv_free(x) _aligned_free(x)
|
||||
#else // HV_SIMD_NONE
|
||||
#define hv_malloc(_n) malloc(_n)
|
||||
#define hv_realloc(a, b) realloc(a, b)
|
||||
#define hv_free(_n) free(_n)
|
||||
#endif
|
||||
#elif HV_APPLE
|
||||
#define hv_alloca(_n) alloca(_n)
|
||||
#define hv_realloc(a, b) realloc(a, b)
|
||||
#if HV_SIMD_AVX
|
||||
#include <mm_malloc.h>
|
||||
#define hv_malloc(_n) _mm_malloc(_n, 32)
|
||||
#define hv_free(x) _mm_free(x)
|
||||
#elif HV_SIMD_SSE
|
||||
#include <mm_malloc.h>
|
||||
#define hv_malloc(_n) _mm_malloc(_n, 16)
|
||||
#define hv_free(x) _mm_free(x)
|
||||
#elif HV_SIMD_NEON
|
||||
// malloc on ios always has 16-byte alignment
|
||||
#define hv_malloc(_n) malloc(_n)
|
||||
#define hv_free(x) free(x)
|
||||
#else // HV_SIMD_NONE
|
||||
#define hv_malloc(_n) malloc(_n)
|
||||
#define hv_free(x) free(x)
|
||||
#endif
|
||||
#else
|
||||
#include <alloca.h>
|
||||
#define hv_alloca(_n) alloca(_n)
|
||||
#define hv_realloc(a, b) realloc(a, b)
|
||||
#if HV_SIMD_AVX
|
||||
#define hv_malloc(_n) aligned_alloc(32, _n)
|
||||
#define hv_free(x) free(x)
|
||||
#elif HV_SIMD_SSE
|
||||
#define hv_malloc(_n) aligned_alloc(16, _n)
|
||||
#define hv_free(x) free(x)
|
||||
#elif HV_SIMD_NEON
|
||||
#if HV_ANDROID
|
||||
#define hv_malloc(_n) memalign(16, _n)
|
||||
#define hv_free(x) free(x)
|
||||
#else
|
||||
#define hv_malloc(_n) aligned_alloc(16, _n)
|
||||
#define hv_free(x) free(x)
|
||||
#endif
|
||||
#else // HV_SIMD_NONE
|
||||
#define hv_malloc(_n) malloc(_n)
|
||||
#define hv_free(_n) free(_n)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Assert
|
||||
#include <assert.h>
|
||||
#define hv_assert(e) assert(e)
|
||||
|
||||
// Export and Inline
|
||||
#if HV_WIN
|
||||
#define HV_EXPORT __declspec(dllexport)
|
||||
#ifndef __cplusplus // MSVC doesn't like redefining "inline" keyword
|
||||
#define inline __inline
|
||||
#endif
|
||||
#define HV_FORCE_INLINE __forceinline
|
||||
#else
|
||||
#define HV_EXPORT
|
||||
#define HV_FORCE_INLINE inline __attribute__((always_inline))
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
// Returns a 32-bit hash of any string. Returns 0 if string is NULL.
|
||||
hv_uint32_t hv_string_to_hash(const char *str);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
// Math
|
||||
#include <math.h>
|
||||
static inline hv_size_t __hv_utils_max_ui(hv_size_t x, hv_size_t y) { return (x > y) ? x : y; }
|
||||
static inline hv_size_t __hv_utils_min_ui(hv_size_t x, hv_size_t y) { return (x < y) ? x : y; }
|
||||
static inline hv_int32_t __hv_utils_max_i(hv_int32_t x, hv_int32_t y) { return (x > y) ? x : y; }
|
||||
static inline hv_int32_t __hv_utils_min_i(hv_int32_t x, hv_int32_t y) { return (x < y) ? x : y; }
|
||||
#define hv_max_ui(a, b) __hv_utils_max_ui(a, b)
|
||||
#define hv_min_ui(a, b) __hv_utils_min_ui(a, b)
|
||||
#define hv_max_i(a, b) __hv_utils_max_i(a, b)
|
||||
#define hv_min_i(a, b) __hv_utils_min_i(a, b)
|
||||
#define hv_max_f(a, b) fmaxf(a, b)
|
||||
#define hv_min_f(a, b) fminf(a, b)
|
||||
#define hv_max_d(a, b) fmax(a, b)
|
||||
#define hv_min_d(a, b) fmin(a, b)
|
||||
#define hv_sin_f(a) sinf(a)
|
||||
#define hv_sinh_f(a) sinhf(a)
|
||||
#define hv_cos_f(a) cosf(a)
|
||||
#define hv_cosh_f(a) coshf(a)
|
||||
#define hv_tan_f(a) tanf(a)
|
||||
#define hv_tanh_f(a) tanhf(a)
|
||||
#define hv_asin_f(a) asinf(a)
|
||||
#define hv_asinh_f(a) asinhf(a)
|
||||
#define hv_acos_f(a) acosf(a)
|
||||
#define hv_acosh_f(a) acoshf(a)
|
||||
#define hv_atan_f(a) atanf(a)
|
||||
#define hv_atanh_f(a) atanhf(a)
|
||||
#define hv_atan2_f(a, b) atan2f(a, b)
|
||||
#define hv_exp_f(a) expf(a)
|
||||
#define hv_abs_f(a) fabsf(a)
|
||||
#define hv_sqrt_f(a) sqrtf(a)
|
||||
#define hv_log_f(a) logf(a)
|
||||
#define hv_ceil_f(a) ceilf(a)
|
||||
#define hv_floor_f(a) floorf(a)
|
||||
#define hv_round_f(a) roundf(a)
|
||||
#define hv_pow_f(a, b) powf(a, b)
|
||||
#if HV_EMSCRIPTEN
|
||||
#define hv_fma_f(a, b, c) ((a*b)+c) // emscripten does not support fmaf (yet?)
|
||||
#else
|
||||
#define hv_fma_f(a, b, c) fmaf(a, b, c)
|
||||
#endif
|
||||
#if HV_WIN
|
||||
// finds ceil(log2(x))
|
||||
#include <intrin.h>
|
||||
static inline hv_uint32_t __hv_utils_min_max_log2(hv_uint32_t x) {
|
||||
unsigned long z = 0;
|
||||
_BitScanReverse(&z, x);
|
||||
return (hv_uint32_t) (z+1);
|
||||
}
|
||||
#else
|
||||
static inline hv_uint32_t __hv_utils_min_max_log2(hv_uint32_t x) {
|
||||
return (hv_uint32_t) (32 - __builtin_clz(x-1));
|
||||
}
|
||||
#endif
|
||||
#define hv_min_max_log2(a) __hv_utils_min_max_log2(a)
|
||||
|
||||
// Atomics
|
||||
#if HV_WIN
|
||||
#include <windows.h>
|
||||
#define hv_atomic_bool volatile LONG
|
||||
#define HV_SPINLOCK_ACQUIRE(_x) while (InterlockedCompareExchange(&_x, true, false)) { }
|
||||
#define HV_SPINLOCK_TRY(_x) return !InterlockedCompareExchange(&_x, true, false)
|
||||
#define HV_SPINLOCK_RELEASE(_x) (_x = false)
|
||||
#elif HV_ANDROID
|
||||
// Android support for atomics isn't that great, we'll do it manually
|
||||
// https://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Atomic-Builtins.html
|
||||
#define hv_atomic_bool hv_uint8_t
|
||||
#define HV_SPINLOCK_ACQUIRE(_x) while (__sync_lock_test_and_set(&_x, 1))
|
||||
#define HV_SPINLOCK_TRY(_x) return !__sync_lock_test_and_set(&_x, 1)
|
||||
#define HV_SPINLOCK_RELEASE(_x) __sync_lock_release(&_x)
|
||||
#elif __cplusplus
|
||||
#include <atomic>
|
||||
#define hv_atomic_bool std::atomic_flag
|
||||
#define HV_SPINLOCK_ACQUIRE(_x) while (_x.test_and_set(std::memory_order_acquire))
|
||||
#define HV_SPINLOCK_TRY(_x) return !_x.test_and_set(std::memory_order_acquire)
|
||||
#define HV_SPINLOCK_RELEASE(_x) _x.clear(std::memory_order_release)
|
||||
#elif defined(__has_include)
|
||||
#if __has_include(<stdatomic.h>)
|
||||
#include <stdatomic.h>
|
||||
#define hv_atomic_bool atomic_flag
|
||||
#define HV_SPINLOCK_ACQUIRE(_x) while (atomic_flag_test_and_set_explicit(&_x, memory_order_acquire))
|
||||
#define HV_SPINLOCK_TRY(_x) return !atomic_flag_test_and_set_explicit(&_x, memory_order_acquire)
|
||||
#define HV_SPINLOCK_RELEASE(_x) atomic_flag_clear_explicit(memory_order_release)
|
||||
#endif
|
||||
#endif
|
||||
#ifndef hv_atomic_bool
|
||||
#define hv_atomic_bool volatile bool
|
||||
#define HV_SPINLOCK_ACQUIRE(_x) \
|
||||
while (_x) {} \
|
||||
_x = true;
|
||||
#define HV_SPINLOCK_TRY(_x) \
|
||||
if (!_x) { \
|
||||
_x = true; \
|
||||
return true; \
|
||||
} else return false;
|
||||
#define HV_SPINLOCK_RELEASE(_x) (_x = false)
|
||||
#endif
|
||||
|
||||
#endif // _HEAVY_UTILS_H_
|
||||
@@ -0,0 +1,5 @@
|
||||
#N canvas 0 0 450 300 12;
|
||||
#X obj 93 88 adc~;
|
||||
#X obj 93 173 dac~;
|
||||
#X connect 0 0 1 0;
|
||||
#X connect 0 1 1 1;
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Test sketch for adc2dac.pd that was compiled with hvcc -n Adc2Dac adc2dac.pd
|
||||
* The ADC receives data from the SineWaveGenerator and the output from the
|
||||
* DAC is copied to I2S (AudioBoardStream)
|
||||
*/
|
||||
|
||||
#include "AudioTools.h"
|
||||
#include "AudioTools/AudioLibs/AudioBoardStream.h"
|
||||
#include "Heavy_Adc2Dac.hpp"
|
||||
#include "AudioTools/AudioLibs/PureDataStream.h"
|
||||
|
||||
AudioInfo info(44100, 2, 16);
|
||||
Heavy_Adc2Dac pd_test(info.sample_rate);
|
||||
PureDataStream pd(pd_test);
|
||||
AudioBoardStream i2s(AudioKitEs8388V1); // final output of decoded stream
|
||||
SineWaveGenerator<int16_t> sineWave; // subclass of SoundGenerator with max amplitude of 32000
|
||||
GeneratedSoundStream<int16_t> sound(sineWave); // Stream generated from sine wave
|
||||
|
||||
StreamCopy copierToPd(pd, sound, 1024);
|
||||
StreamCopy copierFromPd(i2s, pd, 1024);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning);
|
||||
|
||||
// setup output
|
||||
auto config = i2s.defaultConfig(TX_MODE);
|
||||
config.sd_active = false;
|
||||
config.copyFrom(info);
|
||||
i2s.begin(config);
|
||||
|
||||
// setup input
|
||||
sineWave.begin(info, N_B4);
|
||||
|
||||
// setup pd
|
||||
pd.begin();
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// provide data to adc
|
||||
copierToPd.copy();
|
||||
// get data from dac
|
||||
copierFromPd.copy();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,264 @@
|
||||
/**
|
||||
* Copyright (c) 2014-2018 Enzien Audio Ltd.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "HeavyContext.hpp"
|
||||
#include "HvTable.h"
|
||||
|
||||
void defaultSendHook(HeavyContextInterface *context,
|
||||
const char *sendName, hv_uint32_t sendHash, const HvMessage *msg) {
|
||||
HeavyContext *thisContext = reinterpret_cast<HeavyContext *>(context);
|
||||
const hv_uint32_t numBytes = sizeof(ReceiverMessagePair) + msg_getSize(msg) - sizeof(HvMessage);
|
||||
ReceiverMessagePair *p = reinterpret_cast<ReceiverMessagePair *>(hLp_getWriteBuffer(&thisContext->outQueue, numBytes));
|
||||
if (p != nullptr) {
|
||||
p->receiverHash = sendHash;
|
||||
msg_copyToBuffer(msg, (char *) &p->msg, msg_getSize(msg));
|
||||
hLp_produce(&thisContext->outQueue, numBytes);
|
||||
} else {
|
||||
hv_assert(false &&
|
||||
"::defaultSendHook - The out message queue is full and cannot accept more messages until they "
|
||||
"have been processed. Try increasing the outQueueKb size in the new_with_options() constructor.");
|
||||
}
|
||||
}
|
||||
|
||||
HeavyContext::HeavyContext(double sampleRate, int poolKb, int inQueueKb, int outQueueKb) :
|
||||
sampleRate(sampleRate) {
|
||||
|
||||
hv_assert(sampleRate > 0.0); // sample rate must be positive
|
||||
hv_assert(poolKb > 0);
|
||||
hv_assert(inQueueKb > 0);
|
||||
hv_assert(outQueueKb >= 0);
|
||||
|
||||
blockStartTimestamp = 0;
|
||||
printHook = nullptr;
|
||||
userData = nullptr;
|
||||
|
||||
// if outQueueKb is positive, then the outQueue is allocated and the default sendhook is set.
|
||||
// Otherwise outQueue and the sendhook are set to NULL.
|
||||
sendHook = (outQueueKb > 0) ? &defaultSendHook : nullptr;
|
||||
|
||||
HV_SPINLOCK_RELEASE(inQueueLock);
|
||||
HV_SPINLOCK_RELEASE(outQueueLock);
|
||||
|
||||
numBytes = sizeof(HeavyContext);
|
||||
|
||||
numBytes += mq_initWithPoolSize(&mq, poolKb);
|
||||
numBytes += hLp_init(&inQueue, inQueueKb * 1024);
|
||||
numBytes += hLp_init(&outQueue, outQueueKb * 1024); // outQueueKb value of 0 sets everything to NULL
|
||||
}
|
||||
|
||||
HeavyContext::~HeavyContext() {
|
||||
mq_free(&mq);
|
||||
hLp_free(&inQueue);
|
||||
hLp_free(&outQueue);
|
||||
}
|
||||
|
||||
bool HeavyContext::sendBangToReceiver(hv_uint32_t receiverHash) {
|
||||
HvMessage *m = HV_MESSAGE_ON_STACK(1);
|
||||
msg_initWithBang(m, 0);
|
||||
bool success = sendMessageToReceiver(receiverHash, 0.0, m);
|
||||
return success;
|
||||
}
|
||||
|
||||
bool HeavyContext::sendFloatToReceiver(hv_uint32_t receiverHash, float f) {
|
||||
HvMessage *m = HV_MESSAGE_ON_STACK(1);
|
||||
msg_initWithFloat(m, 0, f);
|
||||
bool success = sendMessageToReceiver(receiverHash, 0.0, m);
|
||||
return success;
|
||||
}
|
||||
|
||||
bool HeavyContext::sendSymbolToReceiver(hv_uint32_t receiverHash, const char *s) {
|
||||
hv_assert(s != nullptr);
|
||||
HvMessage *m = HV_MESSAGE_ON_STACK(1);
|
||||
msg_initWithSymbol(m, 0, (char *) s);
|
||||
bool success = sendMessageToReceiver(receiverHash, 0.0, m);
|
||||
return success;
|
||||
}
|
||||
|
||||
bool HeavyContext::sendMessageToReceiverV(hv_uint32_t receiverHash, double delayMs, const char *format, ...) {
|
||||
hv_assert(delayMs >= 0.0);
|
||||
hv_assert(format != nullptr);
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
const int numElem = (int) hv_strlen(format);
|
||||
HvMessage *m = HV_MESSAGE_ON_STACK(numElem);
|
||||
msg_init(m, numElem, blockStartTimestamp + (hv_uint32_t) (hv_max_d(0.0, delayMs)*getSampleRate()/1000.0));
|
||||
for (int i = 0; i < numElem; i++) {
|
||||
switch (format[i]) {
|
||||
case 'b': msg_setBang(m, i); break;
|
||||
case 'f': msg_setFloat(m, i, (float) va_arg(ap, double)); break;
|
||||
case 'h': msg_setHash(m, i, (int) va_arg(ap, int)); break;
|
||||
case 's': msg_setSymbol(m, i, (char *) va_arg(ap, char *)); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
bool success = sendMessageToReceiver(receiverHash, delayMs, m);
|
||||
return success;
|
||||
}
|
||||
|
||||
bool HeavyContext::sendMessageToReceiver(hv_uint32_t receiverHash, double delayMs, HvMessage *m) {
|
||||
hv_assert(delayMs >= 0.0);
|
||||
hv_assert(m != nullptr);
|
||||
|
||||
const hv_uint32_t timestamp = blockStartTimestamp +
|
||||
(hv_uint32_t) (hv_max_d(0.0, delayMs)*(getSampleRate()/1000.0));
|
||||
|
||||
ReceiverMessagePair *p = nullptr;
|
||||
HV_SPINLOCK_ACQUIRE(inQueueLock);
|
||||
const hv_uint32_t numBytes = sizeof(ReceiverMessagePair) + msg_getSize(m) - sizeof(HvMessage);
|
||||
p = (ReceiverMessagePair *) hLp_getWriteBuffer(&inQueue, numBytes);
|
||||
if (p != nullptr) {
|
||||
p->receiverHash = receiverHash;
|
||||
msg_copyToBuffer(m, (char *) &p->msg, msg_getSize(m));
|
||||
msg_setTimestamp(&p->msg, timestamp);
|
||||
hLp_produce(&inQueue, numBytes);
|
||||
} else {
|
||||
hv_assert(false &&
|
||||
"::sendMessageToReceiver - The input message queue is full and cannot accept more messages until they "
|
||||
"have been processed. Try increasing the inQueueKb size in the new_with_options() constructor.");
|
||||
}
|
||||
HV_SPINLOCK_RELEASE(inQueueLock);
|
||||
return (p != nullptr);
|
||||
}
|
||||
|
||||
bool HeavyContext::cancelMessage(HvMessage *m, void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) {
|
||||
return mq_removeMessage(&mq, m, sendMessage);
|
||||
}
|
||||
|
||||
HvMessage *HeavyContext::scheduleMessageForObject(const HvMessage *m,
|
||||
void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *),
|
||||
int letIndex) {
|
||||
HvMessage *n = mq_addMessageByTimestamp(&mq, m, letIndex, sendMessage);
|
||||
return n;
|
||||
}
|
||||
|
||||
float *HeavyContext::getBufferForTable(hv_uint32_t tableHash) {
|
||||
HvTable *t = getTableForHash(tableHash);
|
||||
if (t != nullptr) {
|
||||
return hTable_getBuffer(t);
|
||||
} else return nullptr;
|
||||
}
|
||||
|
||||
int HeavyContext::getLengthForTable(hv_uint32_t tableHash) {
|
||||
HvTable *t = getTableForHash(tableHash);
|
||||
if (t != nullptr) {
|
||||
return hTable_getLength(t);
|
||||
} else return 0;
|
||||
}
|
||||
|
||||
bool HeavyContext::setLengthForTable(hv_uint32_t tableHash, hv_uint32_t newSampleLength) {
|
||||
HvTable *t = getTableForHash(tableHash);
|
||||
if (t != nullptr) {
|
||||
hTable_resize(t, newSampleLength);
|
||||
return true;
|
||||
} else return false;
|
||||
}
|
||||
|
||||
void HeavyContext::lockAcquire() {
|
||||
HV_SPINLOCK_ACQUIRE(inQueueLock);
|
||||
}
|
||||
|
||||
bool HeavyContext::lockTry() {
|
||||
HV_SPINLOCK_TRY(inQueueLock);
|
||||
}
|
||||
|
||||
void HeavyContext::lockRelease() {
|
||||
HV_SPINLOCK_RELEASE(inQueueLock);
|
||||
}
|
||||
|
||||
void HeavyContext::setInputMessageQueueSize(int inQueueKb) {
|
||||
hv_assert(inQueueKb > 0);
|
||||
hLp_free(&inQueue);
|
||||
hLp_init(&inQueue, inQueueKb*1024);
|
||||
}
|
||||
|
||||
void HeavyContext::setOutputMessageQueueSize(int outQueueKb) {
|
||||
hv_assert(outQueueKb > 0);
|
||||
hLp_free(&outQueue);
|
||||
hLp_init(&outQueue, outQueueKb*1024);
|
||||
}
|
||||
|
||||
bool HeavyContext::getNextSentMessage(hv_uint32_t *destinationHash, HvMessage *outMsg, hv_size_t msgLengthBytes) {
|
||||
*destinationHash = 0;
|
||||
ReceiverMessagePair *p = nullptr;
|
||||
hv_assert((sendHook == &defaultSendHook) &&
|
||||
"::getNextSentMessage - this function won't do anything if the msg outQueue "
|
||||
"size is 0, or you've overriden the default sendhook.");
|
||||
if (sendHook == &defaultSendHook) {
|
||||
HV_SPINLOCK_ACQUIRE(outQueueLock);
|
||||
if (hLp_hasData(&outQueue)) {
|
||||
hv_uint32_t numBytes = 0;
|
||||
p = reinterpret_cast<ReceiverMessagePair *>(hLp_getReadBuffer(&outQueue, &numBytes));
|
||||
hv_assert((p != nullptr) && "::getNextSentMessage - something bad happened.");
|
||||
hv_assert(numBytes >= sizeof(ReceiverMessagePair));
|
||||
hv_assert((numBytes <= msgLengthBytes) &&
|
||||
"::getNextSentMessage - the sent message is bigger than the message "
|
||||
"passed to handle it.");
|
||||
*destinationHash = p->receiverHash;
|
||||
hv_memcpy(outMsg, &p->msg, numBytes);
|
||||
hLp_consume(&outQueue);
|
||||
}
|
||||
HV_SPINLOCK_RELEASE(outQueueLock);
|
||||
}
|
||||
return (p != nullptr);
|
||||
}
|
||||
|
||||
hv_uint32_t HeavyContext::getHashForString(const char *str) {
|
||||
return hv_string_to_hash(str);
|
||||
}
|
||||
|
||||
HvTable *_hv_table_get(HeavyContextInterface *c, hv_uint32_t tableHash) {
|
||||
hv_assert(c != nullptr);
|
||||
return reinterpret_cast<HeavyContext *>(c)->getTableForHash(tableHash);
|
||||
}
|
||||
|
||||
void _hv_scheduleMessageForReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, HvMessage *m) {
|
||||
hv_assert(c != nullptr);
|
||||
reinterpret_cast<HeavyContext *>(c)->scheduleMessageForReceiver(receiverHash, m);
|
||||
}
|
||||
|
||||
HvMessage *_hv_scheduleMessageForObject(HeavyContextInterface *c, const HvMessage *m,
|
||||
void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *),
|
||||
int letIndex) {
|
||||
hv_assert(c != nullptr);
|
||||
HvMessage *n = reinterpret_cast<HeavyContext *>(c)->scheduleMessageForObject(
|
||||
m, sendMessage, letIndex);
|
||||
return n;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
HvTable *hv_table_get(HeavyContextInterface *c, hv_uint32_t tableHash) {
|
||||
return _hv_table_get(c, tableHash);
|
||||
}
|
||||
|
||||
void hv_scheduleMessageForReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, HvMessage *m) {
|
||||
_hv_scheduleMessageForReceiver(c, receiverHash, m);
|
||||
}
|
||||
|
||||
HvMessage *hv_scheduleMessageForObject(HeavyContextInterface *c, const HvMessage *m,
|
||||
void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *),
|
||||
int letIndex) {
|
||||
return _hv_scheduleMessageForObject(c, m, sendMessage, letIndex);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
* Copyright (c) 2014-2018 Enzien Audio Ltd.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _HEAVY_CONTEXT_H_
|
||||
#define _HEAVY_CONTEXT_H_
|
||||
|
||||
#include "HeavyContextInterface.hpp"
|
||||
#include "HvLightPipe.h"
|
||||
#include "HvMessageQueue.h"
|
||||
#include "HvMath.h"
|
||||
|
||||
struct HvTable;
|
||||
|
||||
class HeavyContext : public HeavyContextInterface {
|
||||
|
||||
public:
|
||||
HeavyContext(double sampleRate, int poolKb=10, int inQueueKb=2, int outQueueKb=0);
|
||||
virtual ~HeavyContext();
|
||||
|
||||
int getSize() override { return (int) numBytes; }
|
||||
|
||||
double getSampleRate() override { return sampleRate; }
|
||||
|
||||
hv_uint32_t getCurrentSample() override { return blockStartTimestamp; }
|
||||
float samplesToMilliseconds(hv_uint32_t numSamples) override { return (float) (1000.0*numSamples/sampleRate); }
|
||||
hv_uint32_t millisecondsToSamples(float ms) override { return (hv_uint32_t) (hv_max_f(0.0f,ms)*sampleRate/1000.0); }
|
||||
|
||||
void setUserData(void *x) override { userData = x; }
|
||||
void *getUserData() override { return userData; }
|
||||
|
||||
// hook management
|
||||
void setSendHook(HvSendHook_t *f) override { sendHook = f; }
|
||||
HvSendHook_t *getSendHook() override { return sendHook; }
|
||||
|
||||
void setPrintHook(HvPrintHook_t *f) override { printHook = f; }
|
||||
HvPrintHook_t *getPrintHook() override { return printHook; }
|
||||
|
||||
// message scheduling
|
||||
bool sendMessageToReceiver(hv_uint32_t receiverHash, double delayMs, HvMessage *m) override;
|
||||
bool sendMessageToReceiverV(hv_uint32_t receiverHash, double delayMs, const char *fmt, ...) override;
|
||||
bool sendFloatToReceiver(hv_uint32_t receiverHash, float f) override;
|
||||
bool sendBangToReceiver(hv_uint32_t receiverHash) override;
|
||||
bool sendSymbolToReceiver(hv_uint32_t receiverHash, const char *symbol) override;
|
||||
bool cancelMessage(HvMessage *m, void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) override;
|
||||
|
||||
// table manipulation
|
||||
float *getBufferForTable(hv_uint32_t tableHash) override;
|
||||
int getLengthForTable(hv_uint32_t tableHash) override;
|
||||
bool setLengthForTable(hv_uint32_t tableHash, hv_uint32_t newSampleLength) override;
|
||||
|
||||
// lock control
|
||||
void lockAcquire() override;
|
||||
bool lockTry() override;
|
||||
void lockRelease() override;
|
||||
|
||||
// message queue management
|
||||
void setInputMessageQueueSize(int inQueueKb) override;
|
||||
void setOutputMessageQueueSize(int outQueueKb) override;
|
||||
bool getNextSentMessage(hv_uint32_t *destinationHash, HvMessage *outMsg, hv_size_t msgLength) override;
|
||||
|
||||
// utility functions
|
||||
static hv_uint32_t getHashForString(const char *str);
|
||||
|
||||
protected:
|
||||
virtual HvTable *getTableForHash(hv_uint32_t tableHash) = 0;
|
||||
friend HvTable *_hv_table_get(HeavyContextInterface *, hv_uint32_t);
|
||||
|
||||
virtual void scheduleMessageForReceiver(hv_uint32_t receiverHash, HvMessage *m) = 0;
|
||||
friend void _hv_scheduleMessageForReceiver(HeavyContextInterface *, hv_uint32_t, HvMessage *);
|
||||
|
||||
HvMessage *scheduleMessageForObject(const HvMessage *,
|
||||
void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *),
|
||||
int);
|
||||
friend HvMessage *_hv_scheduleMessageForObject(HeavyContextInterface *, const HvMessage *,
|
||||
void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *),
|
||||
int);
|
||||
|
||||
friend void defaultSendHook(HeavyContextInterface *, const char *, hv_uint32_t, const HvMessage *);
|
||||
|
||||
// object state
|
||||
double sampleRate;
|
||||
hv_uint32_t blockStartTimestamp;
|
||||
hv_size_t numBytes;
|
||||
HvMessageQueue mq;
|
||||
HvSendHook_t *sendHook;
|
||||
HvPrintHook_t *printHook;
|
||||
void *userData;
|
||||
HvLightPipe inQueue;
|
||||
HvLightPipe outQueue;
|
||||
hv_atomic_bool inQueueLock;
|
||||
hv_atomic_bool outQueueLock;
|
||||
};
|
||||
|
||||
#endif // _HEAVY_CONTEXT_H_
|
||||
@@ -0,0 +1,291 @@
|
||||
/**
|
||||
* Copyright (c) 2014-2018 Enzien Audio Ltd.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _HEAVY_CONTEXT_INTERFACE_H_
|
||||
#define _HEAVY_CONTEXT_INTERFACE_H_
|
||||
|
||||
#include "HvUtils.h"
|
||||
|
||||
#ifndef _HEAVY_DECLARATIONS_
|
||||
#define _HEAVY_DECLARATIONS_
|
||||
|
||||
class HeavyContextInterface;
|
||||
struct HvMessage;
|
||||
|
||||
typedef enum {
|
||||
HV_PARAM_TYPE_PARAMETER_IN,
|
||||
HV_PARAM_TYPE_PARAMETER_OUT,
|
||||
HV_PARAM_TYPE_EVENT_IN,
|
||||
HV_PARAM_TYPE_EVENT_OUT
|
||||
} HvParameterType;
|
||||
|
||||
typedef struct HvParameterInfo {
|
||||
const char *name; // the human readable parameter name
|
||||
hv_uint32_t hash; // an integer identified used by heavy for this parameter
|
||||
HvParameterType type; // type of this parameter
|
||||
float minVal; // the minimum value of this parameter
|
||||
float maxVal; // the maximum value of this parameter
|
||||
float defaultVal; // the default value of this parameter
|
||||
} HvParameterInfo;
|
||||
|
||||
typedef void (HvSendHook_t) (HeavyContextInterface *context, const char *sendName, hv_uint32_t sendHash, const HvMessage *msg);
|
||||
typedef void (HvPrintHook_t) (HeavyContextInterface *context, const char *printName, const char *str, const HvMessage *msg);
|
||||
|
||||
#endif // _HEAVY_DECLARATIONS_
|
||||
|
||||
|
||||
|
||||
class HeavyContextInterface {
|
||||
|
||||
public:
|
||||
HeavyContextInterface() {}
|
||||
virtual ~HeavyContextInterface() {};
|
||||
|
||||
/** Returns the read-only user-assigned name of this patch. */
|
||||
virtual const char *getName() = 0;
|
||||
|
||||
/** Returns the number of input channels with which this context has been configured. */
|
||||
virtual int getNumInputChannels() = 0;
|
||||
|
||||
/** Returns the number of output channels with which this context has been configured. */
|
||||
virtual int getNumOutputChannels() = 0;
|
||||
|
||||
/**
|
||||
* Returns the total size in bytes of the context.
|
||||
* This value may change if tables are resized.
|
||||
*/
|
||||
virtual int getSize() = 0;
|
||||
|
||||
/** Returns the sample rate with which this context has been configured. */
|
||||
virtual double getSampleRate() = 0;
|
||||
|
||||
/** Returns the current patch time in samples. This value is always exact. */
|
||||
virtual hv_uint32_t getCurrentSample() = 0;
|
||||
virtual float samplesToMilliseconds(hv_uint32_t numSamples) = 0;
|
||||
|
||||
/** Converts milliseconds to samples. Input is limited to non-negative range. */
|
||||
virtual hv_uint32_t millisecondsToSamples(float ms) = 0;
|
||||
|
||||
/** Sets a user-definable value. This value is never manipulated by Heavy. */
|
||||
virtual void setUserData(void *x) = 0;
|
||||
|
||||
/** Returns the user-defined data. */
|
||||
virtual void *getUserData() = 0;
|
||||
|
||||
/**
|
||||
* Set the send hook. The function is called whenever a message is sent to any send object.
|
||||
* Messages returned by this function should NEVER be freed. If the message must persist, call
|
||||
* hv_msg_copy() first.
|
||||
*/
|
||||
virtual void setSendHook(HvSendHook_t *f) = 0;
|
||||
|
||||
/** Returns the send hook, or NULL if unset. */
|
||||
virtual HvSendHook_t *getSendHook() = 0;
|
||||
|
||||
/** Set the print hook. The function is called whenever a message is sent to a print object. */
|
||||
virtual void setPrintHook(HvPrintHook_t *f) = 0;
|
||||
|
||||
/** Returns the print hook, or NULL if unset. */
|
||||
virtual HvPrintHook_t *getPrintHook() = 0;
|
||||
|
||||
/**
|
||||
* Processes one block of samples for a patch instance. The buffer format is an array of float channel arrays.
|
||||
* If the context has not input or output channels, the respective argument may be NULL.
|
||||
* The number of samples to to tbe processed should be a multiple of 1, 4, or 8, depending on if
|
||||
* no, SSE or NEON, or AVX optimisation is being used, respectively.
|
||||
* e.g. [[LLLL][RRRR]]
|
||||
*
|
||||
* @return The number of samples processed.
|
||||
*
|
||||
* This function is NOT thread-safe. It is assumed that only the audio thread will execute this function.
|
||||
*/
|
||||
virtual int process(float **inputBuffers, float **outputBuffer, int n) = 0;
|
||||
|
||||
/**
|
||||
* Processes one block of samples for a patch instance. The buffer format is an uninterleaved float array of channels.
|
||||
* If the context has not input or output channels, the respective argument may be NULL.
|
||||
* The number of samples to to tbe processed should be a multiple of 1, 4, or 8, depending on if
|
||||
* no, SSE or NEON, or AVX optimisation is being used, respectively.
|
||||
* e.g. [LLLLRRRR]
|
||||
*
|
||||
* @return The number of samples processed.
|
||||
*
|
||||
* This function is NOT thread-safe. It is assumed that only the audio thread will execute this function.
|
||||
*/
|
||||
virtual int processInline(float *inputBuffers, float *outputBuffer, int n) = 0;
|
||||
|
||||
/**
|
||||
* Processes one block of samples for a patch instance. The buffer format is an interleaved float array of channels.
|
||||
* If the context has not input or output channels, the respective argument may be NULL.
|
||||
* The number of samples to to tbe processed should be a multiple of 1, 4, or 8, depending on if
|
||||
* no, SSE or NEON, or AVX optimisation is being used, respectively.
|
||||
* e.g. [LRLRLRLR]
|
||||
*
|
||||
* @return The number of samples processed.
|
||||
*
|
||||
* This function is NOT thread-safe. It is assumed that only the audio thread will execute this function.
|
||||
*/
|
||||
virtual int processInlineInterleaved(float *inputBuffers, float *outputBuffer, int n) = 0;
|
||||
|
||||
/**
|
||||
* Sends a formatted message to a receiver that can be scheduled for the future.
|
||||
* The receiver is addressed with its hash, which can also be determined using hv_stringToHash().
|
||||
* This function is thread-safe.
|
||||
*
|
||||
* @return True if the message was accepted. False if the message could not fit onto
|
||||
* the message queue to be processed this block.
|
||||
*/
|
||||
virtual bool sendMessageToReceiver(hv_uint32_t receiverHash, double delayMs, HvMessage *m) = 0;
|
||||
|
||||
/**
|
||||
* Sends a formatted message to a receiver that can be scheduled for the future.
|
||||
* The receiver is addressed with its hash, which can also be determined using hv_stringToHash().
|
||||
* This function is thread-safe.
|
||||
*
|
||||
* @return True if the message was accepted. False if the message could not fit onto
|
||||
* the message queue to be processed this block.
|
||||
*/
|
||||
virtual bool sendMessageToReceiverV(hv_uint32_t receiverHash, double delayMs, const char *fmt, ...) = 0;
|
||||
|
||||
/**
|
||||
* A convenience function to send a float to a receiver to be processed immediately.
|
||||
* The receiver is addressed with its hash, which can also be determined using hv_stringToHash().
|
||||
* This function is thread-safe.
|
||||
*
|
||||
* @return True if the message was accepted. False if the message could not fit onto
|
||||
* the message queue to be processed this block.
|
||||
*/
|
||||
virtual bool sendFloatToReceiver(hv_uint32_t receiverHash, float f) = 0;
|
||||
|
||||
/**
|
||||
* A convenience function to send a bang to a receiver to be processed immediately.
|
||||
* The receiver is addressed with its hash, which can also be determined using hv_stringToHash().
|
||||
* This function is thread-safe.
|
||||
*
|
||||
* @return True if the message was accepted. False if the message could not fit onto
|
||||
* the message queue to be processed this block.
|
||||
*/
|
||||
virtual bool sendBangToReceiver(hv_uint32_t receiverHash) = 0;
|
||||
|
||||
/**
|
||||
* A convenience function to send a symbol to a receiver to be processed immediately.
|
||||
* The receiver is addressed with its hash, which can also be determined using hv_stringToHash().
|
||||
* This function is thread-safe.
|
||||
*
|
||||
* @return True if the message was accepted. False if the message could not fit onto
|
||||
* the message queue to be processed this block.
|
||||
*/
|
||||
virtual bool sendSymbolToReceiver(hv_uint32_t receiverHash, const char *symbol) = 0;
|
||||
|
||||
/**
|
||||
* Cancels a previously scheduled message.
|
||||
*
|
||||
* @param sendMessage May be NULL.
|
||||
*/
|
||||
virtual bool cancelMessage(HvMessage *m, void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)=nullptr) = 0;
|
||||
|
||||
/**
|
||||
* Returns information about each parameter such as name, hash, and range.
|
||||
* The total number of parameters is always returned.
|
||||
*
|
||||
* @param index The parameter index.
|
||||
* @param info A pointer to a HvParameterInfo struct. May be null.
|
||||
*
|
||||
* @return The total number of parameters.
|
||||
*/
|
||||
virtual int getParameterInfo(int index, HvParameterInfo *info) = 0;
|
||||
|
||||
/** Returns a pointer to the raw buffer backing this table. DO NOT free it. */
|
||||
virtual float *getBufferForTable(hv_uint32_t tableHash) = 0;
|
||||
|
||||
/** Returns the length of this table in samples. */
|
||||
virtual int getLengthForTable(hv_uint32_t tableHash) = 0;
|
||||
|
||||
/**
|
||||
* Resizes the table to the given length.
|
||||
*
|
||||
* Existing contents are copied to the new table. Remaining space is cleared
|
||||
* if the table is longer than the original, truncated otherwise.
|
||||
*
|
||||
* @param tableHash The table identifier.
|
||||
* @param newSampleLength The new length of the table, in samples.
|
||||
*
|
||||
* @return False if the table could not be found. True otherwise.
|
||||
*/
|
||||
virtual bool setLengthForTable(hv_uint32_t tableHash, hv_uint32_t newSampleLength) = 0;
|
||||
|
||||
/**
|
||||
* Acquire the input message queue lock.
|
||||
*
|
||||
* This function will block until the message lock as been acquired.
|
||||
* Typical applications will not require the use of this function.
|
||||
*/
|
||||
virtual void lockAcquire() = 0;
|
||||
|
||||
/**
|
||||
* Try to acquire the input message queue lock.
|
||||
*
|
||||
* If the lock has been acquired, hv_lock_release() must be called to release it.
|
||||
* Typical applications will not require the use of this function.
|
||||
*
|
||||
* @return Returns true if the lock has been acquired, false otherwise.
|
||||
*/
|
||||
virtual bool lockTry() = 0;
|
||||
|
||||
/**
|
||||
* Release the input message queue lock.
|
||||
*
|
||||
* Typical applications will not require the use of this function.
|
||||
*/
|
||||
virtual void lockRelease() = 0;
|
||||
|
||||
/**
|
||||
* Set the size of the input message queue in kilobytes.
|
||||
*
|
||||
* The buffer is reset and all existing contents are lost on resize.
|
||||
*
|
||||
* @param inQueueKb Must be positive i.e. at least one.
|
||||
*/
|
||||
virtual void setInputMessageQueueSize(int inQueueKb) = 0;
|
||||
|
||||
/**
|
||||
* Set the size of the output message queue in kilobytes.
|
||||
*
|
||||
* The buffer is reset and all existing contents are lost on resize.
|
||||
* Only the default sendhook uses the outgoing message queue. If the default
|
||||
* sendhook is not being used, then this function is not useful.
|
||||
*
|
||||
* @param outQueueKb Must be postive i.e. at least one.
|
||||
*/
|
||||
virtual void setOutputMessageQueueSize(int outQueueKb) = 0;
|
||||
|
||||
/**
|
||||
* Get the next message in the outgoing queue, will also consume the message.
|
||||
* Returns false if there are no messages.
|
||||
*
|
||||
* @param destinationHash a hash of the name of the receiver the message was sent to.
|
||||
* @param outMsg message pointer that is filled by the next message contents.
|
||||
* @param msgLengthBytes max length of outMsg in bytes.
|
||||
*
|
||||
* @return True if there is a message in the outgoing queue.
|
||||
*/
|
||||
virtual bool getNextSentMessage(hv_uint32_t *destinationHash, HvMessage *outMsg, hv_size_t msgLengthBytes) = 0;
|
||||
|
||||
/** Returns a 32-bit hash of any string. Returns 0 if string is NULL. */
|
||||
static hv_uint32_t getHashForString(const char *str);
|
||||
};
|
||||
|
||||
#endif // _HEAVY_CONTEXT_INTERFACE_H_
|
||||
@@ -0,0 +1,264 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Enzien Audio, Ltd.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions, and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the phrase "powered by heavy",
|
||||
* the heavy logo, and a hyperlink to https://enzienaudio.com, all in a visible
|
||||
* form.
|
||||
*
|
||||
* 2.1 If the Application is distributed in a store system (for example,
|
||||
* the Apple "App Store" or "Google Play"), the phrase "powered by heavy"
|
||||
* shall be included in the app description or the copyright text as well as
|
||||
* the in the app itself. The heavy logo will shall be visible in the app
|
||||
* itself as well.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "Heavy_test.hpp"
|
||||
|
||||
#include <new>
|
||||
|
||||
#define Context(_c) static_cast<Heavy_test *>(_c)
|
||||
|
||||
|
||||
/*
|
||||
* C Functions
|
||||
*/
|
||||
|
||||
extern "C" {
|
||||
HV_EXPORT HeavyContextInterface *hv_test_new(double sampleRate) {
|
||||
// allocate aligned memory
|
||||
void *ptr = hv_malloc(sizeof(Heavy_test));
|
||||
// ensure non-null
|
||||
if (!ptr) return nullptr;
|
||||
// call constructor
|
||||
new(ptr) Heavy_test(sampleRate);
|
||||
return Context(ptr);
|
||||
}
|
||||
|
||||
HV_EXPORT HeavyContextInterface *hv_test_new_with_options(double sampleRate,
|
||||
int poolKb, int inQueueKb, int outQueueKb) {
|
||||
// allocate aligned memory
|
||||
void *ptr = hv_malloc(sizeof(Heavy_test));
|
||||
// ensure non-null
|
||||
if (!ptr) return nullptr;
|
||||
// call constructor
|
||||
new(ptr) Heavy_test(sampleRate, poolKb, inQueueKb, outQueueKb);
|
||||
return Context(ptr);
|
||||
}
|
||||
|
||||
HV_EXPORT void hv_test_free(HeavyContextInterface *instance) {
|
||||
// call destructor
|
||||
Context(instance)->~Heavy_test();
|
||||
// free memory
|
||||
hv_free(instance);
|
||||
}
|
||||
} // extern "C"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Class Functions
|
||||
*/
|
||||
|
||||
Heavy_test::Heavy_test(double sampleRate, int poolKb, int inQueueKb, int outQueueKb)
|
||||
: HeavyContext(sampleRate, poolKb, inQueueKb, outQueueKb) {
|
||||
numBytes += sPhasor_k_init(&sPhasor_GM4o5ge7, 220.0f, sampleRate);
|
||||
|
||||
}
|
||||
|
||||
Heavy_test::~Heavy_test() {
|
||||
// nothing to free
|
||||
}
|
||||
|
||||
HvTable *Heavy_test::getTableForHash(hv_uint32_t tableHash) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Heavy_test::scheduleMessageForReceiver(hv_uint32_t receiverHash, HvMessage *m) {
|
||||
switch (receiverHash) {
|
||||
default: return;
|
||||
}
|
||||
}
|
||||
|
||||
int Heavy_test::getParameterInfo(int index, HvParameterInfo *info) {
|
||||
if (info != nullptr) {
|
||||
switch (index) {
|
||||
default: {
|
||||
info->name = "invalid parameter index";
|
||||
info->hash = 0;
|
||||
info->type = HvParameterType::HV_PARAM_TYPE_PARAMETER_IN;
|
||||
info->minVal = 0.0f;
|
||||
info->maxVal = 0.0f;
|
||||
info->defaultVal = 0.0f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Send Function Implementations
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Context Process Implementation
|
||||
*/
|
||||
|
||||
int Heavy_test::process(float **inputBuffers, float **outputBuffers, int n) {
|
||||
while (hLp_hasData(&inQueue)) {
|
||||
hv_uint32_t numBytes = 0;
|
||||
ReceiverMessagePair *p = reinterpret_cast<ReceiverMessagePair *>(hLp_getReadBuffer(&inQueue, &numBytes));
|
||||
hv_assert(numBytes >= sizeof(ReceiverMessagePair));
|
||||
scheduleMessageForReceiver(p->receiverHash, &p->msg);
|
||||
hLp_consume(&inQueue);
|
||||
}
|
||||
|
||||
sendBangToReceiver(0xDD21C0EB); // send to __hv_bang~ on next cycle
|
||||
const int n4 = n & ~HV_N_SIMD_MASK; // ensure that the block size is a multiple of HV_N_SIMD
|
||||
|
||||
// temporary signal vars
|
||||
hv_bufferf_t Bf0, Bf1, Bf2, Bf3, Bf4;
|
||||
|
||||
// input and output vars
|
||||
hv_bufferf_t O0, O1;
|
||||
|
||||
// declare and init the zero buffer
|
||||
hv_bufferf_t ZERO; __hv_zero_f(VOf(ZERO));
|
||||
|
||||
hv_uint32_t nextBlock = blockStartTimestamp;
|
||||
for (int n = 0; n < n4; n += HV_N_SIMD) {
|
||||
|
||||
// process all of the messages for this block
|
||||
nextBlock += HV_N_SIMD;
|
||||
while (mq_hasMessageBefore(&mq, nextBlock)) {
|
||||
MessageNode *const node = mq_peek(&mq);
|
||||
node->sendMessage(this, node->let, node->m);
|
||||
mq_pop(&mq);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// zero output buffers
|
||||
__hv_zero_f(VOf(O0));
|
||||
__hv_zero_f(VOf(O1));
|
||||
|
||||
// process all signal functions
|
||||
__hv_phasor_k_f(&sPhasor_GM4o5ge7, VOf(Bf0));
|
||||
__hv_var_k_f(VOf(Bf1), 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f);
|
||||
__hv_sub_f(VIf(Bf0), VIf(Bf1), VOf(Bf1));
|
||||
__hv_abs_f(VIf(Bf1), VOf(Bf1));
|
||||
__hv_var_k_f(VOf(Bf0), 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f);
|
||||
__hv_sub_f(VIf(Bf1), VIf(Bf0), VOf(Bf0));
|
||||
__hv_var_k_f(VOf(Bf1), 6.283185307179586f, 6.283185307179586f, 6.283185307179586f, 6.283185307179586f, 6.283185307179586f, 6.283185307179586f, 6.283185307179586f, 6.283185307179586f);
|
||||
__hv_mul_f(VIf(Bf0), VIf(Bf1), VOf(Bf1));
|
||||
__hv_mul_f(VIf(Bf1), VIf(Bf1), VOf(Bf0));
|
||||
__hv_mul_f(VIf(Bf1), VIf(Bf0), VOf(Bf2));
|
||||
__hv_mul_f(VIf(Bf2), VIf(Bf0), VOf(Bf0));
|
||||
__hv_var_k_f(VOf(Bf3), 0.007833333333333f, 0.007833333333333f, 0.007833333333333f, 0.007833333333333f, 0.007833333333333f, 0.007833333333333f, 0.007833333333333f, 0.007833333333333f);
|
||||
__hv_var_k_f(VOf(Bf4), -0.166666666666667f, -0.166666666666667f, -0.166666666666667f, -0.166666666666667f, -0.166666666666667f, -0.166666666666667f, -0.166666666666667f, -0.166666666666667f);
|
||||
__hv_fma_f(VIf(Bf2), VIf(Bf4), VIf(Bf1), VOf(Bf1));
|
||||
__hv_fma_f(VIf(Bf0), VIf(Bf3), VIf(Bf1), VOf(Bf1));
|
||||
__hv_add_f(VIf(Bf1), VIf(O0), VOf(O0));
|
||||
|
||||
// save output vars to output buffer
|
||||
__hv_store_f(outputBuffers[0]+n, VIf(O0));
|
||||
__hv_store_f(outputBuffers[1]+n, VIf(O1));
|
||||
}
|
||||
|
||||
blockStartTimestamp = nextBlock;
|
||||
|
||||
return n4; // return the number of frames processed
|
||||
|
||||
}
|
||||
|
||||
int Heavy_test::processInline(float *inputBuffers, float *outputBuffers, int n4) {
|
||||
hv_assert(!(n4 & HV_N_SIMD_MASK)); // ensure that n4 is a multiple of HV_N_SIMD
|
||||
|
||||
// define the heavy input buffer for 0 channel(s)
|
||||
float **const bIn = NULL;
|
||||
|
||||
// define the heavy output buffer for 2 channel(s)
|
||||
float **const bOut = reinterpret_cast<float **>(hv_alloca(2*sizeof(float *)));
|
||||
bOut[0] = outputBuffers+(0*n4);
|
||||
bOut[1] = outputBuffers+(1*n4);
|
||||
|
||||
int n = process(bIn, bOut, n4);
|
||||
return n;
|
||||
}
|
||||
|
||||
int Heavy_test::processInlineInterleaved(float *inputBuffers, float *outputBuffers, int n4) {
|
||||
// ps hv_assert(n4 & ~HV_N_SIMD_MASK); // ensure that n4 is a multiple of HV_N_SIMD
|
||||
|
||||
// define the heavy input buffer for 0 channel(s), uninterleave
|
||||
float *const bIn = NULL;
|
||||
|
||||
// define the heavy output buffer for 2 channel(s)
|
||||
float *const bOut = reinterpret_cast<float *>(hv_alloca(2*n4*sizeof(float)));
|
||||
|
||||
int n = processInline(bIn, bOut, n4);
|
||||
|
||||
// interleave the heavy output into the output buffer
|
||||
#if HV_SIMD_AVX
|
||||
for (int i = 0, j = 0; j < n4; j += 8, i += 16) {
|
||||
__m256 x = _mm256_load_ps(bOut+j); // LLLLLLLL
|
||||
__m256 y = _mm256_load_ps(bOut+n4+j); // RRRRRRRR
|
||||
__m256 a = _mm256_unpacklo_ps(x, y); // LRLRLRLR
|
||||
__m256 b = _mm256_unpackhi_ps(x, y); // LRLRLRLR
|
||||
_mm256_store_ps(outputBuffers+i, a);
|
||||
_mm256_store_ps(outputBuffers+8+i, b);
|
||||
}
|
||||
#elif HV_SIMD_SSE
|
||||
for (int i = 0, j = 0; j < n4; j += 4, i += 8) {
|
||||
__m128 x = _mm_load_ps(bOut+j); // LLLL
|
||||
__m128 y = _mm_load_ps(bOut+n4+j); // RRRR
|
||||
__m128 a = _mm_unpacklo_ps(x, y); // LRLR
|
||||
__m128 b = _mm_unpackhi_ps(x, y); // LRLR
|
||||
_mm_store_ps(outputBuffers+i, a);
|
||||
_mm_store_ps(outputBuffers+4+i, b);
|
||||
}
|
||||
#elif HV_SIMD_NEON
|
||||
// https://community.arm.com/groups/processors/blog/2012/03/13/coding-for-neon--part-5-rearranging-vectors
|
||||
for (int i = 0, j = 0; j < n4; j += 4, i += 8) {
|
||||
float32x4_t x = vld1q_f32(bOut+j);
|
||||
float32x4_t y = vld1q_f32(bOut+n4+j);
|
||||
float32x4x2_t z = {x, y};
|
||||
vst2q_f32(outputBuffers+i, z); // interleave and store
|
||||
}
|
||||
#else // HV_SIMD_NONE
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
for (int j = 0; j < n4; ++j) {
|
||||
outputBuffers[i+2*j] = bOut[i*n4+j];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return n;
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Enzien Audio, Ltd.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions, and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the phrase "powered by heavy",
|
||||
* the heavy logo, and a hyperlink to https://enzienaudio.com, all in a visible
|
||||
* form.
|
||||
*
|
||||
* 2.1 If the Application is distributed in a store system (for example,
|
||||
* the Apple "App Store" or "Google Play"), the phrase "powered by heavy"
|
||||
* shall be included in the app description or the copyright text as well as
|
||||
* the in the app itself. The heavy logo will shall be visible in the app
|
||||
* itself as well.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _HEAVY_TEST_H_
|
||||
#define _HEAVY_TEST_H_
|
||||
|
||||
#include "HvHeavy.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if HV_APPLE
|
||||
#pragma mark - Heavy Context
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new patch instance.
|
||||
* Sample rate should be positive and in Hertz, e.g. 44100.0.
|
||||
*/
|
||||
HeavyContextInterface *hv_test_new(double sampleRate);
|
||||
|
||||
/**
|
||||
* Creates a new patch instance.
|
||||
* @param sampleRate Sample rate should be positive (> 0) and in Hertz, e.g. 48000.0.
|
||||
* @param poolKb Pool size is in kilobytes, and determines the maximum amount of memory
|
||||
* allocated to messages at any time. By default this is 10 KB.
|
||||
* @param inQueueKb The size of the input message queue in kilobytes. It determines the
|
||||
* amount of memory dedicated to holding scheduled messages between calls to
|
||||
* process(). Default is 2 KB.
|
||||
* @param outQueueKb The size of the output message queue in kilobytes. It determines the
|
||||
* amount of memory dedicated to holding scheduled messages to the default sendHook.
|
||||
* See getNextSentMessage() for info on accessing these messages. Default is 0 KB.
|
||||
*/
|
||||
HeavyContextInterface *hv_test_new_with_options(double sampleRate, int poolKb, int inQueueKb, int outQueueKb);
|
||||
|
||||
/**
|
||||
* Free the patch instance.
|
||||
*/
|
||||
void hv_test_free(HeavyContextInterface *instance);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // _HEAVY_TEST_H_
|
||||
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Enzien Audio, Ltd.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions, and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the phrase "powered by heavy",
|
||||
* the heavy logo, and a hyperlink to https://enzienaudio.com, all in a visible
|
||||
* form.
|
||||
*
|
||||
* 2.1 If the Application is distributed in a store system (for example,
|
||||
* the Apple "App Store" or "Google Play"), the phrase "powered by heavy"
|
||||
* shall be included in the app description or the copyright text as well as
|
||||
* the in the app itself. The heavy logo will shall be visible in the app
|
||||
* itself as well.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _HEAVY_CONTEXT_TEST_HPP_
|
||||
#define _HEAVY_CONTEXT_TEST_HPP_
|
||||
|
||||
// object includes
|
||||
#include "HeavyContext.hpp"
|
||||
#include "HvSignalVar.h"
|
||||
#include "HvSignalPhasor.h"
|
||||
#include "HvMath.h"
|
||||
|
||||
class Heavy_test : public HeavyContext {
|
||||
|
||||
public:
|
||||
Heavy_test(double sampleRate, int poolKb=10, int inQueueKb=2, int outQueueKb=0);
|
||||
~Heavy_test();
|
||||
|
||||
const char *getName() override { return "test"; }
|
||||
int getNumInputChannels() override { return 0; }
|
||||
int getNumOutputChannels() override { return 2; }
|
||||
|
||||
int process(float **inputBuffers, float **outputBuffer, int n) override;
|
||||
int processInline(float *inputBuffers, float *outputBuffer, int n) override;
|
||||
int processInlineInterleaved(float *inputBuffers, float *outputBuffer, int n) override;
|
||||
|
||||
int getParameterInfo(int index, HvParameterInfo *info) override;
|
||||
|
||||
private:
|
||||
HvTable *getTableForHash(hv_uint32_t tableHash) override;
|
||||
void scheduleMessageForReceiver(hv_uint32_t receiverHash, HvMessage *m) override;
|
||||
|
||||
// static sendMessage functions
|
||||
|
||||
// objects
|
||||
SignalPhasor sPhasor_GM4o5ge7;
|
||||
};
|
||||
|
||||
#endif // _HEAVY_CONTEXT_TEST_HPP_
|
||||
@@ -0,0 +1,318 @@
|
||||
/**
|
||||
* Copyright (c) 2014-2018 Enzien Audio Ltd.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "HeavyContext.hpp"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if HV_APPLE
|
||||
#pragma mark - Heavy Table
|
||||
#endif
|
||||
|
||||
HV_EXPORT bool hv_table_setLength(HeavyContextInterface *c, hv_uint32_t tableHash, hv_uint32_t newSampleLength) {
|
||||
hv_assert(c != nullptr);
|
||||
return c->setLengthForTable(tableHash, newSampleLength);
|
||||
}
|
||||
|
||||
HV_EXPORT float *hv_table_getBuffer(HeavyContextInterface *c, hv_uint32_t tableHash) {
|
||||
hv_assert(c != nullptr);
|
||||
return c->getBufferForTable(tableHash);
|
||||
}
|
||||
|
||||
HV_EXPORT hv_uint32_t hv_table_getLength(HeavyContextInterface *c, hv_uint32_t tableHash) {
|
||||
hv_assert(c != nullptr);
|
||||
return c->getLengthForTable(tableHash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#if HV_APPLE
|
||||
#pragma mark - Heavy Message
|
||||
#endif
|
||||
|
||||
HV_EXPORT hv_size_t hv_msg_getByteSize(hv_uint32_t numElements) {
|
||||
return msg_getCoreSize(numElements);
|
||||
}
|
||||
|
||||
HV_EXPORT void hv_msg_init(HvMessage *m, int numElements, hv_uint32_t timestamp) {
|
||||
msg_init(m, numElements, timestamp);
|
||||
}
|
||||
|
||||
HV_EXPORT hv_size_t hv_msg_getNumElements(const HvMessage *m) {
|
||||
return msg_getNumElements(m);
|
||||
}
|
||||
|
||||
HV_EXPORT hv_uint32_t hv_msg_getTimestamp(const HvMessage *m) {
|
||||
return msg_getTimestamp(m);
|
||||
}
|
||||
|
||||
HV_EXPORT void hv_msg_setTimestamp(HvMessage *m, hv_uint32_t timestamp) {
|
||||
msg_setTimestamp(m, timestamp);
|
||||
}
|
||||
|
||||
HV_EXPORT bool hv_msg_isBang(const HvMessage *const m, int i) {
|
||||
return msg_isBang(m,i);
|
||||
}
|
||||
|
||||
HV_EXPORT void hv_msg_setBang(HvMessage *m, int i) {
|
||||
msg_setBang(m,i);
|
||||
}
|
||||
|
||||
HV_EXPORT bool hv_msg_isFloat(const HvMessage *const m, int i) {
|
||||
return msg_isFloat(m, i);
|
||||
}
|
||||
|
||||
HV_EXPORT float hv_msg_getFloat(const HvMessage *const m, int i) {
|
||||
return msg_getFloat(m,i);
|
||||
}
|
||||
|
||||
HV_EXPORT void hv_msg_setFloat(HvMessage *m, int i, float f) {
|
||||
msg_setFloat(m,i,f);
|
||||
}
|
||||
|
||||
HV_EXPORT bool hv_msg_isSymbol(const HvMessage *const m, int i) {
|
||||
return msg_isSymbol(m,i);
|
||||
}
|
||||
|
||||
HV_EXPORT const char *hv_msg_getSymbol(const HvMessage *const m, int i) {
|
||||
return msg_getSymbol(m,i);
|
||||
}
|
||||
|
||||
HV_EXPORT void hv_msg_setSymbol(HvMessage *m, int i, const char *s) {
|
||||
msg_setSymbol(m,i,s);
|
||||
}
|
||||
|
||||
HV_EXPORT bool hv_msg_isHash(const HvMessage *const m, int i) {
|
||||
return msg_isHash(m, i);
|
||||
}
|
||||
|
||||
HV_EXPORT hv_uint32_t hv_msg_getHash(const HvMessage *const m, int i) {
|
||||
return msg_getHash(m, i);
|
||||
}
|
||||
|
||||
HV_EXPORT bool hv_msg_hasFormat(const HvMessage *const m, const char *fmt) {
|
||||
return msg_hasFormat(m, fmt);
|
||||
}
|
||||
|
||||
HV_EXPORT char *hv_msg_toString(const HvMessage *const m) {
|
||||
return msg_toString(m);
|
||||
}
|
||||
|
||||
HV_EXPORT HvMessage *hv_msg_copy(const HvMessage *const m) {
|
||||
return msg_copy(m);
|
||||
}
|
||||
|
||||
HV_EXPORT void hv_msg_free(HvMessage *m) {
|
||||
msg_free(m);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#if HV_APPLE
|
||||
#pragma mark - Heavy Common
|
||||
#endif
|
||||
|
||||
HV_EXPORT int hv_getSize(HeavyContextInterface *c) {
|
||||
hv_assert(c != nullptr);
|
||||
return (int) c->getSize();
|
||||
}
|
||||
|
||||
HV_EXPORT double hv_getSampleRate(HeavyContextInterface *c) {
|
||||
hv_assert(c != nullptr);
|
||||
return c->getSampleRate();
|
||||
}
|
||||
|
||||
HV_EXPORT int hv_getNumInputChannels(HeavyContextInterface *c) {
|
||||
hv_assert(c != nullptr);
|
||||
return c->getNumInputChannels();
|
||||
}
|
||||
|
||||
HV_EXPORT int hv_getNumOutputChannels(HeavyContextInterface *c) {
|
||||
hv_assert(c != nullptr);
|
||||
return c->getNumOutputChannels();
|
||||
}
|
||||
|
||||
HV_EXPORT void hv_setPrintHook(HeavyContextInterface *c, HvPrintHook_t *f) {
|
||||
hv_assert(c != nullptr);
|
||||
c->setPrintHook(f);
|
||||
}
|
||||
|
||||
HV_EXPORT HvPrintHook_t *hv_getPrintHook(HeavyContextInterface *c) {
|
||||
hv_assert(c != nullptr);
|
||||
return c->getPrintHook();
|
||||
}
|
||||
|
||||
HV_EXPORT void hv_setSendHook(HeavyContextInterface *c, HvSendHook_t *f) {
|
||||
hv_assert(c != nullptr);
|
||||
c->setSendHook(f);
|
||||
}
|
||||
|
||||
HV_EXPORT hv_uint32_t hv_stringToHash(const char *s) {
|
||||
return hv_string_to_hash(s);
|
||||
}
|
||||
|
||||
HV_EXPORT bool hv_sendBangToReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash) {
|
||||
hv_assert(c != nullptr);
|
||||
return c->sendBangToReceiver(receiverHash);
|
||||
}
|
||||
|
||||
HV_EXPORT bool hv_sendFloatToReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, float x) {
|
||||
hv_assert(c != nullptr);
|
||||
return c->sendFloatToReceiver(receiverHash, x);
|
||||
}
|
||||
|
||||
HV_EXPORT bool hv_sendSymbolToReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, char *s) {
|
||||
hv_assert(c != nullptr);
|
||||
return c->sendSymbolToReceiver(receiverHash, s);
|
||||
}
|
||||
|
||||
HV_EXPORT bool hv_sendMessageToReceiverV(
|
||||
HeavyContextInterface *c, hv_uint32_t receiverHash, double delayMs, const char *format, ...) {
|
||||
hv_assert(c != nullptr);
|
||||
hv_assert(delayMs >= 0.0);
|
||||
hv_assert(format != nullptr);
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
const int numElem = (int) hv_strlen(format);
|
||||
HvMessage *m = HV_MESSAGE_ON_STACK(numElem);
|
||||
msg_init(m, numElem, c->getCurrentSample() + (hv_uint32_t) (hv_max_d(0.0, delayMs)*c->getSampleRate()/1000.0));
|
||||
for (int i = 0; i < numElem; i++) {
|
||||
switch (format[i]) {
|
||||
case 'b': msg_setBang(m, i); break;
|
||||
case 'f': msg_setFloat(m, i, (float) va_arg(ap, double)); break;
|
||||
case 'h': msg_setHash(m, i, (int) va_arg(ap, int)); break;
|
||||
case 's': msg_setSymbol(m, i, (char *) va_arg(ap, char *)); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
return c->sendMessageToReceiver(receiverHash, delayMs, m);
|
||||
}
|
||||
|
||||
HV_EXPORT bool hv_sendMessageToReceiver(
|
||||
HeavyContextInterface *c, hv_uint32_t receiverHash, double delayMs, HvMessage *m) {
|
||||
hv_assert(c != nullptr);
|
||||
return c->sendMessageToReceiver(receiverHash, delayMs, m);
|
||||
}
|
||||
|
||||
HV_EXPORT void hv_cancelMessage(HeavyContextInterface *c, HvMessage *m, void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) {
|
||||
hv_assert(c != nullptr);
|
||||
c->cancelMessage(m, sendMessage);
|
||||
}
|
||||
|
||||
HV_EXPORT const char *hv_getName(HeavyContextInterface *c) {
|
||||
hv_assert(c != nullptr);
|
||||
return c->getName();
|
||||
}
|
||||
|
||||
HV_EXPORT void hv_setUserData(HeavyContextInterface *c, void *userData) {
|
||||
hv_assert(c != nullptr);
|
||||
c->setUserData(userData);
|
||||
}
|
||||
|
||||
HV_EXPORT void *hv_getUserData(HeavyContextInterface *c) {
|
||||
hv_assert(c != nullptr);
|
||||
return c->getUserData();
|
||||
}
|
||||
|
||||
HV_EXPORT double hv_getCurrentTime(HeavyContextInterface *c) {
|
||||
hv_assert(c != nullptr);
|
||||
return (double) c->samplesToMilliseconds(c->getCurrentSample());
|
||||
}
|
||||
|
||||
HV_EXPORT hv_uint32_t hv_getCurrentSample(HeavyContextInterface *c) {
|
||||
hv_assert(c != nullptr);
|
||||
return c->getCurrentSample();
|
||||
}
|
||||
|
||||
HV_EXPORT float hv_samplesToMilliseconds(HeavyContextInterface *c, hv_uint32_t numSamples) {
|
||||
hv_assert(c != nullptr);
|
||||
return c->samplesToMilliseconds(numSamples);
|
||||
}
|
||||
|
||||
HV_EXPORT hv_uint32_t hv_millisecondsToSamples(HeavyContextInterface *c, float ms) {
|
||||
hv_assert(c != nullptr);
|
||||
return c->millisecondsToSamples(ms);
|
||||
}
|
||||
|
||||
HV_EXPORT int hv_getParameterInfo(HeavyContextInterface *c, int index, HvParameterInfo *info) {
|
||||
hv_assert(c != nullptr);
|
||||
return c->getParameterInfo(index, info);
|
||||
}
|
||||
|
||||
HV_EXPORT void hv_lock_acquire(HeavyContextInterface *c) {
|
||||
hv_assert(c != nullptr);
|
||||
c->lockAcquire();
|
||||
}
|
||||
|
||||
HV_EXPORT bool hv_lock_try(HeavyContextInterface *c) {
|
||||
hv_assert(c != nullptr);
|
||||
return c->lockTry();
|
||||
}
|
||||
|
||||
HV_EXPORT void hv_lock_release(HeavyContextInterface *c) {
|
||||
hv_assert(c != nullptr);
|
||||
c->lockRelease();
|
||||
}
|
||||
|
||||
HV_EXPORT void hv_setInputMessageQueueSize(HeavyContextInterface *c, hv_uint32_t inQueueKb) {
|
||||
hv_assert(c != nullptr);
|
||||
c->setInputMessageQueueSize(inQueueKb);
|
||||
}
|
||||
|
||||
HV_EXPORT void hv_setOutputMessageQueueSize(HeavyContextInterface *c, hv_uint32_t outQueueKb) {
|
||||
hv_assert(c != nullptr);
|
||||
c->setOutputMessageQueueSize(outQueueKb);
|
||||
}
|
||||
|
||||
HV_EXPORT bool hv_getNextSentMessage(HeavyContextInterface *c, hv_uint32_t *destinationHash, HvMessage *outMsg, hv_uint32_t msgLength) {
|
||||
hv_assert(c != nullptr);
|
||||
hv_assert(destinationHash != nullptr);
|
||||
hv_assert(outMsg != nullptr);
|
||||
return c->getNextSentMessage(destinationHash, outMsg, msgLength);
|
||||
}
|
||||
|
||||
|
||||
#if HV_APPLE
|
||||
#pragma mark - Heavy Common
|
||||
#endif
|
||||
|
||||
HV_EXPORT int hv_process(HeavyContextInterface *c, float **inputBuffers, float **outputBuffers, int n) {
|
||||
hv_assert(c != nullptr);
|
||||
return c->process(inputBuffers, outputBuffers, n);
|
||||
}
|
||||
|
||||
HV_EXPORT int hv_processInline(HeavyContextInterface *c, float *inputBuffers, float *outputBuffers, int n) {
|
||||
hv_assert(c != nullptr);
|
||||
return c->processInline(inputBuffers, outputBuffers, n);
|
||||
}
|
||||
|
||||
HV_EXPORT int hv_processInlineInterleaved(HeavyContextInterface *c, float *inputBuffers, float *outputBuffers, int n) {
|
||||
hv_assert(c != nullptr);
|
||||
return c->processInlineInterleaved(inputBuffers, outputBuffers, n);
|
||||
}
|
||||
|
||||
HV_EXPORT void hv_delete(HeavyContextInterface *c) {
|
||||
delete c;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,413 @@
|
||||
/**
|
||||
* Copyright (c) 2014-2018 Enzien Audio Ltd.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _HEAVY_H_
|
||||
#define _HEAVY_H_
|
||||
|
||||
#include "HvUtils.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef _HEAVY_DECLARATIONS_
|
||||
#define _HEAVY_DECLARATIONS_
|
||||
|
||||
#ifdef __cplusplus
|
||||
class HeavyContextInterface;
|
||||
#else
|
||||
typedef struct HeavyContextInterface HeavyContextInterface;
|
||||
#endif
|
||||
|
||||
typedef struct HvMessage HvMessage;
|
||||
|
||||
typedef enum {
|
||||
HV_PARAM_TYPE_PARAMETER_IN,
|
||||
HV_PARAM_TYPE_PARAMETER_OUT,
|
||||
HV_PARAM_TYPE_EVENT_IN,
|
||||
HV_PARAM_TYPE_EVENT_OUT
|
||||
} HvParameterType;
|
||||
|
||||
typedef struct HvParameterInfo {
|
||||
const char *name; // the human readable parameter name
|
||||
hv_uint32_t hash; // an integer identified used by heavy for this parameter
|
||||
HvParameterType type; // type of this parameter
|
||||
float minVal; // the minimum value of this parameter
|
||||
float maxVal; // the maximum value of this parameter
|
||||
float defaultVal; // the default value of this parameter
|
||||
} HvParameterInfo;
|
||||
|
||||
typedef void (HvSendHook_t) (HeavyContextInterface *context, const char *sendName, hv_uint32_t sendHash, const HvMessage *msg);
|
||||
typedef void (HvPrintHook_t) (HeavyContextInterface *context, const char *printName, const char *str, const HvMessage *msg);
|
||||
|
||||
#endif // _HEAVY_DECLARATIONS_
|
||||
|
||||
|
||||
|
||||
#if HV_APPLE
|
||||
#pragma mark - Heavy Context
|
||||
#endif
|
||||
|
||||
/** Deletes a patch instance. */
|
||||
void hv_delete(HeavyContextInterface *c);
|
||||
|
||||
|
||||
|
||||
#if HV_APPLE
|
||||
#pragma mark - Heavy Process
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Processes one block of samples for a patch instance. The buffer format is an array of float channel arrays.
|
||||
* If the context has not input or output channels, the respective argument may be NULL.
|
||||
* The number of samples to to tbe processed should be a multiple of 1, 4, or 8, depending on if
|
||||
* no, SSE or NEON, or AVX optimisation is being used, respectively.
|
||||
* e.g. [[LLLL][RRRR]]
|
||||
* This function support in-place processing.
|
||||
*
|
||||
* @return The number of samples processed.
|
||||
*
|
||||
* This function is NOT thread-safe. It is assumed that only the audio thread will execute this function.
|
||||
*/
|
||||
int hv_process(HeavyContextInterface *c, float **inputBuffers, float **outputBuffers, int n);
|
||||
|
||||
/**
|
||||
* Processes one block of samples for a patch instance. The buffer format is an uninterleaved float array of channels.
|
||||
* If the context has not input or output channels, the respective argument may be NULL.
|
||||
* The number of samples to to tbe processed should be a multiple of 1, 4, or 8, depending on if
|
||||
* no, SSE or NEON, or AVX optimisation is being used, respectively.
|
||||
* e.g. [LLLLRRRR]
|
||||
* This function support in-place processing.
|
||||
*
|
||||
* @return The number of samples processed.
|
||||
*
|
||||
* This function is NOT thread-safe. It is assumed that only the audio thread will execute this function.
|
||||
*/
|
||||
int hv_processInline(HeavyContextInterface *c, float *inputBuffers, float *outputBuffers, int n);
|
||||
|
||||
/**
|
||||
* Processes one block of samples for a patch instance. The buffer format is an interleaved float array of channels.
|
||||
* If the context has not input or output channels, the respective argument may be NULL.
|
||||
* The number of samples to to tbe processed should be a multiple of 1, 4, or 8, depending on if
|
||||
* no, SSE or NEON, or AVX optimisation is being used, respectively.
|
||||
* e.g. [LRLRLRLR]
|
||||
* This function support in-place processing.
|
||||
*
|
||||
* @return The number of samples processed.
|
||||
*
|
||||
* This function is NOT thread-safe. It is assumed that only the audio thread will execute this function.
|
||||
*/
|
||||
int hv_processInlineInterleaved(HeavyContextInterface *c, float *inputBuffers, float *outputBuffers, int n);
|
||||
|
||||
|
||||
|
||||
#if HV_APPLE
|
||||
#pragma mark - Heavy Common
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Returns the total size in bytes of the context.
|
||||
* This value may change if tables are resized.
|
||||
*/
|
||||
int hv_getSize(HeavyContextInterface *c);
|
||||
|
||||
/** Returns the sample rate with which this context has been configured. */
|
||||
double hv_getSampleRate(HeavyContextInterface *c);
|
||||
|
||||
/** Returns the number of input channels with which this context has been configured. */
|
||||
int hv_getNumInputChannels(HeavyContextInterface *c);
|
||||
|
||||
/** Returns the number of output channels with which this context has been configured. */
|
||||
int hv_getNumOutputChannels(HeavyContextInterface *c);
|
||||
|
||||
/** Set the print hook. The function is called whenever a message is sent to a print object. */
|
||||
void hv_setPrintHook(HeavyContextInterface *c, HvPrintHook_t *f);
|
||||
|
||||
/** Returns the print hook, or NULL. */
|
||||
HvPrintHook_t *hv_getPrintHook(HeavyContextInterface *c);
|
||||
|
||||
/**
|
||||
* Set the send hook. The function is called whenever a message is sent to any send object.
|
||||
* Messages returned by this function should NEVER be freed. If the message must persist, call
|
||||
* hv_msg_copy() first.
|
||||
*/
|
||||
void hv_setSendHook(HeavyContextInterface *c, HvSendHook_t *f);
|
||||
|
||||
/** Returns a 32-bit hash of any string. Returns 0 if string is NULL. */
|
||||
hv_uint32_t hv_stringToHash(const char *s);
|
||||
|
||||
/**
|
||||
* A convenience function to send a bang to a receiver to be processed immediately.
|
||||
* The receiver is addressed with its hash, which can also be determined using hv_stringToHash().
|
||||
* This function is thread-safe.
|
||||
*
|
||||
* @return True if the message was accepted. False if the message could not fit onto
|
||||
* the message queue to be processed this block.
|
||||
*/
|
||||
bool hv_sendBangToReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash);
|
||||
|
||||
/**
|
||||
* A convenience function to send a float to a receiver to be processed immediately.
|
||||
* The receiver is addressed with its hash, which can also be determined using hv_stringToHash().
|
||||
* This function is thread-safe.
|
||||
*
|
||||
* @return True if the message was accepted. False if the message could not fit onto
|
||||
* the message queue to be processed this block.
|
||||
*/
|
||||
bool hv_sendFloatToReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, const float x);
|
||||
|
||||
/**
|
||||
* A convenience function to send a symbol to a receiver to be processed immediately.
|
||||
* The receiver is addressed with its hash, which can also be determined using hv_stringToHash().
|
||||
* This function is thread-safe.
|
||||
*
|
||||
* @return True if the message was accepted. False if the message could not fit onto
|
||||
* the message queue to be processed this block.
|
||||
*/
|
||||
bool hv_sendSymbolToReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, char *s);
|
||||
|
||||
/**
|
||||
* Sends a formatted message to a receiver that can be scheduled for the future.
|
||||
* The receiver is addressed with its hash, which can also be determined using hv_stringToHash().
|
||||
* This function is thread-safe.
|
||||
*
|
||||
* @return True if the message was accepted. False if the message could not fit onto
|
||||
* the message queue to be processed this block.
|
||||
*/
|
||||
bool hv_sendMessageToReceiverV(HeavyContextInterface *c, hv_uint32_t receiverHash, double delayMs, const char *format, ...);
|
||||
|
||||
/**
|
||||
* Sends a message to a receiver that can be scheduled for the future.
|
||||
* The receiver is addressed with its hash, which can also be determined using hv_stringToHash().
|
||||
* This function is thread-safe.
|
||||
*
|
||||
* @return True if the message was accepted. False if the message could not fit onto
|
||||
* the message queue to be processed this block.
|
||||
*/
|
||||
bool hv_sendMessageToReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, double delayMs, HvMessage *m);
|
||||
|
||||
/**
|
||||
* Cancels a previously scheduled message.
|
||||
*
|
||||
* @param sendMessage May be NULL.
|
||||
*/
|
||||
void hv_cancelMessage(HeavyContextInterface *c, HvMessage *m, void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *));
|
||||
|
||||
/** Returns the read-only user-assigned name of this patch. */
|
||||
const char *hv_getName(HeavyContextInterface *c);
|
||||
|
||||
/** Sets a user-definable value. This value is never manipulated by Heavy. */
|
||||
void hv_setUserData(HeavyContextInterface *c, void *userData);
|
||||
|
||||
/** Returns the user-defined data. */
|
||||
void *hv_getUserData(HeavyContextInterface *c);
|
||||
|
||||
/** Returns the current patch time in milliseconds. This value may have rounding errors. */
|
||||
double hv_getCurrentTime(HeavyContextInterface *c);
|
||||
|
||||
/** Returns the current patch time in samples. This value is always exact. */
|
||||
hv_uint32_t hv_getCurrentSample(HeavyContextInterface *c);
|
||||
|
||||
/**
|
||||
* Returns information about each parameter such as name, hash, and range.
|
||||
* The total number of parameters is always returned.
|
||||
*
|
||||
* @param index The parameter index.
|
||||
* @param info A pointer to a HvParameterInfo struct. May be null.
|
||||
*
|
||||
* @return The total number of parameters.
|
||||
*/
|
||||
int hv_getParameterInfo(HeavyContextInterface *c, int index, HvParameterInfo *info);
|
||||
|
||||
/** */
|
||||
float hv_samplesToMilliseconds(HeavyContextInterface *c, hv_uint32_t numSamples);
|
||||
|
||||
/** Converts milliseconds to samples. Input is limited to non-negative range. */
|
||||
hv_uint32_t hv_millisecondsToSamples(HeavyContextInterface *c, float ms);
|
||||
|
||||
/**
|
||||
* Acquire the input message queue lock.
|
||||
*
|
||||
* This function will block until the message lock as been acquired.
|
||||
* Typical applications will not require the use of this function.
|
||||
*
|
||||
* @param c A Heavy context.
|
||||
*/
|
||||
void hv_lock_acquire(HeavyContextInterface *c);
|
||||
|
||||
/**
|
||||
* Try to acquire the input message queue lock.
|
||||
*
|
||||
* If the lock has been acquired, hv_lock_release() must be called to release it.
|
||||
* Typical applications will not require the use of this function.
|
||||
*
|
||||
* @param c A Heavy context.
|
||||
*
|
||||
* @return Returns true if the lock has been acquired, false otherwise.
|
||||
*/
|
||||
bool hv_lock_try(HeavyContextInterface *c);
|
||||
|
||||
/**
|
||||
* Release the input message queue lock.
|
||||
*
|
||||
* Typical applications will not require the use of this function.
|
||||
*
|
||||
* @param c A Heavy context.
|
||||
*/
|
||||
void hv_lock_release(HeavyContextInterface *c);
|
||||
|
||||
/**
|
||||
* Set the size of the input message queue in kilobytes.
|
||||
*
|
||||
* The buffer is reset and all existing contents are lost on resize.
|
||||
*
|
||||
* @param c A Heavy context.
|
||||
* @param inQueueKb Must be positive i.e. at least one.
|
||||
*/
|
||||
void hv_setInputMessageQueueSize(HeavyContextInterface *c, hv_uint32_t inQueueKb);
|
||||
|
||||
/**
|
||||
* Set the size of the output message queue in kilobytes.
|
||||
*
|
||||
* The buffer is reset and all existing contents are lost on resize.
|
||||
* Only the default sendhook uses the outgoing message queue. If the default
|
||||
* sendhook is not being used, then this function is not useful.
|
||||
*
|
||||
* @param c A Heavy context.
|
||||
* @param outQueueKb Must be postive i.e. at least one.
|
||||
*/
|
||||
void hv_setOutputMessageQueueSize(HeavyContextInterface *c, hv_uint32_t outQueueKb);
|
||||
|
||||
/**
|
||||
* Get the next message in the outgoing queue, will also consume the message.
|
||||
* Returns false if there are no messages.
|
||||
*
|
||||
* @param c A Heavy context.
|
||||
* @param destinationHash a hash of the name of the receiver the message was sent to.
|
||||
* @param outMsg message pointer that is filled by the next message contents.
|
||||
* @param msgLength length of outMsg in bytes.
|
||||
*
|
||||
* @return True if there is a message in the outgoing queue.
|
||||
*/
|
||||
bool hv_getNextSentMessage(HeavyContextInterface *c, hv_uint32_t *destinationHash, HvMessage *outMsg, hv_uint32_t msgLength);
|
||||
|
||||
|
||||
|
||||
#if HV_APPLE
|
||||
#pragma mark - Heavy Message
|
||||
#endif
|
||||
|
||||
typedef struct HvMessage HvMessage;
|
||||
|
||||
/** Returns the total size in bytes of a HvMessage with a number of elements on the heap. */
|
||||
unsigned long hv_msg_getByteSize(hv_uint32_t numElements);
|
||||
|
||||
/** Initialise a HvMessage structure with the number of elements and a timestamp (in samples). */
|
||||
void hv_msg_init(HvMessage *m, int numElements, hv_uint32_t timestamp);
|
||||
|
||||
/** Returns the number of elements in this message. */
|
||||
unsigned long hv_msg_getNumElements(const HvMessage *m);
|
||||
|
||||
/** Returns the time at which this message exists (in samples). */
|
||||
hv_uint32_t hv_msg_getTimestamp(const HvMessage *m);
|
||||
|
||||
/** Set the time at which this message should be executed (in samples). */
|
||||
void hv_msg_setTimestamp(HvMessage *m, hv_uint32_t timestamp);
|
||||
|
||||
/** Returns true of the indexed element is a bang. False otherwise. Index is not bounds checked. */
|
||||
bool hv_msg_isBang(const HvMessage *const m, int i);
|
||||
|
||||
/** Sets the indexed element to a bang. Index is not bounds checked. */
|
||||
void hv_msg_setBang(HvMessage *m, int i);
|
||||
|
||||
/** Returns true of the indexed element is a float. False otherwise. Index is not bounds checked. */
|
||||
bool hv_msg_isFloat(const HvMessage *const m, int i);
|
||||
|
||||
/** Returns the indexed element as a float value. Index is not bounds checked. */
|
||||
float hv_msg_getFloat(const HvMessage *const m, int i);
|
||||
|
||||
/** Sets the indexed element to float value. Index is not bounds checked. */
|
||||
void hv_msg_setFloat(HvMessage *m, int i, float f);
|
||||
|
||||
/** Returns true of the indexed element is a symbol. False otherwise. Index is not bounds checked. */
|
||||
bool hv_msg_isSymbol(const HvMessage *const m, int i);
|
||||
|
||||
/** Returns the indexed element as a symbol value. Index is not bounds checked. */
|
||||
const char *hv_msg_getSymbol(const HvMessage *const m, int i);
|
||||
|
||||
/** Returns true of the indexed element is a hash. False otherwise. Index is not bounds checked. */
|
||||
bool hv_msg_isHash(const HvMessage *const m, int i);
|
||||
|
||||
/** Returns the indexed element as a hash value. Index is not bounds checked. */
|
||||
hv_uint32_t hv_msg_getHash(const HvMessage *const m, int i);
|
||||
|
||||
/** Sets the indexed element to symbol value. Index is not bounds checked. */
|
||||
void hv_msg_setSymbol(HvMessage *m, int i, const char *s);
|
||||
|
||||
/**
|
||||
* Returns true if the message has the given format, in number of elements and type. False otherwise.
|
||||
* Valid element types are:
|
||||
* 'b': bang
|
||||
* 'f': float
|
||||
* 's': symbol
|
||||
*
|
||||
* For example, a message with three floats would have a format of "fff". A single bang is "b".
|
||||
* A message with two symbols is "ss". These types can be mixed and matched in any way.
|
||||
*/
|
||||
bool hv_msg_hasFormat(const HvMessage *const m, const char *fmt);
|
||||
|
||||
/**
|
||||
* Returns a basic string representation of the message.
|
||||
* The character array MUST be deallocated by the caller.
|
||||
*/
|
||||
char *hv_msg_toString(const HvMessage *const m);
|
||||
|
||||
/** Copy a message onto the stack. The message persists. */
|
||||
HvMessage *hv_msg_copy(const HvMessage *const m);
|
||||
|
||||
/** Free a copied message. */
|
||||
void hv_msg_free(HvMessage *m);
|
||||
|
||||
|
||||
|
||||
#if HV_APPLE
|
||||
#pragma mark - Heavy Table
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Resizes the table to the given length.
|
||||
*
|
||||
* Existing contents are copied to the new table. Remaining space is cleared
|
||||
* if the table is longer than the original, truncated otherwise.
|
||||
*
|
||||
* @param tableHash The table identifier.
|
||||
* @param newSampleLength The new length of the table, in samples. Must be positive.
|
||||
*
|
||||
* @return False if the table could not be found. True otherwise.
|
||||
*/
|
||||
bool hv_table_setLength(HeavyContextInterface *c, hv_uint32_t tableHash, hv_uint32_t newSampleLength);
|
||||
|
||||
/** Returns a pointer to the raw buffer backing this table. DO NOT free it. */
|
||||
float *hv_table_getBuffer(HeavyContextInterface *c, hv_uint32_t tableHash);
|
||||
|
||||
/** Returns the length of this table in samples. */
|
||||
hv_uint32_t hv_table_getLength(HeavyContextInterface *c, hv_uint32_t tableHash);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // _HEAVY_H_
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Copyright (c) 2014-2018 Enzien Audio Ltd.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _HEAVY_INTERNAL_H_
|
||||
#define _HEAVY_INTERNAL_H_
|
||||
|
||||
#include "HvHeavy.h"
|
||||
#include "HvUtils.h"
|
||||
#include "HvTable.h"
|
||||
#include "HvMessage.h"
|
||||
#include "HvMath.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
HvTable *hv_table_get(HeavyContextInterface *c, hv_uint32_t tableHash);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void hv_scheduleMessageForReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, HvMessage *m);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
HvMessage *hv_scheduleMessageForObject(HeavyContextInterface *c, const HvMessage *m,
|
||||
void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *),
|
||||
int letIndex);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,136 @@
|
||||
/**
|
||||
* Copyright (c) 2014-2018 Enzien Audio Ltd.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "HvLightPipe.h"
|
||||
|
||||
#if __SSE__ || HV_SIMD_SSE
|
||||
#include <xmmintrin.h>
|
||||
#define hv_sfence() _mm_sfence()
|
||||
#elif __arm__ || HV_SIMD_NEON
|
||||
#if __ARM_ACLE
|
||||
#include <arm_acle.h>
|
||||
// https://msdn.microsoft.com/en-us/library/hh875058.aspx#BarrierRestrictions
|
||||
// http://doxygen.reactos.org/d8/d47/armintr_8h_a02be7ec76ca51842bc90d9b466b54752.html
|
||||
#define hv_sfence() __dmb(0xE) /* _ARM_BARRIER_ST */
|
||||
#elif defined(__GNUC__)
|
||||
#define hv_sfence() __asm__ volatile ("dmb 0xE":::"memory")
|
||||
#else
|
||||
// http://stackoverflow.com/questions/19965076/gcc-memory-barrier-sync-synchronize-vs-asm-volatile-memory
|
||||
#define hv_sfence() __sync_synchronize()
|
||||
#endif
|
||||
#elif HV_WIN
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms684208(v=vs.85).aspx
|
||||
#define hv_sfence() _WriteBarrier()
|
||||
#else
|
||||
#define hv_sfence() __asm__ volatile("" : : : "memory")
|
||||
#endif
|
||||
|
||||
#define HLP_STOP 0
|
||||
#define HLP_LOOP 0xFFFFFFFF
|
||||
#define HLP_SET_UINT32_AT_BUFFER(a, b) (*((hv_uint32_t *) (a)) = (b))
|
||||
#define HLP_GET_UINT32_AT_BUFFER(a) (*((hv_uint32_t *) (a)))
|
||||
|
||||
hv_uint32_t hLp_init(HvLightPipe *q, hv_uint32_t numBytes) {
|
||||
if (numBytes > 0) {
|
||||
q->buffer = (char *) hv_malloc(numBytes);
|
||||
hv_assert(q->buffer != NULL);
|
||||
HLP_SET_UINT32_AT_BUFFER(q->buffer, HLP_STOP);
|
||||
} else {
|
||||
q->buffer = NULL;
|
||||
}
|
||||
q->writeHead = q->buffer;
|
||||
q->readHead = q->buffer;
|
||||
q->len = numBytes;
|
||||
q->remainingBytes = numBytes;
|
||||
return numBytes;
|
||||
}
|
||||
|
||||
void hLp_free(HvLightPipe *q) {
|
||||
hv_free(q->buffer);
|
||||
}
|
||||
|
||||
hv_uint32_t hLp_hasData(HvLightPipe *q) {
|
||||
hv_uint32_t x = HLP_GET_UINT32_AT_BUFFER(q->readHead);
|
||||
if (x == HLP_LOOP) {
|
||||
q->readHead = q->buffer;
|
||||
x = HLP_GET_UINT32_AT_BUFFER(q->readHead);
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
char *hLp_getWriteBuffer(HvLightPipe *q, hv_uint32_t bytesToWrite) {
|
||||
char *const readHead = q->readHead;
|
||||
char *const oldWriteHead = q->writeHead;
|
||||
const hv_uint32_t totalByteRequirement = bytesToWrite + 2*sizeof(hv_uint32_t);
|
||||
|
||||
// check if there is enough space to write the data in the remaining
|
||||
// length of the buffer
|
||||
if (totalByteRequirement <= q->remainingBytes) {
|
||||
char *const newWriteHead = oldWriteHead + sizeof(hv_uint32_t) + bytesToWrite;
|
||||
|
||||
// check if writing would overwrite existing data in the pipe (return NULL if so)
|
||||
if ((oldWriteHead < readHead) && (newWriteHead >= readHead)) return NULL;
|
||||
else return (oldWriteHead + sizeof(hv_uint32_t));
|
||||
} else {
|
||||
// there isn't enough space, try looping around to the start
|
||||
if (totalByteRequirement <= q->len) {
|
||||
if ((oldWriteHead < readHead) || ((q->buffer + totalByteRequirement) > readHead)) {
|
||||
return NULL; // overwrite condition
|
||||
} else {
|
||||
q->writeHead = q->buffer;
|
||||
q->remainingBytes = q->len;
|
||||
HLP_SET_UINT32_AT_BUFFER(q->buffer, HLP_STOP);
|
||||
hv_sfence();
|
||||
HLP_SET_UINT32_AT_BUFFER(oldWriteHead, HLP_LOOP);
|
||||
return q->buffer + sizeof(hv_uint32_t);
|
||||
}
|
||||
} else {
|
||||
return NULL; // there isn't enough space to write the data
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void hLp_produce(HvLightPipe *q, hv_uint32_t numBytes) {
|
||||
hv_assert(q->remainingBytes >= (numBytes + 2*sizeof(hv_uint32_t)));
|
||||
q->remainingBytes -= (sizeof(hv_uint32_t) + numBytes);
|
||||
char *const oldWriteHead = q->writeHead;
|
||||
q->writeHead += (sizeof(hv_uint32_t) + numBytes);
|
||||
HLP_SET_UINT32_AT_BUFFER(q->writeHead, HLP_STOP);
|
||||
|
||||
// save everything before this point to memory
|
||||
hv_sfence();
|
||||
|
||||
// then save this
|
||||
HLP_SET_UINT32_AT_BUFFER(oldWriteHead, numBytes);
|
||||
}
|
||||
|
||||
char *hLp_getReadBuffer(HvLightPipe *q, hv_uint32_t *numBytes) {
|
||||
*numBytes = HLP_GET_UINT32_AT_BUFFER(q->readHead);
|
||||
char *const readBuffer = q->readHead + sizeof(hv_uint32_t);
|
||||
return readBuffer;
|
||||
}
|
||||
|
||||
void hLp_consume(HvLightPipe *q) {
|
||||
hv_assert(HLP_GET_UINT32_AT_BUFFER(q->readHead) != HLP_STOP);
|
||||
q->readHead += sizeof(hv_uint32_t) + HLP_GET_UINT32_AT_BUFFER(q->readHead);
|
||||
}
|
||||
|
||||
void hLp_reset(HvLightPipe *q) {
|
||||
q->writeHead = q->buffer;
|
||||
q->readHead = q->buffer;
|
||||
q->remainingBytes = q->len;
|
||||
memset(q->buffer, 0, q->len);
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/**
|
||||
* Copyright (c) 2014-2018 Enzien Audio Ltd.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _HEAVY_LIGHTPIPE_H_
|
||||
#define _HEAVY_LIGHTPIPE_H_
|
||||
|
||||
#include "HvUtils.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This pipe assumes that there is only one producer thread and one consumer
|
||||
* thread. This data structure does not support any other configuration.
|
||||
*/
|
||||
typedef struct HvLightPipe {
|
||||
char *buffer;
|
||||
char *writeHead;
|
||||
char *readHead;
|
||||
hv_uint32_t len;
|
||||
hv_uint32_t remainingBytes; // total bytes from write head to end
|
||||
} HvLightPipe;
|
||||
|
||||
/**
|
||||
* Initialise the pipe with a given length, in bytes.
|
||||
* @return Returns the size of the pipe in bytes.
|
||||
*/
|
||||
hv_uint32_t hLp_init(HvLightPipe *q, hv_uint32_t numBytes);
|
||||
|
||||
/**
|
||||
* Frees the internal buffer.
|
||||
* @param q The light pipe.
|
||||
*/
|
||||
void hLp_free(HvLightPipe *q);
|
||||
|
||||
/**
|
||||
* Indicates if data is available for reading.
|
||||
* @param q The light pipe.
|
||||
*
|
||||
* @return Returns the number of bytes available for reading. Zero if no bytes
|
||||
* are available.
|
||||
*/
|
||||
hv_uint32_t hLp_hasData(HvLightPipe *q);
|
||||
|
||||
/**
|
||||
* Returns a pointer to a location in the pipe where numBytes can be written.
|
||||
*
|
||||
* @param numBytes The number of bytes to be written.
|
||||
* @return A pointer to a location where those bytes can be written. Returns
|
||||
* NULL if no more space is available. Successive calls to this
|
||||
* function may eventually return a valid pointer because the readhead
|
||||
* has been advanced on another thread.
|
||||
*/
|
||||
char *hLp_getWriteBuffer(HvLightPipe *q, hv_uint32_t numBytes);
|
||||
|
||||
/**
|
||||
* Indicates to the pipe how many bytes have been written.
|
||||
*
|
||||
* @param numBytes The number of bytes written. In general this should be the
|
||||
* same value as was passed to the preceeding call to
|
||||
* hLp_getWriteBuffer().
|
||||
*/
|
||||
void hLp_produce(HvLightPipe *q, hv_uint32_t numBytes);
|
||||
|
||||
/**
|
||||
* Returns the current read buffer, indicating the number of bytes available
|
||||
* for reading.
|
||||
* @param q The light pipe.
|
||||
* @param numBytes This value will be filled with the number of bytes available
|
||||
* for reading.
|
||||
*
|
||||
* @return A pointer to the read buffer.
|
||||
*/
|
||||
char *hLp_getReadBuffer(HvLightPipe *q, hv_uint32_t *numBytes);
|
||||
|
||||
/**
|
||||
* Indicates that the next set of bytes have been read and are no longer needed.
|
||||
* @param q The light pipe.
|
||||
*/
|
||||
void hLp_consume(HvLightPipe *q);
|
||||
|
||||
// resets the queue to it's initialised state
|
||||
// This should be done when only one thread is accessing the pipe.
|
||||
void hLp_reset(HvLightPipe *q);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _HEAVY_LIGHTPIPE_H_
|
||||
@@ -0,0 +1,736 @@
|
||||
/**
|
||||
* Copyright (c) 2014-2018 Enzien Audio Ltd.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _HEAVY_MATH_H_
|
||||
#define _HEAVY_MATH_H_
|
||||
|
||||
#include "HvUtils.h"
|
||||
|
||||
// https://software.intel.com/sites/landingpage/IntrinsicsGuide/
|
||||
// https://gcc.gnu.org/onlinedocs/gcc-4.8.1/gcc/ARM-NEON-Intrinsics.html
|
||||
// http://codesuppository.blogspot.co.uk/2015/02/sse2neonh-porting-guide-and-header-file.html
|
||||
|
||||
static inline void __hv_zero_f(hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_setzero_ps();
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_setzero_ps();
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vdupq_n_f32(0.0f);
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = 0.0f;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_zero_i(hv_bOuti_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_setzero_si256();
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_setzero_si128();
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vdupq_n_s32(0);
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_load_f(float *bIn, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_load_ps(bIn);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_load_ps(bIn);
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vld1q_f32(bIn);
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = *bIn;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_store_f(float *bOut, hv_bInf_t bIn) {
|
||||
#if HV_SIMD_AVX
|
||||
_mm256_store_ps(bOut, bIn);
|
||||
#elif HV_SIMD_SSE
|
||||
_mm_store_ps(bOut, bIn);
|
||||
#elif HV_SIMD_NEON
|
||||
vst1q_f32(bOut, bIn);
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = bIn;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_log2_f(hv_bInf_t bIn, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
hv_assert(0); // __hv_log2_f() not implemented
|
||||
#elif HV_SIMD_SSE
|
||||
// https://en.wikipedia.org/wiki/Fast_inverse_square_root
|
||||
__m128i a = _mm_castps_si128(bIn);
|
||||
__m128i b = _mm_srli_epi32(a, 23);
|
||||
__m128i c = _mm_sub_epi32(b, _mm_set1_epi32(127)); // exponent (int)
|
||||
__m128 d = _mm_cvtepi32_ps(c); // exponent (float)
|
||||
__m128i e = _mm_or_si128(_mm_andnot_si128(_mm_set1_epi32(0xFF800000), a), _mm_set1_epi32(0x3F800000));
|
||||
__m128 f = _mm_castsi128_ps(e); // 1+m (float)
|
||||
__m128 g = _mm_add_ps(d, f); // e + 1 + m
|
||||
__m128 h = _mm_add_ps(g, _mm_set1_ps(-0.9569643f)); // e + 1 + m + (sigma-1)
|
||||
*bOut = h;
|
||||
#elif HV_SIMD_NEON
|
||||
int32x4_t a = vreinterpretq_s32_f32(bIn);
|
||||
int32x4_t b = vshrq_n_s32(a, 23);
|
||||
int32x4_t c = vsubq_s32(b, vdupq_n_s32(127));
|
||||
float32x4_t d = vcvtq_f32_s32(c);
|
||||
int32x4_t e = vorrq_s32(vbicq_s32(a, vdupq_n_s32(0xFF800000)), vdupq_n_s32(0x3F800000));
|
||||
float32x4_t f = vreinterpretq_f32_s32(e);
|
||||
float32x4_t g = vaddq_f32(d, f);
|
||||
float32x4_t h = vaddq_f32(g, vdupq_n_f32(-0.9569643f));
|
||||
*bOut = h;
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = 1.442695040888963f * hv_log_f(bIn);
|
||||
#endif
|
||||
}
|
||||
|
||||
// NOTE(mhroth): this is a pretty ghetto implementation
|
||||
static inline void __hv_cos_f(hv_bInf_t bIn, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_set_ps(
|
||||
hv_cos_f(bIn[7]), hv_cos_f(bIn[6]), hv_cos_f(bIn[5]), hv_cos_f(bIn[4]),
|
||||
hv_cos_f(bIn[3]), hv_cos_f(bIn[2]), hv_cos_f(bIn[1]), hv_cos_f(bIn[0]));
|
||||
#elif HV_SIMD_SSE
|
||||
const float *const b = (float *) &bIn;
|
||||
*bOut = _mm_set_ps(hv_cos_f(b[3]), hv_cos_f(b[2]), hv_cos_f(b[1]), hv_cos_f(b[0]));
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = (float32x4_t) {hv_cos_f(bIn[0]), hv_cos_f(bIn[1]), hv_cos_f(bIn[2]), hv_cos_f(bIn[3])};
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = hv_cos_f(bIn);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_acos_f(hv_bInf_t bIn, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
hv_assert(0); // __hv_acos_f() not implemented
|
||||
#elif HV_SIMD_SSE
|
||||
hv_assert(0); // __hv_acos_f() not implemented
|
||||
#elif HV_SIMD_NEON
|
||||
hv_assert(0); // __hv_acos_f() not implemented
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = hv_acos_f(bIn);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_cosh_f(hv_bInf_t bIn, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
hv_assert(0); // __hv_cosh_f() not implemented
|
||||
#elif HV_SIMD_SSE
|
||||
hv_assert(0); // __hv_cosh_f() not implemented
|
||||
#elif HV_SIMD_NEON
|
||||
hv_assert(0); // __hv_cosh_f() not implemented
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = hv_cosh_f(bIn);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_acosh_f(hv_bInf_t bIn, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
hv_assert(0); // __hv_acosh_f() not implemented
|
||||
#elif HV_SIMD_SSE
|
||||
hv_assert(0); // __hv_acosh_f() not implemented
|
||||
#elif HV_SIMD_NEON
|
||||
hv_assert(0); // __hv_acosh_f() not implemented
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = hv_acosh_f(bIn);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_sin_f(hv_bInf_t bIn, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
hv_assert(0); // __hv_sin_f() not implemented
|
||||
#elif HV_SIMD_SSE
|
||||
hv_assert(0); // __hv_sin_f() not implemented
|
||||
#elif HV_SIMD_NEON
|
||||
hv_assert(0); // __hv_sin_f() not implemented
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = hv_sin_f(bIn);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_asin_f(hv_bInf_t bIn, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
hv_assert(0); // __hv_asin_f() not implemented
|
||||
#elif HV_SIMD_SSE
|
||||
hv_assert(0); // __hv_asin_f() not implemented
|
||||
#elif HV_SIMD_NEON
|
||||
hv_assert(0); // __hv_asin_f() not implemented
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = hv_asin_f(bIn);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_sinh_f(hv_bInf_t bIn, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
hv_assert(0); // __hv_sinh_f() not implemented
|
||||
#elif HV_SIMD_SSE
|
||||
hv_assert(0); // __hv_sinh_f() not implemented
|
||||
#elif HV_SIMD_NEON
|
||||
hv_assert(0); // __hv_sinh_f() not implemented
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = hv_sinh_f(bIn);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_asinh_f(hv_bInf_t bIn, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
hv_assert(0); // __hv_asinh_f() not implemented
|
||||
#elif HV_SIMD_SSE
|
||||
hv_assert(0); // __hv_asinh_f() not implemented
|
||||
#elif HV_SIMD_NEON
|
||||
hv_assert(0); // __hv_asinh_f() not implemented
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = hv_asinh_f(bIn);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_tan_f(hv_bInf_t bIn, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
hv_assert(0); // __hv_tan_f() not implemented
|
||||
#elif HV_SIMD_SSE
|
||||
hv_assert(0); // __hv_tan_f() not implemented
|
||||
#elif HV_SIMD_NEON
|
||||
hv_assert(0); // __hv_tan_f() not implemented
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = hv_tan_f(bIn);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_atan_f(hv_bInf_t bIn, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
hv_assert(0); // __hv_atan_f() not implemented
|
||||
#elif HV_SIMD_SSE
|
||||
hv_assert(0); // __hv_atan_f() not implemented
|
||||
#elif HV_SIMD_NEON
|
||||
hv_assert(0); // __hv_atan_f() not implemented
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = hv_atan_f(bIn);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_atan2_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
hv_assert(0); // __hv_atan2_f() not implemented
|
||||
#elif HV_SIMD_SSE
|
||||
hv_assert(0); // __hv_atan2_f() not implemented
|
||||
#elif HV_SIMD_NEON
|
||||
hv_assert(0); // __hv_atan2_f() not implemented
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = hv_atan2_f(bIn0, bIn1);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_tanh_f(hv_bInf_t bIn, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
hv_assert(0); // __hv_tanh_f() not implemented
|
||||
#elif HV_SIMD_SSE
|
||||
hv_assert(0); // __hv_tanh_f() not implemented
|
||||
#elif HV_SIMD_NEON
|
||||
hv_assert(0); // __hv_tanh_f() not implemented
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = hv_tanh_f(bIn);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_atanh_f(hv_bInf_t bIn, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
hv_assert(0); // __hv_atanh_f() not implemented
|
||||
#elif HV_SIMD_SSE
|
||||
hv_assert(0); // __hv_atanh_f() not implemented
|
||||
#elif HV_SIMD_NEON
|
||||
hv_assert(0); // __hv_atanh_f() not implemented
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = hv_atanh_f(bIn);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_sqrt_f(hv_bInf_t bIn, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_sqrt_ps(bIn);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_sqrt_ps(bIn);
|
||||
#elif HV_SIMD_NEON
|
||||
const float32x4_t y = vrsqrteq_f32(bIn);
|
||||
*bOut = vmulq_f32(bIn, vmulq_f32(vrsqrtsq_f32(vmulq_f32(bIn, y), y), y)); // numerical results may be inexact
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = hv_sqrt_f(bIn);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_rsqrt_f(hv_bInf_t bIn, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_rsqrt_ps(bIn);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_rsqrt_ps(bIn);
|
||||
#elif HV_SIMD_NEON
|
||||
const float32x4_t y = vrsqrteq_f32(bIn);
|
||||
*bOut = vmulq_f32(vrsqrtsq_f32(vmulq_f32(bIn, y), y), y); // numerical results may be inexact
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = 1.0f/hv_sqrt_f(bIn);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_abs_f(hv_bInf_t bIn, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_andnot_ps(_mm256_set1_ps(-0.0f), bIn);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_andnot_ps(_mm_set1_ps(-0.0f), bIn); // == 1 << 31
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vabsq_f32(bIn);
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = hv_abs_f(bIn);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_neg_f(hv_bInf_t bIn, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_xor_ps(bIn, _mm256_set1_ps(-0.0f));
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_xor_ps(bIn, _mm_set1_ps(-0.0f));
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vnegq_f32(bIn);
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = bIn * -1.0f;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_exp_f(hv_bInf_t bIn, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
float *const b = (float *) hv_alloca(HV_N_SIMD*sizeof(float));
|
||||
_mm256_store_ps(b, bIn);
|
||||
*bOut = _mm256_set_ps(
|
||||
hv_exp_f(b[7]), hv_exp_f(b[6]), hv_exp_f(b[5]), hv_exp_f(b[4]),
|
||||
hv_exp_f(b[3]), hv_exp_f(b[2]), hv_exp_f(b[1]), hv_exp_f(b[0]));
|
||||
#elif HV_SIMD_SSE
|
||||
float *const b = (float *) hv_alloca(HV_N_SIMD*sizeof(float));
|
||||
_mm_store_ps(b, bIn);
|
||||
*bOut = _mm_set_ps(hv_exp_f(b[3]), hv_exp_f(b[2]), hv_exp_f(b[1]), hv_exp_f(b[0]));
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = (float32x4_t) {
|
||||
hv_exp_f(bIn[0]),
|
||||
hv_exp_f(bIn[1]),
|
||||
hv_exp_f(bIn[2]),
|
||||
hv_exp_f(bIn[3])};
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = hv_exp_f(bIn);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_ceil_f(hv_bInf_t bIn, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_ceil_ps(bIn);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_ceil_ps(bIn);
|
||||
#elif HV_SIMD_NEON
|
||||
#if __ARM_ARCH >= 8
|
||||
*bOut = vrndpq_f32(bIn);
|
||||
#else
|
||||
// A slow NEON implementation of __hv_ceil_f() is being used because
|
||||
// the necessary intrinsic cannot be found. It is only available in ARMv8.
|
||||
*bOut = (float32x4_t) {hv_ceil_f(bIn[0]), hv_ceil_f(bIn[1]), hv_ceil_f(bIn[2]), hv_ceil_f(bIn[3])};
|
||||
#endif // vrndpq_f32
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = hv_ceil_f(bIn);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_floor_f(hv_bInf_t bIn, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_floor_ps(bIn);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_floor_ps(bIn);
|
||||
#elif HV_SIMD_NEON
|
||||
#if __ARM_ARCH >= 8
|
||||
*bOut = vrndmq_f32(bIn);
|
||||
#else
|
||||
// A slow implementation of __hv_floor_f() is being used because
|
||||
// the necessary intrinsic cannot be found. It is only available from ARMv8.
|
||||
*bOut = (float32x4_t) {hv_floor_f(bIn[0]), hv_floor_f(bIn[1]), hv_floor_f(bIn[2]), hv_floor_f(bIn[3])};
|
||||
#endif // vrndmq_f32
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = hv_floor_f(bIn);
|
||||
#endif
|
||||
}
|
||||
|
||||
// __add~f
|
||||
static inline void __hv_add_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_add_ps(bIn0, bIn1);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_add_ps(bIn0, bIn1);
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vaddq_f32(bIn0, bIn1);
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = bIn0 + bIn1;
|
||||
#endif
|
||||
}
|
||||
|
||||
// __add~i
|
||||
static inline void __hv_add_i(hv_bIni_t bIn0, hv_bIni_t bIn1, hv_bOuti_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
__m128i x = _mm_add_epi32(_mm256_castsi256_si128(bIn0), _mm256_castsi256_si128(bIn1));
|
||||
__m128i y = _mm_add_epi32(_mm256_extractf128_si256(bIn0, 1), _mm256_extractf128_si256(bIn1, 1));
|
||||
*bOut = _mm256_insertf128_si256(_mm256_castsi128_si256(x), y, 1);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_add_epi32(bIn0, bIn1);
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vaddq_s32(bIn0, bIn1);
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = bIn0 + bIn1;
|
||||
#endif
|
||||
}
|
||||
|
||||
// __sub~f
|
||||
static inline void __hv_sub_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_sub_ps(bIn0, bIn1);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_sub_ps(bIn0, bIn1);
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vsubq_f32(bIn0, bIn1);
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = bIn0 - bIn1;
|
||||
#endif
|
||||
}
|
||||
|
||||
// __mul~f
|
||||
static inline void __hv_mul_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_mul_ps(bIn0, bIn1);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_mul_ps(bIn0, bIn1);
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vmulq_f32(bIn0, bIn1);
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = bIn0 * bIn1;
|
||||
#endif
|
||||
}
|
||||
|
||||
// __*~i
|
||||
static inline void __hv_mul_i(hv_bIni_t bIn0, hv_bIni_t bIn1, hv_bOuti_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
__m128i x = _mm_mullo_epi32(_mm256_castsi256_si128(bIn0), _mm256_castsi256_si128(bIn1));
|
||||
__m128i y = _mm_mullo_epi32(_mm256_extractf128_si256(bIn0, 1), _mm256_extractf128_si256(bIn1, 1));
|
||||
*bOut = _mm256_insertf128_si256(_mm256_castsi128_si256(x), y, 1);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_mullo_epi32(bIn0, bIn1);
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vmulq_s32(bIn0, bIn1);
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = bIn0 * bIn1;
|
||||
#endif
|
||||
}
|
||||
|
||||
// __cast~if
|
||||
static inline void __hv_cast_if(hv_bIni_t bIn, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_cvtepi32_ps(bIn);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_cvtepi32_ps(bIn);
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vcvtq_f32_s32(bIn);
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = (float) bIn;
|
||||
#endif
|
||||
}
|
||||
|
||||
// __cast~fi
|
||||
static inline void __hv_cast_fi(hv_bInf_t bIn, hv_bOuti_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_cvtps_epi32(bIn);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_cvtps_epi32(bIn);
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vcvtq_s32_f32(bIn);
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = (int) bIn;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_div_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
__m256 a = _mm256_cmp_ps(bIn1, _mm256_setzero_ps(), _CMP_EQ_OQ);
|
||||
__m256 b = _mm256_div_ps(bIn0, bIn1);
|
||||
*bOut = _mm256_andnot_ps(a, b);
|
||||
#elif HV_SIMD_SSE
|
||||
__m128 a = _mm_cmpeq_ps(bIn1, _mm_setzero_ps());
|
||||
__m128 b = _mm_div_ps(bIn0, bIn1);
|
||||
*bOut = _mm_andnot_ps(a, b);
|
||||
#elif HV_SIMD_NEON
|
||||
uint32x4_t a = vceqq_f32(bIn1, vdupq_n_f32(0.0f));
|
||||
float32x4_t b = vmulq_f32(bIn0, vrecpeq_f32(bIn1)); // NOTE(mhroth): numerical results may be inexact
|
||||
*bOut = vreinterpretq_f32_u32(vbicq_u32(vreinterpretq_u32_f32(b), a));
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = (bIn1 != 0.0f) ? (bIn0 / bIn1) : 0.0f;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_min_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_min_ps(bIn0, bIn1);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_min_ps(bIn0, bIn1);
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vminq_f32(bIn0, bIn1);
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = hv_min_f(bIn0, bIn1);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_min_i(hv_bIni_t bIn0, hv_bIni_t bIn1, hv_bOuti_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
__m128i x = _mm_min_epi32(_mm256_castsi256_si128(bIn0), _mm256_castsi256_si128(bIn1));
|
||||
__m128i y = _mm_min_epi32(_mm256_extractf128_si256(bIn0, 1), _mm256_extractf128_si256(bIn1, 1));
|
||||
*bOut = _mm256_insertf128_si256(_mm256_castsi128_si256(x), y, 1);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_min_epi32(bIn0, bIn1);
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vminq_s32(bIn0, bIn1);
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = hv_min_i(bIn0, bIn1);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_max_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_max_ps(bIn0, bIn1);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_max_ps(bIn0, bIn1);
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vmaxq_f32(bIn0, bIn1);
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = hv_max_f(bIn0, bIn1);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_max_i(hv_bIni_t bIn0, hv_bIni_t bIn1, hv_bOuti_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
__m128i x = _mm_max_epi32(_mm256_castsi256_si128(bIn0), _mm256_castsi256_si128(bIn1));
|
||||
__m128i y = _mm_max_epi32(_mm256_extractf128_si256(bIn0, 1), _mm256_extractf128_si256(bIn1, 1));
|
||||
*bOut = _mm256_insertf128_si256(_mm256_castsi128_si256(x), y, 1);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_max_epi32(bIn0, bIn1);
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vmaxq_s32(bIn0, bIn1);
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = hv_max_i(bIn0, bIn1);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_pow_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
float *b = (float *) hv_alloca(16*sizeof(float));
|
||||
_mm256_store_ps(b, bIn0);
|
||||
_mm256_store_ps(b+8, bIn1);
|
||||
*bOut = _mm256_set_ps(
|
||||
hv_pow_f(b[7], b[15]),
|
||||
hv_pow_f(b[6], b[14]),
|
||||
hv_pow_f(b[5], b[13]),
|
||||
hv_pow_f(b[4], b[12]),
|
||||
hv_pow_f(b[3], b[11]),
|
||||
hv_pow_f(b[2], b[10]),
|
||||
hv_pow_f(b[1], b[9]),
|
||||
hv_pow_f(b[0], b[8]));
|
||||
#elif HV_SIMD_SSE
|
||||
float *b = (float *) hv_alloca(8*sizeof(float));
|
||||
_mm_store_ps(b, bIn0);
|
||||
_mm_store_ps(b+4, bIn1);
|
||||
*bOut = _mm_set_ps(
|
||||
hv_pow_f(b[3], b[7]),
|
||||
hv_pow_f(b[2], b[6]),
|
||||
hv_pow_f(b[1], b[5]),
|
||||
hv_pow_f(b[0], b[4]));
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = (float32x4_t) {
|
||||
hv_pow_f(bIn0[0], bIn1[0]),
|
||||
hv_pow_f(bIn0[1], bIn1[1]),
|
||||
hv_pow_f(bIn0[2], bIn1[2]),
|
||||
hv_pow_f(bIn0[3], bIn1[3])};
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = hv_pow_f(bIn0, bIn1);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_gt_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_cmp_ps(bIn0, bIn1, _CMP_GT_OQ);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_cmpgt_ps(bIn0, bIn1);
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vreinterpretq_f32_u32(vcgtq_f32(bIn0, bIn1));
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = (bIn0 > bIn1) ? 1.0f : 0.0f;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_gte_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_cmp_ps(bIn0, bIn1, _CMP_GE_OQ);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_cmpge_ps(bIn0, bIn1);
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vreinterpretq_f32_u32(vcgeq_f32(bIn0, bIn1));
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = (bIn0 >= bIn1) ? 1.0f : 0.0f;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_lt_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_cmp_ps(bIn0, bIn1, _CMP_LT_OQ);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_cmplt_ps(bIn0, bIn1);
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vreinterpretq_f32_u32(vcltq_f32(bIn0, bIn1));
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = (bIn0 < bIn1) ? 1.0f : 0.0f;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_lte_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_cmp_ps(bIn0, bIn1, _CMP_LE_OQ);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_cmple_ps(bIn0, bIn1);
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vreinterpretq_f32_u32(vcleq_f32(bIn0, bIn1));
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = (bIn0 <= bIn1) ? 1.0f : 0.0f;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_eq_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_cmp_ps(bIn0, bIn1, _CMP_EQ_OQ);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_cmpeq_ps(bIn0, bIn1);
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vreinterpretq_f32_u32(vceqq_f32(bIn0, bIn1));
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = (bIn0 == bIn1) ? 1.0f : 0.0f;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_neq_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_cmp_ps(bIn0, bIn1, _CMP_NEQ_OQ);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_cmpneq_ps(bIn0, bIn1);
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vreinterpretq_f32_u32(vmvnq_u32(vceqq_f32(bIn0, bIn1)));
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = (bIn0 != bIn1) ? 1.0f : 0.0f;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_or_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_or_ps(bIn1, bIn0);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_or_ps(bIn1, bIn0);
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vreinterpretq_f32_u32(vorrq_u32(vreinterpretq_u32_f32(bIn1), vreinterpretq_u32_f32(bIn0)));
|
||||
#else // HV_SIMD_NONE
|
||||
if (bIn0 == 0.0f && bIn1 == 0.0f) *bOut = 0.0f;
|
||||
else if (bIn0 == 0.0f) *bOut = bIn1;
|
||||
else if (bIn1 == 0.0f) *bOut = bIn0;
|
||||
else hv_assert(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_and_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_and_ps(bIn1, bIn0);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_and_ps(bIn1, bIn0);
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(bIn1), vreinterpretq_u32_f32(bIn0)));
|
||||
#else // HV_SIMD_NONE
|
||||
if (bIn0 == 0.0f || bIn1 == 0.0f) *bOut = 0.0f;
|
||||
else if (bIn0 == 1.0f) *bOut = bIn1;
|
||||
else if (bIn1 == 1.0f) *bOut = bIn0;
|
||||
else hv_assert(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_andnot_f(hv_bInf_t bIn0_mask, hv_bInf_t bIn1, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_andnot_ps(bIn0_mask, bIn1);
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_andnot_ps(bIn0_mask, bIn1);
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vreinterpretq_f32_s32(vbicq_s32(vreinterpretq_s32_f32(bIn1), vreinterpretq_s32_f32(bIn0_mask)));
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = (bIn0_mask == 0.0f) ? bIn1 : 0.0f;
|
||||
#endif
|
||||
}
|
||||
|
||||
// bOut = (bIn0 * bIn1) + bIn2
|
||||
static inline void __hv_fma_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bInf_t bIn2, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
#if HV_SIMD_FMA
|
||||
*bOut = _mm256_fmadd_ps(bIn0, bIn1, bIn2);
|
||||
#else
|
||||
*bOut = _mm256_add_ps(_mm256_mul_ps(bIn0, bIn1), bIn2);
|
||||
#endif // HV_SIMD_FMA
|
||||
#elif HV_SIMD_SSE
|
||||
#if HV_SIMD_FMA
|
||||
*bOut = _mm_fmadd_ps(bIn0, bIn1, bIn2);
|
||||
#else
|
||||
*bOut = _mm_add_ps(_mm_mul_ps(bIn0, bIn1), bIn2);
|
||||
#endif // HV_SIMD_FMA
|
||||
#elif HV_SIMD_NEON
|
||||
#if __ARM_ARCH >= 8
|
||||
*bOut = vfmaq_f32(bIn2, bIn0, bIn1);
|
||||
#else
|
||||
// NOTE(mhroth): it turns out, fma SUUUUCKS on lesser ARM architectures
|
||||
*bOut = vaddq_f32(vmulq_f32(bIn0, bIn1), bIn2);
|
||||
#endif
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = hv_fma_f(bIn0, bIn1, bIn2);
|
||||
#endif
|
||||
}
|
||||
|
||||
// bOut = (bIn0 * bIn1) - bIn2
|
||||
static inline void __hv_fms_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bInf_t bIn2, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
#if HV_SIMD_FMA
|
||||
*bOut = _mm256_fmsub_ps(bIn0, bIn1, bIn2);
|
||||
#else
|
||||
*bOut = _mm256_sub_ps(_mm256_mul_ps(bIn0, bIn1), bIn2);
|
||||
#endif // HV_SIMD_FMA
|
||||
#elif HV_SIMD_SSE
|
||||
#if HV_SIMD_FMA
|
||||
*bOut = _mm_fmsub_ps(bIn0, bIn1, bIn2);
|
||||
#else
|
||||
*bOut = _mm_sub_ps(_mm_mul_ps(bIn0, bIn1), bIn2);
|
||||
#endif // HV_SIMD_FMA
|
||||
#elif HV_SIMD_NEON
|
||||
#if __ARM_ARCH >= 8
|
||||
*bOut = vfmsq_f32(bIn2, bIn0, bIn1);
|
||||
#else
|
||||
// NOTE(mhroth): it turns out, fma SUUUUCKS on lesser ARM architectures
|
||||
*bOut = vsubq_f32(vmulq_f32(bIn0, bIn1), bIn2);
|
||||
#endif
|
||||
#else // HV_SIMD_NONE
|
||||
*bOut = (bIn0 * bIn1) - bIn2;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // _HEAVY_MATH_H_
|
||||
@@ -0,0 +1,199 @@
|
||||
/**
|
||||
* Copyright (c) 2014-2018 Enzien Audio Ltd.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "HvMessage.h"
|
||||
|
||||
HvMessage *msg_init(HvMessage *m, hv_size_t numElements, hv_uint32_t timestamp) {
|
||||
m->timestamp = timestamp;
|
||||
m->numElements = (hv_uint16_t) numElements;
|
||||
m->numBytes = (hv_uint16_t) msg_getCoreSize(numElements);
|
||||
return m;
|
||||
}
|
||||
|
||||
HvMessage *msg_initWithFloat(HvMessage *m, hv_uint32_t timestamp, float f) {
|
||||
m->timestamp = timestamp;
|
||||
m->numElements = 1;
|
||||
m->numBytes = sizeof(HvMessage);
|
||||
msg_setFloat(m, 0, f);
|
||||
return m;
|
||||
}
|
||||
|
||||
HvMessage *msg_initWithBang(HvMessage *m, hv_uint32_t timestamp) {
|
||||
m->timestamp = timestamp;
|
||||
m->numElements = 1;
|
||||
m->numBytes = sizeof(HvMessage);
|
||||
msg_setBang(m, 0);
|
||||
return m;
|
||||
}
|
||||
|
||||
HvMessage *msg_initWithSymbol(HvMessage *m, hv_uint32_t timestamp, const char *s) {
|
||||
m->timestamp = timestamp;
|
||||
m->numElements = 1;
|
||||
m->numBytes = sizeof(HvMessage) + (hv_uint16_t) hv_strlen(s);
|
||||
msg_setSymbol(m, 0, s);
|
||||
return m;
|
||||
}
|
||||
|
||||
HvMessage *msg_initWithHash(HvMessage *m, hv_uint32_t timestamp, hv_uint32_t h) {
|
||||
m->timestamp = timestamp;
|
||||
m->numElements = 1;
|
||||
m->numBytes = sizeof(HvMessage);
|
||||
msg_setHash(m, 0, h);
|
||||
return m;
|
||||
}
|
||||
|
||||
void msg_copyToBuffer(const HvMessage *m, char *buffer, hv_size_t len) {
|
||||
HvMessage *r = (HvMessage *) buffer;
|
||||
|
||||
hv_size_t len_r = msg_getCoreSize(msg_getNumElements(m));
|
||||
|
||||
// assert that the message is not already larger than the length of the buffer
|
||||
hv_assert(len_r <= len);
|
||||
|
||||
// copy the basic message to the buffer
|
||||
hv_memcpy(r, m, len_r);
|
||||
|
||||
char *p = buffer + len_r; // points to the end of the base message
|
||||
for (int i = 0; i < msg_getNumElements(m); ++i) {
|
||||
if (msg_isSymbol(m,i)) {
|
||||
const hv_size_t symLen = (hv_size_t) hv_strlen(msg_getSymbol(m,i)) + 1; // include the trailing null char
|
||||
hv_assert(len_r + symLen <= len); // stay safe!
|
||||
hv_strncpy(p, msg_getSymbol(m,i), symLen);
|
||||
msg_setSymbol(r, i, p);
|
||||
p += symLen;
|
||||
len_r += symLen;
|
||||
}
|
||||
}
|
||||
|
||||
r->numBytes = (hv_uint16_t) len_r; // update the message size in memory
|
||||
}
|
||||
|
||||
// the message is serialised such that all symbol elements are placed in order at the end of the buffer
|
||||
HvMessage *msg_copy(const HvMessage *m) {
|
||||
const hv_uint32_t heapSize = msg_getSize(m);
|
||||
char *r = (char *) hv_malloc(heapSize);
|
||||
hv_assert(r != NULL);
|
||||
msg_copyToBuffer(m, r, heapSize);
|
||||
return (HvMessage *) r;
|
||||
}
|
||||
|
||||
void msg_free(HvMessage *m) {
|
||||
hv_free(m); // because heap messages are serialised in memory, a simple call to free releases the message
|
||||
}
|
||||
|
||||
bool msg_hasFormat(const HvMessage *m, const char *fmt) {
|
||||
hv_assert(fmt != NULL);
|
||||
const int n = msg_getNumElements(m);
|
||||
for (int i = 0; i < n; ++i) {
|
||||
switch (fmt[i]) {
|
||||
case 'b': if (!msg_isBang(m, i)) return false; break;
|
||||
case 'f': if (!msg_isFloat(m, i)) return false; break;
|
||||
case 'h': if (!msg_isHash(m, i)) return false; break;
|
||||
case 's': if (!msg_isSymbol(m, i)) return false; break;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
return (fmt[n] == '\0');
|
||||
}
|
||||
|
||||
bool msg_compareSymbol(const HvMessage *m, int i, const char *s) {
|
||||
switch (msg_getType(m,i)) {
|
||||
case HV_MSG_SYMBOL: return !hv_strcmp(msg_getSymbol(m, i), s);
|
||||
case HV_MSG_HASH: return (msg_getHash(m,i) == hv_string_to_hash(s));
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool msg_equalsElement(const HvMessage *m, int i_m, const HvMessage *n, int i_n) {
|
||||
if (i_m < msg_getNumElements(m) && i_n < msg_getNumElements(n)) {
|
||||
if (msg_getType(m, i_m) == msg_getType(n, i_n)) {
|
||||
switch (msg_getType(m, i_m)) {
|
||||
case HV_MSG_BANG: return true;
|
||||
case HV_MSG_FLOAT: return (msg_getFloat(m, i_m) == msg_getFloat(n, i_n));
|
||||
case HV_MSG_SYMBOL: return msg_compareSymbol(m, i_m, msg_getSymbol(n, i_n));
|
||||
case HV_MSG_HASH: return msg_getHash(m,i_m) == msg_getHash(n,i_n);
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void msg_setElementToFrom(HvMessage *n, int i_n, const HvMessage *const m, int i_m) {
|
||||
switch (msg_getType(m, i_m)) {
|
||||
case HV_MSG_BANG: msg_setBang(n, i_n); break;
|
||||
case HV_MSG_FLOAT: msg_setFloat(n, i_n, msg_getFloat(m, i_m)); break;
|
||||
case HV_MSG_SYMBOL: msg_setSymbol(n, i_n, msg_getSymbol(m, i_m)); break;
|
||||
case HV_MSG_HASH: msg_setHash(n, i_n, msg_getHash(m, i_m));
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
hv_uint32_t msg_getHash(const HvMessage *const m, int i) {
|
||||
hv_assert(i < msg_getNumElements(m)); // invalid index
|
||||
switch (msg_getType(m,i)) {
|
||||
case HV_MSG_BANG: return 0xFFFFFFFF;
|
||||
case HV_MSG_FLOAT: {
|
||||
float f = msg_getFloat(m,i);
|
||||
return *((hv_uint32_t *) &f);
|
||||
}
|
||||
case HV_MSG_SYMBOL: return hv_string_to_hash(msg_getSymbol(m,i));
|
||||
case HV_MSG_HASH: return (&(m->elem)+i)->data.h;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
char *msg_toString(const HvMessage *m) {
|
||||
hv_assert(msg_getNumElements(m) > 0);
|
||||
int *len = (int *) hv_alloca(msg_getNumElements(m)*sizeof(int));
|
||||
int size = 0; // the total length of our final buffer
|
||||
|
||||
// loop through every element in our list of atoms
|
||||
// first loop figures out how long our buffer should be
|
||||
for (int i = 0; i < msg_getNumElements(m); i++) {
|
||||
// length of our string is each atom plus a space, or \0 on the end
|
||||
switch (msg_getType(m, i)) {
|
||||
case HV_MSG_BANG: len[i] = hv_snprintf(NULL, 0, "%s", "bang") + 1; break;
|
||||
case HV_MSG_FLOAT: len[i] = hv_snprintf(NULL, 0, "%g", msg_getFloat(m, i)) + 1; break;
|
||||
case HV_MSG_SYMBOL: len[i] = hv_snprintf(NULL, 0, "%s", msg_getSymbol(m, i)) + 1; break;
|
||||
case HV_MSG_HASH: len[i] = hv_snprintf(NULL, 0, "0x%X", msg_getHash(m, i)) + 1; break;
|
||||
default: break;
|
||||
}
|
||||
size += len[i];
|
||||
}
|
||||
|
||||
hv_assert(size > 0);
|
||||
|
||||
// now we do the piecewise concatenation into our final string
|
||||
// the final buffer we will pass back after concatenating all strings - user should free it
|
||||
char *finalString = (char *) hv_malloc(size*sizeof(char));
|
||||
hv_assert(finalString != NULL);
|
||||
int pos = 0;
|
||||
for (int i = 0; i < msg_getNumElements(m); i++) {
|
||||
// put a string representation of each atom into the final string
|
||||
switch (msg_getType(m, i)) {
|
||||
case HV_MSG_BANG: hv_snprintf(finalString+pos, len[i], "%s", "bang"); break;
|
||||
case HV_MSG_FLOAT: hv_snprintf(finalString+pos, len[i], "%g", msg_getFloat(m, i)); break;
|
||||
case HV_MSG_SYMBOL: hv_snprintf(finalString+pos, len[i], "%s", msg_getSymbol(m, i)); break;
|
||||
case HV_MSG_HASH: hv_snprintf(finalString+pos, len[i], "0x%X", msg_getHash(m, i)); break;
|
||||
default: break;
|
||||
}
|
||||
pos += len[i];
|
||||
finalString[pos-1] = 32; // ASCII space
|
||||
}
|
||||
finalString[size-1] = '\0'; // ensure that the string is null terminated
|
||||
return finalString;
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
/**
|
||||
* Copyright (c) 2014-2018 Enzien Audio Ltd.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _HEAVY_MESSAGE_H_
|
||||
#define _HEAVY_MESSAGE_H_
|
||||
|
||||
#include "HvUtils.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum ElementType {
|
||||
HV_MSG_BANG = 0,
|
||||
HV_MSG_FLOAT = 1,
|
||||
HV_MSG_SYMBOL = 2,
|
||||
HV_MSG_HASH = 3
|
||||
} ElementType;
|
||||
|
||||
typedef struct Element {
|
||||
ElementType type;
|
||||
union {
|
||||
float f; // float
|
||||
const char *s; // symbol
|
||||
hv_uint32_t h; // hash
|
||||
} data;
|
||||
} Element;
|
||||
|
||||
typedef struct HvMessage {
|
||||
hv_uint32_t timestamp; // the sample at which this message should be processed
|
||||
hv_uint16_t numElements;
|
||||
hv_uint16_t numBytes; // the total number of bytes that this message occupies in memory, including strings
|
||||
Element elem;
|
||||
} HvMessage;
|
||||
|
||||
typedef struct ReceiverMessagePair {
|
||||
hv_uint32_t receiverHash;
|
||||
HvMessage msg;
|
||||
} ReceiverMessagePair;
|
||||
|
||||
#define HV_MESSAGE_ON_STACK(_x) (HvMessage *) hv_alloca(msg_getCoreSize(_x))
|
||||
|
||||
/** Returns the number of bytes that this message consumes in memory, not including strings. */
|
||||
static inline hv_size_t msg_getCoreSize(hv_size_t numElements) {
|
||||
hv_assert(numElements > 0);
|
||||
return sizeof(HvMessage) + ((numElements-1) * sizeof(Element));
|
||||
}
|
||||
|
||||
HvMessage *msg_copy(const HvMessage *m);
|
||||
|
||||
/** Copies the message into the given buffer. The buffer must be at least as large as msg_getNumHeapBytes(). */
|
||||
void msg_copyToBuffer(const HvMessage *m, char *buffer, hv_size_t len);
|
||||
|
||||
void msg_setElementToFrom(HvMessage *n, int indexN, const HvMessage *const m, int indexM);
|
||||
|
||||
/** Frees a message on the heap. Does nothing if argument is NULL. */
|
||||
void msg_free(HvMessage *m);
|
||||
|
||||
HvMessage *msg_init(HvMessage *m, hv_size_t numElements, hv_uint32_t timestamp);
|
||||
|
||||
HvMessage *msg_initWithFloat(HvMessage *m, hv_uint32_t timestamp, float f);
|
||||
|
||||
HvMessage *msg_initWithBang(HvMessage *m, hv_uint32_t timestamp);
|
||||
|
||||
HvMessage *msg_initWithSymbol(HvMessage *m, hv_uint32_t timestamp, const char *s);
|
||||
|
||||
HvMessage *msg_initWithHash(HvMessage *m, hv_uint32_t timestamp, hv_uint32_t h);
|
||||
|
||||
static inline hv_uint32_t msg_getTimestamp(const HvMessage *m) {
|
||||
return m->timestamp;
|
||||
}
|
||||
|
||||
static inline void msg_setTimestamp(HvMessage *m, hv_uint32_t timestamp) {
|
||||
m->timestamp = timestamp;
|
||||
}
|
||||
|
||||
static inline int msg_getNumElements(const HvMessage *m) {
|
||||
return (int) m->numElements;
|
||||
}
|
||||
|
||||
/** Returns the total number of bytes this message consumes in memory. */
|
||||
static inline hv_uint32_t msg_getSize(const HvMessage *m) {
|
||||
return m->numBytes;
|
||||
}
|
||||
|
||||
static inline ElementType msg_getType(const HvMessage *m, int index) {
|
||||
hv_assert(index < msg_getNumElements(m)); // invalid index
|
||||
return (&(m->elem)+index)->type;
|
||||
}
|
||||
|
||||
static inline void msg_setBang(HvMessage *m, int index) {
|
||||
hv_assert(index < msg_getNumElements(m)); // invalid index
|
||||
(&(m->elem)+index)->type = HV_MSG_BANG;
|
||||
(&(m->elem)+index)->data.s = NULL;
|
||||
}
|
||||
|
||||
static inline bool msg_isBang(const HvMessage *m, int index) {
|
||||
return (index < msg_getNumElements(m)) ? (msg_getType(m,index) == HV_MSG_BANG) : false;
|
||||
}
|
||||
|
||||
static inline void msg_setFloat(HvMessage *m, int index, float f) {
|
||||
hv_assert(index < msg_getNumElements(m)); // invalid index
|
||||
(&(m->elem)+index)->type = HV_MSG_FLOAT;
|
||||
(&(m->elem)+index)->data.f = f;
|
||||
}
|
||||
|
||||
static inline float msg_getFloat(const HvMessage *const m, int index) {
|
||||
hv_assert(index < msg_getNumElements(m)); // invalid index
|
||||
return (&(m->elem)+index)->data.f;
|
||||
}
|
||||
|
||||
static inline bool msg_isFloat(const HvMessage *const m, int index) {
|
||||
return (index < msg_getNumElements(m)) ? (msg_getType(m,index) == HV_MSG_FLOAT) : false;
|
||||
}
|
||||
|
||||
static inline void msg_setHash(HvMessage *m, int index, hv_uint32_t h) {
|
||||
hv_assert(index < msg_getNumElements(m)); // invalid index
|
||||
(&(m->elem)+index)->type = HV_MSG_HASH;
|
||||
(&(m->elem)+index)->data.h = h;
|
||||
}
|
||||
|
||||
static inline bool msg_isHash(const HvMessage *m, int index) {
|
||||
return (index < msg_getNumElements(m)) ? (msg_getType(m, index) == HV_MSG_HASH) : false;
|
||||
}
|
||||
|
||||
/** Returns true if the element is a hash or symbol. False otherwise. */
|
||||
static inline bool msg_isHashLike(const HvMessage *m, int index) {
|
||||
return (index < msg_getNumElements(m)) ? ((msg_getType(m, index) == HV_MSG_HASH) || (msg_getType(m, index) == HV_MSG_SYMBOL)) : false;
|
||||
}
|
||||
|
||||
/** Returns a 32-bit hash of the given element. */
|
||||
hv_uint32_t msg_getHash(const HvMessage *const m, int i);
|
||||
|
||||
static inline void msg_setSymbol(HvMessage *m, int index, const char *s) {
|
||||
hv_assert(index < msg_getNumElements(m)); // invalid index
|
||||
hv_assert(s != NULL);
|
||||
(&(m->elem)+index)->type = HV_MSG_SYMBOL;
|
||||
(&(m->elem)+index)->data.s = s;
|
||||
// NOTE(mhroth): if the same message container is reused and string reset,
|
||||
// then the message size will be overcounted
|
||||
m->numBytes += (hv_uint16_t) (hv_strlen(s) + 1); // also count '\0'
|
||||
}
|
||||
|
||||
static inline const char *msg_getSymbol(const HvMessage *m, int index) {
|
||||
hv_assert(index < msg_getNumElements(m)); // invalid index
|
||||
return (&(m->elem)+index)->data.s;
|
||||
}
|
||||
|
||||
static inline bool msg_isSymbol(const HvMessage *m, int index) {
|
||||
return (index < msg_getNumElements(m)) ? (msg_getType(m, index) == HV_MSG_SYMBOL) : false;
|
||||
}
|
||||
|
||||
bool msg_compareSymbol(const HvMessage *m, int i, const char *s);
|
||||
|
||||
/** Returns 1 if the element i_m of message m is equal to element i_n of message n. */
|
||||
bool msg_equalsElement(const HvMessage *m, int i_m, const HvMessage *n, int i_n);
|
||||
|
||||
bool msg_hasFormat(const HvMessage *m, const char *fmt);
|
||||
|
||||
/**
|
||||
* Create a string representation of the message. Suitable for use by the print object.
|
||||
* The resulting string must be freed by the caller.
|
||||
*/
|
||||
char *msg_toString(const HvMessage *msg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _HEAVY_MESSAGE_H_
|
||||
@@ -0,0 +1,144 @@
|
||||
/**
|
||||
* Copyright (c) 2014-2018 Enzien Audio Ltd.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "HvMessagePool.h"
|
||||
#include "HvMessage.h"
|
||||
|
||||
// the number of bytes reserved at a time from the pool
|
||||
#define MP_BLOCK_SIZE_BYTES 512
|
||||
|
||||
#if HV_APPLE
|
||||
#pragma mark - MessageList
|
||||
#endif
|
||||
|
||||
typedef struct MessageListNode {
|
||||
char *p;
|
||||
struct MessageListNode *next;
|
||||
} MessageListNode;
|
||||
|
||||
static inline bool ml_hasAvailable(HvMessagePoolList *ml) {
|
||||
return (ml->head != NULL);
|
||||
}
|
||||
|
||||
static char *ml_pop(HvMessagePoolList *ml) {
|
||||
MessageListNode *n = ml->head;
|
||||
ml->head = n->next;
|
||||
n->next = ml->pool;
|
||||
ml->pool = n;
|
||||
char *const p = n->p;
|
||||
n->p = NULL; // set to NULL to make it clear that this node does not have a valid buffer
|
||||
return p;
|
||||
}
|
||||
|
||||
/** Push a MessageListNode with the given pointer onto the head of the queue. */
|
||||
static void ml_push(HvMessagePoolList *ml, void *p) {
|
||||
MessageListNode *n = NULL;
|
||||
if (ml->pool != NULL) {
|
||||
// take an empty MessageListNode from the pool
|
||||
n = ml->pool;
|
||||
ml->pool = n->next;
|
||||
} else {
|
||||
// a MessageListNode is not available, allocate one
|
||||
n = (MessageListNode *) hv_malloc(sizeof(MessageListNode));
|
||||
hv_assert(n != NULL);
|
||||
}
|
||||
n->p = (char *) p;
|
||||
n->next = ml->head;
|
||||
ml->head = n; // push to the front of the queue
|
||||
}
|
||||
|
||||
static void ml_free(HvMessagePoolList *ml) {
|
||||
if (ml != NULL) {
|
||||
while (ml_hasAvailable(ml)) {
|
||||
ml_pop(ml);
|
||||
}
|
||||
while (ml->pool != NULL) {
|
||||
MessageListNode *n = ml->pool;
|
||||
ml->pool = n->next;
|
||||
hv_free(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if HV_APPLE
|
||||
#pragma mark - HvMessagePool
|
||||
#endif
|
||||
|
||||
static hv_size_t mp_messagelistIndexForSize(hv_size_t byteSize) {
|
||||
return (hv_size_t) hv_max_i((hv_min_max_log2((hv_uint32_t) byteSize) - 5), 0);
|
||||
}
|
||||
|
||||
hv_size_t mp_init(HvMessagePool *mp, hv_size_t numKB) {
|
||||
mp->bufferSize = numKB * 1024;
|
||||
mp->buffer = (char *) hv_malloc(mp->bufferSize);
|
||||
hv_assert(mp->buffer != NULL);
|
||||
mp->bufferIndex = 0;
|
||||
|
||||
// initialise all message lists
|
||||
for (int i = 0; i < MP_NUM_MESSAGE_LISTS; i++) {
|
||||
mp->lists[i].head = NULL;
|
||||
mp->lists[i].pool = NULL;
|
||||
}
|
||||
|
||||
return mp->bufferSize;
|
||||
}
|
||||
|
||||
void mp_free(HvMessagePool *mp) {
|
||||
hv_free(mp->buffer);
|
||||
for (int i = 0; i < MP_NUM_MESSAGE_LISTS; i++) {
|
||||
ml_free(&mp->lists[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void mp_freeMessage(HvMessagePool *mp, HvMessage *m) {
|
||||
const hv_size_t b = msg_getSize(m); // the number of bytes that a message occupies in memory
|
||||
const hv_size_t i = mp_messagelistIndexForSize(b); // the HvMessagePoolList index in the pool
|
||||
HvMessagePoolList *ml = &mp->lists[i];
|
||||
const hv_size_t chunkSize = 32 << i;
|
||||
hv_memclear(m, chunkSize); // clear the chunk, just in case
|
||||
ml_push(ml, m);
|
||||
}
|
||||
|
||||
HvMessage *mp_addMessage(HvMessagePool *mp, const HvMessage *m) {
|
||||
const hv_size_t b = msg_getSize(m);
|
||||
// determine the message list index to allocate data from based on the msg size
|
||||
// smallest chunk size is 32 bytes
|
||||
const hv_size_t i = mp_messagelistIndexForSize(b);
|
||||
|
||||
hv_assert(i < MP_NUM_MESSAGE_LISTS); // how many chunk sizes do we want to support? 32, 64, 128, 256 at the moment
|
||||
HvMessagePoolList *ml = &mp->lists[i];
|
||||
const hv_size_t chunkSize = 32 << i;
|
||||
|
||||
if (ml_hasAvailable(ml)) {
|
||||
char *buf = ml_pop(ml);
|
||||
msg_copyToBuffer(m, buf, chunkSize);
|
||||
return (HvMessage *) buf;
|
||||
} else {
|
||||
// if no appropriately sized buffer is immediately available, increase the size of the used buffer
|
||||
const hv_size_t newIndex = mp->bufferIndex + MP_BLOCK_SIZE_BYTES;
|
||||
hv_assert((newIndex <= mp->bufferSize) &&
|
||||
"The message pool buffer size has been exceeded. The context cannot store more messages. "
|
||||
"Try using the new_with_options() initialiser with a larger pool size (default is 10KB).");
|
||||
|
||||
for (hv_size_t j = mp->bufferIndex; j < newIndex; j += chunkSize) {
|
||||
ml_push(ml, mp->buffer + j); // push new nodes onto the list with chunk pointers
|
||||
}
|
||||
mp->bufferIndex = newIndex;
|
||||
char *buf = ml_pop(ml);
|
||||
msg_copyToBuffer(m, buf, chunkSize);
|
||||
return (HvMessage *) buf;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* Copyright (c) 2014-2018 Enzien Audio Ltd.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _MESSAGE_POOL_H_
|
||||
#define _MESSAGE_POOL_H_
|
||||
|
||||
#include "HvUtils.h"
|
||||
|
||||
#ifdef HV_MP_NUM_MESSAGE_LISTS
|
||||
#define MP_NUM_MESSAGE_LISTS HV_MP_NUM_MESSAGE_LISTS
|
||||
#else // HV_MP_NUM_MESSAGE_LISTS
|
||||
#define MP_NUM_MESSAGE_LISTS 4
|
||||
#endif // HV_MP_NUM_MESSAGE_LISTS
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct HvMessagePoolList {
|
||||
struct MessageListNode *head; // list of currently available blocks
|
||||
struct MessageListNode *pool; // list of currently used blocks
|
||||
} HvMessagePoolList;
|
||||
|
||||
typedef struct HvMessagePool {
|
||||
char *buffer; // the buffer of all messages
|
||||
hv_size_t bufferSize; // in bytes
|
||||
hv_size_t bufferIndex; // the number of total reserved bytes
|
||||
|
||||
HvMessagePoolList lists[MP_NUM_MESSAGE_LISTS];
|
||||
} HvMessagePool;
|
||||
|
||||
/**
|
||||
* The HvMessagePool is a basic memory management system. It reserves a large block of memory at initialisation
|
||||
* and proceeds to divide this block into smaller chunks (usually 512 bytes) as they are needed. These chunks are
|
||||
* further divided into 32, 64, 128, or 256 sections. Each of these sections is managed by a HvMessagePoolList (MPL).
|
||||
* An MPL is a linked-list data structure which is initialised such that its own pool of listnodes is filled with nodes
|
||||
* that point at each subblock (e.g. each 32-byte block of a 512-block chunk).
|
||||
*
|
||||
* HvMessagePool is loosely inspired by TCMalloc. http://goog-perftools.sourceforge.net/doc/tcmalloc.html
|
||||
*/
|
||||
|
||||
hv_size_t mp_init(struct HvMessagePool *mp, hv_size_t numKB);
|
||||
|
||||
void mp_free(struct HvMessagePool *mp);
|
||||
|
||||
/**
|
||||
* Adds a message to the pool and returns a pointer to the copy. Returns NULL
|
||||
* if no space was available in the pool.
|
||||
*/
|
||||
struct HvMessage *mp_addMessage(struct HvMessagePool *mp, const struct HvMessage *m);
|
||||
|
||||
void mp_freeMessage(struct HvMessagePool *mp, struct HvMessage *m);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _MESSAGE_POOL_H_
|
||||
@@ -0,0 +1,215 @@
|
||||
/**
|
||||
* Copyright (c) 2014-2018 Enzien Audio Ltd.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "HvMessageQueue.h"
|
||||
|
||||
hv_size_t mq_initWithPoolSize(HvMessageQueue *q, hv_size_t poolSizeKB) {
|
||||
hv_assert(poolSizeKB > 0);
|
||||
q->head = NULL;
|
||||
q->tail = NULL;
|
||||
q->pool = NULL;
|
||||
return mp_init(&q->mp, poolSizeKB);
|
||||
}
|
||||
|
||||
void mq_free(HvMessageQueue *q) {
|
||||
mq_clear(q);
|
||||
while (q->pool != NULL) {
|
||||
MessageNode *n = q->pool;
|
||||
q->pool = q->pool->next;
|
||||
hv_free(n);
|
||||
}
|
||||
mp_free(&q->mp);
|
||||
}
|
||||
|
||||
static MessageNode *mq_getOrCreateNodeFromPool(HvMessageQueue *q) {
|
||||
if (q->pool == NULL) {
|
||||
// if necessary, create a new empty node
|
||||
q->pool = (MessageNode *) hv_malloc(sizeof(MessageNode));
|
||||
hv_assert(q->pool != NULL);
|
||||
q->pool->next = NULL;
|
||||
}
|
||||
MessageNode *node = q->pool;
|
||||
q->pool = q->pool->next;
|
||||
return node;
|
||||
}
|
||||
|
||||
int mq_size(HvMessageQueue *q) {
|
||||
int size = 0;
|
||||
MessageNode *n = q->head;
|
||||
while (n != NULL) {
|
||||
++size;
|
||||
n = n->next;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
HvMessage *mq_addMessage(HvMessageQueue *q, const HvMessage *m, int let,
|
||||
void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) {
|
||||
MessageNode *node = mq_getOrCreateNodeFromPool(q);
|
||||
node->m = mp_addMessage(&q->mp, m);
|
||||
node->let = let;
|
||||
node->sendMessage = sendMessage;
|
||||
node->prev = NULL;
|
||||
node->next = NULL;
|
||||
|
||||
if (q->tail != NULL) {
|
||||
// the list already contains elements
|
||||
q->tail->next = node;
|
||||
node->prev = q->tail;
|
||||
q->tail = node;
|
||||
} else {
|
||||
// the list is empty
|
||||
node->prev = NULL;
|
||||
q->head = node;
|
||||
q->tail = node;
|
||||
}
|
||||
return mq_node_getMessage(node);
|
||||
}
|
||||
|
||||
HvMessage *mq_addMessageByTimestamp(HvMessageQueue *q, const HvMessage *m, int let,
|
||||
void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) {
|
||||
if (mq_hasMessage(q)) {
|
||||
MessageNode *n = mq_getOrCreateNodeFromPool(q);
|
||||
n->m = mp_addMessage(&q->mp, m);
|
||||
n->let = let;
|
||||
n->sendMessage = sendMessage;
|
||||
|
||||
if (msg_getTimestamp(m) < msg_getTimestamp(q->head->m)) {
|
||||
// the message occurs before the current head
|
||||
n->next = q->head;
|
||||
q->head->prev = n;
|
||||
n->prev = NULL;
|
||||
q->head = n;
|
||||
} else if (msg_getTimestamp(m) >= msg_getTimestamp(q->tail->m)) {
|
||||
// the message occurs after the current tail
|
||||
n->next = NULL;
|
||||
n->prev = q->tail;
|
||||
q->tail->next = n;
|
||||
q->tail = n;
|
||||
} else {
|
||||
// the message occurs somewhere between the head and tail
|
||||
MessageNode *node = q->head;
|
||||
while (node != NULL) {
|
||||
if (msg_getTimestamp(m) < msg_getTimestamp(node->next->m)) {
|
||||
MessageNode *r = node->next;
|
||||
node->next = n;
|
||||
n->next = r;
|
||||
n->prev = node;
|
||||
r->prev = n;
|
||||
break;
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
}
|
||||
return n->m;
|
||||
} else {
|
||||
// add a message to the head
|
||||
return mq_addMessage(q, m, let, sendMessage);
|
||||
}
|
||||
}
|
||||
|
||||
void mq_pop(HvMessageQueue *q) {
|
||||
if (mq_hasMessage(q)) {
|
||||
MessageNode *n = q->head;
|
||||
|
||||
mp_freeMessage(&q->mp, n->m);
|
||||
n->m = NULL;
|
||||
|
||||
n->let = 0;
|
||||
n->sendMessage = NULL;
|
||||
|
||||
q->head = n->next;
|
||||
if (q->head == NULL) {
|
||||
q->tail = NULL;
|
||||
} else {
|
||||
q->head->prev = NULL;
|
||||
}
|
||||
n->next = q->pool;
|
||||
n->prev = NULL;
|
||||
q->pool = n;
|
||||
}
|
||||
}
|
||||
|
||||
bool mq_removeMessage(HvMessageQueue *q, HvMessage *m, void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) {
|
||||
if (mq_hasMessage(q)) {
|
||||
if (mq_node_getMessage(q->head) == m) { // msg in head node
|
||||
// only remove the message if sendMessage is the same as the stored one,
|
||||
// if the sendMessage argument is NULL, it is not checked and will remove any matching message pointer
|
||||
if (sendMessage == NULL || q->head->sendMessage == sendMessage) {
|
||||
mq_pop(q);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
MessageNode *prevNode = q->head;
|
||||
MessageNode *currNode = q->head->next;
|
||||
while ((currNode != NULL) && (currNode->m != m)) {
|
||||
prevNode = currNode;
|
||||
currNode = currNode->next;
|
||||
}
|
||||
if (currNode != NULL) {
|
||||
if (sendMessage == NULL || currNode->sendMessage == sendMessage) {
|
||||
mp_freeMessage(&q->mp, m);
|
||||
currNode->m = NULL;
|
||||
currNode->let = 0;
|
||||
currNode->sendMessage = NULL;
|
||||
if (currNode == q->tail) { // msg in tail node
|
||||
prevNode->next = NULL;
|
||||
q->tail = prevNode;
|
||||
} else { // msg in middle node
|
||||
prevNode->next = currNode->next;
|
||||
currNode->next->prev = prevNode;
|
||||
}
|
||||
currNode->next = (q->pool == NULL) ? NULL : q->pool;
|
||||
currNode->prev = NULL;
|
||||
q->pool = currNode;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void mq_clear(HvMessageQueue *q) {
|
||||
while (mq_hasMessage(q)) {
|
||||
mq_pop(q);
|
||||
}
|
||||
}
|
||||
|
||||
void mq_clearAfter(HvMessageQueue *q, const hv_uint32_t timestamp) {
|
||||
MessageNode *n = q->tail;
|
||||
while (n != NULL && timestamp <= msg_getTimestamp(n->m)) {
|
||||
// free the node's message
|
||||
mp_freeMessage(&q->mp, n->m);
|
||||
n->m = NULL;
|
||||
n->let = 0;
|
||||
n->sendMessage = NULL;
|
||||
|
||||
// the tail points at the previous node
|
||||
q->tail = n->prev;
|
||||
|
||||
// put the node back in the pool
|
||||
n->next = q->pool;
|
||||
n->prev = NULL;
|
||||
if (q->pool != NULL) q->pool->prev = n;
|
||||
q->pool = n;
|
||||
|
||||
// update the tail node
|
||||
n = q->tail;
|
||||
}
|
||||
|
||||
if (q->tail == NULL) q->head = NULL;
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* Copyright (c) 2014-2018 Enzien Audio Ltd.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _MESSAGE_QUEUE_H_
|
||||
#define _MESSAGE_QUEUE_H_
|
||||
|
||||
#include "HvMessage.h"
|
||||
#include "HvMessagePool.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
class HeavyContextInterface;
|
||||
#else
|
||||
typedef struct HeavyContextInterface HeavyContextInterface;
|
||||
#endif
|
||||
|
||||
typedef struct MessageNode {
|
||||
struct MessageNode *prev; // doubly linked list
|
||||
struct MessageNode *next;
|
||||
HvMessage *m;
|
||||
void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *);
|
||||
int let;
|
||||
} MessageNode;
|
||||
|
||||
/** A doubly linked list containing scheduled messages. */
|
||||
typedef struct HvMessageQueue {
|
||||
MessageNode *head; // the head of the queue
|
||||
MessageNode *tail; // the tail of the queue
|
||||
MessageNode *pool; // the head of the reserve pool
|
||||
HvMessagePool mp;
|
||||
} HvMessageQueue;
|
||||
|
||||
hv_size_t mq_initWithPoolSize(HvMessageQueue *q, hv_size_t poolSizeKB);
|
||||
|
||||
void mq_free(HvMessageQueue *q);
|
||||
|
||||
int mq_size(HvMessageQueue *q);
|
||||
|
||||
static inline HvMessage *mq_node_getMessage(MessageNode *n) {
|
||||
return n->m;
|
||||
}
|
||||
|
||||
static inline int mq_node_getLet(MessageNode *n) {
|
||||
return n->let;
|
||||
}
|
||||
|
||||
static inline bool mq_hasMessage(HvMessageQueue *q) {
|
||||
return (q->head != NULL);
|
||||
}
|
||||
|
||||
// true if there is a message and it occurs before (<) timestamp
|
||||
static inline bool mq_hasMessageBefore(HvMessageQueue *const q, const hv_uint32_t timestamp) {
|
||||
return mq_hasMessage(q) && (msg_getTimestamp(mq_node_getMessage(q->head)) < timestamp);
|
||||
}
|
||||
|
||||
static inline MessageNode *mq_peek(HvMessageQueue *q) {
|
||||
return q->head;
|
||||
}
|
||||
|
||||
/** Appends the message to the end of the queue. */
|
||||
HvMessage *mq_addMessage(HvMessageQueue *q, const HvMessage *m, int let,
|
||||
void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *));
|
||||
|
||||
/** Insert in ascending order the message acccording to its timestamp. */
|
||||
HvMessage *mq_addMessageByTimestamp(HvMessageQueue *q, const HvMessage *m, int let,
|
||||
void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *));
|
||||
|
||||
/** Pop the message at the head of the queue (and free its memory). */
|
||||
void mq_pop(HvMessageQueue *q);
|
||||
|
||||
/** Remove a message from the queue (and free its memory) */
|
||||
bool mq_removeMessage(HvMessageQueue *q, HvMessage *m,
|
||||
void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *));
|
||||
|
||||
/** Clears (and frees) all messages in the queue. */
|
||||
void mq_clear(HvMessageQueue *q);
|
||||
|
||||
/** Removes all messages occuring at or after the given timestamp. */
|
||||
void mq_clearAfter(HvMessageQueue *q, const hv_uint32_t timestamp);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _MESSAGE_QUEUE_H_
|
||||
@@ -0,0 +1,141 @@
|
||||
/**
|
||||
* Copyright (c) 2014-2018 Enzien Audio Ltd.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "HvSignalPhasor.h"
|
||||
|
||||
#define HV_PHASOR_2_32 4294967296.0
|
||||
|
||||
#if HV_SIMD_AVX
|
||||
static void sPhasor_updatePhase(SignalPhasor *o, float p) {
|
||||
o->phase = _mm256_set1_ps(p+1.0f); // o->phase is in range [1,2]
|
||||
#elif HV_SIMD_SSE
|
||||
static void sPhasor_updatePhase(SignalPhasor *o, hv_uint32_t p) {
|
||||
o->phase = _mm_set1_epi32(p);
|
||||
#elif HV_SIMD_NEON
|
||||
static void sPhasor_updatePhase(SignalPhasor *o, hv_uint32_t p) {
|
||||
o->phase = vdupq_n_u32(p);
|
||||
#else // HV_SIMD_NONE
|
||||
static void sPhasor_updatePhase(SignalPhasor *o, hv_uint32_t p) {
|
||||
o->phase = p;
|
||||
#endif
|
||||
}
|
||||
|
||||
// input phase is in the range of [0,1]. It is independent of o->phase.
|
||||
#if HV_SIMD_AVX
|
||||
static void sPhasor_k_updatePhase(SignalPhasor *o, float p) {
|
||||
o->phase = _mm256_set_ps(
|
||||
p+1.0f+7.0f*o->step.f2sc, p+1.0f+6.0f*o->step.f2sc,
|
||||
p+1.0f+5.0f*o->step.f2sc, p+1.0f+4.0f*o->step.f2sc,
|
||||
p+1.0f+3.0f*o->step.f2sc, p+1.0f+2.0f*o->step.f2sc,
|
||||
p+1.0f+o->step.f2sc, p+1.0f);
|
||||
|
||||
// ensure that o->phase is still in range [1,2]
|
||||
o->phase = _mm256_or_ps(_mm256_andnot_ps(
|
||||
_mm256_set1_ps(-INFINITY), o->phase), _mm256_set1_ps(1.0f));
|
||||
#elif HV_SIMD_SSE
|
||||
static void sPhasor_k_updatePhase(SignalPhasor *o, hv_uint32_t p) {
|
||||
o->phase = _mm_set_epi32(3*o->step.s+p, 2*o->step.s+p, o->step.s+p, p);
|
||||
#elif HV_SIMD_NEON
|
||||
static void sPhasor_k_updatePhase(SignalPhasor *o, hv_uint32_t p) {
|
||||
o->phase = (uint32x4_t) {p, o->step.s+p, 2*o->step.s+p, 3*o->step.s+p};
|
||||
#else // HV_SIMD_NONE
|
||||
static void sPhasor_k_updatePhase(SignalPhasor *o, hv_uint32_t p) {
|
||||
o->phase = p;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void sPhasor_k_updateFrequency(SignalPhasor *o, float f, double r) {
|
||||
#if HV_SIMD_AVX
|
||||
o->step.f2sc = (float) (f/r);
|
||||
o->inc = _mm256_set1_ps((float) (8.0f*f/r));
|
||||
sPhasor_k_updatePhase(o, o->phase[0]);
|
||||
#elif HV_SIMD_SSE
|
||||
o->step.s = (hv_int32_t) (f*(HV_PHASOR_2_32/r));
|
||||
o->inc = _mm_set1_epi32(4*o->step.s);
|
||||
const hv_uint32_t *const p = (hv_uint32_t *) &o->phase;
|
||||
sPhasor_k_updatePhase(o, p[0]);
|
||||
#elif HV_SIMD_NEON
|
||||
o->step.s = (hv_int32_t) (f*(HV_PHASOR_2_32/r));
|
||||
o->inc = vdupq_n_s32(4*o->step.s);
|
||||
sPhasor_k_updatePhase(o, vgetq_lane_u32(o->phase, 0));
|
||||
#else // HV_SIMD_NONE
|
||||
o->step.s = (hv_int32_t) (f*(HV_PHASOR_2_32/r));
|
||||
o->inc = o->step.s;
|
||||
// no need to update phase
|
||||
#endif
|
||||
}
|
||||
|
||||
hv_size_t sPhasor_init(SignalPhasor *o, double samplerate) {
|
||||
#if HV_SIMD_AVX
|
||||
o->phase = _mm256_set1_ps(1.0f);
|
||||
o->inc = _mm256_setzero_ps();
|
||||
o->step.f2sc = (float) (1.0/samplerate);
|
||||
#elif HV_SIMD_SSE
|
||||
o->phase = _mm_setzero_si128();
|
||||
o->inc = _mm_setzero_si128();
|
||||
o->step.f2sc = (float) (HV_PHASOR_2_32/samplerate);
|
||||
#elif HV_SIMD_NEON
|
||||
o->phase = vdupq_n_u32(0);
|
||||
o->inc = vdupq_n_s32(0);
|
||||
o->step.f2sc = (float) (HV_PHASOR_2_32/samplerate);
|
||||
#else // HV_SIMD_NONE
|
||||
o->phase = 0;
|
||||
o->inc = 0;
|
||||
o->step.f2sc = (float) (HV_PHASOR_2_32/samplerate);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sPhasor_onMessage(HeavyContextInterface *_c, SignalPhasor *o, int letIn, const HvMessage *m) {
|
||||
if (letIn == 1) {
|
||||
if (msg_isFloat(m,0)) {
|
||||
float p = msg_getFloat(m,0);
|
||||
while (p < 0.0f) p += 1.0f; // wrap phase to [0,1]
|
||||
while (p > 1.0f) p -= 1.0f;
|
||||
#if HV_SIMD_AVX
|
||||
sPhasor_updatePhase(o, p);
|
||||
#else // HV_SIMD_SSE || HV_SIMD_NEON || HV_SIMD_NONE
|
||||
sPhasor_updatePhase(o, (hv_uint32_t) (p * HV_PHASOR_2_32));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hv_size_t sPhasor_k_init(SignalPhasor *o, float frequency, double samplerate) {
|
||||
__hv_zero_i((hv_bOuti_t) &o->phase);
|
||||
sPhasor_k_updateFrequency(o, frequency, samplerate);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sPhasor_k_onMessage(HeavyContextInterface *_c, SignalPhasor *o, int letIn, const HvMessage *m) {
|
||||
if (msg_isFloat(m,0)) {
|
||||
switch (letIn) {
|
||||
case 0: sPhasor_k_updateFrequency(o, msg_getFloat(m,0), hv_getSampleRate(_c)); break;
|
||||
case 1: {
|
||||
float p = msg_getFloat(m,0);
|
||||
while (p < 0.0f) p += 1.0f; // wrap phase to [0,1]
|
||||
while (p > 1.0f) p -= 1.0f;
|
||||
#if HV_SIMD_AVX
|
||||
sPhasor_k_updatePhase(o, p);
|
||||
#else // HV_SIMD_SSE || HV_SIMD_NEON || HV_SIMD_NONE
|
||||
sPhasor_k_updatePhase(o, (hv_uint32_t) (p * HV_PHASOR_2_32));
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
/**
|
||||
* Copyright (c) 2014-2018 Enzien Audio Ltd.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _HEAVY_SIGNAL_PHASOR_H_
|
||||
#define _HEAVY_SIGNAL_PHASOR_H_
|
||||
|
||||
#include "HvHeavyInternal.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct SignalPhasor {
|
||||
#if HV_SIMD_AVX
|
||||
__m256 phase; // current phase
|
||||
__m256 inc; // phase increment
|
||||
#elif HV_SIMD_SSE
|
||||
__m128i phase;
|
||||
__m128i inc;
|
||||
#elif HV_SIMD_NEON
|
||||
uint32x4_t phase;
|
||||
int32x4_t inc;
|
||||
#else // HV_SIMD_NONE
|
||||
hv_uint32_t phase;
|
||||
hv_int32_t inc;
|
||||
#endif
|
||||
union {
|
||||
float f2sc; // float to step conversion (used for __phasor~f)
|
||||
hv_int32_t s; // step value (used for __phasor_k~f)
|
||||
} step;
|
||||
} SignalPhasor;
|
||||
|
||||
hv_size_t sPhasor_init(SignalPhasor *o, double samplerate);
|
||||
|
||||
hv_size_t sPhasor_k_init(SignalPhasor *o, float frequency, double samplerate);
|
||||
|
||||
void sPhasor_k_onMessage(HeavyContextInterface *_c, SignalPhasor *o, int letIn, const HvMessage *m);
|
||||
|
||||
void sPhasor_onMessage(HeavyContextInterface *_c, SignalPhasor *o, int letIn, const HvMessage *m);
|
||||
|
||||
static inline void __hv_phasor_f(SignalPhasor *o, hv_bInf_t bIn, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
__m256 p = _mm256_mul_ps(bIn, _mm256_set1_ps(o->step.f2sc)); // a b c d e f g h
|
||||
|
||||
__m256 z = _mm256_setzero_ps();
|
||||
|
||||
// http://stackoverflow.com/questions/11906814/how-to-rotate-an-sse-avx-vector
|
||||
__m256 a = _mm256_permute_ps(p, _MM_SHUFFLE(2,1,0,3)); // d a b c h e f g
|
||||
__m256 b = _mm256_permute2f128_ps(a, a, 0x01); // h e f g d a b c
|
||||
__m256 c = _mm256_blend_ps(a, b, 0x10); // d a b c d e f g
|
||||
__m256 d = _mm256_blend_ps(c, z, 0x01); // 0 a b c d e f g
|
||||
__m256 e = _mm256_add_ps(p, d); // a (a+b) (b+c) (c+d) (d+e) (e+f) (f+g) (g+h)
|
||||
|
||||
__m256 f = _mm256_permute_ps(e, _MM_SHUFFLE(1,0,3,2)); // (b+c) (c+d) a (a+b) (f+g) (g+h) (d+e) (e+f)
|
||||
__m256 g = _mm256_permute2f128_ps(f, f, 0x01); // (f+g) (g+h) (d+e) (e+f) (b+c) (c+d) a (a+b)
|
||||
__m256 h = _mm256_blend_ps(f, g, 0x33); // (b+c) (c+d) a (a+b) (b+c) (c+d) (d+e) (e+f)
|
||||
__m256 i = _mm256_blend_ps(h, z, 0x03); // 0 0 a (a+b) (b+c) (c+d) (d+e) (e+f)
|
||||
__m256 j = _mm256_add_ps(e, i); // a (a+b) (a+b+c) (a+b+c+d) (b+c+d+e) (c+d+e+f) (d+e+f+g) (e+f+g+h)
|
||||
|
||||
__m256 k = _mm256_permute2f128_ps(j, z, 0x02); // 0 0 0 0 a (a+b) (a+b+c) (a+b+c+d) (b+c+d+e)
|
||||
__m256 m = _mm256_add_ps(j, k); // a (a+b) (a+b+c) (a+b+c+d) (a+b+c+d+e) (a+b+c+d+e+f) (a+b+c+d+e+f+g) (a+b+c+d+e+f+g+h)
|
||||
|
||||
__m256 n = _mm256_or_ps(_mm256_andnot_ps(
|
||||
_mm256_set1_ps(-INFINITY),
|
||||
_mm256_add_ps(o->phase, m)),
|
||||
_mm256_set1_ps(1.0f));
|
||||
|
||||
*bOut = _mm256_sub_ps(n, _mm256_set1_ps(1.0f));
|
||||
|
||||
__m256 x = _mm256_permute_ps(n, _MM_SHUFFLE(3,3,3,3));
|
||||
o->phase = _mm256_permute2f128_ps(x, x, 0x11);
|
||||
#elif HV_SIMD_SSE
|
||||
__m128i p = _mm_cvtps_epi32(_mm_mul_ps(bIn, _mm_set1_ps(o->step.f2sc))); // convert frequency to step
|
||||
p = _mm_add_epi32(p, _mm_slli_si128(p, 4)); // add incremental steps to phase (prefix sum)
|
||||
p = _mm_add_epi32(p, _mm_slli_si128(p, 8)); // http://stackoverflow.com/questions/10587598/simd-prefix-sum-on-intel-cpu?rq=1
|
||||
p = _mm_add_epi32(o->phase, p);
|
||||
*bOut = _mm_sub_ps(_mm_castsi128_ps(
|
||||
_mm_or_si128(_mm_srli_epi32(p, 9),
|
||||
_mm_set_epi32(0x3F800000, 0x3F800000, 0x3F800000, 0x3F800000))),
|
||||
_mm_set1_ps(1.0f));
|
||||
o->phase = _mm_shuffle_epi32(p, _MM_SHUFFLE(3,3,3,3));
|
||||
#elif HV_SIMD_NEON
|
||||
int32x4_t p = vcvtq_s32_f32(vmulq_n_f32(bIn, o->step.f2sc));
|
||||
p = vaddq_s32(p, vextq_s32(vdupq_n_s32(0), p, 3)); // http://stackoverflow.com/questions/11259596/arm-neon-intrinsics-rotation
|
||||
p = vaddq_s32(p, vextq_s32(vdupq_n_s32(0), p, 2));
|
||||
uint32x4_t pp = vaddq_u32(o->phase, vreinterpretq_u32_s32(p));
|
||||
*bOut = vsubq_f32(vreinterpretq_f32_u32(vorrq_u32(vshrq_n_u32(pp, 9), vdupq_n_u32(0x3F800000))), vdupq_n_f32(1.0f));
|
||||
o->phase = vdupq_n_u32(pp[3]);
|
||||
#else // HV_SIMD_NONE
|
||||
const hv_uint32_t p = (o->phase >> 9) | 0x3F800000;
|
||||
*bOut = *((float *) (&p)) - 1.0f;
|
||||
o->phase += ((int) (bIn * o->step.f2sc));
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __hv_phasor_k_f(SignalPhasor *o, hv_bOutf_t bOut) {
|
||||
#if HV_SIMD_AVX
|
||||
*bOut = _mm256_sub_ps(o->phase, _mm256_set1_ps(1.0f));
|
||||
o->phase = _mm256_or_ps(_mm256_andnot_ps(
|
||||
_mm256_set1_ps(-INFINITY),
|
||||
_mm256_add_ps(o->phase, o->inc)),
|
||||
_mm256_set1_ps(1.0f));
|
||||
#elif HV_SIMD_SSE
|
||||
*bOut = _mm_sub_ps(_mm_castsi128_ps(
|
||||
_mm_or_si128(_mm_srli_epi32(o->phase, 9),
|
||||
_mm_set_epi32(0x3F800000, 0x3F800000, 0x3F800000, 0x3F800000))),
|
||||
_mm_set1_ps(1.0f));
|
||||
o->phase = _mm_add_epi32(o->phase, o->inc);
|
||||
#elif HV_SIMD_NEON
|
||||
*bOut = vsubq_f32(vreinterpretq_f32_u32(
|
||||
vorrq_u32(vshrq_n_u32(o->phase, 9),
|
||||
vdupq_n_u32(0x3F800000))),
|
||||
vdupq_n_f32(1.0f));
|
||||
o->phase = vaddq_u32(o->phase, vreinterpretq_u32_s32(o->inc));
|
||||
#else // HV_SIMD_NONE
|
||||
const hv_uint32_t p = (o->phase >> 9) | 0x3F800000;
|
||||
*bOut = *((float *) (&p)) - 1.0f;
|
||||
o->phase += o->inc;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // _HEAVY_SIGNAL_PHASOR_H_
|
||||
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* Copyright (c) 2014-2018 Enzien Audio Ltd.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "HvSignalVar.h"
|
||||
|
||||
// __var~f
|
||||
|
||||
static void sVarf_update(SignalVarf *o, float k, float step, bool reverse) {
|
||||
#if HV_SIMD_AVX
|
||||
if (reverse) o->v = _mm256_setr_ps(k+7.0f*step, k+6.0f*step, k+5.0f*step, k+4.0f*step, k+3.0f*step, k+2.0f*step, k+step, k);
|
||||
else o->v = _mm256_set_ps(k+7.0f*step, k+6.0f*step, k+5.0f*step, k+4.0f*step, k+3.0f*step, k+2.0f*step, k+step, k);
|
||||
#elif HV_SIMD_SSE
|
||||
if (reverse) o->v = _mm_setr_ps(k+3.0f*step, k+2.0f*step, k+step, k);
|
||||
else o->v = _mm_set_ps(k+3.0f*step, k+2.0f*step, k+step, k);
|
||||
#elif HV_SIMD_NEON
|
||||
if (reverse) o->v = (float32x4_t) {3.0f*step+k, 2.0f*step+k, step+k, k};
|
||||
else o->v = (float32x4_t) {k, step+k, 2.0f*step+k, 3.0f*step+k};
|
||||
#else // HV_SIMD_NONE
|
||||
o->v = k;
|
||||
#endif
|
||||
}
|
||||
|
||||
hv_size_t sVarf_init(SignalVarf *o, float k, float step, bool reverse) {
|
||||
sVarf_update(o, k, step, reverse);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sVarf_onMessage(HeavyContextInterface *_c, SignalVarf *o, const HvMessage *m) {
|
||||
if (msg_isFloat(m,0)) {
|
||||
sVarf_update(o, msg_getFloat(m,0), msg_isFloat(m,1) ? msg_getFloat(m,1) : 0.0f, msg_getNumElements(m) == 3);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// __var~i
|
||||
|
||||
static void sVari_update(SignalVari *o, int k, int step, bool reverse) {
|
||||
#if HV_SIMD_AVX
|
||||
if (reverse) o->v = _mm256_setr_epi32(k+7*step, k+6*step, k+5*step, k+4*step, k+3*step, k+2*step, k+step, k);
|
||||
else o->v = _mm256_set_epi32(k+7*step, k+6*step, k+5*step, k+4*step, k+3*step, k+2*step, k+step, k);
|
||||
#elif HV_SIMD_SSE
|
||||
if (reverse) o->v = _mm_setr_epi32(k+3*step, k+2*step, k+step, k);
|
||||
else o->v = _mm_set_epi32(k+3*step, k+2*step, k+step, k);
|
||||
#elif HV_SIMD_NEON
|
||||
if (reverse) o->v = (int32x4_t) {3*step+k, 2*step+k, step+k, k};
|
||||
else o->v = (int32x4_t) {k, step+k, 2*step+k, 3*step+k};
|
||||
#else // HV_SIMD_NEON
|
||||
o->v = k;
|
||||
#endif
|
||||
}
|
||||
|
||||
hv_size_t sVari_init(SignalVari *o, int k, int step, bool reverse) {
|
||||
sVari_update(o, k, step, reverse);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sVari_onMessage(HeavyContextInterface *_c, SignalVari *o, const HvMessage *m) {
|
||||
if (msg_isFloat(m,0)) {
|
||||
sVari_update(o, (int) msg_getFloat(m,0), msg_isFloat(m,1) ? (int) msg_getFloat(m,1) : 0, msg_getNumElements(m) == 3);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* Copyright (c) 2014-2018 Enzien Audio Ltd.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _HEAVY_SIGNAL_VAR_H_
|
||||
#define _HEAVY_SIGNAL_VAR_H_
|
||||
|
||||
#include "HvHeavyInternal.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// __var~f, __varread~f, __varwrite~f
|
||||
|
||||
typedef struct SignalVarf {
|
||||
hv_bufferf_t v;
|
||||
} SignalVarf;
|
||||
|
||||
hv_size_t sVarf_init(SignalVarf *o, float k, float step, bool reverse);
|
||||
|
||||
static inline void __hv_varread_f(SignalVarf *o, hv_bOutf_t bOut) {
|
||||
*bOut = o->v;
|
||||
}
|
||||
|
||||
static inline void __hv_varwrite_f(SignalVarf *o, hv_bInf_t bIn) {
|
||||
o->v = bIn;
|
||||
}
|
||||
|
||||
void sVarf_onMessage(HeavyContextInterface *_c, SignalVarf *o, const HvMessage *m);
|
||||
|
||||
|
||||
|
||||
// __var~i, __varread~i, __varwrite~i
|
||||
|
||||
typedef struct SignalVari {
|
||||
hv_bufferi_t v;
|
||||
} SignalVari;
|
||||
|
||||
hv_size_t sVari_init(SignalVari *o, int k, int step, bool reverse);
|
||||
|
||||
static inline void __hv_varread_i(SignalVari *o, hv_bOuti_t bOut) {
|
||||
*bOut = o->v;
|
||||
}
|
||||
|
||||
static inline void __hv_varwrite_i(SignalVari *o, hv_bIni_t bIn) {
|
||||
o->v = bIn;
|
||||
}
|
||||
|
||||
void sVari_onMessage(HeavyContextInterface *_c, SignalVari *o, const HvMessage *m);
|
||||
|
||||
|
||||
|
||||
// __var_k~f, __var_k~i
|
||||
|
||||
#if HV_SIMD_AVX
|
||||
#define __hv_var_k_i(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=_mm256_set_epi32(_h,_g,_f,_e,_d,_c,_b,_a)
|
||||
#define __hv_var_k_i_r(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=_mm256_set_epi32(_a,_b,_c,_d,_e,_f,_g,_h)
|
||||
#define __hv_var_k_f(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=_mm256_set_ps(_h,_g,_f,_e,_d,_c,_b,_a)
|
||||
#define __hv_var_k_f_r(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=_mm256_set_ps(_a,_b,_c,_d,_e,_f,_g,_h)
|
||||
#elif HV_SIMD_SSE
|
||||
#define __hv_var_k_i(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=_mm_set_epi32(_d,_c,_b,_a)
|
||||
#define __hv_var_k_i_r(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=_mm_set_epi32(_a,_b,_c,_d)
|
||||
#define __hv_var_k_f(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=_mm_set_ps(_d,_c,_b,_a)
|
||||
#define __hv_var_k_f_r(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=_mm_set_ps(_a,_b,_c,_d)
|
||||
#elif HV_SIMD_NEON
|
||||
#define __hv_var_k_i(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=((int32x4_t) {_a,_b,_c,_d})
|
||||
#define __hv_var_k_i_r(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=((int32x4_t) {_d,_c,_b,_a})
|
||||
#define __hv_var_k_f(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=((float32x4_t) {_a,_b,_c,_d})
|
||||
#define __hv_var_k_f_r(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=((float32x4_t) {_d,_c,_b,_a})
|
||||
#else // HV_SIMD_NONE
|
||||
#define __hv_var_k_i(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=_a
|
||||
#define __hv_var_k_i_r(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=_a
|
||||
#define __hv_var_k_f(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=_a
|
||||
#define __hv_var_k_f_r(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=_a
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // _HEAVY_SIGNAL_VAR_H_
|
||||
@@ -0,0 +1,110 @@
|
||||
/**
|
||||
* Copyright (c) 2014-2018 Enzien Audio Ltd.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "HvTable.h"
|
||||
#include "HvMessage.h"
|
||||
|
||||
hv_size_t hTable_init(HvTable *o, int length) {
|
||||
o->length = length;
|
||||
// true size of the table is always an integer multple of HV_N_SIMD
|
||||
o->size = (length + HV_N_SIMD_MASK) & ~HV_N_SIMD_MASK;
|
||||
// add an extra length for mirroring
|
||||
o->allocated = o->size + HV_N_SIMD;
|
||||
o->head = 0;
|
||||
hv_size_t numBytes = o->allocated * sizeof(float);
|
||||
o->buffer = (float *) hv_malloc(numBytes);
|
||||
hv_assert(o->buffer != NULL);
|
||||
hv_memclear(o->buffer, numBytes);
|
||||
return numBytes;
|
||||
}
|
||||
|
||||
hv_size_t hTable_initWithData(HvTable *o, int length, const float *data) {
|
||||
o->length = length;
|
||||
o->size = (length + HV_N_SIMD_MASK) & ~HV_N_SIMD_MASK;
|
||||
o->allocated = o->size + HV_N_SIMD;
|
||||
o->head = 0;
|
||||
hv_size_t numBytes = o->size * sizeof(float);
|
||||
o->buffer = (float *) hv_malloc(numBytes);
|
||||
hv_assert(o->buffer != NULL);
|
||||
hv_memclear(o->buffer, numBytes);
|
||||
hv_memcpy(o->buffer, data, length*sizeof(float));
|
||||
return numBytes;
|
||||
}
|
||||
|
||||
hv_size_t hTable_initWithFinalData(HvTable *o, int length, float *data) {
|
||||
o->length = length;
|
||||
o->size = length;
|
||||
o->allocated = length;
|
||||
o->buffer = data;
|
||||
o->head = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void hTable_free(HvTable *o) {
|
||||
hv_free(o->buffer);
|
||||
}
|
||||
|
||||
int hTable_resize(HvTable *o, hv_uint32_t newLength) {
|
||||
// TODO(mhroth): update context with memory allocated by table
|
||||
// NOTE(mhroth): mirrored bytes are not necessarily carried over
|
||||
const hv_uint32_t newSize = (newLength + HV_N_SIMD_MASK) & ~HV_N_SIMD_MASK;
|
||||
if (newSize == o->size) return 0; // early exit if no change in size
|
||||
const hv_uint32_t oldSizeBytes = (hv_uint32_t) (o->size * sizeof(float));
|
||||
const hv_uint32_t newAllocated = newSize + HV_N_SIMD;
|
||||
const hv_uint32_t newAllocatedBytes = (hv_uint32_t) (newAllocated * sizeof(float));
|
||||
|
||||
float *b = (float *) hv_realloc(o->buffer, newAllocatedBytes);
|
||||
hv_assert(b != NULL); // error while reallocing!
|
||||
// ensure that hv_realloc has given us a correctly aligned buffer
|
||||
if ((((hv_uintptr_t) (const void *) b) & ((0x1<<HV_N_SIMD)-1)) == 0) {
|
||||
if (newSize > o->size) {
|
||||
hv_memclear(b + o->size, (newAllocated - o->size) * sizeof(float)); // clear new parts of the buffer
|
||||
}
|
||||
o->buffer = b;
|
||||
} else {
|
||||
// if not, we have to re-malloc ourselves
|
||||
char *c = (char *) hv_malloc(newAllocatedBytes);
|
||||
hv_assert(c != NULL); // error while allocating new buffer!
|
||||
if (newAllocatedBytes > oldSizeBytes) {
|
||||
hv_memcpy(c, b, oldSizeBytes);
|
||||
hv_memclear(c + oldSizeBytes, newAllocatedBytes - oldSizeBytes);
|
||||
} else {
|
||||
hv_memcpy(c, b, newAllocatedBytes);
|
||||
}
|
||||
hv_free(b);
|
||||
o->buffer = (float *) c;
|
||||
}
|
||||
o->length = newLength;
|
||||
o->size = newSize;
|
||||
o->allocated = newAllocated;
|
||||
return (int) (newAllocated - oldSizeBytes - (HV_N_SIMD*sizeof(float)));
|
||||
}
|
||||
|
||||
void hTable_onMessage(HeavyContextInterface *_c, HvTable *o, int letIn, const HvMessage *m,
|
||||
void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) {
|
||||
if (msg_compareSymbol(m,0,"resize") && msg_isFloat(m,1) && msg_getFloat(m,1) >= 0.0f) {
|
||||
hTable_resize(o, (int) hv_ceil_f(msg_getFloat(m,1))); // apply ceil to ensure that tables always have enough space
|
||||
|
||||
// send out the new size of the table
|
||||
HvMessage *n = HV_MESSAGE_ON_STACK(1);
|
||||
msg_initWithFloat(n, msg_getTimestamp(m), (float) hTable_getSize(o));
|
||||
sendMessage(_c, 0, n);
|
||||
}
|
||||
|
||||
else if (msg_compareSymbol(m,0,"mirror")) {
|
||||
hv_memcpy(o->buffer+o->size, o->buffer, HV_N_SIMD*sizeof(float));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* Copyright (c) 2014-2018 Enzien Audio Ltd.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _HEAVY_TABLE_H_
|
||||
#define _HEAVY_TABLE_H_
|
||||
|
||||
#include "HvHeavy.h"
|
||||
#include "HvUtils.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct HvTable {
|
||||
float *buffer;
|
||||
// the number of values that the table is requested to have
|
||||
hv_uint32_t length;
|
||||
|
||||
// the number of usable values that the table actually has
|
||||
// this is always an even multiple of HV_N_SIMD
|
||||
hv_uint32_t size;
|
||||
|
||||
// Note that the true size of the table is (size + HV_N_SIMD),
|
||||
// with the trailing values used by the system, e.g. to create a circular
|
||||
// buffer
|
||||
hv_uint32_t allocated;
|
||||
|
||||
hv_uint32_t head; // the most recently written point
|
||||
} HvTable;
|
||||
|
||||
hv_size_t hTable_init(HvTable *o, int length);
|
||||
|
||||
hv_size_t hTable_initWithData(HvTable *o, int length, const float *data);
|
||||
|
||||
hv_size_t hTable_initWithFinalData(HvTable *o, int length, float *data);
|
||||
|
||||
void hTable_free(HvTable *o);
|
||||
|
||||
int hTable_resize(HvTable *o, hv_uint32_t newLength);
|
||||
|
||||
void hTable_onMessage(HeavyContextInterface *_c, HvTable *o, int letIn, const HvMessage *m,
|
||||
void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *));
|
||||
|
||||
static inline float *hTable_getBuffer(HvTable *o) {
|
||||
return o->buffer;
|
||||
}
|
||||
|
||||
// the user-requested length of the table (number of floats)
|
||||
static inline hv_uint32_t hTable_getLength(HvTable *o) {
|
||||
return o->length;
|
||||
}
|
||||
|
||||
// the usable length of the table (an even multiple of HV_N_SIMD)
|
||||
static inline hv_uint32_t hTable_getSize(HvTable *o) {
|
||||
return o->size;
|
||||
}
|
||||
|
||||
// the number of floats allocated to this table (usually size + HV_N_SIMD)
|
||||
static inline hv_uint32_t hTable_getAllocated(HvTable *o) {
|
||||
return o->allocated;
|
||||
}
|
||||
|
||||
static inline hv_uint32_t hTable_getHead(HvTable *o) {
|
||||
return o->head;
|
||||
}
|
||||
|
||||
static inline void hTable_setHead(HvTable *o, hv_uint32_t head) {
|
||||
o->head = head;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // _HEAVY_TABLE_H_
|
||||
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Copyright (c) 2014-2018 Enzien Audio Ltd.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "HvUtils.h"
|
||||
|
||||
hv_uint32_t hv_string_to_hash(const char *str) {
|
||||
// this hash is based MurmurHash2
|
||||
// http://en.wikipedia.org/wiki/MurmurHash
|
||||
// https://sites.google.com/site/murmurhash/
|
||||
static const hv_uint32_t n = 0x5bd1e995;
|
||||
static const hv_int32_t r = 24;
|
||||
|
||||
if (str == NULL) return 0;
|
||||
|
||||
hv_uint32_t len = (hv_uint32_t) hv_strlen(str);
|
||||
hv_uint32_t x = len; // seed (0) ^ len
|
||||
|
||||
while (len >= 4) {
|
||||
#if HV_EMSCRIPTEN
|
||||
hv_uint32_t k = str[0] | (str[1] << 8) | (str[2] << 16) | (str[3] << 24);
|
||||
#else
|
||||
hv_uint32_t k = *((hv_uint32_t *) str);
|
||||
#endif
|
||||
k *= n;
|
||||
k ^= (k >> r);
|
||||
k *= n;
|
||||
x *= n;
|
||||
x ^= k;
|
||||
str += 4; len -= 4;
|
||||
}
|
||||
switch (len) {
|
||||
case 3: x ^= (str[2] << 16);
|
||||
case 2: x ^= (str[1] << 8);
|
||||
case 1: x ^= str[0]; x *= n;
|
||||
default: break;
|
||||
}
|
||||
x ^= (x >> 13);
|
||||
x *= n;
|
||||
x ^= (x >> 15);
|
||||
return x;
|
||||
}
|
||||
@@ -0,0 +1,319 @@
|
||||
/**
|
||||
* Copyright (c) 2014-2018 Enzien Audio Ltd.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _HEAVY_UTILS_H_
|
||||
#define _HEAVY_UTILS_H_
|
||||
|
||||
// platform definitions
|
||||
#if _WIN32 || _WIN64 || _MSC_VER
|
||||
#define HV_WIN 1
|
||||
#elif __APPLE__
|
||||
#define HV_APPLE 1
|
||||
#elif __ANDROID__
|
||||
#define HV_ANDROID 1
|
||||
#elif __unix__ || __unix
|
||||
#define HV_UNIX 1
|
||||
#else
|
||||
#warning Could not detect platform. Assuming Unix-like.
|
||||
#endif
|
||||
|
||||
#ifdef EMSCRIPTEN
|
||||
#define HV_EMSCRIPTEN 1
|
||||
#endif
|
||||
|
||||
// basic includes
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// type definitions
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#define hv_uint8_t uint8_t
|
||||
#define hv_int16_t int16_t
|
||||
#define hv_uint16_t uint16_t
|
||||
#define hv_int32_t int32_t
|
||||
#define hv_uint32_t uint32_t
|
||||
#define hv_uint64_t uint64_t
|
||||
#define hv_size_t size_t
|
||||
#define hv_uintptr_t uintptr_t
|
||||
|
||||
// SIMD-specific includes
|
||||
#if !(HV_SIMD_NONE || HV_SIMD_NEON || HV_SIMD_SSE || HV_SIMD_AVX)
|
||||
#define HV_SIMD_NEON __ARM_NEON__
|
||||
#define HV_SIMD_SSE (__SSE__ && __SSE2__ && __SSE3__ && __SSSE3__ && __SSE4_1__)
|
||||
#define HV_SIMD_AVX (__AVX__ && HV_SIMD_SSE)
|
||||
#endif
|
||||
#ifndef HV_SIMD_FMA
|
||||
#define HV_SIMD_FMA __FMA__
|
||||
#endif
|
||||
|
||||
#if HV_SIMD_AVX || HV_SIMD_SSE
|
||||
#include <immintrin.h>
|
||||
#elif HV_SIMD_NEON
|
||||
#include <arm_neon.h>
|
||||
#endif
|
||||
|
||||
#if HV_SIMD_NEON // NEON
|
||||
#define HV_N_SIMD 4
|
||||
#define hv_bufferf_t float32x4_t
|
||||
#define hv_bufferi_t int32x4_t
|
||||
#define hv_bInf_t float32x4_t
|
||||
#define hv_bOutf_t float32x4_t*
|
||||
#define hv_bIni_t int32x4_t
|
||||
#define hv_bOuti_t int32x4_t*
|
||||
#define VIf(_x) (_x)
|
||||
#define VOf(_x) (&_x)
|
||||
#define VIi(_x) (_x)
|
||||
#define VOi(_x) (&_x)
|
||||
#elif HV_SIMD_AVX // AVX
|
||||
#define HV_N_SIMD 8
|
||||
#define hv_bufferf_t __m256
|
||||
#define hv_bufferi_t __m256i
|
||||
#define hv_bInf_t __m256
|
||||
#define hv_bOutf_t __m256*
|
||||
#define hv_bIni_t __m256i
|
||||
#define hv_bOuti_t __m256i*
|
||||
#define VIf(_x) (_x)
|
||||
#define VOf(_x) (&_x)
|
||||
#define VIi(_x) (_x)
|
||||
#define VOi(_x) (&_x)
|
||||
#elif HV_SIMD_SSE // SSE
|
||||
#define HV_N_SIMD 4
|
||||
#define hv_bufferf_t __m128
|
||||
#define hv_bufferi_t __m128i
|
||||
#define hv_bInf_t __m128
|
||||
#define hv_bOutf_t __m128*
|
||||
#define hv_bIni_t __m128i
|
||||
#define hv_bOuti_t __m128i*
|
||||
#define VIf(_x) (_x)
|
||||
#define VOf(_x) (&_x)
|
||||
#define VIi(_x) (_x)
|
||||
#define VOi(_x) (&_x)
|
||||
#else // DEFAULT
|
||||
#define HV_N_SIMD 1
|
||||
#undef HV_SIMD_NONE
|
||||
#define HV_SIMD_NONE 1
|
||||
#define hv_bufferf_t float
|
||||
#define hv_bufferi_t int
|
||||
#define hv_bInf_t float
|
||||
#define hv_bOutf_t float*
|
||||
#define hv_bIni_t int
|
||||
#define hv_bOuti_t int*
|
||||
#define VIf(_x) (_x)
|
||||
#define VOf(_x) (&_x)
|
||||
#define VIi(_x) (_x)
|
||||
#define VOi(_x) (&_x)
|
||||
#endif
|
||||
|
||||
#define HV_N_SIMD_MASK (HV_N_SIMD-1)
|
||||
|
||||
// Strings
|
||||
#include <string.h>
|
||||
#define hv_strlen(a) strlen(a)
|
||||
#define hv_strcmp(a, b) strcmp(a, b)
|
||||
#define hv_snprintf(a, b, c, ...) snprintf(a, b, c, __VA_ARGS__)
|
||||
#if HV_WIN
|
||||
#define hv_strncpy(_dst, _src, _len) strncpy_s(_dst, _len, _src, _TRUNCATE)
|
||||
#else
|
||||
#define hv_strncpy(_dst, _src, _len) strncpy(_dst, _src, _len)
|
||||
#endif
|
||||
|
||||
// Memory management
|
||||
#define hv_memcpy(a, b, c) memcpy(a, b, c)
|
||||
#define hv_memclear(a, b) memset(a, 0, b)
|
||||
#if HV_WIN
|
||||
#include <malloc.h>
|
||||
#define hv_alloca(_n) _alloca(_n)
|
||||
#if HV_SIMD_AVX
|
||||
#define hv_malloc(_n) _aligned_malloc(_n, 32)
|
||||
#define hv_realloc(a, b) _aligned_realloc(a, b, 32)
|
||||
#define hv_free(x) _aligned_free(x)
|
||||
#elif HV_SIMD_SSE || HV_SIMD_NEON
|
||||
#define hv_malloc(_n) _aligned_malloc(_n, 16)
|
||||
#define hv_realloc(a, b) _aligned_realloc(a, b, 16)
|
||||
#define hv_free(x) _aligned_free(x)
|
||||
#else // HV_SIMD_NONE
|
||||
#define hv_malloc(_n) malloc(_n)
|
||||
#define hv_realloc(a, b) realloc(a, b)
|
||||
#define hv_free(_n) free(_n)
|
||||
#endif
|
||||
#elif HV_APPLE
|
||||
#define hv_alloca(_n) alloca(_n)
|
||||
#define hv_realloc(a, b) realloc(a, b)
|
||||
#if HV_SIMD_AVX
|
||||
#include <mm_malloc.h>
|
||||
#define hv_malloc(_n) _mm_malloc(_n, 32)
|
||||
#define hv_free(x) _mm_free(x)
|
||||
#elif HV_SIMD_SSE
|
||||
#include <mm_malloc.h>
|
||||
#define hv_malloc(_n) _mm_malloc(_n, 16)
|
||||
#define hv_free(x) _mm_free(x)
|
||||
#elif HV_SIMD_NEON
|
||||
// malloc on ios always has 16-byte alignment
|
||||
#define hv_malloc(_n) malloc(_n)
|
||||
#define hv_free(x) free(x)
|
||||
#else // HV_SIMD_NONE
|
||||
#define hv_malloc(_n) malloc(_n)
|
||||
#define hv_free(x) free(x)
|
||||
#endif
|
||||
#else
|
||||
#include <alloca.h>
|
||||
#define hv_alloca(_n) alloca(_n)
|
||||
#define hv_realloc(a, b) realloc(a, b)
|
||||
#if HV_SIMD_AVX
|
||||
#define hv_malloc(_n) aligned_alloc(32, _n)
|
||||
#define hv_free(x) free(x)
|
||||
#elif HV_SIMD_SSE
|
||||
#define hv_malloc(_n) aligned_alloc(16, _n)
|
||||
#define hv_free(x) free(x)
|
||||
#elif HV_SIMD_NEON
|
||||
#if HV_ANDROID
|
||||
#define hv_malloc(_n) memalign(16, _n)
|
||||
#define hv_free(x) free(x)
|
||||
#else
|
||||
#define hv_malloc(_n) aligned_alloc(16, _n)
|
||||
#define hv_free(x) free(x)
|
||||
#endif
|
||||
#else // HV_SIMD_NONE
|
||||
#define hv_malloc(_n) malloc(_n)
|
||||
#define hv_free(_n) free(_n)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Assert
|
||||
#include <assert.h>
|
||||
#define hv_assert(e) assert(e)
|
||||
|
||||
// Export and Inline
|
||||
#if HV_WIN
|
||||
#define HV_EXPORT __declspec(dllexport)
|
||||
#ifndef __cplusplus // MSVC doesn't like redefining "inline" keyword
|
||||
#define inline __inline
|
||||
#endif
|
||||
#define HV_FORCE_INLINE __forceinline
|
||||
#else
|
||||
#define HV_EXPORT
|
||||
#define HV_FORCE_INLINE inline __attribute__((always_inline))
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
// Returns a 32-bit hash of any string. Returns 0 if string is NULL.
|
||||
hv_uint32_t hv_string_to_hash(const char *str);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
// Math
|
||||
#include <math.h>
|
||||
static inline hv_size_t __hv_utils_max_ui(hv_size_t x, hv_size_t y) { return (x > y) ? x : y; }
|
||||
static inline hv_size_t __hv_utils_min_ui(hv_size_t x, hv_size_t y) { return (x < y) ? x : y; }
|
||||
static inline hv_int32_t __hv_utils_max_i(hv_int32_t x, hv_int32_t y) { return (x > y) ? x : y; }
|
||||
static inline hv_int32_t __hv_utils_min_i(hv_int32_t x, hv_int32_t y) { return (x < y) ? x : y; }
|
||||
#define hv_max_ui(a, b) __hv_utils_max_ui(a, b)
|
||||
#define hv_min_ui(a, b) __hv_utils_min_ui(a, b)
|
||||
#define hv_max_i(a, b) __hv_utils_max_i(a, b)
|
||||
#define hv_min_i(a, b) __hv_utils_min_i(a, b)
|
||||
#define hv_max_f(a, b) fmaxf(a, b)
|
||||
#define hv_min_f(a, b) fminf(a, b)
|
||||
#define hv_max_d(a, b) fmax(a, b)
|
||||
#define hv_min_d(a, b) fmin(a, b)
|
||||
#define hv_sin_f(a) sinf(a)
|
||||
#define hv_sinh_f(a) sinhf(a)
|
||||
#define hv_cos_f(a) cosf(a)
|
||||
#define hv_cosh_f(a) coshf(a)
|
||||
#define hv_tan_f(a) tanf(a)
|
||||
#define hv_tanh_f(a) tanhf(a)
|
||||
#define hv_asin_f(a) asinf(a)
|
||||
#define hv_asinh_f(a) asinhf(a)
|
||||
#define hv_acos_f(a) acosf(a)
|
||||
#define hv_acosh_f(a) acoshf(a)
|
||||
#define hv_atan_f(a) atanf(a)
|
||||
#define hv_atanh_f(a) atanhf(a)
|
||||
#define hv_atan2_f(a, b) atan2f(a, b)
|
||||
#define hv_exp_f(a) expf(a)
|
||||
#define hv_abs_f(a) fabsf(a)
|
||||
#define hv_sqrt_f(a) sqrtf(a)
|
||||
#define hv_log_f(a) logf(a)
|
||||
#define hv_ceil_f(a) ceilf(a)
|
||||
#define hv_floor_f(a) floorf(a)
|
||||
#define hv_round_f(a) roundf(a)
|
||||
#define hv_pow_f(a, b) powf(a, b)
|
||||
#if HV_EMSCRIPTEN
|
||||
#define hv_fma_f(a, b, c) ((a*b)+c) // emscripten does not support fmaf (yet?)
|
||||
#else
|
||||
#define hv_fma_f(a, b, c) fmaf(a, b, c)
|
||||
#endif
|
||||
#if HV_WIN
|
||||
// finds ceil(log2(x))
|
||||
#include <intrin.h>
|
||||
static inline hv_uint32_t __hv_utils_min_max_log2(hv_uint32_t x) {
|
||||
unsigned long z = 0;
|
||||
_BitScanReverse(&z, x);
|
||||
return (hv_uint32_t) (z+1);
|
||||
}
|
||||
#else
|
||||
static inline hv_uint32_t __hv_utils_min_max_log2(hv_uint32_t x) {
|
||||
return (hv_uint32_t) (32 - __builtin_clz(x-1));
|
||||
}
|
||||
#endif
|
||||
#define hv_min_max_log2(a) __hv_utils_min_max_log2(a)
|
||||
|
||||
// Atomics
|
||||
#if HV_WIN
|
||||
#include <windows.h>
|
||||
#define hv_atomic_bool volatile LONG
|
||||
#define HV_SPINLOCK_ACQUIRE(_x) while (InterlockedCompareExchange(&_x, true, false)) { }
|
||||
#define HV_SPINLOCK_TRY(_x) return !InterlockedCompareExchange(&_x, true, false)
|
||||
#define HV_SPINLOCK_RELEASE(_x) (_x = false)
|
||||
#elif HV_ANDROID
|
||||
// Android support for atomics isn't that great, we'll do it manually
|
||||
// https://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Atomic-Builtins.html
|
||||
#define hv_atomic_bool hv_uint8_t
|
||||
#define HV_SPINLOCK_ACQUIRE(_x) while (__sync_lock_test_and_set(&_x, 1))
|
||||
#define HV_SPINLOCK_TRY(_x) return !__sync_lock_test_and_set(&_x, 1)
|
||||
#define HV_SPINLOCK_RELEASE(_x) __sync_lock_release(&_x)
|
||||
#elif __cplusplus
|
||||
#include <atomic>
|
||||
#define hv_atomic_bool std::atomic_flag
|
||||
#define HV_SPINLOCK_ACQUIRE(_x) while (_x.test_and_set(std::memory_order_acquire))
|
||||
#define HV_SPINLOCK_TRY(_x) return !_x.test_and_set(std::memory_order_acquire)
|
||||
#define HV_SPINLOCK_RELEASE(_x) _x.clear(std::memory_order_release)
|
||||
#elif defined(__has_include)
|
||||
#if __has_include(<stdatomic.h>)
|
||||
#include <stdatomic.h>
|
||||
#define hv_atomic_bool atomic_flag
|
||||
#define HV_SPINLOCK_ACQUIRE(_x) while (atomic_flag_test_and_set_explicit(&_x, memory_order_acquire))
|
||||
#define HV_SPINLOCK_TRY(_x) return !atomic_flag_test_and_set_explicit(&_x, memory_order_acquire)
|
||||
#define HV_SPINLOCK_RELEASE(_x) atomic_flag_clear_explicit(memory_order_release)
|
||||
#endif
|
||||
#endif
|
||||
#ifndef hv_atomic_bool
|
||||
#define hv_atomic_bool volatile bool
|
||||
#define HV_SPINLOCK_ACQUIRE(_x) \
|
||||
while (_x) {} \
|
||||
_x = true;
|
||||
#define HV_SPINLOCK_TRY(_x) \
|
||||
if (!_x) { \
|
||||
_x = true; \
|
||||
return true; \
|
||||
} else return false;
|
||||
#define HV_SPINLOCK_RELEASE(_x) (_x = false)
|
||||
#endif
|
||||
|
||||
#endif // _HEAVY_UTILS_H_
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Test sketch for test.pd that was compiled with hvcc -n test test.pd
|
||||
*/
|
||||
|
||||
#include "AudioTools.h"
|
||||
#include "AudioTools/AudioLibs/AudioBoardStream.h" // install https://github.com/pschatzmann/arduino-audio-driver
|
||||
#include "Heavy_test.hpp" // import before PureDataStream!
|
||||
#include "AudioTools/AudioLibs/PureDataStream.h"
|
||||
|
||||
Heavy_test pd_test(44100);
|
||||
PureDataStream pd(pd_test);
|
||||
AudioBoardStream out(AudioKitEs8388V1); // or replace with other output
|
||||
StreamCopy copier(out, pd); // copy kit to kit
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning);
|
||||
|
||||
// setup pd
|
||||
pd.begin();
|
||||
|
||||
// setup output
|
||||
auto cfg = out.defaultConfig(TX_MODE);
|
||||
cfg.sd_active = false;
|
||||
cfg.copyFrom(pd.audioInfo());
|
||||
out.begin(cfg);
|
||||
}
|
||||
|
||||
void loop() { copier.copy(); }
|
||||
@@ -0,0 +1,4 @@
|
||||
#N canvas 349 94 450 300 12;
|
||||
#X obj 134 65 osc~ 220;
|
||||
#X obj 132 178 dac~;
|
||||
#X connect 0 0 1 0;
|
||||
@@ -0,0 +1 @@
|
||||
# STK Examples
|
||||
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* @file streams-stk-audioout.ino
|
||||
* @brief Plays random notes on instrument. For available instruments
|
||||
* see https://pschatzmann.github.io/Arduino-STK/html/classstk_1_1Instrmnt.html
|
||||
* I used an AudioBoardStream to test the output, but you can replace it with any other output class (e.g. I2SStream)
|
||||
* @author Phil Schatzmann
|
||||
* @copyright Copyright (c) 2021
|
||||
*/
|
||||
#include "AudioTools.h"
|
||||
#include "AudioTools/AudioLibs/AudioSTK.h" // install https://github.com/pschatzmann/Arduino-STK
|
||||
#include "AudioTools/AudioLibs/AudioBoardStream.h" // install https://github.com/pschatzmann/arduino-audio-driver
|
||||
|
||||
Sitar instrument(440);
|
||||
STKStream<Instrmnt> in(instrument);
|
||||
AudioBoardStream out(AudioKitEs8388V1);
|
||||
StreamCopy copier(out, in);
|
||||
MusicalNotes notes;
|
||||
|
||||
float note_amplitude = 0.5;
|
||||
static float notes_array[] = { // frequencies aleatoric C-scale
|
||||
N_C3, N_D3, N_E3, N_F3, N_G3, N_A3, N_B3,
|
||||
N_C4, N_D4, N_E4, N_F4, N_G4, N_A4, N_B4,
|
||||
N_C5, N_D5, N_E5, N_F5, N_G5, N_A5, N_B5
|
||||
};
|
||||
|
||||
void play() {
|
||||
static bool active=true;
|
||||
static float freq;
|
||||
static uint64_t timeout;
|
||||
|
||||
if (millis()>timeout){
|
||||
if (active){
|
||||
// play note for 800 ms
|
||||
int count = sizeof(notes_array)/sizeof(float);
|
||||
freq = notes_array[random(count)]; // generate random number from 0 to count-1
|
||||
instrument.noteOn(freq, note_amplitude);
|
||||
timeout = millis()+800;
|
||||
active = false;
|
||||
} else {
|
||||
// silence for 100 ms
|
||||
instrument.noteOff(note_amplitude);
|
||||
timeout = millis()+100;
|
||||
active = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
AudioLogger::instance().begin(Serial,AudioLogger::Warning);
|
||||
|
||||
// setup input
|
||||
auto icfg = in.defaultConfig();
|
||||
in.begin(icfg);
|
||||
|
||||
// setup output
|
||||
auto ocfg = out.defaultConfig(TX_MODE);
|
||||
ocfg.copyFrom(icfg);
|
||||
ocfg.sd_active = false;
|
||||
out.begin(ocfg);
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
play();
|
||||
copier.copy();
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user