Hello community,

here is the log from the commit of package python-sounddevice for 
openSUSE:Factory checked in at 2020-07-30 10:02:01
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-sounddevice (Old)
 and      /work/SRC/openSUSE:Factory/.python-sounddevice.new.3592 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-sounddevice"

Thu Jul 30 10:02:01 2020 rev:9 rq:823365 version:0.4.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-sounddevice/python-sounddevice.changes    
2020-03-26 23:35:22.094820409 +0100
+++ 
/work/SRC/openSUSE:Factory/.python-sounddevice.new.3592/python-sounddevice.changes
  2020-07-30 10:03:40.871364510 +0200
@@ -1,0 +2,9 @@
+Wed Jul 29 07:27:48 UTC 2020 - Marketa Calabkova <[email protected]>
+
+- Update to 0.4.0
+  * Drop support for Python 2.x
+  * Fix memory issues in `play()`, `rec()` and `playrec()`
+  * Example application ``play_stream.py``
+  * Remove ``print_devices()``, `query_devices()` can be used instead
+
+-------------------------------------------------------------------

Old:
----
  sounddevice-0.3.15.tar.gz

New:
----
  sounddevice-0.4.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-sounddevice.spec ++++++
--- /var/tmp/diff_new_pack.s0AstM/_old  2020-07-30 10:03:42.227365646 +0200
+++ /var/tmp/diff_new_pack.s0AstM/_new  2020-07-30 10:03:42.227365646 +0200
@@ -17,8 +17,9 @@
 
 
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
+%define skip_python2 1
 Name:           python-sounddevice
-Version:        0.3.15
+Version:        0.4.0
 Release:        0
 Summary:        Module to play and record sound with Python
 License:        MIT

++++++ sounddevice-0.3.15.tar.gz -> sounddevice-0.4.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sounddevice-0.3.15/CONTRIBUTING.rst 
new/sounddevice-0.4.0/CONTRIBUTING.rst
--- old/sounddevice-0.3.15/CONTRIBUTING.rst     2019-09-20 09:17:44.000000000 
+0200
+++ new/sounddevice-0.4.0/CONTRIBUTING.rst      2020-07-15 17:23:35.000000000 
+0200
@@ -1,17 +1,98 @@
 Contributing
-------------
+============
 
 If you find bugs, errors, omissions or other things that need improvement,
 please create an issue or a pull request at
 https://github.com/spatialaudio/python-sounddevice/.
 Contributions are always welcome!
 
