forked from auracaster/pyalsaaudio
This is release 0.4; it was tested with python2.5 and python3.0 (rc2 &
3) git-svn-id: svn://svn.code.sf.net/p/pyalsaaudio/code/trunk@29 ec2f30ec-7544-0410-870e-f70ca00c83f0
This commit is contained in:
7
CHANGES
7
CHANGES
@@ -1,6 +1,11 @@
|
||||
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 in reStructuredText; use Sphinx instead of LaTeX.
|
||||
- Documentation converted to reStructuredText; use Sphinx instead of LaTeX.
|
||||
- added cards()
|
||||
- added PCM.close()
|
||||
- added Mixer.close()
|
||||
- added mixer.getenum()
|
||||
|
||||
|
||||
|
||||
@@ -2,4 +2,4 @@ include *.py
|
||||
include CHANGES
|
||||
include TODO
|
||||
include LICENSE
|
||||
recursive-include doc *.html *.gif *.png *.css *.py *.tex Makefile
|
||||
recursive-include doc *.html *.gif *.png *.css *.py *.rst *.js *.json Makefile
|
||||
@@ -16,8 +16,7 @@
|
||||
#include "Python.h"
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
#include "stringobject.h"
|
||||
#define PyUnicode_AS_DATA PyString_AS_STRING
|
||||
#define PyUnicode_Check PyString_Check
|
||||
#define PyUnicode_FromString PyString_FromString
|
||||
#endif
|
||||
#include <alsa/asoundlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
@@ -36,7 +36,7 @@ The :mod:`alsaaudio` module defines functions and classes for using ALSA.
|
||||
|
||||
.. function:: cards()
|
||||
|
||||
List the available cards.
|
||||
List the available cards by name (suitable for PCM objects).
|
||||
|
||||
.. function:: mixers([cardindex])
|
||||
|
||||
@@ -101,13 +101,13 @@ PCM objects have the following methods:
|
||||
|
||||
.. method:: PCM.pcmtype()
|
||||
|
||||
Returns the type of PCM object. Either PCM_CAPTURE or PCM_PLAYBACK.
|
||||
Returns the type of PCM object. Either ``PCM_CAPTURE`` or ``PCM_PLAYBACK``.
|
||||
|
||||
|
||||
.. method:: PCM.pcmmode()
|
||||
|
||||
Return the mode of the PCM object. One of PCM_NONBLOCK, PCM_ASYNC,
|
||||
or PCM_NORMAL
|
||||
Return the mode of the PCM object. One of ``PCM_NONBLOCK``, ``PCM_ASYNC``,
|
||||
or ``PCM_NORMAL``
|
||||
|
||||
|
||||
.. method:: PCM.cardname()
|
||||
@@ -168,19 +168,19 @@ PCM objects have the following methods:
|
||||
|
||||
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 PCM_NONBLOCK mode, in
|
||||
number of frames (unless the device is in ``PCM_NONBLOCK`` mode, in
|
||||
which case it may return nothing at all)
|
||||
|
||||
|
||||
.. method:: PCM.read()
|
||||
|
||||
In PCM_NORMAL mode, this function blocks until a full period is
|
||||
In ``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 PCM_NONBLOCK mode, the call will not block, but will return
|
||||
In ``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.
|
||||
|
||||
@@ -192,12 +192,12 @@ PCM objects have the following methods:
|
||||
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 PCM_NONBLOCK mode, this call will block if
|
||||
If the device is not in ``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 PCM_NONBLOCK mode, the call will return immediately, with a
|
||||
In ``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.
|
||||
|
||||
@@ -209,17 +209,16 @@ PCM objects have the following methods:
|
||||
|
||||
**A few hints on using PCM devices for playback**
|
||||
|
||||
The most common reason for problems with playback of PCM audio, is that the
|
||||
people don't properly understand that writes to PCM devices must match
|
||||
*exactly* the data rate of the device.
|
||||
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
|
||||
(PCM_NORMAL mode) or return zero (PCM_NONBLOCK mode).
|
||||
(``PCM_NORMAL`` mode) or return zero (``PCM_NONBLOCK`` mode).
|
||||
|
||||
If your program does nothing but play sound, the best strategy is to put the
|
||||
device in PCM_NORMAL mode, and just write as much data to the device as
|
||||
device in ``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.
|
||||
|
||||
@@ -230,7 +229,7 @@ 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
|
||||
acummulate time delays: If you set the timer to expire after 1/10'th
|
||||
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.
|
||||
@@ -244,7 +243,7 @@ Mixer Objects
|
||||
Mixer objects provides access to the ALSA mixer API.
|
||||
|
||||
|
||||
.. class:: Mixer([control], [id], [cardindex])
|
||||
.. class:: Mixer(control='Master', id=0, cardindex=0)
|
||||
|
||||
*control* - specifies which control to manipulate using this mixer
|
||||
object. The list of available controls can be found with the
|
||||
@@ -253,12 +252,13 @@ Mixer objects provides access to the ALSA mixer API.
|
||||
|
||||
*id* - the id of the mixer control. Default is 0
|
||||
|
||||
*cardindex* - specifies which card should be used[#f3]_. 0 is the
|
||||
first sound card. Default is 0.
|
||||
*cardindex* - specifies which card should be used [#f3]_. 0 is the
|
||||
first sound card.
|
||||
|
||||
For a list of available controls, you can also use ``amixer``::
|
||||
|
||||
**Note:** For a list of available controls, you can also use **amixer**::
|
||||
|
||||
amixer -c 1
|
||||
amixer
|
||||
|
||||
Mixer objects have the following methods:
|
||||
|
||||
@@ -283,19 +283,19 @@ Mixer objects have the following methods:
|
||||
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 swithes use the :meth:`setrec` or
|
||||
====================== ================
|
||||
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
|
||||
|
||||
|
||||
@@ -304,16 +304,16 @@ Mixer objects have the following methods:
|
||||
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
|
||||
====================== ================
|
||||
======================== ================
|
||||
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()
|
||||
|
||||
@@ -445,13 +445,15 @@ painful trial and error process.
|
||||
Examples
|
||||
--------
|
||||
|
||||
The following examples are provided:
|
||||
The following example are provided:
|
||||
|
||||
* playwav.py
|
||||
* recordtest.py
|
||||
* playbacktest.py
|
||||
* mixertest.py
|
||||
|
||||
All examples take the commandline option '-c <cardname>'.
|
||||
All examples (except mixertest.py) accept the commandline option
|
||||
*-c <cardname>*.
|
||||
|
||||
To determine a valid card name, use the commandline ALSA player::
|
||||
|
||||
@@ -464,22 +466,24 @@ or::
|
||||
>>> import alsaaudio
|
||||
>>> alsaaudio.cards()
|
||||
|
||||
mixertest.py accepts the commandline option *-c <cardindex>*. Card
|
||||
indices start at 0.
|
||||
|
||||
playwav.py
|
||||
~~~~~~~~~~
|
||||
|
||||
``playwav.py`` plays a wav file. A sample wav file is
|
||||
provided in the source distribution.
|
||||
**playwav.py** plays a wav file.
|
||||
|
||||
To test PCM playback (on your default soundcard), do::
|
||||
To test PCM playback (on your default soundcard), run::
|
||||
|
||||
$ python playwav.py foo.wav
|
||||
$ python playwav.py <wav file>
|
||||
|
||||
recordtest.py and playbacktest.py
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
``recordtest.py`` and ``playbacktest.py`` will record and play a raw
|
||||
**recordtest.py** and **playbacktest.py** will record and play a raw
|
||||
sound file in CD quality.
|
||||
|
||||
To test PCM recordings (on your default soundcard), do::
|
||||
To test PCM recordings (on your default soundcard), run::
|
||||
|
||||
$ python recordtest.py <filename>
|
||||
|
||||
@@ -490,8 +494,53 @@ Play back the recording with::
|
||||
|
||||
$ python playbacktest.py <filename>
|
||||
|
||||
mixertest.py
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Without arguments, **mixertest.py** will list all available *controls*.
|
||||
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.
|
||||
|
||||
.. rubric:: Footnotes
|
||||
|
||||
.. [#f1] ALSA also allows ``PCM_ASYNC``, but this is not supported yet.
|
||||
.. [#f2] But :mod:`alsaaudio` will leave any name alone that has a ':' (colon) in it.
|
||||
.. [#f2] :mod:`alsaaudio` will leave any name alone that has a ':' (colon) in it.
|
||||
.. [#f3] This is inconsistent with the PCM objects, which use names, but it is consistent with aplay and amixer.
|
||||
|
||||
92
mixertest.py
Normal file → Executable file
92
mixertest.py
Normal file → Executable file
@@ -1,3 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
## mixertest.py
|
||||
##
|
||||
## This is an example of using the ALSA mixer API
|
||||
@@ -13,34 +15,40 @@
|
||||
## python mixertest.py Master mute # mute the master mixer
|
||||
## python mixertest.py Master unmute # unmute the master mixer
|
||||
|
||||
import alsaaudio
|
||||
|
||||
# Footnote: I'd normally use print instead of sys.std(out|err).write,
|
||||
# but we're in the middle of the conversion between python 2 and 3
|
||||
# and this code runs on both versions without conversion
|
||||
|
||||
import sys
|
||||
import getopt
|
||||
import alsaaudio
|
||||
|
||||
if len(sys.argv) == 1:
|
||||
# Demonstrates how to read the available mixers
|
||||
print "Available mixer controls:"
|
||||
for m in alsaaudio.mixers():
|
||||
print " '%s'" % m
|
||||
|
||||
if len(sys.argv) == 2:
|
||||
def list_mixers(idx=0):
|
||||
sys.stdout.write("Available mixer controls:\n")
|
||||
for m in alsaaudio.mixers(idx):
|
||||
sys.stdout.write(" '%s'\n" % m)
|
||||
|
||||
def show_mixer(name, idx=0):
|
||||
# Demonstrates how mixer settings are queried.
|
||||
name = sys.argv[1]
|
||||
try:
|
||||
mixer = alsaaudio.Mixer(name)
|
||||
mixer = alsaaudio.Mixer(name, cardindex=idx)
|
||||
except alsaaudio.ALSAAudioError:
|
||||
print "No such mixer"
|
||||
sys.stderr.write("No such mixer\n")
|
||||
sys.exit(1)
|
||||
|
||||
print "Mixer name: '%s'"%mixer.mixer()
|
||||
print "Capabilities",mixer.volumecap()+mixer.switchcap()
|
||||
sys.stdout.write("Mixer name: '%s'\n" % mixer.mixer())
|
||||
sys.stdout.write("Capabilities: %s %s\n" % (' '.join(mixer.volumecap()),
|
||||
' '.join(mixer.switchcap())))
|
||||
volumes = mixer.getvolume()
|
||||
for i in range(len(volumes)):
|
||||
print "Channel %i volume: %i%%"%(i,volumes[i])
|
||||
sys.stdout.write("Channel %i volume: %i%%\n" % (i,volumes[i]))
|
||||
|
||||
try:
|
||||
mutes = mixer.getmute()
|
||||
for i in range(len(mutes)):
|
||||
if mutes[i]: print "Channel %i is muted"%i
|
||||
if mutes[i]:
|
||||
sys.stdout.write("Channel %i is muted\n" % i)
|
||||
except alsaaudio.ALSAAudioError:
|
||||
# May not support muting
|
||||
pass
|
||||
@@ -48,41 +56,65 @@ if len(sys.argv) == 2:
|
||||
try:
|
||||
recs = mixer.getrec()
|
||||
for i in range(len(recs)):
|
||||
if recs[i]: print "Channel %i is recording"%i
|
||||
if recs[i]:
|
||||
sys.stdout.write("Channel %i is recording\n" % i)
|
||||
except alsaaudio.ALSAAudioError:
|
||||
# May not support recording
|
||||
pass
|
||||
|
||||
if (len(sys.argv)) == 3:
|
||||
def set_mixer(name, args, idx=0):
|
||||
# Demonstrates how to set mixer settings
|
||||
name = sys.argv[1]
|
||||
try:
|
||||
mixer = alsaaudio.Mixer(name)
|
||||
mixer = alsaaudio.Mixer(name, cardindex=idx)
|
||||
except alsaaudio.ALSAAudioError:
|
||||
print "No such mixer"
|
||||
sys.stderr.write("No such mixer")
|
||||
sys.exit(1)
|
||||
|
||||
args = sys.argv[2]
|
||||
if args in ['mute','unmute']:
|
||||
# Mute/unmute the mixer
|
||||
if args == 'mute': mixer.setmute(1)
|
||||
else: mixer.setmute(0)
|
||||
sys.exit(0)
|
||||
if args in ['rec','unrec']:
|
||||
# Enable/disable recording
|
||||
if args == 'rec': mixer.setrec(1)
|
||||
else: mixer.setrec(0)
|
||||
if args == 'mute':
|
||||
mixer.setmute(1)
|
||||
else:
|
||||
mixer.setmute(0)
|
||||
sys.exit(0)
|
||||
|
||||
if args in ['rec','unrec']:
|
||||
# Enable/disable recording
|
||||
if args == 'rec':
|
||||
mixer.setrec(1)
|
||||
else:
|
||||
mixer.setrec(0)
|
||||
sys.exit(0)
|
||||
|
||||
if args.find(',')!=-1:
|
||||
if args.find(',') != -1:
|
||||
channel,volume = map(int,args.split(','))
|
||||
else:
|
||||
channel = alsaaudio.MIXER_CHANNEL_ALL
|
||||
volume = int(args)
|
||||
# Set volume for specified channel. MIXER_CHANNEL_ALL means set
|
||||
# volume for all channels
|
||||
mixer.setvolume(volume,channel)
|
||||
mixer.setvolume(volume, channel)
|
||||
|
||||
def usage():
|
||||
sys.stderr.write('usage: mixertest.py [-c <card>] ' \
|
||||
'[ <control>[,<value>]]\n')
|
||||
sys.exit(2)
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
cardindex = 0
|
||||
opts, args = getopt.getopt(sys.argv[1:], 'c:')
|
||||
for o, a in opts:
|
||||
if o == '-c':
|
||||
cardindex = int(a)
|
||||
|
||||
if not len(args):
|
||||
list_mixers(cardindex)
|
||||
elif len(args) == 1:
|
||||
show_mixer(args[0], cardindex)
|
||||
else:
|
||||
set_mixer(args[0], args[1], cardindex)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
67
playbacktest.py
Normal file → Executable file
67
playbacktest.py
Normal file → Executable file
@@ -1,3 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
## playbacktest.py
|
||||
##
|
||||
## This is an example of a simple sound playback script.
|
||||
@@ -7,30 +9,53 @@
|
||||
## 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
|
||||
##
|
||||
## If you have Gnome, you could also just test by doing something like:
|
||||
## python playbacktest.py < /usr/share/sounds/gnibbles/laughter.wav
|
||||
import alsaaudio
|
||||
## python recordtest.py out.raw # talk to the microphone
|
||||
## python playbacktest.py out.raw
|
||||
|
||||
|
||||
# Footnote: I'd normally use print instead of sys.std(out|err).write,
|
||||
# but we're in the middle of the conversion between python 2 and 3
|
||||
# and this code runs on both versions without conversion
|
||||
|
||||
import sys
|
||||
import time
|
||||
import getopt
|
||||
import alsaaudio
|
||||
|
||||
# Open the device in playback mode.
|
||||
out = alsaaudio.PCM(alsaaudio.PCM_PLAYBACK)
|
||||
def usage():
|
||||
sys.stderr.write('usage: playbacktest.py [-c <card>] <file>\n')
|
||||
sys.exit(2)
|
||||
|
||||
# Set attributes: Mono, 8000 Hz, 16 bit little endian frames
|
||||
out.setchannels(1)
|
||||
out.setrate(8000)
|
||||
out.setformat(alsaaudio.PCM_FORMAT_S16_LE)
|
||||
if __name__ == '__main__':
|
||||
|
||||
# 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)
|
||||
card = 'default'
|
||||
|
||||
loops = 10000
|
||||
while loops > 0:
|
||||
loops -= 1
|
||||
# Read data from stdin
|
||||
data = sys.stdin.read(320)
|
||||
out.write(data)
|
||||
opts, args = getopt.getopt(sys.argv[1:], 'c:')
|
||||
for o, a in opts:
|
||||
if o == '-c':
|
||||
card = 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)
|
||||
|
||||
# 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)
|
||||
|
||||
|
||||
|
||||
98
playwav.py
98
playwav.py
@@ -2,87 +2,59 @@
|
||||
|
||||
# Simple test script that plays (some) wav files
|
||||
|
||||
|
||||
# Footnote: I'd normally use print instead of sys.std(out|err).write,
|
||||
# but we're in the middle of the conversion between python 2 and 3
|
||||
# and this code runs on both versions without conversion
|
||||
|
||||
import sys
|
||||
import struct
|
||||
import wave
|
||||
import getopt
|
||||
import alsaaudio
|
||||
|
||||
# this is all a bit simplified, and won't cope with any wav extensions
|
||||
# or multiple data chunks, but it's good enough here
|
||||
|
||||
WAV_FORMAT_PCM = 1
|
||||
WAV_FORMAT_ALAW = 6
|
||||
WAV_FORMAT_MULAW = 7
|
||||
WAV_HEADER = '<4sl4s4slhhllhh4sl'
|
||||
WAV_HEADER_SIZE = struct.calcsize(WAV_HEADER)
|
||||
|
||||
def _b(s):
|
||||
'Helper for 3.0 compatibility'
|
||||
|
||||
if sys.version_info[0] >= 3:
|
||||
return bytes(s, 'UTF-8')
|
||||
|
||||
return s
|
||||
|
||||
def wav_header_unpack(data):
|
||||
(riff, riffsize, wave, fmt, fmtsize, format, nchannels, framerate,
|
||||
datarate, blockalign, bitspersample, data, datalength) \
|
||||
= struct.unpack(WAV_HEADER, data)
|
||||
|
||||
print(data)
|
||||
|
||||
if riff != _b('RIFF') or fmtsize != 16 or fmt != _b('fmt ') \
|
||||
or data != _b('data'):
|
||||
raise ValueError('wav header too complicated')
|
||||
|
||||
return (format, nchannels, framerate, bitspersample, datalength)
|
||||
|
||||
def play(device, f):
|
||||
header = f.read(WAV_HEADER_SIZE)
|
||||
format, nchannels, framerate, bitspersample, datalength \
|
||||
= wav_header_unpack(header)
|
||||
|
||||
# Set attributes
|
||||
device.setchannels(nchannels)
|
||||
device.setrate(framerate)
|
||||
device.setchannels(f.getnchannels())
|
||||
device.setrate(f.getframerate())
|
||||
|
||||
# We assume signed data, little endian
|
||||
if format == WAV_FORMAT_PCM:
|
||||
if bitspersample == 8:
|
||||
device.setformat(alsaaudio.PCM_FORMAT_S8)
|
||||
elif bitspersample == 16:
|
||||
device.setformat(alsaaudio.PCM_FORMAT_S16_LE)
|
||||
elif bitspersample == 24:
|
||||
device.setformat(alsaaudio.PCM_FORMAT_S24_LE)
|
||||
elif bitspersample == 32:
|
||||
device.setformat(alsaaudio.PCM_FORMAT_S32_LE)
|
||||
elif format == WAV_FORMAT_ALAW:
|
||||
device.setformat(alsaaudio.PCM_FORMAT_A_LAW)
|
||||
elif format == WAV_FORMAT_MULAW:
|
||||
device.setformat(alsaaudio.PCM_FORMAT_MU_LAW)
|
||||
if f.getsampwidth() == 1:
|
||||
device.setformat(alsaaudio.PCM_FORMAT_S8)
|
||||
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 %d' % format)
|
||||
raise ValueError('Unsupported format')
|
||||
|
||||
# The period size controls the internal number of frames per period.
|
||||
# The significance of this parameter is documented in the ALSA api.
|
||||
|
||||
# rs = framerate / 25
|
||||
# out.setperiodsize(rs)
|
||||
|
||||
data = f.read()
|
||||
data = f.readframes(320)
|
||||
while data:
|
||||
# Read data from stdin
|
||||
device.write(data)
|
||||
data = f.read()
|
||||
data = f.readframes(320)
|
||||
|
||||
|
||||
def usage():
|
||||
sys.stderr.write('usage: playwav.py [-c <card>] <file>\n')
|
||||
sys.exit(2)
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
print('usage: playwav.py <file>')
|
||||
sys.exit(2)
|
||||
card = 'default'
|
||||
|
||||
f = open(sys.argv[1], 'rb')
|
||||
device = alsaaudio.PCM(alsaaudio.PCM_PLAYBACK)
|
||||
opts, args = getopt.getopt(sys.argv[1:], 'c:')
|
||||
for o, a in opts:
|
||||
if o == '-c':
|
||||
card = a
|
||||
|
||||
if not args:
|
||||
usage()
|
||||
|
||||
f = wave.open(args[0], 'rb')
|
||||
device = alsaaudio.PCM(card=card)
|
||||
|
||||
play(device, f)
|
||||
|
||||
|
||||
81
recordtest.py
Normal file → Executable file
81
recordtest.py
Normal file → Executable file
@@ -1,3 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
## recordtest.py
|
||||
##
|
||||
## This is an example of a simple sound capture script.
|
||||
@@ -7,39 +9,62 @@
|
||||
## writing the data to standard out.
|
||||
##
|
||||
## To test it out do the following:
|
||||
## python recordtest.py > out.raw # talk to the microphone
|
||||
## python recordtest.py out.raw # talk to the microphone
|
||||
## aplay -r 8000 -f S16_LE -c 1 out.raw
|
||||
|
||||
import alsaaudio
|
||||
|
||||
# Footnote: I'd normally use print instead of sys.std(out|err).write,
|
||||
# but we're in the middle of the conversion between python 2 and 3
|
||||
# and this code runs on both versions without conversion
|
||||
|
||||
import sys
|
||||
import time
|
||||
import getopt
|
||||
import alsaaudio
|
||||
|
||||
# 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)
|
||||
def usage():
|
||||
sys.stderr.write('usage: recordtest.py [-c <card>] <file>\n')
|
||||
sys.exit(2)
|
||||
|
||||
# Set attributes: Mono, 8000 Hz, 16 bit little endian samples
|
||||
inp.setchannels(1)
|
||||
inp.setrate(8000)
|
||||
inp.setformat(alsaaudio.PCM_FORMAT_S16_LE)
|
||||
if __name__ == '__main__':
|
||||
|
||||
# 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)
|
||||
card = 'default'
|
||||
|
||||
loops = 1000000
|
||||
while loops > 0:
|
||||
loops -= 1
|
||||
# Read data from device
|
||||
l,data = inp.read()
|
||||
|
||||
if l:
|
||||
# actual data read. Write it to stdout
|
||||
sys.stdout.write(data)
|
||||
time.sleep(.001)
|
||||
opts, args = getopt.getopt(sys.argv[1:], 'c:')
|
||||
for o, a in opts:
|
||||
if o == '-c':
|
||||
card = 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, card)
|
||||
|
||||
# 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)
|
||||
|
||||
17
setup.py
17
setup.py
@@ -8,18 +8,6 @@ from distutils.core import setup
|
||||
from distutils.extension import Extension
|
||||
from sys import version
|
||||
|
||||
try:
|
||||
from distutils.command.build_py import build_py_2to3 as \
|
||||
build_py
|
||||
except ImportError:
|
||||
from distutils.command.build_py import build_py
|
||||
|
||||
try:
|
||||
from distutils.command.build_scripts import build_scripts_2to3 as \
|
||||
build_scripts
|
||||
except ImportError:
|
||||
from distutils.command.build_scripts import build_scripts
|
||||
|
||||
# patch distutils if it's too old to cope with the "classifiers" or
|
||||
# "download_url" keywords
|
||||
from sys import version
|
||||
@@ -39,10 +27,7 @@ setup(
|
||||
maintainer_email = 'lars@ibp.de',
|
||||
license='PSF',
|
||||
platforms=['posix'],
|
||||
scripts=['playbacktest.py', 'recordtest.py', 'playwav.py', 'mixertest.py'],
|
||||
url='http://pyalsaaudio.sourceforge.net/',
|
||||
cmdclass = {'build_py':build_py,
|
||||
'build_scripts':build_scripts},
|
||||
classifiers = [
|
||||
'Development Status :: 4 - Beta',
|
||||
'Intended Audience :: Developers',
|
||||
@@ -55,5 +40,5 @@ setup(
|
||||
'Topic :: Multimedia :: Sound/Audio :: Players',
|
||||
'Topic :: Multimedia :: Sound/Audio :: Capture/Recording',
|
||||
],
|
||||
ext_modules=[Extension('alsaaudio',['alsaaudio.c'],libraries=['asound'])]
|
||||
ext_modules=[Extension('alsaaudio',['alsaaudio.c'], libraries=['asound'])]
|
||||
)
|
||||
|
||||
123
test.py
Normal file → Executable file
123
test.py
Normal file → Executable file
@@ -1,7 +1,122 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# These are internal test. They shouldn't fail, but they don't cover all
|
||||
# of the ALSA API. Most of all 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://sourceforge.net/projects/pyalsaaudio
|
||||
|
||||
import unittest
|
||||
import alsaaudio
|
||||
import sys
|
||||
if len(sys.argv) > 1: name = sys.argv[1]
|
||||
else: name = "Master"
|
||||
|
||||
m = alsaaudio.Mixer(name)
|
||||
|
||||
# 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 a Mixer on every card"""
|
||||
|
||||
# Mixers are addressed by index, not name
|
||||
for i in range(len(alsaaudio.cards())):
|
||||
mixers = alsaaudio.mixers(i)
|
||||
for m in mixers:
|
||||
mixer = alsaaudio.Mixer(m, cardindex=i)
|
||||
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.cards():
|
||||
pcm = alsaaudio.PCM(card=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)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user