mirror of
https://github.com/python/cpython.git
synced 2025-08-03 16:39:00 +00:00
gh-104773: PEP 594: Remove the sunau module (#104863)
* Remove Lib/test/audiodata/pluck-*.au files. * Remove Lib/test/audiotest.au file.
This commit is contained in:
parent
8e5b3b90c8
commit
a5e0f5b230
21 changed files with 16 additions and 981 deletions
|
@ -1,274 +0,0 @@
|
|||
:mod:`sunau` --- Read and write Sun AU files
|
||||
============================================
|
||||
|
||||
.. module:: sunau
|
||||
:synopsis: Provide an interface to the Sun AU sound format.
|
||||
:deprecated:
|
||||
|
||||
.. sectionauthor:: Moshe Zadka <moshez@zadka.site.co.il>
|
||||
|
||||
**Source code:** :source:`Lib/sunau.py`
|
||||
|
||||
.. deprecated-removed:: 3.11 3.13
|
||||
The :mod:`sunau` module is deprecated
|
||||
(see :pep:`PEP 594 <594#sunau>` for details).
|
||||
|
||||
--------------
|
||||
|
||||
The :mod:`sunau` module provides a convenient interface to the Sun AU sound
|
||||
format. Note that this module is interface-compatible with the modules
|
||||
:mod:`aifc` and :mod:`wave`.
|
||||
|
||||
An audio file consists of a header followed by the data. The fields of the
|
||||
header are:
|
||||
|
||||
+---------------+-----------------------------------------------+
|
||||
| Field | Contents |
|
||||
+===============+===============================================+
|
||||
| magic word | The four bytes ``.snd``. |
|
||||
+---------------+-----------------------------------------------+
|
||||
| header size | Size of the header, including info, in bytes. |
|
||||
+---------------+-----------------------------------------------+
|
||||
| data size | Physical size of the data, in bytes. |
|
||||
+---------------+-----------------------------------------------+
|
||||
| encoding | Indicates how the audio samples are encoded. |
|
||||
+---------------+-----------------------------------------------+
|
||||
| sample rate | The sampling rate. |
|
||||
+---------------+-----------------------------------------------+
|
||||
| # of channels | The number of channels in the samples. |
|
||||
+---------------+-----------------------------------------------+
|
||||
| info | ASCII string giving a description of the |
|
||||
| | audio file (padded with null bytes). |
|
||||
+---------------+-----------------------------------------------+
|
||||
|
||||
Apart from the info field, all header fields are 4 bytes in size. They are all
|
||||
32-bit unsigned integers encoded in big-endian byte order.
|
||||
|
||||
The :mod:`sunau` module defines the following functions:
|
||||
|
||||
|
||||
.. function:: open(file, mode)
|
||||
|
||||
If *file* is a string, open the file by that name, otherwise treat it as a
|
||||
seekable file-like object. *mode* can be any of
|
||||
|
||||
``'r'``
|
||||
Read only mode.
|
||||
|
||||
``'w'``
|
||||
Write only mode.
|
||||
|
||||
Note that it does not allow read/write files.
|
||||
|
||||
A *mode* of ``'r'`` returns an :class:`AU_read` object, while a *mode* of ``'w'``
|
||||
or ``'wb'`` returns an :class:`AU_write` object.
|
||||
|
||||
|
||||
The :mod:`sunau` module defines the following exception:
|
||||
|
||||
.. exception:: Error
|
||||
|
||||
An error raised when something is impossible because of Sun AU specs or
|
||||
implementation deficiency.
|
||||
|
||||
|
||||
The :mod:`sunau` module defines the following data items:
|
||||
|
||||
.. data:: AUDIO_FILE_MAGIC
|
||||
|
||||
An integer every valid Sun AU file begins with, stored in big-endian form. This
|
||||
is the string ``.snd`` interpreted as an integer.
|
||||
|
||||
|
||||
.. data:: AUDIO_FILE_ENCODING_MULAW_8
|
||||
AUDIO_FILE_ENCODING_LINEAR_8
|
||||
AUDIO_FILE_ENCODING_LINEAR_16
|
||||
AUDIO_FILE_ENCODING_LINEAR_24
|
||||
AUDIO_FILE_ENCODING_LINEAR_32
|
||||
AUDIO_FILE_ENCODING_ALAW_8
|
||||
|
||||
Values of the encoding field from the AU header which are supported by this
|
||||
module.
|
||||
|
||||
|
||||
.. data:: AUDIO_FILE_ENCODING_FLOAT
|
||||
AUDIO_FILE_ENCODING_DOUBLE
|
||||
AUDIO_FILE_ENCODING_ADPCM_G721
|
||||
AUDIO_FILE_ENCODING_ADPCM_G722
|
||||
AUDIO_FILE_ENCODING_ADPCM_G723_3
|
||||
AUDIO_FILE_ENCODING_ADPCM_G723_5
|
||||
|
||||
Additional known values of the encoding field from the AU header, but which are
|
||||
not supported by this module.
|
||||
|
||||
|
||||
.. _au-read-objects:
|
||||
|
||||
AU_read Objects
|
||||
---------------
|
||||
|
||||
AU_read objects, as returned by :func:`.open` above, have the following methods:
|
||||
|
||||
|
||||
.. method:: AU_read.close()
|
||||
|
||||
Close the stream, and make the instance unusable. (This is called automatically
|
||||
on deletion.)
|
||||
|
||||
|
||||
.. method:: AU_read.getnchannels()
|
||||
|
||||
Returns number of audio channels (1 for mono, 2 for stereo).
|
||||
|
||||
|
||||
.. method:: AU_read.getsampwidth()
|
||||
|
||||
Returns sample width in bytes.
|
||||
|
||||
|
||||
.. method:: AU_read.getframerate()
|
||||
|
||||
Returns sampling frequency.
|
||||
|
||||
|
||||
.. method:: AU_read.getnframes()
|
||||
|
||||
Returns number of audio frames.
|
||||
|
||||
|
||||
.. method:: AU_read.getcomptype()
|
||||
|
||||
Returns compression type. Supported compression types are ``'ULAW'``, ``'ALAW'``
|
||||
and ``'NONE'``.
|
||||
|
||||
|
||||
.. method:: AU_read.getcompname()
|
||||
|
||||
Human-readable version of :meth:`getcomptype`. The supported types have the
|
||||
respective names ``'CCITT G.711 u-law'``, ``'CCITT G.711 A-law'`` and ``'not
|
||||
compressed'``.
|
||||
|
||||
|
||||
.. method:: AU_read.getparams()
|
||||
|
||||
Returns a :func:`~collections.namedtuple` ``(nchannels, sampwidth,
|
||||
framerate, nframes, comptype, compname)``, equivalent to output of the
|
||||
:meth:`get\*` methods.
|
||||
|
||||
|
||||
.. method:: AU_read.readframes(n)
|
||||
|
||||
Reads and returns at most *n* frames of audio, as a :class:`bytes` object. The data
|
||||
will be returned in linear format. If the original data is in u-LAW format, it
|
||||
will be converted.
|
||||
|
||||
|
||||
.. method:: AU_read.rewind()
|
||||
|
||||
Rewind the file pointer to the beginning of the audio stream.
|
||||
|
||||
The following two methods define a term "position" which is compatible between
|
||||
them, and is otherwise implementation dependent.
|
||||
|
||||
|
||||
.. method:: AU_read.setpos(pos)
|
||||
|
||||
Set the file pointer to the specified position. Only values returned from
|
||||
:meth:`tell` should be used for *pos*.
|
||||
|
||||
|
||||
.. method:: AU_read.tell()
|
||||
|
||||
Return current file pointer position. Note that the returned value has nothing
|
||||
to do with the actual position in the file.
|
||||
|
||||
The following two functions are defined for compatibility with the :mod:`aifc`,
|
||||
and don't do anything interesting.
|
||||
|
||||
|
||||
.. method:: AU_read.getmarkers()
|
||||
|
||||
Returns ``None``.
|
||||
|
||||
|
||||
.. method:: AU_read.getmark(id)
|
||||
|
||||
Raise an error.
|
||||
|
||||
|
||||
.. _au-write-objects:
|
||||
|
||||
AU_write Objects
|
||||
----------------
|
||||
|
||||
AU_write objects, as returned by :func:`.open` above, have the following methods:
|
||||
|
||||
|
||||
.. method:: AU_write.setnchannels(n)
|
||||
|
||||
Set the number of channels.
|
||||
|
||||
|
||||
.. method:: AU_write.setsampwidth(n)
|
||||
|
||||
Set the sample width (in bytes.)
|
||||
|
||||
.. versionchanged:: 3.4
|
||||
Added support for 24-bit samples.
|
||||
|
||||
|
||||
.. method:: AU_write.setframerate(n)
|
||||
|
||||
Set the frame rate.
|
||||
|
||||
|
||||
.. method:: AU_write.setnframes(n)
|
||||
|
||||
Set the number of frames. This can be later changed, when and if more frames
|
||||
are written.
|
||||
|
||||
|
||||
.. method:: AU_write.setcomptype(type, name)
|
||||
|
||||
Set the compression type and description. Only ``'NONE'`` and ``'ULAW'`` are
|
||||
supported on output.
|
||||
|
||||
|
||||
.. method:: AU_write.setparams(tuple)
|
||||
|
||||
The *tuple* should be ``(nchannels, sampwidth, framerate, nframes, comptype,
|
||||
compname)``, with values valid for the :meth:`set\*` methods. Set all
|
||||
parameters.
|
||||
|
||||
|
||||
.. method:: AU_write.tell()
|
||||
|
||||
Return current position in the file, with the same disclaimer for the
|
||||
:meth:`AU_read.tell` and :meth:`AU_read.setpos` methods.
|
||||
|
||||
|
||||
.. method:: AU_write.writeframesraw(data)
|
||||
|
||||
Write audio frames, without correcting *nframes*.
|
||||
|
||||
.. versionchanged:: 3.4
|
||||
Any :term:`bytes-like object` is now accepted.
|
||||
|
||||
|
||||
.. method:: AU_write.writeframes(data)
|
||||
|
||||
Write audio frames and make sure *nframes* is correct.
|
||||
|
||||
.. versionchanged:: 3.4
|
||||
Any :term:`bytes-like object` is now accepted.
|
||||
|
||||
|
||||
.. method:: AU_write.close()
|
||||
|
||||
Make sure *nframes* is correct, and close the file.
|
||||
|
||||
This method is called upon deletion.
|
||||
|
||||
Note that it is invalid to set any parameters after calling :meth:`writeframes`
|
||||
or :meth:`writeframesraw`.
|
||||
|
|
@ -21,6 +21,5 @@ backwards compatibility. They have been superseded by other modules.
|
|||
nntplib.rst
|
||||
optparse.rst
|
||||
spwd.rst
|
||||
sunau.rst
|
||||
uu.rst
|
||||
xdrlib.rst
|
||||
|
|
|
@ -207,7 +207,6 @@ Doc/library/stdtypes.rst
|
|||
Doc/library/string.rst
|
||||
Doc/library/struct.rst
|
||||
Doc/library/subprocess.rst
|
||||
Doc/library/sunau.rst
|
||||
Doc/library/sys.rst
|
||||
Doc/library/sys_path_init.rst
|
||||
Doc/library/sysconfig.rst
|
||||
|
|
|
@ -1737,7 +1737,7 @@ Modules
|
|||
+---------------------+---------------------+---------------------+---------------------+---------------------+
|
||||
| :mod:`!cgi` | :mod:`imghdr` | :mod:`nntplib` | :mod:`spwd` | :mod:`xdrlib` |
|
||||
+---------------------+---------------------+---------------------+---------------------+---------------------+
|
||||
| :mod:`!cgitb` | :mod:`mailcap` | :mod:`!ossaudiodev` | :mod:`sunau` | |
|
||||
| :mod:`!cgitb` | :mod:`mailcap` | :mod:`!ossaudiodev` | :mod:`!sunau` | |
|
||||
+---------------------+---------------------+---------------------+---------------------+---------------------+
|
||||
|
||||
(Contributed by Brett Cannon in :issue:`47061` and Victor Stinner in
|
||||
|
|
|
@ -901,7 +901,7 @@ Modules (see :pep:`594`):
|
|||
* :mod:`!pipes`
|
||||
* :mod:`!sndhdr`
|
||||
* :mod:`spwd`
|
||||
* :mod:`sunau`
|
||||
* :mod:`!sunau`
|
||||
* :mod:`!telnetlib`
|
||||
* :mod:`uu`
|
||||
* :mod:`xdrlib`
|
||||
|
|
|
@ -161,6 +161,9 @@ Removed
|
|||
use the `pygame project <https://www.pygame.org/>`_ for audio playback.
|
||||
(Contributed by Victor Stinner in :gh:`104780`.)
|
||||
|
||||
* :pep:`594`: Remove the :mod:`!sunau` module, deprecated in Python 3.11.
|
||||
(Contributed by Victor Stinner in :gh:`104773`.)
|
||||
|
||||
|
||||
Porting to Python 3.13
|
||||
======================
|
||||
|
|
|
@ -1528,10 +1528,10 @@ work on Windows. This change was actually inadvertently made in 3.3.4.
|
|||
sunau
|
||||
-----
|
||||
|
||||
The :meth:`~sunau.getparams` method now returns a namedtuple rather than a
|
||||
The :meth:`~!sunau.getparams` method now returns a namedtuple rather than a
|
||||
plain tuple. (Contributed by Claudiu Popa in :issue:`18901`.)
|
||||
|
||||
:meth:`sunau.open` now supports the context management protocol: when used in a
|
||||
:meth:`!sunau.open` now supports the context management protocol: when used in a
|
||||
:keyword:`with` block, the ``close`` method of the returned object will be
|
||||
called automatically at the end of the block. (Contributed by Serhiy Storchaka
|
||||
in :issue:`18878`.)
|
||||
|
@ -1540,8 +1540,8 @@ in :issue:`18878`.)
|
|||
support for writing 24 sample using the module. (Contributed by
|
||||
Serhiy Storchaka in :issue:`19261`.)
|
||||
|
||||
The :meth:`~sunau.AU_write.writeframesraw` and
|
||||
:meth:`~sunau.AU_write.writeframes` methods now accept any :term:`bytes-like
|
||||
The :meth:`~!sunau.AU_write.writeframesraw` and
|
||||
:meth:`~!sunau.AU_write.writeframes` methods now accept any :term:`bytes-like
|
||||
object`. (Contributed by Serhiy Storchaka in :issue:`8311`.)
|
||||
|
||||
|
||||
|
|
|
@ -2061,8 +2061,8 @@ ssl
|
|||
sunau
|
||||
-----
|
||||
|
||||
:func:`sunau.openfp` has been deprecated and will be removed in Python 3.9.
|
||||
Use :func:`sunau.open` instead.
|
||||
:func:`!sunau.openfp` has been deprecated and will be removed in Python 3.9.
|
||||
Use :func:`!sunau.open` instead.
|
||||
(Contributed by Brian Curtin in :issue:`31985`.)
|
||||
|
||||
|
||||
|
|
533
Lib/sunau.py
533
Lib/sunau.py
|
@ -1,533 +0,0 @@
|
|||
"""Stuff to parse Sun and NeXT audio files.
|
||||
|
||||
An audio file consists of a header followed by the data. The structure
|
||||
of the header is as follows.
|
||||
|
||||
+---------------+
|
||||
| magic word |
|
||||
+---------------+
|
||||
| header size |
|
||||
+---------------+
|
||||
| data size |
|
||||
+---------------+
|
||||
| encoding |
|
||||
+---------------+
|
||||
| sample rate |
|
||||
+---------------+
|
||||
| # of channels |
|
||||
+---------------+
|
||||
| info |
|
||||
| |
|
||||
+---------------+
|
||||
|
||||
The magic word consists of the 4 characters '.snd'. Apart from the
|
||||
info field, all header fields are 4 bytes in size. They are all
|
||||
32-bit unsigned integers encoded in big-endian byte order.
|
||||
|
||||
The header size really gives the start of the data.
|
||||
The data size is the physical size of the data. From the other
|
||||
parameters the number of frames can be calculated.
|
||||
The encoding gives the way in which audio samples are encoded.
|
||||
Possible values are listed below.
|
||||
The info field currently consists of an ASCII string giving a
|
||||
human-readable description of the audio file. The info field is
|
||||
padded with NUL bytes to the header size.
|
||||
|
||||
Usage.
|
||||
|
||||
Reading audio files:
|
||||
f = sunau.open(file, 'r')
|
||||
where file is either the name of a file or an open file pointer.
|
||||
The open file pointer must have methods read(), seek(), and close().
|
||||
When the setpos() and rewind() methods are not used, the seek()
|
||||
method is not necessary.
|
||||
|
||||
This returns an instance of a class with the following public methods:
|
||||
getnchannels() -- returns number of audio channels (1 for
|
||||
mono, 2 for stereo)
|
||||
getsampwidth() -- returns sample width in bytes
|
||||
getframerate() -- returns sampling frequency
|
||||
getnframes() -- returns number of audio frames
|
||||
getcomptype() -- returns compression type ('NONE' or 'ULAW')
|
||||
getcompname() -- returns human-readable version of
|
||||
compression type ('not compressed' matches 'NONE')
|
||||
getparams() -- returns a namedtuple consisting of all of the
|
||||
above in the above order
|
||||
getmarkers() -- returns None (for compatibility with the
|
||||
aifc module)
|
||||
getmark(id) -- raises an error since the mark does not
|
||||
exist (for compatibility with the aifc module)
|
||||
readframes(n) -- returns at most n frames of audio
|
||||
rewind() -- rewind to the beginning of the audio stream
|
||||
setpos(pos) -- seek to the specified position
|
||||
tell() -- return the current position
|
||||
close() -- close the instance (make it unusable)
|
||||
The position returned by tell() and the position given to setpos()
|
||||
are compatible and have nothing to do with the actual position in the
|
||||
file.
|
||||
The close() method is called automatically when the class instance
|
||||
is destroyed.
|
||||
|
||||
Writing audio files:
|
||||
f = sunau.open(file, 'w')
|
||||
where file is either the name of a file or an open file pointer.
|
||||
The open file pointer must have methods write(), tell(), seek(), and
|
||||
close().
|
||||
|
||||
This returns an instance of a class with the following public methods:
|
||||
setnchannels(n) -- set the number of channels
|
||||
setsampwidth(n) -- set the sample width
|
||||
setframerate(n) -- set the frame rate
|
||||
setnframes(n) -- set the number of frames
|
||||
setcomptype(type, name)
|
||||
-- set the compression type and the
|
||||
human-readable compression type
|
||||
setparams(tuple)-- set all parameters at once
|
||||
tell() -- return current position in output file
|
||||
writeframesraw(data)
|
||||
-- write audio frames without pathing up the
|
||||
file header
|
||||
writeframes(data)
|
||||
-- write audio frames and patch up the file header
|
||||
close() -- patch up the file header and close the
|
||||
output file
|
||||
You should set the parameters before the first writeframesraw or
|
||||
writeframes. The total number of frames does not need to be set,
|
||||
but when it is set to the correct value, the header does not have to
|
||||
be patched up.
|
||||
It is best to first set all parameters, perhaps possibly the
|
||||
compression type, and then write audio frames using writeframesraw.
|
||||
When all frames have been written, either call writeframes(b'') or
|
||||
close() to patch up the sizes in the header.
|
||||
The close() method is called automatically when the class instance
|
||||
is destroyed.
|
||||
"""
|
||||
|
||||
from collections import namedtuple
|
||||
import warnings
|
||||
|
||||
warnings._deprecated(__name__, remove=(3, 13))
|
||||
|
||||
|
||||
_sunau_params = namedtuple('_sunau_params',
|
||||
'nchannels sampwidth framerate nframes comptype compname')
|
||||
|
||||
# from <multimedia/audio_filehdr.h>
|
||||
AUDIO_FILE_MAGIC = 0x2e736e64
|
||||
AUDIO_FILE_ENCODING_MULAW_8 = 1
|
||||
AUDIO_FILE_ENCODING_LINEAR_8 = 2
|
||||
AUDIO_FILE_ENCODING_LINEAR_16 = 3
|
||||
AUDIO_FILE_ENCODING_LINEAR_24 = 4
|
||||
AUDIO_FILE_ENCODING_LINEAR_32 = 5
|
||||
AUDIO_FILE_ENCODING_FLOAT = 6
|
||||
AUDIO_FILE_ENCODING_DOUBLE = 7
|
||||
AUDIO_FILE_ENCODING_ADPCM_G721 = 23
|
||||
AUDIO_FILE_ENCODING_ADPCM_G722 = 24
|
||||
AUDIO_FILE_ENCODING_ADPCM_G723_3 = 25
|
||||
AUDIO_FILE_ENCODING_ADPCM_G723_5 = 26
|
||||
AUDIO_FILE_ENCODING_ALAW_8 = 27
|
||||
|
||||
# from <multimedia/audio_hdr.h>
|
||||
AUDIO_UNKNOWN_SIZE = 0xFFFFFFFF # ((unsigned)(~0))
|
||||
|
||||
_simple_encodings = [AUDIO_FILE_ENCODING_MULAW_8,
|
||||
AUDIO_FILE_ENCODING_LINEAR_8,
|
||||
AUDIO_FILE_ENCODING_LINEAR_16,
|
||||
AUDIO_FILE_ENCODING_LINEAR_24,
|
||||
AUDIO_FILE_ENCODING_LINEAR_32,
|
||||
AUDIO_FILE_ENCODING_ALAW_8]
|
||||
|
||||
class Error(Exception):
|
||||
pass
|
||||
|
||||
def _read_u32(file):
|
||||
x = 0
|
||||
for i in range(4):
|
||||
byte = file.read(1)
|
||||
if not byte:
|
||||
raise EOFError
|
||||
x = x*256 + ord(byte)
|
||||
return x
|
||||
|
||||
def _write_u32(file, x):
|
||||
data = []
|
||||
for i in range(4):
|
||||
d, m = divmod(x, 256)
|
||||
data.insert(0, int(m))
|
||||
x = d
|
||||
file.write(bytes(data))
|
||||
|
||||
class Au_read:
|
||||
|
||||
def __init__(self, f):
|
||||
if isinstance(f, str):
|
||||
import builtins
|
||||
f = builtins.open(f, 'rb')
|
||||
self._opened = True
|
||||
else:
|
||||
self._opened = False
|
||||
self.initfp(f)
|
||||
|
||||
def __del__(self):
|
||||
if self._file:
|
||||
self.close()
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, *args):
|
||||
self.close()
|
||||
|
||||
def initfp(self, file):
|
||||
self._file = file
|
||||
self._soundpos = 0
|
||||
magic = int(_read_u32(file))
|
||||
if magic != AUDIO_FILE_MAGIC:
|
||||
raise Error('bad magic number')
|
||||
self._hdr_size = int(_read_u32(file))
|
||||
if self._hdr_size < 24:
|
||||
raise Error('header size too small')
|
||||
if self._hdr_size > 100:
|
||||
raise Error('header size ridiculously large')
|
||||
self._data_size = _read_u32(file)
|
||||
if self._data_size != AUDIO_UNKNOWN_SIZE:
|
||||
self._data_size = int(self._data_size)
|
||||
self._encoding = int(_read_u32(file))
|
||||
if self._encoding not in _simple_encodings:
|
||||
raise Error('encoding not (yet) supported')
|
||||
if self._encoding in (AUDIO_FILE_ENCODING_MULAW_8,
|
||||
AUDIO_FILE_ENCODING_ALAW_8):
|
||||
self._sampwidth = 2
|
||||
self._framesize = 1
|
||||
elif self._encoding == AUDIO_FILE_ENCODING_LINEAR_8:
|
||||
self._framesize = self._sampwidth = 1
|
||||
elif self._encoding == AUDIO_FILE_ENCODING_LINEAR_16:
|
||||
self._framesize = self._sampwidth = 2
|
||||
elif self._encoding == AUDIO_FILE_ENCODING_LINEAR_24:
|
||||
self._framesize = self._sampwidth = 3
|
||||
elif self._encoding == AUDIO_FILE_ENCODING_LINEAR_32:
|
||||
self._framesize = self._sampwidth = 4
|
||||
else:
|
||||
raise Error('unknown encoding')
|
||||
self._framerate = int(_read_u32(file))
|
||||
self._nchannels = int(_read_u32(file))
|
||||
if not self._nchannels:
|
||||
raise Error('bad # of channels')
|
||||
self._framesize = self._framesize * self._nchannels
|
||||
if self._hdr_size > 24:
|
||||
self._info = file.read(self._hdr_size - 24)
|
||||
self._info, _, _ = self._info.partition(b'\0')
|
||||
else:
|
||||
self._info = b''
|
||||
try:
|
||||
self._data_pos = file.tell()
|
||||
except (AttributeError, OSError):
|
||||
self._data_pos = None
|
||||
|
||||
def getfp(self):
|
||||
return self._file
|
||||
|
||||
def getnchannels(self):
|
||||
return self._nchannels
|
||||
|
||||
def getsampwidth(self):
|
||||
return self._sampwidth
|
||||
|
||||
def getframerate(self):
|
||||
return self._framerate
|
||||
|
||||
def getnframes(self):
|
||||
if self._data_size == AUDIO_UNKNOWN_SIZE:
|
||||
return AUDIO_UNKNOWN_SIZE
|
||||
if self._encoding in _simple_encodings:
|
||||
return self._data_size // self._framesize
|
||||
return 0 # XXX--must do some arithmetic here
|
||||
|
||||
def getcomptype(self):
|
||||
if self._encoding == AUDIO_FILE_ENCODING_MULAW_8:
|
||||
return 'ULAW'
|
||||
elif self._encoding == AUDIO_FILE_ENCODING_ALAW_8:
|
||||
return 'ALAW'
|
||||
else:
|
||||
return 'NONE'
|
||||
|
||||
def getcompname(self):
|
||||
if self._encoding == AUDIO_FILE_ENCODING_MULAW_8:
|
||||
return 'CCITT G.711 u-law'
|
||||
elif self._encoding == AUDIO_FILE_ENCODING_ALAW_8:
|
||||
return 'CCITT G.711 A-law'
|
||||
else:
|
||||
return 'not compressed'
|
||||
|
||||
def getparams(self):
|
||||
return _sunau_params(self.getnchannels(), self.getsampwidth(),
|
||||
self.getframerate(), self.getnframes(),
|
||||
self.getcomptype(), self.getcompname())
|
||||
|
||||
def getmarkers(self):
|
||||
return None
|
||||
|
||||
def getmark(self, id):
|
||||
raise Error('no marks')
|
||||
|
||||
def readframes(self, nframes):
|
||||
if self._encoding in _simple_encodings:
|
||||
if nframes == AUDIO_UNKNOWN_SIZE:
|
||||
data = self._file.read()
|
||||
else:
|
||||
data = self._file.read(nframes * self._framesize)
|
||||
self._soundpos += len(data) // self._framesize
|
||||
if self._encoding == AUDIO_FILE_ENCODING_MULAW_8:
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter('ignore', category=DeprecationWarning)
|
||||
import audioop
|
||||
data = audioop.ulaw2lin(data, self._sampwidth)
|
||||
return data
|
||||
return None # XXX--not implemented yet
|
||||
|
||||
def rewind(self):
|
||||
if self._data_pos is None:
|
||||
raise OSError('cannot seek')
|
||||
self._file.seek(self._data_pos)
|
||||
self._soundpos = 0
|
||||
|
||||
def tell(self):
|
||||
return self._soundpos
|
||||
|
||||
def setpos(self, pos):
|
||||
if pos < 0 or pos > self.getnframes():
|
||||
raise Error('position not in range')
|
||||
if self._data_pos is None:
|
||||
raise OSError('cannot seek')
|
||||
self._file.seek(self._data_pos + pos * self._framesize)
|
||||
self._soundpos = pos
|
||||
|
||||
def close(self):
|
||||
file = self._file
|
||||
if file:
|
||||
self._file = None
|
||||
if self._opened:
|
||||
file.close()
|
||||
|
||||
class Au_write:
|
||||
|
||||
def __init__(self, f):
|
||||
if isinstance(f, str):
|
||||
import builtins
|
||||
f = builtins.open(f, 'wb')
|
||||
self._opened = True
|
||||
else:
|
||||
self._opened = False
|
||||
self.initfp(f)
|
||||
|
||||
def __del__(self):
|
||||
if self._file:
|
||||
self.close()
|
||||
self._file = None
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, *args):
|
||||
self.close()
|
||||
|
||||
def initfp(self, file):
|
||||
self._file = file
|
||||
self._framerate = 0
|
||||
self._nchannels = 0
|
||||
self._sampwidth = 0
|
||||
self._framesize = 0
|
||||
self._nframes = AUDIO_UNKNOWN_SIZE
|
||||
self._nframeswritten = 0
|
||||
self._datawritten = 0
|
||||
self._datalength = 0
|
||||
self._info = b''
|
||||
self._comptype = 'ULAW' # default is U-law
|
||||
|
||||
def setnchannels(self, nchannels):
|
||||
if self._nframeswritten:
|
||||
raise Error('cannot change parameters after starting to write')
|
||||
if nchannels not in (1, 2, 4):
|
||||
raise Error('only 1, 2, or 4 channels supported')
|
||||
self._nchannels = nchannels
|
||||
|
||||
def getnchannels(self):
|
||||
if not self._nchannels:
|
||||
raise Error('number of channels not set')
|
||||
return self._nchannels
|
||||
|
||||
def setsampwidth(self, sampwidth):
|
||||
if self._nframeswritten:
|
||||
raise Error('cannot change parameters after starting to write')
|
||||
if sampwidth not in (1, 2, 3, 4):
|
||||
raise Error('bad sample width')
|
||||
self._sampwidth = sampwidth
|
||||
|
||||
def getsampwidth(self):
|
||||
if not self._framerate:
|
||||
raise Error('sample width not specified')
|
||||
return self._sampwidth
|
||||
|
||||
def setframerate(self, framerate):
|
||||
if self._nframeswritten:
|
||||
raise Error('cannot change parameters after starting to write')
|
||||
self._framerate = framerate
|
||||
|
||||
def getframerate(self):
|
||||
if not self._framerate:
|
||||
raise Error('frame rate not set')
|
||||
return self._framerate
|
||||
|
||||
def setnframes(self, nframes):
|
||||
if self._nframeswritten:
|
||||
raise Error('cannot change parameters after starting to write')
|
||||
if nframes < 0:
|
||||
raise Error('# of frames cannot be negative')
|
||||
self._nframes = nframes
|
||||
|
||||
def getnframes(self):
|
||||
return self._nframeswritten
|
||||
|
||||
def setcomptype(self, type, name):
|
||||
if type in ('NONE', 'ULAW'):
|
||||
self._comptype = type
|
||||
else:
|
||||
raise Error('unknown compression type')
|
||||
|
||||
def getcomptype(self):
|
||||
return self._comptype
|
||||
|
||||
def getcompname(self):
|
||||
if self._comptype == 'ULAW':
|
||||
return 'CCITT G.711 u-law'
|
||||
elif self._comptype == 'ALAW':
|
||||
return 'CCITT G.711 A-law'
|
||||
else:
|
||||
return 'not compressed'
|
||||
|
||||
def setparams(self, params):
|
||||
nchannels, sampwidth, framerate, nframes, comptype, compname = params
|
||||
self.setnchannels(nchannels)
|
||||
self.setsampwidth(sampwidth)
|
||||
self.setframerate(framerate)
|
||||
self.setnframes(nframes)
|
||||
self.setcomptype(comptype, compname)
|
||||
|
||||
def getparams(self):
|
||||
return _sunau_params(self.getnchannels(), self.getsampwidth(),
|
||||
self.getframerate(), self.getnframes(),
|
||||
self.getcomptype(), self.getcompname())
|
||||
|
||||
def tell(self):
|
||||
return self._nframeswritten
|
||||
|
||||
def writeframesraw(self, data):
|
||||
if not isinstance(data, (bytes, bytearray)):
|
||||
data = memoryview(data).cast('B')
|
||||
self._ensure_header_written()
|
||||
if self._comptype == 'ULAW':
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter('ignore', category=DeprecationWarning)
|
||||
import audioop
|
||||
data = audioop.lin2ulaw(data, self._sampwidth)
|
||||
nframes = len(data) // self._framesize
|
||||
self._file.write(data)
|
||||
self._nframeswritten = self._nframeswritten + nframes
|
||||
self._datawritten = self._datawritten + len(data)
|
||||
|
||||
def writeframes(self, data):
|
||||
self.writeframesraw(data)
|
||||
if self._nframeswritten != self._nframes or \
|
||||
self._datalength != self._datawritten:
|
||||
self._patchheader()
|
||||
|
||||
def close(self):
|
||||
if self._file:
|
||||
try:
|
||||
self._ensure_header_written()
|
||||
if self._nframeswritten != self._nframes or \
|
||||
self._datalength != self._datawritten:
|
||||
self._patchheader()
|
||||
self._file.flush()
|
||||
finally:
|
||||
file = self._file
|
||||
self._file = None
|
||||
if self._opened:
|
||||
file.close()
|
||||
|
||||
#
|
||||
# private methods
|
||||
#
|
||||
|
||||
def _ensure_header_written(self):
|
||||
if not self._nframeswritten:
|
||||
if not self._nchannels:
|
||||
raise Error('# of channels not specified')
|
||||
if not self._sampwidth:
|
||||
raise Error('sample width not specified')
|
||||
if not self._framerate:
|
||||
raise Error('frame rate not specified')
|
||||
self._write_header()
|
||||
|
||||
def _write_header(self):
|
||||
if self._comptype == 'NONE':
|
||||
if self._sampwidth == 1:
|
||||
encoding = AUDIO_FILE_ENCODING_LINEAR_8
|
||||
self._framesize = 1
|
||||
elif self._sampwidth == 2:
|
||||
encoding = AUDIO_FILE_ENCODING_LINEAR_16
|
||||
self._framesize = 2
|
||||
elif self._sampwidth == 3:
|
||||
encoding = AUDIO_FILE_ENCODING_LINEAR_24
|
||||
self._framesize = 3
|
||||
elif self._sampwidth == 4:
|
||||
encoding = AUDIO_FILE_ENCODING_LINEAR_32
|
||||
self._framesize = 4
|
||||
else:
|
||||
raise Error('internal error')
|
||||
elif self._comptype == 'ULAW':
|
||||
encoding = AUDIO_FILE_ENCODING_MULAW_8
|
||||
self._framesize = 1
|
||||
else:
|
||||
raise Error('internal error')
|
||||
self._framesize = self._framesize * self._nchannels
|
||||
_write_u32(self._file, AUDIO_FILE_MAGIC)
|
||||
header_size = 25 + len(self._info)
|
||||
header_size = (header_size + 7) & ~7
|
||||
_write_u32(self._file, header_size)
|
||||
if self._nframes == AUDIO_UNKNOWN_SIZE:
|
||||
length = AUDIO_UNKNOWN_SIZE
|
||||
else:
|
||||
length = self._nframes * self._framesize
|
||||
try:
|
||||
self._form_length_pos = self._file.tell()
|
||||
except (AttributeError, OSError):
|
||||
self._form_length_pos = None
|
||||
_write_u32(self._file, length)
|
||||
self._datalength = length
|
||||
_write_u32(self._file, encoding)
|
||||
_write_u32(self._file, self._framerate)
|
||||
_write_u32(self._file, self._nchannels)
|
||||
self._file.write(self._info)
|
||||
self._file.write(b'\0'*(header_size - len(self._info) - 24))
|
||||
|
||||
def _patchheader(self):
|
||||
if self._form_length_pos is None:
|
||||
raise OSError('cannot seek')
|
||||
self._file.seek(self._form_length_pos)
|
||||
_write_u32(self._file, self._datawritten)
|
||||
self._datalength = self._datawritten
|
||||
self._file.seek(0, 2)
|
||||
|
||||
def open(f, mode=None):
|
||||
if mode is None:
|
||||
if hasattr(f, 'mode'):
|
||||
mode = f.mode
|
||||
else:
|
||||
mode = 'rb'
|
||||
if mode in ('r', 'rb'):
|
||||
return Au_read(f)
|
||||
elif mode in ('w', 'wb'):
|
||||
return Au_write(f)
|
||||
else:
|
||||
raise Error("mode must be 'r', 'rb', 'w', or 'wb'")
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,160 +0,0 @@
|
|||
import unittest
|
||||
from test import audiotests
|
||||
import io
|
||||
import struct
|
||||
import sys
|
||||
from test.support import warnings_helper
|
||||
|
||||
sunau = warnings_helper.import_deprecated("sunau")
|
||||
audioop = warnings_helper.import_deprecated("audioop")
|
||||
|
||||
|
||||
class SunauTest(audiotests.AudioWriteTests,
|
||||
audiotests.AudioTestsWithSourceFile):
|
||||
module = sunau
|
||||
|
||||
|
||||
class SunauPCM8Test(SunauTest, unittest.TestCase):
|
||||
sndfilename = 'pluck-pcm8.au'
|
||||
sndfilenframes = 3307
|
||||
nchannels = 2
|
||||
sampwidth = 1
|
||||
framerate = 11025
|
||||
nframes = 48
|
||||
comptype = 'NONE'
|
||||
compname = 'not compressed'
|
||||
frames = bytes.fromhex("""\
|
||||
02FF 4B00 3104 8008 CB06 4803 BF01 03FE B8FA B4F3 29EB 1AE6 \
|
||||
EDE4 C6E2 0EE0 EFE0 57E2 FBE8 13EF D8F7 97FB F5FC 08FB DFFB \
|
||||
11FA 3EFB BCFC 66FF CF04 4309 C10E 5112 EE17 8216 7F14 8012 \
|
||||
490E 520D EF0F CE0F E40C 630A 080A 2B0B 510E 8B11 B60E 440A \
|
||||
""")
|
||||
|
||||
|
||||
class SunauPCM16Test(SunauTest, unittest.TestCase):
|
||||
sndfilename = 'pluck-pcm16.au'
|
||||
sndfilenframes = 3307
|
||||
nchannels = 2
|
||||
sampwidth = 2
|
||||
framerate = 11025
|
||||
nframes = 48
|
||||
comptype = 'NONE'
|
||||
compname = 'not compressed'
|
||||
frames = bytes.fromhex("""\
|
||||
022EFFEA 4B5C00F9 311404EF 80DB0844 CBE006B0 48AB03F3 BFE601B5 0367FE80 \
|
||||
B853FA42 B4AFF351 2997EBCD 1A5AE6DC EDF9E492 C627E277 0E06E0B7 EF29E029 \
|
||||
5759E271 FB34E83F 1377EF85 D82CF727 978EFB79 F5F7FC12 0864FB9E DF30FB40 \
|
||||
1183FA30 3EEAFB59 BC78FCB4 66D5FF60 CF130415 431A097D C1BA0EC7 512312A0 \
|
||||
EEE11754 82071666 7FFE1448 80001298 49990EB7 52B40DC1 EFAD0F65 CE3A0FBE \
|
||||
E4B70CE6 63490A57 08CC0A1D 2BBC0B09 51480E46 8BCB113C B6F60EE9 44150A5A \
|
||||
""")
|
||||
|
||||
|
||||
class SunauPCM24Test(SunauTest, unittest.TestCase):
|
||||
sndfilename = 'pluck-pcm24.au'
|
||||
sndfilenframes = 3307
|
||||
nchannels = 2
|
||||
sampwidth = 3
|
||||
framerate = 11025
|
||||
nframes = 48
|
||||
comptype = 'NONE'
|
||||
compname = 'not compressed'
|
||||
frames = bytes.fromhex("""\
|
||||
022D65FFEB9D 4B5A0F00FA54 3113C304EE2B 80DCD6084303 \
|
||||
CBDEC006B261 48A99803F2F8 BFE82401B07D 036BFBFE7B5D \
|
||||
B85756FA3EC9 B4B055F3502B 299830EBCB62 1A5CA7E6D99A \
|
||||
EDFA3EE491BD C625EBE27884 0E05A9E0B6CF EF2929E02922 \
|
||||
5758D8E27067 FB3557E83E16 1377BFEF8402 D82C5BF7272A \
|
||||
978F16FB7745 F5F865FC1013 086635FB9C4E DF30FCFB40EE \
|
||||
117FE0FA3438 3EE6B8FB5AC3 BC77A3FCB2F4 66D6DAFF5F32 \
|
||||
CF13B9041275 431D69097A8C C1BB600EC74E 5120B912A2BA \
|
||||
EEDF641754C0 8207001664B7 7FFFFF14453F 8000001294E6 \
|
||||
499C1B0EB3B2 52B73E0DBCA0 EFB2B20F5FD8 CE3CDB0FBE12 \
|
||||
E4B49C0CEA2D 6344A80A5A7C 08C8FE0A1FFE 2BB9860B0A0E \
|
||||
51486F0E44E1 8BCC64113B05 B6F4EC0EEB36 4413170A5B48 \
|
||||
""")
|
||||
|
||||
|
||||
class SunauPCM32Test(SunauTest, unittest.TestCase):
|
||||
sndfilename = 'pluck-pcm32.au'
|
||||
sndfilenframes = 3307
|
||||
nchannels = 2
|
||||
sampwidth = 4
|
||||
framerate = 11025
|
||||
nframes = 48
|
||||
comptype = 'NONE'
|
||||
compname = 'not compressed'
|
||||
frames = bytes.fromhex("""\
|
||||
022D65BCFFEB9D92 4B5A0F8000FA549C 3113C34004EE2BC0 80DCD680084303E0 \
|
||||
CBDEC0C006B26140 48A9980003F2F8FC BFE8248001B07D92 036BFB60FE7B5D34 \
|
||||
B8575600FA3EC920 B4B05500F3502BC0 29983000EBCB6240 1A5CA7A0E6D99A60 \
|
||||
EDFA3E80E491BD40 C625EB80E27884A0 0E05A9A0E0B6CFE0 EF292940E0292280 \
|
||||
5758D800E2706700 FB3557D8E83E1640 1377BF00EF840280 D82C5B80F7272A80 \
|
||||
978F1600FB774560 F5F86510FC101364 086635A0FB9C4E20 DF30FC40FB40EE28 \
|
||||
117FE0A0FA3438B0 3EE6B840FB5AC3F0 BC77A380FCB2F454 66D6DA80FF5F32B4 \
|
||||
CF13B980041275B0 431D6980097A8C00 C1BB60000EC74E00 5120B98012A2BAA0 \
|
||||
EEDF64C01754C060 820700001664B780 7FFFFFFF14453F40 800000001294E6E0 \
|
||||
499C1B000EB3B270 52B73E000DBCA020 EFB2B2E00F5FD880 CE3CDB400FBE1270 \
|
||||
E4B49CC00CEA2D90 6344A8800A5A7CA0 08C8FE800A1FFEE0 2BB986C00B0A0E00 \
|
||||
51486F800E44E190 8BCC6480113B0580 B6F4EC000EEB3630 441317800A5B48A0 \
|
||||
""")
|
||||
|
||||
|
||||
class SunauULAWTest(SunauTest, unittest.TestCase):
|
||||
sndfilename = 'pluck-ulaw.au'
|
||||
sndfilenframes = 3307
|
||||
nchannels = 2
|
||||
sampwidth = 2
|
||||
framerate = 11025
|
||||
nframes = 48
|
||||
comptype = 'ULAW'
|
||||
compname = 'CCITT G.711 u-law'
|
||||
frames = bytes.fromhex("""\
|
||||
022CFFE8 497C00F4 307C04DC 8284083C CB84069C 497C03DC BE8401AC 036CFE74 \
|
||||
B684FA24 B684F344 2A7CEC04 19FCE704 EE04E504 C584E204 0E3CE104 EF04DF84 \
|
||||
557CE204 FB24E804 12FCEF04 D784F744 9684FB64 F5C4FC24 083CFBA4 DF84FB24 \
|
||||
11FCFA24 3E7CFB64 BA84FCB4 657CFF5C CF84041C 417C09BC C1840EBC 517C12FC \
|
||||
EF0416FC 828415FC 7D7C13FC 828412FC 497C0EBC 517C0DBC F0040F3C CD840FFC \
|
||||
E5040CBC 617C0A3C 08BC0A3C 2C7C0B3C 517C0E3C 8A8410FC B6840EBC 457C0A3C \
|
||||
""")
|
||||
if sys.byteorder != 'big':
|
||||
frames = audioop.byteswap(frames, 2)
|
||||
|
||||
|
||||
class SunauLowLevelTest(unittest.TestCase):
|
||||
|
||||
def test_read_bad_magic_number(self):
|
||||
b = b'SPA'
|
||||
with self.assertRaises(EOFError):
|
||||
sunau.open(io.BytesIO(b))
|
||||
b = b'SPAM'
|
||||
with self.assertRaisesRegex(sunau.Error, 'bad magic number'):
|
||||
sunau.open(io.BytesIO(b))
|
||||
|
||||
def test_read_too_small_header(self):
|
||||
b = struct.pack('>LLLLL', sunau.AUDIO_FILE_MAGIC, 20, 0,
|
||||
sunau.AUDIO_FILE_ENCODING_LINEAR_8, 11025)
|
||||
with self.assertRaisesRegex(sunau.Error, 'header size too small'):
|
||||
sunau.open(io.BytesIO(b))
|
||||
|
||||
def test_read_too_large_header(self):
|
||||
b = struct.pack('>LLLLLL', sunau.AUDIO_FILE_MAGIC, 124, 0,
|
||||
sunau.AUDIO_FILE_ENCODING_LINEAR_8, 11025, 1)
|
||||
b += b'\0' * 100
|
||||
with self.assertRaisesRegex(sunau.Error, 'header size ridiculously large'):
|
||||
sunau.open(io.BytesIO(b))
|
||||
|
||||
def test_read_wrong_encoding(self):
|
||||
b = struct.pack('>LLLLLL', sunau.AUDIO_FILE_MAGIC, 24, 0, 0, 11025, 1)
|
||||
with self.assertRaisesRegex(sunau.Error, r'encoding not \(yet\) supported'):
|
||||
sunau.open(io.BytesIO(b))
|
||||
|
||||
def test_read_wrong_number_of_channels(self):
|
||||
b = struct.pack('>LLLLLL', sunau.AUDIO_FILE_MAGIC, 24, 0,
|
||||
sunau.AUDIO_FILE_ENCODING_LINEAR_8, 11025, 0)
|
||||
with self.assertRaisesRegex(sunau.Error, 'bad # of channels'):
|
||||
sunau.open(io.BytesIO(b))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
|
@ -326,7 +326,7 @@ documentation.
|
|||
|
||||
Improved exceptions raised for invalid number of channels and sample width
|
||||
when read an audio file in modules :mod:`aifc`, :mod:`wave` and
|
||||
:mod:`sunau`.
|
||||
:mod:`!sunau`.
|
||||
|
||||
..
|
||||
|
||||
|
|
|
@ -5577,7 +5577,7 @@ documentation.
|
|||
|
||||
Improved exceptions raised for invalid number of channels and sample width
|
||||
when read an audio file in modules :mod:`aifc`, :mod:`wave` and
|
||||
:mod:`sunau`.
|
||||
:mod:`!sunau`.
|
||||
|
||||
..
|
||||
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
:pep:`594`: Remove the :mod:`!sunau` module, deprecated in Python 3.11.
|
||||
Patch by Victor Stinner.
|
1
Python/stdlib_module_names.h
generated
1
Python/stdlib_module_names.h
generated
|
@ -248,7 +248,6 @@ static const char* _Py_stdlib_module_names[] = {
|
|||
"stringprep",
|
||||
"struct",
|
||||
"subprocess",
|
||||
"sunau",
|
||||
"symtable",
|
||||
"sys",
|
||||
"sysconfig",
|
||||
|
|
|
@ -84,7 +84,7 @@ OMIT_NETWORKING_FILES = (
|
|||
|
||||
OMIT_MODULE_FILES = {
|
||||
"_asyncio": ["asyncio/"],
|
||||
"audioop": ["aifc.py", "sunau.py", "wave.py"],
|
||||
"audioop": ["aifc.py", "wave.py"],
|
||||
"_crypt": ["crypt.py"],
|
||||
"_curses": ["curses/"],
|
||||
"_ctypes": ["ctypes/"],
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue