mirror of
https://github.com/larsimmisch/pyalsaaudio.git
synced 2026-06-01 10:57:01 +00:00
Add pcms function, unify arguments.
Also, fix some memory leaks in error cases
This commit is contained in:
+103
-54
@@ -69,28 +69,6 @@ typedef struct {
|
||||
snd_mixer_t *handle;
|
||||
} alsamixer_t;
|
||||
|
||||
/* Translate a card id to a ALSA cardname
|
||||
|
||||
Returns a newly allocated string.
|
||||
*/
|
||||
char *translate_cardname(char *name)
|
||||
{
|
||||
static char dflt[] = "default";
|
||||
char *full = NULL;
|
||||
|
||||
if (!name || !strcmp(name, dflt))
|
||||
return strdup(dflt);
|
||||
|
||||
// If we find a colon, we assume it is a real ALSA cardname
|
||||
if (strchr(name, ':'))
|
||||
return strdup(name);
|
||||
|
||||
full = malloc(strlen("default:CARD=") + strlen(name) + 1);
|
||||
sprintf(full, "default:CARD=%s", name);
|
||||
|
||||
return full;
|
||||
}
|
||||
|
||||
/******************************************/
|
||||
/* PCM object wrapper */
|
||||
/******************************************/
|
||||
@@ -152,6 +130,61 @@ PyDoc_STRVAR(cards_doc,
|
||||
\n\
|
||||
List the available card ids.");
|
||||
|
||||
static PyObject *
|
||||
alsapcm_list(PyObject *self, PyObject *args)
|
||||
{
|
||||
int pcmtype = SND_PCM_STREAM_PLAYBACK;
|
||||
PyObject *result = NULL;
|
||||
PyObject *item;
|
||||
void **hints, **n;
|
||||
char *name, *io;
|
||||
const char *filter;
|
||||
|
||||
if (!PyArg_ParseTuple(args,"|i:pcms", &pcmtype))
|
||||
return NULL;
|
||||
|
||||
if (pcmtype != SND_PCM_STREAM_PLAYBACK &&
|
||||
pcmtype != SND_PCM_STREAM_CAPTURE)
|
||||
{
|
||||
PyErr_SetString(ALSAAudioError, "PCM type must be PCM_PLAYBACK (0) "
|
||||
"or PCM_CAPTURE (1)");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
result = PyList_New(0);
|
||||
|
||||
if (snd_device_name_hint(-1, "pcm", &hints) < 0)
|
||||
return result;
|
||||
|
||||
n = hints;
|
||||
filter = pcmtype == SND_PCM_STREAM_CAPTURE ? "Input" : "Output";
|
||||
while (*n != NULL) {
|
||||
name = snd_device_name_get_hint(*n, "NAME");
|
||||
io = snd_device_name_get_hint(*n, "IOID");
|
||||
if (io != NULL && strcmp(io, filter) != 0)
|
||||
goto __end;
|
||||
|
||||
item = PyUnicode_FromString(name);
|
||||
PyList_Append(result, item);
|
||||
Py_DECREF(item);
|
||||
|
||||
__end:
|
||||
if (name != NULL)
|
||||
free(name);
|
||||
if (io != NULL)
|
||||
free(io);
|
||||
n++;
|
||||
}
|
||||
snd_device_name_free_hint(hints);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(pcms_doc,
|
||||
"pcms([type])\n\
|
||||
\n\
|
||||
List the available PCM devices");
|
||||
|
||||
static int alsapcm_setup(alsapcm_t *self)
|
||||
{
|
||||
int res,dir;
|
||||
@@ -210,12 +243,25 @@ alsapcm_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||
alsapcm_t *self;
|
||||
int pcmtype = SND_PCM_STREAM_PLAYBACK;
|
||||
int pcmmode = 0;
|
||||
char *kw[] = { "type", "mode", "card", NULL };
|
||||
char *cardname = NULL;
|
||||
char *device = "default";
|
||||
int cardidx = -1;
|
||||
char hw_device[32];
|
||||
char *kw[] = { "type", "mode", "device", "cardindex", NULL };
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iiz", kw,
|
||||
&pcmtype, &pcmmode, &cardname))
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iisi", kw,
|
||||
&pcmtype, &pcmmode, &device, &cardidx))
|
||||
return NULL;
|
||||
|
||||
if (cardidx >= 0) {
|
||||
if (cardidx < 32) {
|
||||
snprintf(hw_device, sizeof(hw_device), "hw:%d", cardidx);
|
||||
device = hw_device;
|
||||
}
|
||||
else {
|
||||
PyErr_SetString(ALSAAudioError, "Invalid card number");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(self = (alsapcm_t *)PyObject_New(alsapcm_t, &ALSAPCMType)))
|
||||
return NULL;
|
||||
@@ -234,21 +280,22 @@ alsapcm_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||
self->handle = 0;
|
||||
self->pcmtype = pcmtype;
|
||||
self->pcmmode = pcmmode;
|
||||
self->cardname = translate_cardname(cardname);
|
||||
self->channels = 2;
|
||||
self->rate = 44100;
|
||||
self->format = SND_PCM_FORMAT_S16_LE;
|
||||
self->periodsize = 32;
|
||||
|
||||
res = snd_pcm_open(&(self->handle), self->cardname, self->pcmtype,
|
||||
|
||||
res = snd_pcm_open(&(self->handle), device, self->pcmtype,
|
||||
self->pcmmode);
|
||||
|
||||
if (res >= 0)
|
||||
|
||||
if (res >= 0) {
|
||||
res = alsapcm_setup(self);
|
||||
|
||||
if (res < 0)
|
||||
{
|
||||
if (self->handle)
|
||||
}
|
||||
if (res >= 0) {
|
||||
self->cardname = strdup(device);
|
||||
}
|
||||
else {
|
||||
if (self->handle)
|
||||
{
|
||||
snd_pcm_close(self->handle);
|
||||
self->handle = 0;
|
||||
@@ -933,21 +980,21 @@ alsamixer_list(PyObject *self, PyObject *args, PyObject *kwds)
|
||||
char *device = "default";
|
||||
PyObject *result;
|
||||
|
||||
char *kw[] = { "cardindex", "device", NULL };
|
||||
char *kw[] = { "device", "cardindex", NULL };
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|is", kw,
|
||||
&cardidx, &device))
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|si", kw,
|
||||
&device, &cardidx))
|
||||
return NULL;
|
||||
|
||||
if (cardidx >= 0) {
|
||||
if (cardidx >= 0 && cardidx < 32) {
|
||||
if (cardidx < 32) {
|
||||
snprintf(hw_device, sizeof(hw_device), "hw:%d", cardidx);
|
||||
device = hw_device;
|
||||
}
|
||||
else {
|
||||
PyErr_SetString(ALSAAudioError, "Invalid card number");
|
||||
return NULL;
|
||||
}
|
||||
device = hw_device;
|
||||
}
|
||||
|
||||
snd_mixer_selem_id_alloca(&sid);
|
||||
@@ -1006,37 +1053,36 @@ alsamixer_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||
int id = 0;
|
||||
snd_mixer_elem_t *elem;
|
||||
int channel;
|
||||
char *kw[] = { "control", "id", "cardindex", "device", NULL };
|
||||
char *kw[] = { "control", "id", "device", "cardindex", NULL };
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|siis", kw,
|
||||
&control, &id, &cardidx, &device))
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|sisi", kw,
|
||||
&control, &id, &device, &cardidx))
|
||||
return NULL;
|
||||
|
||||
if (cardidx >= 0) {
|
||||
if (cardidx >= 0 && cardidx < 32) {
|
||||
if (cardidx < 32) {
|
||||
snprintf(hw_device, sizeof(hw_device), "hw:%d", cardidx);
|
||||
device = hw_device;
|
||||
}
|
||||
else {
|
||||
PyErr_SetString(ALSAAudioError, "Invalid card number");
|
||||
return NULL;
|
||||
}
|
||||
device = hw_device;
|
||||
}
|
||||
|
||||
if (!(self = (alsamixer_t *)PyObject_New(alsamixer_t, &ALSAMixerType)))
|
||||
return NULL;
|
||||
|
||||
self->handle = 0;
|
||||
self->cardname = strdup(device);
|
||||
|
||||
err = alsamixer_gethandle(self->cardname, &self->handle);
|
||||
if (err<0)
|
||||
err = alsamixer_gethandle(device, &self->handle);
|
||||
if (err < 0)
|
||||
{
|
||||
PyErr_SetString(ALSAAudioError,snd_strerror(err));
|
||||
free(self->cardname);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
self->cardname = strdup(device);
|
||||
self->controlname = strdup(control);
|
||||
self->controlid = id;
|
||||
|
||||
@@ -1044,13 +1090,15 @@ alsamixer_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||
if (!elem)
|
||||
{
|
||||
char errtext[128];
|
||||
sprintf(errtext,"Unable to find mixer control '%s',%i on card '%s'",
|
||||
self->controlname,
|
||||
self->controlid,
|
||||
self->cardname);
|
||||
snprintf(errtext, sizeof(errtext),
|
||||
"Unable to find mixer control '%s',%i on card '%s'",
|
||||
self->controlname,
|
||||
self->controlid,
|
||||
self->cardname);
|
||||
snd_mixer_close(self->handle);
|
||||
PyErr_SetString(ALSAAudioError,errtext);
|
||||
free(self->cardname);
|
||||
free(self->controlname);
|
||||
return NULL;
|
||||
}
|
||||
/* Determine mixer capabilities */
|
||||
@@ -2074,6 +2122,7 @@ static PyTypeObject ALSAMixerType = {
|
||||
|
||||
static PyMethodDef alsaaudio_methods[] = {
|
||||
{ "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},
|
||||
{ 0, 0 },
|
||||
};
|
||||
|
||||
+18
-9
@@ -34,34 +34,43 @@ The :mod:`alsaaudio` module defines functions and classes for using ALSA.
|
||||
.. % should be enclosed in \var{...}.
|
||||
|
||||
|
||||
.. function:: pcms([type=PCM_PLAYBACK])
|
||||
|
||||
List available PCM objects by name (the suitable for PCM objects).
|
||||
|
||||
.. function:: cards()
|
||||
|
||||
List the available cards by name (suitable for PCM objects).
|
||||
List the available cards by name.
|
||||
|
||||
.. function:: mixers(cardindex=-1, device='default')
|
||||
.. function:: mixers(device='default', cardindex=-1)
|
||||
|
||||
List the available mixers. The arguments are:
|
||||
|
||||
*cardindex* - specifies which card should be used [#f3]_. If this argument
|
||||
* *device* - the name of the device on which the mixer resides. The default
|
||||
is 'default'.
|
||||
|
||||
* *cardindex* - the card index [#f3]_. If this argument
|
||||
is given, the device name is constructed like this: `hw:<cardindex>` and
|
||||
the `device` keyword argument is ignored. 0 is the
|
||||
first sound card.
|
||||
|
||||
*device* - the name of the device on which the mixer resides. The default is
|
||||
'default'.
|
||||
first hardware sound card.
|
||||
|
||||
**Note:** The arguments for this function were extended in
|
||||
version 0.8. The keyword argument `device` is new and can be used to select
|
||||
virtual devices.
|
||||
|
||||
.. class:: PCM(type=PCM_PLAYBACK, mode=PCM_NORMAL, card='default')
|
||||
.. class:: PCM(type=PCM_PLAYBACK, mode=PCM_NORMAL, device='default',
|
||||
cardindex=-1)
|
||||
|
||||
This class is used to represent a PCM device (both for playback and
|
||||
recording - capture). The arguments are:
|
||||
|
||||
* *type* - can be either ``PCM_CAPTURE`` or ``PCM_PLAYBACK`` (default).
|
||||
* *mode* - can be either ``PCM_NONBLOCK``, or ``PCM_NORMAL`` (default).
|
||||
* *card* - specifies the name of the card that should be used.
|
||||
* *device* - the name of the PCM device that should be used.
|
||||
* *cardindex* - the card index [#f3]_. If this argument
|
||||
is given, the device name is constructed like this: `hw:<cardindex>` and
|
||||
the `device` keyword argument is ignored. 0 is the
|
||||
first hardware sound card.
|
||||
|
||||
.. class:: Mixer(control='Master', id=0, cardindex=-1, device='default')
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ class MixerTest(unittest.TestCase):
|
||||
def testMixer(self):
|
||||
"""Open the default Mixers and the Mixers on every card"""
|
||||
|
||||
for d in ['default'] + range(len(alsaaudio.cards())):
|
||||
for d in ['default'] + list(range(len(alsaaudio.cards()))):
|
||||
if type(d) == type(0):
|
||||
kwargs = { 'cardindex': d }
|
||||
else:
|
||||
@@ -90,10 +90,14 @@ class PCMTest(unittest.TestCase):
|
||||
"""Test PCM objects"""
|
||||
|
||||
def testPCM(self):
|
||||
"Open a PCM object on every card"
|
||||
"Open a PCM object on every device"
|
||||
|
||||
for i in range(len(alsaaudio.cards())):
|
||||
pcm = alsaaudio.PCM(i)
|
||||
for device in alsaaudio.pcms():
|
||||
pcm = alsaaudio.PCM(device=device)
|
||||
pcm.close()
|
||||
|
||||
for device in alsaaudio.pcms(alsaaudio.PCM_CAPTURE):
|
||||
pcm = alsaaudio.PCM(alsaaudio.PCM_CAPTURE, device=device)
|
||||
pcm.close()
|
||||
|
||||
def testPCMAll(self):
|
||||
|
||||
Reference in New Issue
Block a user