diff -r 259c82332199 Doc/library/audioop.rst --- a/Doc/library/audioop.rst Sun Nov 17 16:08:23 2013 -0600 +++ b/Doc/library/audioop.rst Mon Nov 18 12:09:14 2013 +0200 @@ -77,6 +77,14 @@ sample. Samples wrap around in case of overflow. +.. function:: byteswap(fragment, width) + + "Byteswap" all samples in a fragment and returns the modified fragment. + Converts big-endian samples to little-endian and vice versa. + + .. versionadded: 3.4 + + .. function:: cross(fragment, width) Return the number of zero crossings in the fragment passed as an argument. diff -r 259c82332199 Lib/test/audiotests.py --- a/Lib/test/audiotests.py Sun Nov 17 16:08:23 2013 -0600 +++ b/Lib/test/audiotests.py Mon Nov 18 12:09:14 2013 +0200 @@ -5,21 +5,6 @@ import pickle import sys -def byteswap2(data): - a = array.array('h', data) - a.byteswap() - return a.tobytes() - -def byteswap3(data): - ba = bytearray(data) - ba[::3] = data[2::3] - ba[2::3] = data[::3] - return bytes(ba) - -def byteswap4(data): - a = array.array('i', data) - a.byteswap() - return a.tobytes() class UnseekableIO(io.FileIO): def tell(self): diff -r 259c82332199 Lib/test/test_aifc.py --- a/Lib/test/test_aifc.py Sun Nov 17 16:08:23 2013 -0600 +++ b/Lib/test/test_aifc.py Mon Nov 18 12:09:14 2013 +0200 @@ -1,6 +1,7 @@ from test.support import findfile, TESTFN, unlink import unittest from test import audiotests +from audioop import byteswap import os import io import sys @@ -122,7 +123,7 @@ E5040CBC 617C0A3C 08BC0A3C 2C7C0B3C 517C0E3C 8A8410FC B6840EBC 457C0A3C \ """) if sys.byteorder != 'big': - frames = audiotests.byteswap2(frames) + frames = byteswap(frames, 2) class AifcALAWTest(AifcTest, unittest.TestCase): @@ -143,7 +144,7 @@ E4800CC0 62000A40 08C00A40 2B000B40 52000E40 8A001180 B6000EC0 46000A40 \ """) if sys.byteorder != 'big': - frames = audiotests.byteswap2(frames) + frames = byteswap(frames, 2) class AifcMiscTest(audiotests.AudioTests, unittest.TestCase): diff -r 259c82332199 Lib/test/test_audioop.py --- a/Lib/test/test_audioop.py Sun Nov 17 16:08:23 2013 -0600 +++ b/Lib/test/test_audioop.py Mon Nov 18 12:09:14 2013 +0200 @@ -448,6 +448,23 @@ self.assertEqual(audioop.getsample(data, w, 3), maxvalues[w]) self.assertEqual(audioop.getsample(data, w, 4), minvalues[w]) + def test_byteswap(self): + swapped_datas = { + 1: b'\x00\x12\x45\xbb\x7f\x80\xff', + 2: packs[2](0, 0x3412, 0x6745, -0x6646, -0x81, 0x80, -1), + 3: packs[3](0, 0x563412, -0x7698bb, 0x7798ba, -0x81, 0x80, -1), + 4: packs[4](0, 0x78563412, -0x547698bb, 0x557698ba, + -0x81, 0x80, -1), + } + for w in 1, 2, 3, 4: + self.assertEqual(audioop.byteswap(b'', w), b'') + self.assertEqual(audioop.byteswap(datas[w], w), swapped_datas[w]) + self.assertEqual(audioop.byteswap(swapped_datas[w], w), datas[w]) + self.assertEqual(audioop.byteswap(bytearray(datas[w]), w), + swapped_datas[w]) + self.assertEqual(audioop.byteswap(memoryview(datas[w]), w), + swapped_datas[w]) + def test_negativelen(self): # from issue 3306, previously it segfaulted self.assertRaises(audioop.error, diff -r 259c82332199 Lib/test/test_sunau.py --- a/Lib/test/test_sunau.py Sun Nov 17 16:08:23 2013 -0600 +++ b/Lib/test/test_sunau.py Mon Nov 18 12:09:14 2013 +0200 @@ -1,6 +1,7 @@ from test.support import TESTFN import unittest from test import audiotests +from audioop import byteswap import sys import sunau @@ -124,7 +125,7 @@ E5040CBC 617C0A3C 08BC0A3C 2C7C0B3C 517C0E3C 8A8410FC B6840EBC 457C0A3C \ """) if sys.byteorder != 'big': - frames = audiotests.byteswap2(frames) + frames = byteswap(frames, 2) if __name__ == "__main__": diff -r 259c82332199 Lib/test/test_wave.py --- a/Lib/test/test_wave.py Sun Nov 17 16:08:23 2013 -0600 +++ b/Lib/test/test_wave.py Mon Nov 18 12:09:14 2013 +0200 @@ -1,6 +1,7 @@ from test.support import TESTFN import unittest from test import audiotests +from audioop import byteswap import sys import wave @@ -46,7 +47,7 @@ E4B50CEB 63440A5A 08CA0A1F 2BBA0B0B 51460E47 8BCB113C B6F50EEA 44150A59 \ """) if sys.byteorder != 'big': - frames = audiotests.byteswap2(frames) + frames = byteswap(frames, 2) class WavePCM24Test(audiotests.AudioWriteTests, @@ -76,7 +77,7 @@ 51486F0E44E1 8BCC64113B05 B6F4EC0EEB36 4413170A5B48 \ """) if sys.byteorder != 'big': - frames = audiotests.byteswap3(frames) + frames = byteswap(frames, 3) class WavePCM32Test(audiotests.AudioWriteTests, @@ -106,7 +107,7 @@ 51486F800E44E190 8BCC6480113B0580 B6F4EC000EEB3630 441317800A5B48A0 \ """) if sys.byteorder != 'big': - frames = audiotests.byteswap4(frames) + frames = byteswap(frames, 4) if __name__ == '__main__': diff -r 259c82332199 Lib/wave.py --- a/Lib/wave.py Sun Nov 17 16:08:23 2013 -0600 +++ b/Lib/wave.py Mon Nov 18 12:09:14 2013 +0200 @@ -87,12 +87,6 @@ from chunk import Chunk from collections import namedtuple -def _byteswap3(data): - ba = bytearray(data) - ba[::3] = data[2::3] - ba[2::3] = data[::3] - return bytes(ba) - _wave_params = namedtuple('_wave_params', 'nchannels sampwidth framerate nframes comptype compname') @@ -243,29 +237,10 @@ self._data_seek_needed = 0 if nframes == 0: return b'' - if self._sampwidth in (2, 4) and sys.byteorder == 'big': - # unfortunately the fromfile() method does not take - # something that only looks like a file object, so - # we have to reach into the innards of the chunk object - import array - chunk = self._data_chunk - data = array.array(_array_fmts[self._sampwidth]) - assert data.itemsize == self._sampwidth - nitems = nframes * self._nchannels - if nitems * self._sampwidth > chunk.chunksize - chunk.size_read: - nitems = (chunk.chunksize - chunk.size_read) // self._sampwidth - data.fromfile(chunk.file.file, nitems) - # "tell" data chunk how much was read - chunk.size_read = chunk.size_read + nitems * self._sampwidth - # do the same for the outermost chunk - chunk = chunk.file - chunk.size_read = chunk.size_read + nitems * self._sampwidth - data.byteswap() - data = data.tobytes() - else: - data = self._data_chunk.read(nframes * self._framesize) - if self._sampwidth == 3 and sys.byteorder == 'big': - data = _byteswap3(data) + data = self._data_chunk.read(nframes * self._framesize) + if self._sampwidth != 1 and sys.byteorder == 'big': + import audioop + data = audioop.byteswap(data, self._sampwidth) if self._convert and data: data = self._convert(data) self._soundpos = self._soundpos + len(data) // (self._nchannels * self._sampwidth) @@ -441,18 +416,11 @@ nframes = len(data) // (self._sampwidth * self._nchannels) if self._convert: data = self._convert(data) - if self._sampwidth in (2, 4) and sys.byteorder == 'big': - import array - data = array.array(_array_fmts[self._sampwidth], data) - assert data.itemsize == self._sampwidth - data.byteswap() - data.tofile(self._file) - self._datawritten = self._datawritten + len(data) * self._sampwidth - else: - if self._sampwidth == 3 and sys.byteorder == 'big': - data = _byteswap3(data) - self._file.write(data) - self._datawritten = self._datawritten + len(data) + if self._sampwidth != 1 and sys.byteorder == 'big': + import audioop + data = audioop.byteswap(data, self._sampwidth) + self._file.write(data) + self._datawritten = self._datawritten + len(data) self._nframeswritten = self._nframeswritten + nframes def writeframes(self, data): diff -r 259c82332199 Modules/audioop.c --- a/Modules/audioop.c Sun Nov 17 16:08:23 2013 -0600 +++ b/Modules/audioop.c Mon Nov 18 12:09:14 2013 +0200 @@ -1108,6 +1108,42 @@ } static PyObject * +audioop_byteswap(PyObject *self, PyObject *args) +{ + Py_buffer view; + unsigned char *ncp; + Py_ssize_t i; + int size; + PyObject *rv = NULL; + + if (!PyArg_ParseTuple(args, "y*i:swapbytes", + &view, &size)) + return NULL; + + if (!audioop_check_parameters(view.len, size)) + goto exit; + + if (size == 1) { + rv = PyBytes_FromStringAndSize(view.buf, view.len); + goto exit; + } + + rv = PyBytes_FromStringAndSize(NULL, view.len); + if (rv == NULL) + goto exit; + ncp = (unsigned char *)PyBytes_AsString(rv); + + for (i = 0; i < view.len; i += size) { + int j; + for (j = 0; j < size; j++) + ncp[i + size - 1 - j] = ((unsigned char *)view.buf)[i + j]; + } + exit: + PyBuffer_Release(&view); + return rv; +} + +static PyObject * audioop_lin2lin(PyObject *self, PyObject *args) { Py_buffer view; @@ -1698,6 +1734,7 @@ { "tostereo", audioop_tostereo, METH_VARARGS }, { "getsample", audioop_getsample, METH_VARARGS }, { "reverse", audioop_reverse, METH_VARARGS }, + { "byteswap", audioop_byteswap, METH_VARARGS }, { "ratecv", audioop_ratecv, METH_VARARGS }, { 0, 0 } };