diff --git a/py/CHANGES b/py/CHANGES index ce66143..5a6f61c 100644 --- a/py/CHANGES +++ b/py/CHANGES @@ -1,3 +1,15 @@ +Version 0.8.6: +- Added four methods to the 'PCM' class to allow users to get detailed information about the device: + + - 'getformats()' returns a dictionary of name / value pairs, one for each of the card's + supported formats - e.g. '{"U8": 1, "S16_LE": 2}', + - 'getchannels()' returns a list of the supported channel numbers, e.g. '[1, 2]', + - 'getrates()' returns supported sample rates for the device, e.g. '[48000]', + - 'getratebounds()' returns the device's official minimum and maximum supported + sample rates as a tuple, e.g. '(4000, 48000)'. + + (contributed by @jdstmporter) + Version 0.8.5: - Return an empty string/bytestring when 'read()' detects an overrun. Previously the returned data was undefined (contributed by @jcea) diff --git a/py/alsaaudio.c b/py/alsaaudio.c index 624a808..71e017d 100644 --- a/py/alsaaudio.c +++ b/py/alsaaudio.c @@ -29,6 +29,64 @@ #include #include +#define ARRAY_SIZE(a) (sizeof(a) / sizeof *(a)) +static const snd_pcm_format_t ALSAFormats[] = { + SND_PCM_FORMAT_S8, + SND_PCM_FORMAT_U8, + SND_PCM_FORMAT_S16_LE, + SND_PCM_FORMAT_S16_BE, + SND_PCM_FORMAT_U16_LE, + SND_PCM_FORMAT_U16_BE, + SND_PCM_FORMAT_S24_LE, + SND_PCM_FORMAT_S24_BE, + SND_PCM_FORMAT_U24_LE, + SND_PCM_FORMAT_U24_BE, + SND_PCM_FORMAT_S32_LE, + SND_PCM_FORMAT_S32_BE, + SND_PCM_FORMAT_U32_LE, + SND_PCM_FORMAT_U32_BE, + SND_PCM_FORMAT_FLOAT_LE, + SND_PCM_FORMAT_FLOAT_BE, + SND_PCM_FORMAT_FLOAT64_LE, + SND_PCM_FORMAT_FLOAT64_BE, + SND_PCM_FORMAT_IEC958_SUBFRAME_LE, + SND_PCM_FORMAT_IEC958_SUBFRAME_BE, + SND_PCM_FORMAT_MU_LAW, + SND_PCM_FORMAT_A_LAW, + SND_PCM_FORMAT_IMA_ADPCM, + SND_PCM_FORMAT_MPEG, + SND_PCM_FORMAT_GSM, + SND_PCM_FORMAT_SPECIAL, + SND_PCM_FORMAT_S24_3LE, + SND_PCM_FORMAT_S24_3BE, + SND_PCM_FORMAT_U24_3LE, + SND_PCM_FORMAT_U24_3BE, + SND_PCM_FORMAT_S20_3LE, + SND_PCM_FORMAT_S20_3BE, + SND_PCM_FORMAT_U20_3LE, + SND_PCM_FORMAT_U20_3BE, + SND_PCM_FORMAT_S18_3LE, + SND_PCM_FORMAT_S18_3BE, + SND_PCM_FORMAT_U18_3LE, + SND_PCM_FORMAT_U18_3BE +}; +static const unsigned ALSARates[] = { + 4000, + 5512, + 8000, + 11025, + 16000, + 22050, + 32000, + 44100, + 48000, + 64000, + 88200, + 96000, + 176400, + 192000 +}; + PyDoc_STRVAR(alsaaudio_module_doc, "This modules provides support for the ALSA audio API.\n" "\n" @@ -572,6 +630,169 @@ alsapcm_dumpinfo(alsapcm_t *self, PyObject *args) return Py_None; } +// auxiliary function + + +static PyObject * +alsapcm_getformats(alsapcm_t *self, PyObject *args) { + snd_pcm_t *pcm = self->handle; + if (!pcm) { + PyErr_SetString(ALSAAudioError, "PCM device is closed"); + return NULL; + } + snd_pcm_hw_params_t *params; + snd_pcm_hw_params_alloca(¶ms); + int err = snd_pcm_hw_params_any(pcm, params); + if (err < 0) { + PyErr_SetString(ALSAAudioError, "Cannot get hardware parameters"); + return NULL; + } + + PyObject *fmts = PyDict_New(); + for (int i = 0; i < ARRAY_SIZE(ALSAFormats); ++i) { + snd_pcm_format_t format = ALSAFormats[i]; + if (!snd_pcm_hw_params_test_format(pcm, params, format)) { + const char *name = snd_pcm_format_name(format); + PyObject *pname=PyUnicode_FromString(name); + PyObject *value=PyLong_FromLong((long)format); + PyDict_SetItem(fmts,pname,value); + } + } + return fmts; +} + +PyDoc_STRVAR(getformats_doc, +"getformats() -> [str:int]\n\ +\n\ +Returns dictionary of supported format codes keyed by their standard ALSA names."); + +static PyObject * +alsapcm_getratemaxmin(alsapcm_t *self, PyObject *args) { + snd_pcm_t *pcm = self->handle; + if (!pcm) { + PyErr_SetString(ALSAAudioError, "PCM device is closed"); + return NULL; + } + snd_pcm_hw_params_t *params; + snd_pcm_hw_params_alloca(¶ms); + int err = snd_pcm_hw_params_any(pcm, params); + if (err < 0) { + PyErr_SetString(ALSAAudioError, "Cannot get hardware parameters"); + return NULL; + } + unsigned min,max; + if(snd_pcm_hw_params_get_rate_min(params, &min,NULL)<0) { + PyErr_SetString(ALSAAudioError, "Cannot get minimum supported bitrate"); + return NULL; + } + if(snd_pcm_hw_params_get_rate_max(params, &max,NULL)<0) { + PyErr_SetString(ALSAAudioError, "Cannot get maximum supported bitrate"); + return NULL; + } + PyObject *minp=PyLong_FromLong(min); + PyObject *maxp=PyLong_FromLong(max); + return PyTuple_Pack(2,minp,maxp); +} + +PyDoc_STRVAR(getratebounds_doc, +"getratebounds() -> (int,int)\n\ +\n\ +Returns the card's minimum and maximum supported sample rates as a tuple."); + +static PyObject * +alsapcm_getrates(alsapcm_t *self, PyObject *args) { + snd_pcm_t *pcm = self->handle; + if (!pcm) { + PyErr_SetString(ALSAAudioError, "PCM device is closed"); + return NULL; + } + snd_pcm_hw_params_t *params; + snd_pcm_hw_params_alloca(¶ms); + int err = snd_pcm_hw_params_any(pcm, params); + if (err < 0) { + PyErr_SetString(ALSAAudioError, "Cannot get hardware parameters"); + return NULL; + } + unsigned min,max; + if(snd_pcm_hw_params_get_rate_min(params, &min,NULL)<0) { + PyErr_SetString(ALSAAudioError, "Cannot get minimum supported bitrate"); + return NULL; + } + if(snd_pcm_hw_params_get_rate_max(params, &max,NULL)<0) { + PyErr_SetString(ALSAAudioError, "Cannot get maximum supported bitrate"); + return NULL; + } + if(min==max) { + return PyLong_FromLong(min); + } + else if(!snd_pcm_hw_params_test_rate(pcm, params, min + 1, 0)) { + PyObject *minp=PyLong_FromLong(min); + PyObject *maxp=PyLong_FromLong(max); + return PyTuple_Pack(2,minp,maxp); + } + else { + PyObject *rates=PyList_New(0); + for(int i=0;i obj\n\ +\n\ +Returns the sample rates supported by the device.\ +Returned value can be one of three types, depending on the card's properties.\ +There are three cases:\n\ +\n\ +- Card supports only a single rate: returns the rate\n\ +- Card supports a continuous range of rates: returns a tuple of the range's lower and upper bounds (inclusive)\n\ +- Card supports a collection of well-known rates: returns a list of the supported rates"); + +static PyObject * +alsapcm_getchannels(alsapcm_t *self,PyObject *args) { + snd_pcm_t *pcm = self->handle; + if (!pcm) { + PyErr_SetString(ALSAAudioError, "PCM device is closed"); + return NULL; + } + snd_pcm_hw_params_t *params; + snd_pcm_hw_params_alloca(¶ms); + int err = snd_pcm_hw_params_any(pcm, params); + if (err < 0) { + PyErr_SetString(ALSAAudioError, "Cannot get hardware parameters"); + return NULL; + } + unsigned min,max; + if(snd_pcm_hw_params_get_channels_min(params, &min)<0) { + PyErr_SetString(ALSAAudioError, "Cannot get minimum supported number of channels"); + return NULL; + } + if(snd_pcm_hw_params_get_channels_max(params, &max)<0) { + PyErr_SetString(ALSAAudioError, "Cannot get maximum supported number of channels"); + return NULL; + } + + PyObject *out=PyList_New(0); + + for(unsigned ch=min;ch<=max;++ch) { + if (!snd_pcm_hw_params_test_channels(pcm, params, ch)) { + PyObject *pch=PyLong_FromLong(ch); + PyList_Append(out,pch); + } + } + return out; +} +PyDoc_STRVAR(getchannels_doc, +"getchannels() -> [int]\n\ +\n\ +Returns list of supported channel numbers."); + static PyObject * alsapcm_pcmtype(alsapcm_t *self, PyObject *args) { @@ -1101,6 +1322,10 @@ static PyMethodDef alsapcm_methods[] = { {"setperiodsize", (PyCFunction)alsapcm_setperiodsize, METH_VARARGS, setperiodsize_doc}, {"dumpinfo", (PyCFunction)alsapcm_dumpinfo, METH_VARARGS}, + {"getformats", (PyCFunction)alsapcm_getformats, METH_VARARGS, getformats_doc}, + {"getratebounds", (PyCFunction)alsapcm_getratemaxmin, METH_VARARGS, getratebounds_doc}, + {"getrates", (PyCFunction)alsapcm_getrates, METH_VARARGS, getrates_doc}, + {"getchannels", (PyCFunction)alsapcm_getchannels, METH_VARARGS, getchannels_doc}, {"read", (PyCFunction)alsapcm_read, METH_VARARGS, read_doc}, {"write", (PyCFunction)alsapcm_write, METH_VARARGS, write_doc}, {"pause", (PyCFunction)alsapcm_pause, METH_VARARGS, pause_doc}, diff --git a/py/setup.py b/py/setup.py index e2af146..cd683c3 100755 --- a/py/setup.py +++ b/py/setup.py @@ -8,7 +8,7 @@ from setuptools import setup from setuptools.extension import Extension from sys import version -pyalsa_version = '0.8.5' +pyalsa_version = '0.8.6' if __name__ == '__main__': setup( diff --git a/tests/params b/tests/params index 00cbc1f..412c9c4 100755 Binary files a/tests/params and b/tests/params differ diff --git a/tests/params.c b/tests/params.c index 48f161c..1e72d87 100644 --- a/tests/params.c +++ b/tests/params.c @@ -59,6 +59,7 @@ static const snd_pcm_format_t formats[] = { }; static const unsigned int rates[] = { + 4000, 5512, 8000, 11025, @@ -164,7 +165,7 @@ int main(int argc, char *argv[]) printf("Formats:"); for (int i = 0; i < info.nFormats; ++i) { - printf(" %s", snd_pcm_format_name(formats[i])); + printf("%s ", snd_pcm_format_name(info.formats[i])); } putchar('\n'); printf("Rates:"); @@ -178,7 +179,7 @@ int main(int argc, char *argv[]) printf(" %u", info.channels[i]); } putchar('\n'); - /* + err = snd_pcm_open(&pcm, device_name, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); if (err < 0) { @@ -205,8 +206,10 @@ int main(int argc, char *argv[]) printf("Formats:"); for (i = 0; i < ARRAY_SIZE(formats); ++i) { - if (!snd_pcm_hw_params_test_format(pcm, hw_params, formats[i])) + if (!snd_pcm_hw_params_test_format(pcm, hw_params, formats[i])) { printf(" %s", snd_pcm_format_name(formats[i])); + } + } putchar('\n'); @@ -289,7 +292,7 @@ int main(int argc, char *argv[]) snd_pcm_close(pcm); - */ + return 0; }