pax_global_header00006660000000000000000000000064130633416530014516gustar00rootroot0000000000000052 comment=7090e25bcba41413bd7ce89aa73bc0efb1ae1ca1 python-pyaudio-0.2.11/000077500000000000000000000000001306334165300145705ustar00rootroot00000000000000python-pyaudio-0.2.11/.gitignore000066400000000000000000000000731306334165300165600ustar00rootroot00000000000000.DS_Store src/PyAudio.egg-info build/ dist/ docs/ MANIFEST python-pyaudio-0.2.11/CHANGELOG000066400000000000000000000075241306334165300160120ustar00rootroot000000000000002017-03-18 Hubert Pham 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 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 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 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 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 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 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 PyAudio 0.2.4 - Maintenance release: updated directory structure and packaging. 2008-10-29 Hubert Pham PyAudio 0.2.3 - Release the GIL during blocking PortAudio I/O calls. - Fixed Python argument parsing to use a long for PaSampleFormat (rather than int). Thanks to many who have pointed out these two issues and sent patches. - pyaudio.PyAudio.is_format_supported() now throws a ValueError exception if the specified format is not supported for any reason (or returns True if the format is supported). Prior, the method would return False if and only if the specified sample rate was unsupported. is_format_supported() now will always return True or throw an exception. 2008-03-06 Hubert Pham PyAudio 0.2.0 - Added PaMacCoreStreamInfo for Mac OS X Host API Specific Stream Info (e.g., for channel maps). - Added packaging files for building binaries. 2008-02-12 Justin Mazzola Paluska - Initial version of debian packaging. python-pyaudio-0.2.11/INSTALL000066400000000000000000000063051306334165300156250ustar00rootroot00000000000000====================================================================== PyAudio Compilation Hints ====================================================================== Here are some hints for compiling PortAudio and PyAudio on various platforms: * General UNIX Guide: (GNU/Linux, Mac OS X, Cygwin) * Microsoft Windows (native) Generally speaking, installation involves building the PortAudio v19 library and then building PyAudio. ---------------------------------------------------------------------- General UNIX Guide (GNU/Linux, Mac OS X, Cygwin) ---------------------------------------------------------------------- 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 2. Extract PyAudio. To build and install, run: % python setup.py install ---------------------------------------------------------------------- Microsoft Windows ---------------------------------------------------------------------- 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). 1. Install cygwin's gcc and mingw packages. 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: % CC=i686-w64-mingw32-gcc ./configure --enable-static --with-pic % make 3. Before building PyAudio, apply a few necessary modifications: 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. 5. To install PyAudio: % python setup.py install --skip-build Or create a Python wheel and install using pip. python-pyaudio-0.2.11/MANIFEST.in000066400000000000000000000002321306334165300163230ustar00rootroot00000000000000include src/*.c src/*.h src/*.py include Makefile CHANGELOG INSTALL MANIFEST.in recursive-include examples *.py recursive-include tests *.py graft sphinx python-pyaudio-0.2.11/Makefile000066400000000000000000000025451306334165300162360ustar00rootroot00000000000000# This is the PyAudio distribution makefile. .PHONY: docs clean build VERSION := 0.2.11 PYTHON ?= python BUILD_ARGS ?= SPHINX ?= sphinx-build DOCS_OUTPUT=docs/ 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 sphinx)" @echo " clean : remove build files" @echo @echo "To build pyaudio, run:" @echo @echo " python setup.py install" clean: @rm -rf build dist MANIFEST $(DOCS_OUTPUT) src/*.pyc ###################################################################### # Documentation ###################################################################### 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: $(SRCFILES) $(EXAMPLES) $(TESTS) MANIFEST.in @$(PYTHON) setup.py sdist python-pyaudio-0.2.11/README000066400000000000000000000032461306334165300154550ustar00rootroot00000000000000====================================================================== PyAudio v0.2.11: Python Bindings for PortAudio. ====================================================================== See: http://people.csail.mit.edu/hubert/pyaudio/ 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. See INSTALL for compilation hints. ====================================================================== PyAudio : Python Bindings for PortAudio. 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 "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. 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. ====================================================================== python-pyaudio-0.2.11/examples/000077500000000000000000000000001306334165300164065ustar00rootroot00000000000000python-pyaudio-0.2.11/examples/error.py000066400000000000000000000075611306334165300201220ustar00rootroot00000000000000""" PyAudio Example: Test for a variety of error conditions. This example demonstrates exception handling with PyAudio. """ import pyaudio p = pyaudio.PyAudio() # get invalid sample size try: p.get_sample_size(10) 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 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 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 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 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 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" # bad device index try: p.get_device_info_by_index(-1) except IOError as e: assert e.args[1] == pyaudio.paInvalidDevice print("OK: %s" % e.args[0]) else: assert False, "bad device index" ### now for some real work ### stream = p.open(channels = 1, rate = 44100, format = pyaudio.paInt16, input = True, start = False) # (note that we didn't start the stream!) try: data = stream.read(2) 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" stream.start_stream() # try to write to the input stream try: stream.write('foobar') 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 as e: print("OK: Invalid frames") else: assert False, "invalid frames" # read some real data try: data = stream.read(2) except IOError as e: # some slower machines might overflow assert e.args[1] == pyaudio.paInputOverflowed, e print("OK: %s" % e.args[0]) else: print("OK: got %d bytes of data" % len(data)) # close the stream; nothing should work with # this stream afterwards stream.close() # query for properties try: stream.get_input_latency() 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 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 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 as e: assert e.args[1] == pyaudio.paInvalidChannelCount print("OK: %s" % e.args[0]) else: assert False, "invalid number of channels" p.terminate() python-pyaudio-0.2.11/examples/play_wave.py000066400000000000000000000013131306334165300207450ustar00rootroot00000000000000"""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() python-pyaudio-0.2.11/examples/play_wave_callback.py000066400000000000000000000016571306334165300225740ustar00rootroot00000000000000"""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() python-pyaudio-0.2.11/examples/play_wave_macosx_channelmap.py000066400000000000000000000030461306334165300245120ustar00rootroot00000000000000""" PyAudio Example: Mac OS X-only: Play a wave file with channel maps. """ 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() # standard L-R stereo # channel_map = (0, 1) # reverse: R-L stereo # channel_map = (1, 0) # no audio # channel_map = (-1, -1) # left channel audio --> left speaker; no right channel # channel_map = (0, -1) # right channel audio --> right speaker; no left channel # channel_map = (-1, 1) # left channel audio --> right speaker # channel_map = (-1, 0) # right channel audio --> left speaker channel_map = (1, -1) # etc... try: stream_info = pyaudio.PaMacCoreStreamInfo( 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.") sys.exit(-1) 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) # read data data = wf.readframes(chunk) # play stream while len(data) > 0: stream.write(data) data = wf.readframes(chunk) stream.stop_stream() stream.close() p.terminate() python-pyaudio-0.2.11/examples/record.py000066400000000000000000000015521306334165300202410ustar00rootroot00000000000000""" 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() python-pyaudio-0.2.11/examples/system_info.py000066400000000000000000000105011306334165300213140ustar00rootroot00000000000000""" PyAudio Example: Query and print PortAudio HostAPIs, Devices, and their support rates. """ import pyaudio standard_sample_rates = [8000.0, 9600.0, 11025.0, 12000.0, 16000.0, 22050.0, 24000.0, 32000.0, 44100.0, 48000.0, 88200.0, 96000.0, 192000.0] 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("\nHost APIs:\n==========") for i in range(max_apis): apiinfo = p.get_host_api_info_by_index(i) for k in list(apiinfo.items()): print("%s: %s" % k) print("--------------------------") print("\nDevices:\n========") for i in range(max_devs): devinfo = p.get_device_info_by_index(i) # print out device parameters 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'] # 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: try: if p.is_format_supported( f, input_device = devinfo['index'], input_channels = devinfo['maxInputChannels'], input_format = pyaudio.paInt16): input_supported_rates.append(f) except ValueError: pass if devinfo['maxOutputChannels'] > 0: try: if p.is_format_supported( f, output_device = devinfo['index'], output_channels = devinfo['maxOutputChannels'], output_format = pyaudio.paInt16): output_supported_rates.append(f) except ValueError: pass if (devinfo['maxInputChannels'] > 0) and \ (devinfo['maxOutputChannels'] > 0): try: if p.is_format_supported( f, input_device = devinfo['index'], input_channels = devinfo['maxInputChannels'], input_format = pyaudio.paInt16, output_device = devinfo['index'], output_channels = devinfo['maxOutputChannels'], output_format = pyaudio.paInt16): full_duplex_rates.append(f) except ValueError: pass if len(input_supported_rates): print("\tInput rates: %s" % input_supported_rates) if len(output_supported_rates): print("\tOutput rates: %s" % output_supported_rates) if len(full_duplex_rates): print("\tFull duplex: %s" % full_duplex_rates) print("\t--------------------------------") print("\nDefault Devices:\n================") try: def_index = p.get_default_input_device_info()['index'] print("Default Input Device : %s" % def_index) devinfo = p.get_device_info_by_index(def_index) 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 as e: print("No Input devices: %s" % e[0]) try: def_index = p.get_default_output_device_info()['index'] print("Default Output Device: %s" % def_index) devinfo = p.get_device_info_by_index(def_index) 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 as e: print("No Output devices: %s" % e[0]) p.terminate() python-pyaudio-0.2.11/examples/wire_callback.py000066400000000000000000000014661306334165300215510ustar00rootroot00000000000000""" 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() python-pyaudio-0.2.11/examples/wire_full.py000066400000000000000000000013521306334165300207510ustar00rootroot00000000000000""" 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() python-pyaudio-0.2.11/examples/wire_half.py000066400000000000000000000021461306334165300207230ustar00rootroot00000000000000""" 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() python-pyaudio-0.2.11/setup.py000077500000000000000000000101171306334165300163050ustar00rootroot00000000000000""" PyAudio v0.2.11: Python Bindings for PortAudio. 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 "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. 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. """ import os import platform import sys try: from setuptools import setup, Extension except ImportError: from distutils.core import setup, Extension __version__ = "0.2.11" # distutils will try to locate and link dynamically against portaudio. # # If you would rather statically link in the portaudio library (e.g., # typically on Microsoft Windows), run: # # % python setup.py build --static-link # # Specify the environment variable PORTAUDIO_PATH with the build tree # of PortAudio. STATIC_LINKING = False if "--static-link" in sys.argv: STATIC_LINKING = True 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 = [] extra_link_args = [] scripts = [] defines = [] 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') ] # platform specific configuration if sys.platform == 'darwin': extra_link_args += ['-framework', 'CoreAudio', '-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': 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'] 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) ]) python-pyaudio-0.2.11/sphinx/000077500000000000000000000000001306334165300161015ustar00rootroot00000000000000python-pyaudio-0.2.11/sphinx/conf.py000066400000000000000000000173231306334165300174060ustar00rootroot00000000000000#!/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 # " v 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 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') python-pyaudio-0.2.11/sphinx/examples.rst000066400000000000000000000037521306334165300204600ustar00rootroot00000000000000Example: 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(, , , )`` 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). python-pyaudio-0.2.11/sphinx/index.rst000066400000000000000000000013521306334165300177430ustar00rootroot00000000000000PyAudio 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` python-pyaudio-0.2.11/src/000077500000000000000000000000001306334165300153575ustar00rootroot00000000000000python-pyaudio-0.2.11/src/_portaudiomodule.c000066400000000000000000002324751306334165300211130ustar00rootroot00000000000000/** * PyAudio: Python Bindings for PortAudio. * * 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 "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. * * 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 #include "Python.h" #include "portaudio.h" #include "_portaudiomodule.h" #ifdef MACOSX #include "pa_mac_core.h" #endif #define DEFAULT_FRAMES_PER_BUFFER 1024 /* #define VERBOSE */ #define min(a, b) \ ({ \ __typeof__(a) _a = (a); \ __typeof__(b) _b = (b); \ _a < _b ? _a : _b; \ }) /************************************************************ * * Table of Contents * * I. Exportable PortAudio Method Definitions * II. Python Object Wrappers * - PaDeviceInfo * - PaHostInfo * - PaStream * III. PortAudio Method Implementations * - Initialization/Termination * - HostAPI * - DeviceAPI * - Stream Open/Close * - Stream Start/Stop/Info * - Stream Read/Write * IV. Python Module Init * - PaHostApiTypeId enum constants * ************************************************************/ /************************************************************ * * I. Exportable Python Methods * ************************************************************/ static PyMethodDef paMethods[] = { /* version */ {"get_version", pa_get_version, METH_VARARGS, "get version"}, {"get_version_text", pa_get_version_text, METH_VARARGS, "get version text"}, /* inits */ {"initialize", pa_initialize, METH_VARARGS, "initialize portaudio"}, {"terminate", pa_terminate, METH_VARARGS, "terminate portaudio"}, /* host api */ {"get_host_api_count", pa_get_host_api_count, METH_VARARGS, "get host API count"}, {"get_default_host_api", pa_get_default_host_api, METH_VARARGS, "get default host API index"}, {"host_api_type_id_to_host_api_index", pa_host_api_type_id_to_host_api_index, METH_VARARGS, "get default host API index"}, {"host_api_device_index_to_device_index", pa_host_api_device_index_to_device_index, METH_VARARGS, "get default host API index"}, {"get_host_api_info", pa_get_host_api_info, METH_VARARGS, "get host api information"}, /* device api */ {"get_device_count", pa_get_device_count, METH_VARARGS, "get host API count"}, {"get_default_input_device", pa_get_default_input_device, METH_VARARGS, "get default input device index"}, {"get_default_output_device", pa_get_default_output_device, METH_VARARGS, "get default output device index"}, {"get_device_info", pa_get_device_info, METH_VARARGS, "get device information"}, /* stream open/close */ {"open", (PyCFunction)pa_open, METH_VARARGS | METH_KEYWORDS, "open port audio stream"}, {"close", pa_close, METH_VARARGS, "close port audio stream"}, {"get_sample_size", pa_get_sample_size, METH_VARARGS, "get sample size of a format in bytes"}, {"is_format_supported", (PyCFunction)pa_is_format_supported, METH_VARARGS | METH_KEYWORDS, "returns whether specified format is supported"}, /* stream start/stop */ {"start_stream", pa_start_stream, METH_VARARGS, "starts port audio stream"}, {"stop_stream", pa_stop_stream, METH_VARARGS, "stops port audio stream"}, {"abort_stream", pa_abort_stream, METH_VARARGS, "aborts port audio stream"}, {"is_stream_stopped", pa_is_stream_stopped, METH_VARARGS, "returns whether stream is stopped"}, {"is_stream_active", pa_is_stream_active, METH_VARARGS, "returns whether stream is active"}, {"get_stream_time", pa_get_stream_time, METH_VARARGS, "returns stream time"}, {"get_stream_cpu_load", pa_get_stream_cpu_load, METH_VARARGS, "returns stream CPU load -- always 0 for blocking mode"}, /* stream read/write */ {"write_stream", pa_write_stream, METH_VARARGS, "write to stream"}, {"read_stream", pa_read_stream, METH_VARARGS, "read from stream"}, {"get_stream_write_available", pa_get_stream_write_available, METH_VARARGS, "get buffer available for writing"}, {"get_stream_read_available", pa_get_stream_read_available, METH_VARARGS, "get buffer available for reading"}, {NULL, NULL, 0, NULL}}; /************************************************************ * * II. Python Object Wrappers * ************************************************************/ /************************************************************* * PaDeviceInfo Type : Python object wrapper for PaDeviceInfo *************************************************************/ typedef struct { // clang-format off PyObject_HEAD PaDeviceInfo *devInfo; // clang-format on } _pyAudio_paDeviceInfo; static PyObject *_pyAudio_paDeviceInfo_get_structVersion( _pyAudio_paDeviceInfo *self, void *closure) { if (!self->devInfo) { PyErr_SetString(PyExc_AttributeError, "No Device Info available"); return NULL; } return PyLong_FromLong(self->devInfo->structVersion); } static PyObject *_pyAudio_paDeviceInfo_get_name(_pyAudio_paDeviceInfo *self, void *closure) { if ((!self->devInfo) || (self->devInfo->name == NULL)) { PyErr_SetString(PyExc_AttributeError, "No Device Info available"); return NULL; } return PyBytes_FromString(self->devInfo->name); } static PyObject *_pyAudio_paDeviceInfo_get_hostApi(_pyAudio_paDeviceInfo *self, void *closure) { if (!self->devInfo) { PyErr_SetString(PyExc_AttributeError, "No Device Info available"); return NULL; } return PyLong_FromLong(self->devInfo->hostApi); } static PyObject *_pyAudio_paDeviceInfo_get_maxInputChannels( _pyAudio_paDeviceInfo *self, void *closure) { if (!self->devInfo) { PyErr_SetString(PyExc_AttributeError, "No Device Info available"); return NULL; } return PyLong_FromLong(self->devInfo->maxInputChannels); } static PyObject *_pyAudio_paDeviceInfo_get_maxOutputChannels( _pyAudio_paDeviceInfo *self, void *closure) { if (!self->devInfo) { PyErr_SetString(PyExc_AttributeError, "No Device Info available"); return NULL; } return PyLong_FromLong(self->devInfo->maxOutputChannels); } static PyObject *_pyAudio_paDeviceInfo_get_defaultLowInputLatency( _pyAudio_paDeviceInfo *self, void *closure) { if (!self->devInfo) { PyErr_SetString(PyExc_AttributeError, "No Device Info available"); return NULL; } return PyFloat_FromDouble(self->devInfo->defaultLowInputLatency); } static PyObject *_pyAudio_paDeviceInfo_get_defaultLowOutputLatency( _pyAudio_paDeviceInfo *self, void *closure) { if (!self->devInfo) { PyErr_SetString(PyExc_AttributeError, "No Device Info available"); return NULL; } return PyFloat_FromDouble(self->devInfo->defaultLowOutputLatency); } static PyObject *_pyAudio_paDeviceInfo_get_defaultHighInputLatency( _pyAudio_paDeviceInfo *self, void *closure) { if (!self->devInfo) { PyErr_SetString(PyExc_AttributeError, "No Device Info available"); return NULL; } return PyFloat_FromDouble(self->devInfo->defaultHighInputLatency); } static PyObject *_pyAudio_paDeviceInfo_get_defaultHighOutputLatency( _pyAudio_paDeviceInfo *self, void *closure) { if (!self->devInfo) { PyErr_SetString(PyExc_AttributeError, "No Device Info available"); return NULL; } return PyFloat_FromDouble(self->devInfo->defaultHighOutputLatency); } static PyObject *_pyAudio_paDeviceInfo_get_defaultSampleRate( _pyAudio_paDeviceInfo *self, void *closure) { if (!self->devInfo) { PyErr_SetString(PyExc_AttributeError, "No Device Info available"); return NULL; } return PyFloat_FromDouble(self->devInfo->defaultSampleRate); } static int _pyAudio_paDeviceInfo_antiset(_pyAudio_paDeviceInfo *self, PyObject *value, void *closure) { /* read-only: do not allow users to change values */ PyErr_SetString(PyExc_AttributeError, "Fields read-only: cannot modify values"); return -1; } static PyGetSetDef _pyAudio_paDeviceInfo_getseters[] = { {"name", (getter)_pyAudio_paDeviceInfo_get_name, (setter)_pyAudio_paDeviceInfo_antiset, "device name", NULL}, {"structVersion", (getter)_pyAudio_paDeviceInfo_get_structVersion, (setter)_pyAudio_paDeviceInfo_antiset, "struct version", NULL}, {"hostApi", (getter)_pyAudio_paDeviceInfo_get_hostApi, (setter)_pyAudio_paDeviceInfo_antiset, "host api index", NULL}, {"maxInputChannels", (getter)_pyAudio_paDeviceInfo_get_maxInputChannels, (setter)_pyAudio_paDeviceInfo_antiset, "max input channels", NULL}, {"maxOutputChannels", (getter)_pyAudio_paDeviceInfo_get_maxOutputChannels, (setter)_pyAudio_paDeviceInfo_antiset, "max output channels", NULL}, {"defaultLowInputLatency", (getter)_pyAudio_paDeviceInfo_get_defaultLowInputLatency, (setter)_pyAudio_paDeviceInfo_antiset, "default low input latency", NULL}, {"defaultLowOutputLatency", (getter)_pyAudio_paDeviceInfo_get_defaultLowOutputLatency, (setter)_pyAudio_paDeviceInfo_antiset, "default low output latency", NULL}, {"defaultHighInputLatency", (getter)_pyAudio_paDeviceInfo_get_defaultHighInputLatency, (setter)_pyAudio_paDeviceInfo_antiset, "default high input latency", NULL}, {"defaultHighOutputLatency", (getter)_pyAudio_paDeviceInfo_get_defaultHighOutputLatency, (setter)_pyAudio_paDeviceInfo_antiset, "default high output latency", NULL}, {"defaultSampleRate", (getter)_pyAudio_paDeviceInfo_get_defaultSampleRate, (setter)_pyAudio_paDeviceInfo_antiset, "default sample rate", NULL}, {NULL}}; static void _pyAudio_paDeviceInfo_dealloc(_pyAudio_paDeviceInfo *self) { self->devInfo = NULL; Py_TYPE(self)->tp_free((PyObject *)self); } static PyTypeObject _pyAudio_paDeviceInfoType = { // clang-format off PyVarObject_HEAD_INIT(NULL, 0) // clang-format on "_portaudio.paDeviceInfo", /*tp_name*/ sizeof(_pyAudio_paDeviceInfo), /*tp_basicsize*/ 0, /*tp_itemsize*/ (destructor)_pyAudio_paDeviceInfo_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash */ 0, /*tp_call*/ 0, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT, /*tp_flags*/ "Port Audio Device Info", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ 0, /* tp_methods */ 0, /* tp_members */ _pyAudio_paDeviceInfo_getseters, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; static _pyAudio_paDeviceInfo *_create_paDeviceInfo_object(void) { _pyAudio_paDeviceInfo *obj; /* don't allow subclassing */ obj = (_pyAudio_paDeviceInfo *)PyObject_New(_pyAudio_paDeviceInfo, &_pyAudio_paDeviceInfoType); return obj; } /************************************************************* * PaHostApi Info Python Object *************************************************************/ typedef struct { // clang-format off PyObject_HEAD // clang-format on PaHostApiInfo *apiInfo; } _pyAudio_paHostApiInfo; static PyObject *_pyAudio_paHostApiInfo_get_structVersion( _pyAudio_paHostApiInfo *self, void *closure) { if ((!self->apiInfo)) { PyErr_SetString(PyExc_AttributeError, "No HostApi Info available"); return NULL; } return PyLong_FromLong(self->apiInfo->structVersion); } static PyObject *_pyAudio_paHostApiInfo_get_type(_pyAudio_paHostApiInfo *self, void *closure) { if ((!self->apiInfo)) { PyErr_SetString(PyExc_AttributeError, "No HostApi Info available"); return NULL; } return PyLong_FromLong((long)self->apiInfo->type); } static PyObject *_pyAudio_paHostApiInfo_get_name(_pyAudio_paHostApiInfo *self, void *closure) { if ((!self->apiInfo) || (self->apiInfo->name == NULL)) { PyErr_SetString(PyExc_AttributeError, "No HostApi Info available"); return NULL; } return PyUnicode_FromString(self->apiInfo->name); } static PyObject *_pyAudio_paHostApiInfo_get_deviceCount( _pyAudio_paHostApiInfo *self, void *closure) { if ((!self->apiInfo)) { PyErr_SetString(PyExc_AttributeError, "No HostApi Info available"); return NULL; } return PyLong_FromLong(self->apiInfo->deviceCount); } static PyObject *_pyAudio_paHostApiInfo_get_defaultInputDevice( _pyAudio_paHostApiInfo *self, void *closure) { if ((!self->apiInfo)) { PyErr_SetString(PyExc_AttributeError, "No HostApi Info available"); return NULL; } return PyLong_FromLong(self->apiInfo->defaultInputDevice); } static PyObject *_pyAudio_paHostApiInfo_get_defaultOutputDevice( _pyAudio_paHostApiInfo *self, void *closure) { if ((!self->apiInfo)) { PyErr_SetString(PyExc_AttributeError, "No HostApi Info available"); return NULL; } return PyLong_FromLong(self->apiInfo->defaultOutputDevice); } static int _pyAudio_paHostApiInfo_antiset(_pyAudio_paDeviceInfo *self, PyObject *value, void *closure) { /* read-only: do not allow users to change values */ PyErr_SetString(PyExc_AttributeError, "Fields read-only: cannot modify values"); return -1; } static void _pyAudio_paHostApiInfo_dealloc(_pyAudio_paHostApiInfo *self) { self->apiInfo = NULL; Py_TYPE(self)->tp_free((PyObject *)self); } static PyGetSetDef _pyAudio_paHostApiInfo_getseters[] = { {"name", (getter)_pyAudio_paHostApiInfo_get_name, (setter)_pyAudio_paHostApiInfo_antiset, "host api name", NULL}, {"structVersion", (getter)_pyAudio_paHostApiInfo_get_structVersion, (setter)_pyAudio_paHostApiInfo_antiset, "struct version", NULL}, {"type", (getter)_pyAudio_paHostApiInfo_get_type, (setter)_pyAudio_paHostApiInfo_antiset, "host api type", NULL}, {"deviceCount", (getter)_pyAudio_paHostApiInfo_get_deviceCount, (setter)_pyAudio_paHostApiInfo_antiset, "number of devices", NULL}, {"defaultInputDevice", (getter)_pyAudio_paHostApiInfo_get_defaultInputDevice, (setter)_pyAudio_paHostApiInfo_antiset, "default input device index", NULL}, {"defaultOutputDevice", (getter)_pyAudio_paHostApiInfo_get_defaultOutputDevice, (setter)_pyAudio_paDeviceInfo_antiset, "default output device index", NULL}, {NULL}}; static PyTypeObject _pyAudio_paHostApiInfoType = { // clang-format off PyVarObject_HEAD_INIT(NULL, 0) // clang-format on "_portaudio.paHostApiInfo", /*tp_name*/ sizeof(_pyAudio_paHostApiInfo), /*tp_basicsize*/ 0, /*tp_itemsize*/ (destructor)_pyAudio_paHostApiInfo_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash */ 0, /*tp_call*/ 0, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT, /*tp_flags*/ "Port Audio HostApi Info", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ 0, /* tp_methods */ 0, /* tp_members */ _pyAudio_paHostApiInfo_getseters, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; static _pyAudio_paHostApiInfo *_create_paHostApiInfo_object(void) { _pyAudio_paHostApiInfo *obj; /* don't allow subclassing */ obj = (_pyAudio_paHostApiInfo *)PyObject_New(_pyAudio_paHostApiInfo, &_pyAudio_paHostApiInfoType); return obj; } /************************************************************* * Host-Specific Objects *************************************************************/ /************************************************************* * --> Mac OS X *************************************************************/ #ifdef MACOSX typedef struct { // clang-format off PyObject_HEAD // clang-format on PaMacCoreStreamInfo *paMacCoreStreamInfo; int flags; SInt32 *channelMap; int channelMapSize; } _pyAudio_MacOSX_hostApiSpecificStreamInfo; typedef _pyAudio_MacOSX_hostApiSpecificStreamInfo _pyAudio_Mac_HASSI; static void _pyAudio_MacOSX_hostApiSpecificStreamInfo_cleanup( _pyAudio_Mac_HASSI *self) { if (self->paMacCoreStreamInfo != NULL) { free(self->paMacCoreStreamInfo); self->paMacCoreStreamInfo = NULL; } if (self->channelMap != NULL) { free(self->channelMap); self->channelMap = NULL; } self->flags = paMacCorePlayNice; self->channelMapSize = 0; } static void _pyAudio_MacOSX_hostApiSpecificStreamInfo_dealloc( _pyAudio_Mac_HASSI *self) { _pyAudio_MacOSX_hostApiSpecificStreamInfo_cleanup(self); Py_TYPE(self)->tp_free((PyObject *)self); } static int _pyAudio_MacOSX_hostApiSpecificStreamInfo_init(PyObject *_self, PyObject *args, PyObject *kwargs) { _pyAudio_Mac_HASSI *self = (_pyAudio_Mac_HASSI *)_self; PyObject *channel_map = NULL; int flags = paMacCorePlayNice; static char *kwlist[] = {"flags", "channel_map", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|iO", kwlist, &flags, &channel_map)) { return -1; } _pyAudio_MacOSX_hostApiSpecificStreamInfo_cleanup(self); if (channel_map != NULL) { if (!PyTuple_Check(channel_map)) { PyErr_SetString(PyExc_ValueError, "Channel map must be a tuple"); return -1; } // generate SInt32 channelMap self->channelMapSize = (int)PyTuple_Size(channel_map); self->channelMap = (SInt32 *)malloc(sizeof(SInt32) * self->channelMapSize); if (self->channelMap == NULL) { PyErr_SetString(PyExc_SystemError, "Out of memory"); _pyAudio_MacOSX_hostApiSpecificStreamInfo_cleanup(self); return -1; } PyObject *element; int i; for (i = 0; i < self->channelMapSize; ++i) { element = PyTuple_GetItem(channel_map, i); if (element == NULL) { PyErr_SetString(PyExc_ValueError, "Internal error: out of bounds index"); _pyAudio_MacOSX_hostApiSpecificStreamInfo_cleanup(self); return -1; } if (!PyNumber_Check(element)) { PyErr_SetString(PyExc_ValueError, "Channel Map must consist of integer elements"); _pyAudio_MacOSX_hostApiSpecificStreamInfo_cleanup(self); return -1; } PyObject *long_element = PyNumber_Long(element); self->channelMap[i] = (SInt32)PyLong_AsLong(long_element); Py_DECREF(long_element); } } self->paMacCoreStreamInfo = (PaMacCoreStreamInfo *)malloc(sizeof(PaMacCoreStreamInfo)); if (self->paMacCoreStreamInfo == NULL) { PyErr_SetString(PyExc_SystemError, "Out of memeory"); _pyAudio_MacOSX_hostApiSpecificStreamInfo_cleanup(self); return -1; } PaMacCore_SetupStreamInfo(self->paMacCoreStreamInfo, flags); if (self->channelMap) { PaMacCore_SetupChannelMap(self->paMacCoreStreamInfo, self->channelMap, self->channelMapSize); } self->flags = flags; return 0; } static PyObject *_pyAudio_MacOSX_hostApiSpecificStreamInfo_get_flags( _pyAudio_Mac_HASSI *self, void *closure) { return PyLong_FromLong(self->flags); } static PyObject *_pyAudio_MacOSX_hostApiSpecificStreamInfo_get_channel_map( _pyAudio_Mac_HASSI *self, void *closure) { if (self->channelMap == NULL || self->channelMapSize == 0) { Py_INCREF(Py_None); return Py_None; } int i; PyObject *channelMapTuple = PyTuple_New(self->channelMapSize); for (i = 0; i < self->channelMapSize; ++i) { PyObject *element = PyLong_FromLong(self->channelMap[i]); if (!element) { PyErr_SetString(PyExc_SystemError, "Invalid channel map"); return NULL; } if (PyTuple_SetItem(channelMapTuple, i, PyLong_FromLong(self->channelMap[i]))) { // non-zero on error PyErr_SetString(PyExc_SystemError, "Can't create channel map."); return NULL; } } return channelMapTuple; } static int _pyAudio_MacOSX_hostApiSpecificStreamInfo_antiset( _pyAudio_Mac_HASSI *self, PyObject *value, void *closure) { /* read-only: do not allow users to change values */ PyErr_SetString(PyExc_AttributeError, "Fields read-only: cannot modify values"); return -1; } static PyGetSetDef _pyAudio_MacOSX_hostApiSpecificStreamInfo_getseters[] = { {"flags", (getter)_pyAudio_MacOSX_hostApiSpecificStreamInfo_get_flags, (setter)_pyAudio_MacOSX_hostApiSpecificStreamInfo_antiset, "flags", NULL}, {"channel_map", (getter)_pyAudio_MacOSX_hostApiSpecificStreamInfo_get_channel_map, (setter)_pyAudio_MacOSX_hostApiSpecificStreamInfo_antiset, "channel map", NULL}, {NULL}}; static PyTypeObject _pyAudio_MacOSX_hostApiSpecificStreamInfoType = { // clang-format off PyVarObject_HEAD_INIT(NULL, 0) // clang-format on "_portaudio.PaMacCoreStreamInfo", /*tp_name*/ sizeof(_pyAudio_MacOSX_hostApiSpecificStreamInfo), /*tp_basicsize*/ 0, /*tp_itemsize*/ /*tp_dealloc*/ (destructor)_pyAudio_MacOSX_hostApiSpecificStreamInfo_dealloc, 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash */ 0, /*tp_call*/ 0, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT, /*tp_flags*/ "Mac OS X Specific HostAPI configuration", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ 0, /* tp_methods */ 0, /* tp_members */ _pyAudio_MacOSX_hostApiSpecificStreamInfo_getseters, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (int (*)(PyObject *, PyObject *, PyObject *)) _pyAudio_MacOSX_hostApiSpecificStreamInfo_init, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; #endif /************************************************************* * Stream Wrapper Python Object *************************************************************/ typedef struct { PyObject *callback; long main_thread_id; unsigned int frame_size; } PyAudioCallbackContext; typedef struct { // clang-format off PyObject_HEAD // clang-format on PaStream *stream; PaStreamParameters *inputParameters; PaStreamParameters *outputParameters; PaStreamInfo *streamInfo; PyAudioCallbackContext *callbackContext; int is_open; } _pyAudio_Stream; static int _is_open(_pyAudio_Stream *obj) { return (obj) && (obj->is_open); } static void _cleanup_Stream_object(_pyAudio_Stream *streamObject) { if (streamObject->stream != NULL) { // clang-format off Py_BEGIN_ALLOW_THREADS Pa_CloseStream(streamObject->stream); Py_END_ALLOW_THREADS // clang-format on streamObject->stream = NULL; } if (streamObject->streamInfo) streamObject->streamInfo = NULL; if (streamObject->inputParameters != NULL) { free(streamObject->inputParameters); streamObject->inputParameters = NULL; } if (streamObject->outputParameters != NULL) { free(streamObject->outputParameters); streamObject->outputParameters = NULL; } if (streamObject->callbackContext != NULL) { Py_XDECREF(streamObject->callbackContext->callback); free(streamObject->callbackContext); streamObject->callbackContext = NULL; } streamObject->is_open = 0; } static void _pyAudio_Stream_dealloc(_pyAudio_Stream *self) { _cleanup_Stream_object(self); Py_TYPE(self)->tp_free((PyObject *)self); } static PyObject *_pyAudio_Stream_get_structVersion(_pyAudio_Stream *self, void *closure) { if (!_is_open(self)) { PyErr_SetObject(PyExc_IOError, Py_BuildValue("(i,s)", paBadStreamPtr, "Stream closed")); return NULL; } if ((!self->streamInfo)) { PyErr_SetObject(PyExc_IOError, Py_BuildValue("(i,s)", paBadStreamPtr, "No StreamInfo available")); return NULL; } return PyLong_FromLong(self->streamInfo->structVersion); } static PyObject *_pyAudio_Stream_get_inputLatency(_pyAudio_Stream *self, void *closure) { if (!_is_open(self)) { PyErr_SetObject(PyExc_IOError, Py_BuildValue("(i,s)", paBadStreamPtr, "Stream closed")); return NULL; } if ((!self->streamInfo)) { PyErr_SetObject(PyExc_IOError, Py_BuildValue("(i,s)", paBadStreamPtr, "No StreamInfo available")); return NULL; } return PyFloat_FromDouble(self->streamInfo->inputLatency); } static PyObject *_pyAudio_Stream_get_outputLatency(_pyAudio_Stream *self, void *closure) { if (!_is_open(self)) { PyErr_SetObject(PyExc_IOError, Py_BuildValue("(i,s)", paBadStreamPtr, "Stream closed")); return NULL; } if ((!self->streamInfo)) { PyErr_SetObject(PyExc_IOError, Py_BuildValue("(i,s)", paBadStreamPtr, "No StreamInfo available")); return NULL; } return PyFloat_FromDouble(self->streamInfo->outputLatency); } static PyObject *_pyAudio_Stream_get_sampleRate(_pyAudio_Stream *self, void *closure) { if (!_is_open(self)) { PyErr_SetObject(PyExc_IOError, Py_BuildValue("(i,s)", paBadStreamPtr, "Stream closed")); return NULL; } if ((!self->streamInfo)) { PyErr_SetObject(PyExc_IOError, Py_BuildValue("(i,s)", paBadStreamPtr, "No StreamInfo available")); return NULL; } return PyFloat_FromDouble(self->streamInfo->sampleRate); } static int _pyAudio_Stream_antiset(_pyAudio_Stream *self, PyObject *value, void *closure) { /* read-only: do not allow users to change values */ PyErr_SetString(PyExc_AttributeError, "Fields read-only: cannot modify values"); return -1; } static PyGetSetDef _pyAudio_Stream_getseters[] = { {"structVersion", (getter)_pyAudio_Stream_get_structVersion, (setter)_pyAudio_Stream_antiset, "struct version", NULL}, {"inputLatency", (getter)_pyAudio_Stream_get_inputLatency, (setter)_pyAudio_Stream_antiset, "input latency", NULL}, {"outputLatency", (getter)_pyAudio_Stream_get_outputLatency, (setter)_pyAudio_Stream_antiset, "output latency", NULL}, {"sampleRate", (getter)_pyAudio_Stream_get_sampleRate, (setter)_pyAudio_Stream_antiset, "sample rate", NULL}, {NULL}}; static PyTypeObject _pyAudio_StreamType = { // clang-format off PyVarObject_HEAD_INIT(NULL, 0) // clang-format on "_portaudio.Stream", /*tp_name*/ sizeof(_pyAudio_Stream), /*tp_basicsize*/ 0, /*tp_itemsize*/ (destructor)_pyAudio_Stream_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash */ 0, /*tp_call*/ 0, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT, /*tp_flags*/ "Port Audio Stream", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ 0, /* tp_methods */ 0, /* tp_members */ _pyAudio_Stream_getseters, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; static _pyAudio_Stream *_create_Stream_object(void) { _pyAudio_Stream *obj; /* don't allow subclassing */ obj = (_pyAudio_Stream *)PyObject_New(_pyAudio_Stream, &_pyAudio_StreamType); return obj; } /************************************************************ * * III. PortAudio Method Implementations * ************************************************************/ /************************************************************* * Version Info *************************************************************/ static PyObject *pa_get_version(PyObject *self, PyObject *args) { if (!PyArg_ParseTuple(args, "")) { return NULL; } return PyLong_FromLong(Pa_GetVersion()); } static PyObject *pa_get_version_text(PyObject *self, PyObject *args) { if (!PyArg_ParseTuple(args, "")) { return NULL; } return PyUnicode_FromString(Pa_GetVersionText()); } /************************************************************* * Initialization/Termination *************************************************************/ static PyObject *pa_initialize(PyObject *self, PyObject *args) { int err; // clang-format off Py_BEGIN_ALLOW_THREADS err = Pa_Initialize(); Py_END_ALLOW_THREADS // clang-format on if (err != paNoError) { // clang-format off Py_BEGIN_ALLOW_THREADS Pa_Terminate(); Py_END_ALLOW_THREADS // clang-format on #ifdef VERBOSE 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)); #endif PyErr_SetObject(PyExc_IOError, Py_BuildValue("(i,s)", err, Pa_GetErrorText(err))); return NULL; } Py_INCREF(Py_None); return Py_None; } static PyObject *pa_terminate(PyObject *self, PyObject *args) { // clang-format off Py_BEGIN_ALLOW_THREADS Pa_Terminate(); Py_END_ALLOW_THREADS // clang-format on Py_INCREF(Py_None); return Py_None; } /************************************************************* * HostAPI *************************************************************/ static PyObject *pa_get_host_api_count(PyObject *self, PyObject *args) { PaHostApiIndex count; if (!PyArg_ParseTuple(args, "")) { return NULL; } count = Pa_GetHostApiCount(); if (count < 0) { #ifdef VERBOSE fprintf(stderr, "An error occured while using the portaudio stream\n"); fprintf(stderr, "Error number: %d\n", count); fprintf(stderr, "Error message: %s\n", Pa_GetErrorText(count)); #endif PyErr_SetObject(PyExc_IOError, Py_BuildValue("(i,s)", count, Pa_GetErrorText(count))); return NULL; } return PyLong_FromLong(count); } static PyObject *pa_get_default_host_api(PyObject *self, PyObject *args) { PaHostApiIndex index; if (!PyArg_ParseTuple(args, "")) { return NULL; } index = Pa_GetDefaultHostApi(); if (index < 0) { #ifdef VERBOSE fprintf(stderr, "An error occured while using the portaudio stream\n"); fprintf(stderr, "Error number: %d\n", index); fprintf(stderr, "Error message: %s\n", Pa_GetErrorText(index)); #endif PyErr_SetObject(PyExc_IOError, Py_BuildValue("(i,s)", index, Pa_GetErrorText(index))); return NULL; } return PyLong_FromLong(index); } static PyObject *pa_host_api_type_id_to_host_api_index(PyObject *self, PyObject *args) { PaHostApiTypeId typeid; PaHostApiIndex index; if (!PyArg_ParseTuple(args, "i", &typeid)) { return NULL; } index = Pa_HostApiTypeIdToHostApiIndex(typeid); if (index < 0) { #ifdef VERBOSE fprintf(stderr, "An error occured while using the portaudio stream\n"); fprintf(stderr, "Error number: %d\n", index); fprintf(stderr, "Error message: %s\n", Pa_GetErrorText(index)); #endif PyErr_SetObject(PyExc_IOError, Py_BuildValue("(i,s)", index, Pa_GetErrorText(index))); return NULL; } return PyLong_FromLong(index); } static PyObject *pa_host_api_device_index_to_device_index(PyObject *self, PyObject *args) { PaHostApiIndex apiIndex; int hostApiDeviceindex; PaDeviceIndex devIndex; if (!PyArg_ParseTuple(args, "ii", &apiIndex, &hostApiDeviceindex)) { return NULL; } devIndex = Pa_HostApiDeviceIndexToDeviceIndex(apiIndex, hostApiDeviceindex); if (devIndex < 0) { #ifdef VERBOSE fprintf(stderr, "An error occured while using the portaudio stream\n"); fprintf(stderr, "Error number: %d\n", devIndex); fprintf(stderr, "Error message: %s\n", Pa_GetErrorText(devIndex)); #endif PyErr_SetObject(PyExc_IOError, Py_BuildValue("(i,s)", devIndex, Pa_GetErrorText(devIndex))); return NULL; } return PyLong_FromLong(devIndex); } static PyObject *pa_get_host_api_info(PyObject *self, PyObject *args) { PaHostApiIndex index; PaHostApiInfo *_info; _pyAudio_paHostApiInfo *py_info; if (!PyArg_ParseTuple(args, "i", &index)) { return NULL; } _info = (PaHostApiInfo *)Pa_GetHostApiInfo(index); if (!_info) { PyErr_SetObject(PyExc_IOError, Py_BuildValue("(i,s)", paInvalidHostApi, "Invalid host api info")); return NULL; } py_info = _create_paHostApiInfo_object(); py_info->apiInfo = _info; return (PyObject *)py_info; } /************************************************************* * Device API *************************************************************/ static PyObject *pa_get_device_count(PyObject *self, PyObject *args) { PaDeviceIndex count; if (!PyArg_ParseTuple(args, "")) { return NULL; } count = Pa_GetDeviceCount(); if (count < 0) { #ifdef VERBOSE fprintf(stderr, "An error occured while using the portaudio stream\n"); fprintf(stderr, "Error number: %d\n", count); fprintf(stderr, "Error message: %s\n", Pa_GetErrorText(count)); #endif PyErr_SetObject(PyExc_IOError, Py_BuildValue("(i,s)", count, Pa_GetErrorText(count))); return NULL; } return PyLong_FromLong(count); } static PyObject *pa_get_default_input_device(PyObject *self, PyObject *args) { PaDeviceIndex index; if (!PyArg_ParseTuple(args, "")) { return NULL; } index = Pa_GetDefaultInputDevice(); if (index == paNoDevice) { PyErr_SetString(PyExc_IOError, "No Default Input Device Available"); return NULL; } else if (index < 0) { #ifdef VERBOSE fprintf(stderr, "An error occured while using the portaudio stream\n"); fprintf(stderr, "Error number: %d\n", index); fprintf(stderr, "Error message: %s\n", Pa_GetErrorText(index)); #endif PyErr_SetObject(PyExc_IOError, Py_BuildValue("(i,s)", index, Pa_GetErrorText(index))); return NULL; } return PyLong_FromLong(index); } static PyObject *pa_get_default_output_device(PyObject *self, PyObject *args) { PaDeviceIndex index; if (!PyArg_ParseTuple(args, "")) { return NULL; } index = Pa_GetDefaultOutputDevice(); if (index == paNoDevice) { PyErr_SetString(PyExc_IOError, "No Default Output Device Available"); return NULL; } else if (index < 0) { #ifdef VERBOSE fprintf(stderr, "An error occured while using the portaudio stream\n"); fprintf(stderr, "Error number: %d\n", index); fprintf(stderr, "Error message: %s\n", Pa_GetErrorText(index)); #endif PyErr_SetObject(PyExc_IOError, Py_BuildValue("(i,s)", index, Pa_GetErrorText(index))); return NULL; } return PyLong_FromLong(index); } static PyObject *pa_get_device_info(PyObject *self, PyObject *args) { PaDeviceIndex index; PaDeviceInfo *_info; _pyAudio_paDeviceInfo *py_info; if (!PyArg_ParseTuple(args, "i", &index)) { return NULL; } _info = (PaDeviceInfo *)Pa_GetDeviceInfo(index); if (!_info) { PyErr_SetObject(PyExc_IOError, Py_BuildValue("(i,s)", paInvalidDevice, "Invalid device info")); return NULL; } py_info = _create_paDeviceInfo_object(); py_info->devInfo = _info; return (PyObject *)py_info; } /************************************************************* * Stream Open / Close / Supported *************************************************************/ int _stream_callback_cfunction(const void *input, void *output, unsigned long frameCount, const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags, void *userData) { int return_val = paAbort; PyGILState_STATE _state = PyGILState_Ensure(); #ifdef VERBOSE if (statusFlags != 0) { printf("Status flag set: "); if (statusFlags & paInputUnderflow) { printf("input underflow!\n"); } if (statusFlags & paInputOverflow) { printf("input overflow!\n"); } if (statusFlags & paOutputUnderflow) { printf("output underflow!\n"); } if (statusFlags & paOutputUnderflow) { printf("output overflow!\n"); } if (statusFlags & paPrimingOutput) { printf("priming output!\n"); } } #endif PyAudioCallbackContext *context = (PyAudioCallbackContext *)userData; PyObject *py_callback = context->callback; unsigned int bytes_per_frame = context->frame_size; long main_thread_id = context->main_thread_id; PyObject *py_frame_count = PyLong_FromUnsignedLong(frameCount); // clang-format off PyObject *py_time_info = Py_BuildValue("{s:d,s:d,s:d}", "input_buffer_adc_time", timeInfo->inputBufferAdcTime, "current_time", timeInfo->currentTime, "output_buffer_dac_time", timeInfo->outputBufferDacTime); // clang-format on PyObject *py_status_flags = PyLong_FromUnsignedLong(statusFlags); PyObject *py_input_data = Py_None; const char *pData; unsigned output_len; PyObject *py_result; if (input) { py_input_data = PyBytes_FromStringAndSize(input, bytes_per_frame * frameCount); } py_result = PyObject_CallFunctionObjArgs(py_callback, py_input_data, py_frame_count, py_time_info, py_status_flags, NULL); if (py_result == NULL) { #ifdef VERBOSE fprintf(stderr, "An error occured while using the portaudio stream\n"); fprintf(stderr, "Error message: Could not call callback function\n"); #endif PyObject *err = PyErr_Occurred(); if (err) { PyThreadState_SetAsyncExc(main_thread_id, err); // Print out a stack trace to help debugging. // TODO: make VERBOSE a runtime flag so users can control // the amount of logging. PyErr_Print(); } goto end; } // clang-format off if (!PyArg_ParseTuple(py_result, "z#i", &pData, &output_len, &return_val)) { // clang-format on #ifdef VERBOSE fprintf(stderr, "An error occured while using the portaudio stream\n"); fprintf(stderr, "Error message: Could not parse callback return value\n"); #endif PyObject *err = PyErr_Occurred(); if (err) { PyThreadState_SetAsyncExc(main_thread_id, err); // Print out a stack trace to help debugging. // TODO: make VERBOSE a runtime flag so users can control // the amount of logging. PyErr_Print(); } Py_XDECREF(py_result); return_val = paAbort; goto end; } if ((return_val != paComplete) && (return_val != paAbort) && (return_val != paContinue)) { PyErr_SetString(PyExc_ValueError, "Invalid PaStreamCallbackResult from callback"); PyThreadState_SetAsyncExc(main_thread_id, PyErr_Occurred()); PyErr_Print(); // Quit the callback loop Py_DECREF(py_result); return_val = paAbort; goto end; } // Copy bytes for playback only if this is an output stream: if (output) { char *output_data = (char *)output; memcpy(output_data, pData, min(output_len, bytes_per_frame * frameCount)); // Pad out the rest of the buffer with 0s if callback returned // too few frames (and assume paComplete). if (output_len < (frameCount * bytes_per_frame)) { memset(output_data + output_len, 0, (frameCount * bytes_per_frame) - output_len); return_val = paComplete; } } Py_DECREF(py_result); end: if (input) { // Decrement this at the end, after memcpy, in case the user // returns py_input_data back for playback. Py_DECREF(py_input_data); } Py_XDECREF(py_frame_count); Py_XDECREF(py_time_info); Py_XDECREF(py_status_flags); PyGILState_Release(_state); return return_val; } static PyObject *pa_open(PyObject *self, PyObject *args, PyObject *kwargs) { int rate, channels; int input, output, frames_per_buffer; int input_device_index = -1; int output_device_index = -1; PyObject *input_device_index_arg = NULL; PyObject *output_device_index_arg = NULL; PyObject *stream_callback = NULL; PaSampleFormat format; PaError err; PyObject *input_device_index_long; PyObject *output_device_index_long; PaStreamParameters *outputParameters = NULL; PaStreamParameters *inputParameters = NULL; PaStream *stream = NULL; PaStreamInfo *streamInfo = NULL; PyAudioCallbackContext *context = NULL; _pyAudio_Stream *streamObject; static char *kwlist[] = {"rate", "channels", "format", "input", "output", "input_device_index", "output_device_index", "frames_per_buffer", "input_host_api_specific_stream_info", "output_host_api_specific_stream_info", "stream_callback", NULL}; #ifdef MACOSX _pyAudio_MacOSX_hostApiSpecificStreamInfo *inputHostSpecificStreamInfo = NULL; _pyAudio_MacOSX_hostApiSpecificStreamInfo *outputHostSpecificStreamInfo = NULL; #else /* mostly ignored...*/ PyObject *inputHostSpecificStreamInfo = NULL; PyObject *outputHostSpecificStreamInfo = NULL; #endif /* default to neither output nor input */ input = 0; output = 0; frames_per_buffer = DEFAULT_FRAMES_PER_BUFFER; // clang-format off if (!PyArg_ParseTupleAndKeywords(args, kwargs, #ifdef MACOSX "iik|iiOOiO!O!O", #else "iik|iiOOiOOO", #endif kwlist, &rate, &channels, &format, &input, &output, &input_device_index_arg, &output_device_index_arg, &frames_per_buffer, #ifdef MACOSX &_pyAudio_MacOSX_hostApiSpecificStreamInfoType, #endif &inputHostSpecificStreamInfo, #ifdef MACOSX &_pyAudio_MacOSX_hostApiSpecificStreamInfoType, #endif &outputHostSpecificStreamInfo, &stream_callback)) { return NULL; } // clang-format on if (stream_callback && (PyCallable_Check(stream_callback) == 0)) { PyErr_SetString(PyExc_TypeError, "stream_callback must be callable"); return NULL; } if ((input_device_index_arg == NULL) || (input_device_index_arg == Py_None)) { #ifdef VERBOSE printf("Using default input device\n"); #endif input_device_index = -1; } else { if (!PyNumber_Check(input_device_index_arg)) { PyErr_SetString(PyExc_ValueError, "input_device_index must be integer (or None)"); return NULL; } input_device_index_long = PyNumber_Long(input_device_index_arg); input_device_index = (int)PyLong_AsLong(input_device_index_long); Py_DECREF(input_device_index_long); #ifdef VERBOSE printf("Using input device index number: %d\n", input_device_index); #endif } if ((output_device_index_arg == NULL) || (output_device_index_arg == Py_None)) { #ifdef VERBOSE printf("Using default output device\n"); #endif output_device_index = -1; } else { if (!PyNumber_Check(output_device_index_arg)) { PyErr_SetString(PyExc_ValueError, "output_device_index must be integer (or None)"); return NULL; } output_device_index_long = PyNumber_Long(output_device_index_arg); output_device_index = (int)PyLong_AsLong(output_device_index_long); Py_DECREF(output_device_index_long); #ifdef VERBOSE printf("Using output device index number: %d\n", output_device_index); #endif } if (input == 0 && output == 0) { PyErr_SetString(PyExc_ValueError, "Must specify either input or output"); return NULL; } if (channels < 1) { PyErr_SetString(PyExc_ValueError, "Invalid audio channels"); return NULL; } if (output) { outputParameters = (PaStreamParameters *)malloc(sizeof(PaStreamParameters)); if (output_device_index < 0) { outputParameters->device = Pa_GetDefaultOutputDevice(); } else { outputParameters->device = output_device_index; } /* final check -- ensure that there is a default device */ if (outputParameters->device < 0 || outputParameters->device >= Pa_GetDeviceCount()) { free(outputParameters); PyErr_SetObject(PyExc_IOError, Py_BuildValue("(i,s)", paInvalidDevice, "Invalid output device " "(no default output device)")); return NULL; } outputParameters->channelCount = channels; outputParameters->sampleFormat = format; outputParameters->suggestedLatency = Pa_GetDeviceInfo(outputParameters->device)->defaultLowOutputLatency; outputParameters->hostApiSpecificStreamInfo = NULL; #ifdef MACOSX if (outputHostSpecificStreamInfo) { outputParameters->hostApiSpecificStreamInfo = outputHostSpecificStreamInfo->paMacCoreStreamInfo; } #endif } if (input) { inputParameters = (PaStreamParameters *)malloc(sizeof(PaStreamParameters)); if (input_device_index < 0) { inputParameters->device = Pa_GetDefaultInputDevice(); } else { inputParameters->device = input_device_index; } /* final check -- ensure that there is a default device */ if (inputParameters->device < 0) { free(inputParameters); PyErr_SetObject(PyExc_IOError, Py_BuildValue("(i,s)", paInvalidDevice, "Invalid input device " "(no default output device)")); return NULL; } inputParameters->channelCount = channels; inputParameters->sampleFormat = format; inputParameters->suggestedLatency = Pa_GetDeviceInfo(inputParameters->device)->defaultLowInputLatency; inputParameters->hostApiSpecificStreamInfo = NULL; #ifdef MACOSX if (inputHostSpecificStreamInfo) { inputParameters->hostApiSpecificStreamInfo = inputHostSpecificStreamInfo->paMacCoreStreamInfo; } #endif } if (stream_callback) { Py_INCREF(stream_callback); context = (PyAudioCallbackContext *)malloc(sizeof(PyAudioCallbackContext)); context->callback = (PyObject *)stream_callback; context->main_thread_id = PyThreadState_Get()->thread_id; context->frame_size = Pa_GetSampleSize(format) * channels; } // clang-format off Py_BEGIN_ALLOW_THREADS err = Pa_OpenStream(&stream, /* input/output parameters */ /* NULL values are ignored */ inputParameters, outputParameters, /* samples per second */ rate, /* frames in the buffer */ frames_per_buffer, /* we won't output out of range samples so don't bother clipping them */ paClipOff, /* callback, if specified */ (stream_callback) ? (_stream_callback_cfunction) : (NULL), /* callback userData, if applicable */ context); Py_END_ALLOW_THREADS // clang-format on if (err != paNoError) { #ifdef VERBOSE 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)); #endif PyErr_SetObject(PyExc_IOError, Py_BuildValue("(i,s)", err, Pa_GetErrorText(err))); return NULL; } streamInfo = (PaStreamInfo *)Pa_GetStreamInfo(stream); if (!streamInfo) { PyErr_SetObject(PyExc_IOError, Py_BuildValue("(i,s)", paInternalError, "Could not get stream information")); return NULL; } streamObject = _create_Stream_object(); streamObject->stream = stream; streamObject->inputParameters = inputParameters; streamObject->outputParameters = outputParameters; streamObject->is_open = 1; streamObject->streamInfo = streamInfo; streamObject->callbackContext = context; return (PyObject *)streamObject; } static PyObject *pa_close(PyObject *self, PyObject *args) { PyObject *stream_arg; _pyAudio_Stream *streamObject; if (!PyArg_ParseTuple(args, "O!", &_pyAudio_StreamType, &stream_arg)) { return NULL; } streamObject = (_pyAudio_Stream *)stream_arg; _cleanup_Stream_object(streamObject); Py_INCREF(Py_None); return Py_None; } static PyObject *pa_get_sample_size(PyObject *self, PyObject *args) { PaSampleFormat format; int size_in_bytes; if (!PyArg_ParseTuple(args, "k", &format)) { return NULL; } size_in_bytes = Pa_GetSampleSize(format); if (size_in_bytes < 0) { PyErr_SetObject( PyExc_ValueError, Py_BuildValue("(s,i)", Pa_GetErrorText(size_in_bytes), size_in_bytes)); return NULL; } return PyLong_FromLong(size_in_bytes); } static PyObject *pa_is_format_supported(PyObject *self, PyObject *args, PyObject *kwargs) { // clang-format off static char *kwlist[] = { "sample_rate", "input_device", "input_channels", "input_format", "output_device", "output_channels", "output_format", NULL }; // clang-format on int input_device, input_channels; int output_device, output_channels; float sample_rate; PaStreamParameters inputParams; PaStreamParameters outputParams; PaSampleFormat input_format, output_format; PaError error; input_device = input_channels = output_device = output_channels = -1; input_format = output_format = -1; // clang-format off if (!PyArg_ParseTupleAndKeywords(args, kwargs, "f|iikiik", kwlist, &sample_rate, &input_device, &input_channels, &input_format, &output_device, &output_channels, &output_format)) { return NULL; } // clang-format on if (!(input_device < 0)) { inputParams.device = input_device; inputParams.channelCount = input_channels; inputParams.sampleFormat = input_format; inputParams.suggestedLatency = 0; inputParams.hostApiSpecificStreamInfo = NULL; } if (!(output_device < 0)) { outputParams.device = output_device; outputParams.channelCount = output_channels; outputParams.sampleFormat = output_format; outputParams.suggestedLatency = 0; outputParams.hostApiSpecificStreamInfo = NULL; } error = Pa_IsFormatSupported((input_device < 0) ? NULL : &inputParams, (output_device < 0) ? NULL : &outputParams, sample_rate); if (error == paFormatIsSupported) { Py_INCREF(Py_True); return Py_True; } else { PyErr_SetObject(PyExc_ValueError, Py_BuildValue("(s,i)", Pa_GetErrorText(error), error)); return NULL; } } /************************************************************* * Stream Start / Stop / Info *************************************************************/ static PyObject *pa_start_stream(PyObject *self, PyObject *args) { int err; PyObject *stream_arg; _pyAudio_Stream *streamObject; if (!PyArg_ParseTuple(args, "O!", &_pyAudio_StreamType, &stream_arg)) { return NULL; } streamObject = (_pyAudio_Stream *)stream_arg; if (!_is_open(streamObject)) { PyErr_SetObject(PyExc_IOError, Py_BuildValue("(i,s)", paBadStreamPtr, "Stream closed")); return NULL; } // clang-format off Py_BEGIN_ALLOW_THREADS err = Pa_StartStream(streamObject->stream); Py_END_ALLOW_THREADS // clang-format on if ((err != paNoError) && (err != paStreamIsNotStopped)) { _cleanup_Stream_object(streamObject); #ifdef VERBOSE 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)); #endif PyErr_SetObject(PyExc_IOError, Py_BuildValue("(i,s)", err, Pa_GetErrorText(err))); return NULL; } Py_INCREF(Py_None); return Py_None; } static PyObject *pa_stop_stream(PyObject *self, PyObject *args) { int err; PyObject *stream_arg; _pyAudio_Stream *streamObject; if (!PyArg_ParseTuple(args, "O!", &_pyAudio_StreamType, &stream_arg)) { return NULL; } streamObject = (_pyAudio_Stream *)stream_arg; if (!_is_open(streamObject)) { PyErr_SetString(PyExc_IOError, "Stream not open"); return NULL; } // clang-format off Py_BEGIN_ALLOW_THREADS err = Pa_StopStream(streamObject->stream); Py_END_ALLOW_THREADS // clang-format on if ((err != paNoError) && (err != paStreamIsStopped)) { _cleanup_Stream_object(streamObject); #ifdef VERBOSE 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)); #endif PyErr_SetObject(PyExc_IOError, Py_BuildValue("(i,s)", err, Pa_GetErrorText(err))); return NULL; } Py_INCREF(Py_None); return Py_None; } static PyObject *pa_abort_stream(PyObject *self, PyObject *args) { int err; PyObject *stream_arg; _pyAudio_Stream *streamObject; if (!PyArg_ParseTuple(args, "O!", &_pyAudio_StreamType, &stream_arg)) { return NULL; } streamObject = (_pyAudio_Stream *)stream_arg; if (!_is_open(streamObject)) { PyErr_SetString(PyExc_IOError, "Stream not open"); return NULL; } // clang-format off Py_BEGIN_ALLOW_THREADS err = Pa_AbortStream(streamObject->stream); Py_END_ALLOW_THREADS // clang-format on if ((err != paNoError) && (err != paStreamIsStopped)) { _cleanup_Stream_object(streamObject); #ifdef VERBOSE 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)); #endif PyErr_SetObject(PyExc_IOError, Py_BuildValue("(i,s)", err, Pa_GetErrorText(err))); return NULL; } Py_INCREF(Py_None); return Py_None; } static PyObject *pa_is_stream_stopped(PyObject *self, PyObject *args) { int err; PyObject *stream_arg; _pyAudio_Stream *streamObject; if (!PyArg_ParseTuple(args, "O!", &_pyAudio_StreamType, &stream_arg)) { return NULL; } streamObject = (_pyAudio_Stream *)stream_arg; if (!_is_open(streamObject)) { PyErr_SetObject(PyExc_IOError, Py_BuildValue("(i,s)", paBadStreamPtr, "Stream closed")); return NULL; } // clang-format off Py_BEGIN_ALLOW_THREADS err = Pa_IsStreamStopped(streamObject->stream); Py_END_ALLOW_THREADS // clang-format on if (err < 0) { _cleanup_Stream_object(streamObject); #ifdef VERBOSE 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)); #endif PyErr_SetObject(PyExc_IOError, Py_BuildValue("(i,s)", err, Pa_GetErrorText(err))); return NULL; } if (err) { Py_INCREF(Py_True); return Py_True; } Py_INCREF(Py_False); return Py_False; } static PyObject *pa_is_stream_active(PyObject *self, PyObject *args) { int err; PyObject *stream_arg; _pyAudio_Stream *streamObject; if (!PyArg_ParseTuple(args, "O!", &_pyAudio_StreamType, &stream_arg)) { return NULL; } streamObject = (_pyAudio_Stream *)stream_arg; if (!_is_open(streamObject)) { PyErr_SetString(PyExc_IOError, "Stream not open"); return NULL; } // clang-format off Py_BEGIN_ALLOW_THREADS err = Pa_IsStreamActive(streamObject->stream); Py_END_ALLOW_THREADS // clang-format on if (err < 0) { _cleanup_Stream_object(streamObject); #ifdef VERBOSE 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)); #endif PyErr_SetObject(PyExc_IOError, Py_BuildValue("(i,s)", err, Pa_GetErrorText(err))); return NULL; } if (err) { Py_INCREF(Py_True); return Py_True; } Py_INCREF(Py_False); return Py_False; } static PyObject *pa_get_stream_time(PyObject *self, PyObject *args) { double time; PyObject *stream_arg; _pyAudio_Stream *streamObject; if (!PyArg_ParseTuple(args, "O!", &_pyAudio_StreamType, &stream_arg)) { return NULL; } streamObject = (_pyAudio_Stream *)stream_arg; if (!_is_open(streamObject)) { PyErr_SetObject(PyExc_IOError, Py_BuildValue("(i,s)", paBadStreamPtr, "Stream closed")); return NULL; } // clang-format off Py_BEGIN_ALLOW_THREADS time = Pa_GetStreamTime(streamObject->stream); Py_END_ALLOW_THREADS // clang-format on if (time == 0) { _cleanup_Stream_object(streamObject); PyErr_SetObject(PyExc_IOError, Py_BuildValue("(i,s)", paInternalError, "Internal Error")); return NULL; } return PyFloat_FromDouble(time); } static PyObject *pa_get_stream_cpu_load(PyObject *self, PyObject *args) { double cpuload; PyObject *stream_arg; _pyAudio_Stream *streamObject; if (!PyArg_ParseTuple(args, "O!", &_pyAudio_StreamType, &stream_arg)) { return NULL; } streamObject = (_pyAudio_Stream *)stream_arg; if (!_is_open(streamObject)) { PyErr_SetObject(PyExc_IOError, Py_BuildValue("(i,s)", paBadStreamPtr, "Stream closed")); return NULL; } // clang-format off Py_BEGIN_ALLOW_THREADS cpuload = Pa_GetStreamCpuLoad(streamObject->stream); Py_END_ALLOW_THREADS // clang-format on return PyFloat_FromDouble(cpuload); } /************************************************************* * Stream Read/Write *************************************************************/ static PyObject *pa_write_stream(PyObject *self, PyObject *args) { const char *data; int total_size; int total_frames; int err; int should_throw_exception = 0; PyObject *stream_arg; _pyAudio_Stream *streamObject; // clang-format off if (!PyArg_ParseTuple(args, "O!s#i|i", &_pyAudio_StreamType, &stream_arg, &data, &total_size, &total_frames, &should_throw_exception)) { return NULL; } // clang-format on if (total_frames < 0) { PyErr_SetString(PyExc_ValueError, "Invalid number of frames"); return NULL; } streamObject = (_pyAudio_Stream *)stream_arg; if (!_is_open(streamObject)) { PyErr_SetObject(PyExc_IOError, Py_BuildValue("(i,s)", paBadStreamPtr, "Stream closed")); return NULL; } // clang-format off Py_BEGIN_ALLOW_THREADS err = Pa_WriteStream(streamObject->stream, data, total_frames); Py_END_ALLOW_THREADS // clang-format on if (err != paNoError) { if (err == paOutputUnderflowed) { if (should_throw_exception) { goto error; } } else goto error; } Py_INCREF(Py_None); return Py_None; error: _cleanup_Stream_object(streamObject); #ifdef VERBOSE 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)); #endif PyErr_SetObject(PyExc_IOError, Py_BuildValue("(i,s)", err, Pa_GetErrorText(err))); return NULL; } static PyObject *pa_read_stream(PyObject *self, PyObject *args) { int err; int total_frames; short *sampleBlock; int num_bytes; PyObject *rv; int should_raise_exception = 0; PyObject *stream_arg; _pyAudio_Stream *streamObject; PaStreamParameters *inputParameters; // clang-format off if (!PyArg_ParseTuple(args, "O!i|i", &_pyAudio_StreamType, &stream_arg, &total_frames, &should_raise_exception)) { return NULL; } // clang-format on if (total_frames < 0) { PyErr_SetString(PyExc_ValueError, "Invalid number of frames"); return NULL; } streamObject = (_pyAudio_Stream *)stream_arg; if (!_is_open(streamObject)) { PyErr_SetObject(PyExc_IOError, Py_BuildValue("(i,s)", paBadStreamPtr, "Stream closed")); return NULL; } inputParameters = streamObject->inputParameters; num_bytes = (total_frames) * (inputParameters->channelCount) * (Pa_GetSampleSize(inputParameters->sampleFormat)); #ifdef VERBOSE fprintf(stderr, "Allocating %d bytes\n", num_bytes); #endif rv = PyBytes_FromStringAndSize(NULL, num_bytes); sampleBlock = (short *)PyBytes_AsString(rv); if (sampleBlock == NULL) { PyErr_SetObject(PyExc_IOError, Py_BuildValue("(i,s)", paInsufficientMemory, "Out of memory")); return NULL; } // clang-format off Py_BEGIN_ALLOW_THREADS err = Pa_ReadStream(streamObject->stream, sampleBlock, total_frames); Py_END_ALLOW_THREADS // clang-format on if (err != paNoError) { if (err == paInputOverflowed) { if (should_raise_exception) { goto error; } } else { goto error; } } return rv; error: _cleanup_Stream_object(streamObject); Py_XDECREF(rv); PyErr_SetObject(PyExc_IOError, Py_BuildValue("(i,s)", err, Pa_GetErrorText(err))); #ifdef VERBOSE 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)); #endif return NULL; } static PyObject *pa_get_stream_write_available(PyObject *self, PyObject *args) { signed long frames; PyObject *stream_arg; _pyAudio_Stream *streamObject; if (!PyArg_ParseTuple(args, "O!", &_pyAudio_StreamType, &stream_arg)) { return NULL; } streamObject = (_pyAudio_Stream *)stream_arg; if (!_is_open(streamObject)) { PyErr_SetObject(PyExc_IOError, Py_BuildValue("(i,s)", paBadStreamPtr, "Stream closed")); return NULL; } // clang-format off Py_BEGIN_ALLOW_THREADS frames = Pa_GetStreamWriteAvailable(streamObject->stream); Py_END_ALLOW_THREADS // clang-format on return PyLong_FromLong(frames); } static PyObject *pa_get_stream_read_available(PyObject *self, PyObject *args) { signed long frames; PyObject *stream_arg; _pyAudio_Stream *streamObject; if (!PyArg_ParseTuple(args, "O!", &_pyAudio_StreamType, &stream_arg)) { return NULL; } streamObject = (_pyAudio_Stream *)stream_arg; if (!_is_open(streamObject)) { PyErr_SetObject(PyExc_IOError, Py_BuildValue("(i,s)", paBadStreamPtr, "Stream closed")); return NULL; } // clang-format off Py_BEGIN_ALLOW_THREADS frames = Pa_GetStreamReadAvailable(streamObject->stream); Py_END_ALLOW_THREADS // clang-format on return PyLong_FromLong(frames); } /************************************************************ * * IV. Python Module Init * ************************************************************/ #if PY_MAJOR_VERSION >= 3 #define ERROR_INIT NULL #else #define ERROR_INIT /**/ #endif #if PY_MAJOR_VERSION >= 3 static struct PyModuleDef moduledef = { // PyModuleDef_HEAD_INIT, "_portaudio", NULL, -1, paMethods, NULL, NULL, NULL, NULL}; #endif PyMODINIT_FUNC #if PY_MAJOR_VERSION >= 3 PyInit__portaudio(void) #else init_portaudio(void) #endif { PyObject *m; PyEval_InitThreads(); _pyAudio_StreamType.tp_new = PyType_GenericNew; if (PyType_Ready(&_pyAudio_StreamType) < 0) { return ERROR_INIT; } _pyAudio_paDeviceInfoType.tp_new = PyType_GenericNew; if (PyType_Ready(&_pyAudio_paDeviceInfoType) < 0) { return ERROR_INIT; } _pyAudio_paHostApiInfoType.tp_new = PyType_GenericNew; if (PyType_Ready(&_pyAudio_paHostApiInfoType) < 0) { return ERROR_INIT; } #ifdef MACOSX _pyAudio_MacOSX_hostApiSpecificStreamInfoType.tp_new = PyType_GenericNew; if (PyType_Ready(&_pyAudio_MacOSX_hostApiSpecificStreamInfoType) < 0) { return ERROR_INIT; } #endif #if PY_MAJOR_VERSION >= 3 m = PyModule_Create(&moduledef); #else m = Py_InitModule("_portaudio", paMethods); #endif Py_INCREF(&_pyAudio_StreamType); Py_INCREF(&_pyAudio_paDeviceInfoType); Py_INCREF(&_pyAudio_paHostApiInfoType); #ifdef MACOSX Py_INCREF(&_pyAudio_MacOSX_hostApiSpecificStreamInfoType); PyModule_AddObject( m, "paMacCoreStreamInfo", (PyObject *)&_pyAudio_MacOSX_hostApiSpecificStreamInfoType); #endif /* Add PortAudio constants */ /* host apis */ PyModule_AddIntConstant(m, "paInDevelopment", paInDevelopment); PyModule_AddIntConstant(m, "paDirectSound", paDirectSound); PyModule_AddIntConstant(m, "paMME", paMME); PyModule_AddIntConstant(m, "paASIO", paASIO); PyModule_AddIntConstant(m, "paSoundManager", paSoundManager); PyModule_AddIntConstant(m, "paCoreAudio", paCoreAudio); PyModule_AddIntConstant(m, "paOSS", paOSS); PyModule_AddIntConstant(m, "paALSA", paALSA); PyModule_AddIntConstant(m, "paAL", paAL); PyModule_AddIntConstant(m, "paBeOS", paBeOS); PyModule_AddIntConstant(m, "paWDMKS", paWDMKS); PyModule_AddIntConstant(m, "paJACK", paJACK); PyModule_AddIntConstant(m, "paWASAPI", paWASAPI); PyModule_AddIntConstant(m, "paNoDevice", paNoDevice); /* formats */ PyModule_AddIntConstant(m, "paFloat32", paFloat32); PyModule_AddIntConstant(m, "paInt32", paInt32); PyModule_AddIntConstant(m, "paInt24", paInt24); PyModule_AddIntConstant(m, "paInt16", paInt16); PyModule_AddIntConstant(m, "paInt8", paInt8); PyModule_AddIntConstant(m, "paUInt8", paUInt8); PyModule_AddIntConstant(m, "paCustomFormat", paCustomFormat); /* error codes */ PyModule_AddIntConstant(m, "paNoError", paNoError); PyModule_AddIntConstant(m, "paNotInitialized", paNotInitialized); PyModule_AddIntConstant(m, "paUnanticipatedHostError", paUnanticipatedHostError); PyModule_AddIntConstant(m, "paInvalidChannelCount", paInvalidChannelCount); PyModule_AddIntConstant(m, "paInvalidSampleRate", paInvalidSampleRate); PyModule_AddIntConstant(m, "paInvalidDevice", paInvalidDevice); PyModule_AddIntConstant(m, "paInvalidFlag", paInvalidFlag); PyModule_AddIntConstant(m, "paSampleFormatNotSupported", paSampleFormatNotSupported); PyModule_AddIntConstant(m, "paBadIODeviceCombination", paBadIODeviceCombination); PyModule_AddIntConstant(m, "paInsufficientMemory", paInsufficientMemory); PyModule_AddIntConstant(m, "paBufferTooBig", paBufferTooBig); PyModule_AddIntConstant(m, "paBufferTooSmall", paBufferTooSmall); PyModule_AddIntConstant(m, "paNullCallback", paNullCallback); PyModule_AddIntConstant(m, "paBadStreamPtr", paBadStreamPtr); PyModule_AddIntConstant(m, "paTimedOut", paTimedOut); PyModule_AddIntConstant(m, "paInternalError", paInternalError); PyModule_AddIntConstant(m, "paDeviceUnavailable", paDeviceUnavailable); PyModule_AddIntConstant(m, "paIncompatibleHostApiSpecificStreamInfo", paIncompatibleHostApiSpecificStreamInfo); PyModule_AddIntConstant(m, "paStreamIsStopped", paStreamIsStopped); PyModule_AddIntConstant(m, "paStreamIsNotStopped", paStreamIsNotStopped); PyModule_AddIntConstant(m, "paInputOverflowed", paInputOverflowed); PyModule_AddIntConstant(m, "paOutputUnderflowed", paOutputUnderflowed); PyModule_AddIntConstant(m, "paHostApiNotFound", paHostApiNotFound); PyModule_AddIntConstant(m, "paInvalidHostApi", paInvalidHostApi); PyModule_AddIntConstant(m, "paCanNotReadFromACallbackStream", paCanNotReadFromACallbackStream); PyModule_AddIntConstant(m, "paCanNotWriteToACallbackStream", paCanNotWriteToACallbackStream); PyModule_AddIntConstant(m, "paCanNotReadFromAnOutputOnlyStream", paCanNotReadFromAnOutputOnlyStream); PyModule_AddIntConstant(m, "paCanNotWriteToAnInputOnlyStream", paCanNotWriteToAnInputOnlyStream); PyModule_AddIntConstant(m, "paIncompatibleStreamHostApi", paIncompatibleStreamHostApi); /* callback constants */ PyModule_AddIntConstant(m, "paContinue", paContinue); PyModule_AddIntConstant(m, "paComplete", paComplete); PyModule_AddIntConstant(m, "paAbort", paAbort); /* callback status flags */ PyModule_AddIntConstant(m, "paInputUnderflow", paInputUnderflow); PyModule_AddIntConstant(m, "paInputOverflow", paInputOverflow); PyModule_AddIntConstant(m, "paOutputUnderflow", paOutputUnderflow); PyModule_AddIntConstant(m, "paOutputOverflow", paOutputOverflow); PyModule_AddIntConstant(m, "paPrimingOutput", paPrimingOutput); #ifdef MACOSX PyModule_AddIntConstant(m, "paMacCoreChangeDeviceParameters", paMacCoreChangeDeviceParameters); PyModule_AddIntConstant(m, "paMacCoreFailIfConversionRequired", paMacCoreFailIfConversionRequired); PyModule_AddIntConstant(m, "paMacCoreConversionQualityMin", paMacCoreConversionQualityMin); PyModule_AddIntConstant(m, "paMacCoreConversionQualityMedium", paMacCoreConversionQualityMedium); PyModule_AddIntConstant(m, "paMacCoreConversionQualityLow", paMacCoreConversionQualityLow); PyModule_AddIntConstant(m, "paMacCoreConversionQualityHigh", paMacCoreConversionQualityHigh); PyModule_AddIntConstant(m, "paMacCoreConversionQualityMax", paMacCoreConversionQualityMax); PyModule_AddIntConstant(m, "paMacCorePlayNice", paMacCorePlayNice); PyModule_AddIntConstant(m, "paMacCorePro", paMacCorePro); PyModule_AddIntConstant(m, "paMacCoreMinimizeCPUButPlayNice", paMacCoreMinimizeCPUButPlayNice); PyModule_AddIntConstant(m, "paMacCoreMinimizeCPU", paMacCoreMinimizeCPU); #endif #if PY_MAJOR_VERSION >= 3 return m; #endif } python-pyaudio-0.2.11/src/_portaudiomodule.h000066400000000000000000000066401306334165300211110ustar00rootroot00000000000000/** * PyAudio : Python Bindings for PortAudio. * * PyAudio : API Header File * * 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 "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. * * 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. */ #ifndef __PAMODULE_H__ #define __PAMODULE_H__ /* version */ static PyObject * pa_get_version(PyObject *self, PyObject *args); static PyObject * pa_get_version_text(PyObject *self, PyObject *args); /* framework init */ static PyObject * pa_initialize(PyObject *self, PyObject *args); static PyObject * pa_terminate(PyObject *self, PyObject *args); /* host api */ static PyObject * pa_get_host_api_count(PyObject *self, PyObject *args); static PyObject * pa_get_default_host_api(PyObject *self, PyObject *args); static PyObject * pa_host_api_type_id_to_host_api_index(PyObject *self, PyObject *args); static PyObject * pa_host_api_device_index_to_device_index(PyObject *self, PyObject *args); static PyObject * pa_get_host_api_info(PyObject *self, PyObject *args); /* device api */ static PyObject * pa_get_device_count(PyObject *self, PyObject *args); static PyObject * pa_get_default_input_device(PyObject *self, PyObject *args); static PyObject * pa_get_default_output_device(PyObject *self, PyObject *args); static PyObject * pa_get_device_info(PyObject *self, PyObject *args); /* stream open/close */ static PyObject * pa_open(PyObject *self, PyObject *args, PyObject *kwargs); static PyObject * pa_close(PyObject *self, PyObject *args); static PyObject * pa_get_sample_size(PyObject *self, PyObject *args); static PyObject * pa_is_format_supported(PyObject *self, PyObject *args, PyObject *kwargs); /* stream start/stop/info */ static PyObject * pa_start_stream(PyObject *self, PyObject *args); static PyObject * pa_stop_stream(PyObject *self, PyObject *args); static PyObject * pa_abort_stream(PyObject *self, PyObject *args); static PyObject * pa_is_stream_stopped(PyObject *self, PyObject *args); static PyObject * pa_is_stream_active(PyObject *self, PyObject *args); static PyObject * pa_get_stream_time(PyObject *self, PyObject *args); static PyObject * pa_get_stream_cpu_load(PyObject *self, PyObject *args); /* stream write/read */ static PyObject * pa_write_stream(PyObject *self, PyObject *args); static PyObject * pa_read_stream(PyObject *self, PyObject *args); static PyObject * pa_get_stream_write_available(PyObject *self, PyObject *args); static PyObject * pa_get_stream_read_available(PyObject *self, PyObject *args); #endif python-pyaudio-0.2.11/src/pyaudio.py000066400000000000000000001131421306334165300174050ustar00rootroot00000000000000# PyAudio : Python Bindings for PortAudio. # 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 # "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. # 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. """ PyAudio provides Python bindings for PortAudio, the cross-platform audio I/O library. With PyAudio, you can easily use Python to play and record audio on a variety of platforms. PyAudio is inspired by: * pyPortAudio/fastaudio: Python bindings for PortAudio v18 API. * tkSnack: cross-platform sound toolkit for Tcl/Tk and Python. .. include:: ../sphinx/examples.rst Overview -------- **Classes** :py:class:`PyAudio`, :py:class:`Stream` .. only:: pamac **Host Specific Classes** :py:class:`PaMacCoreStreamInfo` **Stream Conversion Convenience Functions** :py:func:`get_sample_size`, :py:func:`get_format_from_width` **PortAudio version** :py:func:`get_portaudio_version`, :py:func:`get_portaudio_version_text` .. |PaSampleFormat| replace:: :ref:`PortAudio Sample Format ` .. _PaSampleFormat: **Portaudio Sample Formats** :py:data:`paFloat32`, :py:data:`paInt32`, :py:data:`paInt24`, :py:data:`paInt16`, :py:data:`paInt8`, :py:data:`paUInt8`, :py:data:`paCustomFormat` .. |PaHostAPI| replace:: :ref:`PortAudio Host API ` .. _PaHostAPI: **PortAudio Host APIs** :py:data:`paInDevelopment`, :py:data:`paDirectSound`, :py:data:`paMME`, :py:data:`paASIO`, :py:data:`paSoundManager`, :py:data:`paCoreAudio`, :py:data:`paOSS`, :py:data:`paALSA`, :py:data:`paAL`, :py:data:`paBeOS`, :py:data:`paWDMKS`, :py:data:`paJACK`, :py:data:`paWASAPI`, :py:data:`paNoDevice` .. |PaErrorCode| replace:: :ref:`PortAudio Error Code ` .. _PaErrorCode: **PortAudio Error Codes** :py:data:`paNoError`, :py:data:`paNotInitialized`, :py:data:`paUnanticipatedHostError`, :py:data:`paInvalidChannelCount`, :py:data:`paInvalidSampleRate`, :py:data:`paInvalidDevice`, :py:data:`paInvalidFlag`, :py:data:`paSampleFormatNotSupported`, :py:data:`paBadIODeviceCombination`, :py:data:`paInsufficientMemory`, :py:data:`paBufferTooBig`, :py:data:`paBufferTooSmall`, :py:data:`paNullCallback`, :py:data:`paBadStreamPtr`, :py:data:`paTimedOut`, :py:data:`paInternalError`, :py:data:`paDeviceUnavailable`, :py:data:`paIncompatibleHostApiSpecificStreamInfo`, :py:data:`paStreamIsStopped`, :py:data:`paStreamIsNotStopped`, :py:data:`paInputOverflowed`, :py:data:`paOutputUnderflowed`, :py:data:`paHostApiNotFound`, :py:data:`paInvalidHostApi`, :py:data:`paCanNotReadFromACallbackStream`, :py:data:`paCanNotWriteToACallbackStream`, :py:data:`paCanNotReadFromAnOutputOnlyStream`, :py:data:`paCanNotWriteToAnInputOnlyStream`, :py:data:`paIncompatibleStreamHostApi` .. |PaCallbackReturnCodes| replace:: :ref:`PortAudio Callback Return Code ` .. _PaCallbackReturnCodes: **PortAudio Callback Return Codes** :py:data:`paContinue`, :py:data:`paComplete`, :py:data:`paAbort` .. |PaCallbackFlags| replace:: :ref:`PortAutio Callback Flag ` .. _PaCallbackFlags: **PortAudio Callback Flags** :py:data:`paInputUnderflow`, :py:data:`paInputOverflow`, :py:data:`paOutputUnderflow`, :py:data:`paOutputOverflow`, :py:data:`paPrimingOutput` """ __author__ = "Hubert Pham" __version__ = "0.2.11" __docformat__ = "restructuredtext en" import sys # attempt to import PortAudio try: import _portaudio as pa except ImportError: print("Could not import the PyAudio C module '_portaudio'.") raise ############################################################ # GLOBALS ############################################################ ##### PaSampleFormat Sample Formats ##### paFloat32 = pa.paFloat32 #: 32 bit float paInt32 = pa.paInt32 #: 32 bit int paInt24 = pa.paInt24 #: 24 bit int paInt16 = pa.paInt16 #: 16 bit int paInt8 = pa.paInt8 #: 8 bit int paUInt8 = pa.paUInt8 #: 8 bit unsigned int paCustomFormat = pa.paCustomFormat #: a custom data format ###### HostAPI TypeId ##### paInDevelopment = pa.paInDevelopment #: Still in development paDirectSound = pa.paDirectSound #: DirectSound (Windows only) paMME = pa.paMME #: Multimedia Extension (Windows only) paASIO = pa.paASIO #: Steinberg Audio Stream Input/Output paSoundManager = pa.paSoundManager #: SoundManager (OSX only) paCoreAudio = pa.paCoreAudio #: CoreAudio (OSX only) paOSS = pa.paOSS #: Open Sound System (Linux only) paALSA = pa.paALSA #: Advanced Linux Sound Architecture (Linux only) paAL = pa.paAL #: Open Audio Library paBeOS = pa.paBeOS #: BeOS Sound System paWDMKS = pa.paWDMKS #: Windows Driver Model (Windows only) paJACK = pa.paJACK #: JACK Audio Connection Kit paWASAPI = pa.paWASAPI #: Windows Vista Audio stack architecture paNoDevice = pa.paNoDevice #: Not actually an audio device ###### portaudio error codes ##### paNoError = pa.paNoError paNotInitialized = pa.paNotInitialized paUnanticipatedHostError = pa.paUnanticipatedHostError paInvalidChannelCount = pa.paInvalidChannelCount paInvalidSampleRate = pa.paInvalidSampleRate paInvalidDevice = pa.paInvalidDevice paInvalidFlag = pa.paInvalidFlag paSampleFormatNotSupported = pa.paSampleFormatNotSupported paBadIODeviceCombination = pa.paBadIODeviceCombination paInsufficientMemory = pa.paInsufficientMemory paBufferTooBig = pa.paBufferTooBig paBufferTooSmall = pa.paBufferTooSmall paNullCallback = pa.paNullCallback paBadStreamPtr = pa.paBadStreamPtr paTimedOut = pa.paTimedOut paInternalError = pa.paInternalError paDeviceUnavailable = pa.paDeviceUnavailable paIncompatibleHostApiSpecificStreamInfo = pa.paIncompatibleHostApiSpecificStreamInfo paStreamIsStopped = pa.paStreamIsStopped paStreamIsNotStopped = pa.paStreamIsNotStopped paInputOverflowed = pa.paInputOverflowed paOutputUnderflowed = pa.paOutputUnderflowed paHostApiNotFound = pa.paHostApiNotFound paInvalidHostApi = pa.paInvalidHostApi paCanNotReadFromACallbackStream = pa.paCanNotReadFromACallbackStream paCanNotWriteToACallbackStream = pa.paCanNotWriteToACallbackStream paCanNotReadFromAnOutputOnlyStream = pa.paCanNotReadFromAnOutputOnlyStream paCanNotWriteToAnInputOnlyStream = pa.paCanNotWriteToAnInputOnlyStream paIncompatibleStreamHostApi = pa.paIncompatibleStreamHostApi ###### portaudio callback return codes ###### paContinue = pa.paContinue #: There is more audio data to come paComplete = pa.paComplete #: This was the last block of audio data paAbort = pa.paAbort #: An error ocurred, stop playback/recording ###### portaudio callback flags ###### paInputUnderflow = pa.paInputUnderflow #: Buffer underflow in input paInputOverflow = pa.paInputOverflow #: Buffer overflow in input paOutputUnderflow = pa.paOutputUnderflow #: Buffer underflow in output paOutputOverflow = pa.paOutputOverflow #: Buffer overflow in output paPrimingOutput = pa.paPrimingOutput #: Just priming, not playing yet ############################################################ # Convenience Functions ############################################################ def get_sample_size(format): """ Returns the size (in bytes) for the specified sample *format*. :param format: A |PaSampleFormat| constant. :raises ValueError: on invalid specified `format`. :rtype: integer """ return pa.get_sample_size(format) 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: when invalid *width* :rtype: A |PaSampleFormat| constant """ if width == 1: if unsigned: return paUInt8 else: return paInt8 elif width == 2: return paInt16 elif width == 3: return paInt24 elif width == 4: return paFloat32 else: raise ValueError("Invalid width: %d" % width) ############################################################ # Versioning ############################################################ def get_portaudio_version(): """ Returns portaudio version. :rtype: string """ return pa.get_version() def get_portaudio_version_text(): """ Returns PortAudio version as a text string. :rtype: string """ return pa.get_version_text() ############################################################ # Wrapper around _portaudio Stream (Internal) ############################################################ # Note: See PyAudio class below for main export. class Stream: """ PortAudio Stream Wrapper. Use :py:func:`PyAudio.open` to make a new :py:class:`Stream`. **Opening and Closing** :py:func:`__init__`, :py:func:`close` **Stream Info** :py:func:`get_input_latency`, :py:func:`get_output_latency`, :py:func:`get_time`, :py:func:`get_cpu_load` **Stream Management** :py:func:`start_stream`, :py:func:`stop_stream`, :py:func:`is_active`, :py:func:`is_stopped` **Input Output** :py:func:`write`, :py:func:`read`, :py:func:`get_read_available`, :py:func:`get_write_available` """ def __init__(self, PA_manager, rate, channels, format, input=False, output=False, input_device_index=None, output_device_index=None, frames_per_buffer=1024, start=True, input_host_api_specific_stream_info=None, output_host_api_specific_stream_info=None, stream_callback=None): """ Initialize a stream; this should be called by :py:func:`PyAudio.open`. A stream can either be input, output, or both. :param PA_manager: A reference to the managing :py:class:`PyAudio` instance :param rate: Sampling rate :param channels: Number of channels :param format: Sampling size and format. See |PaSampleFormat|. :param input: Specifies whether this is an input stream. Defaults to ``False``. :param output: Specifies whether this is an output stream. Defaults to ``False``. :param input_device_index: Index of Input Device to use. Unspecified (or ``None``) uses default device. Ignored if `input` is ``False``. :param output_device_index: Index of Output Device to use. Unspecified (or ``None``) uses the default device. Ignored if `output` is ``False``. :param frames_per_buffer: Specifies the number of frames per buffer. :param start: Start the stream running immediately. Defaults to ``True``. In general, there is no reason to set this to ``False``. :param input_host_api_specific_stream_info: Specifies a host API specific stream information data structure for input. .. only:: pamac See :py:class:`PaMacCoreStreamInfo`. :param output_host_api_specific_stream_info: Specifies a host API specific stream information data structure for output. .. only:: pamac See :py:class:`PaMacCoreStreamInfo`. :param stream_callback: Specifies a callback function for *non-blocking* (callback) operation. Default is ``None``, which indicates *blocking* operation (i.e., :py:func:`Stream.read` and :py:func:`Stream.write`). To use non-blocking operation, specify a callback that conforms to the following signature: .. code-block:: python callback(in_data, # recorded data if input=True; else None frame_count, # number of frames time_info, # dictionary status_flags) # PaCallbackFlags ``time_info`` is a dictionary with the following keys: ``input_buffer_adc_time``, ``current_time``, and ``output_buffer_dac_time``; see the PortAudio documentation for their meanings. ``status_flags`` is one of |PaCallbackFlags|. The callback must return a tuple: .. code-block:: python (out_data, flag) ``out_data`` is a byte array whose length should be the (``frame_count * channels * bytes-per-channel``) if ``output=True`` or ``None`` if ``output=False``. ``flag`` must be either :py:data:`paContinue`, :py:data:`paComplete` or :py:data:`paAbort` (one of |PaCallbackReturnCodes|). When ``output=True`` and ``out_data`` does not contain at least ``frame_count`` frames, :py:data:`paComplete` is assumed for ``flag``. **Note:** ``stream_callback`` is called in a separate thread (from the main thread). Exceptions that occur in the ``stream_callback`` will: 1. print a traceback on standard error to aid debugging, 2. queue the exception to be thrown (at some point) in the main thread, and 3. return `paAbort` to PortAudio to stop the stream. **Note:** Do not call :py:func:`Stream.read` or :py:func:`Stream.write` if using non-blocking operation. **See:** PortAudio's callback signature for additional details: http://portaudio.com/docs/v19-doxydocs/portaudio_8h.html#a8a60fb2a5ec9cbade3f54a9c978e2710 :raise ValueError: Neither input nor output are set True. """ # no stupidity allowed if not (input or output): raise ValueError("Must specify an input or output " + "stream.") # remember parent self._parent = PA_manager # remember if we are an: input, output (or both) self._is_input = input self._is_output = output # are we running? self._is_running = start # remember some parameters self._rate = rate self._channels = channels self._format = format self._frames_per_buffer = frames_per_buffer arguments = { 'rate' : rate, 'channels' : channels, 'format' : format, 'input' : input, 'output' : output, 'input_device_index' : input_device_index, 'output_device_index' : output_device_index, 'frames_per_buffer' : frames_per_buffer} if input_host_api_specific_stream_info: _l = input_host_api_specific_stream_info arguments[ 'input_host_api_specific_stream_info' ] = _l._get_host_api_stream_object() if output_host_api_specific_stream_info: _l = output_host_api_specific_stream_info arguments[ 'output_host_api_specific_stream_info' ] = _l._get_host_api_stream_object() if stream_callback: arguments['stream_callback'] = stream_callback # calling pa.open returns a stream object self._stream = pa.open(**arguments) self._input_latency = self._stream.inputLatency self._output_latency = self._stream.outputLatency if self._is_running: pa.start_stream(self._stream) def close(self): """ Close the stream """ pa.close(self._stream) self._is_running = False self._parent._remove_stream(self) ############################################################ # Stream Info ############################################################ def get_input_latency(self): """ Return the input latency. :rtype: float """ return self._stream.inputLatency def get_output_latency(self): """ Return the output latency. :rtype: float """ return self._stream.outputLatency def get_time(self): """ Return stream time. :rtype: float """ return pa.get_stream_time(self._stream) def get_cpu_load(self): """ Return the CPU load. This is always 0.0 for the blocking API. :rtype: float """ return pa.get_stream_cpu_load(self._stream) ############################################################ # Stream Management ############################################################ def start_stream(self): """ Start the stream. """ if self._is_running: return pa.start_stream(self._stream) self._is_running = True def stop_stream(self): """ Stop the stream. Once the stream is stopped, one may not call write or read. Call :py:func:`start_stream` to resume the stream. """ if not self._is_running: return pa.stop_stream(self._stream) self._is_running = False def is_active(self): """ Returns whether the stream is active. :rtype: bool """ return pa.is_stream_active(self._stream) def is_stopped(self): """ Returns whether the stream is stopped. :rtype: bool """ return pa.is_stream_stopped(self._stream) ############################################################ # Reading/Writing ############################################################ def write(self, frames, num_frames=None, exception_on_underflow=False): """ Write samples to the stream. Do not call when using *non-blocking* mode. :param frames: The frames of data. :param num_frames: The number of frames to write. Defaults to None, in which this value will be automatically computed. :param exception_on_underflow: Specifies whether an IOError exception should be thrown (or silently ignored) on buffer underflow. Defaults to False for improved performance, especially on slower platforms. :raises IOError: if the stream is not an output stream or if the write operation was unsuccessful. :rtype: `None` """ if not self._is_output: raise IOError("Not output stream", paCanNotWriteToAnInputOnlyStream) if num_frames == None: # determine how many frames to read width = get_sample_size(self._format) num_frames = int(len(frames) / (self._channels * width)) #print len(frames), self._channels, self._width, num_frames pa.write_stream(self._stream, frames, num_frames, exception_on_underflow) def read(self, num_frames, exception_on_overflow=True): """ Read samples from the stream. Do not call when using *non-blocking* mode. :param num_frames: The number of frames to read. :param exception_on_overflow: Specifies whether an IOError exception should be thrown (or silently ignored) on input buffer overflow. Defaults to True. :raises IOError: if stream is not an input stream or if the read operation was unsuccessful. :rtype: string """ if not self._is_input: raise IOError("Not input stream", paCanNotReadFromAnOutputOnlyStream) return pa.read_stream(self._stream, num_frames, exception_on_overflow) def get_read_available(self): """ Return the number of frames that can be read without waiting. :rtype: integer """ return pa.get_stream_read_available(self._stream) def get_write_available(self): """ Return the number of frames that can be written without waiting. :rtype: integer """ return pa.get_stream_write_available(self._stream) ############################################################ # Main Export ############################################################ class PyAudio: """ Python interface to PortAudio. Provides methods to: - initialize and terminate PortAudio - open and close streams - query and inspect the available PortAudio Host APIs - query and inspect the available PortAudio audio devices Use this class to open and close streams. **Stream Management** :py:func:`open`, :py:func:`close` **Host API** :py:func:`get_host_api_count`, :py:func:`get_default_host_api_info`, :py:func:`get_host_api_info_by_type`, :py:func:`get_host_api_info_by_index`, :py:func:`get_device_info_by_host_api_device_index` **Device API** :py:func:`get_device_count`, :py:func:`is_format_supported`, :py:func:`get_default_input_device_info`, :py:func:`get_default_output_device_info`, :py:func:`get_device_info_by_index` **Stream Format Conversion** :py:func:`get_sample_size`, :py:func:`get_format_from_width` **Details** """ ############################################################ # Initialization and Termination ############################################################ def __init__(self): """Initialize PortAudio.""" pa.initialize() self._streams = set() def terminate(self): """ Terminate PortAudio. :attention: Be sure to call this method for every instance of this object to release PortAudio resources. """ for stream in self._streams.copy(): stream.close() self._streams = set() pa.terminate() ############################################################ # Stream Format ############################################################ def get_sample_size(self, format): """ Returns the size (in bytes) for the specified sample `format` (a |PaSampleFormat| constant). :param format: A |PaSampleFormat| constant. :raises ValueError: Invalid specified `format`. :rtype: integer """ return pa.get_sample_size(format) def get_format_from_width(self, 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: A |PaSampleFormat| constant. """ if width == 1: if unsigned: return paUInt8 else: return paInt8 elif width == 2: return paInt16 elif width == 3: return paInt24 elif width == 4: return paFloat32 else: raise ValueError("Invalid width: %d" % width) ############################################################ # Stream Factory ############################################################ def open(self, *args, **kwargs): """ Open a new stream. See constructor for :py:func:`Stream.__init__` for parameter details. :returns: A new :py:class:`Stream` """ stream = Stream(self, *args, **kwargs) self._streams.add(stream) return stream def close(self, stream): """ Close a stream. Typically use :py:func:`Stream.close` instead. :param stream: An instance of the :py:class:`Stream` object. :raises ValueError: if stream does not exist. """ if stream not in self._streams: raise ValueError("Stream `%s' not found" % str(stream)) stream.close() def _remove_stream(self, stream): """ Internal method. Removes a stream. :param stream: An instance of the :py:class:`Stream` object. """ if stream in self._streams: self._streams.remove(stream) ############################################################ # Host API Inspection ############################################################ def get_host_api_count(self): """ Return the number of available PortAudio Host APIs. :rtype: integer """ return pa.get_host_api_count() def get_default_host_api_info(self): """ Return a dictionary containing the default Host API parameters. The keys of the dictionary mirror the data fields of PortAudio's ``PaHostApiInfo`` structure. :raises IOError: if no default input device is available :rtype: dict """ defaultHostApiIndex = pa.get_default_host_api() return self.get_host_api_info_by_index(defaultHostApiIndex) def get_host_api_info_by_type(self, host_api_type): """ Return a dictionary containing the Host API parameters for the host API specified by the `host_api_type`. The keys of the dictionary mirror the data fields of PortAudio's ``PaHostApiInfo`` structure. :param host_api_type: The desired |PaHostAPI| :raises IOError: for invalid `host_api_type` :rtype: dict """ index = pa.host_api_type_id_to_host_api_index(host_api_type) return self.get_host_api_info_by_index(index) def get_host_api_info_by_index(self, host_api_index): """ Return a dictionary containing the Host API parameters for the host API specified by the `host_api_index`. The keys of the dictionary mirror the data fields of PortAudio's ``PaHostApiInfo`` structure. :param host_api_index: The host api index :raises IOError: for invalid `host_api_index` :rtype: dict """ return self._make_host_api_dictionary( host_api_index, pa.get_host_api_info(host_api_index) ) def get_device_info_by_host_api_device_index(self, host_api_index, host_api_device_index): """ Return a dictionary containing the Device parameters for a given Host API's n'th device. The keys of the dictionary mirror the data fields of PortAudio's ``PaDeviceInfo`` structure. :param host_api_index: The Host API index number :param host_api_device_index: The n'th device of the host API :raises IOError: for invalid indices :rtype: dict """ long_method_name = pa.host_api_device_index_to_device_index device_index = long_method_name(host_api_index, host_api_device_index) return self.get_device_info_by_index(device_index) def _make_host_api_dictionary(self, index, host_api_struct): """ Internal method to create Host API dictionary that mirrors PortAudio's ``PaHostApiInfo`` structure. :rtype: dict """ return {'index' : index, 'structVersion' : host_api_struct.structVersion, 'type' : host_api_struct.type, 'name' : host_api_struct.name, 'deviceCount' : host_api_struct.deviceCount, 'defaultInputDevice' : host_api_struct.defaultInputDevice, 'defaultOutputDevice' : host_api_struct.defaultOutputDevice} ############################################################ # Device Inspection ############################################################ def get_device_count(self): """ Return the number of PortAudio Host APIs. :rtype: integer """ return pa.get_device_count() def is_format_supported(self, rate, input_device=None, input_channels=None, input_format=None, output_device=None, output_channels=None, output_format=None): """ Check to see if specified device configuration is supported. Returns True if the configuration is supported; throws a ValueError exception otherwise. :param rate: Specifies the desired rate (in Hz) :param input_device: The input device index. Specify ``None`` (default) for half-duplex output-only streams. :param input_channels: The desired number of input channels. Ignored if `input_device` is not specified (or ``None``). :param input_format: PortAudio sample format constant defined in this module :param output_device: The output device index. Specify ``None`` (default) for half-duplex input-only streams. :param output_channels: The desired number of output channels. Ignored if `input_device` is not specified (or ``None``). :param output_format: |PaSampleFormat| constant. :rtype: bool :raises ValueError: tuple containing (error string, |PaErrorCode|). """ if input_device == None and output_device == None: raise ValueError("must specify stream format for input, " +\ "output, or both", paInvalidDevice); kwargs = {} if input_device != None: kwargs['input_device'] = input_device kwargs['input_channels'] = input_channels kwargs['input_format'] = input_format if output_device != None: kwargs['output_device'] = output_device kwargs['output_channels'] = output_channels kwargs['output_format'] = output_format return pa.is_format_supported(rate, **kwargs) def get_default_input_device_info(self): """ Return the default input Device parameters as a dictionary. The keys of the dictionary mirror the data fields of PortAudio's ``PaDeviceInfo`` structure. :raises IOError: No default input device available. :rtype: dict """ device_index = pa.get_default_input_device() return self.get_device_info_by_index(device_index) def get_default_output_device_info(self): """ Return the default output Device parameters as a dictionary. The keys of the dictionary mirror the data fields of PortAudio's ``PaDeviceInfo`` structure. :raises IOError: No default output device available. :rtype: dict """ device_index = pa.get_default_output_device() return self.get_device_info_by_index(device_index) def get_device_info_by_index(self, device_index): """ Return the Device parameters for device specified in `device_index` as a dictionary. The keys of the dictionary mirror the data fields of PortAudio's ``PaDeviceInfo`` structure. :param device_index: The device index :raises IOError: Invalid `device_index`. :rtype: dict """ return self._make_device_info_dictionary( device_index, pa.get_device_info(device_index) ) def _make_device_info_dictionary(self, index, device_info): """ Internal method to create Device Info dictionary that mirrors PortAudio's ``PaDeviceInfo`` structure. :rtype: dict """ device_name = device_info.name # Attempt to decode device_name for codec in ["utf-8", "cp1252"]: try: device_name = device_name.decode(codec) break except: pass # If we fail to decode, we return the raw bytes and let the caller # deal with the encoding. return {'index' : index, 'structVersion' : device_info.structVersion, 'name' : device_name, 'hostApi' : device_info.hostApi, 'maxInputChannels' : device_info.maxInputChannels, 'maxOutputChannels' : device_info.maxOutputChannels, 'defaultLowInputLatency' : device_info.defaultLowInputLatency, 'defaultLowOutputLatency' : device_info.defaultLowOutputLatency, 'defaultHighInputLatency' : device_info.defaultHighInputLatency, 'defaultHighOutputLatency' : device_info.defaultHighOutputLatency, 'defaultSampleRate' : device_info.defaultSampleRate } ###################################################################### # Host Specific Stream Info ###################################################################### try: paMacCoreStreamInfo = pa.paMacCoreStreamInfo except AttributeError: pass else: class PaMacCoreStreamInfo: """ Mac OS X-only: PaMacCoreStreamInfo is a PortAudio Host API Specific Stream Info data structure for specifying Mac OS X-only settings. Instantiate this class (if desired) and pass the instance as the argument in :py:func:`PyAudio.open` to parameters ``input_host_api_specific_stream_info`` or ``output_host_api_specific_stream_info``. (See :py:func:`Stream.__init__`.) :note: Mac OS X only. .. |PaMacCoreFlags| replace:: :ref:`PortAudio Mac Core Flags ` .. _PaMacCoreFlags: **PortAudio Mac Core Flags** :py:data:`paMacCoreChangeDeviceParameters`, :py:data:`paMacCoreFailIfConversionRequired`, :py:data:`paMacCoreConversionQualityMin`, :py:data:`paMacCoreConversionQualityMedium`, :py:data:`paMacCoreConversionQualityLow`, :py:data:`paMacCoreConversionQualityHigh`, :py:data:`paMacCoreConversionQualityMax`, :py:data:`paMacCorePlayNice`, :py:data:`paMacCorePro`, :py:data:`paMacCoreMinimizeCPUButPlayNice`, :py:data:`paMacCoreMinimizeCPU` **Settings** :py:func:`get_flags`, :py:func:`get_channel_map` """ paMacCoreChangeDeviceParameters = pa.paMacCoreChangeDeviceParameters paMacCoreFailIfConversionRequired = pa.paMacCoreFailIfConversionRequired paMacCoreConversionQualityMin = pa.paMacCoreConversionQualityMin paMacCoreConversionQualityMedium = pa.paMacCoreConversionQualityMedium paMacCoreConversionQualityLow = pa.paMacCoreConversionQualityLow paMacCoreConversionQualityHigh = pa.paMacCoreConversionQualityHigh paMacCoreConversionQualityMax = pa.paMacCoreConversionQualityMax paMacCorePlayNice = pa.paMacCorePlayNice paMacCorePro = pa.paMacCorePro paMacCoreMinimizeCPUButPlayNice = pa.paMacCoreMinimizeCPUButPlayNice paMacCoreMinimizeCPU = pa.paMacCoreMinimizeCPU def __init__(self, flags=None, channel_map=None): """ Initialize with flags and channel_map. See PortAudio documentation for more details on these parameters; they are passed almost verbatim to the PortAudio library. :param flags: |PaMacCoreFlags| OR'ed together. See :py:class:`PaMacCoreStreamInfo`. :param channel_map: An array describing the channel mapping. See PortAudio documentation for usage. """ kwargs = {"flags" : flags, "channel_map" : channel_map} if flags == None: del kwargs["flags"] if channel_map == None: del kwargs["channel_map"] self._paMacCoreStreamInfo = paMacCoreStreamInfo(**kwargs) def get_flags(self): """ Return the flags set at instantiation. :rtype: integer """ return self._paMacCoreStreamInfo.flags def get_channel_map(self): """ Return the channel map set at instantiation. :rtype: tuple or None """ return self._paMacCoreStreamInfo.channel_map def _get_host_api_stream_object(self): """Private method.""" return self._paMacCoreStreamInfo python-pyaudio-0.2.11/tests/000077500000000000000000000000001306334165300157325ustar00rootroot00000000000000python-pyaudio-0.2.11/tests/error_tests.py000066400000000000000000000102141306334165300206550ustar00rootroot00000000000000import 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') python-pyaudio-0.2.11/tests/pyaudio_tests.py000066400000000000000000000632771306334165300212170ustar00rootroot00000000000000# -*- 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)