Files
pyalsaaudio/alsaaudio.c
T
larsimmisch dc68788ebf Import from divmod. Original log message:
r833 | casper | 2004-10-05 21:43:58 +0200 (Tue, 05 Oct 2004) | 1 line

Completed mixer read/write support. Docs to follow



git-svn-id: svn://svn.code.sf.net/p/pyalsaaudio/code/trunk@4 ec2f30ec-7544-0410-870e-f70ca00c83f0
2008-01-24 11:22:37 +00:00

956 lines
29 KiB
C

/*
* alsaaudio -- Python interface to ALSA (Advanced Linux Sound Architecture).
* The standard audio API for Linux since kernel 2.6
*
* Contributed by Unispeed A/S (http://www.unispeed.com)
* Author: Casper Wilstup (cwi@unispeed.dk)
*
* License: Python Software Foundation License
*
*/
#include "Python.h"
#include <alsa/asoundlib.h>
#include <stdio.h>
typedef struct {
PyObject_HEAD;
int pcmtype;
int pcmmode;
char *cardname;
snd_pcm_t *handle;
// Configurable parameters
int channels;
int rate;
int format;
snd_pcm_uframes_t periodsize;
int framesize;
} alsapcm_t;
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;
snd_pcm_hw_params_t *hwparams;
snd_pcm_uframes_t frames;
if (self->handle) {
snd_pcm_close(self->handle);
self->handle = 0;
}
res = snd_pcm_open(&(self->handle),self->cardname,self->pcmtype,self->pcmmode);
if (res < 0) return res;
snd_pcm_hw_params_alloca(&hwparams);
res = snd_pcm_hw_params_any(self->handle, hwparams);
if (res < 0) return res;
snd_pcm_hw_params_alloca(&hwparams);
/* Fill it in with default values. */
snd_pcm_hw_params_any(self->handle, hwparams);
snd_pcm_hw_params_set_access(self->handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
snd_pcm_hw_params_set_format(self->handle, hwparams, self->format);
snd_pcm_hw_params_set_channels(self->handle, hwparams, self->channels);
dir = 0;
snd_pcm_hw_params_set_rate(self->handle, hwparams, self->rate, dir);
snd_pcm_hw_params_set_period_size(self->handle, hwparams, self->periodsize, dir);
snd_pcm_hw_params_set_periods(self->handle,hwparams,4,0);
res = snd_pcm_hw_params(self->handle, hwparams);
self->framesize = self->channels * snd_pcm_hw_params_get_sbits(hwparams)/8;
return res;
}
static PyObject *
alsapcm_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
int res;
alsapcm_t *self;
int pcmtype=0;
int pcmmode=0;
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)");
return NULL;
}
if (pcmmode < 0 || pcmmode > SND_PCM_ASYNC) {
PyErr_SetString(ALSAAudioError, "Invalid PCM mode");
return NULL;
}
self->pcmtype = pcmtype;
self->pcmmode = pcmmode;
self->cardname = strdup(cardname);
self->channels = 2;
self->rate = 44100;
self->format = SND_PCM_FORMAT_S16_LE;
self->periodsize = 32;
self->handle = 0;
res = alsapcm_setup(self);
if (res < 0) {
if (self->handle) {
snd_pcm_close(self->handle);
self->handle = 0;
}
PyErr_SetString(ALSAAudioError, snd_strerror(res));
return NULL;
}
return (PyObject *)self;
}
static void alsapcm_dealloc(alsapcm_t *self) {
if (self->handle) {
snd_pcm_drain(self->handle);
snd_pcm_close(self->handle);
}
free(self->cardname);
PyObject_Del(self);
}
static PyObject *
alsapcm_dumpinfo(alsapcm_t *self, PyObject *args) {
unsigned int val,val2;
int dir;
snd_pcm_uframes_t frames;
snd_pcm_hw_params_t *hwparams;
snd_pcm_hw_params_alloca(&hwparams);
snd_pcm_hw_params_current(self->handle,hwparams);
if (!PyArg_ParseTuple(args,"")) return NULL;
printf("PCM handle name = '%s'\n", snd_pcm_name(self->handle));
printf("PCM state = %s\n", snd_pcm_state_name(snd_pcm_state(self->handle)));
snd_pcm_hw_params_get_access(hwparams, (snd_pcm_access_t *) &val);
printf("access type = %s\n", snd_pcm_access_name((snd_pcm_access_t)val));
snd_pcm_hw_params_get_format(hwparams, &val);
printf("format = '%s' (%s)\n",
snd_pcm_format_name((snd_pcm_format_t)val),
snd_pcm_format_description((snd_pcm_format_t)val));
snd_pcm_hw_params_get_subformat(hwparams, (snd_pcm_subformat_t *)&val);
printf("subformat = '%s' (%s)\n",
snd_pcm_subformat_name((snd_pcm_subformat_t)val),
snd_pcm_subformat_description((snd_pcm_subformat_t)val));
snd_pcm_hw_params_get_channels(hwparams, &val);
printf("channels = %d\n", val);
snd_pcm_hw_params_get_rate(hwparams, &val, &dir);
printf("rate = %d bps\n", val);
snd_pcm_hw_params_get_period_time(hwparams, &val, &dir);
printf("period time = %d us\n", val);
snd_pcm_hw_params_get_period_size(hwparams, &frames, &dir);
printf("period size = %d frames\n", (int)frames);
snd_pcm_hw_params_get_buffer_time(hwparams, &val, &dir);
printf("buffer time = %d us\n", val);
snd_pcm_hw_params_get_buffer_size(hwparams, (snd_pcm_uframes_t *) &val);
printf("buffer size = %d frames\n", val);
snd_pcm_hw_params_get_periods(hwparams, &val, &dir);
printf("periods per buffer = %d frames\n", val);
snd_pcm_hw_params_get_rate_numden(hwparams, &val, &val2);
printf("exact rate = %d/%d bps\n", val, val2);
val = snd_pcm_hw_params_get_sbits(hwparams);
printf("significant bits = %d\n", val);
snd_pcm_hw_params_get_tick_time(hwparams, &val, &dir);
printf("tick time = %d us\n", val);
val = snd_pcm_hw_params_is_batch(hwparams);
printf("is batch = %d\n", val);
val = snd_pcm_hw_params_is_block_transfer(hwparams);
printf("is block transfer = %d\n", val);
val = snd_pcm_hw_params_is_double(hwparams);
printf("is double = %d\n", val);
val = snd_pcm_hw_params_is_half_duplex(hwparams);
printf("is half duplex = %d\n", val);
val = snd_pcm_hw_params_is_joint_duplex(hwparams);
printf("is joint duplex = %d\n", val);
val = snd_pcm_hw_params_can_overrange(hwparams);
printf("can overrange = %d\n", val);
val = snd_pcm_hw_params_can_mmap_sample_resolution(hwparams);
printf("can mmap = %d\n", val);
val = snd_pcm_hw_params_can_pause(hwparams);
printf("can pause = %d\n", val);
val = snd_pcm_hw_params_can_resume(hwparams);
printf("can resume = %d\n", val);
val = snd_pcm_hw_params_can_sync_start(hwparams);
printf("can sync start = %d\n", val);
Py_INCREF(Py_None);
return Py_None;
}
static PyObject *
alsapcm_pcmtype(alsapcm_t *self, PyObject *args) {
if (!PyArg_ParseTuple(args,"")) return NULL;
return PyInt_FromLong(self->pcmtype);
}
static PyObject *
alsapcm_pcmmode(alsapcm_t *self, PyObject *args) {
if (!PyArg_ParseTuple(args,"")) return NULL;
return PyInt_FromLong(self->pcmmode);
}
static PyObject *
alsapcm_cardname(alsapcm_t *self, PyObject *args) {
if (!PyArg_ParseTuple(args,"")) return NULL;
return PyString_FromString(self->cardname);
}
static PyObject *
alsapcm_setchannels(alsapcm_t *self, PyObject *args) {
int channels;
int res;
if (!PyArg_ParseTuple(args,"i",&channels)) return NULL;
self->channels = channels;
res = alsapcm_setup(self);
if (res < 0) {
PyErr_SetString(ALSAAudioError, snd_strerror(res));
return NULL;
}
return PyInt_FromLong(channels);
}
static PyObject *
alsapcm_setrate(alsapcm_t *self, PyObject *args) {
int rate;
int res;
if (!PyArg_ParseTuple(args,"i",&rate)) return NULL;
self->rate = rate;
res = alsapcm_setup(self);
if (res < 0) {
PyErr_SetString(ALSAAudioError, snd_strerror(res));
return NULL;
}
return PyInt_FromLong(self->rate);
}
static PyObject *
alsapcm_setformat(alsapcm_t *self, PyObject *args) {
int format;
int res;
if (!PyArg_ParseTuple(args,"i",&format)) return NULL;
self->format = format;
res = alsapcm_setup(self);
if (res < 0) {
PyErr_SetString(ALSAAudioError, snd_strerror(res));
return NULL;
}
return PyInt_FromLong(self->format);
}
static PyObject *
alsapcm_setperiodsize(alsapcm_t *self, PyObject *args) {
int periodsize;
int res;
if (!PyArg_ParseTuple(args,"i",&periodsize)) return NULL;
self->periodsize = periodsize;
res = alsapcm_setup(self);
if (res < 0) {
PyErr_SetString(ALSAAudioError, snd_strerror(res));
return NULL;
}
return PyInt_FromLong(self->periodsize);
}
static PyObject *
alsapcm_read(alsapcm_t *self, PyObject *args) {
int res;
int val,dir;
char buffer[8000];
snd_pcm_hw_params_t *hwparams;
if (self->framesize * self->periodsize > 8000) {
PyErr_SetString(ALSAAudioError,"Capture data too large. Try decreasing period size");
return NULL;
}
if (!PyArg_ParseTuple(args,"")) return NULL;
if (self->pcmtype != SND_PCM_STREAM_CAPTURE) {
PyErr_SetString(ALSAAudioError,"Cannot read from playback PCM");
return NULL;
}
res = snd_pcm_readi(self->handle, buffer, self->periodsize);
if (res == -EPIPE) {
/* EPIPE means overrun */
snd_pcm_prepare(self->handle);
}
else if (res == -EAGAIN) {
res = 0;
}
else if (res < 0) {
PyErr_SetString(ALSAAudioError,snd_strerror(res));
return NULL;
}
return Py_BuildValue("is#",res,buffer,res*self->framesize);
}
static PyObject *alsapcm_write(alsapcm_t *self, PyObject *args) {
char *data;
int datalen;
int res;
if (!PyArg_ParseTuple(args,"s#",&data,&datalen)) return NULL;
if (datalen%self->framesize) {
PyErr_SetString(ALSAAudioError,"Data size must be a multipla of framesize");
return NULL;
}
res = snd_pcm_writei(self->handle, data, datalen/self->framesize);
if (res == -EPIPE) {
/* EPIPE means underrun */
snd_pcm_prepare(self->handle);
snd_pcm_writei(self->handle, data, datalen/self->framesize);
snd_pcm_writei(self->handle, data, datalen/self->framesize);
}
else if (res == -EAGAIN) {
return PyInt_FromLong(0);
}
else if (res < 0) {
PyErr_SetString(ALSAAudioError,snd_strerror(res));
return NULL;
}
return PyInt_FromLong(datalen);
}
/* ALSA PCM Object Bureaucracy */
static PyMethodDef alsapcm_methods[] = {
{"pcmtype", (PyCFunction)alsapcm_pcmtype, METH_VARARGS},
{"pcmmode", (PyCFunction)alsapcm_pcmmode, 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},
{"setperiodsize", (PyCFunction)alsapcm_setperiodsize, METH_VARARGS},
{"dumpinfo", (PyCFunction)alsapcm_dumpinfo, METH_VARARGS},
{"read", (PyCFunction)alsapcm_read, METH_VARARGS},
{"write", (PyCFunction)alsapcm_write, METH_VARARGS},
{NULL, NULL}
};
static PyObject *
alsapcm_getattr(alsapcm_t *self, char *name) {
return Py_FindMethod(alsapcm_methods, (PyObject *)self, name);
}
static PyTypeObject ALSAPCMType = {
PyObject_HEAD_INIT(&PyType_Type)
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 */
/******************************************/
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[] = {
{ "mixers", alsamixer_list, METH_VARARGS },
{ 0, 0 },
};
#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);
PyModule_AddObject(m, "ALSAAudioError", ALSAAudioError);
}
_EXPORT_INT(m,"PCM_PLAYBACK",SND_PCM_STREAM_PLAYBACK);
_EXPORT_INT(m,"PCM_CAPTURE",SND_PCM_STREAM_CAPTURE);
_EXPORT_INT(m,"PCM_NONBLOCK",SND_PCM_NONBLOCK);
_EXPORT_INT(m,"PCM_ASYNC",SND_PCM_ASYNC);
/* PCM Formats */
_EXPORT_INT(m,"PCM_FORMAT_S8",SND_PCM_FORMAT_S8);
_EXPORT_INT(m,"PCM_FORMAT_U8",SND_PCM_FORMAT_U8);
_EXPORT_INT(m,"PCM_FORMAT_S16_LE",SND_PCM_FORMAT_S16_LE);
_EXPORT_INT(m,"PCM_FORMAT_S16_BE",SND_PCM_FORMAT_S16_BE);
_EXPORT_INT(m,"PCM_FORMAT_U16_LE",SND_PCM_FORMAT_U16_LE);
_EXPORT_INT(m,"PCM_FORMAT_U16_BE",SND_PCM_FORMAT_U16_BE);
_EXPORT_INT(m,"PCM_FORMAT_S24_LE",SND_PCM_FORMAT_S24_LE);
_EXPORT_INT(m,"PCM_FORMAT_S24_BE",SND_PCM_FORMAT_S24_BE);
_EXPORT_INT(m,"PCM_FORMAT_U24_LE",SND_PCM_FORMAT_U24_LE);
_EXPORT_INT(m,"PCM_FORMAT_U24_BE",SND_PCM_FORMAT_U24_BE);
_EXPORT_INT(m,"PCM_FORMAT_S32_LE",SND_PCM_FORMAT_S32_LE);
_EXPORT_INT(m,"PCM_FORMAT_S32_BE",SND_PCM_FORMAT_S32_BE);
_EXPORT_INT(m,"PCM_FORMAT_U32_LE",SND_PCM_FORMAT_U32_LE);
_EXPORT_INT(m,"PCM_FORMAT_U32_BE",SND_PCM_FORMAT_U32_BE);
_EXPORT_INT(m,"PCM_FORMAT_FLOAT_LE",SND_PCM_FORMAT_FLOAT_LE);
_EXPORT_INT(m,"PCM_FORMAT_FLOAT_BE",SND_PCM_FORMAT_FLOAT_BE);
_EXPORT_INT(m,"PCM_FORMAT_FLOAT64_LE",SND_PCM_FORMAT_FLOAT64_LE);
_EXPORT_INT(m,"PCM_FORMAT_FLOAT64_BE",SND_PCM_FORMAT_FLOAT64_BE);
_EXPORT_INT(m,"PCM_FORMAT_MU_LAW",SND_PCM_FORMAT_MU_LAW);
_EXPORT_INT(m,"PCM_FORMAT_A_LAW",SND_PCM_FORMAT_A_LAW);
_EXPORT_INT(m,"PCM_FORMAT_IMA_ADPCM",SND_PCM_FORMAT_IMA_ADPCM);
_EXPORT_INT(m,"PCM_FORMAT_MPEG",SND_PCM_FORMAT_MPEG);
_EXPORT_INT(m,"PCM_FORMAT_GSM",SND_PCM_FORMAT_GSM);
}