94 Commits
0.8 ... 0.9.1

Author SHA1 Message Date
Lars Immisch
dfda54642d Prepare 0.9.1 2022-05-03 20:04:26 +01:00
Chris Diamand
3f6fb9844d Support decibel, percentage, and raw volumes in getvolume, setvolume, and getrange (#109)
* Use `pcmtype` keyword for range

Update methods that accept a `direction` argument (i.e.
capture/playback) to get this via positional _or_ keyword arguments.

Code using keyword arguments can be more robust; however the main reason
for this change is to prepare the way for an extra `units` argument to
many of these methods.

Update documentation to consistently use `pcmtype` instead of
a mixture of that and `direction`.

* Support units
2022-03-28 21:46:40 +02:00
Lars Immisch
4d9f6e5b50 Merge pull request #108 from st8ed/fix-polldescriptors
Fix polldescriptors() data types
2022-01-25 15:17:39 +01:00
Kirill Konstantinov
40a4a36b1d Fix polldescriptors() data types 2022-01-25 14:23:21 +03:00
Lars Immisch
38ea69bbaa Merge pull request #100 from soundappraisal/feature_timestamp_mode_and_type
Feature timestamp mode and type
2021-04-12 12:30:23 +02:00
Ronald van Elburg
c8f3916337 On phys_from_sound: Small memory management fixes and code simplification. And add documentation on new functionality. 2021-04-11 15:16:03 +02:00
Ronald van Elburg
f19af8eba0 Remove recordtestchanges. 2021-04-07 12:12:10 +02:00
Ronald van Elburg
b8980d992b Remove recordtestchanges. 2021-04-07 12:10:21 +02:00
Ronald van Elburg
ebd2b5359d Add function to set timestamp mode and type. Add a function to get the alsa version. 2021-04-07 11:59:16 +02:00
Ronald van Elburg
c5f22fd7e0 Second version enable timestamps 2021-04-06 22:48:17 +02:00
Ronald van Elburg
3c3f0af74a First version enable timestamps 2021-04-06 14:31:45 +02:00
Ronald van Elburg
17f3b440cc Show new functions in recordtest.py 2021-04-06 09:09:49 +02:00
Lars Immisch
b2a303121a Merge pull request #98 from soundappraisal/add_timestamp_function
Add timestamp_raw function
2021-04-04 16:27:26 +02:00
Ronald van Elburg
3168833b4e Merge remote-tracking branch 'upstream/master' into add_timestamp_function
# Conflicts:
#	alsaaudio.c
2021-04-02 22:54:18 +02:00
Lars Immisch
c74669850b Merge pull request #92 from soundappraisal/pcm_info_function
Add an PCM.info function: returns pcm properties as a dict
2021-04-02 20:57:15 +02:00
Ronald van Elburg
1a4c0541d7 Change name timestamp_raw fuinction to htimestamp to follow the convention used in the rest of the library: that's the current convention (prefix the name with alsapcm_ for PCM methods). 2021-04-02 13:42:51 +02:00
Ronald van Elburg
e6a6445375 Move creation of dictionary to a point after error handling, when it is relatively certain that the function will succeed.
(cherry picked from commit 1820716a4bc018bb903b95bcf5d7cf83a5ebda9c)
2021-04-02 13:24:55 +02:00
Ronald van Elburg
97f2abcb30 Remove debugging print statement.
(cherry picked from commit dcc43f3da7bf4d083cc6cab18ae464261fadc53f)
2021-04-02 13:24:55 +02:00
Ronald van Elburg
a53ffd0d4f Fix potential memory leaks on new info function.
(cherry picked from commit ade9dd5923edd65c1fcdf2298e8ad024daf66e2a)
2021-04-02 13:24:55 +02:00
Ronald van Elburg
da71e01f9c Remove unused code from timestamp_raw function. 2021-03-31 16:27:55 +02:00
Ronald van Elburg
f6736ec43a first version timestamp function
(cherry picked from commit 21d0527c7b91723b3bfc87ea889bd599dff12576)

# Conflicts:
#	alsaaudio.c
2020-11-02 19:32:34 +01:00
Ronald van Elburg
e48b294b84 PCM.info function: added format, mode and type fields. Also added a doc string describing the info function. 2020-10-28 22:01:04 +01:00
Lars Immisch
d037297632 Merge pull request #91 from soundappraisal/master
Fix #51: Only return valid part of the buffer in the read function
2020-10-27 12:47:36 +01:00
Ronald van Elburg
c8e7261e94 Add an PCM.info function returning the information now printed by dumpinfo as a dictionary. Removed double entry from dumpinfo. 2020-10-27 12:41:59 +01:00
Ronald van Elburg
5c481b4094 Fix #51: Only return valid part of the buffer in the read function; avoid unnecesssary work by only changing size when needed 2020-09-30 15:58:19 +02:00
Ronald van Elburg
1e3c7f3fd0 Fix #51: Only return valid part of the buffer in the read function 2020-09-30 15:11:10 +02:00
Lars Immisch
0ae60f80f3 Better pcm_type deduction in alsamixer_getvolume
Closes #87
2020-07-16 23:36:50 +02:00
Lars Immisch
4018ab4f6c Fix copypasta. 2020-07-16 23:36:12 +02:00
Lars Immisch
07f84a8e95 Move CHANGES to markdown, remove NOTES.md (doc/README.md replaces it) 2020-07-13 22:27:06 +02:00
Lars Immisch
d83e829de1 Formatting and fixed upload description. 2020-07-13 22:18:32 +02:00
Lars Immisch
62e5515341 Document the release process. 2020-07-13 22:00:44 +02:00
Lars Immisch
ed027a6141 More output for playwav 2020-07-13 20:42:25 +01:00
Lars Immisch
5302dc524d Cleanup warnings 2020-07-13 20:59:49 +02:00
Lars Immisch
b17b36be50 Better error messages in tests 2020-07-13 20:51:59 +02:00
Lars Immisch
08bdce9ed9 Tests for Depreciations 2020-07-13 20:20:28 +02:00
Lars Immisch
0224c8a308 Inline documentation (and .gitignore) 2020-07-10 00:54:24 +02:00
Lars Immisch
f07627543c Update documentation 2020-07-10 00:45:57 +02:00
Lars Immisch
df889b94ef Don't use setrate etc. in samples. 2020-07-09 21:22:06 +02:00
Lars Immisch
2a21bf6c42 Support all essential parameters in alsapcm_new. 2020-07-08 22:39:46 +02:00
Lars Immisch
8084297926 Merge pull request #83 from stalkerg/master
fix generate switch capabilities
2020-05-25 12:58:03 +02:00
stalkerg
8fbc04e18d fix generate switch capabilities 2020-05-21 17:21:40 +09:00
Lars Immisch
8ed9f924cd Attempt to fix #45 2020-04-23 21:36:29 +01:00
Lars Immisch
046e7c4e87 Get rid of warnings, adjust CHANGES 2020-04-01 22:47:11 +02:00
Lars Immisch
a4c4c7cb62 Consistent indentation and some code style changes (whould be ws only) 2020-03-09 22:28:08 +01:00
Lars Immisch
f478797f6f Merge branch 'dev/card-detail' of https://github.com/jdstmporter/pyalsaaudio into jdstmporter-dev/card-detail 2020-03-09 22:07:23 +01:00
Lars Immisch
12f807698a Merge #80 2020-03-09 22:05:50 +01:00
Julian Porter
fc011b5ea6 restored gitignore! 2020-03-06 20:21:47 +00:00
Julian Porter
f244a70111 tidied up 2020-03-06 20:06:59 +00:00
Julian Porter
a056a90c61 modified version of pyalsaaudio module 2020-03-06 19:59:04 +00:00
Julian Porter
be1b3e131d demo 2020-03-05 00:50:30 +00:00
Danny
8abf06bedf Prevent hang on close after capturing audio
Currently, after recording audio using pyalsaaudio, the client is unable to close the device.

The reason is that PulseAudio client tries to drain the pipe to the PulseAudio server (presumably in order to prevent Broken Pipe error) on closing. That will never finish since new data will always arrive in the pipe.

Worse, the __del__ handler was auto-closing and thus auto-hanging.

Therefore, pause before de-allocating.
2019-12-02 21:39:44 +00:00
Lars Immisch
dcc831e607 Merge pull request #44 from Oranos25/contribution
add support for snd_pcm_drop function
2019-11-14 13:24:36 +01:00
Lars Immisch
e587df9143 Merge pull request #55 from moham96/patch-1
update playwav.py for python 3
2019-11-14 13:20:12 +01:00
Lars Immisch
82febd3f7e Merge pull request #67 from pdericson/master
Update pyalsaaudio.rst
2018-11-16 12:50:52 +01:00
Peter Ericson
1695066c11 Update pyalsaaudio.rst 2018-11-16 16:51:05 +08:00
Lars Immisch
25717020ef Transactional semantics for the alsapcm_set* calls 2018-02-28 09:52:53 +00:00
Lars Immisch
1aae655d24 Update periodsize only after alsapcm_setup succeeded 2018-02-28 00:35:26 +01:00
MOHAMMAD RASIM
c1c8362eb2 update playwav.py for python 3
use int division for periodsize to be compatible with python 3
2018-02-24 19:40:45 +03:00
Lars Immisch
723eff3887 Prepare next release 2018-02-20 12:18:44 +01:00
Lars Immisch
aa9867de18 Document changes, i.e. #53. 2018-02-20 12:10:20 +01:00
Lars Immisch
58f4522769 Merge pull request #53 from jcea/jcea/read_period_size
Unlimited setperiod buffer size when reading frames
2018-02-20 12:05:37 +01:00
Jesus Cea
f2fb61d324 Unlimited setperiod buffer size when reading frames 2018-02-20 11:52:47 +01:00
Anthony Piau
9e79494a95 add support for snd_pcm_drop function 2017-12-28 16:30:32 +00:00
Lars Immisch
bfe4899721 Merge pull request #39 from michals/master
Support 24bit audio
2017-08-30 20:52:49 +02:00
Michał Šrajer
40a1219dac Support 24bit audio
SND_PCM_FORMAT_S24_LE and similar are for 24bit ints packed in 4-bytes each.
There is a similar family of formats for 3-bytes packed data (as stored in 24bit wave files).

This commit:
 - adds S24_3LE, S24_3BE, U24_3LE, U24_3BE PCM formats to the alsaaudio.c
 - updates documentation
 - updates playwav.py to correctly play typical 24Bit PCM wave files

Closes #38
2017-08-29 19:09:54 +02:00
Lars Immisch
54e2712b7a Document release procedure 2017-07-09 15:01:41 +02:00
Lars Immisch
f9685e0b30 Correct capitalization
as suggested by Ben Loveridge
2017-07-09 13:32:08 +02:00
Lars Immisch
b4a670c50d Doc fixes. 2017-03-31 00:29:19 +02:00
Lars Immisch
370a4b6249 Regenerated doc. 2017-03-31 00:25:00 +02:00
Lars Immisch
eca217dff9 Document PCM.polldescriptors.
Closes #32
2017-03-30 23:20:22 +02:00
Lars Immisch
65d3c4a283 Typo. 2017-03-17 20:42:02 +01:00
Lars Immisch
adc0d800e1 Document EPIPE 2017-03-17 20:40:40 +01:00
Lars Immisch
02cf16d10d Improve documentation 2017-02-25 01:32:54 +01:00
Lars Immisch
94ced0517e Correct the sine example (finally!) Closes #10 2017-02-25 01:04:18 +01:00
Lars Immisch
698e6044d3 Bump version number 2017-02-24 20:57:53 +01:00
Lars Immisch
2c95f4ff6b Larger periodsize.
Before, it wasn't playing properly on my Raspberry Pi + Hifiberry DAC
2017-02-24 20:54:49 +01:00
Lars Immisch
f19d139f64 Fix C-API usage for Python 3. Closes #29 2017-02-24 13:25:36 +01:00
Lars Immisch
dc51fa75b5 Make tests more robust, use devices or card indices. 2017-02-22 23:55:17 +01:00
Lars Immisch
85ff47ad43 Update to setuptools + version bump 2017-02-22 22:59:37 +01:00
Lars Immisch
88f38284bb Update documentation. Closes #18
Make sure no other setup.py from `sys.path` is accidentally loaded
2017-02-22 19:41:57 +01:00
Lars Immisch
fe7561beea Merge branch 'chrisdiamand-master' #27 2017-02-22 18:31:17 +01:00
Chris Diamand
2314aaeb7e Add functions for listing cards and their names
The cards() method does not guarantee that the index in its return
value is the same as the actual card index. Provide a way to get this
information in the form of a card_indexes() function, returning a
list of available card indexes.

Add another method, card_name(), which, given a card index, returns
the short and long names of that card.
2017-02-08 21:48:49 +00:00
Chris Diamand
bf24ec65ca Add a method for setting enums
Add a method, setenum(), for setting the value of an enumerated mixer
element. The argument is an integer index into the list of possible
values returned by getenum().
2017-02-08 20:50:23 +00:00
Lars Immisch
478d0559e6 Merge pull request #21 from PaulSD/master
Add Mixer.handleevents() to acknowledge events identified by select.poll
2016-11-01 15:52:53 +01:00
Paul Donohue
891a30eb08 Add Mixer.handleevents() to acknowledge events identified by select.poll 2016-10-21 12:21:14 -04:00
Lars Immisch
74d9e7d6e1 Merge pull request #11 from lintweaker/master
Add DSD sample formats
2015-09-25 15:30:20 +02:00
Jurgen Kramer
fa10bf6999 Make DSD support depend on ALSA lib version
This patch makes ALSA DSD sample format support depend on the ALSA lib version.
2015-09-25 15:07:49 +02:00
Jurgen Kramer
7de446c3c7 Add DSD sample formats
This patch adds support for using the ALSA DSD sample formats avaiable in
recents kernel/ALSA versions.
2015-09-25 13:34:10 +02:00
Lars Immisch
5cbc88607d We can get the version from git for pip installs. Fixes #3
Maybe we should tag from setup.py instead
2015-05-16 13:44:50 +02:00
Lars Immisch
0fb8b1d9f3 Prepare 0.8.1 2015-05-14 13:28:14 +02:00
Lars Immisch
3cb51bdf90 Correct capitalization 2015-05-14 01:42:13 +02:00
Lars Immisch
6e96f8556c Inclide MANIFEST/dist/ 2015-05-14 01:38:44 +02:00
Lars Immisch
fddd239be1 Add link 2015-05-14 01:37:51 +02:00
Lars Immisch
8950de75bf Document 0.8 changes 2015-05-14 01:32:42 +02:00
18 changed files with 3294 additions and 2002 deletions

9
.gitignore vendored
View File

@@ -1,7 +1,14 @@
*.pyc
*.so
MANIFEST
doc/gh-pages/
doc/html/
doc/doctrees/
doc/_build/
gh-pages/
build/
build/
dist/
.vscode/
/__pycache__/
/pyalsaaudio.egg-info/
*.raw

39
CHANGES
View File

@@ -1,39 +0,0 @@
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

100
CHANGES.md Normal file
View File

@@ -0,0 +1,100 @@
# Version 0.9.1:
- Support decibel, percentage, and raw volumes in getvolume, setvolume, and getrange (#109 from @chrisdiamand):
# Version 0.9.0:
- Added keyword arguments for channels, format, rate and periodsize
- Deprecated `setchannel`, `setformat`, `setrate` and `setperiodsize`
# Version 0.8.6:
- Added four methods to the `PCM` class to allow users to get detailed information about the device:
- `getformats()` returns a dictionary of name / value pairs, one for each of the card's
supported formats - e.g. `{"U8": 1, "S16_LE": 2}`,
- `getchannels()` returns a list of the supported channel numbers, e.g. `[1, 2]`,
- `getrates()` returns supported sample rates for the device, e.g. `[48000]`,
- `getratebounds()` returns the device's official minimum and maximum supported
sample rates as a tuple, e.g. `(4000, 48000)`.
(#82 contributed by @jdstmporter)
- Prevent hang on close after capturing audio (#80 contributed by @daym)
# 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

View File

@@ -5,12 +5,13 @@ 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 api from Python. It
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 this api, and those should be reported to the ALSA team - not
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

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +1,26 @@
# Make a new release
Update the version in setup.py
pyalsa_version = '0.9.0'
Commit and push the update.
Create and push a tag naming the version (i.e. 0.9.0):
git tag 0.9.0
git push origin 0.9.0
Create the package:
python3 setup.py sdist
Upload the package
twine upload dist/*
Don't forget to update the documentation.
# Publish the documentation
The documentation is published through the `gh-pages` branch.

View File

@@ -1,182 +1,160 @@
# -*- coding: utf-8 -*-
#
# alsaaudio documentation build configuration file, created by
# sphinx-quickstart on Sat Nov 22 00:17:09 2008.
# 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.
# This file is execfile()d with the current directory set to its
# containing dir.
#
# The contents of this file are pickled, so don't put values in the namespace
# that aren't pickleable (module imports are okay, they're removed automatically).
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default value; values that are commented out
# serve to show the default value.
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys, os
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
sys.path.append('..')
import sys
sys.path.insert(0, '..')
from setup import pyalsa_version
# If your extensions are in another directory, add it here. If the directory
# is relative to the documentation root, use os.path.abspath to make it
# absolute, like shown here.
#sys.path.append(os.path.abspath('some/directory'))
# General configuration
# ---------------------
# -- General configuration ------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
# 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']
templates_path = ['_templates']
# The suffix of source filenames.
# 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 substitutions.
project = u'alsaaudio'
copyright = u'2008-2009, Casper Wilstrup, Lars Immisch'
# General information about the project.
project = u'alsaaudio documentation'
copyright = u'2017, Lars Immisch & Casper Wilstrup'
author = u'Lars Immisch & Casper Wilstrup'
# The default replacements for |version| and |release|, also used in various
# other places throughout the built documents.
# 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 = pyalsa_version
release = version
# 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'
# 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 documents that shouldn't be included in the build.
#unused_docs = []
# List of directories, relative to source directories, that shouldn't be searched
# for source files.
exclude_trees = ['.build']
# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# 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 style sheet to use for HTML and HTML Help pages. A file of that name
# must exist either in Sphinx' static/ path, or in one of the custom paths
# given in html_static_path.
html_style = 'default.css'
# -- Options for HTML output ----------------------------------------------
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
#html_title = None
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'alabaster'
# 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
# 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']
html_static_path = ['_static']
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
#html_use_modindex = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, the reST sources are included in the HTML build as _sources/<name>.
#html_copy_source = 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 = ''
# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = ''
# -- Options for HTMLHelp output ------------------------------------------
# Output file base name for HTML help builder.
htmlhelp_basename = 'alsaaudiodoc'
htmlhelp_basename = 'alsaaudiodocumentationdoc'
# Options for LaTeX output
# ------------------------
# -- Options for LaTeX output ---------------------------------------------
# The paper size ('letter' or 'a4').
#latex_paper_size = 'letter'
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#latex_font_size = '10pt'
# 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, document class [howto/manual]).
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
('index', 'alsaaudio.tex', u'alsaaudio Documentation',
u'Casper Wilstrup, Lars Immisch', 'manual'),
(master_doc, 'alsaaudiodocumentation.tex', u'alsaaudio documentation Documentation',
u'Lars Immisch', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# -- 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'),
]
# Additional stuff for the LaTeX preamble.
#latex_preamble = ''
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_use_modindex = True

View File

@@ -1,22 +1,32 @@
.. 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
========
Github pages
=================
* `Project page <https://github.com/larsimmisch/pyalsaaudio/>`_
* `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
==================
@@ -24,3 +34,5 @@ Indices and tables
* :ref:`modindex`
* :ref:`search`

View File

@@ -33,13 +33,13 @@ The :mod:`alsaaudio` module defines functions and classes for using ALSA.
.. % should be enclosed in \var{...}.
.. function:: pcms([type=PCM_PLAYBACK])
.. function:: pcms(pcmtype=PCM_PLAYBACK)
List available PCM devices by name.
Arguments are:
* *type* - can be either :const:`PCM_CAPTURE` or :const:`PCM_PLAYBACK`
* *pcmtype* - can be either :const:`PCM_CAPTURE` or :const:`PCM_PLAYBACK`
(default).
**Note:**
@@ -63,7 +63,6 @@ The :mod:`alsaaudio` module defines functions and classes for using ALSA.
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:
@@ -98,6 +97,9 @@ The :mod:`alsaaudio` module defines functions and classes for using ALSA.
changed. Since 0.8, this functions returns the mixers for the default
device, not the mixers for the first card.
.. function:: asoundlib_version()
Return a Python string containing the ALSA version found.
.. _pcm-objects:
@@ -108,7 +110,7 @@ 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)
.. class:: PCM(type=PCM_PLAYBACK, mode=PCM_NORMAL, rate=44100, channels=2, format=PCM_FORMAT_S16_LE, periodsize=32, device='default', cardindex=-1)
This class is used to represent a PCM device (either for playback and
recording). The arguments are:
@@ -117,6 +119,44 @@ following arguments:
(default).
* *mode* - can be either :const:`PCM_NONBLOCK`, or :const:`PCM_NORMAL`
(default).
* *rate* - the sampling rate in Hz. Typical values are ``8000`` (mainly used for telephony), ``16000``, ``44100`` (default), ``48000`` and ``96000``.
* *channels* - the number of channels. The default value is 2 (stereo).
* *format* - the data format. This controls how the PCM device interprets data for playback, and how data is encoded in captures.
The default value is :const:`PCM_FORMAT_S16_LE`.
========================= ===============
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)
========================= ===============
* *periodsize* - the period size in frames. Each write should consist of *periodsize* frames. The default value is 32.
* *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'``.
@@ -125,12 +165,11 @@ following arguments:
the `device` keyword argument is ignored.
``0`` is the first hardware sound card.
This will construct a PCM object with these default settings:
This will construct a PCM object with the given settings.
* Sample format: :const:`PCM_FORMAT_S16_LE`
* Rate: 44100 Hz
* Channels: 2
* Period size: 32 frames
*Changed in 0.9:*
- Added the optional named parameters `rate`, `channels`, `format` and `periodsize`.
*Changed in 0.8:*
@@ -145,7 +184,6 @@ following arguments:
PCM objects have the following methods:
.. method:: PCM.pcmtype()
Returns the type of PCM object. Either :const:`PCM_CAPTURE` or
@@ -162,64 +200,21 @@ PCM objects have the following methods:
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
.. deprecated:: 0.9 Use the `channels` named argument to :func:`PCM`.
.. 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``.
.. deprecated:: 0.9 Use the `rate` named argument to :func:`PCM`.
.. 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)
``PCM_FORMAT_S24_BE`` Signed 24 bit samples for each channel (Big Endian byte order)}
``PCM_FORMAT_U24_LE`` Unsigned 24 bit samples for each channel (Little Endian byte order)
``PCM_FORMAT_U24_BE`` Unsigned 24 bit samples for each channel (Big Endian byte order)
``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
========================= ===============
.. deprecated:: 0.9 Use the `format` named argument to :func:`PCM`.
.. 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)
.. deprecated:: 0.9 Use the `periodsize` named argument to :func:`PCM`.
.. method:: PCM.read()
@@ -233,6 +228,9 @@ PCM objects have the following methods:
``(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)
@@ -256,6 +254,67 @@ PCM objects have the following methods:
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 PCM with *select.poll*.
The *eventmask* value is compatible with `poll.register`__ in the Python
:const:`select` module.
.. method:: PCM.set_tstamp_mode([mode=PCM_TSTAMP_ENABLE])
Set the ALSA timestamp mode on the device. The mode argument can be set to
either :const:`PCM_TSTAMP_NONE` or :const:`PCM_TSTAMP_ENABLE`.
.. method:: PCM.get_tstamp_mode()
Return the integer value corresponding to the ALSA timestamp mode. The
return value can be either :const:`PCM_TSTAMP_NONE` or :const:`PCM_TSTAMP_ENABLE`.
.. method:: PCM.set_tstamp_type([type=PCM_TSTAMP_TYPE_GETTIMEOFDAY])
Set the ALSA timestamp mode on the device. The type argument
can be set to either :const:`PCM_TSTAMP_TYPE_GETTIMEOFDAY`,
:const:`PCM_TSTAMP_TYPE_MONOTONIC` or :const:`PCM_TSTAMP_TYPE_MONOTONIC_RAW`.
.. method:: PCM.get_tstamp_type()
Return the integer value corresponding to the ALSA timestamp type. The
return value can be either :const:`PCM_TSTAMP_TYPE_GETTIMEOFDAY`,
:const:`PCM_TSTAMP_TYPE_MONOTONIC` or :const:`PCM_TSTAMP_TYPE_MONOTONIC_RAW`.
.. method:: PCM.htimestamp()
Return a Python tuple *(seconds, nanoseconds, frames_available_in_buffer)*.
The type of output is controlled by the tstamp_type, as described in the table below.
================================= ===========================================
Timestamp Type Description
================================= ===========================================
``PCM_TSTAMP_TYPE_GETTIMEOFDAY`` System-wide realtime clock with seconds
since epoch.
``PCM_TSTAMP_TYPE_MONOTONIC`` Monotonic time from an unspecified starting
time. Progress is NTP synchronized.
``PCM_TSTAMP_TYPE_MONOTONIC_RAW`` Monotonic time from an unspecified starting
time using only the system clock.
================================= ===========================================
The timestamp mode is controlled by the tstamp_mode, as described in the table below.
================================= ===========================================
Timestamp Mode Description
================================= ===========================================
``PCM_TSTAMP_NONE`` No timestamp.
``PCM_TSTAMP_ENABLE`` Update timestamp at every hardware position
update.
================================= ===========================================
__ 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
@@ -407,11 +466,11 @@ Mixer objects have the following methods:
This method will fail if the mixer has no playback switch capabilities.
.. method:: Mixer.getrange([direction])
.. method:: Mixer.getrange(pcmtype=PCM_PLAYBACK)
Return the volume range of the ALSA mixer controlled by this object.
The optional *direction* argument can be either :const:`PCM_PLAYBACK` or
The optional *pcmtype* 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`.
@@ -425,18 +484,18 @@ Mixer objects have the following methods:
This method will fail if the mixer has no capture switch capabilities.
.. method:: Mixer.getvolume([direction])
.. method:: Mixer.getvolume(pcmtype=PCM_PLAYBACK)
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
The optional *pcmtype* 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])
.. method:: Mixer.setvolume(volume, channel=None, pcmtype=PCM_PLAYBACK)
Change the current volume settings for this mixer. The *volume* argument
controls the new volume setting as an integer percentage.
@@ -445,7 +504,7 @@ Mixer objects have the following methods:
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
The optional *pcmtype* 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`.
@@ -473,9 +532,20 @@ Mixer objects have the following methods:
.. method:: Mixer.polldescriptors()
Returns a tuple of (file descriptor, eventmask) that can be used to
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.
@@ -614,3 +684,5 @@ argument::
.. 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

View File

@@ -33,7 +33,7 @@ wish (even commercial purposes). There is no warranty whatsoever.
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 the SourceForge bug tracker.
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.
@@ -75,7 +75,7 @@ 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 hass full support for sound capture, playback of sound, as well as
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
@@ -110,25 +110,32 @@ And then as root: --- ::
Testing
*******
First of all, run::
$ python test.py
Make sure that :code:`aplay` plays a file through the soundcard you want, then
try::
This is a small test suite that mostly performs consistency tests. If
it fails, please file a `bug report
<https://github.com/larsimmisch/pyalsaaudio/issues>`_.
$ 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 <filename>
$ 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 <filename>
$ 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>`_.

View File

@@ -46,7 +46,7 @@ Data rate
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)
kb/s (almost 5 MB sound data per second)
Period
When the hardware processes data this is done in chunks of frames. The time

View File

@@ -6,40 +6,57 @@
from __future__ import print_function
import sys
from threading import Thread
from queue import Queue, Empty
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 = 44100
sampling_rate = 48000
format = alsaaudio.PCM_FORMAT_S16_LE
framesize = 2 # bytes per frame for the values above
channels = 2
def digitize(s):
if s > 1.0 or s < -1.0:
raise ValueError
return struct.pack('h', int(s * 32767))
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):
# generate a buffer with a sine wave of frequency
size = int(sampling_rate / frequency)
buffer = bytes()
for i in range(size):
buffer = buffer + digitize(sin(i/(2 * pi)))
def generate(frequency, duration = 0.125):
# generate a buffer with a sine wave of `frequency`
# that is approximately `duration` seconds long
return buffer
# 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(1)
self.device.setformat(format)
self.device.setrate(sampling_rate)
self.device = alsaaudio.PCM(channels=channels, format=format, rate=sampling_rate)
self.queue = Queue()
self.change(frequency)
@@ -47,19 +64,15 @@ class SinePlayer(Thread):
'''This is called outside of the player thread'''
# we generate the buffer in the calling thread for less
# latency when switching frequencies
# More than 100 writes/s are pushing it - play multiple buffers
# for higher frequencies
if frequency > sampling_rate / 2:
raise ValueError('maximum frequency is %d' % (sampling_rate / 2))
factor = int(frequency/100.0)
if factor == 0:
factor = 1
buf = generate(frequency) * factor
print('factor: %d, frames: %d' % (factor, len(buf) / framesize))
f = nearest_frequency(frequency)
print('nearest frequency: %f' % f)
self.queue.put( buf)
buf = generate(f)
self.queue.put(buf)
def run(self):
buffer = None

View File

@@ -23,6 +23,12 @@ 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):
@@ -37,11 +43,36 @@ def show_mixer(name, kwargs):
sys.exit(1)
print("Mixer name: '%s'" % mixer.mixer())
print("Capabilities: %s %s" % (' '.join(mixer.volumecap()),
volcap = mixer.volumecap()
print("Capabilities: %s %s" % (' '.join(volcap),
' '.join(mixer.switchcap())))
if "Volume" in volcap or "Joined Volume" in volcap or "Playback Volume" in volcap:
pmin, pmax = mixer.getrange(alsaaudio.PCM_PLAYBACK)
pmin_keyword, pmax_keyword = mixer.getrange(pcmtype=alsaaudio.PCM_PLAYBACK, units=alsaaudio.VOLUME_UNITS_RAW)
pmin_default, pmax_default = mixer.getrange()
assert pmin == pmin_keyword
assert pmax == pmax_keyword
assert pmin == pmin_default
assert pmax == pmax_default
print("Raw playback volume range {}-{}".format(pmin, pmax))
pmin_dB, pmax_dB = mixer.getrange(units=alsaaudio.VOLUME_UNITS_DB)
print("dB playback volume range {}-{}".format(pmin_dB / 100.0, pmax_dB / 100.0))
if "Capture Volume" in volcap or "Joined Capture Volume" in volcap:
# Check that `getrange` works with keyword and positional arguments
cmin, cmax = mixer.getrange(alsaaudio.PCM_CAPTURE)
cmin_keyword, cmax_keyword = mixer.getrange(pcmtype=alsaaudio.PCM_CAPTURE, units=alsaaudio.VOLUME_UNITS_RAW)
assert cmin == cmin_keyword
assert cmax == cmax_keyword
print("Raw capture volume range {}-{}".format(cmin, cmax))
cmin_dB, cmax_dB = mixer.getrange(pcmtype=alsaaudio.PCM_CAPTURE, units=alsaaudio.VOLUME_UNITS_DB)
print("dB capture volume range {}-{}".format(cmin_dB / 100.0, cmax_dB / 100.0))
volumes = mixer.getvolume()
volumes_dB = mixer.getvolume(units=alsaaudio.VOLUME_UNITS_DB)
for i in range(len(volumes)):
print("Channel %i volume: %i%%" % (i,volumes[i]))
print("Channel %i volume: %i%% (%.1f dB)" % (i, volumes[i], volumes_dB[i] / 100.0))
try:
mutes = mixer.getmute()
@@ -113,6 +144,8 @@ if __name__ == '__main__':
else:
usage()
list_cards()
if not len(args):
list_mixers(kwargs)
elif len(args) == 1:

View File

@@ -1,4 +1,5 @@
#!/usr/bin/env python
#!/usr/bin/env python3
# -*- mode: python; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*-
## playbacktest.py
##
@@ -21,35 +22,28 @@ import getopt
import alsaaudio
def usage():
print('usage: playbacktest.py [-c <card>] <file>', file=sys.stderr)
print('usage: playbacktest.py [-d <device>] <file>', file=sys.stderr)
sys.exit(2)
if __name__ == '__main__':
card = 'default'
device = 'default'
opts, args = getopt.getopt(sys.argv[1:], 'c:')
opts, args = getopt.getopt(sys.argv[1:], 'd:')
for o, a in opts:
if o == '-c':
card = a
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, card=card)
# Set attributes: Mono, 44100 Hz, 16 bit little endian frames
out.setchannels(1)
out.setrate(44100)
out.setformat(alsaaudio.PCM_FORMAT_S16_LE)
# Open the device in playback mode in Mono, 44100 Hz, 16 bit little endian frames
# 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)
out = alsaaudio.PCM(alsaaudio.PCM_PLAYBACK, channels=1, rate=44100, format=alsaaudio.PCM_FORMAT_S16_LE, periodsize=160, device=device)
# Read data from stdin
data = f.read(320)
while data:

View File

@@ -1,4 +1,5 @@
#!/usr/bin/env python
#!/usr/bin/env python3
# -*- mode: python; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*-
# Simple test script that plays (some) wav files
@@ -9,55 +10,54 @@ import wave
import getopt
import alsaaudio
def play(device, f):
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())
format = None
# 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_LE)
elif f.getsampwidth() == 4:
device.setformat(alsaaudio.PCM_FORMAT_S32_LE)
else:
raise ValueError('Unsupported format')
# 8bit is unsigned in wav files
if f.getsampwidth() == 1:
format = alsaaudio.PCM_FORMAT_U8
# Otherwise we assume signed data, little endian
elif f.getsampwidth() == 2:
format = alsaaudio.PCM_FORMAT_S16_LE
elif f.getsampwidth() == 3:
format = alsaaudio.PCM_FORMAT_S24_3LE
elif f.getsampwidth() == 4:
format = alsaaudio.PCM_FORMAT_S32_LE
else:
raise ValueError('Unsupported format')
device.setperiodsize(320)
data = f.readframes(320)
while data:
# Read data from stdin
device.write(data)
data = f.readframes(320)
periodsize = f.getframerate() // 8
print('%d channels, %d sampling rate, format %d, periodsize %d\n' % (f.getnchannels(),
f.getframerate(),
format,
periodsize))
device = alsaaudio.PCM(channels=f.getnchannels(), rate=f.getframerate(), format=format, periodsize=periodsize, device=device)
data = f.readframes(periodsize)
while data:
# Read data from stdin
device.write(data)
data = f.readframes(periodsize)
def usage():
print('usage: playwav.py [-c <card>] <file>', file=sys.stderr)
sys.exit(2)
print('usage: playwav.py [-d <device>] <file>', file=sys.stderr)
sys.exit(2)
if __name__ == '__main__':
card = 'default'
device = 'default'
opts, args = getopt.getopt(sys.argv[1:], 'c:')
for o, a in opts:
if o == '-c':
card = a
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(card=card)
play(device, f)
f.close()
if not args:
usage()
with wave.open(args[0], 'rb') as f:
play(device, f)

View File

@@ -1,10 +1,11 @@
#!/usr/bin/env python
#!/usr/bin/env python3
# -*- mode: python; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*-
## recordtest.py
##
## This is an example of a simple sound capture script.
##
## The script opens an ALSA pcm forsound capture. Set
## 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.
##
@@ -22,48 +23,42 @@ import getopt
import alsaaudio
def usage():
print('usage: recordtest.py [-c <card>] <file>', file=sys.stderr)
sys.exit(2)
print('usage: recordtest.py [-d <device>] <file>', file=sys.stderr)
sys.exit(2)
if __name__ == '__main__':
card = 'default'
device = 'default'
opts, args = getopt.getopt(sys.argv[1:], 'c:')
for o, a in opts:
if o == '-c':
card = a
opts, args = getopt.getopt(sys.argv[1:], 'd:')
for o, a in opts:
if o == '-d':
device = a
if not args:
usage()
if not args:
usage()
f = open(args[0], 'wb')
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, card)
# Open the device in nonblocking capture mode in mono, with a sampling rate of 44100 Hz
# and 16 bit little endian samples
# 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 = alsaaudio.PCM(alsaaudio.PCM_CAPTURE, alsaaudio.PCM_NONBLOCK,
channels=1, rate=44100, format=alsaaudio.PCM_FORMAT_S16_LE,
periodsize=160, 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)
loops = 1000000
while loops > 0:
loops -= 1
# Read data from device
l, data = inp.read()
# 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)
if l:
f.write(data)
time.sleep(.001)

View File

@@ -4,25 +4,11 @@
It is fairly complete for PCM devices and Mixer access.
'''
import subprocess
from distutils.core import setup
from distutils.extension import Extension
from setuptools import setup
from setuptools.extension import Extension
from sys import version
def gitrev():
rev = subprocess.check_output(['git', 'describe', '--tags', '--dirty=-dev',
'--always'])
return rev.decode('utf-8').strip()
pyalsa_version = gitrev()
# patch distutils if it's too old to cope with the "classifiers" or
# "download_url" keywords
from sys import version
if version < '2.2.3':
from distutils.dist import DistributionMetadata
DistributionMetadata.classifiers = None
DistributionMetadata.download_url = None
pyalsa_version = '0.9.1'
if __name__ == '__main__':
setup(

219
test.py
View File

@@ -1,4 +1,5 @@
#!/usr/bin/env python
#!/usr/bin/env python3
# -*- mode: python; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*-
# 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.
@@ -12,134 +13,148 @@ 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,))]
PCMMethods = [
('pcmtype', None),
('pcmmode', None),
('cardname', None)
]
PCMDeprecatedMethods = [
('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')),
]
('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"""
"""Test Mixer objects"""
def testMixer(self):
"""Open the default Mixers and the Mixers on every card"""
for d in ['default'] + list(range(len(alsaaudio.cards()))):
if type(d) == type(0):
kwargs = { 'cardindex': d }
else:
kwargs = { 'device': d }
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()
mixers = alsaaudio.mixers(**kwargs)
for m in mixers:
mixer = alsaaudio.Mixer(m, **kwargs)
mixer.close()
def testMixerAll(self):
"Run common Mixer methods on an open object"
def testMixerAll(self):
"Run common Mixer methods on an open object"
mixers = alsaaudio.mixers()
mixer = alsaaudio.Mixer(mixers[0])
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)
for m, a in MixerMethods:
f = alsaaudio.Mixer.__dict__[m]
if a is None:
f(mixer)
else:
f(mixer, *a)
mixer.close()
mixer.close()
def testMixerClose(self):
"""Run common Mixer methods on a closed object and verify it raises an
error"""
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()
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)
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"""
"""Test PCM objects"""
def testPCM(self):
"Open a PCM object on every device"
def testPCM(self):
"Open a PCM object on every card"
for pd in alsaaudio.pcms():
pcm = alsaaudio.PCM(device=pd)
pcm.close()
for c in alsaaudio.card_indexes():
pcm = alsaaudio.PCM(cardindex=c)
pcm.close()
for pd in alsaaudio.pcms(alsaaudio.PCM_CAPTURE):
pcm = alsaaudio.PCM(alsaaudio.PCM_CAPTURE, device=pd)
pcm.close()
def testPCMAll(self):
"Run all PCM methods on an open object"
def testPCMAll(self):
"Run all PCM methods on an open object"
pcm = alsaaudio.PCM()
pcm = alsaaudio.PCM()
for m, a in PCMMethods:
f = alsaaudio.PCM.__dict__[m]
if a is None:
f(pcm)
else:
f(pcm, *a)
for m, a in PCMMethods:
f = alsaaudio.PCM.__dict__[m]
if a is None:
f(pcm)
else:
f(pcm, *a)
pcm.close()
pcm.close()
def testPCMClose(self):
"Run all PCM methods on a closed object and verify it raises an error"
def testPCMClose(self):
"Run all PCM methods on a closed object and verify it raises an error"
pcm = alsaaudio.PCM()
pcm.close()
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)
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")
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
self.assertEqual(len(w), 1, "PCM(card='default') expected a warning" )
self.assertTrue(issubclass(w[-1].category, DeprecationWarning), "PCM(card='default') expected a DeprecationWarning")
for m, a in PCMDeprecatedMethods:
with warnings.catch_warnings(record=True) as w:
# Cause all warnings to always be triggered.
warnings.simplefilter("always")
pcm = alsaaudio.PCM()
f = alsaaudio.PCM.__dict__[m]
if a is None:
f(pcm)
else:
f(pcm, *a)
# Verify we got a DepreciationWarning
method = "%s%s" % (m, str(a))
self.assertEqual(len(w), 1, method + " expected a warning")
self.assertTrue(issubclass(w[-1].category, DeprecationWarning), method + " expected a DeprecationWarning")
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()
unittest.main()