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>
|
||||
|
||||
PyAudio 0.2.4
|
||||
|
@ -39,4 +143,3 @@
|
|||
2008-02-12 Justin Mazzola Paluska <jmp@mit.edu>
|
||||
|
||||
- Initial version of debian packaging.
|
||||
|
||||
|
|
96
INSTALL
96
INSTALL
|
@ -8,81 +8,83 @@ platforms:
|
|||
* General UNIX Guide: (GNU/Linux, Mac OS X, Cygwin)
|
||||
* Microsoft Windows (native)
|
||||
|
||||
Generally speaking, you must first install the PortAudio v19 library
|
||||
before building PyAudio.
|
||||
|
||||
Generally speaking, installation involves building the PortAudio v19
|
||||
library and then building PyAudio.
|
||||
|
||||
----------------------------------------------------------------------
|
||||
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
|
||||
% make
|
||||
% 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
|
||||
|
||||
|
||||
----------------------------------------------------------------------
|
||||
Microsoft Windows
|
||||
----------------------------------------------------------------------
|
||||
|
||||
If you are targeting native Win32 Python, you will need either
|
||||
Microsoft Visual Studio or MinGW (via Cygwin). Here are compilation
|
||||
hints for using MinGW under the Cygwin build environment.
|
||||
Targeting native Win32 Python will require either Microsoft Visual
|
||||
Studio or MinGW (via Cygwin). Here are compilation hints for using
|
||||
MinGW under the Cygwin build environment.
|
||||
|
||||
Note: I've only tested this under Cygwin's build environment. Your
|
||||
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,
|
||||
I'd love to hear about it.)
|
||||
1. Install cygwin's gcc and mingw packages.
|
||||
|
||||
1. Download PortAudio to ./portaudio-v19 in this directory
|
||||
and build. When running configure, be sure to use ``-mno-cygwin``
|
||||
(under cygwin) to generate native Win32 binaries:
|
||||
2. Download PortAudio and build. When running configure, be sure to
|
||||
specify the MinGW compiler (via a CC environment variable) to
|
||||
generate native Win32 binaries:
|
||||
|
||||
% cd ./portaudio-v19
|
||||
% CFLAGS="-mno-cygwin" LDFLAGS="-mno-cygwin" ./configure
|
||||
% make
|
||||
% cd ..
|
||||
% CC=i686-w64-mingw32-gcc ./configure --enable-static --with-pic
|
||||
% make
|
||||
|
||||
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
|
||||
python. The --static-link option statically links in the PortAudio
|
||||
library to the PyAudio module, which is probably the best way to go
|
||||
on Windows.
|
||||
library to the PyAudio module.
|
||||
|
||||
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
|
||||
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.
|
||||
Or create a Python wheel and install using pip.
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
include src/*.c src/*.h src/*.py
|
||||
include Makefile CHANGELOG INSTALL MANIFEST.in
|
||||
recursive-include test *.py *.c
|
||||
graft docs
|
||||
recursive-include examples *.py
|
||||
recursive-include tests *.py
|
||||
graft sphinx
|
||||
|
|
35
Makefile
35
Makefile
|
@ -1,19 +1,24 @@
|
|||
# This is the PyAudio distribution makefile.
|
||||
|
||||
.PHONY: docs clean
|
||||
.PHONY: docs clean build
|
||||
|
||||
EPYDOC ?= epydoc
|
||||
|
||||
VERSION := 0.2.4
|
||||
VERSION := 0.2.11
|
||||
PYTHON ?= python
|
||||
BUILD_ARGS ?=
|
||||
SPHINX ?= sphinx-build
|
||||
DOCS_OUTPUT=docs/
|
||||
DOC_NAME := PyAudio-$(VERSION)
|
||||
DOC_URL=http://people.csail.mit.edu/hubert/pyaudio/
|
||||
PYTHON_BUILD_DIR:=$(shell $(PYTHON) -c "import distutils.util; import sys; print(distutils.util.get_platform() + '-' + sys.version[0:3])")
|
||||
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:
|
||||
@echo "make targets:"
|
||||
@echo
|
||||
@echo " tarball : build source tarball"
|
||||
@echo " docs : generate documentation (requires epydoc)"
|
||||
@echo " docs : generate documentation (requires sphinx)"
|
||||
@echo " clean : remove build files"
|
||||
@echo
|
||||
@echo "To build pyaudio, run:"
|
||||
|
@ -27,13 +32,17 @@ clean:
|
|||
# Documentation
|
||||
######################################################################
|
||||
|
||||
docs:
|
||||
@cd src; \
|
||||
$(EPYDOC) -v -o ../$(DOCS_OUTPUT) --name $(DOC_NAME) --url $(DOC_URL) \
|
||||
--no-private pyaudio.py
|
||||
build: build/$(BUILD_STAMP)
|
||||
|
||||
build/$(BUILD_STAMP): $(SRCFILES)
|
||||
$(PYTHON) setup.py build $(BUILD_ARGS)
|
||||
touch $@
|
||||
|
||||
docs: build
|
||||
PYTHONPATH=build/$(BUILD_DIR) $(SPHINX) -b html sphinx/ $(DOCS_OUTPUT)
|
||||
|
||||
######################################################################
|
||||
# Source Tarball
|
||||
######################################################################
|
||||
tarball: docs $(SRCFILES) MANIFEST.in
|
||||
@python setup.py sdist
|
||||
tarball: $(SRCFILES) $(EXAMPLES) $(TESTS) MANIFEST.in
|
||||
@$(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/
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
======================================================================
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
======================================================================
|
||||
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
"""
|
||||
PyAudio Example:
|
||||
|
||||
Test for a variety of error conditions. This
|
||||
example demonstrates exception handling with
|
||||
PyAudio. """
|
||||
PyAudio Example: Test for a variety of error conditions. This example
|
||||
demonstrates exception handling with PyAudio.
|
||||
"""
|
||||
|
||||
import pyaudio
|
||||
|
||||
|
@ -12,56 +10,56 @@ p = pyaudio.PyAudio()
|
|||
# get invalid sample size
|
||||
try:
|
||||
p.get_sample_size(10)
|
||||
except ValueError, e:
|
||||
assert e[1] == pyaudio.paSampleFormatNotSupported
|
||||
print "OK:", e[0]
|
||||
except ValueError as e:
|
||||
assert e.args[1] == pyaudio.paSampleFormatNotSupported
|
||||
print("OK: %s" % e.args[0])
|
||||
else:
|
||||
assert False, "sample size"
|
||||
|
||||
# get format from invalid width
|
||||
try:
|
||||
p.get_format_from_width(8)
|
||||
except ValueError, e:
|
||||
print "OK: invalid format from width"
|
||||
except ValueError as e:
|
||||
print("OK: invalid format from width")
|
||||
else:
|
||||
assert False, "invalid format"
|
||||
|
||||
# try to get an invalid device
|
||||
try:
|
||||
p.get_host_api_info_by_type(-1)
|
||||
except IOError, e:
|
||||
assert e[1] == pyaudio.paHostApiNotFound
|
||||
print "OK:", e[0]
|
||||
except IOError as e:
|
||||
assert e.args[1] == pyaudio.paHostApiNotFound
|
||||
print("OK: %s" % e.args[0])
|
||||
else:
|
||||
assert False, "invalid host type"
|
||||
|
||||
# try to get host api info by index
|
||||
try:
|
||||
p.get_host_api_info_by_index(-1)
|
||||
except IOError, e:
|
||||
assert e[1] == pyaudio.paInvalidHostApi
|
||||
print "OK:", e[0]
|
||||
except IOError as e:
|
||||
assert e.args[1] == pyaudio.paInvalidHostApi
|
||||
print("OK: %s" % e.args[0])
|
||||
else:
|
||||
assert False, "invalid host api index"
|
||||
|
||||
# try good host api device index
|
||||
try:
|
||||
p.get_device_info_by_host_api_device_index(0, -1)
|
||||
except IOError, e:
|
||||
assert ((e[1] == pyaudio.paInvalidDevice) or \
|
||||
(e[1] == pyaudio.paInvalidHostApi))
|
||||
print "OK:", e[0]
|
||||
except IOError as e:
|
||||
assert ((e.args[1] == pyaudio.paInvalidDevice) or \
|
||||
(e.args[1] == pyaudio.paInvalidHostApi))
|
||||
print("OK: %s" % e.args[0])
|
||||
else:
|
||||
assert False, "device info by host api device idnex"
|
||||
|
||||
|
||||
|
||||
# try bad host api and good device index
|
||||
try:
|
||||
p.get_device_info_by_host_api_device_index(-1, 0)
|
||||
except IOError, e:
|
||||
assert ((e[1] == pyaudio.paInvalidDevice) or \
|
||||
(e[1] == pyaudio.paInvalidHostApi))
|
||||
print "OK:", e[0]
|
||||
except IOError as e:
|
||||
assert ((e.args[1] == pyaudio.paInvalidDevice) or \
|
||||
(e.args[1] == pyaudio.paInvalidHostApi))
|
||||
print("OK: %s" % e.args[0])
|
||||
else:
|
||||
assert False, "device info by host api device idnex"
|
||||
|
||||
|
@ -69,9 +67,9 @@ else:
|
|||
# bad device index
|
||||
try:
|
||||
p.get_device_info_by_index(-1)
|
||||
except IOError, e:
|
||||
assert e[1] == pyaudio.paInvalidDevice
|
||||
print "OK:", e[0]
|
||||
except IOError as e:
|
||||
assert e.args[1] == pyaudio.paInvalidDevice
|
||||
print("OK: %s" % e.args[0])
|
||||
else:
|
||||
assert False, "bad device index"
|
||||
|
||||
|
@ -87,9 +85,9 @@ stream = p.open(channels = 1,
|
|||
|
||||
try:
|
||||
data = stream.read(2)
|
||||
except IOError, e:
|
||||
print "OK:", e[0]
|
||||
assert e[1] == pyaudio.paStreamIsStopped, e[1]
|
||||
except IOError as e:
|
||||
print("OK: %s" % e.args[0])
|
||||
assert e.args[1] == pyaudio.paStreamIsStopped, e.args[1]
|
||||
else:
|
||||
assert False, "Should have caused exception"
|
||||
|
||||
|
@ -98,29 +96,29 @@ stream.start_stream()
|
|||
# try to write to the input stream
|
||||
try:
|
||||
stream.write('foobar')
|
||||
except IOError, e:
|
||||
assert e[1] == pyaudio.paCanNotWriteToAnInputOnlyStream
|
||||
print "OK:", e[0]
|
||||
except IOError as e:
|
||||
assert e.args[1] == pyaudio.paCanNotWriteToAnInputOnlyStream
|
||||
print("OK: %s" % e.args[0])
|
||||
else:
|
||||
assert False, "write to input stream"
|
||||
|
||||
# read some negative data
|
||||
try:
|
||||
data = stream.read(-1)
|
||||
except ValueError, e:
|
||||
print "OK: Invalid frames"
|
||||
except ValueError as e:
|
||||
print("OK: Invalid frames")
|
||||
else:
|
||||
assert False, "invalid frames"
|
||||
|
||||
# read some real data
|
||||
try:
|
||||
data = stream.read(2)
|
||||
except IOError, e:
|
||||
except IOError as e:
|
||||
# some slower machines might overflow
|
||||
assert e[1] == pyaudio.paInputOverflowed, e
|
||||
print "OK:", e[0]
|
||||
assert e.args[1] == pyaudio.paInputOverflowed, e
|
||||
print("OK: %s" % e.args[0])
|
||||
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
|
||||
# this stream afterwards
|
||||
|
@ -130,36 +128,36 @@ stream.close()
|
|||
# query for properties
|
||||
try:
|
||||
stream.get_input_latency()
|
||||
except IOError, e:
|
||||
assert e[1] == pyaudio.paBadStreamPtr
|
||||
print "OK:", e[0]
|
||||
except IOError as e:
|
||||
assert e.args[1] == pyaudio.paBadStreamPtr
|
||||
print("OK: %s" % e.args[0])
|
||||
else:
|
||||
assert False, "closed stream"
|
||||
|
||||
# read some data again
|
||||
try:
|
||||
stream.read(10)
|
||||
except IOError, e:
|
||||
assert e[1] == pyaudio.paBadStreamPtr
|
||||
print "OK:", e[0]
|
||||
except IOError as e:
|
||||
assert e.args[1] == pyaudio.paBadStreamPtr
|
||||
print("OK: %s" % e.args[0])
|
||||
else:
|
||||
assert False, "closed stream"
|
||||
|
||||
# get invalid stream capabilities
|
||||
try:
|
||||
p.is_format_supported(8000, -1, 1, pyaudio.paInt16)
|
||||
except ValueError, e:
|
||||
assert e[1] == pyaudio.paInvalidDevice
|
||||
print "OK:", e[0]
|
||||
except ValueError as e:
|
||||
assert e.args[1] == pyaudio.paInvalidDevice
|
||||
print("OK: %s" % e.args[0])
|
||||
else:
|
||||
assert False, "invalid device"
|
||||
|
||||
# get invalid stream capabilities
|
||||
try:
|
||||
p.is_format_supported(8000, 0, -1, pyaudio.paInt16)
|
||||
except ValueError, e:
|
||||
assert e[1] == pyaudio.paInvalidChannelCount
|
||||
print "OK:", e[0]
|
||||
except ValueError as e:
|
||||
assert e.args[1] == pyaudio.paInvalidChannelCount
|
||||
print("OK: %s" % e.args[0])
|
||||
else:
|
||||
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 wave
|
||||
|
@ -9,7 +11,7 @@ chunk = 1024
|
|||
PyAudio = pyaudio.PyAudio
|
||||
|
||||
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)
|
||||
|
||||
wf = wave.open(sys.argv[1], 'rb')
|
||||
|
@ -40,30 +42,29 @@ channel_map = (1, -1)
|
|||
|
||||
try:
|
||||
stream_info = pyaudio.PaMacCoreStreamInfo(
|
||||
flags = pyaudio.PaMacCoreStreamInfo.paMacCorePlayNice, # default
|
||||
channel_map = channel_map)
|
||||
flags=pyaudio.PaMacCoreStreamInfo.paMacCorePlayNice, # default
|
||||
channel_map=channel_map)
|
||||
except AttributeError:
|
||||
print "Sorry, couldn't find PaMacCoreStreamInfo. Make sure that " \
|
||||
"you're running on Mac OS X."
|
||||
import sys
|
||||
print("Sorry, couldn't find PaMacCoreStreamInfo. Make sure that "
|
||||
"you're running on Mac OS X.")
|
||||
sys.exit(-1)
|
||||
|
||||
print "Stream Info Flags:", stream_info.get_flags()
|
||||
print "Stream Info Channel Map:", stream_info.get_channel_map()
|
||||
|
||||
print("Stream Info Flags:", stream_info.get_flags())
|
||||
print("Stream Info Channel Map:", stream_info.get_channel_map())
|
||||
|
||||
# open stream
|
||||
stream = p.open(format =
|
||||
p.get_format_from_width(wf.getsampwidth()),
|
||||
channels = wf.getnchannels(),
|
||||
rate = wf.getframerate(),
|
||||
output = True,
|
||||
output_host_api_specific_stream_info = stream_info)
|
||||
stream = p.open(
|
||||
format=p.get_format_from_width(wf.getsampwidth()),
|
||||
channels=wf.getnchannels(),
|
||||
rate=wf.getframerate(),
|
||||
output=True,
|
||||
output_host_api_specific_stream_info=stream_info)
|
||||
|
||||
# read data
|
||||
data = wf.readframes(chunk)
|
||||
|
||||
# play stream
|
||||
while data != '':
|
||||
while len(data) > 0:
|
||||
stream.write(data)
|
||||
data = wf.readframes(chunk)
|
||||
|
||||
|
@ -71,6 +72,3 @@ stream.stop_stream()
|
|||
stream.close()
|
||||
|
||||
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_devs = p.get_device_count()
|
||||
|
||||
print "\nPortAudio System Info:\n======================"
|
||||
print "Version: %d" % pyaudio.get_portaudio_version()
|
||||
print "Version Text: %s" % pyaudio.get_portaudio_version_text()
|
||||
print "Number of Host APIs: %d" % max_apis
|
||||
print "Number of Devices : %d" % max_devs
|
||||
print("\nPortAudio System Info:\n======================")
|
||||
print("Version: %d" % pyaudio.get_portaudio_version())
|
||||
print("Version Text: %s" % pyaudio.get_portaudio_version_text())
|
||||
print("Number of Host APIs: %d" % max_apis)
|
||||
print("Number of Devices : %d" % max_devs)
|
||||
|
||||
print "\nHost APIs:\n=========="
|
||||
print("\nHost APIs:\n==========")
|
||||
|
||||
for i in range(max_apis):
|
||||
apiinfo = p.get_host_api_info_by_index(i)
|
||||
for k in apiinfo.items():
|
||||
print "%s: %s" % k
|
||||
print "--------------------------"
|
||||
for k in list(apiinfo.items()):
|
||||
print("%s: %s" % k)
|
||||
print("--------------------------")
|
||||
|
||||
print "\nDevices:\n========"
|
||||
print("\nDevices:\n========")
|
||||
|
||||
for i in range(max_devs):
|
||||
devinfo = p.get_device_info_by_index(i)
|
||||
|
||||
# print out device parameters
|
||||
for k in devinfo.items():
|
||||
for k in list(devinfo.items()):
|
||||
name, value = k
|
||||
|
||||
# if host API, then get friendly name
|
||||
|
||||
|
||||
if name == 'hostApi':
|
||||
value = str(value) + \
|
||||
" (%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
|
||||
|
||||
|
||||
input_supported_rates = []
|
||||
output_supported_rates = []
|
||||
full_duplex_rates = []
|
||||
|
||||
|
||||
for f in standard_sample_rates:
|
||||
|
||||
if devinfo['maxInputChannels'] > 0:
|
||||
|
@ -92,42 +94,41 @@ for i in range(max_devs):
|
|||
pass
|
||||
|
||||
if len(input_supported_rates):
|
||||
print "\tInput rates:", input_supported_rates
|
||||
print("\tInput rates: %s" % input_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):
|
||||
print "\tFull duplex: ", full_duplex_rates
|
||||
|
||||
print "\t--------------------------------"
|
||||
print("\tFull duplex: %s" % full_duplex_rates)
|
||||
|
||||
print "\nDefault Devices:\n================"
|
||||
print("\t--------------------------------")
|
||||
|
||||
print("\nDefault Devices:\n================")
|
||||
try:
|
||||
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)
|
||||
for k in devinfo.items():
|
||||
for k in list(devinfo.items()):
|
||||
name, value = k
|
||||
if name == 'hostApi':
|
||||
value = str(value) + \
|
||||
" (%s)" % p.get_host_api_info_by_index(k[1])['name']
|
||||
print "\t%s: %s" % (name, value)
|
||||
print "\t--------------------------------"
|
||||
except IOError, e:
|
||||
print "No Input devices: %s" % e[0]
|
||||
print("\t%s: %s" % (name, value))
|
||||
print("\t--------------------------------")
|
||||
except IOError as e:
|
||||
print("No Input devices: %s" % e[0])
|
||||
|
||||
try:
|
||||
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)
|
||||
for k in devinfo.items():
|
||||
for k in list(devinfo.items()):
|
||||
name, value = k
|
||||
if name == 'hostApi':
|
||||
value = str(value) + \
|
||||
" (%s)" % p.get_host_api_info_by_index(k[1])['name']
|
||||
print "\t%s: %s" % (name, value)
|
||||
print "\t--------------------------------"
|
||||
except IOError, e:
|
||||
print "No Output devices: %s" % e[0]
|
||||
print("\t%s: %s" % (name, value))
|
||||
print("\t--------------------------------")
|
||||
except IOError as e:
|
||||
print("No Output devices: %s" % e[0])
|
||||
|
||||
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
|
||||
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.
|
||||
"""
|
||||
|
||||
from distutils.core import setup, Extension
|
||||
import sys
|
||||
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
|
||||
# against portaudio.
|
||||
# distutils will try to locate and link dynamically against portaudio.
|
||||
#
|
||||
# You probably don't want to statically link in the PortAudio
|
||||
# library unless you're building on Microsoft Windows.
|
||||
# If you would rather statically link in the portaudio library (e.g.,
|
||||
# typically on Microsoft Windows), run:
|
||||
#
|
||||
# In any case, if you would rather statically link in libportaudio,
|
||||
# run:
|
||||
# % python setup.py build --static-link
|
||||
#
|
||||
# % python setup.py build --static-link
|
||||
#
|
||||
# Be sure to specify the location of the libportaudio.a in
|
||||
# the `extra_link_args' variable below.
|
||||
# Specify the environment variable PORTAUDIO_PATH with the build tree
|
||||
# of PortAudio.
|
||||
|
||||
STATIC_LINKING = False
|
||||
|
||||
|
@ -53,30 +53,34 @@ if "--static-link" in sys.argv:
|
|||
sys.argv.remove("--static-link")
|
||||
|
||||
portaudio_path = os.environ.get("PORTAUDIO_PATH", "./portaudio-v19")
|
||||
mac_sysroot_path = os.environ.get("SYSROOT_PATH", None)
|
||||
|
||||
pyaudio_module_sources = ['src/_portaudiomodule.c']
|
||||
|
||||
include_dirs = []
|
||||
external_libraries = []
|
||||
extra_compile_args = ['-fno-strict-aliasing']
|
||||
extra_compile_args = []
|
||||
extra_link_args = []
|
||||
scripts = []
|
||||
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 = [
|
||||
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
|
||||
if sys.platform == 'darwin':
|
||||
|
@ -84,45 +88,36 @@ if STATIC_LINKING:
|
|||
'-framework', 'AudioToolbox',
|
||||
'-framework', 'AudioUnit',
|
||||
'-framework', 'Carbon']
|
||||
|
||||
elif sys.platform == 'cygwin':
|
||||
external_libraries += ['winmm']
|
||||
extra_link_args += ['-lwinmm']
|
||||
|
||||
elif sys.platform == 'win32':
|
||||
# i.e., Win32 Python with mingw32
|
||||
# run: python setup.py build -cmingw32
|
||||
external_libraries += ['winmm']
|
||||
extra_link_args += ['-lwinmm']
|
||||
|
||||
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
|
||||
# PortAudio on GNU/Linux, be sure to link in whatever sound
|
||||
# backend you used in portaudio (e.g., ALSA, JACK, etc...)
|
||||
|
||||
# I'll start you off with ALSA, since that's the most common
|
||||
# today. If you need JACK support, add it here.
|
||||
|
||||
external_libraries += ['asound']
|
||||
|
||||
|
||||
pyaudio = Extension('_portaudio',
|
||||
sources = pyaudio_module_sources,
|
||||
include_dirs = include_dirs,
|
||||
define_macros = defines,
|
||||
libraries = external_libraries,
|
||||
extra_compile_args = extra_compile_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])
|
||||
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=[
|
||||
Extension('_portaudio',
|
||||
sources=pyaudio_module_sources,
|
||||
include_dirs=include_dirs,
|
||||
define_macros=defines,
|
||||
libraries=external_libraries,
|
||||
extra_compile_args=extra_compile_args,
|
||||
extra_link_args=extra_link_args)
|
||||
])
|
||||
|
|
|
@ -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
|
||||
*
|
||||
* Copyright (c) 2006-2008 Hubert Pham
|
||||
* Copyright (c) 2006 Hubert Pham
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* 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