changeset 14433:95bb94a930a3

always cancel down fractions (frac_t) to avoid overflows and playback problems (e.g. when using resample and equalizer filters together, see http://mplayerhq.hu/pipermail/mplayer-users/2004-December/050058.html)
author reimar
date Sat, 08 Jan 2005 21:34:06 +0000
parents 275b2ce30af7
children 49dd7ad4e37b
files libaf/af.c libaf/af.h libaf/af_channels.c libaf/af_format.c libaf/af_lavcresample.c libaf/af_pan.c libaf/af_resample.c
diffstat 7 files changed, 59 insertions(+), 28 deletions(-) [+]
line wrap: on
line diff
--- a/libaf/af.c	Sat Jan 08 21:06:04 2005 +0000
+++ b/libaf/af.c	Sat Jan 08 21:34:06 2005 +0000
@@ -524,8 +524,7 @@
   register frac_t mul = {1,1};
   // Iterate through all filters 
   do{
-    mul.n *= af->mul.n;
-    mul.d *= af->mul.d;
+    af_frac_mul(&mul, &af->mul);
     af=af->next;
   }while(af);
   return t * (((len/t)*mul.n + 1)/mul.d);
@@ -542,8 +541,7 @@
   register frac_t mul = {1,1};
   // Iterate through all filters 
   do{
-    mul.n *= af->mul.n;
-    mul.d *= af->mul.d;
+    af_frac_mul(&mul, &af->mul);
     af=af->next;
   }while(af);
   return t * (((len/t) * mul.d - 1)/mul.n);
@@ -567,8 +565,7 @@
   register frac_t mul = {1,1};
   // Iterate through all filters and calculate total multiplication factor
   do{
-    mul.n *= af->mul.n;
-    mul.d *= af->mul.d;
+    af_frac_mul(&mul, &af->mul);
     af=af->next;
   }while(af);
   // Sanity check 
@@ -645,6 +642,49 @@
   return NULL;
 }
 
+/**
+ * \brief calculate greatest common divisior of a and b.
+ *   Extended for negative and 0 values. If both are 0 the result is 1.
+ *   The sign of the result will be so that it has the same sign as b.
+ */
+int af_gcd(register int a, register int b) {
+  int b_org = b;
+  while (b != 0) {
+    a %= b;
+    if (a == 0)
+      break;
+    b %= a;
+  }
+  // the result is either in a or b. As the other one is 0 just add them.
+  a += b;
+  if (!a)
+    return 1;
+  if (a * b_org < 0)
+    return -a;
+  return a;
+}
+
+/**
+ * \brief cancel down a fraction f
+ */
+void af_frac_cancel(frac_t *f) {
+  int gcd = af_gcd(f->n, f->d);
+  f->n /= gcd;
+  f->d /= gcd;
+}
+
+/**
+ * \brief multiply out by in and store result in out.
+ *        the resulting fraction wil be cancelled down
+ *        if in and out were.
+ */
+void af_frac_mul(frac_t *out, const frac_t *in) {
+  int gcd1 = af_gcd(out->n, in->d);
+  int gcd2 = af_gcd(in->n, out->d);
+  out->n = (out->n / gcd1) * (in->n / gcd2);
+  out->d = (out->d / gcd2) * (in->d / gcd1);
+}
+
 void af_help (void) {
   int i = 0;
   af_msg(AF_MSG_INFO, "Available audio filters:\n");
--- a/libaf/af.h	Sat Jan 08 21:06:04 2005 +0000
+++ b/libaf/af.h	Sat Jan 08 21:34:06 2005 +0000
@@ -28,6 +28,10 @@
   int d; // Denominator
 } frac_t;
 
+int af_gcd(register int a, register int b);
+void af_frac_cancel(frac_t *f);
+void af_frac_mul(frac_t *out, const frac_t *in);
+
 // Flags used for defining the behavior of an audio filter
 #define AF_FLAGS_REENTRANT 	0x00000000
 #define AF_FLAGS_NOT_REENTRANT 	0x00000001
--- a/libaf/af_channels.c	Sat Jan 08 21:06:04 2005 +0000
+++ b/libaf/af_channels.c	Sat Jan 08 21:34:06 2005 +0000
@@ -151,6 +151,7 @@
     af->data->bps    = ((af_data_t*)arg)->bps;
     af->mul.n        = af->data->nch;
     af->mul.d	     = ((af_data_t*)arg)->nch;