+
+Reporting Problems
+------------------
+
+When creating an issue at
+https://github.com/spatialaudio/python-sounddevice/issues,
+please make sure to provide as much useful information as possible.
+
+You can use Markdown formatting to show Python code, e.g. ::
+
+   I have created a script named `my_script.py`:
+   
+   ```python
+   import sounddevice as sd
+   
+   fs = 48000
+   duration = 1.5
+   
+   data = sd.rec(int(duration * fs), channels=99)
+   sd.wait()
+   print(data.shape)
+   ```
+
+Please provide minimal code
+(remove everything that's not necessary to show the problem),
+but make sure that the code example still has everything that's needed to run 
it,
+including all ``import`` statements.
+
+You should of course also show what happens when you run your code, e.g. ::
+
+   Running my script, I got this error:
+   
+   ```
+   $ python3 my_script.py 
+   Expression 'parameters->channelCount <= maxChans' failed in 
'src/hostapi/alsa/pa_linux_alsa.c', line: 1514
+   Expression 'ValidateParameters( inputParameters, hostApi, 
StreamDirection_In )' failed in 'src/hostapi/alsa/pa_linux_alsa.c', line: 2818
+   Traceback (most recent call last):
+     File "my_script.py", line 6, in <module>
+       data = sd.rec(int(duration * fs), channels=99)
+   ...
+   sounddevice.PortAudioError: Error opening InputStream: Invalid number of 
channels [PaErrorCode -9998]
+   ```
+
+Please remember to provide the full command invocation and the full output.
+You should only remove lines of output when you know they are irrelevant.
+
+You should also mention the operating system and host API you are using
+(e.g. "Linux/ALSA" or "macOS/Core Audio" or "Windows/WASAPI").
+
+If your problem is related to a certain hardware device,
+you should provide the list of devices as reported by ::
+
+   python3 -m sounddevice
+
+If your problem has to do with the version of the PortAudio library you are 
using,
+you should provide the output of this script::
+
+   import sounddevice as sd
+   print(sd._libname)
+   print(sd.get_portaudio_version())
+
+If you don't want to clutter the issue description with a huge load of 
gibberish,
+you can use the ``<details>`` HTML tag to show some content only on demand::
+
+   <details>
+   
+   ```
+   $ python3 -m sounddevice
+     0 Built-in Line Input, Core Audio (2 in, 0 out)
+   > 1 Built-in Digital Input, Core Audio (2 in, 0 out)
+   < 2 Built-in Output, Core Audio (0 in, 2 out)
+     3 Built-in Line Output, Core Audio (0 in, 2 out)
+     4 Built-in Digital Output, Core Audio (0 in, 2 out)
+   ```
+   
+   </details>
+
+
+Development Installation
+------------------------
+
 Instead of pip-installing the latest release from PyPI_, you should get the
-newest development version from Github_::
+newest development version (a.k.a. "master") from Github_::
 
    git clone --recursive https://github.com/spatialaudio/python-sounddevice.git
    cd python-sounddevice
-   python3 setup.py develop --user
+   python3 -m pip install -e . --user
 
 .. _PyPI: https://pypi.org/project/sounddevice/
 .. _Github: https://github.com/spatialaudio/python-sounddevice/
@@ -19,12 +100,6 @@
 This way, your installation always stays up-to-date, even if you pull new
 changes from the Github repository.
 
-If you prefer, you can also replace the last command with::
-
-   python3 -m pip install --user -e .
-
-... where ``-e`` stands for ``--editable``.
-
 Whenever the file ``sounddevice_build.py`` changes (either because you edited 
it
 or it was updated by pulling from Github or switching branches), you have to 
run
 the last command again.
@@ -35,13 +110,17 @@
 
    git submodule update --init
 
+
+Building the Documentation
+--------------------------
+
 If you make changes to the documentation, you can locally re-create the HTML
 pages using Sphinx_.
 You can install it and a few other necessary packages with::
 
    python3 -m pip install -r doc/requirements.txt --user
 
-To create the HTML pages, use::
+To (re-)build the HTML files, use::
 
    python3 setup.py build_sphinx
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sounddevice-0.3.15/NEWS.rst 
new/sounddevice-0.4.0/NEWS.rst
--- old/sounddevice-0.3.15/NEWS.rst     2020-03-18 10:11:15.000000000 +0100
+++ new/sounddevice-0.4.0/NEWS.rst      2020-07-18 15:53:31.000000000 +0200
@@ -1,3 +1,8 @@
+0.4.0 (2020-07-18):
+ * Drop support for Python 2.x
+ * Fix memory issues in `play()`, `rec()` and `playrec()`
+ * Example application ``play_stream.py``
+
 0.3.15 (2020-03-18):
  * This will be the last release supporting Python 2.x!
 
@@ -53,7 +58,7 @@
  * Send PortAudio output to ``/dev/null`` (on Linux and OSX)
 
 0.3.0 (2015-10-28):
- * Remove `print_devices()`, `query_devices()` can be used instead,
+ * Remove ``print_devices()``, `query_devices()` can be used instead,
    since it now returns a `DeviceList` object.
 
 0.2.2 (2015-10-21):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sounddevice-0.3.15/PKG-INFO 
new/sounddevice-0.4.0/PKG-INFO
--- old/sounddevice-0.3.15/PKG-INFO     2020-03-18 10:14:15.994104400 +0100
+++ new/sounddevice-0.4.0/PKG-INFO      2020-07-18 15:54:17.558579400 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: sounddevice
-Version: 0.3.15
+Version: 0.4.0
 Summary: Play and Record Sound with Python
 Home-page: http://python-sounddevice.readthedocs.io/
 Author: Matthias Geier
@@ -32,8 +32,7 @@
 Classifier: License :: OSI Approved :: MIT License
 Classifier: Operating System :: OS Independent
 Classifier: Programming Language :: Python
-Classifier: Programming Language :: Python :: 2
 Classifier: Programming Language :: Python :: 3
 Classifier: Topic :: Multimedia :: Sound/Audio
-Requires-Python: >=2.6
+Requires-Python: >=3
 Provides-Extra: NumPy
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sounddevice-0.3.15/doc/CONTRIBUTING.rst 
new/sounddevice-0.4.0/doc/CONTRIBUTING.rst
--- old/sounddevice-0.3.15/doc/CONTRIBUTING.rst 2019-09-20 09:17:44.000000000 
+0200
+++ new/sounddevice-0.4.0/doc/CONTRIBUTING.rst  2020-07-15 17:23:35.000000000 
+0200
@@ -1,17 +1,98 @@
 Contributing
-------------
+============
 
 If you find bugs, errors, omissions or other things that need improvement,
 please create an issue or a pull request at
 https://github.com/spatialaudio/python-sounddevice/.
 Contributions are always welcome!
 
+
+Reporting Problems
+------------------
+
+When creating an issue at
+https://github.com/spatialaudio/python-sounddevice/issues,
+please make sure to provide as much useful information as possible.
+
+You can use Markdown formatting to show Python code, e.g. ::
+
+   I have created a script named `my_script.py`:
+   
+   ```python
+   import sounddevice as sd
+   
+   fs = 48000
+   duration = 1.5
+   
+   data = sd.rec(int(duration * fs), channels=99)
+   sd.wait()
+   print(data.shape)
+   ```
+
+Please provide minimal code
+(remove everything that's not necessary to show the problem),
+but make sure that the code example still has everything that's needed to run 
it,
+including all ``import`` statements.
+
+You should of course also show what happens when you run your code, e.g. ::
+
+   Running my script, I got this error:
+   
+   ```
+   $ python3 my_script.py 
+   Expression 'parameters->channelCount <= maxChans' failed in 
'src/hostapi/alsa/pa_linux_alsa.c', line: 1514
+   Expression 'ValidateParameters( inputParameters, hostApi, 
StreamDirection_In )' failed in 'src/hostapi/alsa/pa_linux_alsa.c', line: 2818
+   Traceback (most recent call last):
+     File "my_script.py", line 6, in <module>
+       data = sd.rec(int(duration * fs), channels=99)
+   ...
+   sounddevice.PortAudioError: Error opening InputStream: Invalid number of 
channels [PaErrorCode -9998]
+   ```
+
+Please remember to provide the full command invocation and the full output.
+You should only remove lines of output when you know they are irrelevant.
+
+You should also mention the operating system and host API you are using
+(e.g. "Linux/ALSA" or "macOS/Core Audio" or "Windows/WASAPI").
+
+If your problem is related to a certain hardware device,
+you should provide the list of devices as reported by ::
+
+   python3 -m sounddevice
+
+If your problem has to do with the version of the PortAudio library you are 
using,
+you should provide the output of this script::
+
+   import sounddevice as sd
+   print(sd._libname)
+   print(sd.get_portaudio_version())
+
+If you don't want to clutter the issue description with a huge load of 
gibberish,
+you can use the ``<details>`` HTML tag to show some content only on demand::
+
+   <details>
+   
+   ```
+   $ python3 -m sounddevice
+     0 Built-in Line Input, Core Audio (2 in, 0 out)
+   > 1 Built-in Digital Input, Core Audio (2 in, 0 out)
+   < 2 Built-in Output, Core Audio (0 in, 2 out)
+     3 Built-in Line Output, Core Audio (0 in, 2 out)
+     4 Built-in Digital Output, Core Audio (0 in, 2 out)
+   ```
+   
+   </details>
+
+
+Development Installation
+------------------------
+
 Instead of pip-installing the latest release from PyPI_, you should get the
-newest development version from Github_::
+newest development version (a.k.a. "master") from Github_::
 
    git clone --recursive https://github.com/spatialaudio/python-sounddevice.git
    cd python-sounddevice
-   python3 setup.py develop --user
+   python3 -m pip install -e . --user
 
 .. _PyPI: https://pypi.org/project/sounddevice/
 .. _Github: https://github.com/spatialaudio/python-sounddevice/
@@ -19,12 +100,6 @@
 This way, your installation always stays up-to-date, even if you pull new
 changes from the Github repository.
 
-If you prefer, you can also replace the last command with::
-
-   python3 -m pip install --user -e .
-
-... where ``-e`` stands for ``--editable``.
-
 Whenever the file ``sounddevice_build.py`` changes (either because you edited 
it
 or it was updated by pulling from Github or switching branches), you have to 
run
 the last command again.
@@ -35,13 +110,17 @@
 
    git submodule update --init
 
+
+Building the Documentation
+--------------------------
+
 If you make changes to the documentation, you can locally re-create the HTML
 pages using Sphinx_.
 You can install it and a few other necessary packages with::
 
    python3 -m pip install -r doc/requirements.txt --user
 
-To create the HTML pages, use::
+To (re-)build the HTML files, use::
 
    python3 setup.py build_sphinx
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sounddevice-0.3.15/doc/api/expert-mode.rst 
new/sounddevice-0.4.0/doc/api/expert-mode.rst
--- old/sounddevice-0.3.15/doc/api/expert-mode.rst      1970-01-01 
01:00:00.000000000 +0100
+++ new/sounddevice-0.4.0/doc/api/expert-mode.rst       2020-07-15 
17:23:35.000000000 +0200
@@ -0,0 +1,20 @@
+Expert Mode
+===========
+
+.. currentmodule:: sounddevice
+
+.. autosummary::
+   :nosignatures:
+
+   _initialize
+   _terminate
+   _split
+   _StreamBase
+
+.. autofunction:: _initialize
+
+.. autofunction:: _terminate
+
+.. autofunction:: _split
+
+.. autoclass:: _StreamBase
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sounddevice-0.3.15/doc/api/index.rst 
new/sounddevice-0.4.0/doc/api/index.rst
--- old/sounddevice-0.3.15/doc/api/index.rst    2019-10-25 11:23:27.000000000 
+0200
+++ new/sounddevice-0.4.0/doc/api/index.rst     2020-07-15 17:23:35.000000000 
+0200
@@ -14,3 +14,4 @@
    streams
    raw-streams
    misc
+   expert-mode
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sounddevice-0.3.15/doc/conf.py 
new/sounddevice-0.4.0/doc/conf.py
--- old/sounddevice-0.3.15/doc/conf.py  2020-03-18 09:54:19.000000000 +0100
+++ new/sounddevice-0.4.0/doc/conf.py   2020-07-15 17:23:35.000000000 +0200
@@ -1,24 +1,8 @@
-# -*- coding: utf-8 -*-
-#
-# python-sounddevice documentation build configuration file, created by
-# sphinx-quickstart on Fri Jun  5 15:56:15 2015.
-#
-# 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.
-
+"""Configuration file for Sphinx."""
 import sys
 import os
 from subprocess import check_output
 
-# 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('..'))
 sys.path.insert(0, os.path.abspath('.'))
 
@@ -26,14 +10,6 @@
 import fake__sounddevice
 sys.modules['_sounddevice'] = sys.modules['fake__sounddevice']
 
-# -- General configuration ------------------------------------------------
-
-# If your documentation needs a minimal Sphinx version, state it here.
-needs_sphinx = '1.3'  # for sphinx.ext.napoleon
-
-# 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',
     'sphinx.ext.autosummary',
@@ -61,237 +37,59 @@
     'numpy': ('https://docs.scipy.org/doc/numpy/', None),
 }
 
-# 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.
 authors = 'Matthias Geier'
 project = 'python-sounddevice'
 copyright = '2020, ' + authors
 
-# 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.0.0'
-# The full version, including alpha/beta/rc tags.
 try:
     release = check_output(['git', 'describe', '--tags', '--always'])
     release = release.decode().strip()
 except Exception:
     release = '<unknown>'
 
-# 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'
 try:
     today = check_output(['git', 'show', '-s', '--format=%ad', '--date=short'])
     today = today.decode().strip()
 except Exception:
     today = '<unknown date>'
 
-# 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 = 'any'
 
-# 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 = []
-
-# If true, keep warnings as "system message" paragraphs in the built documents.
-#keep_warnings = False
+nitpicky = True
 
-
-# -- 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 = 'sphinx_rtd_theme'
-
-# 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 = {
     'collapse_navigation': False,
 }
-
-# Add any paths that contain custom themes here, relative to this directory.
-#html_theme_path = []
-
-# The name for this set of Sphinx documents.  If None, it defaults to
-# "<project> v<release> documentation".
 html_title = project + ', version ' + release
-
-# 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']
-
-# Add any extra paths that contain custom files (such as robots.txt or
-# .htaccess) here, relative to this directory. These files are copied
-# directly to the root of the documentation.
-#html_extra_path = []
-
-# 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 = False
-
-# If false, no index is generated.
-#html_use_index = True
-
-# If true, the index is split into individual pages for each letter.
-#html_split_index = False
-
-# If true, links to the reST sources are added to the pages.
 html_show_sourcelink = True
-
-# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
-#html_show_sphinx = True
-
-# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
-#html_show_copyright = True
-
-# If true, an OpenSearch description file will be output, and all pages will
-# contain a <link> tag referring to it.  The value of this option must be the
-# base URL from which the finished HTML is served.
-#html_use_opensearch = ''
-
-# This is the file name suffix for HTML files (e.g. ".xhtml").
-#html_file_suffix = None
-
-# Output file base name for HTML help builder.
+html_show_copyright = False
 htmlhelp_basename = 'python-sounddevice'
 
-
-# -- Options for LaTeX output ---------------------------------------------
-
 latex_elements = {
-# The paper size ('letterpaper' or 'a4paper').
 'papersize': 'a4paper',
-
-# The font size ('10pt', '11pt' or '12pt').
-#'pointsize': '10pt',
-
-# Additional stuff for the LaTeX preamble.
 #'preamble': '',
-
 'printindex': '',
 }
-
-# Grouping the document tree into LaTeX files. List of tuples
-# (source start file, target name, title,
-#  author, documentclass [howto, manual, or own class]).
 latex_documents = [('index', 'python-sounddevice.tex', project, authors, 
'howto')]
-
-# 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 = 'footnote'
-
-# Documents to append as an appendix to all manuals.
-#latex_appendices = []
-
-# If false, no module index is generated.
 latex_domain_indices = False
 
 
-# -- Options for manual page output ---------------------------------------
-
-# One entry per manual page. List of tuples
-# (source start file, name, description, authors, manual section).
-#man_pages = [('index', project, project, [authors], 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', project, project, authors, project,
-#     'Play and Record Sound with Python.',
-#     'Miscellaneous'),
-#]
-
-# Documents to append as an appendix to all manuals.
-#texinfo_appendices = []
-
-# If false, no module index is generated.
-#texinfo_domain_indices = True
+def gh_example_role(rolename, rawtext, text, lineno, inliner,
+                    options={}, content=()):
+    from docutils import nodes, utils
+    github_url = 'https://github.com/spatialaudio/python-sounddevice'
+    base_url = github_url + '/blob/' + release + '/examples/%s'
+    text = utils.unescape(text)
+    full_url = base_url % text
+    pnode = nodes.reference(internal=False, refuri=full_url)
+    pnode += nodes.literal(text, text, classes=['file'])
+    return [pnode], []
 
-# How to display URL addresses: 'footnote', 'no', or 'inline'.
-#texinfo_show_urls = 'footnote'
 
-# If true, do not generate a @detailmenu in the "Top" node's menu.
-#texinfo_no_detailmenu = False
+def setup(app):
+    app.add_role('gh-example', gh_example_role)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sounddevice-0.3.15/doc/examples.rst 
new/sounddevice-0.4.0/doc/examples.rst
--- old/sounddevice-0.3.15/doc/examples.rst     2019-09-20 09:17:44.000000000 
+0200
+++ new/sounddevice-0.4.0/doc/examples.rst      2020-07-15 17:23:35.000000000 
+0200
@@ -3,62 +3,69 @@
 Play a Sound File
 -----------------
 
-:download:`play_file.py <../examples/play_file.py>`
+:gh-example:`play_file.py`
 
 .. literalinclude:: ../examples/play_file.py
 
 Play a Very Long Sound File
 ---------------------------
 
-:download:`play_long_file.py <../examples/play_long_file.py>`
+:gh-example:`play_long_file.py`
 
 .. literalinclude:: ../examples/play_long_file.py
 
+Play a Web Stream
+-----------------
+
+:gh-example:`play_stream.py`
+
+.. literalinclude:: ../examples/play_stream.py
+
 Play a Sine Signal
 ------------------
 
-:download:`play_sine.py <../examples/play_sine.py>`
+:gh-example:`play_sine.py`
 
 .. literalinclude:: ../examples/play_sine.py
 
 Input to Output Pass-Through
 ----------------------------
 
-:download:`wire.py <../examples/wire.py>`
+:gh-example:`wire.py`
 
 .. literalinclude:: ../examples/wire.py
 
 Plot Microphone Signal(s) in Real-Time
 --------------------------------------
 
