forked from auracaster/pyalsaaudio
Compare commits
22 Commits
volume-db
...
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 |
29
CHANGES
29
CHANGES
@@ -1,3 +1,32 @@
|
||||
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)
|
||||
|
||||
|
||||
584
alsaaudio.c
584
alsaaudio.c
@@ -29,6 +29,65 @@
|
||||
#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"
|
||||
@@ -299,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) {
|
||||
@@ -399,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);
|
||||
}
|
||||
@@ -440,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);
|
||||
}
|
||||
@@ -572,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)
|
||||
{
|
||||
@@ -634,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;
|
||||
@@ -791,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)
|
||||
@@ -810,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,
|
||||
@@ -950,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)
|
||||
{
|
||||
@@ -1013,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},
|
||||
@@ -1072,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 */
|
||||
};
|
||||
|
||||
|
||||
@@ -2354,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 */
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -30,7 +30,7 @@ def play(device, f):
|
||||
else:
|
||||
raise ValueError('Unsupported format')
|
||||
|
||||
periodsize = f.getframerate() / 8
|
||||
periodsize = f.getframerate() // 8
|
||||
|
||||
device.setperiodsize(periodsize)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user