diff --git a/Dockerfile b/Dockerfile index 3f834fb2..3c33f70a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -175,8 +175,17 @@ RUN mkdir -p Tools \ RUN ln -sf /usr/local/bin/soul /owl/Tools/soul \ && ln -sf /usr/local/bin/FirmwareSender /owl/Tools/FirmwareSender -# hvcc deps (non-fatal). -RUN pip3 install --no-cache-dir -r Tools/hvcc/requirements.txt || true +# hvcc deps. The pingdynasty hvcc fork runs under python2.7 (heavy.mk invokes +# `python2.7 hvcc.py`), and its requirements (enum==0.4.4, Jinja2==2.7.3) are +# python2 packages. ubuntu 20.04 ships `python2` WITHOUT pip, so bootstrap pip +# for python2 first, then install the fork's requirements into python2 — not +# python3 (a `pip3 install` here left python2.7 without `enum` and silently +# broke every Heavy/PD build). +RUN curl -fsSL https://bootstrap.pypa.io/pip/2.7/get-pip.py -o /tmp/get-pip.py \ + && python2 /tmp/get-pip.py \ + && python2 -m pip install --no-cache-dir -r Tools/hvcc/requirements.txt \ + && rm -f /tmp/get-pip.py \ + && python2 -c "import enum, jinja2; print('hvcc py2 deps OK')" # Pre-build the patch libraries for the target platform so a later # `make PATCHNAME=X patch` links immediately (no separate `make libs` needed). diff --git a/HISTORY.md b/HISTORY.md index 929843eb..e7c8ccf4 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,3 +1,11 @@ +v23.1 +----- + +* Fixed missing SmoothValue and StiffValue::normal definitions +* Fixed SmoothValue to use float lambda +* Fixed Dockerfile hvcc build + + v23.0 ----- diff --git a/LibSource/SimpleValue.cpp b/LibSource/SimpleValue.cpp index d06c6e7a..5c9f2436 100644 --- a/LibSource/SimpleValue.cpp +++ b/LibSource/SimpleValue.cpp @@ -1,19 +1,13 @@ #include "SimpleValue.h" #include "math.h" -/** - * The int constants are meant to provide reasonable defaults for use - * with OWL parameters, which are generally 12 bit fixed point, i.e. Q12. - */ #define Q12_FACTOR ((float)(1<<12)) -template<> const float SimpleValue::DEFAULT_LAMBDA = 0.9; template<> const float SimpleValue::DEFAULT_DELTA = 0.02; template<> const float SimpleValue::DEFAULT_EXP_C = 1; template<> const float SimpleValue::DEFAULT_EXP_K = 2*M_LN2; template<> const float SimpleValue::DEFAULT_LOG_C = 1.6487212707; template<> const float SimpleValue::DEFAULT_LOG_K = 5.74033482823*M_LN2; -template<> const int SimpleValue::DEFAULT_LAMBDA = (int)(0.9*Q12_FACTOR); template<> const int SimpleValue::DEFAULT_DELTA = (int)(0.02*Q12_FACTOR); template<> const int SimpleValue::DEFAULT_EXP_C = (int)(1*Q12_FACTOR); template<> const int SimpleValue::DEFAULT_EXP_K = (int)(2*M_LN2*Q12_FACTOR); diff --git a/LibSource/SimpleValue.h b/LibSource/SimpleValue.h index a60e3713..6773c0a5 100644 --- a/LibSource/SimpleValue.h +++ b/LibSource/SimpleValue.h @@ -48,7 +48,6 @@ class SimpleValue { operator T(){ return get(); } - static const T DEFAULT_LAMBDA; static const T DEFAULT_DELTA; static const T DEFAULT_EXP_C; static const T DEFAULT_EXP_K; @@ -57,13 +56,11 @@ class SimpleValue { }; // prevent -Wundefined-var-template warnings -template<> const float SimpleValue::DEFAULT_LAMBDA; template<> const float SimpleValue::DEFAULT_DELTA; template<> const float SimpleValue::DEFAULT_EXP_C; template<> const float SimpleValue::DEFAULT_EXP_K; template<> const float SimpleValue::DEFAULT_LOG_C; template<> const float SimpleValue::DEFAULT_LOG_K; -template<> const int SimpleValue::DEFAULT_LAMBDA; template<> const int SimpleValue::DEFAULT_DELTA; template<> const int SimpleValue::DEFAULT_EXP_C; template<> const int SimpleValue::DEFAULT_EXP_K; diff --git a/LibSource/SmoothValue.h b/LibSource/SmoothValue.h index b1665756..52690adb 100644 --- a/LibSource/SmoothValue.h +++ b/LibSource/SmoothValue.h @@ -5,19 +5,23 @@ /** * Applies exponential smoothing to a scalar value. - * y(n) = lambda*y(n-1) + (1.0-lambda)*y(n) for floats - * y(n) = (lambda*y(n-1) + y(n))/(lambda+1) for ints + * y(n) = lambda*y(n-1) + (1-lambda)*x(n), with 0 <= lambda < 1, + * where x(n) is the new input and y(n) the smoothed output. * Smoothing is applied when the value is written to, not when it is read. - * todo: adjust for sampling frequency / blocksize (time delay of the response (mean) is 1/α data points) + * + * lambda is a dimensionless coefficient and is always a float, independent of + * the value type T. + * todo: adjust lambda for sample rate / blocksize; for input weight + * alpha = 1-lambda the mean lag of the response is lambda/(1-lambda) samples. * https://en.wikipedia.org/wiki/Exponential_smoothing */ template> class SmoothValue : public Value { public: - T lambda = Value::DEFAULT_LAMBDA; + float lambda = 0.9f; SmoothValue() {} SmoothValue(T value) : Value(value) {} - SmoothValue(T lambda, T init) : Value(init), lambda(lambda) {} + SmoothValue(float lambda, T init) : Value(init), lambda(lambda) {} SmoothValue(const SmoothValue& other) : Value(other), lambda(other.lambda) {} void update(T x){ Value::update(Value::get()*lambda + x*(1.0f - lambda)); @@ -45,7 +49,9 @@ class SmoothValue : public Value { operator T(){ return Value::get(); } - static T normal(float lambda, int blocksize); + static float normal(float lambda, int blocksize){ + return powf(lambda, blocksize/128.0f); + } }; template class SmoothValue; @@ -92,7 +98,9 @@ class StiffValue : public Value { operator T(){ return Value::get(); } - static T normal(float delta); + static float normal(float delta){ + return delta; + } }; template class StiffValue; diff --git a/Source/PatchProcessor.cpp b/Source/PatchProcessor.cpp index a0257ed8..189da5fc 100644 --- a/Source/PatchProcessor.cpp +++ b/Source/PatchProcessor.cpp @@ -136,7 +136,7 @@ PatchParameter PatchProcessor::getParameter(const char* name, T min, T max, T if(parameters[pid] != NULL) delete parameters[pid]; ParameterUpdater* updater = NULL; - T l = SmoothValue::normal(lambda, blocksize); + float l = SmoothValue::normal(lambda, blocksize); T d = StiffValue::normal(delta)*abs(max-min); if(skew == 1.0){ if(lambda == 0.0 && delta == 0.0){ diff --git a/TestPatches/PatchRun.cpp b/TestPatches/PatchRun.cpp index 5b78fb9c..e76f0279 100644 --- a/TestPatches/PatchRun.cpp +++ b/TestPatches/PatchRun.cpp @@ -62,8 +62,10 @@ int main(int argc, char** argv){ ASSERT(wav.getBitsPerSample() == 16, "Incorrect number of bits per sample in input file"); ASSERT(wav.isValid(), "Invalid wav"); int16_t* src = (int16_t*)wav.getData(); - int16_t* end = src+wav.getNumberOfSamples(); - while(src+BLOCKSIZE <= end){ + // getNumberOfSamples() is frames per channel; the buffer holds + // frames*channels interleaved int16s. + int16_t* end = src+wav.getNumberOfSamples()*channels; + while(src+BLOCKSIZE*channels <= end){ samples->split16(src, BLOCKSIZE); processor.patch->processAudio(*samples); samples->comb16(src); diff --git a/TestPatches/SmoothValueTestPatch.hpp b/TestPatches/SmoothValueTestPatch.hpp index 1252126a..a7fd8677 100644 --- a/TestPatches/SmoothValueTestPatch.hpp +++ b/TestPatches/SmoothValueTestPatch.hpp @@ -23,24 +23,42 @@ class SmoothValueTestPatch : public TestPatch { CHECK_CLOSE(a, -1, DEFAULT_TOLERANCE); // arrives near target } { -// typedef SmoothValue SmoothInt; TEST("int"); SmoothValue> a = 5; CHECK_CLOSE(a, 5, DEFAULT_TOLERANCE); - CHECK_CLOSE(a.lambda, 0.9, DEFAULT_TOLERANCE); // default lambda - a = 0; + CHECK_CLOSE(a.lambda, 0.9, DEFAULT_TOLERANCE); a.lambda = 0.6; - CHECK_CLOSE(a, 5*0.9, DEFAULT_TOLERANCE); - for(int i=0; i<32; ++i) + a = 0; + CHECK_CLOSE(a, 3, DEFAULT_TOLERANCE); + int prev = a; + for(int i=0; i<32; ++i){ a = -10; - CHECK_CLOSE(a, -10, DEFAULT_TOLERANCE); + CHECK((int)a <= prev); + prev = a; + } + CHECK_CLOSE(a, -8, DEFAULT_TOLERANCE); } { TEST("weirdint"); SmoothInt a = 5; CHECK_CLOSE(a, 5, DEFAULT_TOLERANCE); - CHECK_CLOSE(a.lambda, 0.9, DEFAULT_TOLERANCE); // default lambda - a = 0; + CHECK_CLOSE(a.lambda, 0.9, DEFAULT_TOLERANCE); + a.lambda = 0.6; + int prev = a; + for(int i=0; i<32; ++i){ + a = 100; + CHECK((int)a >= prev); + prev = a; + } + CHECK_CLOSE(a, 98, DEFAULT_TOLERANCE); + } + { + TEST("normal"); + CHECK_CLOSE(SmoothFloat::normal(0.9, 128), 0.9, DEFAULT_TOLERANCE); + CHECK_CLOSE(SmoothFloat::normal(0.9, 64), sqrtf(0.9), DEFAULT_TOLERANCE); + CHECK_CLOSE(SmoothInt::normal(0.81, 256), 0.81*0.81, DEFAULT_TOLERANCE); + CHECK(SmoothFloat::normal(0.95, 16) < 1); + CHECK_CLOSE(StiffInt::normal(0.5), 0.5, DEFAULT_TOLERANCE); } } };