-:download:`plot_input.py <../examples/plot_input.py>`
+:gh-example:`plot_input.py`
 
 .. literalinclude:: ../examples/plot_input.py
 
 Real-Time Text-Mode Spectrogram
 -------------------------------
 
-:download:`spectrogram.py <../examples/spectrogram.py>`
+:gh-example:`spectrogram.py`
 
 .. literalinclude:: ../examples/spectrogram.py
 
 Recording with Arbitrary Duration
 ---------------------------------
 
-:download:`rec_unlimited.py <../examples/rec_unlimited.py>`
+:gh-example:`rec_unlimited.py`
 
 .. literalinclude:: ../examples/rec_unlimited.py
 
 Using a stream in an `asyncio` coroutine
 ----------------------------------------
 
-:download:`asyncio_coroutines.py <../examples/asyncio_coroutines.py>`
+:gh-example:`asyncio_coroutines.py`
 
 .. literalinclude:: ../examples/asyncio_coroutines.py
 
 Creating an `asyncio` generator for audio blocks
 ------------------------------------------------
 
-:download:`asyncio_generators.py <../examples/asyncio_generators.py>`
+:gh-example:`asyncio_generators.py`
 
 .. literalinclude:: ../examples/asyncio_generators.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sounddevice-0.3.15/doc/installation.rst 
new/sounddevice-0.4.0/doc/installation.rst
--- old/sounddevice-0.3.15/doc/installation.rst 2019-09-20 09:17:44.000000000 
+0200
+++ new/sounddevice-0.4.0/doc/installation.rst  2020-07-15 17:23:35.000000000 
+0200
@@ -85,7 +85,7 @@
 .. _PortAudio: http://www.portaudio.com/
 .. _NumPy: https://numpy.org/
 .. _Python: https://www.python.org/
-.. _Anaconda: https://www.anaconda.com/distribution/#download-section
+.. _Anaconda: https://www.anaconda.com/products/individual#Downloads
 .. _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.3.15/doc/version-history.rst 
new/sounddevice-0.4.0/doc/version-history.rst
--- old/sounddevice-0.3.15/doc/version-history.rst      2019-09-20 
09:17:44.000000000 +0200
+++ new/sounddevice-0.4.0/doc/version-history.rst       2020-07-15 
17:23:35.000000000 +0200
@@ -3,8 +3,4 @@
 
 .. currentmodule:: sounddevice
 
-.. default-role:: py:obj
-
 .. include:: ../NEWS.rst
