[pypy-commit] pypy default: Issue #1975: Port some audioop functions to cffi,

amauryfa noreply at buildbot.pypy.org
Fri Feb 13 00:10:58 CET 2015


Author: Amaury Forgeot d'Arc <amauryfa at gmail.com>
Branch: 
Changeset: r75844:62a65dd49942
Date: 2015-02-13 00:08 +0100
http://bitbucket.org/pypy/pypy/changeset/62a65dd49942/

Log:	Issue #1975: Port some audioop functions to cffi, this considerably
	improve the performance of the "pydub" example.

	pypy-no-jit is now ~30% slower than CPython.

diff --git a/lib_pypy/audioop.py b/lib_pypy/audioop.py
--- a/lib_pypy/audioop.py
+++ b/lib_pypy/audioop.py
@@ -364,19 +364,8 @@
     sample_count = _sample_count(cp, size)
 
     rv = ffi.new("unsigned char[]", len(cp) * 2)
-    result = ffi.buffer(rv)
-    clip = _get_clipfn(size)
-
-    for i in range(sample_count):
-        sample = _get_sample(cp, size, i)
-
-        l_sample = clip(sample * fac1)
-        r_sample = clip(sample * fac2)
-
-        _put_sample(result, size, i * 2, l_sample)
-        _put_sample(result, size, i * 2 + 1, r_sample)
-
-    return result[:]
+    lib.tostereo(rv, cp, len(cp), size, fac1, fac2)
+    return ffi.buffer(rv)[:]
 
 
 def add(cp1, cp2, size):
@@ -385,20 +374,9 @@
     if len(cp1) != len(cp2):
         raise error("Lengths should be the same")
 
-    clip = _get_clipfn(size)
-    sample_count = _sample_count(cp1, size)
     rv = ffi.new("unsigned char[]", len(cp1))
-    result = ffi.buffer(rv)
-
-    for i in range(sample_count):
-        sample1 = getsample(cp1, size, i)
-        sample2 = getsample(cp2, size, i)
-
-        sample = clip(sample1 + sample2)
-
-        _put_sample(result, size, i, sample)
-
-    return result[:]
+    lib.add(rv, cp1, cp2, len(cp1), size)
+    return ffi.buffer(rv)[:]
 
 
 def bias(cp, size, bias):
@@ -477,11 +455,10 @@
     inrate //= d
     outrate //= d
 
-    prev_i = [0] * nchannels
-    cur_i = [0] * nchannels
-
     if state is None:
         d = -outrate
+        prev_i = ffi.new('int[]', nchannels)
+        cur_i = ffi.new('int[]', nchannels)
     else:
         d, samps = state
 
@@ -489,56 +466,37 @@
             raise error("illegal state argument")
 
         prev_i, cur_i = zip(*samps)
-        prev_i, cur_i = list(prev_i), list(cur_i)
+        prev_i = ffi.new('int[]', prev_i)
+        cur_i = ffi.new('int[]', cur_i)
+    state_d = ffi.new('int[]', (d,))
 
     q = frame_count // inrate
     ceiling = (q + 1) * outrate
     nbytes = ceiling * bytes_per_frame
 
     rv = ffi.new("unsigned char[]", nbytes)
