mirror of
https://github.com/python/cpython.git
synced 2025-07-19 09:15:34 +00:00
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:
parent
076b6204cb
commit
036da3bd43
25 changed files with 20 additions and 1688 deletions
|
@ -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.
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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` |
|
||||||
+---------------------+---------------------+---------------------+---------------------+---------------------+
|
+---------------------+---------------------+---------------------+---------------------+---------------------+
|
||||||
|
|
|
@ -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`
|
||||||
|
|
|
@ -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
|
||||||
======================
|
======================
|
||||||
|
|
|
@ -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`.)
|
||||||
|
|
||||||
|
|
|
@ -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`.)
|
||||||
|
|
||||||
|
|
||||||
|
|
984
Lib/aifc.py
984
Lib/aifc.py
|
@ -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.
|
@ -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()
|
|
|
@ -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
|
||||||
|
|
|
@ -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`.
|
||||||
|
|
||||||
..
|
..
|
||||||
|
|
|
@ -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`.
|
||||||
|
|
||||||
..
|
..
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
:pep:`594`: Remove the :mod:`!aifc` module, deprecated in Python 3.11. Patch
|
||||||
|
by Victor Stinner.
|
1
Python/stdlib_module_names.h
generated
1
Python/stdlib_module_names.h
generated
|
@ -89,7 +89,6 @@ static const char* _Py_stdlib_module_names[] = {
|
||||||
"_winapi",
|
"_winapi",
|
||||||
"_zoneinfo",
|
"_zoneinfo",
|
||||||
"abc",
|
"abc",
|
||||||
"aifc",
|
|
||||||
"antigravity",
|
"antigravity",
|
||||||
"argparse",
|
"argparse",
|
||||||
"array",
|
"array",
|
||||||
|
|
|
@ -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"],
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue