Issue #16686: Fixed a lot of bugs in audioop module.

* avgpp() and maxpp() no more crash on empty and 1-samples input fragment. They now work when peak-peak values are greater INT_MAX.
* ratecv() no more crashes on empty input fragment.
* Fixed an integer overflow in ratecv().
* Fixed an integer overflow in add() and bias() for 32-bit samples.
* reverse(), lin2lin() and ratecv() no more lose precision for 32-bit samples.
* max() and rms() no more returns negative result for 32-bit sample -0x80000000.
* minmax() now returns correct max value for 32-bit sample -0x80000000.
* avg(), mul(), tomono() and tostereo() now round negative result down and can return 32-bit sample -0x80000000.
* add() now can return 32-bit sample -0x80000000.
This commit is contained in:
Serhiy Storchaka 2013-02-09 11:10:53 +02:00
parent a48b61f8f2
commit 01ad622a2c
4 changed files with 435 additions and 286 deletions

View file

@ -26,6 +26,21 @@ typedef short PyInt16;
#endif
#endif
static const int maxvals[] = {0, 0x7F, 0x7FFF, 0x7FFFFF, 0x7FFFFFFF};
static const int minvals[] = {0, -0x80, -0x8000, -0x800000, -0x80000000};
static const unsigned int masks[] = {0, 0xFF, 0xFFFF, 0xFFFFFF, 0xFFFFFFFF};
static int
fbound(double val, double minval, double maxval)
{
if (val > maxval)
val = maxval;
else if (val < minval + 1)
val = minval;
return val;
}
/* Code shamelessly stolen from sox, 12.17.7, g711.c
** (c) Craig Reese, Joe Campbell and Jeff Poskanzer 1989 */
@ -347,7 +362,7 @@ audioop_max(PyObject *self, PyObject *args)
signed char *cp;
Py_ssize_t len, i;
int size, val = 0;
int max = 0;
unsigned int absval, max = 0;
if ( !PyArg_ParseTuple(args, "s#i:max", &cp, &len, &size) )
return 0;
@ -357,10 +372,11 @@ audioop_max(PyObject *self, PyObject *args)
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);
if ( val < 0 ) val = (-val);
if ( val > max ) max = val;
if (val < 0) absval = (-val);
else absval = val;
if (absval > max) max = absval;
}
return PyLong_FromLong(max);
return PyLong_FromUnsignedLong(max);
}
static PyObject *
@ -369,7 +385,7 @@ audioop_minmax(PyObject *self, PyObject *args)
signed char *cp;
Py_ssize_t len, i;
int size, val = 0;
int min = 0x7fffffff, max = -0x7fffffff;
int min = 0x7fffffff, max = -0x80000000;
if (!PyArg_ParseTuple(args, "s#i:minmax", &cp, &len, &size))
return NULL;
@ -406,7 +422,7 @@ audioop_avg(PyObject *self, PyObject *args)
if ( len == 0 )
val = 0;
else
val = (int)(avg / (double)(len/size));
val = (int)floor(avg / (double)(len/size));
return PyLong_FromLong(val);
}
@ -416,6 +432,7 @@ audioop_rms(PyObject *self, PyObject *args)
signed char *cp;
Py_ssize_t len, i;
int size, val = 0;
unsigned int res;
double sum_squares = 0.0;
if ( !PyArg_ParseTuple(args, "s#i:rms", &cp, &len, &size) )
@ -429,10 +446,10 @@ audioop_rms(PyObject *self, PyObject *args)
sum_squares += (double)val*(double)val;
}
if ( len == 0 )
val = 0;
res = 0;
else
val = (int)sqrt(sum_squares / (double)(len/size));
return PyLong_FromLong(val);
res = (unsigned int)sqrt(sum_squares / (double)(len/size));
return PyLong_FromUnsignedLong(res);
}
static double _sum2(short *a, short *b, Py_ssize_t len)
@ -624,52 +641,46 @@ audioop_avgpp(PyObject *self, PyObject *args)
Py_ssize_t len, i;
int size, val = 0, prevval = 0, prevextremevalid = 0,
prevextreme = 0;
double avg = 0.0;
int diff, prevdiff, extremediff, nextreme = 0;
double sum = 0.0;
unsigned int avg;
int diff, prevdiff, nextreme = 0;
if ( !PyArg_ParseTuple(args, "s#i:avgpp", &cp, &len, &size) )
return 0;
if (!audioop_check_parameters(len, size))
return NULL;
/* Compute first delta value ahead. Also automatically makes us
** skip the first extreme value
*/
if (len <= size)
return PyLong_FromLong(0);
if ( size == 1 ) prevval = (int)*CHARP(cp, 0);
else if ( size == 2 ) prevval = (int)*SHORTP(cp, 0);
else if ( size == 4 ) prevval = (int)*LONGP(cp, 0);
if ( size == 1 ) val = (int)*CHARP(cp, size);
else if ( size == 2 ) val = (int)*SHORTP(cp, size);
else if ( size == 4 ) val = (int)*LONGP(cp, size);
prevdiff = val - prevval;
prevdiff = 17; /* Anything != 0, 1 */
for ( i=size; 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);
diff = val - prevval;
if ( diff*prevdiff < 0 ) {
/* Derivative changed sign. Compute difference to last
** extreme value and remember.
*/
if ( prevextremevalid ) {
extremediff = prevval - prevextreme;
if ( extremediff < 0 )
extremediff = -extremediff;
avg += extremediff;
nextreme++;
if (val != prevval) {
diff = val < prevval;
if (prevdiff == !diff) {
/* Derivative changed sign. Compute difference to last
** extreme value and remember.
*/
if (prevextremevalid) {
sum += fabs((double)prevval - (double)prevextreme);
nextreme++;
}
prevextremevalid = 1;
prevextreme = prevval;
}
prevextremevalid = 1;
prevextreme = prevval;
}
prevval = val;
if ( diff != 0 )
prevval = val;
prevdiff = diff;
}
}
if ( nextreme == 0 )
val = 0;
avg = 0;
else
val = (int)(avg / (double)nextreme);
return PyLong_FromLong(val);
avg = (unsigned int)(sum / (double)nextreme);
return PyLong_FromUnsignedLong(avg);
}
static PyObject *
@ -679,48 +690,47 @@ audioop_maxpp(PyObject *self, PyObject *args)
Py_ssize_t len, i;
int size, val = 0, prevval = 0, prevextremevalid = 0,
prevextreme = 0;
int max = 0;
int diff, prevdiff, extremediff;
unsigned int max = 0, extremediff;
int diff, prevdiff;
if ( !PyArg_ParseTuple(args, "s#i:maxpp", &cp, &len, &size) )
return 0;
if (!audioop_check_parameters(len, size))
return NULL;
/* Compute first delta value ahead. Also automatically makes us
** skip the first extreme value
*/
if (len <= size)
return PyLong_FromLong(0);
if ( size == 1 ) prevval = (int)*CHARP(cp, 0);
else if ( size == 2 ) prevval = (int)*SHORTP(cp, 0);
else if ( size == 4 ) prevval = (int)*LONGP(cp, 0);
if ( size == 1 ) val = (int)*CHARP(cp, size);
else if ( size == 2 ) val = (int)*SHORTP(cp, size);
else if ( size == 4 ) val = (int)*LONGP(cp, size);
prevdiff = val - prevval;
prevdiff = 17; /* Anything != 0, 1 */
for ( i=size; 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);
diff = val - prevval;
if ( diff*prevdiff < 0 ) {
/* Derivative changed sign. Compute difference to
** last extreme value and remember.
*/
if ( prevextremevalid ) {
extremediff = prevval - prevextreme;
if ( extremediff < 0 )
extremediff = -extremediff;
if ( extremediff > max )
max = extremediff;
if (val != prevval) {
diff = val < prevval;
if (prevdiff == !diff) {
/* Derivative changed sign. Compute difference to
** last extreme value and remember.
*/
if (prevextremevalid) {
if (prevval < prevextreme)
extremediff = (unsigned int)prevextreme -
(unsigned int)prevval;
else
extremediff = (unsigned int)prevval -
(unsigned int)prevextreme;
if ( extremediff > max )
max = extremediff;
}
prevextremevalid = 1;
prevextreme = prevval;
}
prevextremevalid = 1;
prevextreme = prevval;
}
prevval = val;
if ( diff != 0 )
prevval = val;
prevdiff = diff;
}
}
return PyLong_FromLong(max);
return PyLong_FromUnsignedLong(max);
}
static PyObject *
@ -755,7 +765,7 @@ audioop_mul(PyObject *self, PyObject *args)
signed char *cp, *ncp;
Py_ssize_t len, i;
int size, val = 0;
double factor, fval, maxval;
double factor, fval, maxval, minval;
PyObject *rv;
if ( !PyArg_ParseTuple(args, "s#id:mul", &cp, &len, &size, &factor ) )
@ -763,13 +773,8 @@ audioop_mul(PyObject *self, PyObject *args)
if (!audioop_check_parameters(len, size))
return NULL;
if ( size == 1 ) maxval = (double) 0x7f;
else if ( size == 2 ) maxval = (double) 0x7fff;
else if ( size == 4 ) maxval = (double) 0x7fffffff;
else {
PyErr_SetString(AudioopError, "Size should be 1, 2 or 4");
return 0;
}
maxval = (double) maxvals[size];
minval = (double) minvals[size];
rv = PyBytes_FromStringAndSize(NULL, len);
if ( rv == 0 )
@ -782,9 +787,7 @@ audioop_mul(PyObject *self, PyObject *args)
else if ( size == 2 ) val = (int)*SHORTP(cp, i);
else if ( size == 4 ) val = (int)*LONGP(cp, i);
fval = (double)val*factor;
if ( fval > maxval ) fval = maxval;
else if ( fval < -maxval ) fval = -maxval;
val = (int)fval;
val = (int)floor(fbound(fval, minval, maxval));
if ( size == 1 ) *CHARP(ncp, i) = (signed char)val;
else if ( size == 2 ) *SHORTP(ncp, i) = (short)val;
else if ( size == 4 ) *LONGP(ncp, i) = (Py_Int32)val;
@ -799,7 +802,7 @@ audioop_tomono(PyObject *self, PyObject *args)
signed char *cp, *ncp;
Py_ssize_t len, i;
int size, val1 = 0, val2 = 0;
double fac1, fac2, fval, maxval;
double fac1, fac2, fval, maxval, minval;
PyObject *rv;
if ( !PyArg_ParseTuple(args, "s*idd:tomono",
@ -817,14 +820,8 @@ audioop_tomono(PyObject *self, PyObject *args)
return NULL;
}
if ( size == 1 ) maxval = (double) 0x7f;
else if ( size == 2 ) maxval = (double) 0x7fff;
else if ( size == 4 ) maxval = (double) 0x7fffffff;
else {
PyBuffer_Release(&pcp);
PyErr_SetString(AudioopError, "Size should be 1, 2 or 4");
return 0;
}
maxval = (double) maxvals[size];
minval = (double) minvals[size];
rv = PyBytes_FromStringAndSize(NULL, len/2);
if ( rv == 0 ) {
@ -842,9 +839,7 @@ audioop_tomono(PyObject *self, PyObject *args)
else if ( size == 2 ) val2 = (int)*SHORTP(cp, i+2);
else if ( size == 4 ) val2 = (int)*LONGP(cp, i+4);
fval = (double)val1*fac1 + (double)val2*fac2;
if ( fval > maxval ) fval = maxval;
else if ( fval < -maxval ) fval = -maxval;
val1 = (int)fval;
val1 = (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;
@ -859,7 +854,7 @@ audioop_tostereo(PyObject *self, PyObject *args)
signed char *cp, *ncp;
Py_ssize_t len, i;
int size, val1, val2, val = 0;
double fac1, fac2, fval, maxval;
double fac1, fac2, fval, maxval, minval;
PyObject *rv;
if ( !PyArg_ParseTuple(args, "s#idd:tostereo",
@ -868,13 +863,8 @@ audioop_tostereo(PyObject *self, PyObject *args)
if (!audioop_check_parameters(len, size))
return NULL;
if ( size == 1 ) maxval = (double) 0x7f;
else if ( size == 2 ) maxval = (double) 0x7fff;
else if ( size == 4 ) maxval = (double) 0x7fffffff;
else {
PyErr_SetString(AudioopError, "Size should be 1, 2 or 4");
return 0;
}
maxval = (double) maxvals[size];
minval = (double) minvals[size];
if (len > PY_SSIZE_T_MAX/2) {
PyErr_SetString(PyExc_MemoryError,
@ -894,14 +884,10 @@ audioop_tostereo(PyObject *self, PyObject *args)
else if ( size == 4 ) val = (int)*LONGP(cp, i);
fval = (double)val*fac1;
if ( fval > maxval ) fval = maxval;
else if ( fval < -maxval ) fval = -maxval;
val1 = (int)fval;
val1 = (int)floor(fbound(fval, minval, maxval));
fval = (double)val*fac2;
if ( fval > maxval ) fval = maxval;
else if ( fval < -maxval ) fval = -maxval;
val2 = (int)fval;
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;
@ -919,7 +905,7 @@ audioop_add(PyObject *self, PyObject *args)
{
signed char *cp1, *cp2, *ncp;
Py_ssize_t len1, len2, i;
int size, val1 = 0, val2 = 0, maxval, newval;
int size, val1 = 0, val2 = 0, minval, maxval, newval;
PyObject *rv;
if ( !PyArg_ParseTuple(args, "s#s#i:add",
@ -932,13 +918,8 @@ audioop_add(PyObject *self, PyObject *args)
return 0;
}
if ( size == 1 ) maxval = 0x7f;
else if ( size == 2 ) maxval = 0x7fff;
else if ( size == 4 ) maxval = 0x7fffffff;
else {
PyErr_SetString(AudioopError, "Size should be 1, 2 or 4");
return 0;
}
maxval = maxvals[size];
minval = minvals[size];
rv = PyBytes_FromStringAndSize(NULL, len1);
if ( rv == 0 )
@ -954,12 +935,19 @@ audioop_add(PyObject *self, PyObject *args)
else if ( size == 2 ) val2 = (int)*SHORTP(cp2, i);
else if ( size == 4 ) val2 = (int)*LONGP(cp2, i);
newval = val1 + val2;
/* truncate in case of overflow */
if (newval > maxval) newval = maxval;
else if (newval < -maxval) newval = -maxval;
else if (size == 4 && (newval^val1) < 0 && (newval^val2) < 0)
newval = val1 > 0 ? maxval : - maxval;
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;
@ -973,9 +961,9 @@ audioop_bias(PyObject *self, PyObject *args)
{
signed char *cp, *ncp;
Py_ssize_t len, i;
int size, val = 0;
int size, bias;
unsigned int val = 0, mask;
PyObject *rv;
int bias;
if ( !PyArg_ParseTuple(args, "s#ii:bias",
&cp, &len, &size , &bias) )
@ -989,15 +977,20 @@ audioop_bias(PyObject *self, PyObject *args)
return 0;
ncp = (signed char *)PyBytes_AsString(rv);
mask = masks[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);
if ( size == 1 ) val = (unsigned int)(unsigned char)*CHARP(cp, i);
else if ( size == 2 ) val = (unsigned int)(unsigned short)*SHORTP(cp, i);
else if ( size == 4 ) val = (unsigned int)(Py_UInt32)*LONGP(cp, i);
if ( size == 1 ) *CHARP(ncp, i) = (signed char)(val+bias);
else if ( size == 2 ) *SHORTP(ncp, i) = (short)(val+bias);
else if ( size == 4 ) *LONGP(ncp, i) = (Py_Int32)(val+bias);
val += (unsigned int)bias;
/* wrap around in case of overflow */
val &= mask;
if ( size == 1 ) *CHARP(ncp, i) = (signed char)(unsigned char)val;
else if ( size == 2 ) *SHORTP(ncp, i) = (short)(unsigned short)val;
else if ( size == 4 ) *LONGP(ncp, i) = (Py_Int32)(Py_UInt32)val;
}
return rv;
}
@ -1024,15 +1017,15 @@ audioop_reverse(PyObject *self, PyObject *args)
ncp = (unsigned char *)PyBytes_AsString(rv);
for ( i=0; i < len; i += size ) {
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 ( size == 1 ) val = ((int)*CHARP(cp, i)) << 24;
else if ( size == 2 ) val = ((int)*SHORTP(cp, i)) << 16;
else if ( size == 4 ) val = (int)*LONGP(cp, i);
j = len - i - size;
if ( size == 1 ) *CHARP(ncp, j) = (signed char)(val >> 8);
else if ( size == 2 ) *SHORTP(ncp, j) = (short)(val);
else if ( size == 4 ) *LONGP(ncp, j) = (Py_Int32)(val<<16);
if ( size == 1 ) *CHARP(ncp, j) = (signed char)(val >> 24);
else if ( size == 2 ) *SHORTP(ncp, j) = (short)(val >> 16);
else if ( size == 4 ) *LONGP(ncp, j) = (Py_Int32)val;
}
return rv;
}
@ -1066,13 +1059,13 @@ audioop_lin2lin(PyObject *self, PyObject *args)
ncp = (unsigned char *)PyBytes_AsString(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 ( size == 1 ) val = ((int)*CHARP(cp, i)) << 24;
else if ( size == 2 ) val = ((int)*SHORTP(cp, i)) << 16;
else if ( size == 4 ) val = (int)*LONGP(cp, i);
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) = (Py_Int32)(val<<16);
if ( size2 == 1 ) *CHARP(ncp, j) = (signed char)(val >> 24);
else if ( size2 == 2 ) *SHORTP(ncp, j) = (short)(val >> 16);
else if ( size2 == 4 ) *LONGP(ncp, j) = (Py_Int32)val;
}
return rv;
}
@ -1136,6 +1129,10 @@ audioop_ratecv(PyObject *self, PyObject *args)
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;
if ((size_t)nchannels > PY_SIZE_MAX/sizeof(int)) {
PyErr_SetString(PyExc_MemoryError,
@ -1175,7 +1172,9 @@ audioop_ratecv(PyObject *self, PyObject *args)
}
/* str <- Space for the output buffer. */
{
if (len == 0)
str = PyBytes_FromStringAndSize(NULL, 0);
else {
/* There are len input frames, so we need (mathematically)
ceiling(len*outrate/inrate) output frames, and each frame
requires bytes_per_frame bytes. Computing this
@ -1190,12 +1189,11 @@ audioop_ratecv(PyObject *self, PyObject *args)
else
str = PyBytes_FromStringAndSize(NULL,
q * outrate * bytes_per_frame);
if (str == NULL) {
PyErr_SetString(PyExc_MemoryError,
"not enough memory for output buffer");
goto exit;
}
}
if (str == NULL) {
PyErr_SetString(PyExc_MemoryError,
"not enough memory for output buffer");
goto exit;
}
ncp = PyBytes_AsString(str);
@ -1229,32 +1227,32 @@ audioop_ratecv(PyObject *self, PyObject *args)
for (chan = 0; chan < nchannels; chan++) {
prev_i[chan] = cur_i[chan];
if (size == 1)
cur_i[chan] = ((int)*CHARP(cp, 0)) << 8;
cur_i[chan] = ((int)*CHARP(cp, 0)) << 24;
else if (size == 2)
cur_i[chan] = (int)*SHORTP(cp, 0);
cur_i[chan] = ((int)*SHORTP(cp, 0)) << 16;
else if (size == 4)
cur_i[chan] = ((int)*LONGP(cp, 0)) >> 16;
cur_i[chan] = (int)*LONGP(cp, 0);
cp += size;
/* implements a simple digital filter */
cur_i[chan] =
(weightA * cur_i[chan] +
weightB * prev_i[chan]) /
(weightA + weightB);
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++) {
cur_o = (prev_i[chan] * d +
cur_i[chan] * (outrate - d)) /
outrate;
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 >> 8);
*CHARP(ncp, 0) = (signed char)(cur_o >> 24);
else if (size == 2)
*SHORTP(ncp, 0) = (short)(cur_o);
*SHORTP(ncp, 0) = (short)(cur_o >> 16);
else if (size == 4)
*LONGP(ncp, 0) = (Py_Int32)(cur_o<<16);
*LONGP(ncp, 0) = (Py_Int32)(cur_o);
ncp += size;
}
d -= inrate;