This commit is contained in:
2026-02-12 21:00:02 -08:00
parent 77f8236347
commit 8bdbf227ca
1141 changed files with 1010880 additions and 2 deletions

View File

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

View File

@@ -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 <: _,_;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 : *)) : *;

View File

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

View File

@@ -0,0 +1,3 @@
declare filename "copy.dsp";
declare name "copy";
process = _,_;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,3 @@
# Maximilian Examples
see [Maximilian](https://github.com/pschatzmann/Maximilian)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1 @@
# STK Examples

View File

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