-
-.. default-role::
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sounddevice-0.3.15/examples/play_stream.py 
new/sounddevice-0.4.0/examples/play_stream.py
--- old/sounddevice-0.3.15/examples/play_stream.py      1970-01-01 
01:00:00.000000000 +0100
+++ new/sounddevice-0.4.0/examples/play_stream.py       2020-07-15 
17:23:35.000000000 +0200
@@ -0,0 +1,125 @@
+#!/usr/bin/env python3
+"""Play a web stream.
+
+ffmpeg-python (https://github.com/kkroening/ffmpeg-python) has to be installed.
+
+If you don't know a stream URL, try http://icecast.spc.org:8000/longplayer
+(see https://longplayer.org/ for a description).
+
+"""
+import argparse
+import queue
+import sys
+
+import ffmpeg
+import sounddevice as sd
+
+
+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(
+    'url', metavar='URL',
+    help='stream URL')
+parser.add_argument(
+    '-d', '--device', type=int_or_str,
+    help='output device (numeric ID or substring)')
+parser.add_argument(
+    '-b', '--blocksize', type=int, default=1024,
+    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)
+
+print('Getting stream information ...')
+
+try:
+    info = ffmpeg.probe(args.url)
+except ffmpeg.Error as e:
+    sys.stderr.buffer.write(e.stderr)
+    parser.exit(e)
+
+streams = info.get('streams', [])
+if len(streams) != 1:
+    parser.exit('There must be exactly one stream available')
+
+stream = streams[0]
+
+if stream.get('codec_type') != 'audio':
+    parser.exit('The stream must be an audio stream')
+
+channels = stream['channels']
+samplerate = float(stream['sample_rate'])
+
+
+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:
+        print('Buffer is empty: increase buffersize?', file=sys.stderr)
+        raise sd.CallbackAbort
+    assert len(data) == len(outdata)
+    outdata[:] = data
+
+
+try:
+    print('Opening stream ...')
+    process = ffmpeg.input(
+        args.url
+    ).output(
+        'pipe:',
+        format='f32le',
+        acodec='pcm_f32le',
+        ac=channels,
+        ar=samplerate,
+        loglevel='quiet',
+    ).run_async(pipe_stdout=True)
+    stream = sd.RawOutputStream(
+        samplerate=samplerate, blocksize=args.blocksize,
+        device=args.device, channels=channels, dtype='float32',
+        callback=callback)
+    read_size = args.blocksize * channels * stream.samplesize
+    print('Buffering ...')
+    for _ in range(args.buffersize):
+        q.put_nowait(process.stdout.read(read_size))
+    print('Starting Playback ...')
+    with stream:
+        timeout = args.blocksize * args.buffersize / samplerate
+        while True:
+            q.put(process.stdout.read(read_size), timeout=timeout)
+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.3.15/setup.py 
new/sounddevice-0.4.0/setup.py
--- old/sounddevice-0.3.15/setup.py     2020-03-18 09:54:19.000000000 +0100
+++ new/sounddevice-0.4.0/setup.py      2020-07-15 17:23:35.000000000 +0200
@@ -10,9 +10,7 @@
         break
 
 PYTHON_INTERPRETERS = '.'.join([
-    'cp26', 'cp27',
     'cp32', 'cp33', 'cp34', 'cp35', 'cp36', 'cp37', 'cp38', 'cp39',
-    'pp27',
     'pp32', 'pp33', 'pp34', 'pp35', 'pp36', 'pp37',
 ])
 MACOSX_VERSIONS = '.'.join([
@@ -50,7 +48,7 @@
         """Create OS-dependent, but Python-independent wheels."""
 
         def get_tag(self):
-            pythons = 'py2.py3.' + PYTHON_INTERPRETERS
+            pythons = 'py3.' + PYTHON_INTERPRETERS
             if system == 'Darwin':
                 oses = MACOSX_VERSIONS
             elif system == 'Windows':
@@ -59,7 +57,7 @@
                 else:
                     oses = 'win_amd64'
             else:
-                pythons = 'py2.py3'
+                pythons = 'py3'
                 oses = 'any'
             return pythons, 'none', oses
 
@@ -72,7 +70,7 @@
     packages=packages,
     package_data=package_data,
     zip_safe=zip_safe,
-    python_requires='>=2.6',
+    python_requires='>=3',
     setup_requires=['CFFI>=1.0'],
     install_requires=['CFFI>=1.0'],
     extras_require={'NumPy': ['NumPy']},
@@ -89,7 +87,6 @@
         'License :: OSI Approved :: MIT License',
         'Operating System :: OS Independent',
         'Programming Language :: Python',
-        'Programming Language :: Python :: 2',
         'Programming Language :: Python :: 3',
         'Topic :: Multimedia :: Sound/Audio',
     ],
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sounddevice-0.3.15/sounddevice.egg-info/PKG-INFO 
new/sounddevice-0.4.0/sounddevice.egg-info/PKG-INFO
--- old/sounddevice-0.3.15/sounddevice.egg-info/PKG-INFO        2020-03-18 
10:14:15.000000000 +0100
+++ new/sounddevice-0.4.0/sounddevice.egg-info/PKG-INFO 2020-07-18 
15:54:17.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: sounddevice
-Version: 0.3.15
+Version: 0.4.0
 Summary: Play and Record Sound with Python
 Home-page: http://python-sounddevice.readthedocs.io/
 Author: Matthias Geier
@@ -32,8 +32,7 @@
 Classifier: License :: OSI Approved :: MIT License
 Classifier: Operating System :: OS Independent
 Classifier: Programming Language :: Python
-Classifier: Programming Language :: Python :: 2
 Classifier: Programming Language :: Python :: 3
 Classifier: Topic :: Multimedia :: Sound/Audio
-Requires-Python: >=2.6
+Requires-Python: >=3
 Provides-Extra: NumPy
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sounddevice-0.3.15/sounddevice.egg-info/SOURCES.txt 
new/sounddevice-0.4.0/sounddevice.egg-info/SOURCES.txt
--- old/sounddevice-0.3.15/sounddevice.egg-info/SOURCES.txt     2020-03-18 
10:14:15.000000000 +0100
+++ new/sounddevice-0.4.0/sounddevice.egg-info/SOURCES.txt      2020-07-18 
15:54:17.000000000 +0200
@@ -18,6 +18,7 @@
 doc/version-history.rst
 doc/api/checking-hardware.rst
 doc/api/convenience-functions.rst
+doc/api/expert-mode.rst
 doc/api/index.rst
 doc/api/misc.rst
 doc/api/module-defaults.rst
@@ -29,6 +30,7 @@
 examples/play_file.py
 examples/play_long_file.py
 examples/play_sine.py
+examples/play_stream.py
 examples/plot_input.py
 examples/priming_output.py
 examples/rec_gui.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sounddevice-0.3.15/sounddevice.py 
new/sounddevice-0.4.0/sounddevice.py
--- old/sounddevice-0.3.15/sounddevice.py       2020-03-18 10:10:01.000000000 
+0100
+++ new/sounddevice-0.4.0/sounddevice.py        2020-07-18 15:51:44.000000000 
+0200
@@ -48,7 +48,7 @@
     https://python-sounddevice.readthedocs.io/
 
 """
-__version__ = '0.3.15'
+__version__ = '0.4.0'
 
 import atexit as _atexit
 import os as _os
@@ -108,7 +108,7 @@
     scripts.  It cannot be used for multiple overlapping playbacks.
 
     This function does the following steps internally:
-    
+
     * Call `stop()` to terminate any currently running invocation
       of `play()`, `rec()` and `playrec()`.
 
@@ -190,7 +190,7 @@
     scripts.
 
     This function does the following steps internally:
-    
+
     * Call `stop()` to terminate any currently running invocation
       of `play()`, `rec()` and `playrec()`.
 
@@ -261,14 +261,14 @@
     argument or by assigning a value to `default.samplerate`),
     the default sampling rate of the sound device will be used
     (see `query_devices()`).
-    
+
     See Also
     --------
     play, playrec
 
     """
     ctx = _CallbackContext()
-    ctx.frames = ctx.check_out(out, frames, channels, dtype, mapping)
+    out, ctx.frames = ctx.check_out(out, frames, channels, dtype, mapping)
 
     def callback(indata, frames, time, status):
         assert len(indata) == frames
@@ -278,7 +278,7 @@
 
     ctx.start_stream(InputStream, samplerate, ctx.input_channels,
                      ctx.input_dtype, callback, blocking, **kwargs)
-    return ctx.out
+    return out
 
 
 def playrec(data, samplerate=None, channels=None, dtype=None,
@@ -287,7 +287,7 @@
     """Simultaneous playback and recording of NumPy arrays.
 
     This function does the following steps internally:
-    
+
     * Call `stop()` to terminate any currently running invocation
       of `play()`, `rec()` and `playrec()`.
 
@@ -355,8 +355,8 @@
     output_frames = ctx.check_data(data, output_mapping, kwargs.get('device'))
     if dtype is None:
         dtype = ctx.data.dtype  # ignore module defaults
-    input_frames = ctx.check_out(out, output_frames, channels, dtype,
-                                 input_mapping)
+    out, input_frames = ctx.check_out(out, output_frames, channels, dtype,
+                                      input_mapping)
     if input_frames != output_frames:
         raise ValueError('len(data) != len(out)')
     ctx.frames = input_frames
@@ -374,7 +374,7 @@
                      callback, blocking,
                      prime_output_buffers_using_stream_callback=False,
                      **kwargs)
-    return ctx.out
+    return out
 
 
 def wait(ignore_errors=True):
@@ -559,14 +559,14 @@
 
     """
     if kind not in ('input', 'output', None):
-        raise ValueError('Invalid kind: {0!r}'.format(kind))
+        raise ValueError('Invalid kind: {!r}'.format(kind))
     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 {0}'.format(device))
+        raise PortAudioError('Error querying device {}'.format(device))
     assert info.structVersion == 2
     name_bytes = _ffi.string(info.name)
     try:
@@ -596,7 +596,7 @@
     }
     if kind and device_dict['max_' + kind + '_channels'] < 1:
         raise ValueError(
-            'Not an {0} device: {1!r}'.format(kind, device_dict['name']))
+            'Not an {} device: {!r}'.format(kind, device_dict['name']))
     return device_dict
 
 
