gh-104773: PEP 594: Remove the aifc module (#104933)

* Remove .aifc and .aiff test files of Lib/test/audiodata/
* Remove Lib/test/Sine-1000Hz-300ms.aif test file
This commit is contained in:
Victor Stinner 2023-05-25 17:20:48 +02:00 committed by GitHub
parent 076b6204cb
commit 036da3bd43
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 20 additions and 1688 deletions

View file

@ -1,247 +0,0 @@
:mod:`aifc` --- Read and write AIFF and AIFC files
==================================================
.. module:: aifc
:synopsis: Read and write audio files in AIFF or AIFC format.
:deprecated:
**Source code:** :source:`Lib/aifc.py`
.. index::
single: Audio Interchange File Format
single: AIFF
single: AIFF-C
.. deprecated-removed:: 3.11 3.13
The :mod:`aifc` module is deprecated
(see :pep:`PEP 594 <594#aifc>` for details).
--------------
This module provides support for reading and writing AIFF and AIFF-C files.
AIFF is Audio Interchange File Format, a format for storing digital audio
samples in a file. AIFF-C is a newer version of the format that includes the
ability to compress the audio data.
Audio files have a number of parameters that describe the audio data. The
sampling rate or frame rate is the number of times per second the sound is
sampled. The number of channels indicate if the audio is mono, stereo, or
quadro. Each frame consists of one sample per channel. The sample size is the
size in bytes of each sample. Thus a frame consists of
``nchannels * samplesize`` bytes, and a second's worth of audio consists of
``nchannels * samplesize * framerate`` bytes.
For example, CD quality audio has a sample size of two bytes (16 bits), uses two
channels (stereo) and has a frame rate of 44,100 frames/second. This gives a
frame size of 4 bytes (2\*2), and a second's worth occupies 2\*2\*44100 bytes
(176,400 bytes).
Module :mod:`aifc` defines the following function:
.. function:: open(file, mode=None)
Open an AIFF or AIFF-C file and return an object instance with methods that are
described below. The argument *file* is either a string naming a file or a
:term:`file object`. *mode* must be ``'r'`` or ``'rb'`` when the file must be
opened for reading, or ``'w'`` or ``'wb'`` when the file must be opened for writing.
If omitted, ``file.mode`` is used if it exists, otherwise ``'rb'`` is used. When
used for writing, the file object should be seekable, unless you know ahead of
time how many samples you are going to write in total and use
:meth:`writeframesraw` and :meth:`setnframes`.
The :func:`.open` function may be used in a :keyword:`with` statement. When
the :keyword:`!with` block completes, the :meth:`~aifc.close` method is called.
.. versionchanged:: 3.4
Support for the :keyword:`with` statement was added.
Objects returned by :func:`.open` when a file is opened for reading have the
following methods:
.. method:: aifc.getnchannels()
Return the number of audio channels (1 for mono, 2 for stereo).
.. method:: aifc.getsampwidth()
Return the size in bytes of individual samples.
.. method:: aifc.getframerate()
Return the sampling rate (number of audio frames per second).
.. method:: aifc.getnframes()
Return the number of audio frames in the file.
.. method:: aifc.getcomptype()
Return a bytes array of length 4 describing the type of compression
used in the audio file. For AIFF files, the returned value is
``b'NONE'``.
.. method:: aifc.getcompname()
Return a bytes array convertible to a human-readable description
of the type of compression used in the audio file. For AIFF files,
the returned value is ``b'not compressed'``.
.. method:: aifc.getparams()
Returns a :func:`~collections.namedtuple` ``(nchannels, sampwidth,
framerate, nframes, comptype, compname)``, equivalent to output of the
:meth:`get\*` methods.
.. method:: aifc.getmarkers()
Return a list of markers in the audio file. A marker consists of a tuple of
three elements. The first is the mark ID (an integer), the second is the mark
position in frames from the beginning of the data (an integer), the third is the
name of the mark (a string).
.. method:: aifc.getmark(id)
Return the tuple as described in :meth:`getmarkers` for the mark with the given
*id*.
.. method:: aifc.readframes(nframes)
Read and return the next *nframes* frames from the audio file. The returned
data is a string containing for each frame the uncompressed samples of all
channels.
.. method:: aifc.rewind()
Rewind the read pointer. The next :meth:`readframes` will start from the
beginning.
.. method:: aifc.setpos(pos)
Seek to the specified frame number.
.. method:: aifc.tell()
Return the current frame number.
.. method:: aifc.close()
Close the AIFF file. After calling this method, the object can no longer be
used.
Objects returned by :func:`.open` when a file is opened for writing have all the
above methods, except for :meth:`readframes` and :meth:`setpos`. In addition
the following methods exist. The :meth:`get\*` methods can only be called after
the corresponding :meth:`set\*` methods have been called. Before the first
:meth:`writeframes` or :meth:`writeframesraw`, all parameters except for the
number of frames must be filled in.
.. method:: aifc.aiff()
Create an AIFF file. The default is that an AIFF-C file is created, unless the
name of the file ends in ``'.aiff'`` in which case the default is an AIFF file.
.. method:: aifc.aifc()
Create an AIFF-C file. The default is that an AIFF-C file is created, unless
the name of the file ends in ``'.aiff'`` in which case the default is an AIFF
file.
.. method:: aifc.setnchannels(nchannels)
Specify the number of channels in the audio file.
.. method:: aifc.setsampwidth(width)
Specify the size in bytes of audio samples.
.. method:: aifc.setframerate(rate)
Specify the sampling frequency in frames per second.
.. method:: aifc.setnframes(nframes)
Specify the number of frames that are to be written to the audio file. If this
parameter is not set, or not set correctly, the file needs to support seeking.
.. method:: aifc.setcomptype(type, name)
.. index::
single: u-LAW
single: A-LAW
single: G.722
Specify the compression type. If not specified, the audio data will
not be compressed. In AIFF files, compression is not possible.
The name parameter should be a human-readable description of the
compression type as a bytes array, the type parameter should be a
bytes array of length 4. Currently the following compression types
are supported: ``b'NONE'``, ``b'ULAW'``, ``b'ALAW'``, ``b'G722'``.
.. method:: aifc.setparams(nchannels, sampwidth, framerate, comptype, compname)
Set all the above parameters at once. The argument is a tuple consisting of the
various parameters. This means that it is possible to use the result of a
:meth:`getparams` call as argument to :meth:`setparams`.
.. method:: aifc.setmark(id, pos, name)
Add a mark with the given id (larger than 0), and the given name at the given
position. This method can be called at any time before :meth:`close`.
.. method:: aifc.tell()
:noindex:
Return the current write position in the output file. Useful in combination
with :meth:`setmark`.
.. method:: aifc.writeframes(data)
Write data to the output file. This method can only be called after the audio
file parameters have been set.
.. versionchanged:: 3.4
Any :term:`bytes-like object` is now accepted.
.. method:: aifc.writeframesraw(data)
Like :meth:`writeframes`, except that the header of the audio file is not
updated.
.. versionchanged:: 3.4
Any :term:`bytes-like object` is now accepted.
.. method:: aifc.close()
:noindex:
Close the AIFF file. The header of the file is updated to reflect the actual
size of the audio data. After calling this method, the object can no longer be
used.

View file

@ -10,7 +10,6 @@ backwards compatibility. They have been superseded by other modules.
.. toctree:: .. toctree::
aifc.rst
audioop.rst audioop.rst
chunk.rst chunk.rst
imghdr.rst imghdr.rst

View file

@ -119,7 +119,7 @@ Wave_read objects, as returned by :func:`.open`, have the following methods:
Rewind the file pointer to the beginning of the audio stream. Rewind the file pointer to the beginning of the audio stream.
The following two methods are defined for compatibility with the :mod:`aifc` The following two methods are defined for compatibility with the old :mod:`!aifc`
module, and don't do anything interesting. module, and don't do anything interesting.

View file

@ -75,7 +75,6 @@ Doc/install/index.rst
Doc/library/__future__.rst Doc/library/__future__.rst
Doc/library/_thread.rst Doc/library/_thread.rst
Doc/library/abc.rst Doc/library/abc.rst
Doc/library/aifc.rst
Doc/library/ast.rst Doc/library/ast.rst
Doc/library/asyncio-dev.rst Doc/library/asyncio-dev.rst
Doc/library/asyncio-eventloop.rst Doc/library/asyncio-eventloop.rst

View file

@ -1031,7 +1031,7 @@ Module changes
Lots of improvements and bugfixes were made to Python's extensive standard Lots of improvements and bugfixes were made to Python's extensive standard
library; some of the affected modules include :mod:`readline`, library; some of the affected modules include :mod:`readline`,
:mod:`ConfigParser`, :mod:`!cgi`, :mod:`calendar`, :mod:`posix`, :mod:`readline`, :mod:`ConfigParser`, :mod:`!cgi`, :mod:`calendar`, :mod:`posix`, :mod:`readline`,
:mod:`xmllib`, :mod:`aifc`, :mod:`chunk, wave`, :mod:`random`, :mod:`shelve`, :mod:`xmllib`, :mod:`!aifc`, :mod:`chunk, wave`, :mod:`random`, :mod:`shelve`,
and :mod:`!nntplib`. Consult the CVS logs for the exact patch-by-patch details. and :mod:`!nntplib`. Consult the CVS logs for the exact patch-by-patch details.
Brian Gallew contributed OpenSSL support for the :mod:`socket` module. OpenSSL Brian Gallew contributed OpenSSL support for the :mod:`socket` module. OpenSSL

View file

@ -1731,7 +1731,7 @@ Modules
slated for removal in Python 3.13: slated for removal in Python 3.13:
+---------------------+---------------------+---------------------+---------------------+---------------------+ +---------------------+---------------------+---------------------+---------------------+---------------------+
| :mod:`aifc` | :mod:`chunk` | :mod:`!msilib` | :mod:`!pipes` | :mod:`!telnetlib` | | :mod:`!aifc` | :mod:`chunk` | :mod:`!msilib` | :mod:`!pipes` | :mod:`!telnetlib` |
+---------------------+---------------------+---------------------+---------------------+---------------------+ +---------------------+---------------------+---------------------+---------------------+---------------------+
| :mod:`audioop` | :mod:`!crypt` | :mod:`!nis` | :mod:`!sndhdr` | :mod:`!uu` | | :mod:`audioop` | :mod:`!crypt` | :mod:`!nis` | :mod:`!sndhdr` | :mod:`!uu` |
+---------------------+---------------------+---------------------+---------------------+---------------------+ +---------------------+---------------------+---------------------+---------------------+---------------------+

View file

@ -918,7 +918,7 @@ and will be removed in Python 3.13.
Modules (see :pep:`594`): Modules (see :pep:`594`):
* :mod:`aifc` * :mod:`!aifc`
* :mod:`audioop` * :mod:`audioop`
* :mod:`!cgi` * :mod:`!cgi`
* :mod:`!cgitb` * :mod:`!cgitb`

View file

@ -221,6 +221,9 @@ Removed
the :mod:`base64` module is a modern alternative. the :mod:`base64` module is a modern alternative.
(Contributed by Victor Stinner in :gh:`104773`.) (Contributed by Victor Stinner in :gh:`104773`.)
* :pep:`594`: Remove the :mod:`!aifc` module, deprecated in Python 3.11.
(Contributed by Victor Stinner in :gh:`104773`.)
Porting to Python 3.13 Porting to Python 3.13
====================== ======================

View file

@ -605,15 +605,15 @@ Using ``ABC`` as a base class has essentially the same effect as specifying
aifc aifc
---- ----
The :meth:`~aifc.aifc.getparams` method now returns a namedtuple rather than a The :meth:`~!aifc.aifc.getparams` method now returns a namedtuple rather than a
plain tuple. (Contributed by Claudiu Popa in :issue:`17818`.) plain tuple. (Contributed by Claudiu Popa in :issue:`17818`.)
:func:`aifc.open` now supports the context management protocol: when used in a :func:`!aifc.open` now supports the context management protocol: when used in a
:keyword:`with` block, the :meth:`~aifc.aifc.close` method of the returned :keyword:`with` block, the :meth:`~!aifc.aifc.close` method of the returned
object will be called automatically at the end of the block. (Contributed by object will be called automatically at the end of the block. (Contributed by
Serhiy Storchacha in :issue:`16486`.) Serhiy Storchacha in :issue:`16486`.)
The :meth:`~aifc.aifc.writeframesraw` and :meth:`~aifc.aifc.writeframes` The :meth:`~!aifc.aifc.writeframesraw` and :meth:`~!aifc.aifc.writeframes`
methods now accept any :term:`bytes-like object`. (Contributed by Serhiy methods now accept any :term:`bytes-like object`. (Contributed by Serhiy
Storchaka in :issue:`8311`.) Storchaka in :issue:`8311`.)

View file

@ -1940,8 +1940,8 @@ Deprecated Python modules, functions and methods
aifc aifc
---- ----
:func:`aifc.openfp` has been deprecated and will be removed in Python 3.9. :func:`!aifc.openfp` has been deprecated and will be removed in Python 3.9.
Use :func:`aifc.open` instead. Use :func:`!aifc.open` instead.
(Contributed by Brian Curtin in :issue:`31985`.) (Contributed by Brian Curtin in :issue:`31985`.)

View file

@ -1,984 +0,0 @@
"""Stuff to parse AIFF-C and AIFF files.
Unless explicitly stated otherwise, the description below is true
both for AIFF-C files and AIFF files.
An AIFF-C file has the following structure.
+-----------------+
| FORM |
+-----------------+
| <size> |
+----+------------+
| | AIFC |
| +------------+
| | <chunks> |
| | . |
| | . |
| | . |
+----+------------+
An AIFF file has the string "AIFF" instead of "AIFC".
A chunk consists of an identifier (4 bytes) followed by a size (4 bytes,
big endian order), followed by the data. The size field does not include
the size of the 8 byte header.
The following chunk types are recognized.
FVER
<version number of AIFF-C defining document> (AIFF-C only).
MARK
<# of markers> (2 bytes)
list of markers:
<marker ID> (2 bytes, must be > 0)
<position> (4 bytes)
<marker name> ("pstring")
COMM
<# of channels> (2 bytes)
<# of sound frames> (4 bytes)
<size of the samples> (2 bytes)
<sampling frequency> (10 bytes, IEEE 80-bit extended
floating point)
in AIFF-C files only:
<compression type> (4 bytes)
<human-readable version of compression type> ("pstring")
SSND
<offset> (4 bytes, not used by this program)
<blocksize> (4 bytes, not used by this program)
<sound data>
A pstring consists of 1 byte length, a string of characters, and 0 or 1
byte pad to make the total length even.
Usage.
Reading AIFF files:
f = aifc.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().
In some types of audio files, if the setpos() method is 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' for AIFF files)
getcompname() -- returns human-readable version of
compression type ('not compressed' for AIFF files)
getparams() -- returns a namedtuple consisting of all of the
above in the above order
getmarkers() -- get the list of marks in the audio file or None
if there are no marks
getmark(id) -- get mark with the specified id (raises an error
if the mark does not exist)
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(), the position given to setpos() and
the position of marks are all 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 AIFF files:
f = aifc.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:
aiff() -- create an AIFF file (AIFF-C default)
aifc() -- create an AIFF-C file
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
setmark(id, pos, name)
-- add specified mark to the list of marks
tell() -- return current position in output file (useful
in combination with setmark())
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.
Marks can be added anytime. If there are any marks, you must call
close() after all frames have been written.
The close() method is called automatically when the class instance
is destroyed.
When a file is opened with the extension '.aiff', an AIFF file is
written, otherwise an AIFF-C file is written. This default can be
changed by calling aiff() or aifc() before the first writeframes or
writeframesraw.
"""
import struct
import builtins
import warnings
__all__ = ["Error", "open"]
warnings._deprecated(__name__, remove=(3, 13))
class Error(Exception):
pass
_AIFC_version = 0xA2805140 # Version 1 of AIFF-C
def _read_long(file):
try:
return struct.unpack('>l', file.read(4))[0]
except struct.error:
raise EOFError from None
def _read_ulong(file):
try:
return struct.unpack('>L', file.read(4))[0]
except struct.error:
raise EOFError from None
def _read_short(file):
try:
return struct.unpack('>h', file.read(2))[0]
except struct.error:
raise EOFError from None
def _read_ushort(file):
try:
return struct.unpack('>H', file.read(2))[0]
except struct.error:
raise EOFError from None
def _read_string(file):
length = ord(file.read(1))
if length == 0:
data = b''
else:
data = file.read(length)
if length & 1 == 0:
dummy = file.read(1)
return data
_HUGE_VAL = 1.79769313486231e+308 # See <limits.h>
def _read_float(f): # 10 bytes
expon = _read_short(f) # 2 bytes
sign = 1
if expon < 0:
sign = -1
expon = expon + 0x8000
himant = _read_ulong(f) # 4 bytes
lomant = _read_ulong(f) # 4 bytes
if expon == himant == lomant == 0:
f = 0.0
elif expon == 0x7FFF:
f = _HUGE_VAL
else:
expon = expon - 16383
f = (himant * 0x100000000 + lomant) * pow(2.0, expon - 63)
return sign * f
def _write_short(f, x):
f.write(struct.pack('>h', x))
def _write_ushort(f, x):
f.write(struct.pack('>H', x))
def _write_long(f, x):
f.write(struct.pack('>l', x))
def _write_ulong(f, x):
f.write(struct.pack('>L', x))
def _write_string(f, s):
if len(s) > 255:
raise ValueError("string exceeds maximum pstring length")
f.write(struct.pack('B', len(s)))
f.write(s)
if len(s) & 1 == 0:
f.write(b'\x00')
def _write_float(f, x):
import math
if x < 0:
sign = 0x8000
x = x * -1
else:
sign = 0
if x == 0:
expon = 0
himant = 0
lomant = 0
else:
fmant, expon = math.frexp(x)
if expon > 16384 or fmant >= 1 or fmant != fmant: # Infinity or NaN
expon = sign|0x7FFF
himant = 0
lomant = 0
else: # Finite
expon = expon + 16382
if expon < 0: # denormalized
fmant = math.ldexp(fmant, expon)
expon = 0
expon = expon | sign
fmant = math.ldexp(fmant, 32)
fsmant = math.floor(fmant)
himant = int(fsmant)
fmant = math.ldexp(fmant - fsmant, 32)
fsmant = math.floor(fmant)
lomant = int(fsmant)
_write_ushort(f, expon)
_write_ulong(f, himant)
_write_ulong(f, lomant)
with warnings.catch_warnings():
warnings.simplefilter("ignore", DeprecationWarning)
from chunk import Chunk
from collections import namedtuple
_aifc_params = namedtuple('_aifc_params',
'nchannels sampwidth framerate nframes comptype compname')
_aifc_params.nchannels.__doc__ = 'Number of audio channels (1 for mono, 2 for stereo)'
_aifc_params.sampwidth.__doc__ = 'Sample width in bytes'
_aifc_params.framerate.__doc__ = 'Sampling frequency'
_aifc_params.nframes.__doc__ = 'Number of audio frames'
_aifc_params.comptype.__doc__ = 'Compression type ("NONE" for AIFF files)'
_aifc_params.compname.__doc__ = ("""\
A human-readable version of the compression type
('not compressed' for AIFF files)""")
class Aifc_read:
# Variables used in this class:
#
# These variables are available to the user though appropriate
# methods of this class:
# _file -- the open file with methods read(), close(), and seek()
# set through the __init__() method
# _nchannels -- the number of audio channels
# available through the getnchannels() method
# _nframes -- the number of audio frames
# available through the getnframes() method
# _sampwidth -- the number of bytes per audio sample
# available through the getsampwidth() method
# _framerate -- the sampling frequency
# available through the getframerate() method
# _comptype -- the AIFF-C compression type ('NONE' if AIFF)
# available through the getcomptype() method
# _compname -- the human-readable AIFF-C compression type
# available through the getcomptype() method
# _markers -- the marks in the audio file
# available through the getmarkers() and getmark()
# methods
# _soundpos -- the position in the audio stream
# available through the tell() method, set through the
# setpos() method
#
# These variables are used internally only:
# _version -- the AIFF-C version number
# _decomp -- the decompressor from builtin module cl
# _comm_chunk_read -- 1 iff the COMM chunk has been read
# _aifc -- 1 iff reading an AIFF-C file
# _ssnd_seek_needed -- 1 iff positioned correctly in audio
# file for readframes()
# _ssnd_chunk -- instantiation of a chunk class for the SSND chunk
# _framesize -- size of one frame in the file
_file = None # Set here since __del__ checks it
def initfp(self, file):
self._version = 0
self._convert = None
self._markers = []
self._soundpos = 0
self._file = file
chunk = Chunk(file)
if chunk.getname() != b'FORM':
raise Error('file does not start with FORM id')
formdata = chunk.read(4)
if formdata == b'AIFF':
self._aifc = 0
elif formdata == b'AIFC':
self._aifc = 1
else:
raise Error('not an AIFF or AIFF-C file')
self._comm_chunk_read = 0
self._ssnd_chunk = None
while 1:
self._ssnd_seek_needed = 1
try:
chunk = Chunk(self._file)
except EOFError:
break
chunkname = chunk.getname()
if chunkname == b'COMM':
self._read_comm_chunk(chunk)
self._comm_chunk_read = 1
elif chunkname == b'SSND':
self._ssnd_chunk = chunk
dummy = chunk.read(8)
self._ssnd_seek_needed = 0
elif chunkname == b'FVER':
self._version = _read_ulong(chunk)
elif chunkname == b'MARK':
self._readmark(chunk)
chunk.skip()
if not self._comm_chunk_read or not self._ssnd_chunk:
raise Error('COMM chunk and/or SSND chunk missing')
def __init__(self, f):
if isinstance(f, str):
file_object = builtins.open(f, 'rb')
try:
self.initfp(file_object)
except:
file_object.close()
raise
else:
# assume it is an open file object already
self.initfp(f)
def __enter__(self):
return self
def __exit__(self, *args):
self.close()
#
# User visible methods.
#
def getfp(self):
return self._file
def rewind(self):
self._ssnd_seek_needed = 1
self._soundpos = 0
def close(self):
file = self._file
if file is not None:
self._file = None
file.close()
def tell(self):
return self._soundpos
def getnchannels(self):
return self._nchannels
def getnframes(self):
return self._nframes
def getsampwidth(self):
return self._sampwidth
def getframerate(self):
return self._framerate
def getcomptype(self):
return self._comptype
def getcompname(self):
return self._compname
## def getversion(self):
## return self._version
def getparams(self):
return _aifc_params(self.getnchannels(), self.getsampwidth(),
self.getframerate(), self.getnframes(),
self.getcomptype(), self.getcompname())
def getmarkers(self):
if len(self._markers) == 0:
return None
return self._markers
def getmark(self, id):
for marker in self._markers:
if id == marker[0]:
return marker
raise Error('marker {0!r} does not exist'.format(id))
def setpos(self, pos):
if pos < 0 or pos > self._nframes:
raise Error('position not in range')
self._soundpos = pos
self._ssnd_seek_needed = 1
def readframes(self, nframes):
if self._ssnd_seek_needed:
self._ssnd_chunk.seek(0)
dummy = self._ssnd_chunk.read(8)
pos = self._soundpos * self._framesize
if pos:
self._ssnd_chunk.seek(pos + 8)
self._ssnd_seek_needed = 0
if nframes == 0:
return b''
data = self._ssnd_chunk.read(nframes * self._framesize)
if self._convert and data:
data = self._convert(data)
self._soundpos = self._soundpos + len(data) // (self._nchannels
* self._sampwidth)
return data
#
# Internal methods.
#
def _alaw2lin(self, data):
with warnings.catch_warnings():
warnings.simplefilter('ignore', category=DeprecationWarning)
import audioop
return audioop.alaw2lin(data, 2)
def _ulaw2lin(self, data):
with warnings.catch_warnings():
warnings.simplefilter('ignore', category=DeprecationWarning)
import audioop
return audioop.ulaw2lin(data, 2)
def _adpcm2lin(self, data):
with warnings.catch_warnings():
warnings.simplefilter('ignore', category=DeprecationWarning)
import audioop
if not hasattr(self, '_adpcmstate'):
# first time
self._adpcmstate = None
data, self._adpcmstate = audioop.adpcm2lin(data, 2, self._adpcmstate)
return data
def _sowt2lin(self, data):
with warnings.catch_warnings():
warnings.simplefilter('ignore', category=DeprecationWarning)
import audioop
return audioop.byteswap(data, 2)
def _read_comm_chunk(self, chunk):
self._nchannels = _read_short(chunk)
self._nframes = _read_long(chunk)
self._sampwidth = (_read_short(chunk) + 7) // 8
self._framerate = int(_read_float(chunk))
if self._sampwidth <= 0:
raise Error('bad sample width')
if self._nchannels <= 0:
raise Error('bad # of channels')
self._framesize = self._nchannels * self._sampwidth
if self._aifc:
#DEBUG: SGI's soundeditor produces a bad size :-(
kludge = 0
if chunk.chunksize == 18:
kludge = 1
warnings.warn('Warning: bad COMM chunk size')
chunk.chunksize = 23
#DEBUG end
self._comptype = chunk.read(4)
#DEBUG start
if kludge:
length = ord(chunk.file.read(1))
if length & 1 == 0:
length = length + 1
chunk.chunksize = chunk.chunksize + length
chunk.file.seek(-1, 1)
#DEBUG end
self._compname = _read_string(chunk)
if self._comptype != b'NONE':
if self._comptype == b'G722':
self._convert = self._adpcm2lin
elif self._comptype in (b'ulaw', b'ULAW'):
self._convert = self._ulaw2lin
elif self._comptype in (b'alaw', b'ALAW'):
self._convert = self._alaw2lin
elif self._comptype in (b'sowt', b'SOWT'):
self._convert = self._sowt2lin
else:
raise Error('unsupported compression type')
self._sampwidth = 2
else:
self._comptype = b'NONE'
self._compname = b'not compressed'
def _readmark(self, chunk):
nmarkers = _read_short(chunk)
# Some files appear to contain invalid counts.
# Cope with this by testing for EOF.
try:
for i in range(nmarkers):
id = _read_short(chunk)
pos = _read_long(chunk)
name = _read_string(chunk)
if pos or name:
# some files appear to have
# dummy markers consisting of
# a position 0 and name ''
self._markers.append((id, pos, name))
except EOFError:
w = ('Warning: MARK chunk contains only %s marker%s instead of %s' %
(len(self._markers), '' if len(self._markers) == 1 else 's',
nmarkers))
warnings.warn(w)
class Aifc_write:
# Variables used in this class:
#
# These variables are user settable through appropriate methods
# of this class:
# _file -- the open file with methods write(), close(), tell(), seek()
# set through the __init__() method
# _comptype -- the AIFF-C compression type ('NONE' in AIFF)
# set through the setcomptype() or setparams() method
# _compname -- the human-readable AIFF-C compression type
# set through the setcomptype() or setparams() method
# _nchannels -- the number of audio channels
# set through the setnchannels() or setparams() method
# _sampwidth -- the number of bytes per audio sample
# set through the setsampwidth() or setparams() method
# _framerate -- the sampling frequency
# set through the setframerate() or setparams() method
# _nframes -- the number of audio frames written to the header
# set through the setnframes() or setparams() method
# _aifc -- whether we're writing an AIFF-C file or an AIFF file
# set through the aifc() method, reset through the
# aiff() method
#
# These variables are used internally only:
# _version -- the AIFF-C version number
# _comp -- the compressor from builtin module cl
# _nframeswritten -- the number of audio frames actually written
# _datalength -- the size of the audio samples written to the header
# _datawritten -- the size of the audio samples actually written
_file = None # Set here since __del__ checks it
def __init__(self, f):
if isinstance(f, str):
file_object = builtins.open(f, 'wb')
try:
self.initfp(file_object)
except:
file_object.close()
raise
# treat .aiff file extensions as non-compressed audio
if f.endswith('.aiff'):
self._aifc = 0
else:
# assume it is an open file object already
self.initfp(f)
def initfp(self, file):
self._file = file
self._version = _AIFC_version
self._comptype = b'NONE'
self._compname = b'not compressed'
self._convert = None
self._nchannels = 0
self._sampwidth = 0
self._framerate = 0
self._nframes = 0
self._nframeswritten = 0
self._datawritten = 0
self._datalength = 0
self._markers = []
self._marklength = 0
self._aifc = 1 # AIFF-C is default
def __del__(self):
self.close()
def __enter__(self):
return self
def __exit__(self, *args):
self.close()
#
# User visible methods.
#
def aiff(self):
if self._nframeswritten:
raise Error('cannot change parameters after starting to write')
self._aifc = 0
def aifc(self):
if self._nframeswritten:
raise Error('cannot change parameters after starting to write')
self._aifc = 1
def setnchannels(self, nchannels):
if self._nframeswritten:
raise Error('cannot change parameters after starting to write')
if nchannels < 1:
raise Error('bad # of channels')
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 < 1 or sampwidth > 4:
raise Error('bad sample width')
self._sampwidth = sampwidth
def getsampwidth(self):
if not self._sampwidth:
raise Error('sample width not set')
return self._sampwidth
def setframerate(self, framerate):
if self._nframeswritten:
raise Error('cannot change parameters after starting to write')
if framerate <= 0:
raise Error('bad frame rate')
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')
self._nframes = nframes
def getnframes(self):
return self._nframeswritten
def setcomptype(self, comptype, compname):
if self._nframeswritten:
raise Error('cannot change parameters after starting to write')
if comptype not in (b'NONE', b'ulaw', b'ULAW',
b'alaw', b'ALAW', b'G722', b'sowt', b'SOWT'):
raise Error('unsupported compression type')
self._comptype = comptype
self._compname = compname
def getcomptype(self):
return self._comptype
def getcompname(self):
return self._compname
## def setversion(self, version):
## if self._nframeswritten:
## raise Error, 'cannot change parameters after starting to write'
## self._version = version
def setparams(self, params):
nchannels, sampwidth, framerate, nframes, comptype, compname = params
if self._nframeswritten:
raise Error('cannot change parameters after starting to write')
if comptype not in (b'NONE', b'ulaw', b'ULAW',
b'alaw', b'ALAW', b'G722', b'sowt', b'SOWT'):
raise Error('unsupported compression type')
self.setnchannels(nchannels)
self.setsampwidth(sampwidth)
self.setframerate(framerate)
self.setnframes(nframes)
self.setcomptype(comptype, compname)
def getparams(self):
if not self._nchannels or not self._sampwidth or not self._framerate:
raise Error('not all parameters set')
return _aifc_params(self._nchannels, self._sampwidth, self._framerate,
self._nframes, self._comptype, self._compname)
def setmark(self, id, pos, name):
if id <= 0:
raise Error('marker ID must be > 0')
if pos < 0:
raise Error('marker position must be >= 0')
if not isinstance(name, bytes):
raise Error('marker name must be bytes')
for i in range(len(self._markers)):
if id == self._markers[i][0]:
self._markers[i] = id, pos, name
return
self._markers.append((id, pos, name))
def getmark(self, id):
for marker in self._markers:
if id == marker[0]:
return marker
raise Error('marker {0!r} does not exist'.format(id))
def getmarkers(self):
if len(self._markers) == 0:
return None
return self._markers
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(len(data))
nframes = len(data) // (self._sampwidth * self._nchannels)
if self._convert:
data = self._convert(data)
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 is None:
return
try:
self._ensure_header_written(0)
if self._datawritten & 1:
# quick pad to even size
self._file.write(b'\x00')
self._datawritten = self._datawritten + 1
self._writemarkers()
if self._nframeswritten != self._nframes or \
self._datalength != self._datawritten or \
self._marklength:
self._patchheader()
finally:
# Prevent ref cycles
self._convert = None
f = self._file
self._file = None
f.close()
#
# Internal methods.
#
def _lin2alaw(self, data):
with warnings.catch_warnings():
warnings.simplefilter('ignore', category=DeprecationWarning)
import audioop
return audioop.lin2alaw(data, 2)
def _lin2ulaw(self, data):
with warnings.catch_warnings():
warnings.simplefilter('ignore', category=DeprecationWarning)
import audioop
return audioop.lin2ulaw(data, 2)
def _lin2adpcm(self, data):
with warnings.catch_warnings():
warnings.simplefilter('ignore', category=DeprecationWarning)
import audioop
if not hasattr(self, '_adpcmstate'):
self._adpcmstate = None
data, self._adpcmstate = audioop.lin2adpcm(data, 2, self._adpcmstate)
return data
def _lin2sowt(self, data):
with warnings.catch_warnings():
warnings.simplefilter('ignore', category=DeprecationWarning)
import audioop
return audioop.byteswap(data, 2)
def _ensure_header_written(self, datasize):
if not self._nframeswritten:
if self._comptype in (b'ULAW', b'ulaw',
b'ALAW', b'alaw', b'G722',
b'sowt', b'SOWT'):
if not self._sampwidth:
self._sampwidth = 2
if self._sampwidth != 2:
raise Error('sample width must be 2 when compressing '
'with ulaw/ULAW, alaw/ALAW, sowt/SOWT '
'or G7.22 (ADPCM)')
if not self._nchannels:
raise Error('# channels not specified')
if not self._sampwidth:
raise Error('sample width not specified')
if not self._framerate:
raise Error('sampling rate not specified')
self._write_header(datasize)
def _init_compression(self):
if self._comptype == b'G722':
self._convert = self._lin2adpcm
elif self._comptype in (b'ulaw', b'ULAW'):
self._convert = self._lin2ulaw
elif self._comptype in (b'alaw', b'ALAW'):
self._convert = self._lin2alaw
elif self._comptype in (b'sowt', b'SOWT'):
self._convert = self._lin2sowt
def _write_header(self, initlength):
if self._aifc and self._comptype != b'NONE':
self._init_compression()
self._file.write(b'FORM')
if not self._nframes:
self._nframes = initlength // (self._nchannels * self._sampwidth)
self._datalength = self._nframes * self._nchannels * self._sampwidth
if self._datalength & 1:
self._datalength = self._datalength + 1
if self._aifc:
if self._comptype in (b'ulaw', b'ULAW', b'alaw', b'ALAW'):
self._datalength = self._datalength // 2
if self._datalength & 1:
self._datalength = self._datalength + 1
elif self._comptype == b'G722':
self._datalength = (self._datalength + 3) // 4
if self._datalength & 1:
self._datalength = self._datalength + 1
try:
self._form_length_pos = self._file.tell()
except (AttributeError, OSError):
self._form_length_pos = None
commlength = self._write_form_length(self._datalength)
if self._aifc:
self._file.write(b'AIFC')
self._file.write(b'FVER')
_write_ulong(self._file, 4)
_write_ulong(self._file, self._version)
else:
self._file.write(b'AIFF')
self._file.write(b'COMM')
_write_ulong(self._file, commlength)
_write_short(self._file, self._nchannels)
if self._form_length_pos is not None:
self._nframes_pos = self._file.tell()
_write_ulong(self._file, self._nframes)
if self._comptype in (b'ULAW', b'ulaw', b'ALAW', b'alaw', b'G722'):
_write_short(self._file, 8)
else:
_write_short(self._file, self._sampwidth * 8)
_write_float(self._file, self._framerate)
if self._aifc:
self._file.write(self._comptype)
_write_string(self._file, self._compname)
self._file.write(b'SSND')
if self._form_length_pos is not None:
self._ssnd_length_pos = self._file.tell()
_write_ulong(self._file, self._datalength + 8)
_write_ulong(self._file, 0)
_write_ulong(self._file, 0)
def _write_form_length(self, datalength):
if self._aifc:
commlength = 18 + 5 + len(self._compname)
if commlength & 1:
commlength = commlength + 1
verslength = 12
else:
commlength = 18
verslength = 0
_write_ulong(self._file, 4 + verslength + self._marklength + \
8 + commlength + 16 + datalength)
return commlength
def _patchheader(self):
curpos = self._file.tell()
if self._datawritten & 1:
datalength = self._datawritten + 1
self._file.write(b'\x00')
else:
datalength = self._datawritten
if datalength == self._datalength and \
self._nframes == self._nframeswritten and \
self._marklength == 0:
self._file.seek(curpos, 0)
return
self._file.seek(self._form_length_pos, 0)
dummy = self._write_form_length(datalength)
self._file.seek(self._nframes_pos, 0)
_write_ulong(self._file, self._nframeswritten)
self._file.seek(self._ssnd_length_pos, 0)
_write_ulong(self._file, datalength + 8)
self._file.seek(curpos, 0)
self._nframes = self._nframeswritten
self._datalength = datalength
def _writemarkers(self):
if len(self._markers) == 0:
return
self._file.write(b'MARK')
length = 2
for marker in self._markers:
id, pos, name = marker
length = length + len(name) + 1 + 6
if len(name) & 1 == 0:
length = length + 1
_write_ulong(self._file, length)
self._marklength = length + 8
_write_short(self._file, len(self._markers))
for marker in self._markers:
id, pos, name = marker
_write_short(self._file, id)
_write_ulong(self._file, pos)
_write_string(self._file, name)
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 Aifc_read(f)
elif mode in ('w', 'wb'):
return Aifc_write(f)
else:
raise Error("mode must be 'r', 'rb', 'w', or 'wb'")
if __name__ == '__main__':
import sys
if not sys.argv[1:]:
sys.argv.append('/usr/demos/data/audio/bach.aiff')
fn = sys.argv[1]
with open(fn, 'r') as f:
print("Reading", fn)
print("nchannels =", f.getnchannels())
print("nframes =", f.getnframes())
print("sampwidth =", f.getsampwidth())
print("framerate =", f.getframerate())
print("comptype =", f.getcomptype())
print("compname =", f.getcompname())
if sys.argv[2:]:
gn = sys.argv[2]
print("Writing", gn)
with open(gn, 'w') as g:
g.setparams(f.getparams())
while 1:
data = f.readframes(1024)
if not data:
break
g.writeframes(data)
print("Done.")

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -1,439 +0,0 @@
from test.support import findfile
from test.support.os_helper import TESTFN, unlink
from test.support.warnings_helper import check_no_resource_warning, import_deprecated
import unittest
from unittest import mock
from test import audiotests
import io
import sys
import struct
aifc = import_deprecated("aifc")
audioop = import_deprecated("audioop")
class AifcTest(audiotests.AudioWriteTests,
audiotests.AudioTestsWithSourceFile):
module = aifc
close_fd = True
test_unseekable_read = None
class AifcPCM8Test(AifcTest, unittest.TestCase):
sndfilename = 'pluck-pcm8.aiff'
sndfilenframes = 3307
nchannels = 2
sampwidth = 1
framerate = 11025
nframes = 48
comptype = b'NONE'
compname = b'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 AifcPCM16Test(AifcTest, unittest.TestCase):
sndfilename = 'pluck-pcm16.aiff'
sndfilenframes = 3307
nchannels = 2
sampwidth = 2
framerate = 11025
nframes = 48
comptype = b'NONE'
compname = b'not compressed'
frames = bytes.fromhex("""\
022EFFEA 4B5D00F6 311804EA 80E10840 CBE106B1 48A903F5 BFE601B2 036CFE7B \
B858FA3E B4B1F34F 299AEBCA 1A5DE6DA EDFAE491 C628E275 0E09E0B5 EF2AE029 \
5758E271 FB35E83F 1376EF86 D82BF727 9790FB76 F5FAFC0F 0867FB9C DF30FB43 \
117EFA36 3EE5FB5B BC79FCB1 66D9FF5D CF150412 431D097C C1BA0EC8 512112A1 \
EEE21753 82071665 7FFF1443 8004128F 49A20EAF 52BB0DBA EFB40F60 CE3C0FBF \
E4B30CEC 63430A5C 08C80A20 2BBB0B08 514A0E43 8BCF1139 B6F60EEB 44120A5E \
""")
class AifcPCM24Test(AifcTest, unittest.TestCase):
sndfilename = 'pluck-pcm24.aiff'
sndfilenframes = 3307
nchannels = 2
sampwidth = 3
framerate = 11025
nframes = 48
comptype = b'NONE'
compname = b'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 AifcPCM32Test(AifcTest, unittest.TestCase):
sndfilename = 'pluck-pcm32.aiff'
sndfilenframes = 3307
nchannels = 2
sampwidth = 4
framerate = 11025
nframes = 48
comptype = b'NONE'
compname = b'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 AifcULAWTest(AifcTest, unittest.TestCase):
sndfilename = 'pluck-ulaw.aifc'
sndfilenframes = 3307
nchannels = 2
sampwidth = 2
framerate = 11025
nframes = 48
comptype = b'ulaw'
compname = b''
frames = bytes.fromhex("""\
022CFFE8 497C0104 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 417C093C 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 AifcALAWTest(AifcTest, unittest.TestCase):
sndfilename = 'pluck-alaw.aifc'
sndfilenframes = 3307
nchannels = 2
sampwidth = 2
framerate = 11025
nframes = 48
comptype = b'alaw'
compname = b''
frames = bytes.fromhex("""\
0230FFE8 4A0000F8 310004E0 82000840 CB0006A0 4A0003F0 BE0001A8 0370FE78 \
BA00FA20 B600F340 2900EB80 1A80E680 ED80E480 C700E280 0E40E080 EF80E080 \
5600E280 FB20E880 1380EF80 D900F740 9600FB60 F5C0FC10 0840FBA0 DF00FB20 \
1180FA20 3F00FB60 BE00FCB0 6600FF58 CF000420 42000940 C1000EC0 52001280 \
EE801780 82001680 7E001480 82001280 4A000EC0 52000DC0 EF800F40 CF000FC0 \
E4800CC0 62000A40 08C00A40 2B000B40 52000E40 8A001180 B6000EC0 46000A40 \
""")
if sys.byteorder != 'big':
frames = audioop.byteswap(frames, 2)
class AifcMiscTest(unittest.TestCase):
def test_skipunknown(self):
#Issue 2245
#This file contains chunk types aifc doesn't recognize.
f = aifc.open(findfile('Sine-1000Hz-300ms.aif'))
f.close()
def test_close_opened_files_on_error(self):
non_aifc_file = findfile('pluck-pcm8.wav', subdir='audiodata')
with check_no_resource_warning(self):
with self.assertRaises(aifc.Error):
# Try opening a non-AIFC file, with the expectation that
# `aifc.open` will fail (without raising a ResourceWarning)
self.f = aifc.open(non_aifc_file, 'rb')
# Aifc_write.initfp() won't raise in normal case. But some errors
# (e.g. MemoryError, KeyboardInterrupt, etc..) can happen.
with mock.patch.object(aifc.Aifc_write, 'initfp',
side_effect=RuntimeError):
with self.assertRaises(RuntimeError):
self.fout = aifc.open(TESTFN, 'wb')
def test_params_added(self):
f = self.f = aifc.open(TESTFN, 'wb')
f.aiff()
f.setparams((1, 1, 1, 1, b'NONE', b''))
f.close()
f = aifc.open(TESTFN, 'rb')
self.addCleanup(f.close)
params = f.getparams()
self.assertEqual(params.nchannels, f.getnchannels())
self.assertEqual(params.sampwidth, f.getsampwidth())
self.assertEqual(params.framerate, f.getframerate())
self.assertEqual(params.nframes, f.getnframes())
self.assertEqual(params.comptype, f.getcomptype())
self.assertEqual(params.compname, f.getcompname())
def test_write_header_comptype_sampwidth(self):
for comptype in (b'ULAW', b'ulaw', b'ALAW', b'alaw', b'G722'):
fout = aifc.open(io.BytesIO(), 'wb')
fout.setnchannels(1)
fout.setframerate(1)
fout.setcomptype(comptype, b'')
fout.close()
self.assertEqual(fout.getsampwidth(), 2)
fout.initfp(None)
def test_write_markers_values(self):
fout = aifc.open(io.BytesIO(), 'wb')
self.assertEqual(fout.getmarkers(), None)
fout.setmark(1, 0, b'foo1')
fout.setmark(1, 1, b'foo2')
self.assertEqual(fout.getmark(1), (1, 1, b'foo2'))
self.assertEqual(fout.getmarkers(), [(1, 1, b'foo2')])
fout.initfp(None)
def test_read_markers(self):
fout = self.fout = aifc.open(TESTFN, 'wb')
fout.aiff()
fout.setparams((1, 1, 1, 1, b'NONE', b''))
fout.setmark(1, 0, b'odd')
fout.setmark(2, 0, b'even')
fout.writeframes(b'\x00')
fout.close()
f = aifc.open(TESTFN, 'rb')
self.addCleanup(f.close)
self.assertEqual(f.getmarkers(), [(1, 0, b'odd'), (2, 0, b'even')])
self.assertEqual(f.getmark(1), (1, 0, b'odd'))
self.assertEqual(f.getmark(2), (2, 0, b'even'))
self.assertRaises(aifc.Error, f.getmark, 3)
class AIFCLowLevelTest(unittest.TestCase):
def test_read_written(self):
def read_written(self, what):
f = io.BytesIO()
getattr(aifc, '_write_' + what)(f, x)
f.seek(0)
return getattr(aifc, '_read_' + what)(f)
for x in (-1, 0, 0.1, 1):
self.assertEqual(read_written(x, 'float'), x)
for x in (float('NaN'), float('Inf')):
self.assertEqual(read_written(x, 'float'), aifc._HUGE_VAL)
for x in (b'', b'foo', b'a' * 255):
self.assertEqual(read_written(x, 'string'), x)
for x in (-0x7FFFFFFF, -1, 0, 1, 0x7FFFFFFF):
self.assertEqual(read_written(x, 'long'), x)
for x in (0, 1, 0xFFFFFFFF):
self.assertEqual(read_written(x, 'ulong'), x)
for x in (-0x7FFF, -1, 0, 1, 0x7FFF):
self.assertEqual(read_written(x, 'short'), x)
for x in (0, 1, 0xFFFF):
self.assertEqual(read_written(x, 'ushort'), x)
def test_read_raises(self):
f = io.BytesIO(b'\x00')
self.assertRaises(EOFError, aifc._read_ulong, f)
self.assertRaises(EOFError, aifc._read_long, f)
self.assertRaises(EOFError, aifc._read_ushort, f)
self.assertRaises(EOFError, aifc._read_short, f)
def test_write_long_string_raises(self):
f = io.BytesIO()
with self.assertRaises(ValueError):
aifc._write_string(f, b'too long' * 255)
def test_wrong_open_mode(self):
with self.assertRaises(aifc.Error):
aifc.open(TESTFN, 'wrong_mode')
def test_read_wrong_form(self):
b1 = io.BytesIO(b'WRNG' + struct.pack('>L', 0))
b2 = io.BytesIO(b'FORM' + struct.pack('>L', 4) + b'WRNG')
self.assertRaises(aifc.Error, aifc.open, b1)
self.assertRaises(aifc.Error, aifc.open, b2)
def test_read_no_comm_chunk(self):
b = io.BytesIO(b'FORM' + struct.pack('>L', 4) + b'AIFF')
self.assertRaises(aifc.Error, aifc.open, b)
def test_read_no_ssnd_chunk(self):
b = b'FORM' + struct.pack('>L', 4) + b'AIFC'
b += b'COMM' + struct.pack('>LhlhhLL', 38, 1, 0, 8,
0x4000 | 12, 11025<<18, 0)
b += b'NONE' + struct.pack('B', 14) + b'not compressed' + b'\x00'
with self.assertRaisesRegex(aifc.Error, 'COMM chunk and/or SSND chunk'
' missing'):
aifc.open(io.BytesIO(b))
def test_read_wrong_compression_type(self):
b = b'FORM' + struct.pack('>L', 4) + b'AIFC'
b += b'COMM' + struct.pack('>LhlhhLL', 23, 1, 0, 8,
0x4000 | 12, 11025<<18, 0)
b += b'WRNG' + struct.pack('B', 0)
self.assertRaises(aifc.Error, aifc.open, io.BytesIO(b))
def test_read_wrong_number_of_channels(self):
for nchannels in 0, -1:
b = b'FORM' + struct.pack('>L', 4) + b'AIFC'
b += b'COMM' + struct.pack('>LhlhhLL', 38, nchannels, 0, 8,
0x4000 | 12, 11025<<18, 0)
b += b'NONE' + struct.pack('B', 14) + b'not compressed' + b'\x00'
b += b'SSND' + struct.pack('>L', 8) + b'\x00' * 8
with self.assertRaisesRegex(aifc.Error, 'bad # of channels'):
aifc.open(io.BytesIO(b))
def test_read_wrong_sample_width(self):
for sampwidth in 0, -1:
b = b'FORM' + struct.pack('>L', 4) + b'AIFC'
b += b'COMM' + struct.pack('>LhlhhLL', 38, 1, 0, sampwidth,
0x4000 | 12, 11025<<18, 0)
b += b'NONE' + struct.pack('B', 14) + b'not compressed' + b'\x00'
b += b'SSND' + struct.pack('>L', 8) + b'\x00' * 8
with self.assertRaisesRegex(aifc.Error, 'bad sample width'):
aifc.open(io.BytesIO(b))
def test_read_wrong_marks(self):
b = b'FORM' + struct.pack('>L', 4) + b'AIFF'
b += b'COMM' + struct.pack('>LhlhhLL', 18, 1, 0, 8,
0x4000 | 12, 11025<<18, 0)
b += b'SSND' + struct.pack('>L', 8) + b'\x00' * 8
b += b'MARK' + struct.pack('>LhB', 3, 1, 1)
with self.assertWarns(UserWarning) as cm:
f = aifc.open(io.BytesIO(b))
self.assertEqual(str(cm.warning), 'Warning: MARK chunk contains '
'only 0 markers instead of 1')
self.assertEqual(f.getmarkers(), None)
def test_read_comm_kludge_compname_even(self):
b = b'FORM' + struct.pack('>L', 4) + b'AIFC'
b += b'COMM' + struct.pack('>LhlhhLL', 18, 1, 0, 8,
0x4000 | 12, 11025<<18, 0)
b += b'NONE' + struct.pack('B', 4) + b'even' + b'\x00'
b += b'SSND' + struct.pack('>L', 8) + b'\x00' * 8
with self.assertWarns(UserWarning) as cm:
f = aifc.open(io.BytesIO(b))
self.assertEqual(str(cm.warning), 'Warning: bad COMM chunk size')
self.assertEqual(f.getcompname(), b'even')
def test_read_comm_kludge_compname_odd(self):
b = b'FORM' + struct.pack('>L', 4) + b'AIFC'
b += b'COMM' + struct.pack('>LhlhhLL', 18, 1, 0, 8,
0x4000 | 12, 11025<<18, 0)
b += b'NONE' + struct.pack('B', 3) + b'odd'
b += b'SSND' + struct.pack('>L', 8) + b'\x00' * 8
with self.assertWarns(UserWarning) as cm:
f = aifc.open(io.BytesIO(b))
self.assertEqual(str(cm.warning), 'Warning: bad COMM chunk size')
self.assertEqual(f.getcompname(), b'odd')
def test_write_params_raises(self):
fout = aifc.open(io.BytesIO(), 'wb')
wrong_params = (0, 0, 0, 0, b'WRNG', '')
self.assertRaises(aifc.Error, fout.setparams, wrong_params)
self.assertRaises(aifc.Error, fout.getparams)
self.assertRaises(aifc.Error, fout.setnchannels, 0)
self.assertRaises(aifc.Error, fout.getnchannels)
self.assertRaises(aifc.Error, fout.setsampwidth, 0)
self.assertRaises(aifc.Error, fout.getsampwidth)
self.assertRaises(aifc.Error, fout.setframerate, 0)
self.assertRaises(aifc.Error, fout.getframerate)
self.assertRaises(aifc.Error, fout.setcomptype, b'WRNG', '')
fout.aiff()
fout.setnchannels(1)
fout.setsampwidth(1)
fout.setframerate(1)
fout.setnframes(1)
fout.writeframes(b'\x00')
self.assertRaises(aifc.Error, fout.setparams, (1, 1, 1, 1, 1, 1))
self.assertRaises(aifc.Error, fout.setnchannels, 1)
self.assertRaises(aifc.Error, fout.setsampwidth, 1)
self.assertRaises(aifc.Error, fout.setframerate, 1)
self.assertRaises(aifc.Error, fout.setnframes, 1)
self.assertRaises(aifc.Error, fout.setcomptype, b'NONE', '')
self.assertRaises(aifc.Error, fout.aiff)
self.assertRaises(aifc.Error, fout.aifc)
def test_write_params_singles(self):
fout = aifc.open(io.BytesIO(), 'wb')
fout.aifc()
fout.setnchannels(1)
fout.setsampwidth(2)
fout.setframerate(3)
fout.setnframes(4)
fout.setcomptype(b'NONE', b'name')
self.assertEqual(fout.getnchannels(), 1)
self.assertEqual(fout.getsampwidth(), 2)
self.assertEqual(fout.getframerate(), 3)
self.assertEqual(fout.getnframes(), 0)
self.assertEqual(fout.tell(), 0)
self.assertEqual(fout.getcomptype(), b'NONE')
self.assertEqual(fout.getcompname(), b'name')
fout.writeframes(b'\x00' * 4 * fout.getsampwidth() * fout.getnchannels())
self.assertEqual(fout.getnframes(), 4)
self.assertEqual(fout.tell(), 4)
def test_write_params_bunch(self):
fout = aifc.open(io.BytesIO(), 'wb')
fout.aifc()
p = (1, 2, 3, 4, b'NONE', b'name')
fout.setparams(p)
self.assertEqual(fout.getparams(), p)
fout.initfp(None)
def test_write_header_raises(self):
fout = aifc.open(io.BytesIO(), 'wb')
self.assertRaises(aifc.Error, fout.close)
fout = aifc.open(io.BytesIO(), 'wb')
fout.setnchannels(1)
self.assertRaises(aifc.Error, fout.close)
fout = aifc.open(io.BytesIO(), 'wb')
fout.setnchannels(1)
fout.setsampwidth(1)
self.assertRaises(aifc.Error, fout.close)
def test_write_header_comptype_raises(self):
for comptype in (b'ULAW', b'ulaw', b'ALAW', b'alaw', b'G722'):
fout = aifc.open(io.BytesIO(), 'wb')
fout.setsampwidth(1)
fout.setcomptype(comptype, b'')
self.assertRaises(aifc.Error, fout.close)
fout.initfp(None)
def test_write_markers_raises(self):
fout = aifc.open(io.BytesIO(), 'wb')
self.assertRaises(aifc.Error, fout.setmark, 0, 0, b'')
self.assertRaises(aifc.Error, fout.setmark, 1, -1, b'')
self.assertRaises(aifc.Error, fout.setmark, 1, 0, None)
self.assertRaises(aifc.Error, fout.getmark, 1)
fout.initfp(None)
def test_write_aiff_by_extension(self):
sampwidth = 2
filename = TESTFN + '.aiff'
fout = self.fout = aifc.open(filename, 'wb')
self.addCleanup(unlink, filename)
fout.setparams((1, sampwidth, 1, 1, b'ULAW', b''))
frames = b'\x00' * fout.getnchannels() * sampwidth
fout.writeframes(frames)
fout.close()
f = self.f = aifc.open(filename, 'rb')
self.assertEqual(f.getcomptype(), b'NONE')
f.close()
if __name__ == "__main__":
unittest.main()

View file

@ -21,9 +21,9 @@ This returns an instance of a class with the following public methods:
getparams() -- returns a namedtuple consisting of all of the getparams() -- returns a namedtuple consisting of all of the
above in the above order above in the above order
getmarkers() -- returns None (for compatibility with the getmarkers() -- returns None (for compatibility with the
aifc module) old aifc module)
getmark(id) -- raises an error since the mark does not getmark(id) -- raises an error since the mark does not
exist (for compatibility with the aifc module) exist (for compatibility with the old aifc module)
readframes(n) -- returns at most n frames of audio readframes(n) -- returns at most n frames of audio
rewind() -- rewind to the beginning of the audio stream rewind() -- rewind to the beginning of the audio stream
setpos(pos) -- seek to the specified position setpos(pos) -- seek to the specified position

View file

@ -325,7 +325,7 @@ documentation.
.. section: Library .. section: Library
Improved exceptions raised for invalid number of channels and sample width Improved exceptions raised for invalid number of channels and sample width
when read an audio file in modules :mod:`aifc`, :mod:`wave` and when read an audio file in modules :mod:`!aifc`, :mod:`wave` and
:mod:`!sunau`. :mod:`!sunau`.
.. ..

View file

@ -5576,7 +5576,7 @@ documentation.
.. section: Library .. section: Library
Improved exceptions raised for invalid number of channels and sample width Improved exceptions raised for invalid number of channels and sample width
when read an audio file in modules :mod:`aifc`, :mod:`wave` and when read an audio file in modules :mod:`!aifc`, :mod:`wave` and
:mod:`!sunau`. :mod:`!sunau`.
.. ..

View file

@ -0,0 +1,2 @@
:pep:`594`: Remove the :mod:`!aifc` module, deprecated in Python 3.11. Patch
by Victor Stinner.

View file

@ -89,7 +89,6 @@ static const char* _Py_stdlib_module_names[] = {
"_winapi", "_winapi",
"_zoneinfo", "_zoneinfo",
"abc", "abc",
"aifc",
"antigravity", "antigravity",
"argparse", "argparse",
"array", "array",

View file

@ -78,7 +78,7 @@ OMIT_NETWORKING_FILES = (
OMIT_MODULE_FILES = { OMIT_MODULE_FILES = {
"_asyncio": ["asyncio/"], "_asyncio": ["asyncio/"],
"audioop": ["aifc.py", "wave.py"], "audioop": ["wave.py"],
"_curses": ["curses/"], "_curses": ["curses/"],
"_ctypes": ["ctypes/"], "_ctypes": ["ctypes/"],
"_decimal": ["decimal.py"], "_decimal": ["decimal.py"],