Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-sounddevice for openSUSE:Factory checked in at 2022-10-08 01:26:10 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-sounddevice (Old) and /work/SRC/openSUSE:Factory/.python-sounddevice.new.2275 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-sounddevice" Sat Oct 8 01:26:10 2022 rev:11 rq:1008875 version:0.4.5 Changes: -------- --- /work/SRC/openSUSE:Factory/python-sounddevice/python-sounddevice.changes 2021-04-19 21:06:08.768042576 +0200 +++ /work/SRC/openSUSE:Factory/.python-sounddevice.new.2275/python-sounddevice.changes 2022-10-08 01:26:34.274414915 +0200 @@ -1,0 +2,8 @@ +Fri Oct 7 16:18:39 UTC 2022 - Yogalakshmi Arunachalam <yarunacha...@suse.com> + +- Update to version 0.4.5 + * Add index field to device dict + * Require Python >= 3.7 + * Add PaWasapi_IsLoopback() to cdef (high-level interface not yet available) + +------------------------------------------------------------------- Old: ---- sounddevice-0.4.1.tar.gz New: ---- sounddevice-0.4.5.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-sounddevice.spec ++++++ --- /var/tmp/diff_new_pack.UvS91K/_old 2022-10-08 01:26:34.650415778 +0200 +++ /var/tmp/diff_new_pack.UvS91K/_new 2022-10-08 01:26:34.654415787 +0200 @@ -1,7 +1,7 @@ # # spec file for package python-sounddevice # -# Copyright (c) 2021 SUSE LLC +# Copyright (c) 2022 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -20,7 +20,7 @@ %define skip_python2 1 %define skip_python36 1 Name: python-sounddevice -Version: 0.4.1 +Version: 0.4.5 Release: 0 Summary: Module to play and record sound with Python License: MIT ++++++ sounddevice-0.4.1.tar.gz -> sounddevice-0.4.5.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sounddevice-0.4.1/CONTRIBUTING.rst new/sounddevice-0.4.5/CONTRIBUTING.rst --- old/sounddevice-0.4.1/CONTRIBUTING.rst 2020-09-23 12:45:04.000000000 +0200 +++ new/sounddevice-0.4.5/CONTRIBUTING.rst 2021-10-12 22:18:35.000000000 +0200 @@ -126,4 +126,4 @@ The generated files will be available in the directory ``build/sphinx/html/``. -.. _Sphinx: http://sphinx-doc.org/ +.. _Sphinx: https://www.sphinx-doc.org/ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sounddevice-0.4.1/LICENSE new/sounddevice-0.4.5/LICENSE --- old/sounddevice-0.4.1/LICENSE 2020-07-15 17:23:35.000000000 +0200 +++ new/sounddevice-0.4.5/LICENSE 2022-08-21 17:22:18.000000000 +0200 @@ -1,4 +1,4 @@ -Copyright (c) 2015-2020 Matthias Geier +Copyright (c) 2015-2022 Matthias Geier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sounddevice-0.4.1/NEWS.rst new/sounddevice-0.4.5/NEWS.rst --- old/sounddevice-0.4.1/NEWS.rst 2020-09-26 19:14:45.000000000 +0200 +++ new/sounddevice-0.4.5/NEWS.rst 2022-08-21 17:24:03.000000000 +0200 @@ -1,3 +1,19 @@ +0.4.5 (2022-08-21): + * Add ``index`` field to device dict + * Require Python >= 3.7 + * Add PaWasapi_IsLoopback() to cdef (high-level interface not yet available) + +0.4.4 (2021-12-31): + * Exact device string matches can now include the host API name + +0.4.3 (2021-10-20): + * Fix dimension check in `Stream.write()` + * Provide "universal" (x86_64 and arm64) ``.dylib`` for macOS + +0.4.2 (2021-07-18): + * Update PortAudio binaries to version 19.7.0 + * Wheel names are now shorter + 0.4.1 (2020-09-26): * `CallbackFlags` attributes are now writable @@ -73,13 +89,13 @@ 0.2.0 (2015-07-03): * Support for wheels including a dylib for Mac OS X and DLLs for Windows. - The code for creating the wheels is largely taken from PySoundFile_. + The code for creating the wheels is largely taken from the soundfile_ module. * Remove logging (this seemed too intrusive) * Return callback status from `wait()` and add the new function `get_status()` * `playrec()`: Rename the arguments *input_channels* and *input_dtype* to *channels* and *dtype*, respectively - .. _PySoundFile: https://github.com/bastibe/SoundFile/ + .. _soundfile: https://github.com/bastibe/python-soundfile/ 0.1.0 (2015-06-20): Initial release. Some ideas are taken from PySoundCard_. Thanks to Bastian diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sounddevice-0.4.1/PKG-INFO new/sounddevice-0.4.5/PKG-INFO --- old/sounddevice-0.4.1/PKG-INFO 2020-09-26 19:17:25.373517000 +0200 +++ new/sounddevice-0.4.5/PKG-INFO 2022-08-21 17:36:27.595208600 +0200 @@ -1,32 +1,12 @@ Metadata-Version: 2.1 Name: sounddevice -Version: 0.4.1 +Version: 0.4.5 Summary: Play and Record Sound with Python Home-page: http://python-sounddevice.readthedocs.io/ Author: Matthias Geier Author-email: matthias.ge...@gmail.com License: MIT -Description: Play and Record Sound with Python - ================================= - - This Python_ module provides bindings for the PortAudio_ library and a few - convenience functions to play and record NumPy_ arrays containing audio signals. - - The ``sounddevice`` module is available for Linux, macOS and Windows. - - Documentation: - https://python-sounddevice.readthedocs.io/ - - Source code repository and issue tracker: - https://github.com/spatialaudio/python-sounddevice/ - - License: - MIT -- see the file ``LICENSE`` for details. - - .. _Python: https://www.python.org/ - .. _PortAudio: http://www.portaudio.com/ - .. _NumPy: https://numpy.org/ - +Project-URL: Source, https://github.com/spatialaudio/python-sounddevice Keywords: sound,audio,PortAudio,play,record,playrec Platform: any Classifier: License :: OSI Approved :: MIT License @@ -34,5 +14,27 @@ Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Topic :: Multimedia :: Sound/Audio -Requires-Python: >=3 +Requires-Python: >=3.7 Provides-Extra: NumPy +License-File: LICENSE + +Play and Record Sound with Python +================================= + +This Python_ module provides bindings for the PortAudio_ library and a few +convenience functions to play and record NumPy_ arrays containing audio signals. + +The ``sounddevice`` module is available for Linux, macOS and Windows. + +Documentation: + https://python-sounddevice.readthedocs.io/ + +Source code repository and issue tracker: + https://github.com/spatialaudio/python-sounddevice/ + +License: + MIT -- see the file ``LICENSE`` for details. + +.. _Python: https://www.python.org/ +.. _PortAudio: http://www.portaudio.com/ +.. _NumPy: https://numpy.org/ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sounddevice-0.4.1/doc/CONTRIBUTING.rst new/sounddevice-0.4.5/doc/CONTRIBUTING.rst --- old/sounddevice-0.4.1/doc/CONTRIBUTING.rst 2020-09-23 12:45:04.000000000 +0200 +++ new/sounddevice-0.4.5/doc/CONTRIBUTING.rst 2021-10-12 22:18:35.000000000 +0200 @@ -126,4 +126,4 @@ The generated files will be available in the directory ``build/sphinx/html/``. -.. _Sphinx: http://sphinx-doc.org/ +.. _Sphinx: https://www.sphinx-doc.org/ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sounddevice-0.4.1/doc/api/checking-hardware.rst new/sounddevice-0.4.5/doc/api/checking-hardware.rst --- old/sounddevice-0.4.1/doc/api/checking-hardware.rst 2020-07-15 17:23:35.000000000 +0200 +++ new/sounddevice-0.4.5/doc/api/checking-hardware.rst 2022-07-19 19:26:16.000000000 +0200 @@ -3,14 +3,16 @@ .. currentmodule:: sounddevice -.. autosummary:: - :nosignatures: +.. topic:: Overview - query_devices - DeviceList - query_hostapis - check_input_settings - check_output_settings + .. autosummary:: + :nosignatures: + + query_devices + DeviceList + query_hostapis + check_input_settings + check_output_settings .. autofunction:: query_devices diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sounddevice-0.4.1/doc/api/convenience-functions.rst new/sounddevice-0.4.5/doc/api/convenience-functions.rst --- old/sounddevice-0.4.1/doc/api/convenience-functions.rst 2020-07-15 17:23:35.000000000 +0200 +++ new/sounddevice-0.4.5/doc/api/convenience-functions.rst 2022-07-19 19:26:16.000000000 +0200 @@ -3,16 +3,18 @@ .. currentmodule:: sounddevice -.. autosummary:: - :nosignatures: +.. topic:: Overview - play - rec - playrec - wait - stop - get_status - get_stream + .. autosummary:: + :nosignatures: + + play + rec + playrec + wait + stop + get_status + get_stream .. autofunction:: play diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sounddevice-0.4.1/doc/api/expert-mode.rst new/sounddevice-0.4.5/doc/api/expert-mode.rst --- old/sounddevice-0.4.1/doc/api/expert-mode.rst 2020-07-15 17:23:35.000000000 +0200 +++ new/sounddevice-0.4.5/doc/api/expert-mode.rst 2022-07-19 19:26:16.000000000 +0200 @@ -3,13 +3,15 @@ .. currentmodule:: sounddevice -.. autosummary:: - :nosignatures: +.. topic:: Overview - _initialize - _terminate - _split - _StreamBase + .. autosummary:: + :nosignatures: + + _initialize + _terminate + _split + _StreamBase .. autofunction:: _initialize diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sounddevice-0.4.1/doc/api/misc.rst new/sounddevice-0.4.5/doc/api/misc.rst --- old/sounddevice-0.4.1/doc/api/misc.rst 2020-07-25 21:44:37.000000000 +0200 +++ new/sounddevice-0.4.5/doc/api/misc.rst 2022-07-19 19:26:16.000000000 +0200 @@ -3,15 +3,17 @@ .. currentmodule:: sounddevice -.. autosummary:: - :nosignatures: +.. topic:: Overview - sleep - get_portaudio_version - CallbackFlags - CallbackStop - CallbackAbort - PortAudioError + .. autosummary:: + :nosignatures: + + sleep + get_portaudio_version + CallbackFlags + CallbackStop + CallbackAbort + PortAudioError .. autofunction:: sleep @@ -20,8 +22,8 @@ .. autoclass:: CallbackFlags :members: -.. autoclass:: CallbackStop +.. autoexception:: CallbackStop -.. autoclass:: CallbackAbort +.. autoexception:: CallbackAbort -.. autoclass:: PortAudioError +.. autoexception:: PortAudioError diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sounddevice-0.4.1/doc/api/module-defaults.rst new/sounddevice-0.4.5/doc/api/module-defaults.rst --- old/sounddevice-0.4.1/doc/api/module-defaults.rst 2020-07-15 17:23:35.000000000 +0200 +++ new/sounddevice-0.4.5/doc/api/module-defaults.rst 2022-07-19 19:26:16.000000000 +0200 @@ -3,10 +3,5 @@ .. currentmodule:: sounddevice -.. autosummary:: - :nosignatures: - - default - .. autoclass:: default :members: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sounddevice-0.4.1/doc/api/platform-specific-settings.rst new/sounddevice-0.4.5/doc/api/platform-specific-settings.rst --- old/sounddevice-0.4.1/doc/api/platform-specific-settings.rst 2020-07-15 17:23:35.000000000 +0200 +++ new/sounddevice-0.4.5/doc/api/platform-specific-settings.rst 2022-07-19 19:26:16.000000000 +0200 @@ -3,12 +3,14 @@ .. currentmodule:: sounddevice -.. autosummary:: - :nosignatures: +.. topic:: Overview - AsioSettings - CoreAudioSettings - WasapiSettings + .. autosummary:: + :nosignatures: + + AsioSettings + CoreAudioSettings + WasapiSettings .. autoclass:: AsioSettings diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sounddevice-0.4.1/doc/api/raw-streams.rst new/sounddevice-0.4.5/doc/api/raw-streams.rst --- old/sounddevice-0.4.1/doc/api/raw-streams.rst 2020-07-15 17:23:35.000000000 +0200 +++ new/sounddevice-0.4.5/doc/api/raw-streams.rst 2022-07-19 19:26:16.000000000 +0200 @@ -3,12 +3,14 @@ .. currentmodule:: sounddevice -.. autosummary:: - :nosignatures: +.. topic:: Overview - RawStream - RawInputStream - RawOutputStream + .. autosummary:: + :nosignatures: + + RawStream + RawInputStream + RawOutputStream .. autoclass:: RawStream :members: read, write diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sounddevice-0.4.1/doc/api/streams.rst new/sounddevice-0.4.5/doc/api/streams.rst --- old/sounddevice-0.4.1/doc/api/streams.rst 2020-07-15 17:23:35.000000000 +0200 +++ new/sounddevice-0.4.5/doc/api/streams.rst 2022-07-19 19:26:16.000000000 +0200 @@ -3,12 +3,14 @@ .. currentmodule:: sounddevice -.. autosummary:: - :nosignatures: +.. topic:: Overview - Stream - InputStream - OutputStream + .. autosummary:: + :nosignatures: + + Stream + InputStream + OutputStream .. autoclass:: Stream :members: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sounddevice-0.4.1/doc/conf.py new/sounddevice-0.4.5/doc/conf.py --- old/sounddevice-0.4.1/doc/conf.py 2020-08-04 20:57:56.000000000 +0200 +++ new/sounddevice-0.4.5/doc/conf.py 2021-07-18 12:17:30.000000000 +0200 @@ -16,6 +16,7 @@ 'sphinx.ext.intersphinx', 'sphinx.ext.viewcode', 'sphinx.ext.napoleon', # support for NumPy-style docstrings + 'sphinx_last_updated_by_git', ] autoclass_content = 'init' @@ -41,7 +42,6 @@ authors = 'Matthias Geier' project = 'python-sounddevice' -copyright = '2020, ' + authors try: release = check_output(['git', 'describe', '--tags', '--always']) @@ -59,14 +59,15 @@ nitpicky = True -html_theme = 'sphinx_rtd_theme' +html_theme = 'insipid' html_theme_options = { - 'collapse_navigation': False, } html_title = project + ', version ' + release +html_favicon = 'favicon.svg' html_domain_indices = False -html_show_sourcelink = True html_show_copyright = False +html_copy_source = False +html_permalinks_icon = '??' htmlhelp_basename = 'python-sounddevice' latex_elements = { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sounddevice-0.4.1/doc/examples.rst new/sounddevice-0.4.5/doc/examples.rst --- old/sounddevice-0.4.1/doc/examples.rst 2020-08-04 20:57:56.000000000 +0200 +++ new/sounddevice-0.4.5/doc/examples.rst 2021-07-18 12:15:42.000000000 +0200 @@ -14,6 +14,13 @@ .. literalinclude:: ../examples/play_long_file.py +Play a Very Long Sound File without Using NumPy +----------------------------------------------- + +:gh-example:`play_long_file_raw.py` + +.. literalinclude:: ../examples/play_long_file_raw.py + Play a Web Stream ----------------- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sounddevice-0.4.1/doc/fake__sounddevice.py new/sounddevice-0.4.5/doc/fake__sounddevice.py --- old/sounddevice-0.4.1/doc/fake__sounddevice.py 2020-07-15 17:23:35.000000000 +0200 +++ new/sounddevice-0.4.5/doc/fake__sounddevice.py 2022-04-11 23:18:43.000000000 +0200 @@ -17,7 +17,7 @@ ctypes.util.find_library = new_find_library -class ffi(object): +class ffi: NULL = NotImplemented I_AM_FAKE = True # This is used for the documentation of "default" @@ -29,7 +29,7 @@ ffi = ffi() -class FakeLibrary(object): +class FakeLibrary: # from portaudio.h: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sounddevice-0.4.1/doc/installation.rst new/sounddevice-0.4.5/doc/installation.rst --- old/sounddevice-0.4.1/doc/installation.rst 2020-09-23 12:45:04.000000000 +0200 +++ new/sounddevice-0.4.5/doc/installation.rst 2022-07-19 18:50:21.000000000 +0200 @@ -12,7 +12,7 @@ .. image:: https://anaconda.org/conda-forge/python-sounddevice/badges/version.svg :target: https://anaconda.org/conda-forge/python-sounddevice -If you are using the ``conda`` package manager (e.g. with Anaconda_ for +If you are using the ``conda`` package manager (e.g. with Anaconda_ or Miniconda_ for Linux/macOS/Windows), you can install the ``sounddevice`` module from the ``conda-forge`` channel:: @@ -45,11 +45,6 @@ If you want to try the latest development version, have a look at the section about :doc:`CONTRIBUTING`. -.. only:: html - - .. image:: https://badge.fury.io/py/sounddevice.svg - :target: https://pypi.org/project/sounddevice/ - To install the latest release from PyPI, use:: python3 -m pip install sounddevice @@ -86,6 +81,7 @@ .. _NumPy: https://numpy.org/ .. _Python: https://www.python.org/ .. _Anaconda: https://www.anaconda.com/products/individual#Downloads +.. _Miniconda: https://docs.conda.io/miniconda.html .. _WinPython: https://winpython.github.io/ .. _CFFI: https://cffi.readthedocs.io/ .. _PyPI: https://pypi.org/project/sounddevice/ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sounddevice-0.4.1/doc/requirements.txt new/sounddevice-0.4.5/doc/requirements.txt --- old/sounddevice-0.4.1/doc/requirements.txt 2020-07-15 17:23:35.000000000 +0200 +++ new/sounddevice-0.4.5/doc/requirements.txt 2022-08-21 17:06:06.000000000 +0200 @@ -1,4 +1,2 @@ -Jinja2 -Pygments -Sphinx -Sphinx-RTD-Theme +insipid-sphinx-theme +sphinx-last-updated-by-git diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sounddevice-0.4.1/examples/play_file.py new/sounddevice-0.4.5/examples/play_file.py --- old/sounddevice-0.4.1/examples/play_file.py 2020-07-15 17:23:35.000000000 +0200 +++ new/sounddevice-0.4.5/examples/play_file.py 2022-03-21 22:19:39.000000000 +0100 @@ -1,15 +1,28 @@ #!/usr/bin/env python3 """Load an audio file into memory and play its contents. -NumPy and the soundfile module (https://PySoundFile.readthedocs.io/) +NumPy and the soundfile module (https://python-soundfile.readthedocs.io/) must be installed for this to work. This example program loads the whole file into memory before starting playback. To play very long files, you should use play_long_file.py instead. +This example could simply be implemented like this:: + + import sounddevice as sd + import soundfile as sf + + data, fs = sf.read('my-file.wav') + sd.play(data, fs) + sd.wait() + +... but in this example we show a more low-level implementation +using a callback stream. + """ import argparse +import threading import sounddevice as sd import soundfile as sf @@ -43,13 +56,30 @@ help='output device (numeric ID or substring)') args = parser.parse_args(remaining) +event = threading.Event() + try: - data, fs = sf.read(args.filename, dtype='float32') - sd.play(data, fs, device=args.device) - status = sd.wait() + data, fs = sf.read(args.filename, always_2d=True) + + current_frame = 0 + + def callback(outdata, frames, time, status): + global current_frame + if status: + print(status) + chunksize = min(len(data) - current_frame, frames) + outdata[:chunksize] = data[current_frame:current_frame + chunksize] + if chunksize < frames: + outdata[chunksize:] = 0 + raise sd.CallbackStop() + current_frame += chunksize + + stream = sd.OutputStream( + samplerate=fs, device=args.device, channels=data.shape[1], + callback=callback, finished_callback=event.set) + with stream: + event.wait() # Wait until playback is finished except KeyboardInterrupt: parser.exit('\nInterrupted by user') except Exception as e: parser.exit(type(e).__name__ + ': ' + str(e)) -if status: - parser.exit('Error during playback: ' + str(status)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sounddevice-0.4.1/examples/play_long_file.py new/sounddevice-0.4.5/examples/play_long_file.py --- old/sounddevice-0.4.1/examples/play_long_file.py 2020-08-04 20:57:56.000000000 +0200 +++ new/sounddevice-0.4.5/examples/play_long_file.py 2021-07-18 12:15:42.000000000 +0200 @@ -1,16 +1,16 @@ #!/usr/bin/env python3 """Play an audio file using a limited amount of memory. -The soundfile module (https://PySoundFile.readthedocs.io/) must be -installed for this to work. NumPy is not needed. +The soundfile module (https://python-soundfile.readthedocs.io/) must be +installed for this to work. In contrast to play_file.py, which loads the whole file into memory before starting playback, this example program only holds a given number of audio blocks in memory and is therefore able to play files that are larger than the available RAM. -A similar example could of course be implemented using NumPy, -but this example shows what can be done when NumPy is not available. +This example is implemented using NumPy, see play_long_file_raw.py +for a version that doesn't need NumPy. """ import argparse @@ -77,7 +77,7 @@ raise sd.CallbackAbort from e if len(data) < len(outdata): outdata[:len(data)] = data - outdata[len(data):] = b'\x00' * (len(outdata) - len(data)) + outdata[len(data):].fill(0) raise sd.CallbackStop else: outdata[:] = data @@ -86,18 +86,18 @@ try: with sf.SoundFile(args.filename) as f: for _ in range(args.buffersize): - data = f.buffer_read(args.blocksize, dtype='float32') - if not data: + data = f.read(args.blocksize) + if not len(data): break q.put_nowait(data) # Pre-fill queue - stream = sd.RawOutputStream( + stream = sd.OutputStream( samplerate=f.samplerate, blocksize=args.blocksize, - device=args.device, channels=f.channels, dtype='float32', + device=args.device, channels=f.channels, callback=callback, finished_callback=event.set) with stream: timeout = args.blocksize * args.buffersize / f.samplerate - while data: - data = f.buffer_read(args.blocksize, dtype='float32') + while len(data): + data = f.read(args.blocksize) q.put(data, timeout=timeout) event.wait() # Wait until playback is finished except KeyboardInterrupt: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sounddevice-0.4.1/examples/play_long_file_raw.py new/sounddevice-0.4.5/examples/play_long_file_raw.py --- old/sounddevice-0.4.1/examples/play_long_file_raw.py 1970-01-01 01:00:00.000000000 +0100 +++ new/sounddevice-0.4.5/examples/play_long_file_raw.py 2021-07-18 12:15:42.000000000 +0200 @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 +"""Play an audio file using a limited amount of memory. + +This is the same as play_long_file.py, but implemented without using NumPy. + +""" +import argparse +import queue +import sys +import threading + +import sounddevice as sd +import soundfile as sf + + +def int_or_str(text): + """Helper function for argument parsing.""" + try: + return int(text) + except ValueError: + return text + + +parser = argparse.ArgumentParser(add_help=False) +parser.add_argument( + '-l', '--list-devices', action='store_true', + help='show list of audio devices and exit') +args, remaining = parser.parse_known_args() +if args.list_devices: + print(sd.query_devices()) + parser.exit(0) +parser = argparse.ArgumentParser( + description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter, + parents=[parser]) +parser.add_argument( + 'filename', metavar='FILENAME', + help='audio file to be played back') +parser.add_argument( + '-d', '--device', type=int_or_str, + help='output device (numeric ID or substring)') +parser.add_argument( + '-b', '--blocksize', type=int, default=2048, + help='block size (default: %(default)s)') +parser.add_argument( + '-q', '--buffersize', type=int, default=20, + help='number of blocks used for buffering (default: %(default)s)') +args = parser.parse_args(remaining) +if args.blocksize == 0: + parser.error('blocksize must not be zero') +if args.buffersize < 1: + parser.error('buffersize must be at least 1') + +q = queue.Queue(maxsize=args.buffersize) +event = threading.Event() + + +def callback(outdata, frames, time, status): + assert frames == args.blocksize + if status.output_underflow: + print('Output underflow: increase blocksize?', file=sys.stderr) + raise sd.CallbackAbort + assert not status + try: + data = q.get_nowait() + except queue.Empty as e: + print('Buffer is empty: increase buffersize?', file=sys.stderr) + raise sd.CallbackAbort from e + if len(data) < len(outdata): + outdata[:len(data)] = data + outdata[len(data):] = b'\x00' * (len(outdata) - len(data)) + raise sd.CallbackStop + else: + outdata[:] = data + + +try: + with sf.SoundFile(args.filename) as f: + for _ in range(args.buffersize): + data = f.buffer_read(args.blocksize, dtype='float32') + if not data: + break + q.put_nowait(data) # Pre-fill queue + stream = sd.RawOutputStream( + samplerate=f.samplerate, blocksize=args.blocksize, + device=args.device, channels=f.channels, dtype='float32', + callback=callback, finished_callback=event.set) + with stream: + timeout = args.blocksize * args.buffersize / f.samplerate + while data: + data = f.buffer_read(args.blocksize, dtype='float32') + q.put(data, timeout=timeout) + event.wait() # Wait until playback is finished +except KeyboardInterrupt: + parser.exit('\nInterrupted by user') +except queue.Full: + # A timeout occurred, i.e. there was an error in the callback + parser.exit(1) +except Exception as e: + parser.exit(type(e).__name__ + ': ' + str(e)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sounddevice-0.4.1/examples/plot_input.py new/sounddevice-0.4.5/examples/plot_input.py --- old/sounddevice-0.4.1/examples/plot_input.py 2020-07-15 17:23:35.000000000 +0200 +++ new/sounddevice-0.4.5/examples/plot_input.py 2022-04-11 23:18:43.000000000 +0200 @@ -100,7 +100,7 @@ fig, ax = plt.subplots() lines = ax.plot(plotdata) if len(args.channels) > 1: - ax.legend(['channel {}'.format(c) for c in args.channels], + ax.legend([f'channel {c}' for c in args.channels], loc='lower left', ncol=len(args.channels)) ax.axis((0, len(plotdata), -1, 1)) ax.set_yticks([0]) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sounddevice-0.4.1/examples/rec_gui.py new/sounddevice-0.4.5/examples/rec_gui.py --- old/sounddevice-0.4.1/examples/rec_gui.py 2020-07-15 17:23:35.000000000 +0200 +++ new/sounddevice-0.4.5/examples/rec_gui.py 2022-06-03 20:11:50.000000000 +0200 @@ -42,39 +42,36 @@ def body(self, master): ttk.Label(master, text='Select host API:').pack(anchor='w') - hostapi_list = ttk.Combobox(master, state='readonly', width=50) - hostapi_list.pack() - hostapi_list['values'] = [hostapi['name'] - for hostapi in sd.query_hostapis()] + self.hostapi_list = ttk.Combobox(master, state='readonly', width=50) + self.hostapi_list.pack() + self.hostapi_list['values'] = [ + hostapi['name'] for hostapi in sd.query_hostapis()] ttk.Label(master, text='Select sound device:').pack(anchor='w') - device_ids = [] - device_list = ttk.Combobox(master, state='readonly', width=50) - device_list.pack() - - def update_device_list(*args): - hostapi = sd.query_hostapis(hostapi_list.current()) - nonlocal device_ids - device_ids = [ - idx - for idx in hostapi['devices'] - if sd.query_devices(idx)['max_output_channels'] > 0] - device_list['values'] = [ - sd.query_devices(idx)['name'] for idx in device_ids] - default = hostapi['default_output_device'] - if default >= 0: - device_list.current(device_ids.index(default)) - device_list.event_generate('<<ComboboxSelected>>') - - def select_device(*args): - self.result = device_ids[device_list.current()] - - hostapi_list.bind('<<ComboboxSelected>>', update_device_list) - device_list.bind('<<ComboboxSelected>>', select_device) + self.device_ids = [] + self.device_list = ttk.Combobox(master, state='readonly', width=50) + self.device_list.pack() + self.hostapi_list.bind('<<ComboboxSelected>>', self.update_device_list) with contextlib.suppress(sd.PortAudioError): - hostapi_list.current(sd.default.hostapi) - hostapi_list.event_generate('<<ComboboxSelected>>') + self.hostapi_list.current(sd.default.hostapi) + self.hostapi_list.event_generate('<<ComboboxSelected>>') + + def update_device_list(self, *args): + hostapi = sd.query_hostapis(self.hostapi_list.current()) + self.device_ids = [ + idx + for idx in hostapi['devices'] + if sd.query_devices(idx)['max_input_channels'] > 0] + self.device_list['values'] = [ + sd.query_devices(idx)['name'] for idx in self.device_ids] + default = hostapi['default_input_device'] + if default >= 0: + self.device_list.current(self.device_ids.index(default)) + + def validate(self): + self.result = self.device_ids[self.device_list.current()] + return True class RecGui(tk.Tk): @@ -203,7 +200,8 @@ def on_settings(self, *args): w = SettingsWindow(self, 'Settings') - self.create_stream(device=w.result) + if w.result is not None: + self.create_stream(device=w.result) def init_buttons(self): self.rec_button['text'] = 'record' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sounddevice-0.4.1/examples/rec_unlimited.py new/sounddevice-0.4.5/examples/rec_unlimited.py --- old/sounddevice-0.4.1/examples/rec_unlimited.py 2020-07-15 17:23:35.000000000 +0200 +++ new/sounddevice-0.4.5/examples/rec_unlimited.py 2022-07-19 19:26:16.000000000 +0200 @@ -1,7 +1,8 @@ #!/usr/bin/env python3 """Create a recording with arbitrary duration. -The soundfile module (https://PySoundFile.readthedocs.io/) has to be installed! +The soundfile module (https://python-soundfile.readthedocs.io/) +has to be installed! """ import argparse diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sounddevice-0.4.1/examples/spectrogram.py new/sounddevice-0.4.5/examples/spectrogram.py --- old/sounddevice-0.4.1/examples/spectrogram.py 2020-07-15 17:23:35.000000000 +0200 +++ new/sounddevice-0.4.5/examples/spectrogram.py 2022-04-11 23:18:43.000000000 +0200 @@ -66,7 +66,7 @@ if char == '\t': bg, fg = fg, bg else: - gradient.append('\x1b[{};{}m{}'.format(fg, bg + 10, char)) + gradient.append(f'\x1b[{fg};{bg + 10}m{char}') try: samplerate = sd.query_devices(args.device, 'input')['default_samplerate'] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sounddevice-0.4.1/examples/wire.py new/sounddevice-0.4.5/examples/wire.py --- old/sounddevice-0.4.1/examples/wire.py 2020-07-15 17:23:35.000000000 +0200 +++ new/sounddevice-0.4.5/examples/wire.py 2022-07-19 19:26:16.000000000 +0200 @@ -1,7 +1,7 @@ #!/usr/bin/env python3 """Pass input directly to output. -https://app.assembla.com/spaces/portaudio/git/source/master/test/patest_wire.c +https://github.com/PortAudio/portaudio/blob/master/test/patest_wire.c """ import argparse diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sounddevice-0.4.1/setup.py new/sounddevice-0.4.5/setup.py --- old/sounddevice-0.4.1/setup.py 2020-07-15 17:23:35.000000000 +0200 +++ new/sounddevice-0.4.5/setup.py 2022-04-11 23:18:43.000000000 +0200 @@ -9,12 +9,9 @@ exec(line) break -PYTHON_INTERPRETERS = '.'.join([ - 'cp32', 'cp33', 'cp34', 'cp35', 'cp36', 'cp37', 'cp38', 'cp39', - 'pp32', 'pp33', 'pp34', 'pp35', 'pp36', 'pp37', -]) MACOSX_VERSIONS = '.'.join([ - 'macosx_10_6_x86_64', + 'macosx_10_6_x86_64', # for compatibility with pip < v21 + 'macosx_10_6_universal2', ]) # environment variables for cross-platform package creation @@ -48,7 +45,6 @@ """Create OS-dependent, but Python-independent wheels.""" def get_tag(self): - pythons = 'py3.' + PYTHON_INTERPRETERS if system == 'Darwin': oses = MACOSX_VERSIONS elif system == 'Windows': @@ -57,9 +53,8 @@ else: oses = 'win_amd64' else: - pythons = 'py3' oses = 'any' - return pythons, 'none', oses + return 'py3', 'none', oses cmdclass = {'bdist_wheel': bdist_wheel_half_pure} @@ -70,7 +65,7 @@ packages=packages, package_data=package_data, zip_safe=zip_safe, - python_requires='>=3', + python_requires='>=3.7', setup_requires=['CFFI>=1.0'], install_requires=['CFFI>=1.0'], extras_require={'NumPy': ['NumPy']}, @@ -82,6 +77,9 @@ license='MIT', keywords='sound audio PortAudio play record playrec'.split(), url='http://python-sounddevice.readthedocs.io/', + project_urls={ + 'Source': 'https://github.com/spatialaudio/python-sounddevice', + }, platforms='any', classifiers=[ 'License :: OSI Approved :: MIT License', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sounddevice-0.4.1/sounddevice.egg-info/PKG-INFO new/sounddevice-0.4.5/sounddevice.egg-info/PKG-INFO --- old/sounddevice-0.4.1/sounddevice.egg-info/PKG-INFO 2020-09-26 19:17:25.000000000 +0200 +++ new/sounddevice-0.4.5/sounddevice.egg-info/PKG-INFO 2022-08-21 17:36:27.000000000 +0200 @@ -1,32 +1,12 @@ Metadata-Version: 2.1 Name: sounddevice -Version: 0.4.1 +Version: 0.4.5 Summary: Play and Record Sound with Python Home-page: http://python-sounddevice.readthedocs.io/ Author: Matthias Geier Author-email: matthias.ge...@gmail.com License: MIT -Description: Play and Record Sound with Python - ================================= - - This Python_ module provides bindings for the PortAudio_ library and a few - convenience functions to play and record NumPy_ arrays containing audio signals. - - The ``sounddevice`` module is available for Linux, macOS and Windows. - - Documentation: - https://python-sounddevice.readthedocs.io/ - - Source code repository and issue tracker: - https://github.com/spatialaudio/python-sounddevice/ - - License: - MIT -- see the file ``LICENSE`` for details. - - .. _Python: https://www.python.org/ - .. _PortAudio: http://www.portaudio.com/ - .. _NumPy: https://numpy.org/ - +Project-URL: Source, https://github.com/spatialaudio/python-sounddevice Keywords: sound,audio,PortAudio,play,record,playrec Platform: any Classifier: License :: OSI Approved :: MIT License @@ -34,5 +14,27 @@ Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Topic :: Multimedia :: Sound/Audio -Requires-Python: >=3 +Requires-Python: >=3.7 Provides-Extra: NumPy +License-File: LICENSE + +Play and Record Sound with Python +================================= + +This Python_ module provides bindings for the PortAudio_ library and a few +convenience functions to play and record NumPy_ arrays containing audio signals. + +The ``sounddevice`` module is available for Linux, macOS and Windows. + +Documentation: + https://python-sounddevice.readthedocs.io/ + +Source code repository and issue tracker: + https://github.com/spatialaudio/python-sounddevice/ + +License: + MIT -- see the file ``LICENSE`` for details. + +.. _Python: https://www.python.org/ +.. _PortAudio: http://www.portaudio.com/ +.. _NumPy: https://numpy.org/ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sounddevice-0.4.1/sounddevice.egg-info/SOURCES.txt new/sounddevice-0.4.5/sounddevice.egg-info/SOURCES.txt --- old/sounddevice-0.4.1/sounddevice.egg-info/SOURCES.txt 2020-09-26 19:17:25.000000000 +0200 +++ new/sounddevice-0.4.5/sounddevice.egg-info/SOURCES.txt 2022-08-21 17:36:27.000000000 +0200 @@ -29,6 +29,7 @@ examples/asyncio_generators.py examples/play_file.py examples/play_long_file.py +examples/play_long_file_raw.py examples/play_sine.py examples/play_stream.py examples/plot_input.py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sounddevice-0.4.1/sounddevice.py new/sounddevice-0.4.5/sounddevice.py --- old/sounddevice-0.4.1/sounddevice.py 2020-09-26 19:15:40.000000000 +0200 +++ new/sounddevice-0.4.5/sounddevice.py 2022-08-21 17:21:44.000000000 +0200 @@ -1,4 +1,4 @@ -# Copyright (c) 2015-2020 Matthias Geier +# Copyright (c) 2015-2021 Matthias Geier # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -48,7 +48,7 @@ https://python-sounddevice.readthedocs.io/ """ -__version__ = '0.4.1' +__version__ = '0.4.5' import atexit as _atexit import os as _os @@ -113,7 +113,8 @@ * Start the stream. * If ``blocking=True`` was given, wait until playback is done. - If not, return immediately. + If not, return immediately + (to start waiting at a later point, `wait()` can be used). If you need more control (e.g. block-wise gapless playback, multiple overlapping playbacks, ...), you should explicitly create an @@ -195,7 +196,8 @@ * Start the stream. * If ``blocking=True`` was given, wait until recording is done. - If not, return immediately. + If not, return immediately + (to start waiting at a later point, `wait()` can be used). If you need more control (e.g. block-wise gapless recording, overlapping recordings, ...), you should explicitly create an @@ -292,7 +294,8 @@ * Start the stream. * If ``blocking=True`` was given, wait until playback/recording is - done. If not, return immediately. + done. If not, return immediately + (to start waiting at a later point, `wait()` can be used). If you need more control (e.g. block-wise gapless playback and recording, realtime processing, ...), @@ -476,6 +479,8 @@ ``'name'`` The name of the device. + ``'index'`` + The device index. ``'hostapi'`` The ID of the corresponding host API. Use `query_hostapis()` to get information about a host API. @@ -554,14 +559,14 @@ """ if kind not in ('input', 'output', None): - raise ValueError('Invalid kind: {!r}'.format(kind)) + raise ValueError(f'Invalid kind: {kind!r}') if device is None and kind is None: return DeviceList(query_devices(i) for i in range(_check(_lib.Pa_GetDeviceCount()))) device = _get_device_id(device, kind, raise_on_error=True) info = _lib.Pa_GetDeviceInfo(device) if not info: - raise PortAudioError('Error querying device {}'.format(device)) + raise PortAudioError(f'Error querying device {device}') assert info.structVersion == 2 name_bytes = _ffi.string(info.name) try: @@ -580,6 +585,7 @@ raise device_dict = { 'name': name, + 'index': device, 'hostapi': info.hostApi, 'max_input_channels': info.maxInputChannels, 'max_output_channels': info.maxOutputChannels, @@ -637,7 +643,7 @@ for i in range(_check(_lib.Pa_GetHostApiCount()))) info = _lib.Pa_GetHostApiInfo(index) if not info: - raise PortAudioError('Error querying host API {}'.format(index)) + raise PortAudioError(f'Error querying host API {index}') assert info.structVersion == 1 return { 'name': _ffi.string(info.name).decode(), @@ -713,7 +719,7 @@ return _lib.Pa_GetVersion(), _ffi.string(_lib.Pa_GetVersionText()).decode() -class _StreamBase(object): +class _StreamBase: """Direct or indirect base class for all stream classes.""" def __init__(self, kind, samplerate=None, blocksize=None, device=None, @@ -892,7 +898,7 @@ _check(_lib.Pa_OpenStream(self._ptr, iparameters, oparameters, samplerate, blocksize, stream_flags, callback_ptr, userdata), - 'Error opening {}'.format(self.__class__.__name__)) + f'Error opening {self.__class__.__name__}') # dereference PaStream** --> PaStream* self._ptr = self._ptr[0] @@ -1524,8 +1530,12 @@ data = np.asarray(data) _, dtype = _split(self._dtype) _, channels = _split(self._channels) - if data.ndim > 1 and data.shape[1] != channels: - raise ValueError('Number of channels must match') + if data.ndim < 2: + data = data.reshape(-1, 1) + elif data.ndim > 2: + raise ValueError('data must be one- or two-dimensional') + if data.shape[1] != channels: + raise ValueError('number of channels must match') if data.dtype != dtype: raise TypeError('dtype mismatch: {!r} vs {!r}'.format( data.dtype.name, dtype)) @@ -1605,6 +1615,10 @@ Device index(es) or query string(s) specifying the device(s) to be used. The default value(s) can be changed with `default.device`. + If a string is given, the device is selected which contains + all space-separated parts in the right order. Each device + string contains the name of the corresponding host API in + the end. The string comparison is case-insensitive. channels : int or pair of int, optional The number of channels of sound to be delivered to the stream callback or accessed by `read()` or `write()`. It @@ -1624,20 +1638,30 @@ The packed 24 bit format ``'int24'`` is only supported in the "raw" stream classes, see `RawStream`. The default value(s) can be changed with `default.dtype`. + If NumPy is available, the corresponding `numpy.dtype` + objects can be used as well. The floating point + representations ``'float32'`` and ``'float64'`` use ``+1.0`` + and ``-1.0`` as the maximum and minimum values, + respectively. ``'uint8'`` is an unsigned 8 bit format where + ``128`` is considered "ground". latency : float or {'low', 'high'} or pair thereof, optional The desired latency in seconds. The special values ``'low'`` and ``'high'`` (latter being the default) select - the default low and high latency, respectively (see - `query_devices()`). The default value(s) can be changed - with `default.latency`. - Where practical, implementations should configure their - latency based on this parameter, otherwise they may choose - the closest viable latency instead. Unless the suggested - latency is greater than the absolute upper limit for the - device, implementations should round the *latency* up to the - next practical value -- i.e. to provide an equal or higher - latency wherever possible. Actual latency values for an - open stream may be retrieved using the `latency` attribute. + the device's default low and high latency, respectively (see + `query_devices()`). ``'high'`` is typically more robust + (i.e. buffer under-/overflows are less likely), + but the latency may be too large for interactive applications. + + .. note:: Specifying the desired latency as ``'high'`` does + not *guarantee* a stable audio stream. For reference, by + default Audacity_ specifies a desired latency of ``0.1`` + seconds and typically achieves robust performance. + + .. _Audacity: https://www.audacityteam.org/ + + The default value(s) can be changed with `default.latency`. + Actual latency values for an open stream can be retrieved + using the `latency` attribute. extra_settings : settings object or pair thereof, optional This can be used for host-API-specific input/output settings. See `default.extra_settings`. @@ -1801,7 +1825,7 @@ digits = len(str(_lib.Pa_GetDeviceCount() - 1)) hostapi_names = [hostapi['name'] for hostapi in query_hostapis()] text = '\n'.join( - u'{mark} {idx:{dig}} {name}, {ha} ({ins} in, {outs} out)'.format( + '{mark} {idx:{dig}} {name}, {ha} ({ins} in, {outs} out)'.format( mark=(' ', '>', '<', '*')[(idx == idev) + 2 * (idx == odev)], idx=idx, dig=digits, @@ -1813,7 +1837,7 @@ return text -class CallbackFlags(object): +class CallbackFlags: """Flag bits for the *status* argument to a stream *callback*. If you experience under-/overflows, you can try to increase the @@ -1863,7 +1887,7 @@ flags = str(self) if not flags: flags = 'no flags set' - return '<sounddevice.CallbackFlags: {}>'.format(flags) + return f'<sounddevice.CallbackFlags: {flags}>' def __str__(self): return ', '.join(name.replace('_', ' ') for name in dir(self) @@ -1981,7 +2005,7 @@ self._flags &= ~flag -class _InputOutputPair(object): +class _InputOutputPair: """Parameter pairs for device, channels, dtype and latency.""" _indexmapping = {'input': 0, 'output': 1} @@ -2006,7 +2030,7 @@ return '[{0[0]!r}, {0[1]!r}]'.format(self) -class default(object): +class default: """Get/set defaults for the *sounddevice* module. The attributes `device`, `channels`, `dtype`, `latency` and @@ -2051,12 +2075,9 @@ device = None, None """Index or query string of default input/output device. - If not overwritten, this is queried from PortAudio. + See the *device* argument of `Stream`. - If a string is given, the device is selected which contains all - space-separated parts in the right order. Each device string - contains the name of the corresponding host API in the end. - The string comparison is case-insensitive. + If not overwritten, this is queried from PortAudio. See Also -------- @@ -2064,14 +2085,19 @@ """ channels = _default_channels = None, None - """Number of input/output channels. + """Default number of input/output channels. + + See the *channels* argument of `Stream`. - The maximum number of channels for a given device can be found out - with `query_devices()`. + See Also + -------- + :func:`query_devices` """ dtype = _default_dtype = 'float32', 'float32' - """Data type used for input/output samples. + """Default data type used for input/output samples. + + See the *dtype* argument of `Stream`. The types ``'float32'``, ``'int32'``, ``'int16'``, ``'int8'`` and ``'uint8'`` can be used for all streams and functions. @@ -2081,29 +2107,9 @@ `RawStream` support ``'int24'`` (packed 24 bit format, which is *not* supported in NumPy!). - If NumPy is available, the corresponding `numpy.dtype` objects can - be used as well. - - The floating point representations ``'float32'`` and ``'float64'`` - use +1.0 and -1.0 as the maximum and minimum values, respectively. - ``'uint8'`` is an unsigned 8 bit format where 128 is considered - "ground". - """ latency = _default_latency = 'high', 'high' - """Suggested input/output latency in seconds. - - The special values ``'low'`` and ``'high'`` can be used to select - the default low/high latency of the chosen device. - ``'high'`` is typically more robust (i.e. buffer under-/overflows - are less likely), but the latency may be too large for interactive - applications. - - See Also - -------- - :func:`query_devices` - - """ + """See the *latency* argument of `Stream`.""" extra_settings = _default_extra_settings = None, None """Host-API-specific input/output settings. @@ -2214,7 +2220,7 @@ def __str__(self): errormsg = self.args[0] if self.args else '' if len(self.args) > 1: - errormsg = '{} [PaErrorCode {}]'.format(errormsg, self.args[1]) + errormsg = f'{errormsg} [PaErrorCode {self.args[1]}]' if len(self.args) > 2: host_api, hosterror_code, hosterror_text = self.args[2] hostname = query_hostapis(host_api)['name'] @@ -2250,7 +2256,7 @@ """ -class AsioSettings(object): +class AsioSettings: def __init__(self, channel_selectors): """ASIO-specific input/output settings. @@ -2302,7 +2308,7 @@ channelSelectors=self._selectors)) -class CoreAudioSettings(object): +class CoreAudioSettings: def __init__(self, channel_map=None, change_device_parameters=False, fail_if_conversion_required=False, conversion_quality='max'): @@ -2394,7 +2400,7 @@ len(self._channel_map)) -class WasapiSettings(object): +class WasapiSettings: def __init__(self, exclusive=False): """WASAPI-specific input/output settings. @@ -2435,7 +2441,7 @@ )) -class _CallbackContext(object): +class _CallbackContext: """Helper class for re-use in play()/rec()/playrec() callbacks.""" blocksize = None @@ -2465,6 +2471,9 @@ data = np.asarray(data) if data.ndim < 2: data = data.reshape(-1, 1) + elif data.ndim > 2: + raise ValueError( + 'audio data to be played back must be one- or two-dimensional') frames, channels = data.shape dtype = _check_dtype(data.dtype) mapping_is_explicit = mapping is not None @@ -2722,7 +2731,7 @@ errormsg = _ffi.string(_lib.Pa_GetErrorText(err)).decode() if msg: - errormsg = '{}: {}'.format(msg, errormsg) + errormsg = f'{msg}: {errormsg}' if err == _lib.paUnanticipatedHostError: # (gh82) We grab the host error info here rather than inside @@ -2779,7 +2788,7 @@ pos += len(substring) else: matches.append((id, full_string)) - if device_string.lower() == query_string: + if query_string in [device_string.lower(), full_string.lower()]: exact_device_matches.append(id) if kind is None: @@ -2797,7 +2806,7 @@ if raise_on_error: raise ValueError('Multiple ' + kind + ' devices found for ' + repr(id_or_query_string) + ':\n' + - '\n'.join('[{}] {}'.format(id, name) + '\n'.join(f'[{id}] {name}' for id, name in matches)) else: return -1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sounddevice-0.4.1/sounddevice_build.py new/sounddevice-0.4.5/sounddevice_build.py --- old/sounddevice-0.4.1/sounddevice_build.py 2020-07-15 17:23:35.000000000 +0200 +++ new/sounddevice-0.4.5/sounddevice_build.py 2022-03-03 20:40:29.000000000 +0100 @@ -307,6 +307,8 @@ PaWasapiStreamCategory streamCategory; PaWasapiStreamOption streamOption; } PaWasapiStreamInfo; + +int PaWasapi_IsLoopback( PaDeviceIndex device ); """) ffibuilder.cdef("""