@@ -642,7 +642,7 @@
                      for i in range(_check(_lib.Pa_GetHostApiCount())))
     info = _lib.Pa_GetHostApiInfo(index)
     if not info:
-        raise PortAudioError('Error querying host API {0}'.format(index))
+        raise PortAudioError('Error querying host API {}'.format(index))
     assert info.structVersion == 1
     return {
         'name': _ffi.string(info.name).decode(),
@@ -727,6 +727,45 @@
                  dither_off=None, never_drop_input=None,
                  prime_output_buffers_using_stream_callback=None,
                  userdata=None, wrap_callback=None):
+        """Base class for PortAudio streams.
+
+        This class should only be used by library authors who want to
+        create their own custom stream classes.
+        Most users should use the derived classes
+        `Stream`, `InputStream`, `OutputStream`,
+        `RawStream`, `RawInputStream` and `RawOutputStream` instead.
+
+        This class has the same properties and methods as `Stream`,
+        except for `read_available`/:meth:`~Stream.read` and
+        `write_available`/:meth:`~Stream.write`.
+
+        It can be created with the same parameters as `Stream`,
+        except that there are three additional parameters
+        and the *callback* parameter also accepts a C function pointer.
+
+        Parameters
+        ----------
+        kind : {'input', 'output', 'duplex'}
+            The desired type of stream: for recording, playback or both.
+        callback : Python callable or CData function pointer, optional
+            If *wrap_callback* is ``None`` this can be a function pointer
+            provided by CFFI.
+            Otherwise, it has to be a Python callable.
+        wrap_callback : {'array', 'buffer'}, optional
+            If *callback* is a Python callable, this selects whether
+            the audio data is provided as NumPy array (like in `Stream`)
+            or as Python buffer object (like in `RawStream`).
+        userdata : CData void pointer
+            This is passed to the underlying C callback function
+            on each call and can only be accessed from a *callback*
+            provided as ``CData`` function pointer.
+
+        Examples
+        --------
+        A usage example of this class can be seen at
+        https://github.com/spatialaudio/python-rtmixer.
+
+        """
         assert kind in ('input', 'output', 'duplex')
         assert wrap_callback in ('array', 'buffer', None)
         if blocksize is None:
@@ -858,7 +897,7 @@
         _check(_lib.Pa_OpenStream(self._ptr, iparameters, oparameters,
                                   samplerate, blocksize, stream_flags,
                                   callback_ptr, userdata),
-               'Error opening {0}'.format(self.__class__.__name__))
+               'Error opening {}'.format(self.__class__.__name__))
 
         # dereference PaStream** --> PaStream*
         self._ptr = self._ptr[0]
@@ -1493,7 +1532,7 @@
         if data.ndim > 1 and data.shape[1] != channels:
             raise ValueError('Number of channels must match')
         if data.dtype != dtype:
-            raise TypeError('dtype mismatch: {0!r} vs {1!r}'.format(
+            raise TypeError('dtype mismatch: {!r} vs {!r}'.format(
                 data.dtype.name, dtype))
         if not data.flags.c_contiguous:
             raise TypeError('data must be C-contiguous')
@@ -1776,14 +1815,18 @@
                 ins=info['max_input_channels'],
                 outs=info['max_output_channels'])
             for idx, info in enumerate(self))
