]> granicus.if.org Git - python/commitdiff
Issue #19641: Added the audioop.byteswap() function to convert big-endian
authorSerhiy Storchaka <storchaka@gmail.com>
Sat, 23 Nov 2013 20:26:01 +0000 (22:26 +0200)
committerSerhiy Storchaka <storchaka@gmail.com>
Sat, 23 Nov 2013 20:26:01 +0000 (22:26 +0200)
samples to little-endian and vice versa.

Doc/library/audioop.rst
Doc/whatsnew/3.4.rst
Lib/test/audiotests.py
Lib/test/test_aifc.py
Lib/test/test_audioop.py
Lib/test/test_sunau.py
Lib/test/test_wave.py
Lib/wave.py
Misc/NEWS
Modules/audioop.c

index ca6cfb3df82773f8a2222987bf715e2d272622c4..fbb7fc629442827cdb5035557fc33e55df10f4ef 100644 (file)
@@ -77,6 +77,14 @@ The module defines the following variables and functions:
    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.
index 6f949a997d5d55d7bf2dbb46b9a8e82c01bad3ff..31f7cbd11d977a75b0cbe48faa0868497e6bee65 100644 (file)
@@ -415,6 +415,9 @@ audioop
 
 Added support for 24-bit samples (:issue:`12866`).
 
+Added the :func:`~audioop.byteswap` function to convert big-endian samples
+to little-endian and vice versa (:issue:`19641`).
+
 
 base64
 ------
index 7581fe2ed458813a29f3266bba80adb328a0d562..b7497ce5b06d27ac5356e0d551182dbadb37f26a 100644 (file)
@@ -5,24 +5,6 @@ import io
 import pickle
 import sys
 
-def byteswap2(data):
-    a = array.array('h')
-    a.frombytes(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')
-    a.frombytes(data)
-    a.byteswap()
-    return a.tobytes()
-
 class UnseekableIO(io.FileIO):
     def tell(self):
         raise io.UnsupportedOperation
index b4270bc655f68ce938a79f06d2f3fd630ddcd870..041b23688c66bfbc51156526ed58c87fc652028f 100644 (file)
@@ -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 @@ class AifcULAWTest(AifcTest, unittest.TestCase):
       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 @@ class AifcALAWTest(AifcTest, unittest.TestCase):
       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):
index fe96b75dfaa9c667ea41a6bc79f0e58d71665b6b..d5075450e63758b86f3038e3fd1c9e528a05b6e5 100644 (file)
@@ -448,6 +448,23 @@ class TestAudioop(unittest.TestCase):
             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: datas[1],
+            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,
index 81acd964c220b272b327799780bdbabc73056ae6..af9ffec622de33c0603d7822b447443187d9e685 100644 (file)
@@ -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 @@ class SunauULAWTest(audiotests.AudioWriteTests,
       E5040CBC 617C0A3C 08BC0A3C 2C7C0B3C 517C0E3C 8A8410FC B6840EBC 457C0A3C \
       """)
     if sys.byteorder != 'big':
-        frames = audiotests.byteswap2(frames)
+        frames = byteswap(frames, 2)
 
 
 if __name__ == "__main__":
index 5be12519d9e4bb5d9895e4ee28565cb719d5f509..549ca89615166685d140a3db82ec83c01ebe8838 100644 (file)
@@ -1,6 +1,7 @@
 from test.support import TESTFN
 import unittest
 from test import audiotests
+from audioop import byteswap
 import sys
 import wave
 
@@ -46,13 +47,7 @@ class WavePCM16Test(audiotests.AudioWriteTests,
       E4B50CEB 63440A5A 08CA0A1F 2BBA0B0B 51460E47 8BCB113C B6F50EEA 44150A59 \
       """)
     if sys.byteorder != 'big':
-        frames = audiotests.byteswap2(frames)
-
-    if sys.byteorder == 'big':
-        @unittest.expectedFailure
-        def test_unseekable_incompleted_write(self):
-            super().test_unseekable_incompleted_write()
-
+        frames = byteswap(frames, 2)
 
 
 class WavePCM24Test(audiotests.AudioWriteTests,
@@ -82,7 +77,7 @@ class WavePCM24Test(audiotests.AudioWriteTests,
       51486F0E44E1 8BCC64113B05 B6F4EC0EEB36 4413170A5B48 \
       """)
     if sys.byteorder != 'big':
-        frames = audiotests.byteswap3(frames)
+        frames = byteswap(frames, 3)
 
 
 class WavePCM32Test(audiotests.AudioWriteTests,
@@ -112,12 +107,7 @@ class WavePCM32Test(audiotests.AudioWriteTests,
       51486F800E44E190 8BCC6480113B0580 B6F4EC000EEB3630 441317800A5B48A0 \
       """)
     if sys.byteorder != 'big':
-        frames = audiotests.byteswap4(frames)
-
-    if sys.byteorder == 'big':
-        @unittest.expectedFailure
-        def test_unseekable_incompleted_write(self):
-            super().test_unseekable_incompleted_write()
+        frames = byteswap(frames, 4)
 
 
 if __name__ == '__main__':
index 672d04b8c9b7646e313236ed5a37d1da2514965b..b56395ead700732925ea538a56352ad3d0b44694 100644 (file)
@@ -82,17 +82,12 @@ WAVE_FORMAT_PCM = 0x0001
 
 _array_fmts = None, 'b', 'h', None, 'i'
 
+import audioop
 import struct
 import sys
 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 +238,9 @@ class Wave_read:
             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':
+            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,20 +416,10 @@ class Wave_write:
         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
-            a = array.array(_array_fmts[self._sampwidth])
-            a.frombytes(data)
-            data = a
-            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':
+            data = audioop.byteswap(data, self._sampwidth)
+        self._file.write(data)
+        self._datawritten += len(data)
         self._nframeswritten = self._nframeswritten + nframes
 
     def writeframes(self, data):
index 5185efef3f8f64072c2baf023e8984709a0b16a7..f6823b431c970235f43a8c5033cd838d5d1efd2f 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -68,6 +68,9 @@ Core and Builtins
 Library
 -------
 
+- Issue #19641: Added the audioop.byteswap() function to convert big-endian
+  samples to little-endian and vice versa.
+
 - Issue #15204: Deprecated the 'U' mode in file-like objects.
 
 - Issue #17810: Implement PEP 3154, pickle protocol 4.
index 5c83a7d6b0abec40b9b99ce35e265af0a9e08e09..ae3ff060b4742ca3cd298ed7c1bcd935c1ac477c 100644 (file)
@@ -1107,6 +1107,37 @@ audioop_reverse(PyObject *self, PyObject *args)
     return rv;
 }
 
+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;
+
+    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)
 {
@@ -1698,6 +1729,7 @@ static PyMethodDef audioop_methods[] = {
     { "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 }
 };