Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closing Issue/67 #76

Merged
merged 6 commits into from
Jan 1, 2016
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 36 additions & 1 deletion include/core/JamomaInterpolator.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ namespace Jamoma {
@param x1 Sample value at prior integer index
@param x2 Sample value at next integer index
@param x3 Unused sample value
@param delta Fractional location between x1 (delta=0) and x1 (delta=1)
@param delta Fractional location between x1 (delta=0) and x2 (delta=1)
@return The interpolated value
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to be consistent with other places in our code, should it be x0 (current input) and x1 (previous input) ?

*/
template<class T>
Expand All @@ -108,6 +108,41 @@ namespace Jamoma {
return x1 + delta * (x2-x1);
}
};

/** Allpass interpolation
Testing shows this algorithm will become less accurate the more points it computes between two known samples.
Also, because it uses an internal history, the reset() function should be used when switching between non-continuous segments of sampled audio data.
@param x0 Unused sample value
@param x1 Sample value at prior integer index
@param x2 Sample value at next integer index
@param x3 Unused sample value
@param delta Fractional location between x1 (delta=0) and x2 (delta=1) @n
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment about x0 and x1

Be aware that delta=1.0 may not return the exact value at x2 given the nature of this algorithm.
@return The interpolated value
*/
template<class T>
class Allpass : Base {
public:
static const int delay = 1;
T last_out = T(0.0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it is a private member then it should be mCamelCase (you noted that you saw this at the last moment). It should be private, right? Also since this is filter nomenclature it should be named mY1 because it the output from 1 sample in the past.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that should be private so mY1 would be appropriate.


constexpr T operator()(T x1, T x2, double delta) noexcept {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment about x0 and x1

T out = x1 + delta * (x2-last_out);
last_out = out;
return out;
}

constexpr T operator()(T x0, T x1, T x2, T x3, double delta) noexcept {
// NW: ideally we would call the operator above to remain DRY, but I could not get syntax right
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you try something like (*this)(x0, x1, delta); ?

T out = x1 + delta * (x2-last_out);
last_out = out;
return out;
}

void reset() {
last_out = T(0.0);
}
};


/** Cosine interpolation
Expand Down
116 changes: 116 additions & 0 deletions test/Interpolator/Interpolator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class InterpolatorTest {
testNone();
testNearest();
testLinear();
testAllpass();
testCosine();
testCubic();
testHermite();
Expand Down Expand Up @@ -211,6 +212,121 @@ class InterpolatorTest {
mTest->TEST_ASSERT("testLinear overloaded operator produced consistent output", badSampleCount == 0);
}


void testAllpass() {
int badSampleCount = 0;
Jamoma::Interpolator::Allpass<Jamoma::Sample> my_interp;

auto x0 = -1.0;
auto x1 = 2.0;
auto x2 = 1.0;
auto x3 = 4.0;

// The following output was generated using the Octave code
// in InterpolatorTargetOutput.m by NW
Jamoma::SampleVector expectedOutputAllpass = {
2.015625,
1.96826171875,
1.954612731933594,
1.94033670425415,
1.926536194980145,
1.913137231720611,
1.900125615280558,
1.88748429808993,
1.875197520581104,
1.863250387409203,
1.851628839664043,
1.840319592562992,
1.829310082760642,
1.818588419396109,
1.808143339204037,
1.797964165198991,
1.788040768619018,
1.778363533825901,
1.768923325895436,
1.759711460657676,
1.7507196769717,
1.741940111040978,
1.733365272594648,
1.724988022777007,
1.716801553602732,
1.70879936884889,
1.700975266266874,
1.693323321008243,
1.68583787016814,
1.678513498358684,
1.671345024232512,
1.664327487883744,
1.657456139059945,
1.650726426124404,
1.644133985713216,
1.637674633036316,
1.63134435277588,
1.625139290539321,
1.619055744827601,
1.613090159482749,
1.607239116581364,
1.60149932974348,
1.595867637828599,
1.590340998992838,
1.584916485083161,
1.579591276346478,
1.574362656433055,
1.569228007675209,
1.564184806623668,
1.559230619825259,
1.554363099826747,
1.549579981390768,
1.54487907791077,
1.540258278012788,
1.535715542332761,
1.531248900458834,
1.526856448028851,
1.522536343973854,
1.518286807899103,
1.51410611759459,
1.509992606667656,
1.505944662290708,
1.501960723057584,
1.498039276942416
};

Jamoma::Sample temp = 0.0;
Jamoma::Sample tempExpected = 0.0;
double delta = 0.0;

for (int i = 0; i < expectedOutputAllpass.size(); i++) {
delta = (i + 1.0) / 64.0;
temp = my_interp(x1,x2,delta);
tempExpected = expectedOutputAllpass[i];
if (! mTest->compare(temp, tempExpected, true, 8) ) {
badSampleCount++;
std::cout << "sample " << i << " had a difference of " << std::fabs(temp - tempExpected) << std::endl;
}
}

mTest->TEST_ASSERT("testAllpass produced correct interpolation output", badSampleCount == 0);

// reset varaiables
badSampleCount = 0;
temp = 0.0;
tempExpected = 0.0;
delta = 0.0;
my_interp.reset();

for (int i = 0; i < expectedOutputAllpass.size(); i++) {
delta = (i + 1.0) / 64.0;
temp = my_interp(x0,x1,x2,x3,delta);
tempExpected = expectedOutputAllpass[i];
if (! mTest->compare(temp, tempExpected, true, 8) ) {
badSampleCount++;
std::cout << "sample " << i << " had a difference of " << std::fabs(temp - tempExpected) << std::endl;
}
}

mTest->TEST_ASSERT("testAllpass overloaded operator produced consistent output", badSampleCount == 0);
}


void testCosine() {
int badSampleCount = 0;
Expand Down
21 changes: 21 additions & 0 deletions test/Interpolator/InterpolatorTargetOutput.m
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
output_splinegen = double (1 : 64);
output_cubicgen = double (1 : 64);
output_cosinegen = double (1 : 64);
output_allpassgen = double (1 : 64);
lastout_allpass = 0.0;

% the following function is adapted from gen~.interpolation example from Max 7.1
function retval = interp_hermitegen(v,delta)
Expand Down Expand Up @@ -93,6 +95,22 @@
retval = x + a2*(y-x);
endfunction

% reference: https://ccrma.stanford.edu/~jos/pasp/First_Order_Allpass_Interpolation.html
function retval = interp_allpassgen(v,delta,history)
retval = 0.0;
delta_int = fix(delta);
a = delta - delta_int;
% the following if statement corrects for a difference between deltas of 0.0 and 1.0 in this algorithm
if (a == 0.0)
delta_int = delta_int - 1;
a = 1.0;
endif
x1 = v(delta_int);
x2 = v(delta_int+1);
out = x1 + a*(x2-history);
retval = out;
endfunction

for i = 1:64
current_delta = 2.0 + i / 64;
output_linear(i) = interp1(x,current_delta);
Expand All @@ -102,6 +120,8 @@
output_splinegen(i) = interp_splinegen(x,current_delta);
output_cubicgen(i) = interp_cubicgen(x,current_delta);
output_cosinegen(i) = interp_cosinegen(x,current_delta);
output_allpassgen(i) = interp_allpassgen(x,current_delta,lastout_allpass);
lastout_allpass = output_allpassgen(i);
endfor

save expectedOutput.mat output_linear
Expand All @@ -111,3 +131,4 @@
save -append expectedOutput.mat output_splinegen
save -append expectedOutput.mat output_cubicgen
save -append expectedOutput.mat output_cosinegen
save -append expectedOutput.mat output_allpassgen