summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Modules/audioop.c236
1 files changed, 210 insertions, 26 deletions
diff --git a/Modules/audioop.c b/Modules/audioop.c
index 92152b9..385a2f9 100644
--- a/Modules/audioop.c
+++ b/Modules/audioop.c
@@ -33,7 +33,7 @@ OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include "allobjects.h"
#include "modsupport.h"
-/* Code shamelessly stealen from sox,
+/* Code shamelessly stolen from sox,
** (c) Craig Reese, Joe Campbell and Jeff Poskanzer 1989 */
#define MINLIN -32768
@@ -270,44 +270,187 @@ audioop_rms(self, args)
return newintobject(val);
}
+double _sum2(a, b, len)
+ short *a;
+ short *b;
+ int len;
+{
+ int i;
+ double sum = 0.0;
+
+ for( i=0; i<len; i++) {
+ sum = sum + (double)a[i]*(double)b[i];
+ }
+ return sum;
+}
+
+/*
+** Findfit tries to locate a sample within another sample. Its main use
+** is in echo-cancellation (to find the feedback of the output signal in
+** the input signal).
+** The method used is as follows:
+**
+** let R be the reference signal (length n) and A the input signal (length N)
+** with N > n, and let all sums be over i from 0 to n-1.
+**
+** Now, for each j in {0..N-n} we compute a factor fj so that -fj*R matches A
+** as good as possible, i.e. sum( (A[j+i]+fj*R[i])^2 ) is minimal. This
+** equation gives fj = sum( A[j+i]R[i] ) / sum(R[i]^2).
+**
+** Next, we compute the relative distance between the original signal and
+** the modified signal and minimize that over j:
+** vj = sum( (A[j+i]-fj*R[i])^2 ) / sum( A[j+i]^2 ) =>
+** vj = ( sum(A[j+i]^2)*sum(R[i]^2) - sum(A[j+i]R[i])^2 ) / sum( A[j+i]^2 )
+**
+** In the code variables correspond as follows:
+** cp1 A
+** cp2 R
+** len1 N
+** len2 n
+** aj_m1 A[j-1]
+** aj_lm1 A[j+n-1]
+** sum_ri_2 sum(R[i]^2)
+** sum_aij_2 sum(A[i+j]^2)
+** sum_aij_ri sum(A[i+j]R[i])
+**
+** sum_ri is calculated once, sum_aij_2 is updated each step and sum_aij_ri
+** is completely recalculated each step.
+*/
static object *
-audioop_rms2(self, args)
+audioop_findfit(self, args)
object *self;
object *args;
{
- signed char *cp1, *cp2;
- int len1, len2, size, val1, val2;
- int i;
- float sum_squares = 0.0;
+ short *cp1, *cp2;
+ int len1, len2;
+ int j, best_j;
+ double aj_m1, aj_lm1;
+ double sum_ri_2, sum_aij_2, sum_aij_ri, result, best_result, factor;
- if ( !getargs(args, "(s#s#i)", &cp1, &len1, &cp2, &len2, &size) )
+ if ( !getargs(args, "(s#s#)", &cp1, &len1, &cp2, &len2) )
return 0;
- if ( size != 1 && size != 2 && size != 4 ) {
- err_setstr(AudioopError, "Size should be 1, 2 or 4");
+ if ( len1 & 1 || len2 & 1 ) {
+ err_setstr(AudioopError, "Strings should be even-sized");
+ return 0;
+ }
+ len1 >>= 1;
+ len2 >>= 1;
+
+ if ( len1 < len2 ) {
+ err_setstr(AudioopError, "First sample should be longer");
+ return 0;
+ }
+ sum_ri_2 = _sum2(cp2, cp2, len2);
+ sum_aij_2 = _sum2(cp1, cp1, len2);
+ sum_aij_ri = _sum2(cp1, cp2, len2);
+
+ result = (sum_ri_2*sum_aij_2 - sum_aij_ri*sum_aij_ri) / sum_aij_2;
+
+ best_result = result;
+ best_j = 0;
+ j = 0;
+
+ for ( j=1; j<=len1-len2; j++) {
+ aj_m1 = (double)cp1[j-1];
+ aj_lm1 = (double)cp1[j+len2-1];
+
+ sum_aij_2 = sum_aij_2 + aj_lm1*aj_lm1 - aj_m1*aj_m1;
+ sum_aij_ri = _sum2(cp1+j, cp2, len2);
+
+ result = (sum_ri_2*sum_aij_2 - sum_aij_ri*sum_aij_ri) / sum_aij_2;
+
+ if ( result < best_result ) {
+ best_result = result;
+ best_j = j;
+ }
+
+ }
+
+ factor = _sum2(cp1+best_j, cp2, len2) / sum_ri_2;
+
+ return mkvalue("(if)", best_j, factor);
+}
+
+/*
+** findfactor finds a factor f so that the energy in A-fB is minimal.
+** See the comment for findfit for details.
+*/
+static object *
+audioop_findfactor(self, args)
+ object *self;
+ object *args;
+{
+ short *cp1, *cp2;
+ int len1, len2;
+ double sum_ri_2, sum_aij_ri, result;
+
+ if ( !getargs(args, "(s#s#)", &cp1, &len1, &cp2, &len2) )
+ return 0;
+ if ( len1 & 1 || len2 & 1 ) {
+ err_setstr(AudioopError, "Strings should be even-sized");
return 0;
}
if ( len1 != len2 ) {
err_setstr(AudioopError, "Samples should be same size");
return 0;
}
- for ( i=0; i<len1; i+= size) {
- if ( size == 1 ) {
- val1 = (int)*CHARP(cp1, i);
- val2 = (int)*CHARP(cp2, i);
- } else if ( size == 2 ) {
- val1 = (int)*SHORTP(cp1, i);
- val2 = (int)*SHORTP(cp2, i);
- } else if ( size == 4 ) {
- val1 = (int)*LONGP(cp1, i);
- val2 = (int)*LONGP(cp2, i);
+ len2 >>= 1;
+ sum_ri_2 = _sum2(cp2, cp2, len2);
+ sum_aij_ri = _sum2(cp1, cp2, len2);
+
+ result = sum_aij_ri / sum_ri_2;
+
+ return newfloatobject(result);
+}
+
+/*
+** findmax returns the index of the n-sized segment of the input sample
+** that contains the most energy.
+*/
+static object *
+audioop_findmax(self, args)
+ object *self;
+ object *args;
+{
+ short *cp1;
+ int len1, len2;
+ int j, best_j;
+ double aj_m1, aj_lm1;
+ double result, best_result;
+
+ if ( !getargs(args, "(s#i)", &cp1, &len1, &len2) )
+ return 0;
+ if ( len1 & 1 ) {
+ err_setstr(AudioopError, "Strings should be even-sized");
+ return 0;
+ }
+ len1 >>= 1;
+
+ if ( len1 < len2 ) {
+ err_setstr(AudioopError, "Input sample should be longer");
+ return 0;
+ }
+
+ result = _sum2(cp1, cp1, len2);
+
+ best_result = result;
+ best_j = 0;
+ j = 0;
+
+ for ( j=1; j<=len1-len2; j++) {
+ aj_m1 = (double)cp1[j-1];
+ aj_lm1 = (double)cp1[j+len2-1];
+
+ result = result + aj_lm1*aj_lm1 - aj_m1*aj_m1;
+
+ if ( result > best_result ) {
+ best_result = result;
+ best_j = j;
}
- sum_squares += (float)val1*(float)val2;
+
}
- if ( len1 == 0 )
- val1 = 0;
- else
- val1 = (int)sqrt(sum_squares / (float)(len1/size));
- return newintobject(val1);
+
+ return newintobject(best_j);
}
static object *
@@ -679,6 +822,44 @@ audioop_bias(self, args)
}
static object *
+audioop_lin2lin(self, args)
+ object *self;
+ object *args;
+{
+ signed char *cp;
+ unsigned char *ncp;
+ int len, size, size2, val;
+ object *rv;
+ int i, j;
+
+ if ( !getargs(args, "(s#ii)",
+ &cp, &len, &size, &size2) )
+ return 0;
+
+ if ( (size != 1 && size != 2 && size != 4) ||
+ (size2 != 1 && size2 != 2 && size2 != 4)) {
+ err_setstr(AudioopError, "Size should be 1, 2 or 4");
+ return 0;
+ }
+
+ rv = newsizedstringobject(NULL, (len/size)*size2);
+ if ( rv == 0 )
+ return 0;
+ ncp = (unsigned char *)getstringvalue(rv);
+
+ for ( i=0, j=0; i < len; i += size, j += size2 ) {
+ if ( size == 1 ) val = ((int)*CHARP(cp, i)) << 8;
+ else if ( size == 2 ) val = (int)*SHORTP(cp, i);
+ else if ( size == 4 ) val = ((int)*LONGP(cp, i)) >> 16;
+
+ if ( size2 == 1 ) *CHARP(ncp, j) = (signed char)(val >> 8);
+ else if ( size2 == 2 ) *SHORTP(ncp, j) = (short)(val);
+ else if ( size2 == 4 ) *LONGP(ncp, j) = (long)(val<<16);
+ }
+ return rv;
+}
+
+static object *
audioop_lin2ulaw(self, args)
object *self;
object *args;
@@ -1088,13 +1269,16 @@ static struct methodlist audioop_methods[] = {
{ "maxpp", audioop_maxpp },
{ "avgpp", audioop_avgpp },
{ "rms", audioop_rms },
- { "rms2", audioop_rms2 },
+ { "findfit", audioop_findfit },
+ { "findmax", audioop_findmax },
+ { "findfactor", audioop_findfactor },
{ "cross", audioop_cross },
{ "mul", audioop_mul },
{ "add", audioop_add },
{ "bias", audioop_bias },
{ "ulaw2lin", audioop_ulaw2lin },
{ "lin2ulaw", audioop_lin2ulaw },
+ { "lin2lin", audioop_lin2lin },
{ "adpcm2lin", audioop_adpcm2lin },
{ "lin2adpcm", audioop_lin2adpcm },
{ "adpcm32lin", audioop_adpcm32lin },