Compare commits

...

14 Commits
0.8.2 ... 0.8.4

Author SHA1 Message Date
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
10 changed files with 234 additions and 52 deletions

View File

@@ -20,7 +20,13 @@
#define PyLong_Check PyInt_Check
#define PyLong_AS_LONG PyInt_AS_LONG
#endif
#if PY_MAJOR_VERSION < 3
#define PyLong_FromLong PyInt_FromLong
#endif
#include <alsa/asoundlib.h>
#include <alsa/version.h>
#include <stdio.h>
PyDoc_STRVAR(alsaaudio_module_doc,
@@ -170,6 +176,76 @@ PyDoc_STRVAR(cards_doc,
\n\
List the available card ids.");
static PyObject *
alsacard_list_indexes(PyObject *self, PyObject *args)
{
int rc;
int card = -1;
PyObject *result = NULL;
if (!PyArg_ParseTuple(args,":card_indexes"))
return NULL;
result = PyList_New(0);
for (rc = snd_card_next(&card); !rc && (card >= 0);
rc = snd_card_next(&card))
{
PyObject *item = PyLong_FromLong(card);
PyList_Append(result, item);
Py_DECREF(item);
}
return result;
}
PyDoc_STRVAR(card_indexes_doc,
"card_indexes()\n\
\n\
List the available card indexes.");
static PyObject *
alsacard_name(PyObject *self, PyObject *args)
{
int err, card;
PyObject *result = NULL;
char *name = NULL, *longname = NULL;
if (!PyArg_ParseTuple(args,"i:card_name", &card))
return NULL;
err = snd_card_get_name(card, &name);
if (err < 0) {
PyErr_Format(ALSAAudioError, "%s [%d]", snd_strerror(err), card);
goto exit;
}
err = snd_card_get_longname(card, &longname);
if (err < 0) {
PyErr_Format(ALSAAudioError, "%s [%d]", snd_strerror(err), card);
goto exit;
}
result = PyTuple_New(2);
PyTuple_SetItem(result, 0, PyUnicode_FromString(name));
PyTuple_SetItem(result, 1, PyUnicode_FromString(longname));
exit:
free(name);
free(longname);
return result;
}
PyDoc_STRVAR(card_name_doc,
"card_name(card_index) -> Tuple of (name, longname)\n\
\n\
Return the card name and long name for card 'card_index'.");
static PyObject *
alsapcm_list(PyObject *self, PyObject *args)
{
@@ -755,7 +831,11 @@ frames as bytes (or a string in Python 2.x). The length of the returned data\n\
will be periodsize*framesize bytes.\n\
\n\
In PCM_NONBLOCK mode, the call will not block, but will return (0,'')\n\
if no new period has become available since the last call to read.");
if no new period has become available since the last call to read.\n\
\n\
In case of an overrun, this function will return a negative size: -EPIPE.\n\
This indicates that data was lost, even if the operation itself succeeded.\n\
Try using a larger periodsize");
static PyObject *alsapcm_write(alsapcm_t *self, PyObject *args)
@@ -1744,6 +1824,60 @@ PyDoc_STRVAR(getenum_doc,
Returns a a tuple. The first element is name of the active enumerated item, \n\
the second a list available enumerated items.");
static PyObject *
alsamixer_setenum(alsamixer_t *self, PyObject *args)
{
snd_mixer_elem_t *elem;
int index, count, rc;
if (!PyArg_ParseTuple(args, "i:setenum", &index))
return NULL;
if (!self->handle)
{
PyErr_SetString(ALSAAudioError, "Mixer is closed");
return NULL;
}
elem = alsamixer_find_elem(self->handle,self->controlname,self->controlid);
if (!snd_mixer_selem_is_enumerated(elem)) {
PyErr_SetString(ALSAAudioError, "Not an enumerated control");
return NULL;
}
count = snd_mixer_selem_get_enum_items(elem);
if (count < 0)
{
PyErr_Format(ALSAAudioError, "%s [%s]", snd_strerror(count),
self->cardname);
return NULL;
}
if (index < 0 || index >= count) {
PyErr_Format(ALSAAudioError, "Enum index out of range 0 <= %d < %d",
index, count);
return NULL;
}
rc = snd_mixer_selem_set_enum_item(elem, 0, index);
if (rc)
{
PyErr_Format(ALSAAudioError, "%s [%s]", snd_strerror(rc),
self->cardname);
return NULL;
}
Py_RETURN_NONE;
}
PyDoc_STRVAR(setenum_doc,
"setenum(index) -> None\n\
\n\
Sets the value of the enum, where 'index' is an index into the list of\n\
available enumerated items returned by getenum().");
static PyObject *
alsamixer_getmute(alsamixer_t *self, PyObject *args)
{
@@ -2115,6 +2249,38 @@ PyDoc_STRVAR(polldescriptors_doc,
Return a list of file descriptors and event masks\n\
suitable for use with poll to monitor changes on this mixer.");
static PyObject *
alsamixer_handleevents(alsamixer_t *self, PyObject *args)
{
int handled;
if (!PyArg_ParseTuple(args,":handleevents"))
return NULL;
if (!self->handle)
{
PyErr_SetString(ALSAAudioError, "Mixer is closed");
return NULL;
}
handled = snd_mixer_handle_events(self->handle);
if (handled < 0)
{
PyErr_Format(ALSAAudioError, "%s [%s]", snd_strerror(handled),
self->cardname);
return NULL;
}
return PyLong_FromLong(handled);
}
PyDoc_STRVAR(handleevents_doc,
"handleevents() -> int\n\
\n\
Acknowledge events on the polldescriptors() file descriptors\n\
to prevent subsequent polls from returning the same events again.\n\
Returns the number of events that were acknowledged.");
static PyMethodDef alsamixer_methods[] = {
{"cardname", (PyCFunction)alsamixer_cardname, METH_VARARGS,
mixer_cardname_doc},
@@ -2133,10 +2299,13 @@ static PyMethodDef alsamixer_methods[] = {
{"getrec", (PyCFunction)alsamixer_getrec, METH_VARARGS, getrec_doc},
{"setvolume", (PyCFunction)alsamixer_setvolume, METH_VARARGS,
setvolume_doc},
{"setenum", (PyCFunction)alsamixer_setenum, METH_VARARGS, setenum_doc},
{"setmute", (PyCFunction)alsamixer_setmute, METH_VARARGS, setmute_doc},
{"setrec", (PyCFunction)alsamixer_setrec, METH_VARARGS, setrec_doc},
{"polldescriptors", (PyCFunction)alsamixer_polldescriptors, METH_VARARGS,
polldescriptors_doc},
{"handleevents", (PyCFunction)alsamixer_handleevents, METH_VARARGS,
handleevents_doc},
{NULL, NULL}
};
@@ -2201,6 +2370,8 @@ static PyTypeObject ALSAMixerType = {
/******************************************/
static PyMethodDef alsaaudio_methods[] = {
{ "card_indexes", (PyCFunction)alsacard_list_indexes, METH_VARARGS, card_indexes_doc},
{ "card_name", (PyCFunction)alsacard_name, METH_VARARGS, card_name_doc},
{ "cards", (PyCFunction)alsacard_list, METH_VARARGS, cards_doc},
{ "pcms", (PyCFunction)alsapcm_list, METH_VARARGS, pcms_doc},
{ "mixers", (PyCFunction)alsamixer_list, METH_VARARGS|METH_KEYWORDS, mixers_doc},
@@ -2308,6 +2479,18 @@ PyObject *PyInit_alsaaudio(void)
_EXPORT_INT(m, "PCM_FORMAT_MPEG",SND_PCM_FORMAT_MPEG);
_EXPORT_INT(m, "PCM_FORMAT_GSM",SND_PCM_FORMAT_GSM);
/* DSD sample formats are included in ALSA 1.0.29 and higher
* define OVERRIDE_DSD_COMPILE to include DSD sample support
* if you use a patched ALSA lib version
*/
#if SND_LIB_VERSION >= 0x1001d || defined OVERRIDE_DSD_COMPILE
_EXPORT_INT(m, "PCM_FORMAT_DSD_U8", SND_PCM_FORMAT_DSD_U8);
_EXPORT_INT(m, "PCM_FORMAT_DSD_U16_LE", SND_PCM_FORMAT_DSD_U16_LE);
_EXPORT_INT(m, "PCM_FORMAT_DSD_U32_LE", SND_PCM_FORMAT_DSD_U32_LE);
_EXPORT_INT(m, "PCM_FORMAT_DSD_U32_BE", SND_PCM_FORMAT_DSD_U32_BE);
#endif
/* Mixer stuff */
_EXPORT_INT(m, "MIXER_CHANNEL_ALL", MIXER_CHANNEL_ALL);

View File

@@ -13,7 +13,7 @@
import sys, os
sys.path.append('..')
sys.path.insert(0, '..')
from setup import pyalsa_version
# If your extensions are in another directory, add it here. If the directory
@@ -39,7 +39,7 @@ master_doc = 'index'
# General substitutions.
project = u'alsaaudio'
copyright = u'2008-2009, Casper Wilstrup, Lars Immisch'
copyright = u'2008-20017, Casper Wilstrup, Lars Immisch'
# The default replacements for |version| and |release|, also used in various
# other places throughout the built documents.

View File

@@ -476,6 +476,12 @@ Mixer objects have the following methods:
Returns a tuple of (file descriptor, eventmask) that can be used to
wait for changes on the mixer with *select.poll*.
.. 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.

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.

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):
@@ -113,6 +119,8 @@ if __name__ == '__main__':
else:
usage()
list_cards()
if not len(args):
list_mixers(kwargs)
elif len(args) == 1:

View File

@@ -21,17 +21,17 @@ 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()
@@ -39,7 +39,7 @@ if __name__ == '__main__':
f = open(args[0], 'rb')
# Open the device in playback mode.
out = alsaaudio.PCM(alsaaudio.PCM_PLAYBACK, card=card)
out = alsaaudio.PCM(alsaaudio.PCM_PLAYBACK, device=device)
# Set attributes: Mono, 44100 Hz, 16 bit little endian frames
out.setchannels(1)

View File

@@ -30,33 +30,35 @@ def play(device, f):
else:
raise ValueError('Unsupported format')
device.setperiodsize(320)
periodsize = f.getframerate() / 8
device.setperiodsize(periodsize)
data = f.readframes(320)
data = f.readframes(periodsize)
while data:
# Read data from stdin
device.write(data)
data = f.readframes(320)
data = f.readframes(periodsize)
def usage():
print('usage: playwav.py [-c <card>] <file>', file=sys.stderr)
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:')
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 = wave.open(args[0], 'rb')
device = alsaaudio.PCM(card=card)
device = alsaaudio.PCM(device=device)
play(device, f)

View File

@@ -4,7 +4,7 @@
##
## 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,17 +22,17 @@ import getopt
import alsaaudio
def usage():
print('usage: recordtest.py [-c <card>] <file>', file=sys.stderr)
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:')
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()
@@ -42,7 +42,7 @@ if __name__ == '__main__':
# 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)
inp = alsaaudio.PCM(alsaaudio.PCM_CAPTURE, alsaaudio.PCM_NONBLOCK, device=device)
# Set attributes: Mono, 44100 Hz, 16 bit little endian samples
inp.setchannels(1)

View File

@@ -4,19 +4,11 @@
It is fairly complete for PCM devices and Mixer access.
'''
from distutils.core import setup
from distutils.extension import Extension
from setuptools import setup
from setuptools.extension import Extension
from sys import version
pyalsa_version = '0.8.2'
# 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.8.4'
if __name__ == '__main__':
setup(

21
test.py
View File

@@ -44,16 +44,11 @@ class MixerTest(unittest.TestCase):
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 }
mixers = alsaaudio.mixers(**kwargs)
for c in alsaaudio.card_indexes():
mixers = alsaaudio.mixers(cardindex=c)
for m in mixers:
mixer = alsaaudio.Mixer(m, **kwargs)
mixer = alsaaudio.Mixer(m, cardindex=c)
mixer.close()
def testMixerAll(self):
@@ -90,14 +85,10 @@ class PCMTest(unittest.TestCase):
"""Test PCM objects"""
def testPCM(self):
"Open a PCM object on every device"
"Open a PCM object on every card"
for pd in alsaaudio.pcms():
pcm = alsaaudio.PCM(device=pd)
pcm.close()
for pd in alsaaudio.pcms(alsaaudio.PCM_CAPTURE):
pcm = alsaaudio.PCM(alsaaudio.PCM_CAPTURE, device=pd)
for c in alsaaudio.card_indexes():
pcm = alsaaudio.PCM(cardindex=c)
pcm.close()
def testPCMAll(self):