forked from auracaster/pyalsaaudio
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc51fa75b5 | ||
|
|
85ff47ad43 | ||
|
|
88f38284bb | ||
|
|
fe7561beea | ||
|
|
2314aaeb7e | ||
|
|
bf24ec65ca | ||
|
|
478d0559e6 | ||
|
|
891a30eb08 | ||
|
|
74d9e7d6e1 | ||
|
|
fa10bf6999 | ||
|
|
7de446c3c7 |
180
alsaaudio.c
180
alsaaudio.c
@@ -21,6 +21,7 @@
|
||||
#define PyLong_AS_LONG PyInt_AS_LONG
|
||||
#endif
|
||||
#include <alsa/asoundlib.h>
|
||||
#include <alsa/version.h>
|
||||
#include <stdio.h>
|
||||
|
||||
PyDoc_STRVAR(alsaaudio_module_doc,
|
||||
@@ -170,6 +171,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 = PyInt_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 +826,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 +1819,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 +2244,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 +2294,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 +2365,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 +2474,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);
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
12
playwav.py
12
playwav.py
@@ -40,23 +40,23 @@ def play(device, f):
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
14
setup.py
14
setup.py
@@ -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.3'
|
||||
|
||||
if __name__ == '__main__':
|
||||
setup(
|
||||
|
||||
21
test.py
21
test.py
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user