-        if _sys.version_info.major < 3:
-            return text.encode(_sys.stdout.encoding or 'utf-8', 'replace')
         return text
 
 
 class CallbackFlags(object):
     """Flag bits for the *status* argument to a stream *callback*.
 
+    If you experience under-/overflows, you can try to increase the
+    ``latency`` and/or ``blocksize`` settings.
+    You should also avoid anything that could block the callback
+    function for a long time, e.g. extensive computations, waiting for
+    another thread, reading/writing files, network connections, etc.
+
     See Also
     --------
     Stream
@@ -1812,7 +1855,7 @@
         flags = str(self)
         if not flags:
             flags = 'no flags set'
-        return '<sounddevice.CallbackFlags: {0}>'.format(flags)
+        return '<sounddevice.CallbackFlags: {}>'.format(flags)
 
     def __str__(self):
         return ', '.join(name.replace('_', ' ') for name in dir(self)
@@ -1821,8 +1864,6 @@
     def __bool__(self):
         return bool(self._flags)
 
-    __nonzero__ = __bool__  # For Python 2.x
-
     def __ior__(self, other):
         if not isinstance(other, CallbackFlags):
             return NotImplemented
@@ -1852,8 +1893,9 @@
         In a stream opened with ``blocksize=0``, indicates that data
         prior to the first sample of the input buffer was discarded due
         to an overflow, possibly because the stream callback is using
-        too much CPU time.  Otherwise indicates that data prior to one
-        or more samples in the input buffer was discarded.
+        too much CPU time.  In a stream opened with a non-zero
+        *blocksize*, it indicates that data prior to one or more samples
+        in the input buffer was discarded.
 
         This can happen in full-duplex and input-only streams (including
         `playrec()` and `rec()`).
@@ -2141,11 +2183,11 @@
     def __str__(self):
         errormsg = self.args[0] if self.args else ''
         if len(self.args) > 1:
-            errormsg = '{0} [PaErrorCode {1}]'.format(errormsg, self.args[1])
+            errormsg = '{} [PaErrorCode {}]'.format(errormsg, self.args[1])
         if len(self.args) > 2:
             host_api, hosterror_code, hosterror_text = self.args[2]
             hostname = query_hostapis(host_api)['name']
-            errormsg = "{0}: '{1}' [{2} error {3}]".format(
+            errormsg = "{}: '{}' [{} error {}]".format(
                 errormsg, hosterror_text, hostname, hosterror_code)
 
         return errormsg
@@ -2367,6 +2409,7 @@
 
     blocksize = None
     data = None
+    out = None
     frame = 0
     input_channels = output_channels = None
     input_dtype = output_dtype = None
@@ -2448,7 +2491,7 @@
         self.input_channels = channels
         self.input_dtype = dtype
         self.input_mapping = mapping
-        return frames
+        return out, frames
 
     def callback_enter(self, status, data):
         """Check status and blocksize."""
@@ -2486,6 +2529,12 @@
 
     def finished_callback(self):
         self.event.set()
+        # Drop temporary audio buffers to free memory
+        self.data = None
+        self.out = None
+        # Drop CFFI objects to avoid reference cycles
+        self.stream._callback = None
+        self.stream._finished_callback = None
 
     def start_stream(self, StreamClass, samplerate, channels, dtype, callback,
                      blocking, **kwargs):
@@ -2617,7 +2666,12 @@
 
 
 def _split(value):
-    """Split input/output value into two values."""
+    """Split input/output value into two values.
+
+    This can be useful for generic code that allows using the same value
+    for input and output but also a pair of two separate values.
+
+    """
     if isinstance(value, _basestring):
         # iterable, but not meant for splitting
         return value, value
@@ -2637,7 +2691,7 @@
 
     errormsg = _ffi.string(_lib.Pa_GetErrorText(err)).decode()
     if msg:
-        errormsg = '{0}: {1}'.format(msg, errormsg)
+        errormsg = '{}: {}'.format(msg, errormsg)
 
     if err == _lib.paUnanticipatedHostError:
         # (gh82) We grab the host error info here rather than inside
@@ -2669,7 +2723,7 @@
         if idev == odev:
             id_or_query_string = idev
         else:
-            raise ValueError('Input and output device are different: {0!r}'
+            raise ValueError('Input and output device are different: {!r}'
                              .format(id_or_query_string))
 
     if isinstance(id_or_query_string, int):
@@ -2712,7 +2766,7 @@
         if raise_on_error:
             raise ValueError('Multiple ' + kind + ' devices found for ' +
                              repr(id_or_query_string) + ':\n' +
-                             '\n'.join('[{0}] {1}'.format(id, name)
+                             '\n'.join('[{}] {}'.format(id, name)
                                        for id, name in matches))
         else:
             return -1
@@ -2722,9 +2776,12 @@
 def _initialize():
     """Initialize PortAudio.
 
-    This temporarily forwards messages from stderr to /dev/null
+    This temporarily forwards messages from stderr to ``/dev/null``
     (where supported).
 
+    In most cases, this doesn't have to be called explicitly, because it
+    is automatically called with the ``import sounddevice`` statement.
+
     """
     old_stderr = None
     try:
@@ -2752,7 +2809,11 @@
 
 
 def _terminate():
-    """Terminate PortAudio."""
+    """Terminate PortAudio.
+
+    In most cases, this doesn't have to be called explicitly.
+
+    """
     global _initialized
     _check(_lib.Pa_Terminate(), 'Error terminating PortAudio')
     _initialized -= 1


Reply via email to