Compare commits
84 Commits
Author | SHA1 | Date |
---|---|---|
Derek | f749f2187e | |
Derek | 2ee560056e | |
Hubert Pham | 7090e25bcb | |
Hubert Pham | 045ebc7767 | |
Hubert Pham | 653fdfeb87 | |
Hubert Pham | 281a8f32e7 | |
Hubert Pham | 39de78a74a | |
Hubert Pham | 2e696d98c0 | |
Hubert Pham | 833ebc0b14 | |
Hubert Pham | fe528b2050 | |
Hubert Pham | 8285b9bc5c | |
Hubert Pham | a0f6d13628 | |
Hubert Pham | 47a80684e1 | |
Hubert Pham | 439327bcd7 | |
Hubert Pham | c9bc0193b0 | |
Hubert Pham | 884cedae7f | |
Hubert Pham | 50e08d41d4 | |
Hubert Pham | 7a61080270 | |
Hubert Pham | e59fa4a24e | |
Hubert Pham | 870016f6d1 | |
Hubert Pham | bff409df19 | |
Hubert Pham | da8c238eee | |
Hubert Pham | dce064b428 | |
Hubert Pham | ab5e5b775e | |
Hubert Pham | 5a4da7d870 | |
Hubert Pham | 0e5eb7cb9d | |
Hubert Pham | 3e6adb4588 | |
Hubert Pham | d786cc59a2 | |
Hubert Pham | ebea06b12c | |
Hubert Pham | 1783aaf9bc | |
Hubert Pham | 53ab5eb107 | |
Hubert Pham | fc7bd1d2b0 | |
Hubert Pham | 7cefbc5718 | |
Hubert Pham | 7ec435d961 | |
Hubert Pham | f1abe26c96 | |
Christoph Gohlke | b9b1cb5a71 | |
Hubert Pham | a0bdcab7b3 | |
Hubert Pham | 9459d994b1 | |
Hubert Pham | ed830f7b5b | |
Hubert Pham | 949d4b7271 | |
Hubert Pham | 930a5cd04b | |
Hubert Pham | 2953234b0b | |
Hubert Pham | 806d8623e9 | |
Hubert Pham | 72a9bd1dd2 | |
Hubert Pham | 9136083440 | |
Hubert Pham | 1cb6b8c1f6 | |
Hubert Pham | d7bad725ed | |
Hubert Pham | 39c41c8cae | |
Hubert Pham | 679c1c2ead | |
Hubert Pham | 34e54db968 | |
Hubert Pham | 04f2db1222 | |
Hubert Pham | d35ff7a829 | |
Hubert Pham | f3f8c5e2cb | |
Hubert Pham | b6736b5375 | |
Hubert Pham | 27424b6045 | |
Hubert Pham | bcbfcd8ab5 | |
Hubert Pham | 73cc135bbc | |
Hubert Pham | b087335fdf | |
Hubert Pham | 5a547e0cd1 | |
Hubert Pham | 801b8a21f8 | |
Hubert Pham | a271c17c54 | |
Hubert Pham | ab339f8352 | |
Hubert Pham | 1615e4ce80 | |
Hubert Pham | 1125da6f99 | |
Hubert Pham | ba10e04d1e | |
Hubert Pham | 9de8018304 | |
Hubert Pham | 1bc6abb9b6 | |
Hubert Pham | 4036322c68 | |
Hubert Pham | 4287326f36 | |
Hubert Pham | 51eafbef69 | |
Hubert Pham | 3d3c57ffbe | |
Hubert Pham | 9a99f93bb0 | |
Hubert Pham | eedfb3dd8f | |
Hubert Pham | ca85fd0d5f | |
Hubert Pham | 2a8d011698 | |
Hubert Pham | 6f9efe0ba3 | |
Hubert Pham | 742b3b3781 | |
Hubert Pham | ab8c3259fa | |
Hubert Pham | b6d5593a2b | |
Hubert Pham | 1fb9e8db6c | |
Hubert Pham | 3f5bc25c72 | |
Hubert Pham | 3f55f344a0 | |
Hubert Pham | 3b8fdb72e7 | |
Hubert Pham | ed2bbdfc9a |
105
CHANGELOG
105
CHANGELOG
|
@ -1,3 +1,107 @@
|
||||||
|
2017-03-18 Hubert Pham <hubert@mit.edu>
|
||||||
|
|
||||||
|
PyAudio 0.2.11
|
||||||
|
|
||||||
|
- Fix use-after-free memory issue in callback handler.
|
||||||
|
|
||||||
|
Thanks to both Blaise Potard and Matthias Schaff for their patches!
|
||||||
|
|
||||||
|
- Fix docstring for get_output_latency().
|
||||||
|
|
||||||
|
Thanks to Timothy Port for finding the issue!
|
||||||
|
|
||||||
|
2017-01-10 Hubert Pham <hubert@mit.edu>
|
||||||
|
|
||||||
|
PyAudio 0.2.10
|
||||||
|
|
||||||
|
- Release the GIL during PortAudio I/O calls to avoid potential deadlock.
|
||||||
|
|
||||||
|
Thanks to Michael Graczyk for submitting a patch!
|
||||||
|
|
||||||
|
- Add a few automated unit tests.
|
||||||
|
|
||||||
|
2015-10-18 Hubert Pham <hubert@mit.edu>
|
||||||
|
|
||||||
|
PyAudio 0.2.9
|
||||||
|
|
||||||
|
- Fix overflow error handling logic for pa_read_stream.
|
||||||
|
|
||||||
|
Stream.read takes an additional parameter that specifies whether
|
||||||
|
an exception is raised on audio buffer overflow, for parity with
|
||||||
|
Stream.write. Includes relevant bug fixes in the C module logic.
|
||||||
|
|
||||||
|
Thanks to Tony Jacobson for submitting a patch!
|
||||||
|
|
||||||
|
- Fix IOError arguments.
|
||||||
|
|
||||||
|
IOError exceptions previously had values in the strerror and errno fields
|
||||||
|
swapped, which is now corrected.
|
||||||
|
|
||||||
|
Thanks to Sami Liedes for the report!
|
||||||
|
|
||||||
|
- Miscellaneous updates.
|
||||||
|
|
||||||
|
Python library surfaces issues with importing low-level C module.
|
||||||
|
Code formatting update.
|
||||||
|
Updates to examples for Python 3 compatibility.
|
||||||
|
|
||||||
|
2014-02-16 Hubert Pham <hubert@mit.edu>
|
||||||
|
|
||||||
|
PyAudio 0.2.8
|
||||||
|
|
||||||
|
- Device names: support non-UTF8 encoded device names.
|
||||||
|
|
||||||
|
get_device_info_by_index() now attempts to decode the device name
|
||||||
|
using a few known encodings (defaults to UTF-8 and CP1252). If
|
||||||
|
those fail, PyAudio passes the raw bytes for the device name.
|
||||||
|
Previously, PyAudio assumed a UTF-8 encoding, which is not always
|
||||||
|
true.
|
||||||
|
|
||||||
|
- Callback-mode: fix deadlock on some platforms when calling
|
||||||
|
pa.stop_stream.
|
||||||
|
|
||||||
|
Thanks to Jason Roehm for this patch!
|
||||||
|
|
||||||
|
2012-10-20 Hubert Pham <hubert@mit.edu>
|
||||||
|
|
||||||
|
PyAudio 0.2.7
|
||||||
|
|
||||||
|
- Callback-mode: support callables.
|
||||||
|
|
||||||
|
Thanks to John Luebs and Bastian Bechtold for this patch.
|
||||||
|
|
||||||
|
- Update documentation to use Sphinx.
|
||||||
|
|
||||||
|
Thanks again to Bastian Bechtold for his incredible contribution!
|
||||||
|
|
||||||
|
|
||||||
|
2012-09-01 Hubert Pham <hubert@mit.edu>
|
||||||
|
|
||||||
|
PyAudio 0.2.6
|
||||||
|
|
||||||
|
- Added support for Python 3. As of this update, PyAudio is
|
||||||
|
compatible with Python 2.6, Python 2.7, and Python 3.2.
|
||||||
|
|
||||||
|
Many thanks to Bastian Bechtold and Bob Jamison for their patches!
|
||||||
|
|
||||||
|
- Fixed a bug in which a list could be modified during iteration.
|
||||||
|
|
||||||
|
Many thanks to Danilo J. S. Bellini for reporting this error!
|
||||||
|
|
||||||
|
- Fixed a memory bug involving Mac OS X channel maps.
|
||||||
|
|
||||||
|
|
||||||
|
2012-09-01 Hubert Pham <hubert@mit.edu>
|
||||||
|
|
||||||
|
PyAudio 0.2.5
|
||||||
|
|
||||||
|
- Added support for callback (non-blocking) operation.
|
||||||
|
|
||||||
|
Many thanks to Bastian Bechtold for his initial contribution and
|
||||||
|
his generous help towards releasing this feature. Callback mode
|
||||||
|
would not have happened without Bastian's help!
|
||||||
|
|
||||||
|
|
||||||
2010-08-12 Hubert Pham <hubert@mit.edu>
|
2010-08-12 Hubert Pham <hubert@mit.edu>
|
||||||
|
|
||||||
PyAudio 0.2.4
|
PyAudio 0.2.4
|
||||||
|
@ -39,4 +143,3 @@
|
||||||
2008-02-12 Justin Mazzola Paluska <jmp@mit.edu>
|
2008-02-12 Justin Mazzola Paluska <jmp@mit.edu>
|
||||||
|
|
||||||
- Initial version of debian packaging.
|
- Initial version of debian packaging.
|
||||||
|
|
||||||
|
|
96
INSTALL
96
INSTALL
|
@ -8,81 +8,83 @@ platforms:
|
||||||
* General UNIX Guide: (GNU/Linux, Mac OS X, Cygwin)
|
* General UNIX Guide: (GNU/Linux, Mac OS X, Cygwin)
|
||||||
* Microsoft Windows (native)
|
* Microsoft Windows (native)
|
||||||
|
|
||||||
Generally speaking, you must first install the PortAudio v19 library
|
Generally speaking, installation involves building the PortAudio v19
|
||||||
before building PyAudio.
|
library and then building PyAudio.
|
||||||
|
|
||||||
|
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
General UNIX Guide (GNU/Linux, Mac OS X, Cygwin)
|
General UNIX Guide (GNU/Linux, Mac OS X, Cygwin)
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
1. Build and install PortAudio, i.e.:
|
1. Use a package manager to install PortAudio v19.
|
||||||
|
|
||||||
|
To build PortAudio from source instead, extract the source and run:
|
||||||
|
|
||||||
% ./configure
|
% ./configure
|
||||||
% make
|
% make
|
||||||
% make install # you may need to be root
|
% make install # you may need to be root
|
||||||
|
|
||||||
(Or better yet, use your package manager to install PortAudio v19)
|
2. Extract PyAudio. To build and install, run:
|
||||||
|
|
||||||
2. Extract PyAudio; to build and install, run:
|
|
||||||
|
|
||||||
% python setup.py install
|
% python setup.py install
|
||||||
|
|
||||||
|
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
Microsoft Windows
|
Microsoft Windows
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
If you are targeting native Win32 Python, you will need either
|
Targeting native Win32 Python will require either Microsoft Visual
|
||||||
Microsoft Visual Studio or MinGW (via Cygwin). Here are compilation
|
Studio or MinGW (via Cygwin). Here are compilation hints for using
|
||||||
hints for using MinGW under the Cygwin build environment.
|
MinGW under the Cygwin build environment.
|
||||||
|
|
||||||
Note: I've only tested this under Cygwin's build environment. Your
|
Note: I've only tested this under Cygwin's build environment. Your
|
||||||
mileage may vary in other environments (i.e., compiling PortAudio with
|
mileage may vary in other environments (i.e., compiling PortAudio with
|
||||||
MinGW's compiler and environment).
|
MinGW's compiler).
|
||||||
|
|
||||||
(If you have instructions for building PyAudio using Visual Studio,
|
1. Install cygwin's gcc and mingw packages.
|
||||||
I'd love to hear about it.)
|
|
||||||
|
|
||||||
1. Download PortAudio to ./portaudio-v19 in this directory
|
2. Download PortAudio and build. When running configure, be sure to
|
||||||
and build. When running configure, be sure to use ``-mno-cygwin``
|
specify the MinGW compiler (via a CC environment variable) to
|
||||||
(under cygwin) to generate native Win32 binaries:
|
generate native Win32 binaries:
|
||||||
|
|
||||||
% cd ./portaudio-v19
|
% CC=i686-w64-mingw32-gcc ./configure --enable-static --with-pic
|
||||||
% CFLAGS="-mno-cygwin" LDFLAGS="-mno-cygwin" ./configure
|
% make
|
||||||
% make
|
|
||||||
% cd ..
|
|
||||||
|
|
||||||
2. To build PyAudio, run (from this directory):
|
3. Before building PyAudio, apply a few necessary modifications:
|
||||||
|
|
||||||
% python setup.py build --static-link -cmingw32
|
a. Python distutils calls ``gcc'' to build the C extension, so
|
||||||
|
temporarily move your MinGW compiler to /usr/bin/gcc.
|
||||||
|
|
||||||
|
b. Modify Python's Lib/distutils/cygwincompiler.py so that
|
||||||
|
mscvr900.dll is not included in the build. See:
|
||||||
|
http://bugs.python.org/issue16472.
|
||||||
|
|
||||||
|
Both Python 2.7 and Python 3+ require similar modification.
|
||||||
|
|
||||||
|
c. For some versions of Python (e.g., Python 2.7 32-bit), it is
|
||||||
|
necessary to further modify Python's
|
||||||
|
Lib/distutils/cygwincompiler.py and remove references to
|
||||||
|
-cmingw32, a flag which is no longer supported.
|
||||||
|
See http://hg.python.org/cpython/rev/6b89176f1be5/.
|
||||||
|
|
||||||
|
d. For some versions of 64-bit Python 3 (e.g., Python 3.2, 3.3, 3.4),
|
||||||
|
it is necessary to generate .a archive of the Python DLL.
|
||||||
|
See https://bugs.python.org/issue20785. Example for Python 3.4:
|
||||||
|
|
||||||
|
% cd /path/to/Python34-x64/libs/
|
||||||
|
% gendef /path/to/Windows/System32/python34.dll
|
||||||
|
% dlltool --as-flags=--64 -m i386:x64-64 -k --output-lib libpython34.a \
|
||||||
|
--input-def python34.def
|
||||||
|
|
||||||
|
4. To build PyAudio, run:
|
||||||
|
|
||||||
|
% PORTAUDIO_PATH=/path/to/portaudio_tree /path/to/win/python \
|
||||||
|
setup.py build --static-link -cmingw32
|
||||||
|
|
||||||
Be sure to invoke the native Win32 python rather than cygwin's
|
Be sure to invoke the native Win32 python rather than cygwin's
|
||||||
python. The --static-link option statically links in the PortAudio
|
python. The --static-link option statically links in the PortAudio
|
||||||
library to the PyAudio module, which is probably the best way to go
|
library to the PyAudio module.
|
||||||
on Windows.
|
|
||||||
|
|
||||||
From: http://boodebr.org/main/python/build-windows-extensions
|
5. To install PyAudio:
|
||||||
|
|
||||||
Update: 2008-09-10
|
% python setup.py install --skip-build
|
||||||
|
|
||||||
Recent versions of Cygwin binutils have version numbers that are
|
Or create a Python wheel and install using pip.
|
||||||
breaking the version number parsing, resulting in errors like:
|
|
||||||
ValueError: invalid version number '2.18.50.20080625'
|
|
||||||
|
|
||||||
To fix this, edit distutils/version.py. At line 100, replace:
|
|
||||||
|
|
||||||
version_re = re.compile(r'^(\d+) \. (\d+) (\. (\d+))? ([ab](\d+))?$',
|
|
||||||
re.VERBOSE)
|
|
||||||
|
|
||||||
with
|
|
||||||
|
|
||||||
version_re = re.compile(r'^(\d+) \. (\d+) (\. (\d+))? (\. (\d+))?$',
|
|
||||||
re.VERBOSE)
|
|
||||||
|
|
||||||
3. To install PyAudio:
|
|
||||||
|
|
||||||
% python setup.py install --skip-build
|
|
||||||
|
|
||||||
The --skip-build option prevents Python from searching your system
|
|
||||||
for Visual Studio and the .NET framework.
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
include src/*.c src/*.h src/*.py
|
include src/*.c src/*.h src/*.py
|
||||||
include Makefile CHANGELOG INSTALL MANIFEST.in
|
include Makefile CHANGELOG INSTALL MANIFEST.in
|
||||||
recursive-include test *.py *.c
|
recursive-include examples *.py
|
||||||
graft docs
|
recursive-include tests *.py
|
||||||
|
graft sphinx
|
||||||
|
|
35
Makefile
35
Makefile
|
@ -1,19 +1,24 @@
|
||||||
# This is the PyAudio distribution makefile.
|
# This is the PyAudio distribution makefile.
|
||||||
|
|
||||||
.PHONY: docs clean
|
.PHONY: docs clean build
|
||||||
|
|
||||||
EPYDOC ?= epydoc
|
VERSION := 0.2.11
|
||||||
|
PYTHON ?= python
|
||||||
VERSION := 0.2.4
|
BUILD_ARGS ?=
|
||||||
|
SPHINX ?= sphinx-build
|
||||||
DOCS_OUTPUT=docs/
|
DOCS_OUTPUT=docs/
|
||||||
DOC_NAME := PyAudio-$(VERSION)
|
PYTHON_BUILD_DIR:=$(shell $(PYTHON) -c "import distutils.util; import sys; print(distutils.util.get_platform() + '-' + sys.version[0:3])")
|
||||||
DOC_URL=http://people.csail.mit.edu/hubert/pyaudio/
|
BUILD_DIR:=lib.$(PYTHON_BUILD_DIR)
|
||||||
|
BUILD_STAMP:=$(BUILD_DIR)/build
|
||||||
|
SRCFILES := src/*.c src/*.h src/*.py
|
||||||
|
EXAMPLES := examples/*.py
|
||||||
|
TESTS := tests/*.py
|
||||||
|
|
||||||
what:
|
what:
|
||||||
@echo "make targets:"
|
@echo "make targets:"
|
||||||
@echo
|
@echo
|
||||||
@echo " tarball : build source tarball"
|
@echo " tarball : build source tarball"
|
||||||
@echo " docs : generate documentation (requires epydoc)"
|
@echo " docs : generate documentation (requires sphinx)"
|
||||||
@echo " clean : remove build files"
|
@echo " clean : remove build files"
|
||||||
@echo
|
@echo
|
||||||
@echo "To build pyaudio, run:"
|
@echo "To build pyaudio, run:"
|
||||||
|
@ -27,13 +32,17 @@ clean:
|
||||||
# Documentation
|
# Documentation
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
docs:
|
build: build/$(BUILD_STAMP)
|
||||||
@cd src; \
|
|
||||||
$(EPYDOC) -v -o ../$(DOCS_OUTPUT) --name $(DOC_NAME) --url $(DOC_URL) \
|
build/$(BUILD_STAMP): $(SRCFILES)
|
||||||
--no-private pyaudio.py
|
$(PYTHON) setup.py build $(BUILD_ARGS)
|
||||||
|
touch $@
|
||||||
|
|
||||||
|
docs: build
|
||||||
|
PYTHONPATH=build/$(BUILD_DIR) $(SPHINX) -b html sphinx/ $(DOCS_OUTPUT)
|
||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
# Source Tarball
|
# Source Tarball
|
||||||
######################################################################
|
######################################################################
|
||||||
tarball: docs $(SRCFILES) MANIFEST.in
|
tarball: $(SRCFILES) $(EXAMPLES) $(TESTS) MANIFEST.in
|
||||||
@python setup.py sdist
|
@$(PYTHON) setup.py sdist
|
||||||
|
|
10
README
10
README
|
@ -1,23 +1,20 @@
|
||||||
======================================================================
|
======================================================================
|
||||||
PyAudio v0.2.4: Python Bindings for PortAudio.
|
PyAudio v0.2.12: Python Bindings for PortAudio.
|
||||||
======================================================================
|
======================================================================
|
||||||
|
|
||||||
See: http://people.csail.mit.edu/hubert/pyaudio/
|
See: http://people.csail.mit.edu/hubert/pyaudio/
|
||||||
|
|
||||||
PyAudio provides Python bindings for PortAudio, the cross-platform
|
PyAudio provides Python bindings for PortAudio v19, the cross-platform
|
||||||
audio I/O library. Using PyAudio, you can easily use Python to play
|
audio I/O library. Using PyAudio, you can easily use Python to play
|
||||||
and record audio on a variety of platforms.
|
and record audio on a variety of platforms.
|
||||||
|
|
||||||
PyAudio is designed to work with the PortAudio v19 API 2.0. Note that
|
|
||||||
PyAudio currently only supports blocking-mode audio I/O.
|
|
||||||
|
|
||||||
See INSTALL for compilation hints.
|
See INSTALL for compilation hints.
|
||||||
|
|
||||||
======================================================================
|
======================================================================
|
||||||
|
|
||||||
PyAudio : Python Bindings for PortAudio.
|
PyAudio : Python Bindings for PortAudio.
|
||||||
|
|
||||||
Copyright (c) 2006-2010 Hubert Pham
|
Copyright (c) 2006 Hubert Pham
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
a copy of this software and associated documentation files (the
|
a copy of this software and associated documentation files (the
|
||||||
|
@ -39,4 +36,3 @@ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
======================================================================
|
======================================================================
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
"""
|
"""
|
||||||
PyAudio Example:
|
PyAudio Example: Test for a variety of error conditions. This example
|
||||||
|
demonstrates exception handling with PyAudio.
|
||||||
Test for a variety of error conditions. This
|
"""
|
||||||
example demonstrates exception handling with
|
|
||||||
PyAudio. """
|
|
||||||
|
|
||||||
import pyaudio
|
import pyaudio
|
||||||
|
|
||||||
|
@ -12,56 +10,56 @@ p = pyaudio.PyAudio()
|
||||||
# get invalid sample size
|
# get invalid sample size
|
||||||
try:
|
try:
|
||||||
p.get_sample_size(10)
|
p.get_sample_size(10)
|
||||||
except ValueError, e:
|
except ValueError as e:
|
||||||
assert e[1] == pyaudio.paSampleFormatNotSupported
|
assert e.args[1] == pyaudio.paSampleFormatNotSupported
|
||||||
print "OK:", e[0]
|
print("OK: %s" % e.args[0])
|
||||||
else:
|
else:
|
||||||
assert False, "sample size"
|
assert False, "sample size"
|
||||||
|
|
||||||
# get format from invalid width
|
# get format from invalid width
|
||||||
try:
|
try:
|
||||||
p.get_format_from_width(8)
|
p.get_format_from_width(8)
|
||||||
except ValueError, e:
|
except ValueError as e:
|
||||||
print "OK: invalid format from width"
|
print("OK: invalid format from width")
|
||||||
else:
|
else:
|
||||||
assert False, "invalid format"
|
assert False, "invalid format"
|
||||||
|
|
||||||
# try to get an invalid device
|
# try to get an invalid device
|
||||||
try:
|
try:
|
||||||
p.get_host_api_info_by_type(-1)
|
p.get_host_api_info_by_type(-1)
|
||||||
except IOError, e:
|
except IOError as e:
|
||||||
assert e[1] == pyaudio.paHostApiNotFound
|
assert e.args[1] == pyaudio.paHostApiNotFound
|
||||||
print "OK:", e[0]
|
print("OK: %s" % e.args[0])
|
||||||
else:
|
else:
|
||||||
assert False, "invalid host type"
|
assert False, "invalid host type"
|
||||||
|
|
||||||
# try to get host api info by index
|
# try to get host api info by index
|
||||||
try:
|
try:
|
||||||
p.get_host_api_info_by_index(-1)
|
p.get_host_api_info_by_index(-1)
|
||||||
except IOError, e:
|
except IOError as e:
|
||||||
assert e[1] == pyaudio.paInvalidHostApi
|
assert e.args[1] == pyaudio.paInvalidHostApi
|
||||||
print "OK:", e[0]
|
print("OK: %s" % e.args[0])
|
||||||
else:
|
else:
|
||||||
assert False, "invalid host api index"
|
assert False, "invalid host api index"
|
||||||
|
|
||||||
# try good host api device index
|
# try good host api device index
|
||||||
try:
|
try:
|
||||||
p.get_device_info_by_host_api_device_index(0, -1)
|
p.get_device_info_by_host_api_device_index(0, -1)
|
||||||
except IOError, e:
|
except IOError as e:
|
||||||
assert ((e[1] == pyaudio.paInvalidDevice) or \
|
assert ((e.args[1] == pyaudio.paInvalidDevice) or \
|
||||||
(e[1] == pyaudio.paInvalidHostApi))
|
(e.args[1] == pyaudio.paInvalidHostApi))
|
||||||
print "OK:", e[0]
|
print("OK: %s" % e.args[0])
|
||||||
else:
|
else:
|
||||||
assert False, "device info by host api device idnex"
|
assert False, "device info by host api device idnex"
|
||||||
|
|
||||||
|
|
||||||
# try bad host api and good device index
|
# try bad host api and good device index
|
||||||
try:
|
try:
|
||||||
p.get_device_info_by_host_api_device_index(-1, 0)
|
p.get_device_info_by_host_api_device_index(-1, 0)
|
||||||
except IOError, e:
|
except IOError as e:
|
||||||
assert ((e[1] == pyaudio.paInvalidDevice) or \
|
assert ((e.args[1] == pyaudio.paInvalidDevice) or \
|
||||||
(e[1] == pyaudio.paInvalidHostApi))
|
(e.args[1] == pyaudio.paInvalidHostApi))
|
||||||
print "OK:", e[0]
|
print("OK: %s" % e.args[0])
|
||||||
else:
|
else:
|
||||||
assert False, "device info by host api device idnex"
|
assert False, "device info by host api device idnex"
|
||||||
|
|
||||||
|
@ -69,9 +67,9 @@ else:
|
||||||
# bad device index
|
# bad device index
|
||||||
try:
|
try:
|
||||||
p.get_device_info_by_index(-1)
|
p.get_device_info_by_index(-1)
|
||||||
except IOError, e:
|
except IOError as e:
|
||||||
assert e[1] == pyaudio.paInvalidDevice
|
assert e.args[1] == pyaudio.paInvalidDevice
|
||||||
print "OK:", e[0]
|
print("OK: %s" % e.args[0])
|
||||||
else:
|
else:
|
||||||
assert False, "bad device index"
|
assert False, "bad device index"
|
||||||
|
|
||||||
|
@ -87,9 +85,9 @@ stream = p.open(channels = 1,
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data = stream.read(2)
|
data = stream.read(2)
|
||||||
except IOError, e:
|
except IOError as e:
|
||||||
print "OK:", e[0]
|
print("OK: %s" % e.args[0])
|
||||||
assert e[1] == pyaudio.paStreamIsStopped, e[1]
|
assert e.args[1] == pyaudio.paStreamIsStopped, e.args[1]
|
||||||
else:
|
else:
|
||||||
assert False, "Should have caused exception"
|
assert False, "Should have caused exception"
|
||||||
|
|
||||||
|
@ -98,29 +96,29 @@ stream.start_stream()
|
||||||
# try to write to the input stream
|
# try to write to the input stream
|
||||||
try:
|
try:
|
||||||
stream.write('foobar')
|
stream.write('foobar')
|
||||||
except IOError, e:
|
except IOError as e:
|
||||||
assert e[1] == pyaudio.paCanNotWriteToAnInputOnlyStream
|
assert e.args[1] == pyaudio.paCanNotWriteToAnInputOnlyStream
|
||||||
print "OK:", e[0]
|
print("OK: %s" % e.args[0])
|
||||||
else:
|
else:
|
||||||
assert False, "write to input stream"
|
assert False, "write to input stream"
|
||||||
|
|
||||||
# read some negative data
|
# read some negative data
|
||||||
try:
|
try:
|
||||||
data = stream.read(-1)
|
data = stream.read(-1)
|
||||||
except ValueError, e:
|
except ValueError as e:
|
||||||
print "OK: Invalid frames"
|
print("OK: Invalid frames")
|
||||||
else:
|
else:
|
||||||
assert False, "invalid frames"
|
assert False, "invalid frames"
|
||||||
|
|
||||||
# read some real data
|
# read some real data
|
||||||
try:
|
try:
|
||||||
data = stream.read(2)
|
data = stream.read(2)
|
||||||
except IOError, e:
|
except IOError as e:
|
||||||
# some slower machines might overflow
|
# some slower machines might overflow
|
||||||
assert e[1] == pyaudio.paInputOverflowed, e
|
assert e.args[1] == pyaudio.paInputOverflowed, e
|
||||||
print "OK:", e[0]
|
print("OK: %s" % e.args[0])
|
||||||
else:
|
else:
|
||||||
print "OK: got %d bytes of data" % len(data)
|
print("OK: got %d bytes of data" % len(data))
|
||||||
|
|
||||||
# close the stream; nothing should work with
|
# close the stream; nothing should work with
|
||||||
# this stream afterwards
|
# this stream afterwards
|
||||||
|
@ -130,36 +128,36 @@ stream.close()
|
||||||
# query for properties
|
# query for properties
|
||||||
try:
|
try:
|
||||||
stream.get_input_latency()
|
stream.get_input_latency()
|
||||||
except IOError, e:
|
except IOError as e:
|
||||||
assert e[1] == pyaudio.paBadStreamPtr
|
assert e.args[1] == pyaudio.paBadStreamPtr
|
||||||
print "OK:", e[0]
|
print("OK: %s" % e.args[0])
|
||||||
else:
|
else:
|
||||||
assert False, "closed stream"
|
assert False, "closed stream"
|
||||||
|
|
||||||
# read some data again
|
# read some data again
|
||||||
try:
|
try:
|
||||||
stream.read(10)
|
stream.read(10)
|
||||||
except IOError, e:
|
except IOError as e:
|
||||||
assert e[1] == pyaudio.paBadStreamPtr
|
assert e.args[1] == pyaudio.paBadStreamPtr
|
||||||
print "OK:", e[0]
|
print("OK: %s" % e.args[0])
|
||||||
else:
|
else:
|
||||||
assert False, "closed stream"
|
assert False, "closed stream"
|
||||||
|
|
||||||
# get invalid stream capabilities
|
# get invalid stream capabilities
|
||||||
try:
|
try:
|
||||||
p.is_format_supported(8000, -1, 1, pyaudio.paInt16)
|
p.is_format_supported(8000, -1, 1, pyaudio.paInt16)
|
||||||
except ValueError, e:
|
except ValueError as e:
|
||||||
assert e[1] == pyaudio.paInvalidDevice
|
assert e.args[1] == pyaudio.paInvalidDevice
|
||||||
print "OK:", e[0]
|
print("OK: %s" % e.args[0])
|
||||||
else:
|
else:
|
||||||
assert False, "invalid device"
|
assert False, "invalid device"
|
||||||
|
|
||||||
# get invalid stream capabilities
|
# get invalid stream capabilities
|
||||||
try:
|
try:
|
||||||
p.is_format_supported(8000, 0, -1, pyaudio.paInt16)
|
p.is_format_supported(8000, 0, -1, pyaudio.paInt16)
|
||||||
except ValueError, e:
|
except ValueError as e:
|
||||||
assert e[1] == pyaudio.paInvalidChannelCount
|
assert e.args[1] == pyaudio.paInvalidChannelCount
|
||||||
print "OK:", e[0]
|
print("OK: %s" % e.args[0])
|
||||||
else:
|
else:
|
||||||
assert False, "invalid number of channels"
|
assert False, "invalid number of channels"
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
"""PyAudio Example: Play a wave file."""
|
||||||
|
|
||||||
|
import pyaudio
|
||||||
|
import wave
|
||||||
|
import sys
|
||||||
|
|
||||||
|
CHUNK = 1024
|
||||||
|
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
print("Plays a wave file.\n\nUsage: %s filename.wav" % sys.argv[0])
|
||||||
|
sys.exit(-1)
|
||||||
|
|
||||||
|
wf = wave.open(sys.argv[1], 'rb')
|
||||||
|
|
||||||
|
# instantiate PyAudio (1)
|
||||||
|
p = pyaudio.PyAudio()
|
||||||
|
|
||||||
|
# open stream (2)
|
||||||
|
stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
|
||||||
|
channels=wf.getnchannels(),
|
||||||
|
rate=wf.getframerate(),
|
||||||
|
output=True)
|
||||||
|
|
||||||
|
# read data
|
||||||
|
data = wf.readframes(CHUNK)
|
||||||
|
|
||||||
|
# play stream (3)
|
||||||
|
while len(data) > 0:
|
||||||
|
stream.write(data)
|
||||||
|
data = wf.readframes(CHUNK)
|
||||||
|
|
||||||
|
# stop stream (4)
|
||||||
|
stream.stop_stream()
|
||||||
|
stream.close()
|
||||||
|
|
||||||
|
# close PyAudio (5)
|
||||||
|
p.terminate()
|
|
@ -0,0 +1,42 @@
|
||||||
|
"""PyAudio Example: Play a wave file (callback version)."""
|
||||||
|
|
||||||
|
import pyaudio
|
||||||
|
import wave
|
||||||
|
import time
|
||||||
|
import sys
|
||||||
|
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
print("Plays a wave file.\n\nUsage: %s filename.wav" % sys.argv[0])
|
||||||
|
sys.exit(-1)
|
||||||
|
|
||||||
|
wf = wave.open(sys.argv[1], 'rb')
|
||||||
|
|
||||||
|
# instantiate PyAudio (1)
|
||||||
|
p = pyaudio.PyAudio()
|
||||||
|
|
||||||
|
# define callback (2)
|
||||||
|
def callback(in_data, frame_count, time_info, status):
|
||||||
|
data = wf.readframes(frame_count)
|
||||||
|
return (data, pyaudio.paContinue)
|
||||||
|
|
||||||
|
# open stream using callback (3)
|
||||||
|
stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
|
||||||
|
channels=wf.getnchannels(),
|
||||||
|
rate=wf.getframerate(),
|
||||||
|
output=True,
|
||||||
|
stream_callback=callback)
|
||||||
|
|
||||||
|
# start the stream (4)
|
||||||
|
stream.start_stream()
|
||||||
|
|
||||||
|
# wait for stream to finish (5)
|
||||||
|
while stream.is_active():
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
# stop stream (6)
|
||||||
|
stream.stop_stream()
|
||||||
|
stream.close()
|
||||||
|
wf.close()
|
||||||
|
|
||||||
|
# close PyAudio (7)
|
||||||
|
p.terminate()
|
|
@ -1,4 +1,6 @@
|
||||||
""" PyAudio Example: Mac OS X-only: Play a wave file with channel maps. """
|
"""
|
||||||
|
PyAudio Example: Mac OS X-only: Play a wave file with channel maps.
|
||||||
|
"""
|
||||||
|
|
||||||
import pyaudio
|
import pyaudio
|
||||||
import wave
|
import wave
|
||||||
|
@ -9,7 +11,7 @@ chunk = 1024
|
||||||
PyAudio = pyaudio.PyAudio
|
PyAudio = pyaudio.PyAudio
|
||||||
|
|
||||||
if len(sys.argv) < 2:
|
if len(sys.argv) < 2:
|
||||||
print "Plays a wave file.\n\nUsage: %s filename.wav" % sys.argv[0]
|
print("Plays a wave file.\n\nUsage: %s filename.wav" % sys.argv[0])
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
|
|
||||||
wf = wave.open(sys.argv[1], 'rb')
|
wf = wave.open(sys.argv[1], 'rb')
|
||||||
|
@ -40,30 +42,29 @@ channel_map = (1, -1)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
stream_info = pyaudio.PaMacCoreStreamInfo(
|
stream_info = pyaudio.PaMacCoreStreamInfo(
|
||||||
flags = pyaudio.PaMacCoreStreamInfo.paMacCorePlayNice, # default
|
flags=pyaudio.PaMacCoreStreamInfo.paMacCorePlayNice, # default
|
||||||
channel_map = channel_map)
|
channel_map=channel_map)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
print "Sorry, couldn't find PaMacCoreStreamInfo. Make sure that " \
|
print("Sorry, couldn't find PaMacCoreStreamInfo. Make sure that "
|
||||||
"you're running on Mac OS X."
|
"you're running on Mac OS X.")
|
||||||
import sys
|
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
|
|
||||||
print "Stream Info Flags:", stream_info.get_flags()
|
print("Stream Info Flags:", stream_info.get_flags())
|
||||||
print "Stream Info Channel Map:", stream_info.get_channel_map()
|
print("Stream Info Channel Map:", stream_info.get_channel_map())
|
||||||
|
|
||||||
# open stream
|
# open stream
|
||||||
stream = p.open(format =
|
stream = p.open(
|
||||||
p.get_format_from_width(wf.getsampwidth()),
|
format=p.get_format_from_width(wf.getsampwidth()),
|
||||||
channels = wf.getnchannels(),
|
channels=wf.getnchannels(),
|
||||||
rate = wf.getframerate(),
|
rate=wf.getframerate(),
|
||||||
output = True,
|
output=True,
|
||||||
output_host_api_specific_stream_info = stream_info)
|
output_host_api_specific_stream_info=stream_info)
|
||||||
|
|
||||||
# read data
|
# read data
|
||||||
data = wf.readframes(chunk)
|
data = wf.readframes(chunk)
|
||||||
|
|
||||||
# play stream
|
# play stream
|
||||||
while data != '':
|
while len(data) > 0:
|
||||||
stream.write(data)
|
stream.write(data)
|
||||||
data = wf.readframes(chunk)
|
data = wf.readframes(chunk)
|
||||||
|
|
||||||
|
@ -71,6 +72,3 @@ stream.stop_stream()
|
||||||
stream.close()
|
stream.close()
|
||||||
|
|
||||||
p.terminate()
|
p.terminate()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
"""
|
||||||
|
PyAudio example: Record a few seconds of audio and save to a WAVE
|
||||||
|
file.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import pyaudio
|
||||||
|
import wave
|
||||||
|
import sys
|
||||||
|
|
||||||
|
CHUNK = 1024
|
||||||
|
FORMAT = pyaudio.paInt16
|
||||||
|
CHANNELS = 2
|
||||||
|
RATE = 44100
|
||||||
|
RECORD_SECONDS = 5
|
||||||
|
WAVE_OUTPUT_FILENAME = "output.wav"
|
||||||
|
|
||||||
|
if sys.platform == 'darwin':
|
||||||
|
CHANNELS = 1
|
||||||
|
|
||||||
|
p = pyaudio.PyAudio()
|
||||||
|
|
||||||
|
stream = p.open(format=FORMAT,
|
||||||
|
channels=CHANNELS,
|
||||||
|
rate=RATE,
|
||||||
|
input=True,
|
||||||
|
frames_per_buffer=CHUNK)
|
||||||
|
|
||||||
|
print("* recording")
|
||||||
|
|
||||||
|
frames = []
|
||||||
|
|
||||||
|
for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
|
||||||
|
data = stream.read(CHUNK)
|
||||||
|
frames.append(data)
|
||||||
|
|
||||||
|
print("* done recording")
|
||||||
|
|
||||||
|
stream.stop_stream()
|
||||||
|
stream.close()
|
||||||
|
p.terminate()
|
||||||
|
|
||||||
|
wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
|
||||||
|
wf.setnchannels(CHANNELS)
|
||||||
|
wf.setsampwidth(p.get_sample_size(FORMAT))
|
||||||
|
wf.setframerate(RATE)
|
||||||
|
wf.writeframes(b''.join(frames))
|
||||||
|
wf.close()
|
|
@ -16,42 +16,44 @@ p = pyaudio.PyAudio()
|
||||||
max_apis = p.get_host_api_count()
|
max_apis = p.get_host_api_count()
|
||||||
max_devs = p.get_device_count()
|
max_devs = p.get_device_count()
|
||||||
|
|
||||||
print "\nPortAudio System Info:\n======================"
|
print("\nPortAudio System Info:\n======================")
|
||||||
print "Version: %d" % pyaudio.get_portaudio_version()
|
print("Version: %d" % pyaudio.get_portaudio_version())
|
||||||
print "Version Text: %s" % pyaudio.get_portaudio_version_text()
|
print("Version Text: %s" % pyaudio.get_portaudio_version_text())
|
||||||
print "Number of Host APIs: %d" % max_apis
|
print("Number of Host APIs: %d" % max_apis)
|
||||||
print "Number of Devices : %d" % max_devs
|
print("Number of Devices : %d" % max_devs)
|
||||||
|
|
||||||
print "\nHost APIs:\n=========="
|
print("\nHost APIs:\n==========")
|
||||||
|
|
||||||
for i in range(max_apis):
|
for i in range(max_apis):
|
||||||
apiinfo = p.get_host_api_info_by_index(i)
|
apiinfo = p.get_host_api_info_by_index(i)
|
||||||
for k in apiinfo.items():
|
for k in list(apiinfo.items()):
|
||||||
print "%s: %s" % k
|
print("%s: %s" % k)
|
||||||
print "--------------------------"
|
print("--------------------------")
|
||||||
|
|
||||||
print "\nDevices:\n========"
|
print("\nDevices:\n========")
|
||||||
|
|
||||||
for i in range(max_devs):
|
for i in range(max_devs):
|
||||||
devinfo = p.get_device_info_by_index(i)
|
devinfo = p.get_device_info_by_index(i)
|
||||||
|
|
||||||
# print out device parameters
|
# print out device parameters
|
||||||
for k in devinfo.items():
|
for k in list(devinfo.items()):
|
||||||
name, value = k
|
name, value = k
|
||||||
|
|
||||||
# if host API, then get friendly name
|
# if host API, then get friendly name
|
||||||
|
|
||||||
if name == 'hostApi':
|
if name == 'hostApi':
|
||||||
value = str(value) + \
|
value = str(value) + \
|
||||||
" (%s)" % p.get_host_api_info_by_index(k[1])['name']
|
" (%s)" % p.get_host_api_info_by_index(k[1])['name']
|
||||||
print "\t%s: %s" % (name, value)
|
|
||||||
|
# Crashing? See http://stackoverflow.com/a/5146914
|
||||||
|
print("\t%s: %s" % (name, value))
|
||||||
|
|
||||||
# print out supported format rates
|
# print out supported format rates
|
||||||
|
|
||||||
input_supported_rates = []
|
input_supported_rates = []
|
||||||
output_supported_rates = []
|
output_supported_rates = []
|
||||||
full_duplex_rates = []
|
full_duplex_rates = []
|
||||||
|
|
||||||
for f in standard_sample_rates:
|
for f in standard_sample_rates:
|
||||||
|
|
||||||
if devinfo['maxInputChannels'] > 0:
|
if devinfo['maxInputChannels'] > 0:
|
||||||
|
@ -92,42 +94,41 @@ for i in range(max_devs):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if len(input_supported_rates):
|
if len(input_supported_rates):
|
||||||
print "\tInput rates:", input_supported_rates
|
print("\tInput rates: %s" % input_supported_rates)
|
||||||
if len(output_supported_rates):
|
if len(output_supported_rates):
|
||||||
print "\tOutput rates:", output_supported_rates
|
print("\tOutput rates: %s" % output_supported_rates)
|
||||||
if len(full_duplex_rates):
|
if len(full_duplex_rates):
|
||||||
print "\tFull duplex: ", full_duplex_rates
|
print("\tFull duplex: %s" % full_duplex_rates)
|
||||||
|
|
||||||
print "\t--------------------------------"
|
|
||||||
|
|
||||||
print "\nDefault Devices:\n================"
|
print("\t--------------------------------")
|
||||||
|
|
||||||
|
print("\nDefault Devices:\n================")
|
||||||
try:
|
try:
|
||||||
def_index = p.get_default_input_device_info()['index']
|
def_index = p.get_default_input_device_info()['index']
|
||||||
print "Default Input Device :", def_index
|
print("Default Input Device : %s" % def_index)
|
||||||
devinfo = p.get_device_info_by_index(def_index)
|
devinfo = p.get_device_info_by_index(def_index)
|
||||||
for k in devinfo.items():
|
for k in list(devinfo.items()):
|
||||||
name, value = k
|
name, value = k
|
||||||
if name == 'hostApi':
|
if name == 'hostApi':
|
||||||
value = str(value) + \
|
value = str(value) + \
|
||||||
" (%s)" % p.get_host_api_info_by_index(k[1])['name']
|
" (%s)" % p.get_host_api_info_by_index(k[1])['name']
|
||||||
print "\t%s: %s" % (name, value)
|
print("\t%s: %s" % (name, value))
|
||||||
print "\t--------------------------------"
|
print("\t--------------------------------")
|
||||||
except IOError, e:
|
except IOError as e:
|
||||||
print "No Input devices: %s" % e[0]
|
print("No Input devices: %s" % e[0])
|
||||||
|
|
||||||
try:
|
try:
|
||||||
def_index = p.get_default_output_device_info()['index']
|
def_index = p.get_default_output_device_info()['index']
|
||||||
print "Default Output Device:", def_index
|
print("Default Output Device: %s" % def_index)
|
||||||
devinfo = p.get_device_info_by_index(def_index)
|
devinfo = p.get_device_info_by_index(def_index)
|
||||||
for k in devinfo.items():
|
for k in list(devinfo.items()):
|
||||||
name, value = k
|
name, value = k
|
||||||
if name == 'hostApi':
|
if name == 'hostApi':
|
||||||
value = str(value) + \
|
value = str(value) + \
|
||||||
" (%s)" % p.get_host_api_info_by_index(k[1])['name']
|
" (%s)" % p.get_host_api_info_by_index(k[1])['name']
|
||||||
print "\t%s: %s" % (name, value)
|
print("\t%s: %s" % (name, value))
|
||||||
print "\t--------------------------------"
|
print("\t--------------------------------")
|
||||||
except IOError, e:
|
except IOError as e:
|
||||||
print "No Output devices: %s" % e[0]
|
print("No Output devices: %s" % e[0])
|
||||||
|
|
||||||
p.terminate()
|
p.terminate()
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
"""
|
||||||
|
PyAudio Example: Make a wire between input and output (i.e., record a
|
||||||
|
few samples and play them back immediately).
|
||||||
|
|
||||||
|
This is the callback (non-blocking) version.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import pyaudio
|
||||||
|
import time
|
||||||
|
import sys
|
||||||
|
|
||||||
|
WIDTH = 2
|
||||||
|
CHANNELS = 2
|
||||||
|
RATE = 44100
|
||||||
|
DURATION = 5
|
||||||
|
|
||||||
|
if sys.platform == 'darwin':
|
||||||
|
CHANNELS = 1
|
||||||
|
|
||||||
|
p = pyaudio.PyAudio()
|
||||||
|
|
||||||
|
def callback(in_data, frame_count, time_info, status):
|
||||||
|
return (in_data, pyaudio.paContinue)
|
||||||
|
|
||||||
|
stream = p.open(format=p.get_format_from_width(WIDTH),
|
||||||
|
channels=CHANNELS,
|
||||||
|
rate=RATE,
|
||||||
|
input=True,
|
||||||
|
output=True,
|
||||||
|
stream_callback=callback)
|
||||||
|
|
||||||
|
stream.start_stream()
|
||||||
|
|
||||||
|
start = time.time()
|
||||||
|
|
||||||
|
while stream.is_active() and (time.time() - start) < DURATION:
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
stream.stop_stream()
|
||||||
|
stream.close()
|
||||||
|
|
||||||
|
p.terminate()
|
|
@ -0,0 +1,40 @@
|
||||||
|
"""
|
||||||
|
PyAudio Example: Make a wire between input and output (i.e., record a
|
||||||
|
few samples and play them back immediately).
|
||||||
|
|
||||||
|
This is the full duplex version.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import pyaudio
|
||||||
|
import sys
|
||||||
|
|
||||||
|
CHUNK = 1024
|
||||||
|
WIDTH = 2
|
||||||
|
CHANNELS = 2
|
||||||
|
RATE = 44100
|
||||||
|
RECORD_SECONDS = 5
|
||||||
|
|
||||||
|
if sys.platform == 'darwin':
|
||||||
|
CHANNELS = 1
|
||||||
|
|
||||||
|
p = pyaudio.PyAudio()
|
||||||
|
|
||||||
|
stream = p.open(format=p.get_format_from_width(WIDTH),
|
||||||
|
channels=CHANNELS,
|
||||||
|
rate=RATE,
|
||||||
|
input=True,
|
||||||
|
output=True,
|
||||||
|
frames_per_buffer=CHUNK)
|
||||||
|
|
||||||
|
print("* recording")
|
||||||
|
|
||||||
|
for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
|
||||||
|
data = stream.read(CHUNK)
|
||||||
|
stream.write(data, CHUNK)
|
||||||
|
|
||||||
|
print("* done")
|
||||||
|
|
||||||
|
stream.stop_stream()
|
||||||
|
stream.close()
|
||||||
|
|
||||||
|
p.terminate()
|
|
@ -0,0 +1,48 @@
|
||||||
|
"""
|
||||||
|
PyAudio Example: Make a wire between input and output (i.e., record a
|
||||||
|
few samples and play them back immediately).
|
||||||
|
|
||||||
|
This is the half duplex version.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import pyaudio
|
||||||
|
import sys
|
||||||
|
|
||||||
|
CHUNK = 1024
|
||||||
|
WIDTH = 2
|
||||||
|
CHANNELS = 2
|
||||||
|
RATE = 44100
|
||||||
|
RECORD_SECONDS = 5
|
||||||
|
|
||||||
|
if sys.platform == 'darwin':
|
||||||
|
CHANNELS = 1
|
||||||
|
|
||||||
|
p = pyaudio.PyAudio()
|
||||||
|
|
||||||
|
# Open input stream using default device:
|
||||||
|
stream_input = p.open(format=p.get_format_from_width(WIDTH),
|
||||||
|
channels=CHANNELS,
|
||||||
|
rate=RATE,
|
||||||
|
input=True,
|
||||||
|
frames_per_buffer=CHUNK)
|
||||||
|
|
||||||
|
# Open out stream using default device:
|
||||||
|
stream_output = p.open(format=p.get_format_from_width(WIDTH),
|
||||||
|
channels=CHANNELS,
|
||||||
|
rate=RATE,
|
||||||
|
output=True,
|
||||||
|
frames_per_buffer=CHUNK)
|
||||||
|
|
||||||
|
print("* recording")
|
||||||
|
|
||||||
|
for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
|
||||||
|
data = stream_input.read(CHUNK)
|
||||||
|
stream_output.write(data, CHUNK)
|
||||||
|
|
||||||
|
print("* done")
|
||||||
|
|
||||||
|
stream_input.stop_stream()
|
||||||
|
stream_output.stop_stream()
|
||||||
|
stream_input.close()
|
||||||
|
stream_output.close()
|
||||||
|
p.terminate()
|
|
@ -1,7 +1,7 @@
|
||||||
"""
|
"""
|
||||||
PyAudio v0.2.4: Python Bindings for PortAudio.
|
PyAudio v0.2.11: Python Bindings for PortAudio.
|
||||||
|
|
||||||
Copyright (c) 2006-2010 Hubert Pham
|
Copyright (c) 2006 Hubert Pham
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
a copy of this software and associated documentation files (the
|
a copy of this software and associated documentation files (the
|
||||||
|
@ -26,25 +26,25 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||||
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from distutils.core import setup, Extension
|
|
||||||
import sys
|
|
||||||
import os
|
import os
|
||||||
|
import platform
|
||||||
|
import sys
|
||||||
|
try:
|
||||||
|
from setuptools import setup, Extension
|
||||||
|
except ImportError:
|
||||||
|
from distutils.core import setup, Extension
|
||||||
|
|
||||||
__version__ = "0.2.4"
|
__version__ = "0.2.12"
|
||||||
|
|
||||||
# Note: distutils will try to locate and link dynamically
|
# distutils will try to locate and link dynamically against portaudio.
|
||||||
# against portaudio.
|
|
||||||
#
|
#
|
||||||
# You probably don't want to statically link in the PortAudio
|
# If you would rather statically link in the portaudio library (e.g.,
|
||||||
# library unless you're building on Microsoft Windows.
|
# typically on Microsoft Windows), run:
|
||||||
#
|
#
|
||||||
# In any case, if you would rather statically link in libportaudio,
|
# % python setup.py build --static-link
|
||||||
# run:
|
|
||||||
#
|
#
|
||||||
# % python setup.py build --static-link
|
# Specify the environment variable PORTAUDIO_PATH with the build tree
|
||||||
#
|
# of PortAudio.
|
||||||
# Be sure to specify the location of the libportaudio.a in
|
|
||||||
# the `extra_link_args' variable below.
|
|
||||||
|
|
||||||
STATIC_LINKING = False
|
STATIC_LINKING = False
|
||||||
|
|
||||||
|
@ -53,30 +53,34 @@ if "--static-link" in sys.argv:
|
||||||
sys.argv.remove("--static-link")
|
sys.argv.remove("--static-link")
|
||||||
|
|
||||||
portaudio_path = os.environ.get("PORTAUDIO_PATH", "./portaudio-v19")
|
portaudio_path = os.environ.get("PORTAUDIO_PATH", "./portaudio-v19")
|
||||||
|
mac_sysroot_path = os.environ.get("SYSROOT_PATH", None)
|
||||||
|
|
||||||
pyaudio_module_sources = ['src/_portaudiomodule.c']
|
pyaudio_module_sources = ['src/_portaudiomodule.c']
|
||||||
|
|
||||||
include_dirs = []
|
include_dirs = []
|
||||||
external_libraries = []
|
external_libraries = []
|
||||||
extra_compile_args = ['-fno-strict-aliasing']
|
extra_compile_args = []
|
||||||
extra_link_args = []
|
extra_link_args = []
|
||||||
scripts = []
|
scripts = []
|
||||||
defines = []
|
defines = []
|
||||||
|
|
||||||
if STATIC_LINKING:
|
if sys.platform == 'darwin':
|
||||||
|
defines += [('MACOSX', '1')]
|
||||||
|
if mac_sysroot_path:
|
||||||
|
extra_compile_args += ["-isysroot", mac_sysroot_path]
|
||||||
|
extra_link_args += ["-isysroot", mac_sysroot_path]
|
||||||
|
elif sys.platform == 'win32':
|
||||||
|
bits = platform.architecture()[0]
|
||||||
|
if '64' in bits:
|
||||||
|
defines.append(('MS_WIN64', '1'))
|
||||||
|
|
||||||
|
if not STATIC_LINKING:
|
||||||
|
external_libraries = ['portaudio']
|
||||||
|
extra_link_args = []
|
||||||
|
else:
|
||||||
|
include_dirs = [os.path.join(portaudio_path, 'include/')]
|
||||||
extra_link_args = [
|
extra_link_args = [
|
||||||
os.path.join(portaudio_path, 'lib/.libs/libportaudio.a')
|
os.path.join(portaudio_path, 'lib/.libs/libportaudio.a')
|
||||||
]
|
]
|
||||||
include_dirs = [os.path.join(portaudio_path, 'include/')]
|
|
||||||
else:
|
|
||||||
# dynamic linking
|
|
||||||
external_libraries = ['portaudio']
|
|
||||||
extra_link_args = []
|
|
||||||
|
|
||||||
if sys.platform == 'darwin':
|
|
||||||
defines += [('MACOSX', '1')]
|
|
||||||
|
|
||||||
if STATIC_LINKING:
|
|
||||||
|
|
||||||
# platform specific configuration
|
# platform specific configuration
|
||||||
if sys.platform == 'darwin':
|
if sys.platform == 'darwin':
|
||||||
|
@ -84,45 +88,36 @@ if STATIC_LINKING:
|
||||||
'-framework', 'AudioToolbox',
|
'-framework', 'AudioToolbox',
|
||||||
'-framework', 'AudioUnit',
|
'-framework', 'AudioUnit',
|
||||||
'-framework', 'Carbon']
|
'-framework', 'Carbon']
|
||||||
|
|
||||||
elif sys.platform == 'cygwin':
|
elif sys.platform == 'cygwin':
|
||||||
external_libraries += ['winmm']
|
external_libraries += ['winmm']
|
||||||
extra_link_args += ['-lwinmm']
|
extra_link_args += ['-lwinmm']
|
||||||
|
|
||||||
elif sys.platform == 'win32':
|
elif sys.platform == 'win32':
|
||||||
# i.e., Win32 Python with mingw32
|
# i.e., Win32 Python with mingw32
|
||||||
# run: python setup.py build -cmingw32
|
# run: python setup.py build -cmingw32
|
||||||
external_libraries += ['winmm']
|
external_libraries += ['winmm']
|
||||||
extra_link_args += ['-lwinmm']
|
extra_link_args += ['-lwinmm']
|
||||||
|
|
||||||
elif sys.platform == 'linux2':
|
elif sys.platform == 'linux2':
|
||||||
external_libraries += ['rt', 'm', 'pthread']
|
extra_link_args += ['-lrt', '-lm', '-lpthread']
|
||||||
|
# GNU/Linux has several audio systems (backends) available; be
|
||||||
|
# sure to specify the desired ones here. Start with ALSA and
|
||||||
|
# JACK, since that's common today.
|
||||||
|
extra_link_args += ['-lasound', '-ljack']
|
||||||
|
|
||||||
# Since you're insisting on linking statically against
|
setup(name='PyAudio',
|
||||||
# PortAudio on GNU/Linux, be sure to link in whatever sound
|
version=__version__,
|
||||||
# backend you used in portaudio (e.g., ALSA, JACK, etc...)
|
author="Hubert Pham",
|
||||||
|
url="http://people.csail.mit.edu/hubert/pyaudio/",
|
||||||
# I'll start you off with ALSA, since that's the most common
|
description='PortAudio Python Bindings',
|
||||||
# today. If you need JACK support, add it here.
|
long_description=__doc__.lstrip(),
|
||||||
|
scripts=scripts,
|
||||||
external_libraries += ['asound']
|
py_modules=['pyaudio'],
|
||||||
|
package_dir={'': 'src'},
|
||||||
|
ext_modules=[
|
||||||
pyaudio = Extension('_portaudio',
|
Extension('_portaudio',
|
||||||
sources = pyaudio_module_sources,
|
sources=pyaudio_module_sources,
|
||||||
include_dirs = include_dirs,
|
include_dirs=include_dirs,
|
||||||
define_macros = defines,
|
define_macros=defines,
|
||||||
libraries = external_libraries,
|
libraries=external_libraries,
|
||||||
extra_compile_args = extra_compile_args,
|
extra_compile_args=extra_compile_args,
|
||||||
extra_link_args = extra_link_args)
|
extra_link_args=extra_link_args)
|
||||||
|
])
|
||||||
setup (name = 'PyAudio',
|
|
||||||
version = __version__,
|
|
||||||
author = "Hubert Pham",
|
|
||||||
url = "http://people.csail.mit.edu/hubert/pyaudio/",
|
|
||||||
description = 'PortAudio Python Bindings',
|
|
||||||
long_description = __doc__.lstrip(),
|
|
||||||
scripts = scripts,
|
|
||||||
py_modules = ['pyaudio'],
|
|
||||||
package_dir = {'': 'src'},
|
|
||||||
ext_modules = [pyaudio])
|
|
||||||
|
|
|
@ -0,0 +1,252 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# PyAudio documentation build configuration file, created by
|
||||||
|
# sphinx-quickstart on Wed Aug 29 08:37:41 2012.
|
||||||
|
#
|
||||||
|
# This file is execfile()d with the current directory set to its containing dir.
|
||||||
|
#
|
||||||
|
# Note that not all possible configuration values are present in this
|
||||||
|
# autogenerated file.
|
||||||
|
#
|
||||||
|
# All configuration values have a default; values that are commented out
|
||||||
|
# serve to show the default.
|
||||||
|
|
||||||
|
import sys, os
|
||||||
|
|
||||||
|
# If extensions (or modules to document with autodoc) are in another directory,
|
||||||
|
# add these directories to sys.path here. If the directory is relative to the
|
||||||
|
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||||
|
#sys.path.insert(0, os.path.abspath('.'))
|
||||||
|
|
||||||
|
# -- General configuration -----------------------------------------------------
|
||||||
|
|
||||||
|
# If your documentation needs a minimal Sphinx version, state it here.
|
||||||
|
#needs_sphinx = '1.0'
|
||||||
|
|
||||||
|
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||||
|
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||||
|
extensions = ['sphinx.ext.autodoc']
|
||||||
|
|
||||||
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
|
templates_path = ['_templates']
|
||||||
|
|
||||||
|
# The suffix of source filenames.
|
||||||
|
source_suffix = '.rst'
|
||||||
|
|
||||||
|
# The encoding of source files.
|
||||||
|
#source_encoding = 'utf-8-sig'
|
||||||
|
|
||||||
|
# The master toctree document.
|
||||||
|
master_doc = 'index'
|
||||||
|
|
||||||
|
# General information about the project.
|
||||||
|
project = 'PyAudio'
|
||||||
|
copyright = '2006, Hubert Pham'
|
||||||
|
|
||||||
|
# The version info for the project you're documenting, acts as replacement for
|
||||||
|
# |version| and |release|, also used in various other places throughout the
|
||||||
|
# built documents.
|
||||||
|
#
|
||||||
|
# The short X.Y version.
|
||||||
|
version = '0.2.11'
|
||||||
|
# The full version, including alpha/beta/rc tags.
|
||||||
|
release = '0.2.11'
|
||||||
|
|
||||||
|
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||||
|
# for a list of supported languages.
|
||||||
|
#language = None
|
||||||
|
|
||||||
|
# There are two options for replacing |today|: either, you set today to some
|
||||||
|
# non-false value, then it is used:
|
||||||
|
#today = ''
|
||||||
|
# Else, today_fmt is used as the format for a strftime call.
|
||||||
|
#today_fmt = '%B %d, %Y'
|
||||||
|
|
||||||
|
# List of patterns, relative to source directory, that match files and
|
||||||
|
# directories to ignore when looking for source files.
|
||||||
|
exclude_patterns = ['_build']
|
||||||
|
|
||||||
|
# The reST default role (used for this markup: `text`) to use for all documents.
|
||||||
|
#default_role = None
|
||||||
|
|
||||||
|
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||||
|
#add_function_parentheses = True
|
||||||
|
|
||||||
|
# If true, the current module name will be prepended to all description
|
||||||
|
# unit titles (such as .. function::).
|
||||||
|
#add_module_names = True
|
||||||
|
|
||||||
|
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||||
|
# output. They are ignored by default.
|
||||||
|
#show_authors = False
|
||||||
|
|
||||||
|
# The name of the Pygments (syntax highlighting) style to use.
|
||||||
|
pygments_style = 'sphinx'
|
||||||
|
|
||||||
|
# A list of ignored prefixes for module index sorting.
|
||||||
|
#modindex_common_prefix = []
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for HTML output ---------------------------------------------------
|
||||||
|
|
||||||
|
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||||
|
# a list of builtin themes.
|
||||||
|
html_theme = 'nature'
|
||||||
|
|
||||||
|
# Theme options are theme-specific and customize the look and feel of a theme
|
||||||
|
# further. For a list of options available for each theme, see the
|
||||||
|
# documentation.
|
||||||
|
html_theme_options = {
|
||||||
|
"nosidebar": True
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add any paths that contain custom themes here, relative to this directory.
|
||||||
|
#html_theme_path = []
|
||||||
|
|
||||||
|
# The name for this set of Sphinx documents. If None, it defaults to
|
||||||
|
# "<project> v<release> documentation".
|
||||||
|
#html_title = None
|
||||||
|
|
||||||
|
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||||
|
#html_short_title = None
|
||||||
|
|
||||||
|
# The name of an image file (relative to this directory) to place at the top
|
||||||
|
# of the sidebar.
|
||||||
|
#html_logo = None
|
||||||
|
|
||||||
|
# The name of an image file (within the static path) to use as favicon of the
|
||||||
|
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||||
|
# pixels large.
|
||||||
|
#html_favicon = None
|
||||||
|
|
||||||
|
# Add any paths that contain custom static files (such as style sheets) here,
|
||||||
|
# relative to this directory. They are copied after the builtin static files,
|
||||||
|
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||||
|
#html_static_path = ['_static']
|
||||||
|
|
||||||
|
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||||
|
# using the given strftime format.
|
||||||
|
#html_last_updated_fmt = '%b %d, %Y'
|
||||||
|
|
||||||
|
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||||
|
# typographically correct entities.
|
||||||
|
#html_use_smartypants = True
|
||||||
|
|
||||||
|
# Custom sidebar templates, maps document names to template names.
|
||||||
|
#html_sidebars = {}
|
||||||
|
|
||||||
|
# Additional templates that should be rendered to pages, maps page names to
|
||||||
|
# template names.
|
||||||
|
#html_additional_pages = {}
|
||||||
|
|
||||||
|
# If false, no module index is generated.
|
||||||
|
#html_domain_indices = True
|
||||||
|
|
||||||
|
# If false, no index is generated.
|
||||||
|
#html_use_index = True
|
||||||
|
|
||||||
|
# If true, the index is split into individual pages for each letter.
|
||||||
|
#html_split_index = False
|
||||||
|
|
||||||
|
# If true, links to the reST sources are added to the pages.
|
||||||
|
#html_show_sourcelink = True
|
||||||
|
|
||||||
|
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
||||||
|
#html_show_sphinx = True
|
||||||
|
|
||||||
|
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
||||||
|
#html_show_copyright = True
|
||||||
|
|
||||||
|
# If true, an OpenSearch description file will be output, and all pages will
|
||||||
|
# contain a <link> tag referring to it. The value of this option must be the
|
||||||
|
# base URL from which the finished HTML is served.
|
||||||
|
#html_use_opensearch = ''
|
||||||
|
|
||||||
|
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
||||||
|
#html_file_suffix = None
|
||||||
|
|
||||||
|
# Output file base name for HTML help builder.
|
||||||
|
htmlhelp_basename = 'PyAudiodoc'
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for LaTeX output --------------------------------------------------
|
||||||
|
|
||||||
|
latex_elements = {
|
||||||
|
# The paper size ('letterpaper' or 'a4paper').
|
||||||
|
#'papersize': 'letterpaper',
|
||||||
|
|
||||||
|
# The font size ('10pt', '11pt' or '12pt').
|
||||||
|
#'pointsize': '10pt',
|
||||||
|
|
||||||
|
# Additional stuff for the LaTeX preamble.
|
||||||
|
#'preamble': '',
|
||||||
|
}
|
||||||
|
|
||||||
|
# Grouping the document tree into LaTeX files. List of tuples
|
||||||
|
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||||
|
latex_documents = [
|
||||||
|
('index', 'PyAudio.tex', 'PyAudio Documentation',
|
||||||
|
'Hubert Pham', 'manual'),
|
||||||
|
]
|
||||||
|
|
||||||
|
# The name of an image file (relative to this directory) to place at the top of
|
||||||
|
# the title page.
|
||||||
|
#latex_logo = None
|
||||||
|
|
||||||
|
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||||
|
# not chapters.
|
||||||
|
#latex_use_parts = False
|
||||||
|
|
||||||
|
# If true, show page references after internal links.
|
||||||
|
#latex_show_pagerefs = False
|
||||||
|
|
||||||
|
# If true, show URL addresses after external links.
|
||||||
|
#latex_show_urls = False
|
||||||
|
|
||||||
|
# Documents to append as an appendix to all manuals.
|
||||||
|
#latex_appendices = []
|
||||||
|
|
||||||
|
# If false, no module index is generated.
|
||||||
|
#latex_domain_indices = True
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for manual page output --------------------------------------------
|
||||||
|
|
||||||
|
# One entry per manual page. List of tuples
|
||||||
|
# (source start file, name, description, authors, manual section).
|
||||||
|
man_pages = [
|
||||||
|
('index', 'pyaudio', 'PyAudio Documentation',
|
||||||
|
['Hubert Pham'], 1)
|
||||||
|
]
|
||||||
|
|
||||||
|
# If true, show URL addresses after external links.
|
||||||
|
#man_show_urls = False
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for Texinfo output ------------------------------------------------
|
||||||
|
|
||||||
|
# Grouping the document tree into Texinfo files. List of tuples
|
||||||
|
# (source start file, target name, title, author,
|
||||||
|
# dir menu entry, description, category)
|
||||||
|
texinfo_documents = [
|
||||||
|
('index', 'PyAudio', 'PyAudio Documentation',
|
||||||
|
'Hubert Pham', 'PyAudio', 'One line description of project.',
|
||||||
|
'Miscellaneous'),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Documents to append as an appendix to all manuals.
|
||||||
|
#texinfo_appendices = []
|
||||||
|
|
||||||
|
# If false, no module index is generated.
|
||||||
|
#texinfo_domain_indices = True
|
||||||
|
|
||||||
|
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
||||||
|
#texinfo_show_urls = 'footnote'
|
||||||
|
|
||||||
|
try:
|
||||||
|
from _portaudio import paMacCoreStreamInfo
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
tags.add('pamac')
|
|
@ -0,0 +1,50 @@
|
||||||
|
Example: Blocking Mode Audio I/O
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
.. literalinclude:: ../examples/play_wave.py
|
||||||
|
|
||||||
|
To use PyAudio, first instantiate PyAudio using
|
||||||
|
:py:func:`pyaudio.PyAudio` (1), which sets up the portaudio system.
|
||||||
|
|
||||||
|
To record or play audio, open a stream on the desired device with the
|
||||||
|
desired audio parameters using :py:func:`pyaudio.PyAudio.open`
|
||||||
|
(2). This sets up a :py:class:`pyaudio.Stream` to play or record
|
||||||
|
audio.
|
||||||
|
|
||||||
|
Play audio by writing audio data to the stream using
|
||||||
|
:py:func:`pyaudio.Stream.write`, or read audio data from the stream
|
||||||
|
using :py:func:`pyaudio.Stream.read`. (3)
|
||||||
|
|
||||||
|
Note that in "blocking mode", each :py:func:`pyaudio.Stream.write` or
|
||||||
|
:py:func:`pyaudio.Stream.read` blocks until all the given/requested
|
||||||
|
frames have been played/recorded. Alternatively, to generate audio
|
||||||
|
data on the fly or immediately process recorded audio data, use the
|
||||||
|
"callback mode" outlined below.
|
||||||
|
|
||||||
|
Use :py:func:`pyaudio.Stream.stop_stream` to pause playing/recording,
|
||||||
|
and :py:func:`pyaudio.Stream.close` to terminate the stream. (4)
|
||||||
|
|
||||||
|
Finally, terminate the portaudio session using
|
||||||
|
:py:func:`pyaudio.PyAudio.terminate` (5)
|
||||||
|
|
||||||
|
Example: Callback Mode Audio I/O
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
.. literalinclude:: ../examples/play_wave_callback.py
|
||||||
|
|
||||||
|
In callback mode, PyAudio will call a specified callback function (2)
|
||||||
|
whenever it needs new audio data (to play) and/or when there is new
|
||||||
|
(recorded) audio data available. Note that PyAudio calls the callback
|
||||||
|
function in a separate thread. The function has the following
|
||||||
|
signature ``callback(<input_data>, <frame_count>, <time_info>,
|
||||||
|
<status_flag>)`` and must return a tuple containing ``frame_count``
|
||||||
|
frames of audio data and a flag signifying whether there are more
|
||||||
|
frames to play/record.
|
||||||
|
|
||||||
|
Start processing the audio stream using
|
||||||
|
:py:func:`pyaudio.Stream.start_stream` (4), which will call the
|
||||||
|
callback function repeatedly until that function returns
|
||||||
|
:py:data:`pyaudio.paComplete`.
|
||||||
|
|
||||||
|
To keep the stream active, the main thread must not terminate, e.g.,
|
||||||
|
by sleeping (5).
|
|
@ -0,0 +1,52 @@
|
||||||
|
PyAudio Documentation
|
||||||
|
=====================
|
||||||
|
|
||||||
|
.. contents::
|
||||||
|
|
||||||
|
------------
|
||||||
|
Introduction
|
||||||
|
------------
|
||||||
|
|
||||||
|
.. automodule:: pyaudio
|
||||||
|
:members:
|
||||||
|
:special-members:
|
||||||
|
:exclude-members: PyAudio, Stream, PaMacCoreStreamInfo
|
||||||
|
|
||||||
|
Details
|
||||||
|
-------
|
||||||
|
|
||||||
|
-------------
|
||||||
|
Class PyAudio
|
||||||
|
-------------
|
||||||
|
|
||||||
|
.. autoclass:: pyaudio.PyAudio
|
||||||
|
:members:
|
||||||
|
:special-members:
|
||||||
|
|
||||||
|
------------
|
||||||
|
Class Stream
|
||||||
|
------------
|
||||||
|
|
||||||
|
.. autoclass:: pyaudio.Stream
|
||||||
|
:members:
|
||||||
|
:special-members:
|
||||||
|
|
||||||
|
-----------------
|
||||||
|
Platform Specific
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
.. only:: pamac
|
||||||
|
|
||||||
|
Class PaMacCoreStreamInfo
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
.. autoclass:: pyaudio.PaMacCoreStreamInfo
|
||||||
|
:members:
|
||||||
|
:special-members:
|
||||||
|
|
||||||
|
|
||||||
|
Indices and tables
|
||||||
|
==================
|
||||||
|
|
||||||
|
* :ref:`genindex`
|
||||||
|
* :ref:`search`
|
File diff suppressed because it is too large
Load Diff
|
@ -3,7 +3,7 @@
|
||||||
*
|
*
|
||||||
* PyAudio : API Header File
|
* PyAudio : API Header File
|
||||||
*
|
*
|
||||||
* Copyright (c) 2006-2008 Hubert Pham
|
* Copyright (c) 2006 Hubert Pham
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person
|
* Permission is hereby granted, free of charge, to any person
|
||||||
* obtaining a copy of this software and associated documentation
|
* obtaining a copy of this software and associated documentation
|
||||||
|
|
800
src/pyaudio.py
800
src/pyaudio.py
File diff suppressed because it is too large
Load Diff
|
@ -1,77 +0,0 @@
|
||||||
# An example of what NOT to do:
|
|
||||||
# Don't reuse a stream object after closing it!
|
|
||||||
|
|
||||||
import _portaudio
|
|
||||||
import wave
|
|
||||||
import sys
|
|
||||||
|
|
||||||
chunk = 1024
|
|
||||||
|
|
||||||
def get_format_from_width(width, unsigned = True):
|
|
||||||
"""
|
|
||||||
Returns a PortAudio format constant for
|
|
||||||
the specified `width`.
|
|
||||||
|
|
||||||
:param `width`:
|
|
||||||
The desired sample width in bytes (1, 2, 3, or 4)
|
|
||||||
:param `unsigned`:
|
|
||||||
For 1 byte width, specifies signed or unsigned
|
|
||||||
format.
|
|
||||||
|
|
||||||
:raises ValueError: for invalid `width`
|
|
||||||
:rtype: `PaSampleFormat`
|
|
||||||
|
|
||||||
"""
|
|
||||||
p = _portaudio
|
|
||||||
|
|
||||||
if width == 1:
|
|
||||||
if unsigned:
|
|
||||||
return p.paUInt8
|
|
||||||
else:
|
|
||||||
return p.paInt8
|
|
||||||
elif width == 2:
|
|
||||||
return p.paInt16
|
|
||||||
elif width == 3:
|
|
||||||
return p.paInt24
|
|
||||||
elif width == 4:
|
|
||||||
return p.paFloat32
|
|
||||||
else:
|
|
||||||
raise ValueError, "Invalid width: %d" % width
|
|
||||||
|
|
||||||
|
|
||||||
if len(sys.argv) < 2:
|
|
||||||
print "Usage: %s filename.wav" % sys.argv[0]
|
|
||||||
sys.exit(-1)
|
|
||||||
|
|
||||||
wf = wave.open(sys.argv[1], 'rb')
|
|
||||||
|
|
||||||
print "* initializing"
|
|
||||||
_portaudio.initialize()
|
|
||||||
|
|
||||||
print "* opening"
|
|
||||||
stream = _portaudio.open(format = get_format_from_width(wf.getsampwidth()),
|
|
||||||
channels = wf.getnchannels(),
|
|
||||||
rate = wf.getframerate(),
|
|
||||||
input = True,
|
|
||||||
output = True)
|
|
||||||
|
|
||||||
data = wf.readframes(chunk)
|
|
||||||
|
|
||||||
print "* starting stream"
|
|
||||||
_portaudio.start_stream(stream)
|
|
||||||
|
|
||||||
while data != '':
|
|
||||||
_portaudio.write_stream(stream, data, chunk)
|
|
||||||
data = wf.readframes(chunk)
|
|
||||||
|
|
||||||
# OK...
|
|
||||||
_portaudio.close(stream)
|
|
||||||
|
|
||||||
# Fixed -- no longer relevant. An exception will be thrown.
|
|
||||||
|
|
||||||
# -----DEPRECATED COMMENT:
|
|
||||||
# BUT! don't re-use the stream object after closing it!
|
|
||||||
# Depending on the platform, this might crash Python.
|
|
||||||
print "* CRASH ----------*"
|
|
||||||
print _portaudio.get_stream_read_available(stream)
|
|
||||||
|
|
|
@ -1,266 +0,0 @@
|
||||||
/** @file patest_read_record.c
|
|
||||||
@brief Record input into an array; Save array to a file; Playback recorded
|
|
||||||
data. Implemented using the blocking API (Pa_ReadStream(), Pa_WriteStream() )
|
|
||||||
@author Phil Burk http://www.softsynth.com
|
|
||||||
@author Ross Bencina rossb@audiomulch.com
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
* $Id: patest_read_record.c 757 2004-02-13 07:48:10Z rossbencina $
|
|
||||||
*
|
|
||||||
* This program uses the PortAudio Portable Audio Library.
|
|
||||||
* For more information see: http://www.portaudio.com
|
|
||||||
* Copyright (c) 1999-2000 Ross Bencina and Phil Burk
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
* a copy of this software and associated documentation files
|
|
||||||
* (the "Software"), to deal in the Software without restriction,
|
|
||||||
* including without limitation the rights to use, copy, modify, merge,
|
|
||||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
|
||||||
* and to permit persons to whom the Software is furnished to do so,
|
|
||||||
* subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be
|
|
||||||
* included in all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* Any person wishing to distribute modifications to the Software is
|
|
||||||
* requested to send the modifications to the original developer so that
|
|
||||||
* they can be incorporated into the canonical version.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
||||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
|
||||||
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
|
||||||
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include "portaudio.h"
|
|
||||||
|
|
||||||
/* #define SAMPLE_RATE (17932) /* Test failure to open with this value. */
|
|
||||||
#define SAMPLE_RATE (44100)
|
|
||||||
#define FRAMES_PER_BUFFER (1024)
|
|
||||||
#define NUM_SECONDS (5)
|
|
||||||
#define NUM_CHANNELS (2)
|
|
||||||
/* #define DITHER_FLAG (paDitherOff) */
|
|
||||||
#define DITHER_FLAG (0) /**/
|
|
||||||
|
|
||||||
/* Select sample format. */
|
|
||||||
#if 1
|
|
||||||
#define PA_SAMPLE_TYPE paFloat32
|
|
||||||
typedef float SAMPLE;
|
|
||||||
#define SAMPLE_SILENCE (0.0f)
|
|
||||||
#define PRINTF_S_FORMAT "%.8f"
|
|
||||||
#elif 1
|
|
||||||
#define PA_SAMPLE_TYPE paInt16
|
|
||||||
typedef short SAMPLE;
|
|
||||||
#define SAMPLE_SILENCE (0)
|
|
||||||
#define PRINTF_S_FORMAT "%d"
|
|
||||||
#elif 0
|
|
||||||
#define PA_SAMPLE_TYPE paInt8
|
|
||||||
typedef char SAMPLE;
|
|
||||||
#define SAMPLE_SILENCE (0)
|
|
||||||
#define PRINTF_S_FORMAT "%d"
|
|
||||||
#else
|
|
||||||
#define PA_SAMPLE_TYPE paUInt8
|
|
||||||
typedef unsigned char SAMPLE;
|
|
||||||
#define SAMPLE_SILENCE (128)
|
|
||||||
#define PRINTF_S_FORMAT "%d"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define FORMATID "fmt "
|
|
||||||
#define DATAID "data"
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
char chunkID[4];
|
|
||||||
long chunkSize;
|
|
||||||
short wFormatTag;
|
|
||||||
unsigned short wChannels;
|
|
||||||
unsigned long dwSamplesPerSec;
|
|
||||||
unsigned long dwAvgBytesPerSec;
|
|
||||||
unsigned short wBlockAlign;
|
|
||||||
unsigned short wBitsPerSample;
|
|
||||||
} FormatChunk;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
char chunkID[4];
|
|
||||||
long chunkSize;
|
|
||||||
} DataChunkHeader;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int checkHeader(FILE *fid) {
|
|
||||||
|
|
||||||
char riff[4];
|
|
||||||
char wave[4];
|
|
||||||
|
|
||||||
fread(riff, 4, sizeof(char), fid);
|
|
||||||
/* throwaway 4 bytes */
|
|
||||||
fread(wave, 4, sizeof(char), fid);
|
|
||||||
fread(wave, 4, sizeof(char), fid);
|
|
||||||
|
|
||||||
if (!((strncmp(riff, "RIFF", 4) == 0) &&
|
|
||||||
(strncmp(wave, "WAVE", 4) == 0))) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int getData(FILE *fid, char **data) {
|
|
||||||
|
|
||||||
DataChunkHeader dch;
|
|
||||||
|
|
||||||
while (strncmp(dch.chunkID, DATAID, 4) != 0) {
|
|
||||||
fread(&dch, sizeof(DataChunkHeader), 1, fid);
|
|
||||||
if (feof(fid) || ferror(fid))
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("Size of data: %d\n", dch.chunkSize);
|
|
||||||
|
|
||||||
*data = (char *) malloc ( dch.chunkSize * sizeof(char) );
|
|
||||||
fread(*data, sizeof(char), dch.chunkSize, fid);
|
|
||||||
if (feof(fid) || ferror(fid)) {
|
|
||||||
free(data);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return dch.chunkSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
int getFormatChunk(FILE *fid, FormatChunk *formatChunk) {
|
|
||||||
|
|
||||||
while (strncmp(formatChunk->chunkID, FORMATID, 4) != 0) {
|
|
||||||
fread(formatChunk, sizeof(FormatChunk), 1, fid);
|
|
||||||
if (feof(fid) || ferror(fid))
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************/
|
|
||||||
int main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
PaStreamParameters outputParameters;
|
|
||||||
PaStream *stream;
|
|
||||||
PaError err;
|
|
||||||
/*int i;
|
|
||||||
int totalFrames;
|
|
||||||
int numSamples;
|
|
||||||
int numBytes;
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* read wave file */
|
|
||||||
char *filename;
|
|
||||||
FILE *fid;
|
|
||||||
|
|
||||||
if (argc < 2) {
|
|
||||||
printf("Usage: %s filename.wav\n", argv[0]);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* filename */
|
|
||||||
filename = argv[1];
|
|
||||||
printf("Filename: %s\n", filename);
|
|
||||||
|
|
||||||
/* open file */
|
|
||||||
fid = fopen(filename, "rb");
|
|
||||||
if (fid == NULL) {
|
|
||||||
printf("Could not open file %s\n", filename);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* check header */
|
|
||||||
if (checkHeader(fid) < 0) {
|
|
||||||
printf("Not a wave file!\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
FormatChunk formatChunk;
|
|
||||||
int data_size;
|
|
||||||
char *data;
|
|
||||||
|
|
||||||
if (getFormatChunk(fid, &formatChunk) < 0) {
|
|
||||||
printf("Couldn't read header\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("Chunk Size : %d\n", formatChunk.chunkSize);
|
|
||||||
printf("Compressed : %d\n", formatChunk.wFormatTag != 1);
|
|
||||||
printf("Channels : %d\n", formatChunk.wChannels);
|
|
||||||
printf("SamplesPerSecond : %d\n", formatChunk.dwSamplesPerSec);
|
|
||||||
printf("dwAvgBytesPerSec : %d\n", formatChunk.dwAvgBytesPerSec);
|
|
||||||
printf("wBlockAlign : %d\n", formatChunk.wBlockAlign);
|
|
||||||
printf("wBitsPerSample : %d\n", formatChunk.wBitsPerSample);
|
|
||||||
|
|
||||||
if ((data_size = getData(fid, &data)) < 0) {
|
|
||||||
printf("Couldn't read data\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int total_frames = data_size / formatChunk.wBlockAlign;
|
|
||||||
|
|
||||||
printf("Total Frames : %d\n", total_frames);
|
|
||||||
/* fclose(fid); */
|
|
||||||
|
|
||||||
|
|
||||||
err = Pa_Initialize();
|
|
||||||
if( err != paNoError ) goto error;
|
|
||||||
|
|
||||||
/* Playback recorded data. -------------------------------------------- */
|
|
||||||
|
|
||||||
outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
|
|
||||||
|
|
||||||
outputParameters.channelCount = formatChunk.wChannels;
|
|
||||||
outputParameters.sampleFormat = paInt16;
|
|
||||||
|
|
||||||
outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
|
|
||||||
|
|
||||||
printf("YO YO\n"); fflush(stdout);
|
|
||||||
outputParameters.hostApiSpecificStreamInfo = NULL;
|
|
||||||
|
|
||||||
printf("Begin playback.\n"); fflush(stdout);
|
|
||||||
err = Pa_OpenStream(
|
|
||||||
&stream,
|
|
||||||
NULL, /* no input */
|
|
||||||
&outputParameters,
|
|
||||||
formatChunk.dwSamplesPerSec,
|
|
||||||
0, /*FRAMES_PER_BUFFER, */
|
|
||||||
paClipOff, /* we won't output out of range samples so don't bother clipping them */
|
|
||||||
NULL, /* no callback, use blocking API */
|
|
||||||
NULL ); /* no callback, so no callback userData */
|
|
||||||
if( err != paNoError ) goto error;
|
|
||||||
|
|
||||||
if( stream )
|
|
||||||
{
|
|
||||||
err = Pa_StartStream( stream );
|
|
||||||
if( err != paNoError ) goto error;
|
|
||||||
printf("Waiting for playback to finish.\n"); fflush(stdout);
|
|
||||||
|
|
||||||
err = Pa_WriteStream( stream, data, total_frames );
|
|
||||||
if( err != paNoError ) goto error;
|
|
||||||
|
|
||||||
err = Pa_CloseStream( stream );
|
|
||||||
if( err != paNoError ) goto error;
|
|
||||||
printf("Done.\n"); fflush(stdout);
|
|
||||||
}
|
|
||||||
free( data );
|
|
||||||
|
|
||||||
Pa_Terminate();
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
error:
|
|
||||||
Pa_Terminate();
|
|
||||||
fprintf( stderr, "An error occured while using the portaudio stream\n" );
|
|
||||||
fprintf( stderr, "Error number: %d\n", err );
|
|
||||||
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,82 +0,0 @@
|
||||||
"""
|
|
||||||
PyAudio Example: Low Level C Module test.
|
|
||||||
|
|
||||||
Play a wave file.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import _portaudio
|
|
||||||
import wave
|
|
||||||
import sys
|
|
||||||
|
|
||||||
def get_format_from_width(width, unsigned = True):
|
|
||||||
"""
|
|
||||||
Returns a PortAudio format constant for
|
|
||||||
the specified `width`.
|
|
||||||
|
|
||||||
:param `width`:
|
|
||||||
The desired sample width in bytes (1, 2, 3, or 4)
|
|
||||||
:param `unsigned`:
|
|
||||||
For 1 byte width, specifies signed or unsigned
|
|
||||||
format.
|
|
||||||
|
|
||||||
:raises ValueError: for invalid `width`
|
|
||||||
:rtype: `PaSampleFormat`
|
|
||||||
|
|
||||||
"""
|
|
||||||
p = _portaudio
|
|
||||||
|
|
||||||
if width == 1:
|
|
||||||
if unsigned:
|
|
||||||
return p.paUInt8
|
|
||||||
else:
|
|
||||||
return p.paInt8
|
|
||||||
elif width == 2:
|
|
||||||
return p.paInt16
|
|
||||||
elif width == 3:
|
|
||||||
return p.paInt24
|
|
||||||
elif width == 4:
|
|
||||||
return p.paFloat32
|
|
||||||
else:
|
|
||||||
raise ValueError, "Invalid width: %d" % width
|
|
||||||
|
|
||||||
|
|
||||||
chunk = 1024
|
|
||||||
|
|
||||||
if len(sys.argv) < 2:
|
|
||||||
print "Plays a wave file.\n\nUsage: %s filename.wav" % sys.argv[0]
|
|
||||||
sys.exit(-1)
|
|
||||||
|
|
||||||
wf = wave.open(sys.argv[1], 'rb')
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
print "* initializing"
|
|
||||||
_portaudio.initialize()
|
|
||||||
|
|
||||||
print "* opening"
|
|
||||||
stream = _portaudio.open(rate = wf.getframerate(),
|
|
||||||
channels = wf.getnchannels(),
|
|
||||||
format = get_format_from_width(wf.getsampwidth()),
|
|
||||||
output = True)
|
|
||||||
|
|
||||||
data = wf.readframes(chunk)
|
|
||||||
|
|
||||||
print "* starting stream"
|
|
||||||
_portaudio.start_stream(stream)
|
|
||||||
|
|
||||||
print "available: %d" % _portaudio.get_stream_write_available(stream)
|
|
||||||
|
|
||||||
while data != '':
|
|
||||||
_portaudio.write_stream(stream, data, chunk)
|
|
||||||
data = wf.readframes(chunk)
|
|
||||||
|
|
||||||
print "* stopping stream"
|
|
||||||
_portaudio.stop_stream(stream)
|
|
||||||
|
|
||||||
print "* closing stream"
|
|
||||||
_portaudio.close(stream)
|
|
||||||
|
|
||||||
# always match an initialize() call with a terminate()
|
|
||||||
_portaudio.terminate()
|
|
||||||
|
|
||||||
|
|
|
@ -1,61 +0,0 @@
|
||||||
"""
|
|
||||||
PyAudio Example: Low Level C Module test.
|
|
||||||
|
|
||||||
Record a few seconds of audio and save to a WAVE file.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import _portaudio
|
|
||||||
import wave
|
|
||||||
import sys
|
|
||||||
|
|
||||||
chunk = 1024
|
|
||||||
|
|
||||||
FORMAT = _portaudio.paInt16
|
|
||||||
CHANNELS = 2
|
|
||||||
RATE = 44100
|
|
||||||
RECORD_SECONDS = 5
|
|
||||||
WAVE_OUTPUT_FILENAME = "output.wav"
|
|
||||||
|
|
||||||
if sys.platform == 'darwin':
|
|
||||||
CHANNELS = 1
|
|
||||||
|
|
||||||
print "* initializing"
|
|
||||||
_portaudio.initialize()
|
|
||||||
|
|
||||||
print "* opening"
|
|
||||||
stream = _portaudio.open(format = FORMAT,
|
|
||||||
channels = CHANNELS,
|
|
||||||
rate = RATE,
|
|
||||||
input = True,
|
|
||||||
frames_per_buffer = chunk)
|
|
||||||
|
|
||||||
print "* starting stream"
|
|
||||||
_portaudio.start_stream(stream)
|
|
||||||
|
|
||||||
print "* recording"
|
|
||||||
all = []
|
|
||||||
|
|
||||||
for i in range(0, 44100 / chunk * RECORD_SECONDS):
|
|
||||||
data = _portaudio.read_stream(stream, chunk)
|
|
||||||
all.append(data)
|
|
||||||
|
|
||||||
print "* stopping stream"
|
|
||||||
_portaudio.stop_stream(stream)
|
|
||||||
|
|
||||||
print "* closing stream"
|
|
||||||
_portaudio.close(stream)
|
|
||||||
|
|
||||||
# match all initialize() with terminate() calls
|
|
||||||
_portaudio.terminate()
|
|
||||||
|
|
||||||
# write data to WAVE file
|
|
||||||
data = ''.join(all)
|
|
||||||
|
|
||||||
wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
|
|
||||||
wf.setnchannels(CHANNELS)
|
|
||||||
wf.setsampwidth(_portaudio.get_sample_size(FORMAT))
|
|
||||||
wf.setframerate(RATE)
|
|
||||||
wf.writeframes(data)
|
|
||||||
wf.close()
|
|
||||||
|
|
||||||
|
|
|
@ -1,60 +0,0 @@
|
||||||
"""
|
|
||||||
PyAudio Example: Low Level C Module test.
|
|
||||||
|
|
||||||
Display detected Host APIs and Devices.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import _portaudio as p
|
|
||||||
|
|
||||||
p.initialize()
|
|
||||||
max_apis = p.get_host_api_count()
|
|
||||||
max_devs = p.get_device_count()
|
|
||||||
|
|
||||||
print "\nPortAudio System Info:\n======================"
|
|
||||||
print "Version: %d" % p.get_version()
|
|
||||||
print "Version Text: %s" % p.get_version_text()
|
|
||||||
print "Number of Host APIs: %d" % max_apis
|
|
||||||
print "Number of Devices : %d" % max_devs
|
|
||||||
|
|
||||||
print "\nHost APIs:\n=========="
|
|
||||||
|
|
||||||
for i in range(max_apis):
|
|
||||||
apiinfo = p.get_host_api_info(i)
|
|
||||||
print "Number : ", i
|
|
||||||
print "Name : ", apiinfo.name
|
|
||||||
print "Type : ", apiinfo.type
|
|
||||||
print "Devices : ", apiinfo.deviceCount
|
|
||||||
print "defaultInputDevice : ", apiinfo.defaultInputDevice
|
|
||||||
print "defaultOutputDevice : ", apiinfo.defaultOutputDevice
|
|
||||||
print "--------------------------"
|
|
||||||
|
|
||||||
print "\nDevices:\n========"
|
|
||||||
|
|
||||||
for i in range(max_devs):
|
|
||||||
devinfo = p.get_device_info(i)
|
|
||||||
print "Number : ", i
|
|
||||||
print "Name : ", devinfo.name
|
|
||||||
print "hostApi Index : ", devinfo.hostApi
|
|
||||||
print "maxInputChannels : ", devinfo.maxInputChannels
|
|
||||||
print "maxOutputChannels : ", devinfo.maxOutputChannels
|
|
||||||
print "defaultLowInputLatency : ", devinfo.defaultLowInputLatency
|
|
||||||
print "defaultLowOutputLatency : ", devinfo.defaultLowOutputLatency
|
|
||||||
print "defaultHighInputLatency : ", devinfo.defaultHighInputLatency
|
|
||||||
print "defaultHighOutputLatency : ", devinfo.defaultHighOutputLatency
|
|
||||||
print "defaultSampleRate : ", devinfo.defaultSampleRate
|
|
||||||
print "--------------------------------"
|
|
||||||
|
|
||||||
print "\nDefault Devices:\n================"
|
|
||||||
try:
|
|
||||||
print "Input :", p.get_default_input_device()
|
|
||||||
except IOError, e:
|
|
||||||
print "No Input devices."
|
|
||||||
|
|
||||||
try:
|
|
||||||
print "Output :", p.get_default_output_device()
|
|
||||||
except IOError, e:
|
|
||||||
print "No Output devices."
|
|
||||||
|
|
||||||
p.terminate()
|
|
||||||
|
|
||||||
|
|
|
@ -1,51 +0,0 @@
|
||||||
"""
|
|
||||||
PyAudio Example: Low Level C Module test.
|
|
||||||
|
|
||||||
Make a wire between input and output
|
|
||||||
(i.e., record a few samples and play them back immediately).
|
|
||||||
|
|
||||||
Full Duplex version. See wire2.py for Half Duplex.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import _portaudio
|
|
||||||
import sys
|
|
||||||
|
|
||||||
chunk = 1024
|
|
||||||
FORMAT = _portaudio.paInt16
|
|
||||||
CHANNELS = 2
|
|
||||||
RATE = 44100
|
|
||||||
RECORD_SECONDS = 5
|
|
||||||
|
|
||||||
# poor PPC macs...
|
|
||||||
if sys.platform == 'darwin':
|
|
||||||
CHANNELS = 1
|
|
||||||
|
|
||||||
print "* initializing"
|
|
||||||
_portaudio.initialize()
|
|
||||||
|
|
||||||
print "* opening"
|
|
||||||
stream = _portaudio.open(format = FORMAT,
|
|
||||||
channels = CHANNELS,
|
|
||||||
rate = RATE,
|
|
||||||
input = True,
|
|
||||||
output = True,
|
|
||||||
frames_per_buffer = chunk)
|
|
||||||
|
|
||||||
print "* starting stream"
|
|
||||||
_portaudio.start_stream(stream)
|
|
||||||
|
|
||||||
print "* recording"
|
|
||||||
|
|
||||||
for i in range(0, 44100 / chunk * RECORD_SECONDS):
|
|
||||||
data = _portaudio.read_stream(stream, chunk)
|
|
||||||
_portaudio.write_stream(stream, data, chunk)
|
|
||||||
|
|
||||||
print "* stopping stream"
|
|
||||||
_portaudio.stop_stream(stream)
|
|
||||||
|
|
||||||
print "* closing stream"
|
|
||||||
_portaudio.close(stream)
|
|
||||||
|
|
||||||
# match all initialize() with terminate() calls
|
|
||||||
_portaudio.terminate()
|
|
||||||
|
|
|
@ -1,61 +0,0 @@
|
||||||
"""
|
|
||||||
PyAudio Example: Low Level C Module test.
|
|
||||||
|
|
||||||
Make a wire between input and output
|
|
||||||
(i.e., record a few samples and play them back immediately).
|
|
||||||
|
|
||||||
Full Duplex version. See wire2.py for Half Duplex.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import _portaudio
|
|
||||||
import sys
|
|
||||||
|
|
||||||
chunk = 1024
|
|
||||||
FORMAT = _portaudio.paInt16
|
|
||||||
CHANNELS = 2
|
|
||||||
RATE = 44100
|
|
||||||
RECORD_SECONDS = 5
|
|
||||||
|
|
||||||
# poor PPC macs...
|
|
||||||
if sys.platform == 'darwin':
|
|
||||||
CHANNELS = 1
|
|
||||||
|
|
||||||
print "* initializing"
|
|
||||||
_portaudio.initialize()
|
|
||||||
|
|
||||||
print "* opening"
|
|
||||||
stream_input = _portaudio.open(format = FORMAT,
|
|
||||||
channels = CHANNELS,
|
|
||||||
rate = RATE,
|
|
||||||
input = True,
|
|
||||||
frames_per_buffer = chunk)
|
|
||||||
|
|
||||||
stream_output = _portaudio.open(format = FORMAT,
|
|
||||||
channels = CHANNELS,
|
|
||||||
rate = RATE,
|
|
||||||
output = True,
|
|
||||||
frames_per_buffer = chunk)
|
|
||||||
|
|
||||||
print "* starting stream"
|
|
||||||
_portaudio.start_stream(stream_input)
|
|
||||||
_portaudio.start_stream(stream_output)
|
|
||||||
|
|
||||||
print "* recording"
|
|
||||||
|
|
||||||
for i in range(0, 44100 / chunk * RECORD_SECONDS):
|
|
||||||
data = _portaudio.read_stream(stream_input, chunk)
|
|
||||||
_portaudio.write_stream(stream_output, data, chunk)
|
|
||||||
|
|
||||||
print "* stopping stream"
|
|
||||||
_portaudio.stop_stream(stream_input)
|
|
||||||
_portaudio.stop_stream(stream_output)
|
|
||||||
|
|
||||||
print "* closing stream"
|
|
||||||
_portaudio.close(stream_input)
|
|
||||||
_portaudio.close(stream_output)
|
|
||||||
|
|
||||||
# match initialize() with terminate() calls
|
|
||||||
_portaudio.terminate()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
""" PyAudio Example: Play a wave file """
|
|
||||||
|
|
||||||
import pyaudio
|
|
||||||
import wave
|
|
||||||
import sys
|
|
||||||
|
|
||||||
chunk = 1024
|
|
||||||
|
|
||||||
PyAudio = pyaudio.PyAudio
|
|
||||||
|
|
||||||
if len(sys.argv) < 2:
|
|
||||||
print "Plays a wave file.\n\nUsage: %s filename.wav" % sys.argv[0]
|
|
||||||
sys.exit(-1)
|
|
||||||
|
|
||||||
wf = wave.open(sys.argv[1], 'rb')
|
|
||||||
|
|
||||||
p = PyAudio()
|
|
||||||
|
|
||||||
# open stream
|
|
||||||
stream = p.open(format =
|
|
||||||
p.get_format_from_width(wf.getsampwidth()),
|
|
||||||
channels = wf.getnchannels(),
|
|
||||||
rate = wf.getframerate(),
|
|
||||||
output = True)
|
|
||||||
|
|
||||||
# read data
|
|
||||||
data = wf.readframes(chunk)
|
|
||||||
|
|
||||||
# play stream
|
|
||||||
while data != '':
|
|
||||||
stream.write(data)
|
|
||||||
data = wf.readframes(chunk)
|
|
||||||
|
|
||||||
stream.stop_stream()
|
|
||||||
stream.close()
|
|
||||||
|
|
||||||
p.terminate()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,48 +0,0 @@
|
||||||
"""
|
|
||||||
PyAudio example:
|
|
||||||
Record a few seconds of audio and save to a WAVE file.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import pyaudio
|
|
||||||
import wave
|
|
||||||
import sys
|
|
||||||
|
|
||||||
chunk = 1024
|
|
||||||
FORMAT = pyaudio.paInt16
|
|
||||||
CHANNELS = 2
|
|
||||||
RATE = 44100
|
|
||||||
RECORD_SECONDS = 5
|
|
||||||
WAVE_OUTPUT_FILENAME = "output.wav"
|
|
||||||
|
|
||||||
if sys.platform == 'darwin':
|
|
||||||
CHANNELS = 1
|
|
||||||
|
|
||||||
p = pyaudio.PyAudio()
|
|
||||||
|
|
||||||
stream = p.open(format = FORMAT,
|
|
||||||
channels = CHANNELS,
|
|
||||||
rate = RATE,
|
|
||||||
input = True,
|
|
||||||
frames_per_buffer = chunk)
|
|
||||||
|
|
||||||
print "* recording"
|
|
||||||
all = []
|
|
||||||
|
|
||||||
for i in range(0, RATE / chunk * RECORD_SECONDS):
|
|
||||||
data = stream.read(chunk)
|
|
||||||
all.append(data)
|
|
||||||
|
|
||||||
print "* done recording"
|
|
||||||
|
|
||||||
stream.stop_stream()
|
|
||||||
stream.close()
|
|
||||||
p.terminate()
|
|
||||||
|
|
||||||
# write data to WAVE file
|
|
||||||
data = ''.join(all)
|
|
||||||
wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
|
|
||||||
wf.setnchannels(CHANNELS)
|
|
||||||
wf.setsampwidth(p.get_sample_size(FORMAT))
|
|
||||||
wf.setframerate(RATE)
|
|
||||||
wf.writeframes(data)
|
|
||||||
wf.close()
|
|
40
test/wire.py
40
test/wire.py
|
@ -1,40 +0,0 @@
|
||||||
"""
|
|
||||||
PyAudio Example:
|
|
||||||
|
|
||||||
Make a wire between input and output (i.e., record a few samples
|
|
||||||
and play them back immediately).
|
|
||||||
|
|
||||||
Full Duplex version; see wire2.py for Half Duplex. """
|
|
||||||
|
|
||||||
import pyaudio
|
|
||||||
import sys
|
|
||||||
|
|
||||||
chunk = 1024
|
|
||||||
WIDTH = 2
|
|
||||||
CHANNELS = 2
|
|
||||||
RATE = 44100
|
|
||||||
RECORD_SECONDS = 5
|
|
||||||
|
|
||||||
if sys.platform == 'darwin':
|
|
||||||
CHANNELS = 1
|
|
||||||
|
|
||||||
p = pyaudio.PyAudio()
|
|
||||||
|
|
||||||
stream = p.open(format =
|
|
||||||
p.get_format_from_width(WIDTH),
|
|
||||||
channels = CHANNELS,
|
|
||||||
rate = RATE,
|
|
||||||
input = True,
|
|
||||||
output = True,
|
|
||||||
frames_per_buffer = chunk)
|
|
||||||
|
|
||||||
print "* recording"
|
|
||||||
for i in range(0, 44100 / chunk * RECORD_SECONDS):
|
|
||||||
data = stream.read(chunk)
|
|
||||||
stream.write(data, chunk)
|
|
||||||
print "* done"
|
|
||||||
|
|
||||||
stream.stop_stream()
|
|
||||||
stream.close()
|
|
||||||
p.terminate()
|
|
||||||
|
|
|
@ -1,50 +0,0 @@
|
||||||
"""
|
|
||||||
PyAudio Example:
|
|
||||||
|
|
||||||
Make a wire between input and output (i.e., record a few samples
|
|
||||||
and play them back immediately).
|
|
||||||
|
|
||||||
Half Duplex version; see wire.py for Full Duplex. """
|
|
||||||
|
|
||||||
import pyaudio
|
|
||||||
import sys
|
|
||||||
|
|
||||||
chunk = 1024
|
|
||||||
WIDTH = 2
|
|
||||||
CHANNELS = 2
|
|
||||||
RATE = 44100
|
|
||||||
RECORD_SECONDS = 5
|
|
||||||
|
|
||||||
if sys.platform == 'darwin':
|
|
||||||
CHANNELS = 1
|
|
||||||
|
|
||||||
p = pyaudio.PyAudio()
|
|
||||||
|
|
||||||
# use default input device
|
|
||||||
stream_input = p.open(format =
|
|
||||||
p.get_format_from_width(WIDTH),
|
|
||||||
channels = CHANNELS,
|
|
||||||
rate = RATE,
|
|
||||||
input = True,
|
|
||||||
frames_per_buffer = chunk)
|
|
||||||
|
|
||||||
# use default output device
|
|
||||||
stream_output = p.open(format =
|
|
||||||
p.get_format_from_width(WIDTH),
|
|
||||||
channels = CHANNELS,
|
|
||||||
rate = RATE,
|
|
||||||
output = True,
|
|
||||||
frames_per_buffer = chunk)
|
|
||||||
|
|
||||||
print "* recording"
|
|
||||||
for i in range(0, 44100 / chunk * RECORD_SECONDS):
|
|
||||||
data = stream_input.read(chunk)
|
|
||||||
stream_output.write(data, chunk)
|
|
||||||
print "* done"
|
|
||||||
|
|
||||||
stream_input.stop_stream()
|
|
||||||
stream_output.stop_stream()
|
|
||||||
stream_input.close()
|
|
||||||
stream_output.close()
|
|
||||||
p.terminate()
|
|
||||||
|
|
|
@ -0,0 +1,120 @@
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
import pyaudio
|
||||||
|
|
||||||
|
class PyAudioErrorTests(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.p = pyaudio.PyAudio()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.p.terminate()
|
||||||
|
|
||||||
|
def test_invalid_sample_size(self):
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
self.p.get_sample_size(10)
|
||||||
|
|
||||||
|
def test_invalid_width(self):
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
self.p.get_format_from_width(8)
|
||||||
|
|
||||||
|
def test_invalid_device(self):
|
||||||
|
with self.assertRaises(IOError):
|
||||||
|
self.p.get_host_api_info_by_type(-1)
|
||||||
|
|
||||||
|
def test_invalid_hostapi(self):
|
||||||
|
with self.assertRaises(IOError):
|
||||||
|
self.p.get_host_api_info_by_index(-1)
|
||||||
|
|
||||||
|
def test_invalid_host_api_devinfo(self):
|
||||||
|
with self.assertRaises(IOError):
|
||||||
|
self.p.get_device_info_by_host_api_device_index(0, -1)
|
||||||
|
|
||||||
|
with self.assertRaises(IOError):
|
||||||
|
self.p.get_device_info_by_host_api_device_index(-1, 0)
|
||||||
|
|
||||||
|
def test_invalid_device_devinfo(self):
|
||||||
|
with self.assertRaises(IOError):
|
||||||
|
self.p.get_device_info_by_index(-1)
|
||||||
|
|
||||||
|
def test_error_without_stream_start(self):
|
||||||
|
with self.assertRaises(IOError):
|
||||||
|
stream = self.p.open(channels=1,
|
||||||
|
rate=44100,
|
||||||
|
format=pyaudio.paInt16,
|
||||||
|
input=True,
|
||||||
|
start=False) # not starting stream
|
||||||
|
stream.read(2)
|
||||||
|
|
||||||
|
def test_error_writing_to_readonly_stream(self):
|
||||||
|
with self.assertRaises(IOError):
|
||||||
|
stream = self.p.open(channels=1,
|
||||||
|
rate=44100,
|
||||||
|
format=pyaudio.paInt16,
|
||||||
|
input=True)
|
||||||
|
stream.write('foo')
|
||||||
|
|
||||||
|
def test_error_negative_frames(self):
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
stream = self.p.open(channels=1,
|
||||||
|
rate=44100,
|
||||||
|
format=pyaudio.paInt16,
|
||||||
|
input=True)
|
||||||
|
stream.read(-1)
|
||||||
|
|
||||||
|
def test_invalid_attr_on_closed_stream(self):
|
||||||
|
stream = self.p.open(channels=1,
|
||||||
|
rate=44100,
|
||||||
|
format=pyaudio.paInt16,
|
||||||
|
input=True)
|
||||||
|
stream.close()
|
||||||
|
with self.assertRaises(IOError):
|
||||||
|
stream.get_input_latency()
|
||||||
|
with self.assertRaises(IOError):
|
||||||
|
stream.read(1)
|
||||||
|
|
||||||
|
def test_invalid_format_supported(self):
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
self.p.is_format_supported(8000, -1, 1, pyaudio.paInt16)
|
||||||
|
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
self.p.is_format_supported(8000, 0, -1, pyaudio.paInt16)
|
||||||
|
|
||||||
|
def test_write_underflow_exception(self):
|
||||||
|
stream = self.p.open(channels=1,
|
||||||
|
rate=44100,
|
||||||
|
format=pyaudio.paInt16,
|
||||||
|
output=True)
|
||||||
|
time.sleep(0.5)
|
||||||
|
stream.write('\x00\x00\x00\x00', exception_on_underflow=False)
|
||||||
|
|
||||||
|
# It's difficult to invoke an underflow on ALSA, so skip.
|
||||||
|
if sys.platform in ('linux', 'linux2'):
|
||||||
|
return
|
||||||
|
|
||||||
|
with self.assertRaises(IOError) as err:
|
||||||
|
time.sleep(0.5)
|
||||||
|
stream.write('\x00\x00\x00\x00', exception_on_underflow=True)
|
||||||
|
|
||||||
|
self.assertEqual(err.exception.errno, pyaudio.paOutputUnderflowed)
|
||||||
|
self.assertEqual(err.exception.strerror, 'Output underflowed')
|
||||||
|
|
||||||
|
def test_read_overflow_exception(self):
|
||||||
|
stream = self.p.open(channels=1,
|
||||||
|
rate=44100,
|
||||||
|
format=pyaudio.paInt16,
|
||||||
|
input=True)
|
||||||
|
time.sleep(0.5)
|
||||||
|
stream.read(2, exception_on_overflow=False)
|
||||||
|
|
||||||
|
# It's difficult to invoke an underflow on ALSA, so skip.
|
||||||
|
if sys.platform in ('linux', 'linux2'):
|
||||||
|
return
|
||||||
|
|
||||||
|
with self.assertRaises(IOError) as err:
|
||||||
|
time.sleep(0.5)
|
||||||
|
stream.read(2, exception_on_overflow=True)
|
||||||
|
|
||||||
|
self.assertEqual(err.exception.errno, pyaudio.paInputOverflowed)
|
||||||
|
self.assertEqual(err.exception.strerror, 'Input overflowed')
|
|
@ -0,0 +1,642 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""Automated unit tests for testing audio playback and capture.
|
||||||
|
|
||||||
|
These tests require an OS loopback sound device that forwards audio
|
||||||
|
output, generated by PyAudio for playback, and forwards it to an input
|
||||||
|
device, which PyAudio can record and verify against a test signal.
|
||||||
|
|
||||||
|
On Mac OS X, Soundflower can create such a device.
|
||||||
|
|
||||||
|
On GNU/Linux, the snd-aloop kernel module provides a loopback ALSA
|
||||||
|
device. Use examples/system_info.py to identify the name of the loopback
|
||||||
|
device.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import math
|
||||||
|
import struct
|
||||||
|
import time
|
||||||
|
import unittest
|
||||||
|
import wave
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import numpy
|
||||||
|
|
||||||
|
import pyaudio
|
||||||
|
|
||||||
|
DUMP_CAPTURE=False
|
||||||
|
|
||||||
|
class PyAudioTests(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.p = pyaudio.PyAudio()
|
||||||
|
(self.loopback_input_idx,
|
||||||
|
self.loopback_output_idx) = self.get_audio_loopback()
|
||||||
|
assert (self.loopback_input_idx is None
|
||||||
|
or self.loopback_input_idx >= 0), "No loopback device found"
|
||||||
|
assert (self.loopback_output_idx is None
|
||||||
|
or self.loopback_output_idx >= 0), "No loopback device found"
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.p.terminate()
|
||||||
|
|
||||||
|
def get_audio_loopback(self):
|
||||||
|
if sys.platform == 'darwin':
|
||||||
|
return self._find_audio_loopback(
|
||||||
|
'Soundflower (2ch)', 'Soundflower (2ch)')
|
||||||
|
if sys.platform in ('linux', 'linux2'):
|
||||||
|
return self._find_audio_loopback(
|
||||||
|
'Loopback: PCM (hw:1,0)', 'Loopback: PCM (hw:1,1)')
|
||||||
|
if sys.platform == 'win32':
|
||||||
|
# Assumes running in a VM, in which the hypervisor can
|
||||||
|
# set up a loopback device to back the "default" audio devices.
|
||||||
|
# Here, None indicates default device.
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
return -1, -1
|
||||||
|
|
||||||
|
def _find_audio_loopback(self, indev, outdev):
|
||||||
|
"""Utility to find audio loopback device."""
|
||||||
|
input_idx, output_idx = -1, -1
|
||||||
|
for device_idx in range(self.p.get_device_count()):
|
||||||
|
devinfo = self.p.get_device_info_by_index(device_idx)
|
||||||
|
if (outdev == devinfo.get('name') and
|
||||||
|
devinfo.get('maxOutputChannels', 0) > 0):
|
||||||
|
output_idx = device_idx
|
||||||
|
|
||||||
|
if (indev == devinfo.get('name') and
|
||||||
|
devinfo.get('maxInputChannels', 0) > 0):
|
||||||
|
input_idx = device_idx
|
||||||
|
|
||||||
|
if output_idx > -1 and input_idx > -1:
|
||||||
|
break
|
||||||
|
|
||||||
|
return input_idx, output_idx
|
||||||
|
|
||||||
|
def test_system_info(self):
|
||||||
|
"""Basic system info tests"""
|
||||||
|
self.assertTrue(self.p.get_host_api_count() > 0)
|
||||||
|
self.assertTrue(self.p.get_device_count() > 0)
|
||||||
|
api_info = self.p.get_host_api_info_by_index(0)
|
||||||
|
self.assertTrue(len(api_info.items()) > 0)
|
||||||
|
|
||||||
|
def test_input_output_blocking(self):
|
||||||
|
"""Test blocking-based record and playback."""
|
||||||
|
rate = 44100 # frames per second
|
||||||
|
width = 2 # bytes per sample
|
||||||
|
channels = 2
|
||||||
|
# Blocking-mode might add some initial choppiness on some
|
||||||
|
# platforms/loopback devices, so set a longer duration.
|
||||||
|
duration = 3 # seconds
|
||||||
|
frames_per_chunk = 1024
|
||||||
|
|
||||||
|
freqs = [130.81, 329.63, 440.0, 466.16, 587.33, 739.99]
|
||||||
|
test_signal = self.create_reference_signal(freqs, rate, width, duration)
|
||||||
|
audio_chunks = self.signal_to_chunks(
|
||||||
|
test_signal, frames_per_chunk, channels)
|
||||||
|
|
||||||
|
out_stream = self.p.open(
|
||||||
|
format=self.p.get_format_from_width(width),
|
||||||
|
channels=channels,
|
||||||
|
rate=rate,
|
||||||
|
output=True,
|
||||||
|
frames_per_buffer=frames_per_chunk,
|
||||||
|
output_device_index=self.loopback_output_idx)
|
||||||
|
in_stream = self.p.open(
|
||||||
|
format=self.p.get_format_from_width(width),
|
||||||
|
channels=channels,
|
||||||
|
rate=rate,
|
||||||
|
input=True,
|
||||||
|
frames_per_buffer=frames_per_chunk,
|
||||||
|
input_device_index=self.loopback_input_idx)
|
||||||
|
|
||||||
|
captured = []
|
||||||
|
for chunk in audio_chunks:
|
||||||
|
out_stream.write(chunk)
|
||||||
|
captured.append(in_stream.read(frames_per_chunk))
|
||||||
|
# Capture a few more frames, since there is some lag.
|
||||||
|
for i in range(8):
|
||||||
|
captured.append(in_stream.read(frames_per_chunk))
|
||||||
|
|
||||||
|
in_stream.stop_stream()
|
||||||
|
out_stream.stop_stream()
|
||||||
|
|
||||||
|
if DUMP_CAPTURE:
|
||||||
|
self.write_wav('test_blocking.wav', b''.join(captured),
|
||||||
|
width, channels, rate)
|
||||||
|
|
||||||
|
captured_signal = self.pcm16_to_numpy(b''.join(captured))
|
||||||
|
captured_left_channel = captured_signal[::2]
|
||||||
|
captured_right_channel = captured_signal[1::2]
|
||||||
|
|
||||||
|
self.assert_pcm16_spectrum_nearly_equal(
|
||||||
|
rate,
|
||||||
|
captured_left_channel,
|
||||||
|
test_signal,
|
||||||
|
len(freqs))
|
||||||
|
self.assert_pcm16_spectrum_nearly_equal(
|
||||||
|
rate,
|
||||||
|
captured_right_channel,
|
||||||
|
test_signal,
|
||||||
|
len(freqs))
|
||||||
|
|
||||||
|
def test_input_output_callback(self):
|
||||||
|
"""Test callback-based record and playback."""
|
||||||
|
rate = 44100 # frames per second
|
||||||
|
width = 2 # bytes per sample
|
||||||
|
channels = 2
|
||||||
|
duration = 1 # second
|
||||||
|
frames_per_chunk = 1024
|
||||||
|
|
||||||
|
freqs = [130.81, 329.63, 440.0, 466.16, 587.33, 739.99]
|
||||||
|
test_signal = self.create_reference_signal(freqs, rate, width, duration)
|
||||||
|
audio_chunks = self.signal_to_chunks(
|
||||||
|
test_signal, frames_per_chunk, channels)
|
||||||
|
|
||||||
|
state = {'count': 0}
|
||||||
|
def out_callback(_, frame_count, time_info, status):
|
||||||
|
if state['count'] >= len(audio_chunks):
|
||||||
|
return ('', pyaudio.paComplete)
|
||||||
|
rval = (audio_chunks[state['count']], pyaudio.paContinue)
|
||||||
|
state['count'] += 1
|
||||||
|
return rval
|
||||||
|
|
||||||
|
captured = []
|
||||||
|
def in_callback(in_data, frame_count, time_info, status):
|
||||||
|
captured.append(in_data)
|
||||||
|
return (None, pyaudio.paContinue)
|
||||||
|
|
||||||
|
out_stream = self.p.open(
|
||||||
|
format=self.p.get_format_from_width(width),
|
||||||
|
channels=channels,
|
||||||
|
rate=rate,
|
||||||
|
output=True,
|
||||||
|
frames_per_buffer=frames_per_chunk,
|
||||||
|
output_device_index=self.loopback_output_idx,
|
||||||
|
stream_callback=out_callback)
|
||||||
|
|
||||||
|
in_stream = self.p.open(
|
||||||
|
format=self.p.get_format_from_width(width),
|
||||||
|
channels=channels,
|
||||||
|
rate=rate,
|
||||||
|
input=True,
|
||||||
|
frames_per_buffer=frames_per_chunk,
|
||||||
|
input_device_index=self.loopback_input_idx,
|
||||||
|
stream_callback=in_callback)
|
||||||
|
|
||||||
|
in_stream.start_stream()
|
||||||
|
out_stream.start_stream()
|
||||||
|
time.sleep(duration + 1)
|
||||||
|
in_stream.stop_stream()
|
||||||
|
out_stream.stop_stream()
|
||||||
|
|
||||||
|
if DUMP_CAPTURE:
|
||||||
|
self.write_wav('test_callback.wav', b''.join(captured),
|
||||||
|
width, channels, rate)
|
||||||
|
|
||||||
|
captured_signal = self.pcm16_to_numpy(b''.join(captured))
|
||||||
|
captured_left_channel = captured_signal[::2]
|
||||||
|
captured_right_channel = captured_signal[1::2]
|
||||||
|
|
||||||
|
self.assert_pcm16_spectrum_nearly_equal(
|
||||||
|
rate,
|
||||||
|
captured_left_channel,
|
||||||
|
test_signal,
|
||||||
|
len(freqs))
|
||||||
|
self.assert_pcm16_spectrum_nearly_equal(
|
||||||
|
rate,
|
||||||
|
captured_right_channel,
|
||||||
|
test_signal,
|
||||||
|
len(freqs))
|
||||||
|
|
||||||
|
def test_device_lock_gil_order(self):
|
||||||
|
"""Ensure no deadlock between Pa_{Open,Start,Stop}Stream and GIL."""
|
||||||
|
# This test targets OSX/macOS CoreAudio, which seems to use
|
||||||
|
# audio device locks. On ALSA and Win32 MME, this problem
|
||||||
|
# doesn't seem to appear despite not releasing the GIL when
|
||||||
|
# calling into PortAudio.
|
||||||
|
rate = 44100 # frames per second
|
||||||
|
width = 2 # bytes per sample
|
||||||
|
channels = 2
|
||||||
|
frames_per_chunk = 1024
|
||||||
|
|
||||||
|
def out_callback(_, frame_count, time_info, status):
|
||||||
|
return ('', pyaudio.paComplete)
|
||||||
|
|
||||||
|
def in_callback(in_data, frame_count, time_info, status):
|
||||||
|
# Release the GIL for a bit
|
||||||
|
time.sleep(2)
|
||||||
|
return (None, pyaudio.paComplete)
|
||||||
|
|
||||||
|
in_stream = self.p.open(
|
||||||
|
format=self.p.get_format_from_width(width),
|
||||||
|
channels=channels,
|
||||||
|
rate=rate,
|
||||||
|
input=True,
|
||||||
|
start=False,
|
||||||
|
frames_per_buffer=frames_per_chunk,
|
||||||
|
input_device_index=self.loopback_input_idx,
|
||||||
|
stream_callback=in_callback)
|
||||||
|
# In a separate (C) thread, portaudio/driver will grab the device lock,
|
||||||
|
# then the GIL to call in_callback.
|
||||||
|
in_stream.start_stream()
|
||||||
|
# Wait a bit to let that callback thread start.
|
||||||
|
time.sleep(1)
|
||||||
|
# in_callback will eventually drop the GIL when executing
|
||||||
|
# time.sleep (while retaining the device lock), allowing the
|
||||||
|
# following code to run. Opening a stream and starting it MUST
|
||||||
|
# release the GIL before attempting to acquire the device
|
||||||
|
# lock. Otherwise, the following code will wait for the device
|
||||||
|
# lock (while holding the GIL), while the in_callback thread
|
||||||
|
# will be waiting for the GIL once time.sleep completes (while
|
||||||
|
# holding the device lock), leading to deadlock.
|
||||||
|
out_stream = self.p.open(
|
||||||
|
format=self.p.get_format_from_width(width),
|
||||||
|
channels=channels,
|
||||||
|
rate=rate,
|
||||||
|
output=True,
|
||||||
|
frames_per_buffer=frames_per_chunk,
|
||||||
|
output_device_index=self.loopback_output_idx,
|
||||||
|
stream_callback=out_callback)
|
||||||
|
out_stream.start_stream()
|
||||||
|
|
||||||
|
time.sleep(0.1)
|
||||||
|
in_stream.stop_stream()
|
||||||
|
out_stream.stop_stream()
|
||||||
|
|
||||||
|
def test_stream_state_gil(self):
|
||||||
|
"""Ensure no deadlock between Pa_IsStream{Active,Stopped} and GIL."""
|
||||||
|
rate = 44100 # frames per second
|
||||||
|
width = 2 # bytes per sample
|
||||||
|
channels = 2
|
||||||
|
frames_per_chunk = 1024
|
||||||
|
|
||||||
|
def out_callback(_, frame_count, time_info, status):
|
||||||
|
return ('', pyaudio.paComplete)
|
||||||
|
|
||||||
|
def in_callback(in_data, frame_count, time_info, status):
|
||||||
|
# Release the GIL for a bit
|
||||||
|
time.sleep(2)
|
||||||
|
return (None, pyaudio.paComplete)
|
||||||
|
|
||||||
|
in_stream = self.p.open(
|
||||||
|
format=self.p.get_format_from_width(width),
|
||||||
|
channels=channels,
|
||||||
|
rate=rate,
|
||||||
|
input=True,
|
||||||
|
start=False,
|
||||||
|
frames_per_buffer=frames_per_chunk,
|
||||||
|
input_device_index=self.loopback_input_idx,
|
||||||
|
stream_callback=in_callback)
|
||||||
|
out_stream = self.p.open(
|
||||||
|
format=self.p.get_format_from_width(width),
|
||||||
|
channels=channels,
|
||||||
|
rate=rate,
|
||||||
|
output=True,
|
||||||
|
start=False,
|
||||||
|
frames_per_buffer=frames_per_chunk,
|
||||||
|
output_device_index=self.loopback_output_idx,
|
||||||
|
stream_callback=out_callback)
|
||||||
|
# In a separate (C) thread, portaudio/driver will grab the device lock,
|
||||||
|
# then the GIL to call in_callback.
|
||||||
|
in_stream.start_stream()
|
||||||
|
# Wait a bit to let that callback thread start.
|
||||||
|
time.sleep(1)
|
||||||
|
# in_callback will eventually drop the GIL when executing
|
||||||
|
# time.sleep (while retaining the device lock), allowing the
|
||||||
|
# following code to run. Checking the state of the stream MUST
|
||||||
|
# not require the device lock, but if it does, it must release the GIL
|
||||||
|
# before attempting to acquire the device
|
||||||
|
# lock. Otherwise, the following code will wait for the device
|
||||||
|
# lock (while holding the GIL), while the in_callback thread
|
||||||
|
# will be waiting for the GIL once time.sleep completes (while
|
||||||
|
# holding the device lock), leading to deadlock.
|
||||||
|
self.assertTrue(in_stream.is_active())
|
||||||
|
self.assertFalse(in_stream.is_stopped())
|
||||||
|
|
||||||
|
self.assertTrue(out_stream.is_stopped())
|
||||||
|
self.assertFalse(out_stream.is_active())
|
||||||
|
out_stream.start_stream()
|
||||||
|
self.assertFalse(out_stream.is_stopped())
|
||||||
|
self.assertTrue(out_stream.is_active())
|
||||||
|
|
||||||
|
time.sleep(0.1)
|
||||||
|
in_stream.stop_stream()
|
||||||
|
out_stream.stop_stream()
|
||||||
|
|
||||||
|
def test_get_stream_time_gil(self):
|
||||||
|
"""Ensure no deadlock between PA_GetStreamTime and GIL."""
|
||||||
|
rate = 44100 # frames per second
|
||||||
|
width = 2 # bytes per sample
|
||||||
|
channels = 2
|
||||||
|
frames_per_chunk = 1024
|
||||||
|
|
||||||
|
def out_callback(_, frame_count, time_info, status):
|
||||||
|
return ('', pyaudio.paComplete)
|
||||||
|
|
||||||
|
def in_callback(in_data, frame_count, time_info, status):
|
||||||
|
# Release the GIL for a bit
|
||||||
|
time.sleep(2)
|
||||||
|
return (None, pyaudio.paComplete)
|
||||||
|
|
||||||
|
in_stream = self.p.open(
|
||||||
|
format=self.p.get_format_from_width(width),
|
||||||
|
channels=channels,
|
||||||
|
rate=rate,
|
||||||
|
input=True,
|
||||||
|
start=False,
|
||||||
|
frames_per_buffer=frames_per_chunk,
|
||||||
|
input_device_index=self.loopback_input_idx,
|
||||||
|
stream_callback=in_callback)
|
||||||
|
out_stream = self.p.open(
|
||||||
|
format=self.p.get_format_from_width(width),
|
||||||
|
channels=channels,
|
||||||
|
rate=rate,
|
||||||
|
output=True,
|
||||||
|
start=False,
|
||||||
|
frames_per_buffer=frames_per_chunk,
|
||||||
|
output_device_index=self.loopback_output_idx,
|
||||||
|
stream_callback=out_callback)
|
||||||
|
# In a separate (C) thread, portaudio/driver will grab the device lock,
|
||||||
|
# then the GIL to call in_callback.
|
||||||
|
in_stream.start_stream()
|
||||||
|
# Wait a bit to let that callback thread start.
|
||||||
|
time.sleep(1)
|
||||||
|
# in_callback will eventually drop the GIL when executing
|
||||||
|
# time.sleep (while retaining the device lock), allowing the
|
||||||
|
# following code to run. Getting the stream time MUST not
|
||||||
|
# require the device lock, but if it does, it must release the
|
||||||
|
# GIL before attempting to acquire the device lock. Otherwise,
|
||||||
|
# the following code will wait for the device lock (while
|
||||||
|
# holding the GIL), while the in_callback thread will be
|
||||||
|
# waiting for the GIL once time.sleep completes (while holding
|
||||||
|
# the device lock), leading to deadlock.
|
||||||
|
self.assertGreater(in_stream.get_time(), -1)
|
||||||
|
self.assertGreater(out_stream.get_time(), 1)
|
||||||
|
|
||||||
|
time.sleep(0.1)
|
||||||
|
in_stream.stop_stream()
|
||||||
|
out_stream.stop_stream()
|
||||||
|
|
||||||
|
def test_get_stream_cpuload_gil(self):
|
||||||
|
"""Ensure no deadlock between Pa_GetStreamCpuLoad and GIL."""
|
||||||
|
rate = 44100 # frames per second
|
||||||
|
width = 2 # bytes per sample
|
||||||
|
channels = 2
|
||||||
|
frames_per_chunk = 1024
|
||||||
|
|
||||||
|
def out_callback(_, frame_count, time_info, status):
|
||||||
|
return ('', pyaudio.paComplete)
|
||||||
|
|
||||||
|
def in_callback(in_data, frame_count, time_info, status):
|
||||||
|
# Release the GIL for a bit
|
||||||
|
time.sleep(2)
|
||||||
|
return (None, pyaudio.paComplete)
|
||||||
|
|
||||||
|
in_stream = self.p.open(
|
||||||
|
format=self.p.get_format_from_width(width),
|
||||||
|
channels=channels,
|
||||||
|
rate=rate,
|
||||||
|
input=True,
|
||||||
|
start=False,
|
||||||
|
frames_per_buffer=frames_per_chunk,
|
||||||
|
input_device_index=self.loopback_input_idx,
|
||||||
|
stream_callback=in_callback)
|
||||||
|
out_stream = self.p.open(
|
||||||
|
format=self.p.get_format_from_width(width),
|
||||||
|
channels=channels,
|
||||||
|
rate=rate,
|
||||||
|
output=True,
|
||||||
|
start=False,
|
||||||
|
frames_per_buffer=frames_per_chunk,
|
||||||
|
output_device_index=self.loopback_output_idx,
|
||||||
|
stream_callback=out_callback)
|
||||||
|
# In a separate (C) thread, portaudio/driver will grab the device lock,
|
||||||
|
# then the GIL to call in_callback.
|
||||||
|
in_stream.start_stream()
|
||||||
|
# Wait a bit to let that callback thread start.
|
||||||
|
time.sleep(1)
|
||||||
|
# in_callback will eventually drop the GIL when executing
|
||||||
|
# time.sleep (while retaining the device lock), allowing the
|
||||||
|
# following code to run. Getting the stream cpuload MUST not
|
||||||
|
# require the device lock, but if it does, it must release the
|
||||||
|
# GIL before attempting to acquire the device lock. Otherwise,
|
||||||
|
# the following code will wait for the device lock (while
|
||||||
|
# holding the GIL), while the in_callback thread will be
|
||||||
|
# waiting for the GIL once time.sleep completes (while holding
|
||||||
|
# the device lock), leading to deadlock.
|
||||||
|
self.assertGreater(in_stream.get_cpu_load(), -1)
|
||||||
|
self.assertGreater(out_stream.get_cpu_load(), -1)
|
||||||
|
|
||||||
|
time.sleep(0.1)
|
||||||
|
in_stream.stop_stream()
|
||||||
|
out_stream.stop_stream()
|
||||||
|
|
||||||
|
def test_get_stream_write_available_gil(self):
|
||||||
|
"""Ensure no deadlock between Pa_GetStreamWriteAvailable and GIL."""
|
||||||
|
rate = 44100 # frames per second
|
||||||
|
width = 2 # bytes per sample
|
||||||
|
channels = 2
|
||||||
|
frames_per_chunk = 1024
|
||||||
|
|
||||||
|
def in_callback(in_data, frame_count, time_info, status):
|
||||||
|
# Release the GIL for a bit
|
||||||
|
time.sleep(2)
|
||||||
|
return (None, pyaudio.paComplete)
|
||||||
|
|
||||||
|
in_stream = self.p.open(
|
||||||
|
format=self.p.get_format_from_width(width),
|
||||||
|
channels=channels,
|
||||||
|
rate=rate,
|
||||||
|
input=True,
|
||||||
|
start=False,
|
||||||
|
frames_per_buffer=frames_per_chunk,
|
||||||
|
input_device_index=self.loopback_input_idx,
|
||||||
|
stream_callback=in_callback)
|
||||||
|
out_stream = self.p.open(
|
||||||
|
format=self.p.get_format_from_width(width),
|
||||||
|
channels=channels,
|
||||||
|
rate=rate,
|
||||||
|
output=True,
|
||||||
|
frames_per_buffer=frames_per_chunk,
|
||||||
|
output_device_index=self.loopback_output_idx)
|
||||||
|
# In a separate (C) thread, portaudio/driver will grab the device lock,
|
||||||
|
# then the GIL to call in_callback.
|
||||||
|
in_stream.start_stream()
|
||||||
|
# Wait a bit to let that callback thread start.
|
||||||
|
time.sleep(1)
|
||||||
|
# in_callback will eventually drop the GIL when executing
|
||||||
|
# time.sleep (while retaining the device lock), allowing the
|
||||||
|
# following code to run. Getting the stream write available MUST not
|
||||||
|
# require the device lock, but if it does, it must release the
|
||||||
|
# GIL before attempting to acquire the device lock. Otherwise,
|
||||||
|
# the following code will wait for the device lock (while
|
||||||
|
# holding the GIL), while the in_callback thread will be
|
||||||
|
# waiting for the GIL once time.sleep completes (while holding
|
||||||
|
# the device lock), leading to deadlock.
|
||||||
|
self.assertGreater(out_stream.get_write_available(), -1)
|
||||||
|
|
||||||
|
time.sleep(0.1)
|
||||||
|
in_stream.stop_stream()
|
||||||
|
|
||||||
|
def test_get_stream_read_available_gil(self):
|
||||||
|
"""Ensure no deadlock between Pa_GetStreamReadAvailable and GIL."""
|
||||||
|
rate = 44100 # frames per second
|
||||||
|
width = 2 # bytes per sample
|
||||||
|
channels = 2
|
||||||
|
frames_per_chunk = 1024
|
||||||
|
|
||||||
|
def out_callback(in_data, frame_count, time_info, status):
|
||||||
|
# Release the GIL for a bit
|
||||||
|
time.sleep(2)
|
||||||
|
return (None, pyaudio.paComplete)
|
||||||
|
|
||||||
|
in_stream = self.p.open(
|
||||||
|
format=self.p.get_format_from_width(width),
|
||||||
|
channels=channels,
|
||||||
|
rate=rate,
|
||||||
|
input=True,
|
||||||
|
frames_per_buffer=frames_per_chunk,
|
||||||
|
input_device_index=self.loopback_input_idx)
|
||||||
|
out_stream = self.p.open(
|
||||||
|
format=self.p.get_format_from_width(width),
|
||||||
|
channels=channels,
|
||||||
|
rate=rate,
|
||||||
|
output=True,
|
||||||
|
start=False,
|
||||||
|
frames_per_buffer=frames_per_chunk,
|
||||||
|
output_device_index=self.loopback_output_idx,
|
||||||
|
stream_callback=out_callback)
|
||||||
|
# In a separate (C) thread, portaudio/driver will grab the device lock,
|
||||||
|
# then the GIL to call in_callback.
|
||||||
|
out_stream.start_stream()
|
||||||
|
# Wait a bit to let that callback thread start.
|
||||||
|
time.sleep(1)
|
||||||
|
# in_callback will eventually drop the GIL when executing
|
||||||
|
# time.sleep (while retaining the device lock), allowing the
|
||||||
|
# following code to run. Getting the stream read available MUST not
|
||||||
|
# require the device lock, but if it does, it must release the
|
||||||
|
# GIL before attempting to acquire the device lock. Otherwise,
|
||||||
|
# the following code will wait for the device lock (while
|
||||||
|
# holding the GIL), while the in_callback thread will be
|
||||||
|
# waiting for the GIL once time.sleep completes (while holding
|
||||||
|
# the device lock), leading to deadlock.
|
||||||
|
self.assertGreater(in_stream.get_read_available(), -1)
|
||||||
|
|
||||||
|
time.sleep(0.1)
|
||||||
|
in_stream.stop_stream()
|
||||||
|
|
||||||
|
def test_terminate_gil(self):
|
||||||
|
"""Ensure no deadlock between Pa_Terminate and GIL."""
|
||||||
|
rate = 44100 # frames per second
|
||||||
|
width = 2 # bytes per sample
|
||||||
|
channels = 2
|
||||||
|
frames_per_chunk = 1024
|
||||||
|
|
||||||
|
def out_callback(in_data, frame_count, time_info, status):
|
||||||
|
# Release the GIL for a bit
|
||||||
|
time.sleep(2)
|
||||||
|
return (None, pyaudio.paComplete)
|
||||||
|
|
||||||
|
out_stream = self.p.open(
|
||||||
|
format=self.p.get_format_from_width(width),
|
||||||
|
channels=channels,
|
||||||
|
rate=rate,
|
||||||
|
output=True,
|
||||||
|
start=False,
|
||||||
|
frames_per_buffer=frames_per_chunk,
|
||||||
|
output_device_index=self.loopback_output_idx,
|
||||||
|
stream_callback=out_callback)
|
||||||
|
# In a separate (C) thread, portaudio/driver will grab the device lock,
|
||||||
|
# then the GIL to call in_callback.
|
||||||
|
out_stream.start_stream()
|
||||||
|
# Wait a bit to let that callback thread start.
|
||||||
|
time.sleep(1)
|
||||||
|
# in_callback will eventually drop the GIL when executing
|
||||||
|
# time.sleep (while retaining the device lock), allowing the
|
||||||
|
# following code to run. Terminating PyAudio MUST not
|
||||||
|
# require the device lock, but if it does, it must release the
|
||||||
|
# GIL before attempting to acquire the device lock. Otherwise,
|
||||||
|
# the following code will wait for the device lock (while
|
||||||
|
# holding the GIL), while the in_callback thread will be
|
||||||
|
# waiting for the GIL once time.sleep completes (while holding
|
||||||
|
# the device lock), leading to deadlock.
|
||||||
|
self.p.terminate()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def create_reference_signal(freqs, sampling_rate, width, duration):
|
||||||
|
"""Return reference signal with several sinuoids with frequencies
|
||||||
|
specified by freqs."""
|
||||||
|
total_frames = int(sampling_rate * duration)
|
||||||
|
max_amp = float(2**(width * 8 - 1) - 1)
|
||||||
|
avg_amp = max_amp / len(freqs)
|
||||||
|
return [
|
||||||
|
int(sum(avg_amp * math.sin(2*math.pi*freq*(k/float(sampling_rate)))
|
||||||
|
for freq in freqs))
|
||||||
|
for k in range(total_frames)]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def signal_to_chunks(frame_data, frames_per_chunk, channels):
|
||||||
|
"""Given an array of values comprising the signal, return an iterable
|
||||||
|
of binary chunks, with each chunk containing frames_per_chunk
|
||||||
|
frames. Each frame represents a single value from the signal,
|
||||||
|
duplicated for each channel specified by channels.
|
||||||
|
"""
|
||||||
|
frames = [struct.pack('h', x) * channels for x in frame_data]
|
||||||
|
# Chop up frames into chunks
|
||||||
|
return [b''.join(chunk_frames) for chunk_frames in
|
||||||
|
tuple(frames[i:i+frames_per_chunk]
|
||||||
|
for i in range(0, len(frames), frames_per_chunk))]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def pcm16_to_numpy(bytestring):
|
||||||
|
"""From PCM 16-bit bytes, return an equivalent numpy array of values."""
|
||||||
|
return struct.unpack('%dh' % (len(bytestring) / 2), bytestring)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def write_wav(filename, data, width, channels, rate):
|
||||||
|
"""Write PCM data to wave file."""
|
||||||
|
wf = wave.open(filename, 'wb')
|
||||||
|
wf.setnchannels(channels)
|
||||||
|
wf.setsampwidth(width)
|
||||||
|
wf.setframerate(rate)
|
||||||
|
wf.writeframes(data)
|
||||||
|
wf.close()
|
||||||
|
|
||||||
|
def assert_pcm16_spectrum_nearly_equal(self, sampling_rate, cap, ref,
|
||||||
|
num_freq_peaks_expected):
|
||||||
|
"""Compares the discrete fourier transform of a captured signal
|
||||||
|
against the reference signal and ensures that the frequency peaks
|
||||||
|
match."""
|
||||||
|
# When passing a reference signal through the loopback device,
|
||||||
|
# the captured signal may include additional noise, as well as
|
||||||
|
# time lag, so testing that the captured signal is "similar
|
||||||
|
# enough" to the reference using bit-wise equality won't work
|
||||||
|
# well. Instead, the approach here a) assumes the reference
|
||||||
|
# signal is a sum of sinusoids and b) computes the discrete
|
||||||
|
# fourier transform of the reference and captured signals, and
|
||||||
|
# ensures that the frequencies of the top
|
||||||
|
# num_freq_peaks_expected frequency peaks are close.
|
||||||
|
cap_fft = numpy.absolute(numpy.fft.rfft(cap))
|
||||||
|
ref_fft = numpy.absolute(numpy.fft.rfft(ref))
|
||||||
|
# Find the indices of the peaks:
|
||||||
|
cap_peak_indices = sorted(numpy.argpartition(
|
||||||
|
cap_fft, -num_freq_peaks_expected)[-num_freq_peaks_expected:])
|
||||||
|
ref_peak_indices = sorted(numpy.argpartition(
|
||||||
|
ref_fft, -num_freq_peaks_expected)[-num_freq_peaks_expected:])
|
||||||
|
# Ensure that the corresponding frequencies of the peaks are close:
|
||||||
|
for cap_freq_index, ref_freq_index in zip(cap_peak_indices,
|
||||||
|
ref_peak_indices):
|
||||||
|
cap_freq = cap_freq_index / float(len(cap)) * (sampling_rate / 2)
|
||||||
|
ref_freq = ref_freq_index / float(len(ref)) * (sampling_rate / 2)
|
||||||
|
diff = abs(cap_freq - ref_freq)
|
||||||
|
self.assertLess(diff, 1.0)
|
||||||
|
|
||||||
|
# As an additional test, verify that the spectrum (not just
|
||||||
|
# the peaks) of the reference and captured signal are similar
|
||||||
|
# by computing the cross-correlation of the spectra. Assuming they
|
||||||
|
# are nearly identical, the cross-correlation should contain a large
|
||||||
|
# peak when the spectra overlap and mostly 0s elsewhere. Verify that
|
||||||
|
# using a histogram of the cross-correlation:
|
||||||
|
freq_corr_hist, _ = numpy.histogram(
|
||||||
|
numpy.correlate(cap_fft, ref_fft, mode='full'),
|
||||||
|
bins=10)
|
||||||
|
self.assertLess(sum(freq_corr_hist[2:])/sum(freq_corr_hist), 1e-2)
|
Loading…
Reference in New Issue