+    af_frac_cancel(&af->mul);
     return check_routes(s,((af_data_t*)arg)->nch,af->data->nch);
   case AF_CONTROL_COMMAND_LINE:{
     int nch = 0;
--- a/libaf/af_format.c	Sat Jan 08 21:06:04 2005 +0000
+++ b/libaf/af_format.c	Sat Jan 08 21:34:06 2005 +0000
@@ -306,6 +306,7 @@
     af->data->nch  = ((af_data_t*)arg)->nch;
     af->mul.n      = af->data->bps;
     af->mul.d      = ((af_data_t*)arg)->bps;
+    af_frac_cancel(&af->mul);
     
     af->play = play; // set default
     
--- a/libaf/af_lavcresample.c	Sat Jan 08 21:06:04 2005 +0000
+++ b/libaf/af_lavcresample.c	Sat Jan 08 21:34:06 2005 +0000
@@ -40,7 +40,6 @@
 // Initialization and runtime control
 static int control(struct af_instance_s* af, int cmd, void* arg)
 {
-  int g;
   af_resample_t* s   = (af_resample_t*)af->setup; 
   af_data_t *data= (af_data_t*)arg;
   int out_rate, test_output_res; // helpers for checking input format
@@ -54,9 +53,9 @@
     if (af->data->nch > CHANS) af->data->nch = CHANS;
     af->data->format = AF_FORMAT_S16_NE;
     af->data->bps    = 2;
-    g= ff_gcd(af->data->rate, data->rate);
-    af->mul.n = af->data->rate/g;
-    af->mul.d = data->rate/g;
+    af->mul.n = af->data->rate;
+    af->mul.d = data->rate;
+    af_frac_cancel(&af->mul);
     af->delay = 500*s->filter_length/(double)min(af->data->rate, data->rate);
 
     if(s->avrctx) av_resample_close(s->avrctx);
--- a/libaf/af_pan.c	Sat Jan 08 21:06:04 2005 +0000
+++ b/libaf/af_pan.c	Sat Jan 08 21:34:06 2005 +0000
@@ -41,6 +41,7 @@
     af->data->bps    = 4;
     af->mul.n        = af->data->nch;
     af->mul.d	     = ((af_data_t*)arg)->nch;
+    af_frac_cancel(&af->mul);
 
     if((af->data->format != ((af_data_t*)arg)->format) || 
        (af->data->bps != ((af_data_t*)arg)->bps)){
--- a/libaf/af_resample.c	Sat Jan 08 21:06:04 2005 +0000
+++ b/libaf/af_resample.c	Sat Jan 08 21:34:06 2005 +0000
@@ -62,22 +62,6 @@
   int		setup;	// Setup parameters cmdline or through postcreate
 } af_resample_t;
 
-// Euclids algorithm for calculating Greatest Common Divisor GCD(a,b)
-static inline int gcd(register int a, register int b)
-{
-  register int r = min(a,b);
-  a=max(a,b);
-  b=r;
-
-  r=a%b;
-  while(r!=0){
-    a=b;
-    b=r;
-    r=a%b;
-  }
-  return b;
-}
-
 // Fast linear interpolation resample with modest audio quality
 static int linint(af_data_t* c,af_data_t* l, af_resample_t* s)
 {
@@ -202,11 +186,12 @@
 	     s->step);
       af->mul.n = af->data->rate;
       af->mul.d = n->rate;
+      af_frac_cancel(&af->mul);
       return rv;
     }
 
     // Calculate up and down sampling factors
-    d=gcd(af->data->rate,n->rate);
+    d=af_gcd(af->data->rate,n->rate);
 
     // If sloppy resampling is enabled limit the upsampling factor
     if(((s->setup & FREQ_MASK) == FREQ_SLOPPY) && (af->data->rate/d > 5000)){
@@ -214,7 +199,7 @@
       int dn=n->rate/2;
       int m=2;
       while(af->data->rate/(d*m) > 5000){
-	d=gcd(up,dn); 
+	d=af_gcd(up,dn); 
 	up/=2; dn/=2; m*=2;
       }
       d*=m;