forked from auracaster/pyalsaaudio
demo
This commit is contained in:
85
py/CHANGES
Normal file
85
py/CHANGES
Normal file
@@ -0,0 +1,85 @@
|
||||
Version 0.8.5:
|
||||
- Return an empty string/bytestring when 'read()' detects an
|
||||
overrun. Previously the returned data was undefined (contributed by @jcea)
|
||||
|
||||
- Unlimited setperiod buffer size when reading frames (contributed by @jcea)
|
||||
|
||||
Version 0.8.4:
|
||||
- Fix Python3 API usage broken in 0.8.3
|
||||
|
||||
Version 0.8.3:
|
||||
- Add DSD sample formats (contributed by @lintweaker)
|
||||
- Add Mixer.handleevents() to acknowledge events identified by select.poll (contributed by @PaulSD)
|
||||
- Add functions for listing cards and their names (contributed by @chrisdiamand)
|
||||
- Add a method for setting enums (contributed by @chrisdiamand)
|
||||
|
||||
Version 0.8.2:
|
||||
- fix #3 (we cannot get the revision from git for pip installs)
|
||||
|
||||
Version 0.8.1:
|
||||
- document changes (this file)
|
||||
|
||||
Version 0.8:
|
||||
- 'PCM()' has new 'device' and 'cardindex' keyword arguments.
|
||||
|
||||
The keyword 'device' allows to select virtual devices, 'cardindex' can be
|
||||
used to select hardware cards by index (as with 'mixers()' and 'Mixer()').
|
||||
|
||||
The 'card' keyword argument is still supported, but deprecated.
|
||||
|
||||
The reason for this change is that the 'card' keyword argument guessed
|
||||
a device name from the card name, but this only works sometimes, and breaks
|
||||
opening virtual devices.
|
||||
|
||||
- new function 'pcms()' to list available PCM devices.
|
||||
|
||||
- mixers() and Mixer() take an additional 'device' keyword argument.
|
||||
This allows to list or open virtual devices.
|
||||
|
||||
- The default behaviour of Mixer() without any arguments has changed.
|
||||
Now Mixer() will try to open the 'default' Mixer instead of the Mixer
|
||||
that is associated with card 0.
|
||||
|
||||
- fix a memory leak under Python 3.x
|
||||
|
||||
- some more memory leaks in error conditions fixed.
|
||||
|
||||
Version 0.7:
|
||||
- fixed several memory leaks (patch 3372909), contributed by Erik Kulyk)
|
||||
|
||||
|
||||
Version 0.6:
|
||||
- mostly reverted patch 2594366: alsapcm_setup did not do complete error
|
||||
checking for good reasons; some ALSA functions in alsapcm_setup may fail without
|
||||
rendering the device unusable
|
||||
|
||||
|
||||
Version 0.5:
|
||||
- applied patch 2777035: Fixed setrec method in alsaaudio.c
|
||||
This included a mixertest with more features
|
||||
- fixed/applied patch 2594366: alsapcm_setup does not do any error checking
|
||||
|
||||
|
||||
Version 0.4:
|
||||
- API changes: mixers() and Mixer() now take a card index instead of a
|
||||
card name as optional parameter.
|
||||
- Support for Python 3.0
|
||||
- Documentation converted to reStructuredText; use Sphinx instead of LaTeX.
|
||||
- added cards()
|
||||
- added PCM.close()
|
||||
- added Mixer.close()
|
||||
- added mixer.getenum()
|
||||
|
||||
|
||||
Version 0.3:
|
||||
- wrapped blocking calls with Py_BEGIN_ALLOW_THREADS/Py_END_ALLOW_THREADS
|
||||
- added pause
|
||||
|
||||
|
||||
Version 0.2:
|
||||
- Many bugfixes related to playback in particular
|
||||
- Module documentation in the doc subdirectory
|
||||
|
||||
|
||||
Version 0.1:
|
||||
- Initial version
|
||||
54
py/LICENSE
Normal file
54
py/LICENSE
Normal file
@@ -0,0 +1,54 @@
|
||||
PyAlsaAudio is released under the same conditions as Python itself.
|
||||
The original wording of this license can be found below.
|
||||
|
||||
|
||||
PSF LICENSE AGREEMENT FOR PYTHON 2.4
|
||||
------------------------------------
|
||||
|
||||
1. This LICENSE AGREEMENT is between the Python Software Foundation
|
||||
("PSF"), and the Individual or Organization ("Licensee") accessing and
|
||||
otherwise using Python 2.4 software in source or binary form and its
|
||||
associated documentation.
|
||||
|
||||
2. Subject to the terms and conditions of this License Agreement, PSF
|
||||
hereby grants Licensee a nonexclusive, royalty-free, world-wide
|
||||
license to reproduce, analyze, test, perform and/or display publicly,
|
||||
prepare derivative works, distribute, and otherwise use Python 2.4
|
||||
alone or in any derivative version, provided, however, that PSF's
|
||||
License Agreement and PSF's notice of copyright, i.e., "Copyright (c)
|
||||
2001, 2002, 2003, 2004 Python Software Foundation; All Rights Reserved"
|
||||
are retained in Python 2.4 alone or in any derivative version prepared
|
||||
by Licensee.
|
||||
|
||||
3. In the event Licensee prepares a derivative work that is based on
|
||||
or incorporates Python 2.4 or any part thereof, and wants to make
|
||||
the derivative work available to others as provided herein, then
|
||||
Licensee hereby agrees to include in any such work a brief summary of
|
||||
the changes made to Python 2.4.
|
||||
|
||||
4. PSF is making Python 2.4 available to Licensee on an "AS IS"
|
||||
basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
|
||||
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
|
||||
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
|
||||
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 2.4 WILL NOT
|
||||
INFRINGE ANY THIRD PARTY RIGHTS.
|
||||
|
||||
5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
|
||||
2.4 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
|
||||
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 2.4,
|
||||
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
|
||||
|
||||
6. This License Agreement will automatically terminate upon a material
|
||||
breach of its terms and conditions.
|
||||
|
||||
7. Nothing in this License Agreement shall be deemed to create any
|
||||
relationship of agency, partnership, or joint venture between PSF and
|
||||
Licensee. This License Agreement does not grant permission to use PSF
|
||||
trademarks or trade name in a trademark sense to endorse or promote
|
||||
products or services of Licensee, or any third party.
|
||||
|
||||
8. By copying, installing or otherwise using Python 2.4, Licensee
|
||||
agrees to be bound by the terms and conditions of this License
|
||||
Agreement.
|
||||
|
||||
|
||||
5
py/MANIFEST.in
Normal file
5
py/MANIFEST.in
Normal file
@@ -0,0 +1,5 @@
|
||||
include *.py
|
||||
include CHANGES
|
||||
include TODO
|
||||
include LICENSE
|
||||
recursive-include doc *.html *.gif *.png *.css *.py *.rst *.js *.json Makefile
|
||||
11
py/NOTES.md
Normal file
11
py/NOTES.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# Publishing the documentation
|
||||
|
||||
- Install Sphinx; `sudo pip install sphinx`
|
||||
- Clone gh-pages branch: `cd doc; git clone -b gh-pages git@github.com:larsimmisch/pyalsaaudio.git gh-pages`
|
||||
- `cd doc; make publish`
|
||||
|
||||
# Release procedure
|
||||
|
||||
- Update version number in setup.py
|
||||
- Create tag and push it, i.e. `git tag x.y.z; git push origin x.y.z`
|
||||
- `python setup.py sdist upload -r pypi`
|
||||
69
py/README.md
Normal file
69
py/README.md
Normal file
@@ -0,0 +1,69 @@
|
||||
# PyAlsaAudio
|
||||
|
||||
For documentation, see http://larsimmisch.github.io/pyalsaaudio/
|
||||
|
||||
> Author: Casper Wilstrup (cwi@aves.dk)
|
||||
> Maintainer: Lars Immisch (lars@ibp.de)
|
||||
|
||||
This package contains wrappers for accessing the
|
||||
[ALSA](http://www.alsa-project.org/) API from Python. It
|
||||
is currently fairly complete for PCM devices, and has some support for mixers.
|
||||
|
||||
If you find bugs in the wrappers please open an issue in the issue tracker.
|
||||
Please don't send bug reports regarding ALSA specifically. There are several
|
||||
bugs in the ALSA API, and those should be reported to the ALSA team - not
|
||||
me.
|
||||
|
||||
This software is licensed under the PSF license - the same one used
|
||||
by the majority of the python distribution. Basically you can use it
|
||||
for anything you wish (even commercial purposes). There is no warranty
|
||||
whatsoever.
|
||||
|
||||
|
||||
# Installation
|
||||
|
||||
## PyPI
|
||||
|
||||
To install pyalsaaudio via `pip` (or `easy_install`):
|
||||
|
||||
```
|
||||
$ pip install pyalsaaudio
|
||||
```
|
||||
|
||||
## Manual installation
|
||||
|
||||
*Note:* the wrappers need a kernel with ALSA support, and the
|
||||
ALSA library and headers. The installation of these varies from distribution
|
||||
to distribution.
|
||||
|
||||
On Debian or Ubuntu, make sure to install `libasound2-dev`. On Arch,
|
||||
install `alsa-lib`. When in doubt, search your distribution for a
|
||||
package that contains `libasound.so` and `asoundlib.h`.
|
||||
|
||||
First, get the sources and change to the source directory:
|
||||
```
|
||||
$ git clone https://github.com/larsimmisch/pyalsaaudio.git
|
||||
$ cd pyalsaaudio
|
||||
```
|
||||
|
||||
Then, build:
|
||||
```
|
||||
$ python setup.py build
|
||||
```
|
||||
|
||||
And install:
|
||||
```
|
||||
$ sudo python setup.py install
|
||||
```
|
||||
|
||||
# Using the API
|
||||
The API documentation is included in the doc subdirectory of the source
|
||||
distribution; it is also online on [http://larsimmisch.github.io/pyalsaaudio/](http://larsimmisch.github.io/pyalsaaudio/).
|
||||
|
||||
There are some example programs included with the source:
|
||||
|
||||
* [playwav.py](https://github.com/larsimmisch/pyalsaaudio/blob/master/playwav.py) plays back a wav file
|
||||
* [playbacktest.py](https://github.com/larsimmisch/pyalsaaudio/blob/master/playbacktest.py) plays back raw sound data read from stdin
|
||||
* [recordtest.py](https://github.com/larsimmisch/pyalsaaudio/blob/master/recordtest.py) captures sound from the microphone and writes
|
||||
it raw to stdout.
|
||||
* [mixertest.py](https://github.com/larsimmisch/pyalsaaudio/blob/master/mixertest.py) can be used to manipulate the mixers.
|
||||
3
py/TODO
Normal file
3
py/TODO
Normal file
@@ -0,0 +1,3 @@
|
||||
- Better example code (aplay,arecord,amixer workalike for example)
|
||||
- Implement MIDI/sequencer support.
|
||||
|
||||
2600
py/alsaaudio.c
Normal file
2600
py/alsaaudio.c
Normal file
File diff suppressed because it is too large
Load Diff
78
py/doc/Makefile
Normal file
78
py/doc/Makefile
Normal file
@@ -0,0 +1,78 @@
|
||||
# Makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
PAPER =
|
||||
|
||||
# Internal variables.
|
||||
PAPEROPT_a4 = -D latex_paper_size=a4
|
||||
PAPEROPT_letter = -D latex_paper_size=letter
|
||||
ALLSPHINXOPTS = -d doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
||||
|
||||
.PHONY: help clean html web pickle htmlhelp latex changes linkcheck install
|
||||
|
||||
all: html
|
||||
|
||||
help:
|
||||
@echo "Please use \`make <target>' where <target> is one of"
|
||||
@echo " html to make standalone HTML files"
|
||||
@echo " pickle to make pickle files (usable by e.g. sphinx-web)"
|
||||
@echo " htmlhelp to make HTML files and a HTML help project"
|
||||
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
|
||||
@echo " changes to make an overview over all changed/added/deprecated items"
|
||||
@echo " linkcheck to check all external links for integrity"
|
||||
|
||||
clean:
|
||||
-rm -rf html doctrees pickle htmlhelp latex changes linkcheck
|
||||
|
||||
html:
|
||||
mkdir -p html doctrees
|
||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) html
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in html."
|
||||
|
||||
pickle:
|
||||
mkdir -p pickle doctrees
|
||||
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) pickle
|
||||
@echo
|
||||
@echo "Build finished; now you can process the pickle files or run"
|
||||
@echo " sphinx-web pickle"
|
||||
@echo "to start the sphinx-web server."
|
||||
|
||||
web: pickle
|
||||
|
||||
htmlhelp:
|
||||
mkdir -p htmlhelp doctrees
|
||||
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) htmlhelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run HTML Help Workshop with the" \
|
||||
".hhp project file in htmlhelp."
|
||||
|
||||
latex:
|
||||
mkdir -p latex doctrees
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) latex
|
||||
@echo
|
||||
@echo "Build finished; the LaTeX files are in .build/latex."
|
||||
@echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
|
||||
"run these through (pdf)latex."
|
||||
|
||||
changes:
|
||||
mkdir -p changes doctrees
|
||||
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) changes
|
||||
@echo
|
||||
@echo "The overview file is in changes."
|
||||
|
||||
linkcheck:
|
||||
mkdir -p linkcheck doctrees
|
||||
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) linkcheck
|
||||
@echo
|
||||
@echo "Link check complete; look for any errors in the above output " \
|
||||
"or in linkcheck/output.txt."
|
||||
|
||||
gh-pages: html
|
||||
cd gh-pages; git pull --rebase; cd ..; cp -r ./html/* gh-pages
|
||||
|
||||
publish: gh-pages
|
||||
cd gh-pages; git commit -a; git push; cd ..
|
||||
17
py/doc/README.md
Normal file
17
py/doc/README.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# Publish the documentation
|
||||
|
||||
The documentation is published through the `gh-pages` branch.
|
||||
|
||||
To publish the documentation, you need to clone the `gh-pages` branch of this repository into
|
||||
`doc/gh-pages`. In `doc`, do:
|
||||
|
||||
git clone -b gh-pages git@github.com:larsimmisch/pyalsaaudio.git gh-pages
|
||||
|
||||
(This is a bit of a hack)
|
||||
|
||||
Once that is set up, you can publish new documentation using:
|
||||
|
||||
make publish
|
||||
|
||||
Be careful when new files are generated, however, you will have to add them
|
||||
manually to git.
|
||||
160
py/doc/conf.py
Normal file
160
py/doc/conf.py
Normal file
@@ -0,0 +1,160 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# alsaaudio documentation documentation build configuration file, created by
|
||||
# sphinx-quickstart on Thu Mar 30 23:52:21 2017.
|
||||
#
|
||||
# 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.
|
||||
|
||||
# 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.
|
||||
#
|
||||
# import os
|
||||
# import sys
|
||||
# sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
import sys
|
||||
sys.path.insert(0, '..')
|
||||
from setup import pyalsa_version
|
||||
|
||||
|
||||
# -- General configuration ------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#
|
||||
# needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = []
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix(es) of source filenames.
|
||||
# You can specify multiple suffix as a list of string:
|
||||
#
|
||||
# source_suffix = ['.rst', '.md']
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'alsaaudio documentation'
|
||||
copyright = u'2017, Lars Immisch & Casper Wilstrup'
|
||||
author = u'Lars Immisch & Casper Wilstrup'
|
||||
|
||||
# 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 = pyalsa_version
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = version
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#
|
||||
# This is also used if you do content translation via gettext catalogs.
|
||||
# Usually you set "language" from the command line for these cases.
|
||||
language = None
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
# This patterns also effect to html_static_path and html_extra_path
|
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# If true, `todo` and `todoList` produce output, else they produce nothing.
|
||||
todo_include_todos = False
|
||||
|
||||
|
||||
# -- 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 = 'alabaster'
|
||||
|
||||
# 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 = {}
|
||||
|
||||
# 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']
|
||||
|
||||
|
||||
# -- Options for HTMLHelp output ------------------------------------------
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'alsaaudiodocumentationdoc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output ---------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#
|
||||
# 'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#
|
||||
# 'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#
|
||||
# 'preamble': '',
|
||||
|
||||
# Latex figure (float) alignment
|
||||
#
|
||||
# 'figure_align': 'htbp',
|
||||
}
|
||||
|
||||
# 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 = [
|
||||
(master_doc, 'alsaaudiodocumentation.tex', u'alsaaudio documentation Documentation',
|
||||
u'Lars Immisch', 'manual'),
|
||||
]
|
||||
|
||||
|
||||
# -- Options for manual page output ---------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
(master_doc, 'alsaaudiodocumentation', u'alsaaudio documentation Documentation',
|
||||
[author], 1)
|
||||
]
|
||||
|
||||
|
||||
# -- 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 = [
|
||||
(master_doc, 'alsaaudiodocumentation', u'alsaaudio documentation Documentation',
|
||||
author, 'alsaaudiodocumentation', 'One line description of project.',
|
||||
'Miscellaneous'),
|
||||
]
|
||||
|
||||
|
||||
|
||||
38
py/doc/index.rst
Normal file
38
py/doc/index.rst
Normal file
@@ -0,0 +1,38 @@
|
||||
.. alsaaudio documentation documentation master file, created by
|
||||
sphinx-quickstart on Thu Mar 30 23:52:21 2017.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
alsaaudio documentation
|
||||
===================================================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Contents:
|
||||
|
||||
pyalsaaudio
|
||||
terminology
|
||||
libalsaaudio
|
||||
|
||||
Download
|
||||
========
|
||||
|
||||
* `Download from pypi <https://pypi.python.org/pypi/pyalsaaudio>`_
|
||||
|
||||
|
||||
Github
|
||||
======
|
||||
|
||||
* `Repository <https://github.com/larsimmisch/pyalsaaudio/>`_
|
||||
* `Bug tracker <https://github.com/larsimmisch/pyalsaaudio/issues>`_
|
||||
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
||||
|
||||
|
||||
|
||||
647
py/doc/libalsaaudio.rst
Normal file
647
py/doc/libalsaaudio.rst
Normal file
@@ -0,0 +1,647 @@
|
||||
****************
|
||||
:mod:`alsaaudio`
|
||||
****************
|
||||
|
||||
.. module:: alsaaudio
|
||||
:platform: Linux
|
||||
|
||||
|
||||
.. % \declaremodule{builtin}{alsaaudio} % standard library, in C
|
||||
.. % not standard, in C
|
||||
|
||||
.. moduleauthor:: Casper Wilstrup <cwi@aves.dk>
|
||||
.. moduleauthor:: Lars Immisch <lars@ibp.de>
|
||||
|
||||
.. % Author of the module code;
|
||||
|
||||
|
||||
|
||||
The :mod:`alsaaudio` module defines functions and classes for using ALSA.
|
||||
|
||||
.. % ---- 3.1. ----
|
||||
.. % For each function, use a ``funcdesc'' block. This has exactly two
|
||||
.. % parameters (each parameters is contained in a set of curly braces):
|
||||
.. % the first parameter is the function name (this automatically
|
||||
.. % generates an index entry); the second parameter is the function's
|
||||
.. % argument list. If there are no arguments, use an empty pair of
|
||||
.. % curly braces. If there is more than one argument, separate the
|
||||
.. % arguments with backslash-comma. Optional parts of the parameter
|
||||
.. % list are contained in \optional{...} (this generates a set of square
|
||||
.. % brackets around its parameter). Arguments are automatically set in
|
||||
.. % italics in the parameter list. Each argument should be mentioned at
|
||||
.. % least once in the description; each usage (even inside \code{...})
|
||||
.. % should be enclosed in \var{...}.
|
||||
|
||||
|
||||
.. function:: pcms([type=PCM_PLAYBACK])
|
||||
|
||||
List available PCM devices by name.
|
||||
|
||||
Arguments are:
|
||||
|
||||
* *type* - can be either :const:`PCM_CAPTURE` or :const:`PCM_PLAYBACK`
|
||||
(default).
|
||||
|
||||
**Note:**
|
||||
|
||||
For :const:`PCM_PLAYBACK`, the list of device names should be equivalent
|
||||
to the list of device names that ``aplay -L`` displays on the commandline::
|
||||
|
||||
$ aplay -L
|
||||
|
||||
For :const:`PCM_CAPTURE`, the list of device names should be equivalent
|
||||
to the list of device names that ``arecord -L`` displays on the
|
||||
commandline::
|
||||
|
||||
$ arecord -L
|
||||
|
||||
*New in 0.8*
|
||||
|
||||
.. function:: cards()
|
||||
|
||||
List the available ALSA cards by name. This function is only moderately
|
||||
useful. If you want to see a list of available PCM devices, use :func:`pcms`
|
||||
instead.
|
||||
|
||||
|
||||
.. function:: mixers(cardindex=-1, device='default')
|
||||
|
||||
List the available mixers. The arguments are:
|
||||
|
||||
* *cardindex* - the card index. If this argument is given, the device name
|
||||
is constructed as: 'hw:*cardindex*' and
|
||||
the `device` keyword argument is ignored. ``0`` is the first hardware sound
|
||||
card.
|
||||
|
||||
* *device* - the name of the device on which the mixer resides. The default
|
||||
is ``'default'``.
|
||||
|
||||
**Note:** For a list of available controls, you can also use ``amixer`` on
|
||||
the commandline::
|
||||
|
||||
$ amixer
|
||||
|
||||
To elaborate the example, calling :func:`mixers` with the argument
|
||||
``cardindex=0`` should give the same list of Mixer controls as::
|
||||
|
||||
$ amixer -c 0
|
||||
|
||||
And calling :func:`mixers` with the argument ``device='foo'`` should give
|
||||
the same list of Mixer controls as::
|
||||
|
||||
$ amixer -D foo
|
||||
|
||||
*Changed in 0.8*:
|
||||
|
||||
- The keyword argument `device` is new and can be used to
|
||||
select virtual devices. As a result, the default behaviour has subtly
|
||||
changed. Since 0.8, this functions returns the mixers for the default
|
||||
device, not the mixers for the first card.
|
||||
|
||||
|
||||
.. _pcm-objects:
|
||||
|
||||
PCM Objects
|
||||
-----------
|
||||
|
||||
PCM objects in :mod:`alsaaudio` can play or capture (record) PCM
|
||||
sound through speakers or a microphone. The PCM constructor takes the
|
||||
following arguments:
|
||||
|
||||
.. class:: PCM(type=PCM_PLAYBACK, mode=PCM_NORMAL, device='default', cardindex=-1)
|
||||
|
||||
This class is used to represent a PCM device (either for playback and
|
||||
recording). The arguments are:
|
||||
|
||||
* *type* - can be either :const:`PCM_CAPTURE` or :const:`PCM_PLAYBACK`
|
||||
(default).
|
||||
* *mode* - can be either :const:`PCM_NONBLOCK`, or :const:`PCM_NORMAL`
|
||||
(default).
|
||||
* *device* - the name of the PCM device that should be used (for example
|
||||
a value from the output of :func:`pcms`). The default value is
|
||||
``'default'``.
|
||||
* *cardindex* - the card index. If this argument is given, the device name
|
||||
is constructed as 'hw:*cardindex*' and
|
||||
the `device` keyword argument is ignored.
|
||||
``0`` is the first hardware sound card.
|
||||
|
||||
This will construct a PCM object with these default settings:
|
||||
|
||||
* Sample format: :const:`PCM_FORMAT_S16_LE`
|
||||
* Rate: 44100 Hz
|
||||
* Channels: 2
|
||||
* Period size: 32 frames
|
||||
|
||||
*Changed in 0.8:*
|
||||
|
||||
- The `card` keyword argument is still supported,
|
||||
but deprecated. Please use `device` instead.
|
||||
|
||||
- The keyword argument `cardindex` was added.
|
||||
|
||||
The `card` keyword is deprecated because it guesses the real ALSA
|
||||
name of the card. This was always fragile and broke some legitimate usecases.
|
||||
|
||||
|
||||
PCM objects have the following methods:
|
||||
|
||||
|
||||
.. method:: PCM.pcmtype()
|
||||
|
||||
Returns the type of PCM object. Either :const:`PCM_CAPTURE` or
|
||||
:const:`PCM_PLAYBACK`.
|
||||
|
||||
|
||||
.. method:: PCM.pcmmode()
|
||||
|
||||
Return the mode of the PCM object. One of :const:`PCM_NONBLOCK`,
|
||||
:const:`PCM_ASYNC`, or :const:`PCM_NORMAL`
|
||||
|
||||
|
||||
.. method:: PCM.cardname()
|
||||
|
||||
Return the name of the sound card used by this PCM object.
|
||||
|
||||
|
||||
.. method:: PCM.setchannels(nchannels)
|
||||
|
||||
Used to set the number of capture or playback channels. Common
|
||||
values are: ``1`` = mono, ``2`` = stereo, and ``6`` = full 6 channel audio.
|
||||
Few sound cards support more than 2 channels
|
||||
|
||||
|
||||
.. method:: PCM.setrate(rate)
|
||||
|
||||
Set the sample rate in Hz for the device. Typical values are ``8000``
|
||||
(mainly used for telephony), ``16000``, ``44100`` (CD quality),
|
||||
``48000`` and ``96000``.
|
||||
|
||||
|
||||
.. method:: PCM.setformat(format)
|
||||
|
||||
The sound *format* of the device. Sound format controls how the PCM device
|
||||
interpret data for playback, and how data is encoded in captures.
|
||||
|
||||
The following formats are provided by ALSA:
|
||||
|
||||
========================= ===============
|
||||
Format Description
|
||||
========================= ===============
|
||||
``PCM_FORMAT_S8`` Signed 8 bit samples for each channel
|
||||
``PCM_FORMAT_U8`` Signed 8 bit samples for each channel
|
||||
``PCM_FORMAT_S16_LE`` Signed 16 bit samples for each channel Little Endian byte order)
|
||||
``PCM_FORMAT_S16_BE`` Signed 16 bit samples for each channel (Big Endian byte order)
|
||||
``PCM_FORMAT_U16_LE`` Unsigned 16 bit samples for each channel (Little Endian byte order)
|
||||
``PCM_FORMAT_U16_BE`` Unsigned 16 bit samples for each channel (Big Endian byte order)
|
||||
``PCM_FORMAT_S24_LE`` Signed 24 bit samples for each channel (Little Endian byte order in 4 bytes)
|
||||
``PCM_FORMAT_S24_BE`` Signed 24 bit samples for each channel (Big Endian byte order in 4 bytes)
|
||||
``PCM_FORMAT_U24_LE`` Unsigned 24 bit samples for each channel (Little Endian byte order in 4 bytes)
|
||||
``PCM_FORMAT_U24_BE`` Unsigned 24 bit samples for each channel (Big Endian byte order in 4 bytes)
|
||||
``PCM_FORMAT_S32_LE`` Signed 32 bit samples for each channel (Little Endian byte order)
|
||||
``PCM_FORMAT_S32_BE`` Signed 32 bit samples for each channel (Big Endian byte order)
|
||||
``PCM_FORMAT_U32_LE`` Unsigned 32 bit samples for each channel (Little Endian byte order)
|
||||
``PCM_FORMAT_U32_BE`` Unsigned 32 bit samples for each channel (Big Endian byte order)
|
||||
``PCM_FORMAT_FLOAT_LE`` 32 bit samples encoded as float (Little Endian byte order)
|
||||
``PCM_FORMAT_FLOAT_BE`` 32 bit samples encoded as float (Big Endian byte order)
|
||||
``PCM_FORMAT_FLOAT64_LE`` 64 bit samples encoded as float (Little Endian byte order)
|
||||
``PCM_FORMAT_FLOAT64_BE`` 64 bit samples encoded as float (Big Endian byte order)
|
||||
``PCM_FORMAT_MU_LAW`` A logarithmic encoding (used by Sun .au files and telephony)
|
||||
``PCM_FORMAT_A_LAW`` Another logarithmic encoding
|
||||
``PCM_FORMAT_IMA_ADPCM`` A 4:1 compressed format defined by the Interactive Multimedia Association.
|
||||
``PCM_FORMAT_MPEG`` MPEG encoded audio?
|
||||
``PCM_FORMAT_GSM`` 9600 bits/s constant rate encoding for speech
|
||||
``PCM_FORMAT_S24_3LE`` Signed 24 bit samples for each channel (Little Endian byte order in 3 bytes)
|
||||
``PCM_FORMAT_S24_3BE`` Signed 24 bit samples for each channel (Big Endian byte order in 3 bytes)
|
||||
``PCM_FORMAT_U24_3LE`` Unsigned 24 bit samples for each channel (Little Endian byte order in 3 bytes)
|
||||
``PCM_FORMAT_U24_3BE`` Unsigned 24 bit samples for each channel (Big Endian byte order in 3 bytes)
|
||||
========================= ===============
|
||||
|
||||
|
||||
.. method:: PCM.setperiodsize(period)
|
||||
|
||||
Sets the actual period size in frames. Each write should consist of
|
||||
exactly this number of frames, and each read will return this
|
||||
number of frames (unless the device is in :const:`PCM_NONBLOCK` mode, in
|
||||
which case it may return nothing at all)
|
||||
|
||||
|
||||
.. method:: PCM.read()
|
||||
|
||||
In :const:`PCM_NORMAL` mode, this function blocks until a full period is
|
||||
available, and then returns a tuple (length,data) where *length* is
|
||||
the number of frames of captured data, and *data* is the captured
|
||||
sound frames as a string. The length of the returned data will be
|
||||
periodsize\*framesize bytes.
|
||||
|
||||
In :const:`PCM_NONBLOCK` mode, the call will not block, but will return
|
||||
``(0,'')`` if no new period has become available since the last
|
||||
call to read.
|
||||
|
||||
In case of an overrun, this function will return a negative size: :const:`-EPIPE`.
|
||||
This indicates that data was lost, even if the operation itself succeeded.
|
||||
Try using a larger periodsize.
|
||||
|
||||
.. method:: PCM.write(data)
|
||||
|
||||
Writes (plays) the sound in data. The length of data *must* be a
|
||||
multiple of the frame size, and *should* be exactly the size of a
|
||||
period. If less than 'period size' frames are provided, the actual
|
||||
playout will not happen until more data is written.
|
||||
|
||||
If the device is not in :const:`PCM_NONBLOCK` mode, this call will block if
|
||||
the kernel buffer is full, and until enough sound has been played
|
||||
to allow the sound data to be buffered. The call always returns the
|
||||
size of the data provided.
|
||||
|
||||
In :const:`PCM_NONBLOCK` mode, the call will return immediately, with a
|
||||
return value of zero, if the buffer is full. In this case, the data
|
||||
should be written at a later time.
|
||||
|
||||
|
||||
.. method:: PCM.pause([enable=True])
|
||||
|
||||
If *enable* is :const:`True`, playback or capture is paused.
|
||||
Otherwise, playback/capture is resumed.
|
||||
|
||||
|
||||
.. method:: PCM.polldescriptors()
|
||||
|
||||
Returns a tuple of *(file descriptor, eventmask)* that can be used to
|
||||
wait for changes on the mixer with *select.poll*.
|
||||
|
||||
The *eventmask* value is compatible with `poll.register`__ in the Python
|
||||
:const:`select` module.
|
||||
|
||||
__ poll_objects_
|
||||
|
||||
**A few hints on using PCM devices for playback**
|
||||
|
||||
The most common reason for problems with playback of PCM audio is that writes
|
||||
to PCM devices must *exactly* match the data rate of the device.
|
||||
|
||||
If too little data is written to the device, it will underrun, and
|
||||
ugly clicking sounds will occur. Conversely, of too much data is
|
||||
written to the device, the write function will either block
|
||||
(:const:`PCM_NORMAL` mode) or return zero (:const:`PCM_NONBLOCK` mode).
|
||||
|
||||
If your program does nothing but play sound, the best strategy is to put the
|
||||
device in :const:`PCM_NORMAL` mode, and just write as much data to the device as
|
||||
possible. This strategy can also be achieved by using a separate
|
||||
thread with the sole task of playing out sound.
|
||||
|
||||
In GUI programs, however, it may be a better strategy to setup the device,
|
||||
preload the buffer with a few periods by calling write a couple of times, and
|
||||
then use some timer method to write one period size of data to the device every
|
||||
period. The purpose of the preloading is to avoid underrun clicks if the used
|
||||
timer doesn't expire exactly on time.
|
||||
|
||||
Also note, that most timer APIs that you can find for Python will
|
||||
accummulate time delays: If you set the timer to expire after 1/10'th
|
||||
of a second, the actual timeout will happen slightly later, which will
|
||||
accumulate to quite a lot after a few seconds. Hint: use time.time()
|
||||
to check how much time has really passed, and add extra writes as nessecary.
|
||||
|
||||
|
||||
.. _mixer-objects:
|
||||
|
||||
Mixer Objects
|
||||
-------------
|
||||
|
||||
Mixer objects provides access to the ALSA mixer API.
|
||||
|
||||
|
||||
.. class:: Mixer(control='Master', id=0, cardindex=-1, device='default')
|
||||
|
||||
Arguments are:
|
||||
|
||||
* *control* - specifies which control to manipulate using this mixer
|
||||
object. The list of available controls can be found with the
|
||||
:mod:`alsaaudio`.\ :func:`mixers` function. The default value is
|
||||
``'Master'`` - other common controls may be ``'Master Mono'``, ``'PCM'``,
|
||||
``'Line'``, etc.
|
||||
|
||||
* *id* - the id of the mixer control. Default is ``0``.
|
||||
|
||||
* *cardindex* - specifies which card should be used. If this argument
|
||||
is given, the device name is constructed like this: 'hw:*cardindex*' and
|
||||
the `device` keyword argument is ignored. ``0`` is the
|
||||
first sound card.
|
||||
|
||||
* *device* - the name of the device on which the mixer resides. The default
|
||||
value is ``'default'``.
|
||||
|
||||
*Changed in 0.8*:
|
||||
|
||||
- The keyword argument `device` is new and can be used to select virtual
|
||||
devices.
|
||||
|
||||
Mixer objects have the following methods:
|
||||
|
||||
.. method:: Mixer.cardname()
|
||||
|
||||
Return the name of the sound card used by this Mixer object
|
||||
|
||||
|
||||
.. method:: Mixer.mixer()
|
||||
|
||||
Return the name of the specific mixer controlled by this object, For example
|
||||
``'Master'`` or ``'PCM'``
|
||||
|
||||
|
||||
.. method:: Mixer.mixerid()
|
||||
|
||||
Return the ID of the ALSA mixer controlled by this object.
|
||||
|
||||
|
||||
.. method:: Mixer.switchcap()
|
||||
|
||||
Returns a list of the switches which are defined by this specific mixer.
|
||||
Possible values in this list are:
|
||||
|
||||
====================== ================
|
||||
Switch Description
|
||||
====================== ================
|
||||
'Mute' This mixer can mute
|
||||
'Joined Mute' This mixer can mute all channels at the same time
|
||||
'Playback Mute' This mixer can mute the playback output
|
||||
'Joined Playback Mute' Mute playback for all channels at the same time}
|
||||
'Capture Mute' Mute sound capture
|
||||
'Joined Capture Mute' Mute sound capture for all channels at a time}
|
||||
'Capture Exclusive' Not quite sure what this is
|
||||
====================== ================
|
||||
|
||||
To manipulate these switches use the :meth:`setrec` or
|
||||
:meth:`setmute` methods
|
||||
|
||||
|
||||
.. method:: Mixer.volumecap()
|
||||
|
||||
Returns a list of the volume control capabilities of this
|
||||
mixer. Possible values in the list are:
|
||||
|
||||
======================== ================
|
||||
Capability Description
|
||||
======================== ================
|
||||
'Volume' This mixer can control volume
|
||||
'Joined Volume' This mixer can control volume for all channels at the same time
|
||||
'Playback Volume' This mixer can manipulate the playback output
|
||||
'Joined Playback Volume' Manipulate playback volumne for all channels at the same time
|
||||
'Capture Volume' Manipulate sound capture volume
|
||||
'Joined Capture Volume' Manipulate sound capture volume for all channels at a time
|
||||
======================== ================
|
||||
|
||||
.. method:: Mixer.getenum()
|
||||
|
||||
For enumerated controls, return the currently selected item and the list of
|
||||
items available.
|
||||
|
||||
Returns a tuple *(string, list of strings)*.
|
||||
|
||||
For example, my soundcard has a Mixer called *Mono Output Select*. Using
|
||||
*amixer*, I get::
|
||||
|
||||
$ amixer get "Mono Output Select"
|
||||
Simple mixer control 'Mono Output Select',0
|
||||
Capabilities: enum
|
||||
Items: 'Mix' 'Mic'
|
||||
Item0: 'Mix'
|
||||
|
||||
Using :mod:`alsaaudio`, one could do::
|
||||
|
||||
>>> import alsaaudio
|
||||
>>> m = alsaaudio.Mixer('Mono Output Select')
|
||||
>>> m.getenum()
|
||||
('Mix', ['Mix', 'Mic'])
|
||||
|
||||
This method will return an empty tuple if the mixer is not an enumerated
|
||||
control.
|
||||
|
||||
|
||||
.. method:: Mixer.getmute()
|
||||
|
||||
Return a list indicating the current mute setting for each
|
||||
channel. 0 means not muted, 1 means muted.
|
||||
|
||||
This method will fail if the mixer has no playback switch capabilities.
|
||||
|
||||
|
||||
.. method:: Mixer.getrange([direction])
|
||||
|
||||
Return the volume range of the ALSA mixer controlled by this object.
|
||||
|
||||
The optional *direction* argument can be either :const:`PCM_PLAYBACK` or
|
||||
:const:`PCM_CAPTURE`, which is relevant if the mixer can control both
|
||||
playback and capture volume. The default value is :const:`PCM_PLAYBACK`
|
||||
if the mixer has playback channels, otherwise it is :const:`PCM_CAPTURE`.
|
||||
|
||||
|
||||
.. method:: Mixer.getrec()
|
||||
|
||||
Return a list indicating the current record mute setting for each channel. 0
|
||||
means not recording, 1 means recording.
|
||||
|
||||
This method will fail if the mixer has no capture switch capabilities.
|
||||
|
||||
|
||||
.. method:: Mixer.getvolume([direction])
|
||||
|
||||
Returns a list with the current volume settings for each channel. The list
|
||||
elements are integer percentages.
|
||||
|
||||
The optional *direction* argument can be either :const:`PCM_PLAYBACK` or
|
||||
:const:`PCM_CAPTURE`, which is relevant if the mixer can control both
|
||||
playback and capture volume. The default value is :const:`PCM_PLAYBACK`
|
||||
if the mixer has playback channels, otherwise it is :const:`PCM_CAPTURE`.
|
||||
|
||||
|
||||
.. method:: Mixer.setvolume(volume, [channel], [direction])
|
||||
|
||||
Change the current volume settings for this mixer. The *volume* argument
|
||||
controls the new volume setting as an integer percentage.
|
||||
|
||||
If the optional argument *channel* is present, the volume is set
|
||||
only for this channel. This assumes that the mixer can control the
|
||||
volume for the channels independently.
|
||||
|
||||
The optional *direction* argument can be either :const:`PCM_PLAYBACK` or
|
||||
:const:`PCM_CAPTURE`, which is relevant if the mixer can control both
|
||||
playback and capture volume. The default value is :const:`PCM_PLAYBACK`
|
||||
if the mixer has playback channels, otherwise it is :const:`PCM_CAPTURE`.
|
||||
|
||||
.. method:: Mixer.setmute(mute, [channel])
|
||||
|
||||
Sets the mute flag to a new value. The *mute* argument is either 0 for not
|
||||
muted, or 1 for muted.
|
||||
|
||||
The optional *channel* argument controls which channel is
|
||||
muted. The default is to set the mute flag for all channels.
|
||||
|
||||
This method will fail if the mixer has no playback mute capabilities
|
||||
|
||||
|
||||
.. method:: Mixer.setrec(capture, [channel])
|
||||
|
||||
Sets the capture mute flag to a new value. The *capture* argument
|
||||
is either 0 for no capture, or 1 for capture.
|
||||
|
||||
The optional *channel* argument controls which channel is
|
||||
changed. The default is to set the capture flag for all channels.
|
||||
|
||||
This method will fail if the mixer has no capture switch capabilities.
|
||||
|
||||
.. method:: Mixer.polldescriptors()
|
||||
|
||||
Returns a tuple of *(file descriptor, eventmask)* that can be used to
|
||||
wait for changes on the mixer with *select.poll*.
|
||||
|
||||
The *eventmask* value is compatible with `poll.register`__ in the Python
|
||||
:const:`select` module.
|
||||
|
||||
__ poll_objects_
|
||||
|
||||
.. method:: Mixer.handleevents()
|
||||
|
||||
Acknowledge events on the *polldescriptors* file descriptors
|
||||
to prevent subsequent polls from returning the same events again.
|
||||
Returns the number of events that were acknowledged.
|
||||
|
||||
**A rant on the ALSA Mixer API**
|
||||
|
||||
The ALSA mixer API is extremely complicated - and hardly documented at all.
|
||||
:mod:`alsaaudio` implements a much simplified way to access this API. In
|
||||
designing the API I've had to make some choices which may limit what can and
|
||||
cannot be controlled through the API. However, if I had chosen to implement the
|
||||
full API, I would have reexposed the horrible complexity/documentation ratio of
|
||||
the underlying API. At least the :mod:`alsaaudio` API is easy to
|
||||
understand and use.
|
||||
|
||||
If my design choises prevents you from doing something that the underlying API
|
||||
would have allowed, please let me know, so I can incorporate these needs into
|
||||
future versions.
|
||||
|
||||
If the current state of affairs annoys you, the best you can do is to write a
|
||||
HOWTO on the API and make this available on the net. Until somebody does this,
|
||||
the availability of ALSA mixer capable devices will stay quite limited.
|
||||
|
||||
Unfortunately, I'm not able to create such a HOWTO myself, since I only
|
||||
understand half of the API, and that which I do understand has come from a
|
||||
painful trial and error process.
|
||||
|
||||
.. % ==== 4. ====
|
||||
|
||||
|
||||
.. _pcm-example:
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
The following example are provided:
|
||||
|
||||
* `playwav.py`
|
||||
* `recordtest.py`
|
||||
* `playbacktest.py`
|
||||
* `mixertest.py`
|
||||
|
||||
All examples (except `mixertest.py`) accept the commandline option
|
||||
*-c <cardname>*.
|
||||
|
||||
To determine a valid card name, use the commandline ALSA player::
|
||||
|
||||
$ aplay -L
|
||||
|
||||
or::
|
||||
|
||||
$ python
|
||||
|
||||
>>> import alsaaudio
|
||||
>>> alsaaudio.pcms()
|
||||
|
||||
mixertest.py accepts the commandline options *-d <device>* and
|
||||
*-c <cardindex>*.
|
||||
|
||||
playwav.py
|
||||
~~~~~~~~~~
|
||||
|
||||
**playwav.py** plays a wav file.
|
||||
|
||||
To test PCM playback (on your default soundcard), run::
|
||||
|
||||
$ python playwav.py <wav file>
|
||||
|
||||
recordtest.py and playbacktest.py
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
**recordtest.py** and **playbacktest.py** will record and play a raw
|
||||
sound file in CD quality.
|
||||
|
||||
To test PCM recordings (on your default soundcard), run::
|
||||
|
||||
$ python recordtest.py <filename>
|
||||
|
||||
Speak into the microphone, and interrupt the recording at any time
|
||||
with ``Ctl-C``.
|
||||
|
||||
Play back the recording with::
|
||||
|
||||
$ python playbacktest.py <filename>
|
||||
|
||||
mixertest.py
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Without arguments, **mixertest.py** will list all available *controls* on the
|
||||
default soundcard.
|
||||
|
||||
The output might look like this::
|
||||
|
||||
$ ./mixertest.py
|
||||
Available mixer controls:
|
||||
'Master'
|
||||
'Master Mono'
|
||||
'Headphone'
|
||||
'PCM'
|
||||
'Line'
|
||||
'Line In->Rear Out'
|
||||
'CD'
|
||||
'Mic'
|
||||
'PC Speaker'
|
||||
'Aux'
|
||||
'Mono Output Select'
|
||||
'Capture'
|
||||
'Mix'
|
||||
'Mix Mono'
|
||||
|
||||
With a single argument - the *control*, it will display the settings of
|
||||
that control; for example::
|
||||
|
||||
$ ./mixertest.py Master
|
||||
Mixer name: 'Master'
|
||||
Capabilities: Playback Volume Playback Mute
|
||||
Channel 0 volume: 61%
|
||||
Channel 1 volume: 61%
|
||||
|
||||
With two arguments, the *control* and a *parameter*, it will set the
|
||||
parameter on the mixer::
|
||||
|
||||
$ ./mixertest.py Master mute
|
||||
|
||||
This will mute the Master mixer.
|
||||
|
||||
Or::
|
||||
|
||||
$ ./mixertest.py Master 40
|
||||
|
||||
This sets the volume to 40% on all channels.
|
||||
|
||||
To select a different soundcard, use either the *device* or *cardindex*
|
||||
argument::
|
||||
|
||||
$ ./mixertest.py -c 0 Master
|
||||
Mixer name: 'Master'
|
||||
Capabilities: Playback Volume Playback Mute
|
||||
Channel 0 volume: 61%
|
||||
Channel 1 volume: 61%
|
||||
|
||||
.. rubric:: Footnotes
|
||||
|
||||
.. [#f1] ALSA also allows ``PCM_ASYNC``, but this is not supported yet.
|
||||
|
||||
.. _poll_objects: http://docs.python.org/library/select.html#poll-objects
|
||||
141
py/doc/pyalsaaudio.rst
Normal file
141
py/doc/pyalsaaudio.rst
Normal file
@@ -0,0 +1,141 @@
|
||||
************
|
||||
Introduction
|
||||
************
|
||||
|
||||
:Author: Casper Wilstrup <cwi@aves.dk>
|
||||
:Author: Lars Immisch <lars@ibp.de>
|
||||
|
||||
.. |release| replace:: version
|
||||
|
||||
.. % At minimum, give your name and an email address. You can include a
|
||||
.. % snail-mail address if you like.
|
||||
|
||||
.. % This makes the Abstract go on a separate page in the HTML version;
|
||||
.. % if a copyright notice is used, it should go immediately after this.
|
||||
.. %
|
||||
|
||||
|
||||
.. _front:
|
||||
|
||||
This software is licensed under the PSF license - the same one used by the
|
||||
majority of the python distribution. Basically you can use it for anything you
|
||||
wish (even commercial purposes). There is no warranty whatsoever.
|
||||
|
||||
.. % Copyright statement should go here, if needed.
|
||||
|
||||
.. % The abstract should be a paragraph or two long, and describe the
|
||||
.. % scope of the document.
|
||||
|
||||
|
||||
.. topic:: Abstract
|
||||
|
||||
This package contains wrappers for accessing the ALSA API from Python. It is
|
||||
currently fairly complete for PCM devices and Mixer access. MIDI sequencer
|
||||
support is low on our priority list, but volunteers are welcome.
|
||||
|
||||
If you find bugs in the wrappers please use thegithub issue tracker.
|
||||
Please don't send bug reports regarding ALSA specifically. There are several
|
||||
bugs in this API, and those should be reported to the ALSA team - not me.
|
||||
|
||||
|
||||
************
|
||||
What is ALSA
|
||||
************
|
||||
|
||||
The Advanced Linux Sound Architecture (ALSA) provides audio and MIDI
|
||||
functionality to the Linux operating system.
|
||||
|
||||
Logically ALSA consists of these components:
|
||||
|
||||
* A set of kernel drivers. --- These drivers are responsible for handling the
|
||||
physical sound hardware from within the Linux kernel, and have been the
|
||||
standard sound implementation in Linux since kernel version 2.5
|
||||
|
||||
* A kernel level API for manipulating the ALSA devices.
|
||||
|
||||
* A user-space C library for simplified access to the sound hardware from
|
||||
userspace applications. This library is called *libasound* and is required by
|
||||
all ALSA capable applications.
|
||||
|
||||
More information about ALSA may be found on the project homepage
|
||||
`<http://www.alsa-project.org>`_
|
||||
|
||||
|
||||
ALSA and Python
|
||||
===============
|
||||
|
||||
The older Linux sound API (OSS) which is now deprecated is well supported from
|
||||
the standard Python library, through the ossaudiodev module. No native ALSA
|
||||
support exists in the standard library.
|
||||
|
||||
There are a few other "ALSA for Python" projects available, including at least
|
||||
two different projects called pyAlsa. Neither of these seem to be under active
|
||||
development at the time - and neither are very feature complete.
|
||||
|
||||
I wrote PyAlsaAudio to fill this gap. My long term goal is to have the module
|
||||
included in the standard Python library, but that looks currently unlikely.
|
||||
|
||||
PyAlsaAudio has full support for sound capture, playback of sound, as well as
|
||||
the ALSA Mixer API.
|
||||
|
||||
MIDI support is not available, and since I don't own any MIDI hardware, it's
|
||||
difficult for me to implement it. Volunteers to work on this would be greatly
|
||||
appreciated.
|
||||
|
||||
|
||||
************
|
||||
Installation
|
||||
************
|
||||
|
||||
Note: the wrappers link with the alsasound library (from the alsa-lib package)
|
||||
and need the ALSA headers for compilation. Verify that you have
|
||||
/usr/lib/libasound.so and /usr/include/alsa (or similar paths) before building.
|
||||
|
||||
*On Debian (and probably Ubuntu), install libasound2-dev.*
|
||||
|
||||
Naturally you also need to use a kernel with proper ALSA support. This is the
|
||||
default in Linux kernel 2.6 and later. If you are using kernel version 2.4 you
|
||||
may need to install the ALSA patches yourself - although most distributions
|
||||
ship with ALSA kernels.
|
||||
|
||||
To install, execute the following: --- ::
|
||||
|
||||
$ python setup.py build
|
||||
|
||||
And then as root: --- ::
|
||||
|
||||
# python setup.py install
|
||||
|
||||
*******
|
||||
Testing
|
||||
*******
|
||||
|
||||
Make sure that :code:`aplay` plays a file through the soundcard you want, then
|
||||
try::
|
||||
|
||||
$ python playwav.py <filename.wav>
|
||||
|
||||
If :code:`aplay` needs a device argument, like
|
||||
:code:`aplay -D hw:CARD=sndrpihifiberry,DEV=0`, use::
|
||||
|
||||
$ python playwav.py -d hw:CARD=sndrpihifiberry,DEV=0 <filename.wav>
|
||||
|
||||
To test PCM recordings (on your default soundcard), verify your
|
||||
microphone works, then do::
|
||||
|
||||
$ python recordtest.py -d <device> <filename>
|
||||
|
||||
Speak into the microphone, and interrupt the recording at any time
|
||||
with ``Ctl-C``.
|
||||
|
||||
Play back the recording with::
|
||||
|
||||
$ python playbacktest.py-d <device> <filename>
|
||||
|
||||
There is a minimal test suite in :code:`test.py`, but it is
|
||||
a bit dependent on the ALSA configuration and may fail without indicating
|
||||
a real problem.
|
||||
|
||||
If you find bugs/problems, please file a `bug report
|
||||
<https://github.com/larsimmisch/pyalsaaudio/issues>`_.
|
||||
|
||||
75
py/doc/terminology.rst
Normal file
75
py/doc/terminology.rst
Normal file
@@ -0,0 +1,75 @@
|
||||
****************************
|
||||
PCM Terminology and Concepts
|
||||
****************************
|
||||
|
||||
In order to use PCM devices it is useful to be familiar with some concepts and
|
||||
terminology.
|
||||
|
||||
Sample
|
||||
PCM audio, whether it is input or output, consists of *samples*.
|
||||
A single sample represents the amplitude of one channel of sound
|
||||
at a certain point in time. A lot of individual samples are
|
||||
necessary to represent actual sound; for CD audio, 44100 samples
|
||||
are taken every second.
|
||||
|
||||
Samples can be of many different sizes, ranging from 8 bit to 64
|
||||
bit precision. The specific format of each sample can also vary -
|
||||
they can be big endian byte integers, little endian byte integers, or
|
||||
floating point numbers.
|
||||
|
||||
Musically, the sample size determines the dynamic range. The
|
||||
dynamic range is the difference between the quietest and the
|
||||
loudest signal that can be resproduced.
|
||||
|
||||
Frame
|
||||
A frame consists of exactly one sample per channel. If there is only one
|
||||
channel (Mono sound) a frame is simply a single sample. If the sound is
|
||||
stereo, each frame consists of two samples, etc.
|
||||
|
||||
Frame size
|
||||
This is the size in bytes of each frame. This can vary a lot: if each sample
|
||||
is 8 bits, and we're handling mono sound, the frame size is one byte.
|
||||
Similarly in 6 channel audio with 64 bit floating point samples, the frame
|
||||
size is 48 bytes
|
||||
|
||||
Rate
|
||||
PCM sound consists of a flow of sound frames. The sound rate controls how
|
||||
often the current frame is replaced. For example, a rate of 8000 Hz
|
||||
means that a new frame is played or captured 8000 times per second.
|
||||
|
||||
Data rate
|
||||
This is the number of bytes, which must be recorded or provided per
|
||||
second at a certain frame size and rate.
|
||||
|
||||
8000 Hz mono sound with 8 bit (1 byte) samples has a data rate of
|
||||
8000 \* 1 \* 1 = 8 kb/s or 64kbit/s. This is typically used for telephony.
|
||||
|
||||
At the other end of the scale, 96000 Hz, 6 channel sound with 64
|
||||
bit (8 bytes) samples has a data rate of 96000 \* 6 \* 8 = 4608
|
||||
kb/s (almost 5 MB sound data per second)
|
||||
|
||||
Period
|
||||
When the hardware processes data this is done in chunks of frames. The time
|
||||
interval between each processing (A/D or D/A conversion) is known
|
||||
as the period.
|
||||
The size of the period has direct implication on the latency of the
|
||||
sound input or output. For low-latency the period size should be
|
||||
very small, while low CPU resource usage would usually demand
|
||||
larger period sizes. With ALSA, the CPU utilization is not impacted
|
||||
much by the period size, since the kernel layer buffers multiple
|
||||
periods internally, so each period generates an interrupt and a
|
||||
memory copy, but userspace can be slower and read or write multiple
|
||||
periods at the same time.
|
||||
|
||||
Period size
|
||||
This is the size of each period in Hz. *Not bytes, but Hz!.* In
|
||||
:mod:`alsaaudio` the period size is set directly, and it is
|
||||
therefore important to understand the significance of this
|
||||
number. If the period size is configured to for example 32,
|
||||
each write should contain exactly 32 frames of sound data, and each
|
||||
read will return either 32 frames of data or nothing at all.
|
||||
|
||||
Once you understand these concepts, you will be ready to use the PCM API. Read
|
||||
on.
|
||||
|
||||
|
||||
97
py/isine.py
Normal file
97
py/isine.py
Normal file
@@ -0,0 +1,97 @@
|
||||
''' Use this example from an interactive python session, for example:
|
||||
|
||||
>>> from isine import change
|
||||
>>> change(880)
|
||||
'''
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import sys
|
||||
from threading import Thread
|
||||
from multiprocessing import Queue
|
||||
|
||||
if sys.version_info[0] < 3:
|
||||
from Queue import Empty
|
||||
else:
|
||||
from queue import Empty
|
||||
|
||||
from math import pi, sin
|
||||
import struct
|
||||
import alsaaudio
|
||||
|
||||
sampling_rate = 48000
|
||||
|
||||
format = alsaaudio.PCM_FORMAT_S16_LE
|
||||
framesize = 2 # bytes per frame for the values above
|
||||
channels = 2
|
||||
|
||||
def nearest_frequency(frequency):
|
||||
# calculate the nearest frequency where the wave form fits into the buffer
|
||||
# in other words, select f so that sampling_rate/f is an integer
|
||||
return float(sampling_rate)/int(sampling_rate/frequency)
|
||||
|
||||
def generate(frequency, duration = 0.125):
|
||||
# generate a buffer with a sine wave of `frequency`
|
||||
# that is approximately `duration` seconds long
|
||||
|
||||
# the buffersize we approximately want
|
||||
target_size = int(sampling_rate * channels * duration)
|
||||
|
||||
# the length of a full sine wave at the frequency
|
||||
cycle_size = int(sampling_rate / frequency)
|
||||
|
||||
# number of full cycles we can fit into target_size
|
||||
factor = int(target_size / cycle_size)
|
||||
|
||||
size = max(int(cycle_size * factor), 1)
|
||||
|
||||
sine = [ int(32767 * sin(2 * pi * frequency * i / sampling_rate)) \
|
||||
for i in range(size)]
|
||||
|
||||
return struct.pack('%dh' % size, *sine)
|
||||
|
||||
|
||||
class SinePlayer(Thread):
|
||||
|
||||
def __init__(self, frequency = 440.0):
|
||||
Thread.__init__(self)
|
||||
self.setDaemon(True)
|
||||
self.device = alsaaudio.PCM()
|
||||
self.device.setchannels(channels)
|
||||
self.device.setformat(format)
|
||||
self.device.setrate(sampling_rate)
|
||||
self.queue = Queue()
|
||||
self.change(frequency)
|
||||
|
||||
def change(self, frequency):
|
||||
'''This is called outside of the player thread'''
|
||||
# we generate the buffer in the calling thread for less
|
||||
# latency when switching frequencies
|
||||
|
||||
if frequency > sampling_rate / 2:
|
||||
raise ValueError('maximum frequency is %d' % (sampling_rate / 2))
|
||||
|
||||
f = nearest_frequency(frequency)
|
||||
print('nearest frequency: %f' % f)
|
||||
|
||||
buf = generate(f)
|
||||
self.queue.put(buf)
|
||||
|
||||
def run(self):
|
||||
buffer = None
|
||||
while True:
|
||||
try:
|
||||
buffer = self.queue.get(False)
|
||||
self.device.setperiodsize(int(len(buffer) / framesize))
|
||||
self.device.write(buffer)
|
||||
except Empty:
|
||||
if buffer:
|
||||
self.device.write(buffer)
|
||||
|
||||
|
||||
isine = SinePlayer()
|
||||
isine.start()
|
||||
|
||||
def change(f):
|
||||
isine.change(f)
|
||||
|
||||
129
py/mixertest.py
Executable file
129
py/mixertest.py
Executable file
@@ -0,0 +1,129 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
## mixertest.py
|
||||
##
|
||||
## This is an example of using the ALSA mixer API
|
||||
##
|
||||
## The script will set the volume or mute switch of the specified Mixer
|
||||
## depending on command line options.
|
||||
##
|
||||
## Examples:
|
||||
## python mixertest.py # list available mixers
|
||||
## python mixertest.py Master # show Master mixer settings
|
||||
## python mixertest.py Master 80 # set the master volume to 80%
|
||||
## python mixertest.py Master 1,90 # set channel 1 volume to 90%
|
||||
## python mixertest.py Master [un]mute # [un]mute the master mixer
|
||||
## python mixertest.py Capture [un]rec # [dis/en]able capture
|
||||
## python mixertest.py Master 0,[un]mute # [un]mute channel 0
|
||||
## python mixertest.py Capture 0,[un]rec # [dis/en]able capture on channel 0
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import sys
|
||||
import getopt
|
||||
import alsaaudio
|
||||
|
||||
def list_cards():
|
||||
print("Available sound cards:")
|
||||
for i in alsaaudio.card_indexes():
|
||||
(name, longname) = alsaaudio.card_name(i)
|
||||
print(" %d: %s (%s)" % (i, name, longname))
|
||||
|
||||
def list_mixers(kwargs):
|
||||
print("Available mixer controls:")
|
||||
for m in alsaaudio.mixers(**kwargs):
|
||||
print(" '%s'" % m)
|
||||
|
||||
def show_mixer(name, kwargs):
|
||||
# Demonstrates how mixer settings are queried.
|
||||
try:
|
||||
mixer = alsaaudio.Mixer(name, **kwargs)
|
||||
except alsaaudio.ALSAAudioError:
|
||||
print("No such mixer", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
print("Mixer name: '%s'" % mixer.mixer())
|
||||
print("Capabilities: %s %s" % (' '.join(mixer.volumecap()),
|
||||
' '.join(mixer.switchcap())))
|
||||
volumes = mixer.getvolume()
|
||||
for i in range(len(volumes)):
|
||||
print("Channel %i volume: %i%%" % (i,volumes[i]))
|
||||
|
||||
try:
|
||||
mutes = mixer.getmute()
|
||||
for i in range(len(mutes)):
|
||||
if mutes[i]:
|
||||
print("Channel %i is muted" % i)
|
||||
except alsaaudio.ALSAAudioError:
|
||||
# May not support muting
|
||||
pass
|
||||
|
||||
try:
|
||||
recs = mixer.getrec()
|
||||
for i in range(len(recs)):
|
||||
if recs[i]:
|
||||
print("Channel %i is recording" % i)
|
||||
except alsaaudio.ALSAAudioError:
|
||||
# May not support recording
|
||||
pass
|
||||
|
||||
def set_mixer(name, args, kwargs):
|
||||
# Demonstrates how to set mixer settings
|
||||
try:
|
||||
mixer = alsaaudio.Mixer(name, **kwargs)
|
||||
except alsaaudio.ALSAAudioError:
|
||||
print("No such mixer", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
if args.find(',') != -1:
|
||||
args_array = args.split(',')
|
||||
channel = int(args_array[0])
|
||||
args = ','.join(args_array[1:])
|
||||
else:
|
||||
channel = alsaaudio.MIXER_CHANNEL_ALL
|
||||
|
||||
if args in ['mute', 'unmute']:
|
||||
# Mute/unmute the mixer
|
||||
if args == 'mute':
|
||||
mixer.setmute(1, channel)
|
||||
else:
|
||||
mixer.setmute(0, channel)
|
||||
|
||||
elif args in ['rec','unrec']:
|
||||
# Enable/disable recording
|
||||
if args == 'rec':
|
||||
mixer.setrec(1, channel)
|
||||
else:
|
||||
mixer.setrec(0, channel)
|
||||
|
||||
else:
|
||||
# Set volume for specified channel. MIXER_CHANNEL_ALL means set
|
||||
# volume for all channels
|
||||
volume = int(args)
|
||||
mixer.setvolume(volume, channel)
|
||||
|
||||
def usage():
|
||||
print('usage: mixertest.py [-c <card>] [ <control>[,<value>]]',
|
||||
file=sys.stderr)
|
||||
sys.exit(2)
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
kwargs = {}
|
||||
opts, args = getopt.getopt(sys.argv[1:], 'c:d:?h')
|
||||
for o, a in opts:
|
||||
if o == '-c':
|
||||
kwargs = { 'cardindex': int(a) }
|
||||
elif o == '-d':
|
||||
kwargs = { 'device': a }
|
||||
else:
|
||||
usage()
|
||||
|
||||
list_cards()
|
||||
|
||||
if not len(args):
|
||||
list_mixers(kwargs)
|
||||
elif len(args) == 1:
|
||||
show_mixer(args[0], kwargs)
|
||||
else:
|
||||
set_mixer(args[0], args[1], kwargs)
|
||||
25
py/play_rusage.py
Normal file
25
py/play_rusage.py
Normal file
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Contributed by Stein Magnus Jodal
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import resource
|
||||
import time
|
||||
|
||||
import alsaaudio
|
||||
|
||||
seconds = 0
|
||||
max_rss = 0
|
||||
device = alsaaudio.PCM()
|
||||
|
||||
while True:
|
||||
device.write(b'\x00' * 44100)
|
||||
time.sleep(1)
|
||||
seconds += 1
|
||||
if seconds % 10 == 0:
|
||||
prev_rss = max_rss
|
||||
max_rss = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
|
||||
diff_rss = max_rss - prev_rss
|
||||
print('After %ds: max RSS %d kB, increased %d kB' % (
|
||||
seconds, max_rss, diff_rss))
|
||||
59
py/playbacktest.py
Executable file
59
py/playbacktest.py
Executable file
@@ -0,0 +1,59 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
## playbacktest.py
|
||||
##
|
||||
## This is an example of a simple sound playback script.
|
||||
##
|
||||
## The script opens an ALSA pcm for sound playback. Set
|
||||
## various attributes of the device. It then reads data
|
||||
## from stdin and writes it to the device.
|
||||
##
|
||||
## To test it out do the following:
|
||||
## python recordtest.py out.raw # talk to the microphone
|
||||
## python playbacktest.py out.raw
|
||||
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import sys
|
||||
import time
|
||||
import getopt
|
||||
import alsaaudio
|
||||
|
||||
def usage():
|
||||
print('usage: playbacktest.py [-d <device>] <file>', file=sys.stderr)
|
||||
sys.exit(2)
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
device = 'default'
|
||||
|
||||
opts, args = getopt.getopt(sys.argv[1:], 'd:')
|
||||
for o, a in opts:
|
||||
if o == '-d':
|
||||
device = a
|
||||
|
||||
if not args:
|
||||
usage()
|
||||
|
||||
f = open(args[0], 'rb')
|
||||
|
||||
# Open the device in playback mode.
|
||||
out = alsaaudio.PCM(alsaaudio.PCM_PLAYBACK, device=device)
|
||||
|
||||
# Set attributes: Mono, 44100 Hz, 16 bit little endian frames
|
||||
out.setchannels(1)
|
||||
out.setrate(44100)
|
||||
out.setformat(alsaaudio.PCM_FORMAT_S16_LE)
|
||||
|
||||
# The period size controls the internal number of frames per period.
|
||||
# The significance of this parameter is documented in the ALSA api.
|
||||
out.setperiodsize(160)
|
||||
|
||||
# Read data from stdin
|
||||
data = f.read(320)
|
||||
while data:
|
||||
out.write(data)
|
||||
data = f.read(320)
|
||||
|
||||
|
||||
65
py/playwav.py
Executable file
65
py/playwav.py
Executable file
@@ -0,0 +1,65 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Simple test script that plays (some) wav files
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import sys
|
||||
import wave
|
||||
import getopt
|
||||
import alsaaudio
|
||||
|
||||
def play(device, f):
|
||||
|
||||
print('%d channels, %d sampling rate\n' % (f.getnchannels(),
|
||||
f.getframerate()))
|
||||
# Set attributes
|
||||
device.setchannels(f.getnchannels())
|
||||
device.setrate(f.getframerate())
|
||||
|
||||
# 8bit is unsigned in wav files
|
||||
if f.getsampwidth() == 1:
|
||||
device.setformat(alsaaudio.PCM_FORMAT_U8)
|
||||
# Otherwise we assume signed data, little endian
|
||||
elif f.getsampwidth() == 2:
|
||||
device.setformat(alsaaudio.PCM_FORMAT_S16_LE)
|
||||
elif f.getsampwidth() == 3:
|
||||
device.setformat(alsaaudio.PCM_FORMAT_S24_3LE)
|
||||
elif f.getsampwidth() == 4:
|
||||
device.setformat(alsaaudio.PCM_FORMAT_S32_LE)
|
||||
else:
|
||||
raise ValueError('Unsupported format')
|
||||
|
||||
periodsize = f.getframerate() // 8
|
||||
|
||||
device.setperiodsize(periodsize)
|
||||
|
||||
data = f.readframes(periodsize)
|
||||
while data:
|
||||
# Read data from stdin
|
||||
device.write(data)
|
||||
data = f.readframes(periodsize)
|
||||
|
||||
|
||||
def usage():
|
||||
print('usage: playwav.py [-d <device>] <file>', file=sys.stderr)
|
||||
sys.exit(2)
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
device = 'default'
|
||||
|
||||
opts, args = getopt.getopt(sys.argv[1:], 'd:')
|
||||
for o, a in opts:
|
||||
if o == '-d':
|
||||
device = a
|
||||
|
||||
if not args:
|
||||
usage()
|
||||
|
||||
f = wave.open(args[0], 'rb')
|
||||
device = alsaaudio.PCM(device=device)
|
||||
|
||||
play(device, f)
|
||||
|
||||
f.close()
|
||||
69
py/recordtest.py
Executable file
69
py/recordtest.py
Executable file
@@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
## recordtest.py
|
||||
##
|
||||
## This is an example of a simple sound capture script.
|
||||
##
|
||||
## The script opens an ALSA pcm device for sound capture, sets
|
||||
## various attributes of the capture, and reads in a loop,
|
||||
## writing the data to standard out.
|
||||
##
|
||||
## To test it out do the following:
|
||||
## python recordtest.py out.raw # talk to the microphone
|
||||
## aplay -r 8000 -f S16_LE -c 1 out.raw
|
||||
|
||||
#!/usr/bin/env python
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import sys
|
||||
import time
|
||||
import getopt
|
||||
import alsaaudio
|
||||
|
||||
def usage():
|
||||
print('usage: recordtest.py [-d <device>] <file>', file=sys.stderr)
|
||||
sys.exit(2)
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
device = 'default'
|
||||
|
||||
opts, args = getopt.getopt(sys.argv[1:], 'd:')
|
||||
for o, a in opts:
|
||||
if o == '-d':
|
||||
device = a
|
||||
|
||||
if not args:
|
||||
usage()
|
||||
|
||||
f = open(args[0], 'wb')
|
||||
|
||||
# Open the device in nonblocking capture mode. The last argument could
|
||||
# just as well have been zero for blocking mode. Then we could have
|
||||
# left out the sleep call in the bottom of the loop
|
||||
inp = alsaaudio.PCM(alsaaudio.PCM_CAPTURE, alsaaudio.PCM_NONBLOCK, device=device)
|
||||
|
||||
# Set attributes: Mono, 44100 Hz, 16 bit little endian samples
|
||||
inp.setchannels(1)
|
||||
inp.setrate(44100)
|
||||
inp.setformat(alsaaudio.PCM_FORMAT_S16_LE)
|
||||
|
||||
# The period size controls the internal number of frames per period.
|
||||
# The significance of this parameter is documented in the ALSA api.
|
||||
# For our purposes, it is suficcient to know that reads from the device
|
||||
# will return this many frames. Each frame being 2 bytes long.
|
||||
# This means that the reads below will return either 320 bytes of data
|
||||
# or 0 bytes of data. The latter is possible because we are in nonblocking
|
||||
# mode.
|
||||
inp.setperiodsize(160)
|
||||
|
||||
loops = 1000000
|
||||
while loops > 0:
|
||||
loops -= 1
|
||||
# Read data from device
|
||||
l, data = inp.read()
|
||||
|
||||
if l:
|
||||
f.write(data)
|
||||
time.sleep(.001)
|
||||
40
py/setup.py
Executable file
40
py/setup.py
Executable file
@@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
'''This package contains wrappers for accessing the ALSA API from Python.
|
||||
It is fairly complete for PCM devices and Mixer access.
|
||||
'''
|
||||
|
||||
from setuptools import setup
|
||||
from setuptools.extension import Extension
|
||||
from sys import version
|
||||
|
||||
pyalsa_version = '0.8.5'
|
||||
|
||||
if __name__ == '__main__':
|
||||
setup(
|
||||
name = 'pyalsaaudio',
|
||||
version = pyalsa_version,
|
||||
description = 'ALSA bindings',
|
||||
long_description = __doc__,
|
||||
author = 'Casper Wilstrup',
|
||||
author_email='cwi@aves.dk',
|
||||
maintainer = 'Lars Immisch',
|
||||
maintainer_email = 'lars@ibp.de',
|
||||
license='PSF',
|
||||
platforms=['posix'],
|
||||
url='http://larsimmisch.github.io/pyalsaaudio/',
|
||||
classifiers = [
|
||||
'Development Status :: 4 - Beta',
|
||||
'Intended Audience :: Developers',
|
||||
'License :: OSI Approved :: Python Software Foundation License',
|
||||
'Operating System :: POSIX :: Linux',
|
||||
'Programming Language :: Python :: 2',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Topic :: Multimedia :: Sound/Audio',
|
||||
'Topic :: Multimedia :: Sound/Audio :: Mixers',
|
||||
'Topic :: Multimedia :: Sound/Audio :: Players',
|
||||
'Topic :: Multimedia :: Sound/Audio :: Capture/Recording',
|
||||
],
|
||||
ext_modules=[Extension('alsaaudio',['alsaaudio.c'],
|
||||
libraries=['asound'])]
|
||||
)
|
||||
136
py/test.py
Executable file
136
py/test.py
Executable file
@@ -0,0 +1,136 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# These are internal tests. They shouldn't fail, but they don't cover all
|
||||
# of the ALSA API. Most importantly PCM.read and PCM.write are missing.
|
||||
# These need to be tested by playbacktest.py and recordtest.py
|
||||
|
||||
# In case of a problem, run these tests. If they fail, file a bug report on
|
||||
# http://github.com/larsimmisch/pyalsaaudio/issues
|
||||
|
||||
import unittest
|
||||
import alsaaudio
|
||||
import warnings
|
||||
|
||||
# we can't test read and write well - these are tested otherwise
|
||||
PCMMethods = [('pcmtype', None),
|
||||
('pcmmode', None),
|
||||
('cardname', None),
|
||||
('setchannels', (2,)),
|
||||
('setrate', (44100,)),
|
||||
('setformat', (alsaaudio.PCM_FORMAT_S8,)),
|
||||
('setperiodsize', (320,))]
|
||||
|
||||
# A clever test would look at the Mixer capabilities and selectively run the
|
||||
# omitted tests, but I am too tired for that.
|
||||
|
||||
MixerMethods = [('cardname', None),
|
||||
('mixer', None),
|
||||
('mixerid', None),
|
||||
('switchcap', None),
|
||||
('volumecap', None),
|
||||
('getvolume', None),
|
||||
('getrange', None),
|
||||
('getenum', None),
|
||||
# ('getmute', None),
|
||||
# ('getrec', None),
|
||||
# ('setvolume', (60,)),
|
||||
# ('setmute', (0,))
|
||||
# ('setrec', (0')),
|
||||
]
|
||||
|
||||
class MixerTest(unittest.TestCase):
|
||||
"""Test Mixer objects"""
|
||||
|
||||
def testMixer(self):
|
||||
"""Open the default Mixers and the Mixers on every card"""
|
||||
|
||||
for c in alsaaudio.card_indexes():
|
||||
mixers = alsaaudio.mixers(cardindex=c)
|
||||
|
||||
for m in mixers:
|
||||
mixer = alsaaudio.Mixer(m, cardindex=c)
|
||||
mixer.close()
|
||||
|
||||
def testMixerAll(self):
|
||||
"Run common Mixer methods on an open object"
|
||||
|
||||
mixers = alsaaudio.mixers()
|
||||
mixer = alsaaudio.Mixer(mixers[0])
|
||||
|
||||
for m, a in MixerMethods:
|
||||
f = alsaaudio.Mixer.__dict__[m]
|
||||
if a is None:
|
||||
f(mixer)
|
||||
else:
|
||||
f(mixer, *a)
|
||||
|
||||
mixer.close()
|
||||
|
||||
def testMixerClose(self):
|
||||
"""Run common Mixer methods on a closed object and verify it raises an
|
||||
error"""
|
||||
|
||||
mixers = alsaaudio.mixers()
|
||||
mixer = alsaaudio.Mixer(mixers[0])
|
||||
mixer.close()
|
||||
|
||||
for m, a in MixerMethods:
|
||||
f = alsaaudio.Mixer.__dict__[m]
|
||||
if a is None:
|
||||
self.assertRaises(alsaaudio.ALSAAudioError, f, mixer)
|
||||
else:
|
||||
self.assertRaises(alsaaudio.ALSAAudioError, f, mixer, *a)
|
||||
|
||||
class PCMTest(unittest.TestCase):
|
||||
"""Test PCM objects"""
|
||||
|
||||
def testPCM(self):
|
||||
"Open a PCM object on every card"
|
||||
|
||||
for c in alsaaudio.card_indexes():
|
||||
pcm = alsaaudio.PCM(cardindex=c)
|
||||
pcm.close()
|
||||
|
||||
def testPCMAll(self):
|
||||
"Run all PCM methods on an open object"
|
||||
|
||||
pcm = alsaaudio.PCM()
|
||||
|
||||
for m, a in PCMMethods:
|
||||
f = alsaaudio.PCM.__dict__[m]
|
||||
if a is None:
|
||||
f(pcm)
|
||||
else:
|
||||
f(pcm, *a)
|
||||
|
||||
pcm.close()
|
||||
|
||||
def testPCMClose(self):
|
||||
"Run all PCM methods on a closed object and verify it raises an error"
|
||||
|
||||
pcm = alsaaudio.PCM()
|
||||
pcm.close()
|
||||
|
||||
for m, a in PCMMethods:
|
||||
f = alsaaudio.PCM.__dict__[m]
|
||||
if a is None:
|
||||
self.assertRaises(alsaaudio.ALSAAudioError, f, pcm)
|
||||
else:
|
||||
self.assertRaises(alsaaudio.ALSAAudioError, f, pcm, *a)
|
||||
|
||||
def testPCMDeprecated(self):
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
# Cause all warnings to always be triggered.
|
||||
warnings.simplefilter("always")
|
||||
|
||||
try:
|
||||
pcm = alsaaudio.PCM(card='default')
|
||||
except alsaaudio.ALSAAudioError:
|
||||
pass
|
||||
|
||||
# Verify we got a DepreciationWarning
|
||||
assert len(w) == 1
|
||||
assert issubclass(w[-1].category, DeprecationWarning)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user