mirror of
https://github.com/larsimmisch/pyalsaaudio.git
synced 2026-04-16 16:15:31 +00:00
Compare commits
48 Commits
0.8.1
...
pcm-constr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d3aa602a04 | ||
|
|
046e7c4e87 | ||
|
|
a4c4c7cb62 | ||
|
|
f478797f6f | ||
|
|
12f807698a | ||
|
|
fc011b5ea6 | ||
|
|
f244a70111 | ||
|
|
a056a90c61 | ||
|
|
be1b3e131d | ||
|
|
8abf06bedf | ||
|
|
dcc831e607 | ||
|
|
e587df9143 | ||
|
|
82febd3f7e | ||
|
|
1695066c11 | ||
|
|
25717020ef | ||
|
|
1aae655d24 | ||
|
|
c1c8362eb2 | ||
|
|
723eff3887 | ||
|
|
aa9867de18 | ||
|
|
58f4522769 | ||
|
|
f2fb61d324 | ||
|
|
9e79494a95 | ||
|
|
bfe4899721 | ||
|
|
40a1219dac | ||
|
|
54e2712b7a | ||
|
|
f9685e0b30 | ||
|
|
b4a670c50d | ||
|
|
370a4b6249 | ||
|
|
eca217dff9 | ||
|
|
65d3c4a283 | ||
|
|
adc0d800e1 | ||
|
|
02cf16d10d | ||
|
|
94ced0517e | ||
|
|
698e6044d3 | ||
|
|
2c95f4ff6b | ||
|
|
f19d139f64 | ||
|
|
dc51fa75b5 | ||
|
|
85ff47ad43 | ||
|
|
88f38284bb | ||
|
|
fe7561beea | ||
|
|
2314aaeb7e | ||
|
|
bf24ec65ca | ||
|
|
478d0559e6 | ||
|
|
891a30eb08 | ||
|
|
74d9e7d6e1 | ||
|
|
fa10bf6999 | ||
|
|
7de446c3c7 | ||
|
|
5cbc88607d |
32
CHANGES
32
CHANGES
@@ -1,3 +1,35 @@
|
||||
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)'.
|
||||
|
||||
(#82 contributed by @jdstmporter)
|
||||
|
||||
- Prevent hang on close after capturing audio (#80 contributed by @daym)
|
||||
|
||||
Version 0.8.5:
|
||||
- Return an empty string/bytestring when 'read()' detects an
|
||||
overrun. Previously the returned data was undefined (contributed by @jcea)
|
||||
|
||||
- Unlimited setperiod buffer size when reading frames (contributed by @jcea)
|
||||
|
||||
Version 0.8.4:
|
||||
- Fix Python3 API usage broken in 0.8.3
|
||||
|
||||
Version 0.8.3:
|
||||
- Add DSD sample formats (contributed by @lintweaker)
|
||||
- Add Mixer.handleevents() to acknowledge events identified by select.poll (contributed by @PaulSD)
|
||||
- Add functions for listing cards and their names (contributed by @chrisdiamand)
|
||||
- Add a method for setting enums (contributed by @chrisdiamand)
|
||||
|
||||
Version 0.8.2:
|
||||
- fix #3 (we cannot get the revision from git for pip installs)
|
||||
|
||||
Version 0.8.1:
|
||||
- document changes (this file)
|
||||
|
||||
|
||||
11
NOTES.md
Normal file
11
NOTES.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# Publishing the documentation
|
||||
|
||||
- Install Sphinx; `sudo pip install sphinx`
|
||||
- Clone gh-pages branch: `cd doc; git clone -b gh-pages git@github.com:larsimmisch/pyalsaaudio.git gh-pages`
|
||||
- `cd doc; make publish`
|
||||
|
||||
# Release procedure
|
||||
|
||||
- Update version number in setup.py
|
||||
- Create tag and push it, i.e. `git tag x.y.z; git push origin x.y.z`
|
||||
- `python setup.py sdist upload -r pypi`
|
||||
773
alsaaudio.c
773
alsaaudio.c
@@ -20,9 +20,74 @@
|
||||
#define PyLong_Check PyInt_Check
|
||||
#define PyLong_AS_LONG PyInt_AS_LONG
|
||||
#endif
|
||||
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
#define PyLong_FromLong PyInt_FromLong
|
||||
#endif
|
||||
|
||||
#include <alsa/asoundlib.h>
|
||||
#include <alsa/version.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#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"
|
||||
@@ -170,6 +235,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 = PyLong_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)
|
||||
{
|
||||
@@ -223,74 +358,26 @@ PyDoc_STRVAR(pcms_doc,
|
||||
\n\
|
||||
List the available PCM devices");
|
||||
|
||||
static int alsapcm_setup(alsapcm_t *self)
|
||||
{
|
||||
int res,dir;
|
||||
unsigned int val;
|
||||
snd_pcm_format_t fmt;
|
||||
snd_pcm_uframes_t frames;
|
||||
snd_pcm_hw_params_t *hwparams;
|
||||
|
||||
/* Allocate a hwparam structure on the stack,
|
||||
and fill it with configuration space */
|
||||
snd_pcm_hw_params_alloca(&hwparams);
|
||||
res = snd_pcm_hw_params_any(self->handle, hwparams);
|
||||
if (res < 0)
|
||||
return res;
|
||||
|
||||
/* Fill it with default values.
|
||||
|
||||
We don't care if any of this fails - we'll read the actual values
|
||||
back out.
|
||||
*/
|
||||
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);
|
||||
|
||||
/* Write it to the device */
|
||||
res = snd_pcm_hw_params(self->handle, hwparams);
|
||||
|
||||
/* Query current settings. These may differ from the requested values,
|
||||
which should therefore be sync'ed with actual values */
|
||||
snd_pcm_hw_params_current(self->handle, hwparams);
|
||||
|
||||
snd_pcm_hw_params_get_format(hwparams, &fmt); self->format = fmt;
|
||||
snd_pcm_hw_params_get_channels(hwparams, &val); self->channels = val;
|
||||
snd_pcm_hw_params_get_rate(hwparams, &val, &dir); self->rate = val;
|
||||
snd_pcm_hw_params_get_period_size(hwparams, &frames, &dir);
|
||||
self->periodsize = (int) frames;
|
||||
|
||||
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;
|
||||
PyObject *pcmtypeobj = NULL;
|
||||
long pcmtype;
|
||||
int pcmmode = 0;
|
||||
unsigned int rate = 48000;
|
||||
unsigned int channels = 2;
|
||||
snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE;
|
||||
char *device = "default";
|
||||
char *card = NULL;
|
||||
int cardidx = -1;
|
||||
char hw_device[128];
|
||||
char *kw[] = { "type", "mode", "device", "cardindex", "card", NULL };
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Oisiz", kw,
|
||||
int latency = 200;
|
||||
char *kw[] = { "type", "mode", "device", "cardindex", "card", "format", "rate", "channels", "latency", NULL };
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Oisiziiii", kw,
|
||||
&pcmtypeobj, &pcmmode, &device,
|
||||
&cardidx, &card))
|
||||
&cardidx, &card, &rate, &format, &channels, &latency))
|
||||
return NULL;
|
||||
|
||||
if (cardidx >= 0) {
|
||||
@@ -323,29 +410,9 @@ alsapcm_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (pcmmode < 0 || pcmmode > SND_PCM_ASYNC) {
|
||||
PyErr_SetString(ALSAAudioError, "Invalid PCM mode");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(self = (alsapcm_t *)PyObject_New(alsapcm_t, &ALSAPCMType)))
|
||||
return NULL;
|
||||
|
||||
self->handle = 0;
|
||||
self->pcmtype = pcmtype;
|
||||
self->pcmmode = pcmmode;
|
||||
self->channels = 2;
|
||||
self->rate = 44100;
|
||||
self->format = SND_PCM_FORMAT_S16_LE;
|
||||
self->periodsize = 32;
|
||||
|
||||
res = snd_pcm_open(&(self->handle), device, self->pcmtype,
|
||||
self->pcmmode);
|
||||
|
||||
if (res >= 0) {
|
||||
res = alsapcm_setup(self);
|
||||
}
|
||||
|
||||
|
||||
if (res >= 0) {
|
||||
self->cardname = strdup(device);
|
||||
}
|
||||
@@ -364,6 +431,7 @@ alsapcm_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||
static void alsapcm_dealloc(alsapcm_t *self)
|
||||
{
|
||||
if (self->handle) {
|
||||
snd_pcm_pause(self->handle, 1);
|
||||
snd_pcm_drain(self->handle);
|
||||
snd_pcm_close(self->handle);
|
||||
}
|
||||
@@ -496,6 +564,184 @@ 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 (size_t 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 (size_t i=0; i<ARRAY_SIZE(ALSARates); i++) {
|
||||
unsigned rate = ALSARates[i];
|
||||
if (!snd_pcm_hw_params_test_rate(pcm, params, rate, 0)) {
|
||||
PyObject *prate=PyLong_FromLong(rate);
|
||||
PyList_Append(rates,prate);
|
||||
}
|
||||
}
|
||||
return rates;
|
||||
}
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(getrates_doc,
|
||||
"getrates() -> 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)
|
||||
{
|
||||
@@ -558,147 +804,13 @@ PyDoc_STRVAR(cardname_doc,
|
||||
\n\
|
||||
Returns the name of the sound card used by this PCM object.");
|
||||
|
||||
|
||||
static PyObject *
|
||||
alsapcm_setchannels(alsapcm_t *self, PyObject *args)
|
||||
{
|
||||
int channels;
|
||||
int res;
|
||||
if (!PyArg_ParseTuple(args,"i:setchannels", &channels))
|
||||
return NULL;
|
||||
|
||||
if (!self->handle) {
|
||||
PyErr_SetString(ALSAAudioError, "PCM device is closed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
self->channels = channels;
|
||||
res = alsapcm_setup(self);
|
||||
if (res < 0)
|
||||
{
|
||||
PyErr_Format(ALSAAudioError, "%s [%s]", snd_strerror(res),
|
||||
self->cardname);
|
||||
return NULL;
|
||||
}
|
||||
return PyLong_FromLong(self->channels);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(setchannels_doc,
|
||||
"setchannels(numchannels)\n\
|
||||
\n\
|
||||
Used to set the number of capture or playback channels. Common values\n\
|
||||
are: 1 = mono, 2 = stereo, and 6 = full 6 channel audio.\n\
|
||||
\n\
|
||||
Few sound cards support more than 2 channels.");
|
||||
|
||||
|
||||
static PyObject *
|
||||
alsapcm_setrate(alsapcm_t *self, PyObject *args)
|
||||
{
|
||||
int rate;
|
||||
int res;
|
||||
if (!PyArg_ParseTuple(args,"i:setrate", &rate))
|
||||
return NULL;
|
||||
|
||||
if (!self->handle)
|
||||
{
|
||||
PyErr_SetString(ALSAAudioError, "PCM device is closed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
self->rate = rate;
|
||||
res = alsapcm_setup(self);
|
||||
if (res < 0)
|
||||
{
|
||||
PyErr_Format(ALSAAudioError, "%s [%s]", snd_strerror(res),
|
||||
self->cardname);
|
||||
return NULL;
|
||||
}
|
||||
return PyLong_FromLong(self->rate);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(setrate_doc,
|
||||
"setrate(rate)\n\
|
||||
\n\
|
||||
Set the sample rate in Hz for the device. Typical values are\n\
|
||||
8000 (telephony), 11025, 44100 (CD), 48000 (DVD audio) and 96000");
|
||||
|
||||
|
||||
static PyObject *
|
||||
alsapcm_setformat(alsapcm_t *self, PyObject *args)
|
||||
{
|
||||
int format;
|
||||
int res;
|
||||
if (!PyArg_ParseTuple(args,"i:setformat", &format))
|
||||
return NULL;
|
||||
|
||||
if (!self->handle)
|
||||
{
|
||||
PyErr_SetString(ALSAAudioError, "PCM device is closed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
self->format = format;
|
||||
res = alsapcm_setup(self);
|
||||
if (res < 0)
|
||||
{
|
||||
PyErr_Format(ALSAAudioError, "%s [%s]", snd_strerror(res),
|
||||
self->cardname);
|
||||
return NULL;
|
||||
}
|
||||
return PyLong_FromLong(self->format);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(setformat_doc,
|
||||
"setformat(rate)\n");
|
||||
|
||||
static PyObject *
|
||||
alsapcm_setperiodsize(alsapcm_t *self, PyObject *args)
|
||||
{
|
||||
int periodsize;
|
||||
int res;
|
||||
|
||||
if (!PyArg_ParseTuple(args,"i:setperiodsize", &periodsize))
|
||||
return NULL;
|
||||
|
||||
if (!self->handle)
|
||||
{
|
||||
PyErr_SetString(ALSAAudioError, "PCM device is closed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
self->periodsize = periodsize;
|
||||
res = alsapcm_setup(self);
|
||||
if (res < 0)
|
||||
{
|
||||
PyErr_Format(ALSAAudioError, "%s [%s]", snd_strerror(res),
|
||||
self->cardname);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
return PyLong_FromLong(self->periodsize);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(setperiodsize_doc,
|
||||
"setperiodsize(period) -> int\n\
|
||||
\n\
|
||||
Sets the actual period size in frames. Each write should consist of\n\
|
||||
exactly this number of frames, and each read will return this number of\n\
|
||||
frames (unless the device is in PCM_NONBLOCK mode, in which case it\n\
|
||||
may return nothing at all).");
|
||||
|
||||
static PyObject *
|
||||
alsapcm_read(alsapcm_t *self, PyObject *args)
|
||||
{
|
||||
int res;
|
||||
char buffer[8000];
|
||||
|
||||
if (self->framesize * self->periodsize > 8000) {
|
||||
|
||||
PyErr_SetString(ALSAAudioError,"Capture data too large. "
|
||||
"Try decreasing period size");
|
||||
return NULL;
|
||||
}
|
||||
int size = self->framesize * self->periodsize;
|
||||
PyObject *buffer_obj, *tuple_obj, *res_obj;
|
||||
char *buffer;
|
||||
|
||||
if (!PyArg_ParseTuple(args,":read"))
|
||||
return NULL;
|
||||
@@ -715,6 +827,18 @@ alsapcm_read(alsapcm_t *self, PyObject *args)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
buffer_obj = PyString_FromStringAndSize(NULL, size);
|
||||
if (!buffer_obj)
|
||||
return NULL;
|
||||
buffer = PyString_AS_STRING(buffer_obj);
|
||||
#else
|
||||
buffer_obj = PyBytes_FromStringAndSize(NULL, size);
|
||||
if (!buffer_obj)
|
||||
return NULL;
|
||||
buffer = PyBytes_AS_STRING(buffer_obj);
|
||||
#endif
|
||||
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
res = snd_pcm_readi(self->handle, buffer, self->periodsize);
|
||||
if (res == -EPIPE)
|
||||
@@ -734,15 +858,39 @@ alsapcm_read(alsapcm_t *self, PyObject *args)
|
||||
PyErr_Format(ALSAAudioError, "%s [%s]", snd_strerror(res),
|
||||
self->cardname);
|
||||
|
||||
Py_DECREF(buffer_obj);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (res <= 0) {
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
return Py_BuildValue("is#", res, buffer, res*self->framesize);
|
||||
/* If the following fails, it will free the object */
|
||||
if (_PyString_Resize(&buffer_obj, 0))
|
||||
return NULL;
|
||||
#else
|
||||
return Py_BuildValue("iy#", res, buffer, res*self->framesize);
|
||||
/* If the following fails, it will free the object */
|
||||
if (_PyBytes_Resize(&buffer_obj, 0))
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
res_obj = PyLong_FromLong(res);
|
||||
if (!res_obj) {
|
||||
Py_DECREF(buffer_obj);
|
||||
return NULL;
|
||||
}
|
||||
tuple_obj = PyTuple_New(2);
|
||||
if (!tuple_obj) {
|
||||
Py_DECREF(buffer_obj);
|
||||
Py_DECREF(res_obj);
|
||||
return NULL;
|
||||
}
|
||||
/* Steal reference counts */
|
||||
PyTuple_SET_ITEM(tuple_obj, 0, res_obj);
|
||||
PyTuple_SET_ITEM(tuple_obj, 1, buffer_obj);
|
||||
|
||||
return tuple_obj;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(read_doc,
|
||||
@@ -755,7 +903,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)
|
||||
@@ -870,6 +1022,43 @@ PyDoc_STRVAR(pause_doc,
|
||||
If enable is 1, playback or capture is paused. If enable is 0,\n\
|
||||
playback/capture is resumed.");
|
||||
|
||||
|
||||
static PyObject *alsapcm_drop(alsapcm_t *self)
|
||||
{
|
||||
int res;
|
||||
|
||||
if (!self->handle) {
|
||||
PyErr_SetString(ALSAAudioError, "PCM device is closed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
res = snd_pcm_drop(self->handle);
|
||||
|
||||
if (res < 0)
|
||||
{
|
||||
PyErr_Format(ALSAAudioError, "%s [%s]", snd_strerror(res),
|
||||
self->cardname);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
res = snd_pcm_prepare(self->handle);
|
||||
if (res < 0)
|
||||
{
|
||||
PyErr_Format(ALSAAudioError, "%s [%s]", snd_strerror(res),
|
||||
self->cardname);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return PyLong_FromLong(res);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(drop_doc,
|
||||
"drop(enable=1)\n\
|
||||
\n\
|
||||
stop current read and drop residual packet");
|
||||
|
||||
static PyObject *
|
||||
alsapcm_polldescriptors(alsapcm_t *self, PyObject *args)
|
||||
{
|
||||
@@ -933,16 +1122,15 @@ static PyMethodDef alsapcm_methods[] = {
|
||||
{"pcmtype", (PyCFunction)alsapcm_pcmtype, METH_VARARGS, pcmtype_doc},
|
||||
{"pcmmode", (PyCFunction)alsapcm_pcmmode, METH_VARARGS, pcmmode_doc},
|
||||
{"cardname", (PyCFunction)alsapcm_cardname, METH_VARARGS, cardname_doc},
|
||||
{"setchannels", (PyCFunction)alsapcm_setchannels, METH_VARARGS,
|
||||
setchannels_doc },
|
||||
{"setrate", (PyCFunction)alsapcm_setrate, METH_VARARGS, setrate_doc},
|
||||
{"setformat", (PyCFunction)alsapcm_setformat, METH_VARARGS, setformat_doc},
|
||||
{"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},
|
||||
{"drop", (PyCFunction)alsapcm_drop, METH_VARARGS, drop_doc},
|
||||
{"close", (PyCFunction)alsapcm_close, METH_VARARGS, pcm_close_doc},
|
||||
{"polldescriptors", (PyCFunction)alsapcm_polldescriptors, METH_VARARGS,
|
||||
pcm_polldescriptors_doc},
|
||||
@@ -992,14 +1180,14 @@ static PyTypeObject ALSAPCMType = {
|
||||
0, /* tp_as_buffer */
|
||||
Py_TPFLAGS_DEFAULT, /* tp_flags */
|
||||
"ALSA PCM device.", /* tp_doc */
|
||||
0, /* tp_traverse */
|
||||
0, /* tp_clear */
|
||||
0, /* tp_richcompare */
|
||||
0, /* tp_weaklistoffset */
|
||||
0, /* tp_iter */
|
||||
0, /* tp_iternext */
|
||||
alsapcm_methods, /* tp_methods */
|
||||
0, /* tp_members */
|
||||
0, /* tp_traverse */
|
||||
0, /* tp_clear */
|
||||
0, /* tp_richcompare */
|
||||
0, /* tp_weaklistoffset */
|
||||
0, /* tp_iter */
|
||||
0, /* tp_iternext */
|
||||
alsapcm_methods, /* tp_methods */
|
||||
0, /* tp_members */
|
||||
};
|
||||
|
||||
|
||||
@@ -1744,6 +1932,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 +2357,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 +2407,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}
|
||||
};
|
||||
@@ -2185,14 +2462,14 @@ static PyTypeObject ALSAMixerType = {
|
||||
0, /* tp_as_buffer*/
|
||||
Py_TPFLAGS_DEFAULT, /* tp_flags */
|
||||
"ALSA Mixer Control.", /* tp_doc */
|
||||
0, /* tp_traverse */
|
||||
0, /* tp_clear */
|
||||
0, /* tp_richcompare */
|
||||
0, /* tp_weaklistoffset */
|
||||
0, /* tp_iter */
|
||||
0, /* tp_iternext */
|
||||
alsamixer_methods, /* tp_methods */
|
||||
0, /* tp_members */
|
||||
0, /* tp_traverse */
|
||||
0, /* tp_clear */
|
||||
0, /* tp_richcompare */
|
||||
0, /* tp_weaklistoffset */
|
||||
0, /* tp_iter */
|
||||
0, /* tp_iternext */
|
||||
alsamixer_methods, /* tp_methods */
|
||||
0, /* tp_members */
|
||||
};
|
||||
|
||||
|
||||
@@ -2201,6 +2478,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},
|
||||
@@ -2307,6 +2586,22 @@ PyObject *PyInit_alsaaudio(void)
|
||||
_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);
|
||||
_EXPORT_INT(m, "PCM_FORMAT_S24_3LE",SND_PCM_FORMAT_S24_3LE);
|
||||
_EXPORT_INT(m, "PCM_FORMAT_S24_3BE",SND_PCM_FORMAT_S24_3BE);
|
||||
_EXPORT_INT(m, "PCM_FORMAT_U24_3LE",SND_PCM_FORMAT_U24_3LE);
|
||||
_EXPORT_INT(m, "PCM_FORMAT_U24_3BE",SND_PCM_FORMAT_U24_3BE);
|
||||
|
||||
/* 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);
|
||||
|
||||
230
doc/conf.py
230
doc/conf.py
@@ -1,182 +1,160 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# alsaaudio documentation build configuration file, created by
|
||||
# sphinx-quickstart on Sat Nov 22 00:17:09 2008.
|
||||
# alsaaudio documentation documentation build configuration file, created by
|
||||
# sphinx-quickstart on Thu Mar 30 23:52:21 2017.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its containing dir.
|
||||
# This file is execfile()d with the current directory set to its
|
||||
# containing dir.
|
||||
#
|
||||
# The contents of this file are pickled, so don't put values in the namespace
|
||||
# that aren't pickleable (module imports are okay, they're removed automatically).
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default value; values that are commented out
|
||||
# serve to show the default value.
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
import sys, os
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#
|
||||
# import os
|
||||
# import sys
|
||||
# sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
sys.path.append('..')
|
||||
import sys
|
||||
sys.path.insert(0, '..')
|
||||
from setup import pyalsa_version
|
||||
|
||||
# If your extensions are in another directory, add it here. If the directory
|
||||
# is relative to the documentation root, use os.path.abspath to make it
|
||||
# absolute, like shown here.
|
||||
#sys.path.append(os.path.abspath('some/directory'))
|
||||
|
||||
# General configuration
|
||||
# ---------------------
|
||||
# -- General configuration ------------------------------------------------
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#
|
||||
# needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = []
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['.templates']
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix of source filenames.
|
||||
# The suffix(es) of source filenames.
|
||||
# You can specify multiple suffix as a list of string:
|
||||
#
|
||||
# source_suffix = ['.rst', '.md']
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General substitutions.
|
||||
project = u'alsaaudio'
|
||||
copyright = u'2008-2009, Casper Wilstrup, Lars Immisch'
|
||||
# General information about the project.
|
||||
project = u'alsaaudio documentation'
|
||||
copyright = u'2017, Lars Immisch & Casper Wilstrup'
|
||||
author = u'Lars Immisch & Casper Wilstrup'
|
||||
|
||||
# The default replacements for |version| and |release|, also used in various
|
||||
# other places throughout the built documents.
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = pyalsa_version
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = pyalsa_version
|
||||
release = version
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
#today = ''
|
||||
# Else, today_fmt is used as the format for a strftime call.
|
||||
today_fmt = '%B %d, %Y'
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#
|
||||
# This is also used if you do content translation via gettext catalogs.
|
||||
# Usually you set "language" from the command line for these cases.
|
||||
language = None
|
||||
|
||||
# List of documents that shouldn't be included in the build.
|
||||
#unused_docs = []
|
||||
|
||||
# List of directories, relative to source directories, that shouldn't be searched
|
||||
# for source files.
|
||||
exclude_trees = ['.build']
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all documents.
|
||||
#default_role = None
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
#add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
#add_module_names = True
|
||||
|
||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||
# output. They are ignored by default.
|
||||
#show_authors = False
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
# This patterns also effect to html_static_path and html_extra_path
|
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# If true, `todo` and `todoList` produce output, else they produce nothing.
|
||||
todo_include_todos = False
|
||||
|
||||
# Options for HTML output
|
||||
# -----------------------
|
||||
|
||||
# The style sheet to use for HTML and HTML Help pages. A file of that name
|
||||
# must exist either in Sphinx' static/ path, or in one of the custom paths
|
||||
# given in html_static_path.
|
||||
html_style = 'default.css'
|
||||
# -- Options for HTML output ----------------------------------------------
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
#html_title = None
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
#
|
||||
html_theme = 'alabaster'
|
||||
|
||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||
#html_short_title = None
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top
|
||||
# of the sidebar.
|
||||
#html_logo = None
|
||||
|
||||
# The name of an image file (within the static path) to use as favicon of the
|
||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
#html_favicon = None
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#
|
||||
# html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['static']
|
||||
html_static_path = ['_static']
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
html_last_updated_fmt = '%b %d, %Y'
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
#html_use_smartypants = True
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
#html_sidebars = {}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# template names.
|
||||
#html_additional_pages = {}
|
||||
|
||||
# If false, no module index is generated.
|
||||
#html_use_modindex = True
|
||||
|
||||
# If false, no index is generated.
|
||||
#html_use_index = True
|
||||
|
||||
# If true, the index is split into individual pages for each letter.
|
||||
#html_split_index = False
|
||||
|
||||
# If true, the reST sources are included in the HTML build as _sources/<name>.
|
||||
#html_copy_source = True
|
||||
|
||||
# If true, an OpenSearch description file will be output, and all pages will
|
||||
# contain a <link> tag referring to it. The value of this option must be the
|
||||
# base URL from which the finished HTML is served.
|
||||
#html_use_opensearch = ''
|
||||
|
||||
# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
|
||||
#html_file_suffix = ''
|
||||
# -- Options for HTMLHelp output ------------------------------------------
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'alsaaudiodoc'
|
||||
htmlhelp_basename = 'alsaaudiodocumentationdoc'
|
||||
|
||||
|
||||
# Options for LaTeX output
|
||||
# ------------------------
|
||||
# -- Options for LaTeX output ---------------------------------------------
|
||||
|
||||
# The paper size ('letter' or 'a4').
|
||||
#latex_paper_size = 'letter'
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#
|
||||
# 'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#latex_font_size = '10pt'
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#
|
||||
# 'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#
|
||||
# 'preamble': '',
|
||||
|
||||
# Latex figure (float) alignment
|
||||
#
|
||||
# 'figure_align': 'htbp',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, document class [howto/manual]).
|
||||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [
|
||||
('index', 'alsaaudio.tex', u'alsaaudio Documentation',
|
||||
u'Casper Wilstrup, Lars Immisch', 'manual'),
|
||||
(master_doc, 'alsaaudiodocumentation.tex', u'alsaaudio documentation Documentation',
|
||||
u'Lars Immisch', 'manual'),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
# the title page.
|
||||
#latex_logo = None
|
||||
|
||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||
# not chapters.
|
||||
#latex_use_parts = False
|
||||
# -- Options for manual page output ---------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
(master_doc, 'alsaaudiodocumentation', u'alsaaudio documentation Documentation',
|
||||
[author], 1)
|
||||
]
|
||||
|
||||
|
||||
# -- Options for Texinfo output -------------------------------------------
|
||||
|
||||
# Grouping the document tree into Texinfo files. List of tuples
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
(master_doc, 'alsaaudiodocumentation', u'alsaaudio documentation Documentation',
|
||||
author, 'alsaaudiodocumentation', 'One line description of project.',
|
||||
'Miscellaneous'),
|
||||
]
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#latex_preamble = ''
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#latex_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#latex_use_modindex = True
|
||||
|
||||
@@ -1,22 +1,32 @@
|
||||
.. alsaaudio documentation documentation master file, created by
|
||||
sphinx-quickstart on Thu Mar 30 23:52:21 2017.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
alsaaudio documentation
|
||||
=======================
|
||||
===================================================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Contents:
|
||||
|
||||
pyalsaaudio
|
||||
terminology
|
||||
libalsaaudio
|
||||
|
||||
Download
|
||||
========
|
||||
|
||||
Github pages
|
||||
=================
|
||||
|
||||
* `Project page <https://github.com/larsimmisch/pyalsaaudio/>`_
|
||||
* `Download from pypi <https://pypi.python.org/pypi/pyalsaaudio>`_
|
||||
|
||||
|
||||
Github
|
||||
======
|
||||
|
||||
* `Repository <https://github.com/larsimmisch/pyalsaaudio/>`_
|
||||
* `Bug tracker <https://github.com/larsimmisch/pyalsaaudio/issues>`_
|
||||
|
||||
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
@@ -24,3 +34,5 @@ Indices and tables
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -193,10 +193,10 @@ PCM objects have the following methods:
|
||||
``PCM_FORMAT_S16_BE`` Signed 16 bit samples for each channel (Big Endian byte order)
|
||||
``PCM_FORMAT_U16_LE`` Unsigned 16 bit samples for each channel (Little Endian byte order)
|
||||
``PCM_FORMAT_U16_BE`` Unsigned 16 bit samples for each channel (Big Endian byte order)
|
||||
``PCM_FORMAT_S24_LE`` Signed 24 bit samples for each channel (Little Endian byte order)
|
||||
``PCM_FORMAT_S24_BE`` Signed 24 bit samples for each channel (Big Endian byte order)}
|
||||
``PCM_FORMAT_U24_LE`` Unsigned 24 bit samples for each channel (Little Endian byte order)
|
||||
``PCM_FORMAT_U24_BE`` Unsigned 24 bit samples for each channel (Big Endian byte order)
|
||||
``PCM_FORMAT_S24_LE`` Signed 24 bit samples for each channel (Little Endian byte order in 4 bytes)
|
||||
``PCM_FORMAT_S24_BE`` Signed 24 bit samples for each channel (Big Endian byte order in 4 bytes)
|
||||
``PCM_FORMAT_U24_LE`` Unsigned 24 bit samples for each channel (Little Endian byte order in 4 bytes)
|
||||
``PCM_FORMAT_U24_BE`` Unsigned 24 bit samples for each channel (Big Endian byte order in 4 bytes)
|
||||
``PCM_FORMAT_S32_LE`` Signed 32 bit samples for each channel (Little Endian byte order)
|
||||
``PCM_FORMAT_S32_BE`` Signed 32 bit samples for each channel (Big Endian byte order)
|
||||
``PCM_FORMAT_U32_LE`` Unsigned 32 bit samples for each channel (Little Endian byte order)
|
||||
@@ -210,6 +210,10 @@ PCM objects have the following methods:
|
||||
``PCM_FORMAT_IMA_ADPCM`` A 4:1 compressed format defined by the Interactive Multimedia Association.
|
||||
``PCM_FORMAT_MPEG`` MPEG encoded audio?
|
||||
``PCM_FORMAT_GSM`` 9600 bits/s constant rate encoding for speech
|
||||
``PCM_FORMAT_S24_3LE`` Signed 24 bit samples for each channel (Little Endian byte order in 3 bytes)
|
||||
``PCM_FORMAT_S24_3BE`` Signed 24 bit samples for each channel (Big Endian byte order in 3 bytes)
|
||||
``PCM_FORMAT_U24_3LE`` Unsigned 24 bit samples for each channel (Little Endian byte order in 3 bytes)
|
||||
``PCM_FORMAT_U24_3BE`` Unsigned 24 bit samples for each channel (Big Endian byte order in 3 bytes)
|
||||
========================= ===============
|
||||
|
||||
|
||||
@@ -233,6 +237,9 @@ PCM objects have the following methods:
|
||||
``(0,'')`` if no new period has become available since the last
|
||||
call to read.
|
||||
|
||||
In case of an overrun, this function will return a negative size: :const:`-EPIPE`.
|
||||
This indicates that data was lost, even if the operation itself succeeded.
|
||||
Try using a larger periodsize.
|
||||
|
||||
.. method:: PCM.write(data)
|
||||
|
||||
@@ -256,6 +263,17 @@ PCM objects have the following methods:
|
||||
If *enable* is :const:`True`, playback or capture is paused.
|
||||
Otherwise, playback/capture is resumed.
|
||||
|
||||
|
||||
.. method:: PCM.polldescriptors()
|
||||
|
||||
Returns a tuple of *(file descriptor, eventmask)* that can be used to
|
||||
wait for changes on the mixer with *select.poll*.
|
||||
|
||||
The *eventmask* value is compatible with `poll.register`__ in the Python
|
||||
:const:`select` module.
|
||||
|
||||
__ poll_objects_
|
||||
|
||||
**A few hints on using PCM devices for playback**
|
||||
|
||||
The most common reason for problems with playback of PCM audio is that writes
|
||||
@@ -473,9 +491,20 @@ Mixer objects have the following methods:
|
||||
|
||||
.. method:: Mixer.polldescriptors()
|
||||
|
||||
Returns a tuple of (file descriptor, eventmask) that can be used to
|
||||
Returns a tuple of *(file descriptor, eventmask)* that can be used to
|
||||
wait for changes on the mixer with *select.poll*.
|
||||
|
||||
The *eventmask* value is compatible with `poll.register`__ in the Python
|
||||
:const:`select` module.
|
||||
|
||||
__ poll_objects_
|
||||
|
||||
.. 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.
|
||||
@@ -614,3 +643,5 @@ argument::
|
||||
.. rubric:: Footnotes
|
||||
|
||||
.. [#f1] ALSA also allows ``PCM_ASYNC``, but this is not supported yet.
|
||||
|
||||
.. _poll_objects: http://docs.python.org/library/select.html#poll-objects
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -75,7 +75,7 @@ development at the time - and neither are very feature complete.
|
||||
I wrote PyAlsaAudio to fill this gap. My long term goal is to have the module
|
||||
included in the standard Python library, but that looks currently unlikely.
|
||||
|
||||
PyAlsaAudio hass full support for sound capture, playback of sound, as well as
|
||||
PyAlsaAudio has full support for sound capture, playback of sound, as well as
|
||||
the ALSA Mixer API.
|
||||
|
||||
MIDI support is not available, and since I don't own any MIDI hardware, it's
|
||||
@@ -110,25 +110,32 @@ And then as root: --- ::
|
||||
Testing
|
||||
*******
|
||||
|
||||
First of all, run::
|
||||
|
||||
$ python test.py
|
||||
Make sure that :code:`aplay` plays a file through the soundcard you want, then
|
||||
try::
|
||||
|
||||
This is a small test suite that mostly performs consistency tests. If
|
||||
it fails, please file a `bug report
|
||||
<https://github.com/larsimmisch/pyalsaaudio/issues>`_.
|
||||
$ python playwav.py <filename.wav>
|
||||
|
||||
If :code:`aplay` needs a device argument, like
|
||||
:code:`aplay -D hw:CARD=sndrpihifiberry,DEV=0`, use::
|
||||
|
||||
$ python playwav.py -d hw:CARD=sndrpihifiberry,DEV=0 <filename.wav>
|
||||
|
||||
To test PCM recordings (on your default soundcard), verify your
|
||||
microphone works, then do::
|
||||
|
||||
$ python recordtest.py <filename>
|
||||
$ python recordtest.py -d <device> <filename>
|
||||
|
||||
Speak into the microphone, and interrupt the recording at any time
|
||||
with ``Ctl-C``.
|
||||
|
||||
Play back the recording with::
|
||||
|
||||
$ python playbacktest.py <filename>
|
||||
$ python playbacktest.py-d <device> <filename>
|
||||
|
||||
There is a minimal test suite in :code:`test.py`, but it is
|
||||
a bit dependent on the ALSA configuration and may fail without indicating
|
||||
a real problem.
|
||||
|
||||
If you find bugs/problems, please file a `bug report
|
||||
<https://github.com/larsimmisch/pyalsaaudio/issues>`_.
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ Data rate
|
||||
|
||||
At the other end of the scale, 96000 Hz, 6 channel sound with 64
|
||||
bit (8 bytes) samples has a data rate of 96000 \* 6 \* 8 = 4608
|
||||
kb/s (almost 5 Mb sound data per second)
|
||||
kb/s (almost 5 MB sound data per second)
|
||||
|
||||
Period
|
||||
When the hardware processes data this is done in chunks of frames. The time
|
||||
|
||||
66
isine.py
66
isine.py
@@ -6,30 +6,50 @@
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import sys
|
||||
from threading import Thread
|
||||
from queue import Queue, Empty
|
||||
from multiprocessing import Queue
|
||||
|
||||
if sys.version_info[0] < 3:
|
||||
from Queue import Empty
|
||||
else:
|
||||
from queue import Empty
|
||||
|
||||
from math import pi, sin
|
||||
import struct
|
||||
import alsaaudio
|
||||
|
||||
sampling_rate = 44100
|
||||
sampling_rate = 48000
|
||||
|
||||
format = alsaaudio.PCM_FORMAT_S16_LE
|
||||
framesize = 2 # bytes per frame for the values above
|
||||
channels = 2
|
||||
|
||||
def digitize(s):
|
||||
if s > 1.0 or s < -1.0:
|
||||
raise ValueError
|
||||
|
||||
return struct.pack('h', int(s * 32767))
|
||||
def nearest_frequency(frequency):
|
||||
# calculate the nearest frequency where the wave form fits into the buffer
|
||||
# in other words, select f so that sampling_rate/f is an integer
|
||||
return float(sampling_rate)/int(sampling_rate/frequency)
|
||||
|
||||
def generate(frequency):
|
||||
# generate a buffer with a sine wave of frequency
|
||||
size = int(sampling_rate / frequency)
|
||||
buffer = bytes()
|
||||
for i in range(size):
|
||||
buffer = buffer + digitize(sin(i/(2 * pi)))
|
||||
def generate(frequency, duration = 0.125):
|
||||
# generate a buffer with a sine wave of `frequency`
|
||||
# that is approximately `duration` seconds long
|
||||
|
||||
return buffer
|
||||
# the buffersize we approximately want
|
||||
target_size = int(sampling_rate * channels * duration)
|
||||
|
||||
# the length of a full sine wave at the frequency
|
||||
cycle_size = int(sampling_rate / frequency)
|
||||
|
||||
# number of full cycles we can fit into target_size
|
||||
factor = int(target_size / cycle_size)
|
||||
|
||||
size = max(int(cycle_size * factor), 1)
|
||||
|
||||
sine = [ int(32767 * sin(2 * pi * frequency * i / sampling_rate)) \
|
||||
for i in range(size)]
|
||||
|
||||
return struct.pack('%dh' % size, *sine)
|
||||
|
||||
|
||||
class SinePlayer(Thread):
|
||||
|
||||
@@ -37,7 +57,7 @@ class SinePlayer(Thread):
|
||||
Thread.__init__(self)
|
||||
self.setDaemon(True)
|
||||
self.device = alsaaudio.PCM()
|
||||
self.device.setchannels(1)
|
||||
self.device.setchannels(channels)
|
||||
self.device.setformat(format)
|
||||
self.device.setrate(sampling_rate)
|
||||
self.queue = Queue()
|
||||
@@ -47,19 +67,15 @@ class SinePlayer(Thread):
|
||||
'''This is called outside of the player thread'''
|
||||
# we generate the buffer in the calling thread for less
|
||||
# latency when switching frequencies
|
||||
|
||||
|
||||
# More than 100 writes/s are pushing it - play multiple buffers
|
||||
# for higher frequencies
|
||||
if frequency > sampling_rate / 2:
|
||||
raise ValueError('maximum frequency is %d' % (sampling_rate / 2))
|
||||
|
||||
factor = int(frequency/100.0)
|
||||
if factor == 0:
|
||||
factor = 1
|
||||
|
||||
buf = generate(frequency) * factor
|
||||
print('factor: %d, frames: %d' % (factor, len(buf) / framesize))
|
||||
f = nearest_frequency(frequency)
|
||||
print('nearest frequency: %f' % f)
|
||||
|
||||
self.queue.put( buf)
|
||||
buf = generate(f)
|
||||
self.queue.put(buf)
|
||||
|
||||
def run(self):
|
||||
buffer = None
|
||||
|
||||
@@ -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)
|
||||
|
||||
22
playwav.py
22
playwav.py
@@ -24,39 +24,41 @@ def play(device, f):
|
||||
elif f.getsampwidth() == 2:
|
||||
device.setformat(alsaaudio.PCM_FORMAT_S16_LE)
|
||||
elif f.getsampwidth() == 3:
|
||||
device.setformat(alsaaudio.PCM_FORMAT_S24_LE)
|
||||
device.setformat(alsaaudio.PCM_FORMAT_S24_3LE)
|
||||
elif f.getsampwidth() == 4:
|
||||
device.setformat(alsaaudio.PCM_FORMAT_S32_LE)
|
||||
else:
|
||||
raise ValueError('Unsupported format')
|
||||
|
||||
device.setperiodsize(320)
|
||||
periodsize = f.getframerate() // 8
|
||||
|
||||
device.setperiodsize(periodsize)
|
||||
|
||||
data = f.readframes(320)
|
||||
data = f.readframes(periodsize)
|
||||
while data:
|
||||
# Read data from stdin
|
||||
device.write(data)
|
||||
data = f.readframes(320)
|
||||
data = f.readframes(periodsize)
|
||||
|
||||
|
||||
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)
|
||||
|
||||
20
setup.py
20
setup.py
@@ -4,25 +4,11 @@
|
||||
It is fairly complete for PCM devices and Mixer access.
|
||||
'''
|
||||
|
||||
import subprocess
|
||||
from distutils.core import setup
|
||||
from distutils.extension import Extension
|
||||
from setuptools import setup
|
||||
from setuptools.extension import Extension
|
||||
from sys import version
|
||||
|
||||
def gitrev():
|
||||
rev = subprocess.check_output(['git', 'describe', '--tags', '--dirty=-dev',
|
||||
'--always'])
|
||||
return rev.decode('utf-8').strip()
|
||||
|
||||
pyalsa_version = gitrev()
|
||||
|
||||
# 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.6'
|
||||
|
||||
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