diff --git a/TODO b/TODO index e12a0b5..436bb6f 100644 --- a/TODO +++ b/TODO @@ -1,3 +1,5 @@ - Implement the Mixer API - +- Write documentation +- Better example code (aplay workalike for instance) - Implement MIDI/sequencer support. + diff --git a/alsaaudio.c b/alsaaudio.c index 6086c43..c5ba214 100644 --- a/alsaaudio.c +++ b/alsaaudio.c @@ -1,14 +1,11 @@ /* * alsaaudio -- Python interface to ALSA (Advanced Linux Sound Architecture). - * The standard audio API for Linux since kernel 2.0 + * The standard audio API for Linux since kernel 2.6 * * Contributed by Unispeed A/S (http://www.unispeed.com) - * Primary author: Casper Wilstup (cwi@unispeed.dk) + * Author: Casper Wilstup (cwi@unispeed.dk) * - * - * (c) 2004 by Unispeed A/S. All rights reserved. - * - * License: CRNI's Python 1.6 license (or any later version of the same) + * License: Python Software Foundation License * */ @@ -21,7 +18,7 @@ typedef struct { PyObject_HEAD; int pcmtype; int pcmmode; - char *pcmname; + char *cardname; snd_pcm_t *handle; @@ -34,10 +31,33 @@ typedef struct { } alsapcm_t; -static PyTypeObject ALSAAudioType; +typedef struct { + PyObject_HEAD; + + /* Mixer identification */ + char *cardname; + char *controlname; + int controlid; + + /* Capabilities */ + unsigned int volume_cap; + unsigned int switch_cap; + unsigned int pchannels; + unsigned int cchannels; + + snd_mixer_t *handle; + +} alsamixer_t; static PyObject *ALSAAudioError; + +/******************************************/ +/* PCM object wrapper */ +/******************************************/ + +static PyTypeObject ALSAPCMType; + static int alsapcm_setup(alsapcm_t *self) { int res,dir; unsigned int val; @@ -48,7 +68,7 @@ static int alsapcm_setup(alsapcm_t *self) { snd_pcm_close(self->handle); self->handle = 0; } - res = snd_pcm_open(&(self->handle),self->pcmname,self->pcmtype,self->pcmmode); + res = snd_pcm_open(&(self->handle),self->cardname,self->pcmtype,self->pcmmode); if (res < 0) return res; snd_pcm_hw_params_alloca(&hwparams); @@ -73,16 +93,15 @@ static int alsapcm_setup(alsapcm_t *self) { return res; } -/* ALSA PCM Object members */ -static alsapcm_t * -newalsapcmobject(PyObject *args) { +static PyObject * +alsapcm_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { int res; alsapcm_t *self; int pcmtype=0; int pcmmode=0; - char *pcmname = "default"; - if (!PyArg_ParseTuple(args,"|iis",&pcmtype,&pcmmode,&pcmname)) return NULL; - if (!(self = (alsapcm_t *)PyObject_New(struct alsa_audio, &ALSAAudioType))) return NULL; + char *cardname = "default"; + if (!PyArg_ParseTuple(args,"|iis",&pcmtype,&pcmmode,&cardname)) return NULL; + if (!(self = (alsapcm_t *)PyObject_New(alsapcm_t, &ALSAPCMType))) 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_CAPTUPE (1)"); @@ -94,7 +113,7 @@ newalsapcmobject(PyObject *args) { } self->pcmtype = pcmtype; self->pcmmode = pcmmode; - self->pcmname = strdup(pcmname); + self->cardname = strdup(cardname); self->channels = 2; self->rate = 44100; @@ -111,7 +130,7 @@ newalsapcmobject(PyObject *args) { PyErr_SetString(ALSAAudioError, snd_strerror(res)); return NULL; } - return self; + return (PyObject *)self; } static void alsapcm_dealloc(alsapcm_t *self) { @@ -119,6 +138,7 @@ static void alsapcm_dealloc(alsapcm_t *self) { snd_pcm_drain(self->handle); snd_pcm_close(self->handle); } + free(self->cardname); PyObject_Del(self); } @@ -228,9 +248,9 @@ alsapcm_pcmmode(alsapcm_t *self, PyObject *args) { } static PyObject * -alsapcm_pcmname(alsapcm_t *self, PyObject *args) { +alsapcm_cardname(alsapcm_t *self, PyObject *args) { if (!PyArg_ParseTuple(args,"")) return NULL; - return PyString_FromString(self->pcmname); + return PyString_FromString(self->cardname); } static PyObject * @@ -350,20 +370,13 @@ static PyObject *alsapcm_write(alsapcm_t *self, PyObject *args) { return PyInt_FromLong(datalen); } -/* Module functions */ - -static PyObject * -alsa_openpcm(PyObject *self, PyObject *args) { - return (PyObject *)newalsapcmobject(args); -} - /* ALSA PCM Object Bureaucracy */ static PyMethodDef alsapcm_methods[] = { {"pcmtype", (PyCFunction)alsapcm_pcmtype, METH_VARARGS}, {"pcmmode", (PyCFunction)alsapcm_pcmmode, METH_VARARGS}, - {"pcmname", (PyCFunction)alsapcm_pcmname, METH_VARARGS}, + {"cardname", (PyCFunction)alsapcm_cardname, METH_VARARGS}, {"setchannels", (PyCFunction)alsapcm_setchannels, METH_VARARGS}, {"setrate", (PyCFunction)alsapcm_setrate, METH_VARARGS}, {"setformat", (PyCFunction)alsapcm_setformat, METH_VARARGS}, @@ -382,45 +395,528 @@ alsapcm_getattr(alsapcm_t *self, char *name) { return Py_FindMethod(alsapcm_methods, (PyObject *)self, name); } -static PyTypeObject ALSAAudioType = { +static PyTypeObject ALSAPCMType = { PyObject_HEAD_INIT(&PyType_Type) - 0, - "alsaaudio.pcm", - sizeof(alsapcm_t), - 0, - /* methods */ - (destructor) alsapcm_dealloc, - 0, - (getattrfunc)alsapcm_getattr, - 0, - 0, - 0, + 0, /*ob_size*/ + "alsaaudio.pcm", /*tp_name*/ + sizeof(alsapcm_t), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor) alsapcm_dealloc, /*tp_dealloc*/ + 0, /*print*/ + (getattrfunc)alsapcm_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + "ALSA PCM device", /*tp_doc*/ }; +/******************************************/ +/* Mixer object wrapper */ +/******************************************/ -/* Initialization */ +static PyTypeObject ALSAMixerType; + +#define MIXER_CAP_VOLUME 0x0001 +#define MIXER_CAP_VOLUME_JOINED 0x0002 +#define MIXER_CAP_PVOLUME 0x0004 +#define MIXER_CAP_PVOLUME_JOINED 0x0008 +#define MIXER_CAP_CVOLUME 0x0010 +#define MIXER_CAP_CVOLUME_JOINED 0x0020 + +#define MIXER_CAP_SWITCH 0x0001 +#define MIXER_CAP_SWITCH_JOINED 0x0002 +#define MIXER_CAP_PSWITCH 0x0004 +#define MIXER_CAP_PSWITCH_JOINED 0x0008 +#define MIXER_CAP_CSWITCH 0x0010 +#define MIXER_CAP_CSWITCH_JOINED 0x0020 +#define MIXER_CAP_CSWITCH_EXCLUSIVE 0x0040 + +#define MIXER_CHANNEL_ALL -1 + +int +alsamixer_gethandle(char *cardname, snd_mixer_t **handle) { + int err; + if ((err = snd_mixer_open(handle, 0)) < 0) return err; + if ((err = snd_mixer_attach(*handle, cardname)) < 0) return err; + if ((err = snd_mixer_selem_register(*handle, NULL, NULL)) < 0) return err; + if ((err = snd_mixer_load(*handle)) < 0) return err; + + return 0; +} + +static PyObject * +alsamixer_list(PyObject *self, PyObject *args) { + snd_mixer_t *handle; + snd_mixer_selem_id_t *sid; + snd_mixer_elem_t *elem; + int err; + char *cardname = "default"; + PyObject *result = PyList_New(0); + + if (!PyArg_ParseTuple(args,"|s",&cardname)) return NULL; + + snd_mixer_selem_id_alloca(&sid); + err = alsamixer_gethandle(cardname,&handle); + if (err < 0) { + PyErr_SetString(ALSAAudioError,snd_strerror(err)); + snd_mixer_close(handle); + return NULL; + } + for (elem = snd_mixer_first_elem(handle); elem; elem = snd_mixer_elem_next(elem)) { + PyObject *mixer; + snd_mixer_selem_get_id(elem, sid); + mixer = PyString_FromString(snd_mixer_selem_id_get_name(sid)); + PyList_Append(result,mixer); + Py_DECREF(mixer); + } + snd_mixer_close(handle); + + return result; +} + +static snd_mixer_elem_t * +alsamixer_find_elem(snd_mixer_t *handle, char *control, int id) { + snd_mixer_selem_id_t *sid; + + snd_mixer_selem_id_alloca(&sid); + snd_mixer_selem_id_set_index(sid, id); + snd_mixer_selem_id_set_name(sid, control); + return snd_mixer_find_selem(handle, sid); +} + +static PyObject * +alsamixer_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { + alsamixer_t *self; + int err; + char *cardname = "default"; + char *control = "Master"; + int id = 0; + snd_mixer_elem_t *elem; + int channel; + + if (!PyArg_ParseTuple(args,"|sis",&control,&id,&cardname)) return NULL; + if (!(self = (alsamixer_t *)PyObject_New(alsamixer_t, &ALSAMixerType))) return NULL; + + err = alsamixer_gethandle(cardname,&self->handle); + if (err<0) { + PyErr_SetString(ALSAAudioError,snd_strerror(err)); + return NULL; + } + self->cardname = strdup(cardname); + self->controlname = strdup(control); + self->controlid = id; + + elem = alsamixer_find_elem(self->handle,control,id); + if (!elem) { + char errtext[128]; + sprintf(errtext,"Unable to find mixer control '%s',%i",self->controlname,self->controlid); + snd_mixer_close(self->handle); + PyErr_SetString(ALSAAudioError,errtext); + return NULL; + } + /* Determine mixer capabilities */ + self->volume_cap = self->switch_cap = 0; + if (snd_mixer_selem_has_common_volume(elem)) { + self->volume_cap |= MIXER_CAP_VOLUME; + if (snd_mixer_selem_has_playback_volume_joined(elem)) self->volume_cap |= MIXER_CAP_VOLUME_JOINED; + } + else { + if (snd_mixer_selem_has_playback_volume(elem)) { + self->volume_cap |= MIXER_CAP_PVOLUME; + if (snd_mixer_selem_has_playback_volume_joined(elem)) self->volume_cap |= MIXER_CAP_PVOLUME_JOINED; + } + if (snd_mixer_selem_has_capture_volume(elem)) { + self->volume_cap |= MIXER_CAP_CVOLUME; + if (snd_mixer_selem_has_capture_volume_joined(elem)) self->volume_cap |= MIXER_CAP_CVOLUME_JOINED; + } + } + + if (snd_mixer_selem_has_common_switch(elem)) { + self->switch_cap |= MIXER_CAP_SWITCH; + if (snd_mixer_selem_has_playback_switch_joined(elem)) self->switch_cap |= MIXER_CAP_SWITCH_JOINED; + } + else { + if (snd_mixer_selem_has_playback_switch(elem)) { + self->switch_cap |= MIXER_CAP_PSWITCH; + if (snd_mixer_selem_has_playback_switch_joined(elem)) self->switch_cap |= MIXER_CAP_PSWITCH_JOINED; + } + if (snd_mixer_selem_has_capture_switch(elem)) { + self->switch_cap |= MIXER_CAP_CSWITCH; + if (snd_mixer_selem_has_capture_switch_joined(elem)) self->switch_cap |= MIXER_CAP_CSWITCH_JOINED; + if (snd_mixer_selem_has_capture_switch_exclusive(elem)) self->switch_cap |= MIXER_CAP_CSWITCH_EXCLUSIVE; + } + } + self->pchannels = 0; + if (self->volume_cap | MIXER_CAP_PVOLUME || self->switch_cap | MIXER_CAP_PSWITCH) { + if (snd_mixer_selem_is_playback_mono(elem)) self->pchannels = 1; + else { + for (channel=0; channel <= SND_MIXER_SCHN_LAST; channel++) { + if (snd_mixer_selem_has_playback_channel(elem, channel)) self->pchannels++; + else break; + } + } + } + self->cchannels = 0; + if (self->volume_cap | MIXER_CAP_CVOLUME || self->switch_cap | MIXER_CAP_CSWITCH) { + if (snd_mixer_selem_is_capture_mono(elem)) self->cchannels = 1; + else { + for (channel=0; channel <= SND_MIXER_SCHN_LAST; channel++) { + if (snd_mixer_selem_has_capture_channel(elem, channel)) self->cchannels++; + else break; + } + } + } + return (PyObject *)self; +} + +static void alsamixer_dealloc(alsamixer_t *self) { + if (self->handle) { + snd_mixer_close(self->handle); + free(self->cardname); + free(self->controlname); + self->handle = 0; + } + PyObject_Del(self); +} + +static PyObject * +alsamixer_cardname(alsamixer_t *self, PyObject *args) { + if (!PyArg_ParseTuple(args,"")) return NULL; + return PyString_FromString(self->cardname); +} + +static PyObject * +alsamixer_mixer(alsamixer_t *self, PyObject *args) { + if (!PyArg_ParseTuple(args,"")) return NULL; + return PyString_FromString(self->controlname); +} + +static PyObject * +alsamixer_mixerid(alsamixer_t *self, PyObject *args) { + if (!PyArg_ParseTuple(args,"")) return NULL; + return PyInt_FromLong(self->controlid); +} + +static PyObject * +alsamixer_volumecap(alsamixer_t *self, PyObject *args) { + PyObject *result; + if (!PyArg_ParseTuple(args,"")) return NULL; + result = PyList_New(0); + if (self->volume_cap&MIXER_CAP_VOLUME) + PyList_Append(result,PyString_FromString("Volume")); + if (self->volume_cap&MIXER_CAP_VOLUME_JOINED) + PyList_Append(result,PyString_FromString("Joined Volume")); + if (self->volume_cap&MIXER_CAP_PVOLUME) + PyList_Append(result,PyString_FromString("Playback Volume")); + if (self->volume_cap&MIXER_CAP_PVOLUME_JOINED) + PyList_Append(result,PyString_FromString("Joined Playback Volume")); + if (self->volume_cap&MIXER_CAP_CVOLUME) + PyList_Append(result,PyString_FromString("Capture Volume")); + if (self->volume_cap&MIXER_CAP_CVOLUME_JOINED) + PyList_Append(result,PyString_FromString("Joined Capture Volume")); + + return result; +} +static PyObject * +alsamixer_switchcap(alsamixer_t *self, PyObject *args) { + PyObject *result; + if (!PyArg_ParseTuple(args,"")) return NULL; + result = PyList_New(0); + if (self->volume_cap&MIXER_CAP_SWITCH) + PyList_Append(result,PyString_FromString("Mute")); + if (self->volume_cap&MIXER_CAP_SWITCH_JOINED) + PyList_Append(result,PyString_FromString("Joined Mute")); + if (self->volume_cap&MIXER_CAP_PSWITCH) + PyList_Append(result,PyString_FromString("Playback Mute")); + if (self->volume_cap&MIXER_CAP_PSWITCH_JOINED) + PyList_Append(result,PyString_FromString("Joined Playback Mute")); + if (self->volume_cap&MIXER_CAP_CSWITCH) + PyList_Append(result,PyString_FromString("Capture Mute")); + if (self->volume_cap&MIXER_CAP_CSWITCH_JOINED) + PyList_Append(result,PyString_FromString("Joined Capture Mute")); + if (self->volume_cap&MIXER_CAP_CSWITCH_EXCLUSIVE) + PyList_Append(result,PyString_FromString("Capture Exclusive")); + return result; +} + +static PyObject * +alsamixer_getvolume(alsamixer_t *self, PyObject *args) { + snd_mixer_elem_t *elem; + int direction; + int channel; + long ival; + char *dirstr = 0; + PyObject *result; + + if (!PyArg_ParseTuple(args,"|s",&dirstr)) return NULL; + + elem = alsamixer_find_elem(self->handle,self->controlname,self->controlid); + + if (!dirstr) { + if (self->pchannels) direction = 0; + else direction = 1; + } + else if (strcasecmp(dirstr,"playback")==0) direction = 0; + else if (strcasecmp(dirstr,"capture")==0) direction = 1; + else { + PyErr_SetString(ALSAAudioError,"Invalid direction argument for mixer"); + return NULL; + } + result = PyList_New(0); + for (channel = 0; channel <= SND_MIXER_SCHN_LAST; channel++) { + if (direction == 0 && snd_mixer_selem_has_playback_channel(elem, channel)) { + snd_mixer_selem_get_playback_volume(elem, channel, &ival); + PyList_Append(result,PyInt_FromLong(ival)); + } + else if (direction == 1 && snd_mixer_selem_has_capture_channel(elem, channel) + && snd_mixer_selem_has_capture_volume(elem)) { + snd_mixer_selem_get_capture_volume(elem, channel, &ival); + PyList_Append(result,PyInt_FromLong(ival)); + } + } + return result; +} + +static PyObject * +alsamixer_getmute(alsamixer_t *self, PyObject *args) { + snd_mixer_elem_t *elem; + int i; + int ival; + PyObject *result; + if (!PyArg_ParseTuple(args,"")) return NULL; + + elem = alsamixer_find_elem(self->handle,self->controlname,self->controlid); + if (!snd_mixer_selem_has_playback_switch(elem)) { + PyErr_SetString(ALSAAudioError,"Mixer has no mute switch"); + return NULL; + } + result = PyList_New(0); + for (i = 0; i <= SND_MIXER_SCHN_LAST; i++) { + if (snd_mixer_selem_has_playback_channel(elem, i)) { + snd_mixer_selem_get_playback_switch(elem, i, &ival); + PyList_Append(result,PyInt_FromLong(!ival)); + } + } + return result; +} + +static PyObject * +alsamixer_getrec(alsamixer_t *self, PyObject *args) { + snd_mixer_elem_t *elem; + int i; + int ival; + PyObject *result; + if (!PyArg_ParseTuple(args,"")) return NULL; + + elem = alsamixer_find_elem(self->handle,self->controlname,self->controlid); + if (!snd_mixer_selem_has_capture_switch(elem)) { + PyErr_SetString(ALSAAudioError,"Mixer has no mute switch"); + return NULL; + } + result = PyList_New(0); + for (i = 0; i <= SND_MIXER_SCHN_LAST; i++) { + if (snd_mixer_selem_has_capture_channel(elem, i)) { + snd_mixer_selem_get_capture_switch(elem, i, &ival); + PyList_Append(result,PyInt_FromLong(!ival)); + } + } + return result; +} + +static PyObject * +alsamixer_setvolume(alsamixer_t *self, PyObject *args) { + snd_mixer_elem_t *elem; + int direction; + int i; + int volume; + char *dirstr = 0; + int channel = MIXER_CHANNEL_ALL; + int done = 0; + + if (!PyArg_ParseTuple(args,"i|is",&volume,&channel,&dirstr)) return NULL; + + elem = alsamixer_find_elem(self->handle,self->controlname,self->controlid); + + if (!dirstr) { + if (self->pchannels) direction = 0; + else direction = 1; + } + else if (strcasecmp(dirstr,"playback")==0) direction = 0; + else if (strcasecmp(dirstr,"capture")==0) direction = 1; + else { + PyErr_SetString(ALSAAudioError,"Invalid direction argument. Use 'playback' or 'capture'"); + return NULL; + } + for (i = 0; i <= SND_MIXER_SCHN_LAST; i++) { + if (channel == -1 || channel == i) { + if (direction == 0 && snd_mixer_selem_has_playback_channel(elem, i)) { + snd_mixer_selem_set_playback_volume(elem, i, volume); + done++; + } + else if (direction == 1 && snd_mixer_selem_has_capture_channel(elem, channel) + && snd_mixer_selem_has_capture_volume(elem)) { + snd_mixer_selem_set_capture_volume(elem, i, volume); + done++; + } + } + } + if(!done) { + PyErr_SetString(ALSAAudioError,"No such channel"); + return NULL; + } + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +alsamixer_setmute(alsamixer_t *self, PyObject *args) { + snd_mixer_elem_t *elem; + int i; + int mute = 0; + int done = 0; + int channel = MIXER_CHANNEL_ALL; + if (!PyArg_ParseTuple(args,"i|i",&mute,&channel)) return NULL; + + elem = alsamixer_find_elem(self->handle,self->controlname,self->controlid); + if (!snd_mixer_selem_has_playback_switch(elem)) { + PyErr_SetString(ALSAAudioError,"Mixer has no mute switch"); + return NULL; + } + for (i = 0; i <= SND_MIXER_SCHN_LAST; i++) { + if (channel == MIXER_CHANNEL_ALL || channel == i) { + if (snd_mixer_selem_has_playback_channel(elem, i)) { + snd_mixer_selem_set_playback_switch(elem, i, !mute); + done++; + } + } + } + if (!done) { + PyErr_SetString(ALSAAudioError,"Invalid channel number"); + return NULL; + } + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +alsamixer_setrec(alsamixer_t *self, PyObject *args) { + snd_mixer_elem_t *elem; + int i; + int rec = 0; + int done = 0; + int channel = MIXER_CHANNEL_ALL; + if (!PyArg_ParseTuple(args,"i|i",&rec,&channel)) return NULL; + + elem = alsamixer_find_elem(self->handle,self->controlname,self->controlid); + if (!snd_mixer_selem_has_capture_switch(elem)) { + PyErr_SetString(ALSAAudioError,"Mixer has no record switch"); + return NULL; + } + for (i = 0; i <= SND_MIXER_SCHN_LAST; i++) { + if (channel == MIXER_CHANNEL_ALL || channel == i) { + if (snd_mixer_selem_has_capture_channel(elem, i)) { + snd_mixer_selem_set_playback_switch(elem, i, rec); + done++; + } + } + } + if (!done) { + PyErr_SetString(ALSAAudioError,"Invalid channel number"); + return NULL; + } + Py_INCREF(Py_None); + return Py_None; +} + +static PyMethodDef alsamixer_methods[] = { + {"cardname", (PyCFunction)alsamixer_cardname, METH_VARARGS}, + {"mixer", (PyCFunction)alsamixer_mixer, METH_VARARGS}, + {"mixerid", (PyCFunction)alsamixer_mixerid, METH_VARARGS}, + {"switchcap", (PyCFunction)alsamixer_switchcap, METH_VARARGS}, + {"volumecap", (PyCFunction)alsamixer_volumecap, METH_VARARGS}, + {"getvolume", (PyCFunction)alsamixer_getvolume, METH_VARARGS}, + {"getmute", (PyCFunction)alsamixer_getmute, METH_VARARGS}, + {"getrec", (PyCFunction)alsamixer_getrec, METH_VARARGS}, + {"setvolume", (PyCFunction)alsamixer_setvolume, METH_VARARGS}, + {"setmute", (PyCFunction)alsamixer_setmute, METH_VARARGS}, + {"setrec", (PyCFunction)alsamixer_setrec, METH_VARARGS}, + {NULL, NULL} +}; + + +static PyObject * +alsamixer_getattr(alsapcm_t *self, char *name) { + return Py_FindMethod(alsamixer_methods, (PyObject *)self, name); +} + +static PyTypeObject ALSAMixerType = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /*ob_size*/ + "alsaaudio.mixer", /*tp_name*/ + sizeof(alsamixer_t), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor) alsamixer_dealloc, /*tp_dealloc*/ + 0, /*print*/ + (getattrfunc)alsamixer_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + "ALSA Mixer Control", /*tp_doc*/ +}; + + +/******************************************/ +/* Module initialization */ +/******************************************/ static PyMethodDef alsaaudio_methods[] = { - { "openpcm", alsa_openpcm, METH_VARARGS }, + { "mixers", alsamixer_list, METH_VARARGS }, { 0, 0 }, }; -#define _EXPORT_CONSTANT(mod, name) \ - if (PyModule_AddIntConstant(mod, #name, (long) (name)) == -1) return; #define _EXPORT_INT(mod, name, value) \ if (PyModule_AddIntConstant(mod, name, (long) value) == -1) return; void initalsaaudio(void) { PyObject *m; + ALSAPCMType.tp_new = alsapcm_new; + ALSAMixerType.tp_new = alsamixer_new; m = Py_InitModule("alsaaudio",alsaaudio_methods); ALSAAudioError = PyErr_NewException("alsaaudio.ALSAAudioError", NULL, NULL); if (ALSAAudioError) { /* Each call to PyModule_AddObject decrefs it; compensate: */ + + Py_INCREF(&ALSAPCMType); + PyModule_AddObject(m,"PCM",(PyObject *)&ALSAPCMType); + + Py_INCREF(&ALSAMixerType); + PyModule_AddObject(m,"Mixer",(PyObject *)&ALSAMixerType); + Py_INCREF(ALSAAudioError); - Py_INCREF(ALSAAudioError); - PyModule_AddObject(m, "error", ALSAAudioError); PyModule_AddObject(m, "ALSAAudioError", ALSAAudioError); } diff --git a/playbacktest.py b/playbacktest.py index acc86f0..61218a6 100644 --- a/playbacktest.py +++ b/playbacktest.py @@ -15,7 +15,7 @@ import sys import time # Open the device in playback mode. -out = alsaaudio.openpcm(alsaaudio.PCM_PLAYBACK) +out = alsaaudio.PCM(alsaaudio.PCM_PLAYBACK) # Set attributes: Mono, 8000 Hz, 16 bit little endian frames out.setchannels(1) diff --git a/recordtest.py b/recordtest.py index 5a47e57..8a5b9e3 100644 --- a/recordtest.py +++ b/recordtest.py @@ -17,7 +17,7 @@ import time # 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.openpcm(alsaaudio.PCM_CAPTURE,alsaaudio.PCM_NONBLOCK) +inp = alsaaudio.PCM(alsaaudio.PCM_CAPTURE,alsaaudio.PCM_NONBLOCK) # Set attributes: Mono, 8000 Hz, 16 bit little endian samples inp.setchannels(1)