-    result = ffi.buffer(rv)
-
-    samples = _get_samples(cp, size)
-    out_i = 0
-    while True:
-        while d < 0:
-            if frame_count == 0:
-                samps = zip(prev_i, cur_i)
-                retval = result[:]
-
-                # slice off extra bytes
-                trim_index = (out_i * bytes_per_frame) - len(retval)
-                retval = retval[:trim_index]
-
-                return (retval, (d, tuple(samps)))
-
-            for chan in range(nchannels):
-                prev_i[chan] = cur_i[chan]
-                cur_i[chan] = next(samples)
-
-                cur_i[chan] = (
-                    (weightA * cur_i[chan] + weightB * prev_i[chan])
-                    // (weightA + weightB)
-                )
-
-            frame_count -= 1
-            d += outrate
-
-        while d >= 0:
-            for chan in range(nchannels):
-                cur_o = (
-                    (prev_i[chan] * d + cur_i[chan] * (outrate - d))
-                    // outrate
-                )
-                _put_sample(result, size, out_i, _overflow(cur_o, size))
-                out_i += 1
-                d -= inrate
+    trim_index = lib.ratecv(rv, cp, frame_count, size,
+                            nchannels, inrate, outrate,
+                            state_d, prev_i, cur_i,
+                            weightA, weightB)
+    result = ffi.buffer(rv)[:trim_index]
+    samps = zip(prev_i, cur_i)
+    return (result, (d, tuple(samps)))
 
 
 ffi = FFI()
 ffi.cdef("""
 typedef short PyInt16;
 
+int ratecv(char* rv, char* cp, size_t len, int size,
+           int nchannels, int inrate, int outrate,
+           int* state_d, int* prev_i, int* cur_i,
+           int weightA, int weightB);
+
+void tostereo(char* rv, char* cp, size_t len, int size,
+              double fac1, double fac2);
+void add(char* rv, char* cp1, char* cp2, size_t len1, int size);
+
 /* 2's complement (14-bit range) */
 unsigned char
 st_14linear2ulaw(PyInt16 pcm_val);
@@ -829,7 +787,166 @@
 #define LONGP(cp, i) ((Py_Int32 *)(cp+i))
 """
 
-lib = ffi.verify(_AUDIOOP_C_MODULE + """
+lib = ffi.verify(_AUDIOOP_C_MODULE + r"""
+#include <math.h>
+
+static const int maxvals[] = {0, 0x7F, 0x7FFF, 0x7FFFFF, 0x7FFFFFFF};
+/* -1 trick is needed on Windows to support -0x80000000 without a warning */
+static const int minvals[] = {0, -0x80, -0x8000, -0x800000, -0x7FFFFFFF-1};
+
+static int
+fbound(double val, double minval, double maxval)
+{
+    if (val > maxval)
+        val = maxval;
+    else if (val < minval + 1)
+        val = minval;
+    return val;
+}
+
+static int
+gcd(int a, int b)
+{
+    while (b > 0) {
+        int tmp = a % b;
+        a = b;
+        b = tmp;
+    }
+    return a;
+}
+
+int ratecv(char* rv, char* cp, size_t len, int size,
+           int nchannels, int inrate, int outrate,
+           int* state_d, int* prev_i, int* cur_i,
+           int weightA, int weightB)
+{
+    char *ncp = rv;
+    int d, chan;
+
+    /* divide inrate and outrate by their greatest common divisor */
+    d = gcd(inrate, outrate);
+    inrate /= d;
+    outrate /= d;
+    /* divide weightA and weightB by their greatest common divisor */
+    d = gcd(weightA, weightB);
+    weightA /= d;
+    weightA /= d;
+
+    d = *state_d;
+
+    for (;;) {
+        while (d < 0) {
+            if (len == 0) {
+                *state_d = d;
+                return ncp - rv;
+            }
+            for (chan = 0; chan < nchannels; chan++) {
+                prev_i[chan] = cur_i[chan];
+                if (size == 1)
+                    cur_i[chan] = ((int)*CHARP(cp, 0)) << 24;
+                else if (size == 2)
+                    cur_i[chan] = ((int)*SHORTP(cp, 0)) << 16;
+                else if (size == 4)
+                    cur_i[chan] = (int)*LONGP(cp, 0);
+                cp += size;
+                /* implements a simple digital filter */
+                cur_i[chan] = (int)(
+                    ((double)weightA * (double)cur_i[chan] +
+                     (double)weightB * (double)prev_i[chan]) /
+                    ((double)weightA + (double)weightB));
+            }
+            len--;
+            d += outrate;
+        }
+        while (d >= 0) {
+            for (chan = 0; chan < nchannels; chan++) {
+                int cur_o;
+                cur_o = (int)(((double)prev_i[chan] * (double)d +
+                         (double)cur_i[chan] * (double)(outrate - d)) /
+                    (double)outrate);
+                if (size == 1)
+                    *CHARP(ncp, 0) = (signed char)(cur_o >> 24);
+                else if (size == 2)
+                    *SHORTP(ncp, 0) = (short)(cur_o >> 16);
+                else if (size == 4)
+                    *LONGP(ncp, 0) = (Py_Int32)(cur_o);
+                ncp += size;
+            }
+            d -= inrate;
+        }
+    }
+}
+
+void tostereo(char* rv, char* cp, size_t len, int size,
+              double fac1, double fac2)
+{
+    int val1, val2, val = 0;
+    double fval, maxval, minval;
+    char *ncp = rv;
+    int i;
+
+    maxval = (double) maxvals[size];
+    minval = (double) minvals[size];
+
+    for ( i=0; i < len; i += size ) {
+        if ( size == 1 )      val = (int)*CHARP(cp, i);
+        else if ( size == 2 ) val = (int)*SHORTP(cp, i);
+        else if ( size == 4 ) val = (int)*LONGP(cp, i);
+
+        fval = (double)val*fac1;
+        val1 = (int)floor(fbound(fval, minval, maxval));
+
+        fval = (double)val*fac2;
+        val2 = (int)floor(fbound(fval, minval, maxval));
+
+        if ( size == 1 )      *CHARP(ncp, i*2) = (signed char)val1;
+        else if ( size == 2 ) *SHORTP(ncp, i*2) = (short)val1;
+        else if ( size == 4 ) *LONGP(ncp, i*2) = (Py_Int32)val1;
+
+        if ( size == 1 )      *CHARP(ncp, i*2+1) = (signed char)val2;
+        else if ( size == 2 ) *SHORTP(ncp, i*2+2) = (short)val2;
+        else if ( size == 4 ) *LONGP(ncp, i*2+4) = (Py_Int32)val2;
+    }
+}
+
+void add(char* rv, char* cp1, char* cp2, size_t len1, int size)
+{
+    int i;
+    int val1 = 0, val2 = 0, minval, maxval, newval;
+    char* ncp = rv;
+
+    maxval = maxvals[size];
+    minval = minvals[size];
+
+    for ( i=0; i < len1; i += size ) {
+        if ( size == 1 )      val1 = (int)*CHARP(cp1, i);
+        else if ( size == 2 ) val1 = (int)*SHORTP(cp1, i);
+        else if ( size == 4 ) val1 = (int)*LONGP(cp1, i);
+
+        if ( size == 1 )      val2 = (int)*CHARP(cp2, i);
+        else if ( size == 2 ) val2 = (int)*SHORTP(cp2, i);
+        else if ( size == 4 ) val2 = (int)*LONGP(cp2, i);
+
+        if (size < 4) {
+            newval = val1 + val2;
+            /* truncate in case of overflow */
+            if (newval > maxval)
+                newval = maxval;
+            else if (newval < minval)
+                newval = minval;
+        }
+        else {
+            double fval = (double)val1 + (double)val2;
+            /* truncate in case of overflow */
+            newval = (int)floor(fbound(fval, minval, maxval));
+        }
+
+        if ( size == 1 )      *CHARP(ncp, i) = (signed char)newval;
+        else if ( size == 2 ) *SHORTP(ncp, i) = (short)newval;
+        else if ( size == 4 ) *LONGP(ncp, i) = (Py_Int32)newval;
+    }
+}
+
 void lin2adcpm(unsigned char* ncp, unsigned char* cp, size_t len,
                size_t size, int* state)
 {


More information about the pypy-commit mailing list