diff --git a/alsaaudio.c b/alsaaudio.c index 1b60f57..a9bfc58 100644 --- a/alsaaudio.c +++ b/alsaaudio.c @@ -86,7 +86,10 @@ static const unsigned ALSARates[] = { 88200, 96000, 176400, - 192000 + 192000, + 352800, + 384000 + }; typedef enum volume_units_t { @@ -95,15 +98,6 @@ typedef enum volume_units_t { VOLUME_UNITS_DB, } volume_units_t; -PyDoc_STRVAR(alsaaudio_module_doc, - "This modules provides support for the ALSA audio API.\n" - "\n" - "To control the PCM device, use the PCM class, Mixers\n" - "are controlled using the Mixer class.\n" - "\n" - "The following functions are also provided:\n" - "mixers() -- Return a list of available mixer names\n"); - typedef struct { PyObject_HEAD; long pcmtype; @@ -113,9 +107,10 @@ typedef struct { snd_pcm_t *handle; // Configurable parameters - int channels; + unsigned int channels; unsigned int rate; - int format; + snd_pcm_format_t format; + unsigned int periods; snd_pcm_uframes_t periodsize; int framesize; @@ -179,11 +174,18 @@ get_pcmtype(PyObject *obj) #endif if (PyUnicode_Check(obj)) { +#if PY_MAJOR_VERSION > 2 + if (PyUnicode_CompareWithASCIIString(obj, "playback") == 0) + return SND_PCM_STREAM_PLAYBACK; + else if (PyUnicode_CompareWithASCIIString(obj, "capture") == 0) + return SND_PCM_STREAM_CAPTURE; +#else const char *dirstr = PyUnicode_AS_DATA(obj); if (strcasecmp(dirstr, "playback")==0) return SND_PCM_STREAM_PLAYBACK; else if (strcasecmp(dirstr, "capture")==0) return SND_PCM_STREAM_CAPTURE; +#endif } PyErr_SetString(ALSAAudioError, "PCM type must be PCM_PLAYBACK (0) " @@ -250,12 +252,6 @@ alsacard_list(PyObject *self, PyObject *args) return result; } -PyDoc_STRVAR(cards_doc, -"cards()\n\ -\n\ -List the available card ids."); - - static PyObject * alsacard_list_indexes(PyObject *self, PyObject *args) { @@ -280,12 +276,6 @@ alsacard_list_indexes(PyObject *self, PyObject *args) return result; } -PyDoc_STRVAR(card_indexes_doc, -"card_indexes()\n\ -\n\ -List the available card indexes."); - - static PyObject * alsacard_name(PyObject *self, PyObject *args) { @@ -319,12 +309,6 @@ exit: 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, PyObject *kwds) { @@ -376,17 +360,9 @@ alsapcm_list(PyObject *self, PyObject *args, PyObject *kwds) return result; } -PyDoc_STRVAR(pcms_doc, -"pcms([pcmtype])\n\ -\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, @@ -402,7 +378,6 @@ static int alsapcm_setup(alsapcm_t *self) 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); @@ -410,11 +385,10 @@ static int alsapcm_setup(alsapcm_t *self) self->channels); dir = 0; - unsigned int periods = 4; snd_pcm_hw_params_set_rate_near(self->handle, hwparams, &self->rate, &dir); snd_pcm_hw_params_set_period_size_near(self->handle, hwparams, &self->periodsize, &dir); - snd_pcm_hw_params_set_periods_near(self->handle, hwparams, &periods, &dir); + snd_pcm_hw_params_set_periods_near(self->handle, hwparams, &self->periods, &dir); /* Write it to the device */ res = snd_pcm_hw_params(self->handle, hwparams); @@ -423,11 +397,11 @@ static int alsapcm_setup(alsapcm_t *self) 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; + snd_pcm_hw_params_get_format(hwparams, &self->format); + snd_pcm_hw_params_get_channels(hwparams, &self->channels); + snd_pcm_hw_params_get_rate(hwparams, &self->rate, &dir); + snd_pcm_hw_params_get_period_size(hwparams, &self->periodsize, &dir); + snd_pcm_hw_params_get_periods(hwparams, &self->periods, &dir); self->framesize = self->channels * snd_pcm_hw_params_get_sbits(hwparams)/8; @@ -449,13 +423,15 @@ alsapcm_new(PyTypeObject *type, PyObject *args, PyObject *kwds) int rate = 44100; int channels = 2; int format = SND_PCM_FORMAT_S16_LE; + int periods = 4; int periodsize = 32; - char *kw[] = { "type", "mode", "device", "cardindex", "card", "rate", "channels", "format", "periodsize", NULL }; + char *kw[] = { "type", "mode", "device", "cardindex", "card", + "rate", "channels", "format", "periodsize", "periods", NULL }; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Oisiziiii", kw, - &pcmtypeobj, &pcmmode, &device, - &cardidx, &card, &rate, &channels, &format, &periodsize)) + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Oisiziiiii", kw, + &pcmtypeobj, &pcmmode, &device, &cardidx, &card, + &rate, &channels, &format, &periodsize, &periods)) return NULL; if (cardidx >= 0) { @@ -502,6 +478,7 @@ alsapcm_new(PyTypeObject *type, PyObject *args, PyObject *kwds) self->channels = channels; self->rate = rate; self->format = format; + self->periods = periods; self->periodsize = periodsize; res = snd_pcm_open(&(self->handle), device, self->pcmtype, @@ -528,11 +505,8 @@ 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); + if (self->handle) snd_pcm_close(self->handle); - } free(self->cardname); PyObject_Del(self); } @@ -545,10 +519,12 @@ alsapcm_close(alsapcm_t *self, PyObject *args) if (self->handle) { - Py_BEGIN_ALLOW_THREADS - snd_pcm_drain(self->handle); + if (self->pcmtype == SND_PCM_STREAM_PLAYBACK) { + Py_BEGIN_ALLOW_THREADS + snd_pcm_drain(self->handle); + Py_END_ALLOW_THREADS + } snd_pcm_close(self->handle); - Py_END_ALLOW_THREADS self->handle = 0; } @@ -557,16 +533,13 @@ alsapcm_close(alsapcm_t *self, PyObject *args) return Py_None; } -PyDoc_STRVAR(pcm_close_doc, -"close() -> None\n\ -\n\ -Close a PCM device."); - static PyObject * alsapcm_dumpinfo(alsapcm_t *self, PyObject *args) { unsigned int val,val2; + snd_pcm_access_t acc; snd_pcm_format_t fmt; + snd_pcm_subformat_t subfmt; int dir; snd_pcm_uframes_t frames; snd_pcm_hw_params_t *hwparams; @@ -585,18 +558,18 @@ alsapcm_dumpinfo(alsapcm_t *self, PyObject *args) printf("PCM state = %s\n", snd_pcm_state_name(snd_pcm_state(self->handle))); - snd_pcm_hw_params_get_access(hwparams, (snd_pcm_access_t *) &val); - printf("access type = %s\n", snd_pcm_access_name((snd_pcm_access_t)val)); + snd_pcm_hw_params_get_access(hwparams, &acc); + printf("access type = %s\n", snd_pcm_access_name(acc)); snd_pcm_hw_params_get_format(hwparams, &fmt); printf("format = '%s' (%s)\n", snd_pcm_format_name(fmt), snd_pcm_format_description(fmt)); - snd_pcm_hw_params_get_subformat(hwparams, (snd_pcm_subformat_t *)&val); + snd_pcm_hw_params_get_subformat(hwparams, &subfmt); printf("subformat = '%s' (%s)\n", - snd_pcm_subformat_name((snd_pcm_subformat_t)val), - snd_pcm_subformat_description((snd_pcm_subformat_t)val)); + snd_pcm_subformat_name(subfmt), + snd_pcm_subformat_description(subfmt)); snd_pcm_hw_params_get_channels(hwparams, &val); printf("channels = %d\n", val); @@ -613,11 +586,11 @@ alsapcm_dumpinfo(alsapcm_t *self, PyObject *args) snd_pcm_hw_params_get_buffer_time(hwparams, &val, &dir); printf("buffer time = %d us\n", val); - snd_pcm_hw_params_get_buffer_size(hwparams, (snd_pcm_uframes_t *) &val); - printf("buffer size = %d frames\n", val); + snd_pcm_hw_params_get_buffer_size(hwparams, &frames); + printf("buffer size = %d frames\n", (int)frames); snd_pcm_hw_params_get_periods(hwparams, &val, &dir); - printf("periods per buffer = %d frames\n", val); + printf("periods per buffer = %d\n", val); snd_pcm_hw_params_get_rate_numden(hwparams, &val, &val2); printf("exact rate = %d/%d bps\n", val, val2); @@ -659,7 +632,6 @@ alsapcm_dumpinfo(alsapcm_t *self, PyObject *args) return Py_None; } - static PyObject * alsapcm_info(alsapcm_t *self, PyObject *args) { @@ -667,7 +639,9 @@ alsapcm_info(alsapcm_t *self, PyObject *args) PyObject *value; unsigned int val,val2; + snd_pcm_access_t acc; snd_pcm_format_t fmt; + snd_pcm_subformat_t subfmt; int dir; snd_pcm_uframes_t frames; snd_pcm_hw_params_t *hwparams; @@ -709,8 +683,8 @@ alsapcm_info(alsapcm_t *self, PyObject *args) PyDict_SetItemString(info,"state",value); Py_DECREF(value); - snd_pcm_hw_params_get_access(hwparams, (snd_pcm_access_t *) &val); - value=PyUnicode_FromString(snd_pcm_access_name((snd_pcm_access_t)val)); + snd_pcm_hw_params_get_access(hwparams, &acc); + value=PyUnicode_FromString(snd_pcm_access_name(acc)); PyDict_SetItemString(info,"access_type",value); Py_DECREF(value); @@ -750,11 +724,11 @@ alsapcm_info(alsapcm_t *self, PyObject *args) Py_DECREF(value); - snd_pcm_hw_params_get_subformat(hwparams, (snd_pcm_subformat_t *)&val); - value=PyUnicode_FromString(snd_pcm_subformat_name((snd_pcm_subformat_t)val)); + snd_pcm_hw_params_get_subformat(hwparams, &subfmt); + value=PyUnicode_FromString(snd_pcm_subformat_name(subfmt)); PyDict_SetItemString(info,"subformat_name",value); Py_DECREF(value); - value=PyUnicode_FromString(snd_pcm_subformat_description((snd_pcm_subformat_t)val)); + value=PyUnicode_FromString(snd_pcm_subformat_description(subfmt)); PyDict_SetItemString(info,"subformat_description",value); Py_DECREF(value); @@ -783,14 +757,14 @@ alsapcm_info(alsapcm_t *self, PyObject *args) PyDict_SetItemString(info,"buffer_time", value); Py_DECREF(value); - snd_pcm_hw_params_get_buffer_size(hwparams, (snd_pcm_uframes_t *) &val); - value=PyLong_FromUnsignedLong((unsigned long) val); + snd_pcm_hw_params_get_buffer_size(hwparams, &frames); + value=PyLong_FromUnsignedLong((unsigned long) frames); PyDict_SetItemString(info,"buffer_size", value); Py_DECREF(value); snd_pcm_hw_params_get_periods(hwparams, &val, &dir); value=PyLong_FromUnsignedLong((unsigned long) val); - PyDict_SetItemString(info,"get_periods", value); + PyDict_SetItemString(info,"periods", value); Py_DECREF(value); snd_pcm_hw_params_get_rate_numden(hwparams, &val, &val2); @@ -857,19 +831,6 @@ alsapcm_info(alsapcm_t *self, PyObject *args) return info; } - -PyDoc_STRVAR(pcm_info_doc, -"info() -> dict\n\ -\n\ -Returns a dictionary with the alsa device parameters as it is realized. \n\ -Keys are retrieved from the alsa library if they can be accessed, if not \n\ -they represent values stored by pyalsaaudio and they are prefixed with ' (call value) '. \n\ -\n\ -For a complete overview of all keys produced see the documentation on PCM.info at: \n\ -https://larsimmisch.github.io/pyalsaaudio/libalsaaudio.html#pcm-objects \n\ -"); - - static PyObject * alsa_asoundlib_version(PyObject * module, PyObject *args) { @@ -879,11 +840,20 @@ alsa_asoundlib_version(PyObject * module, PyObject *args) return PyUnicode_FromString(snd_asoundlib_version()); } -PyDoc_STRVAR(asoundlib_version_doc, -"asoundlib_version() -> string\n\ -\n\ -Returns ALSA version string. \n\ -"); +static PyObject * +alsapcm_state(alsapcm_t *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args,":state")) + return NULL; + + if (!self->handle) + { + PyErr_SetString(ALSAAudioError, "PCM device is closed"); + return NULL; + } + + return PyLong_FromUnsignedLong((unsigned long) snd_pcm_state(self->handle)); +} static PyObject * alsapcm_htimestamp(alsapcm_t *self, PyObject *args) @@ -892,6 +862,11 @@ alsapcm_htimestamp(alsapcm_t *self, PyObject *args) snd_pcm_uframes_t avail; PyObject *result = NULL; + if (!self->handle) { + PyErr_SetString(ALSAAudioError, "PCM device is closed"); + return NULL; + } + snd_pcm_htimestamp(self->handle , &avail, &tstamp); result = PyTuple_New(3); @@ -902,15 +877,6 @@ alsapcm_htimestamp(alsapcm_t *self, PyObject *args) return result; } - -PyDoc_STRVAR(htimestamp_doc, -"htimestamp() -> tuple\n\ -\n\ -Returns a tuple containing the seconds since epoch in the first element \n\ -, nanoseconds in the second element, and number of frames available in \n\ - the buffer at the time of the time stamp. \n"); - - static PyObject * alsapcm_set_tstamp_mode(alsapcm_t *self, PyObject *args) { @@ -944,13 +910,6 @@ alsapcm_set_tstamp_mode(alsapcm_t *self, PyObject *args) return Py_None; } - -PyDoc_STRVAR(set_tstamp_mode_doc, -"set_tstamp_mode() -> None\n\ -\n\ -Set the timestamp mode of the device. \n"); - - static PyObject * alsapcm_get_tstamp_mode(alsapcm_t *self, PyObject *args) { @@ -981,13 +940,6 @@ alsapcm_get_tstamp_mode(alsapcm_t *self, PyObject *args) return PyLong_FromUnsignedLong((unsigned long) mode); } - -PyDoc_STRVAR(get_tstamp_mode_doc, -"get_tstamp_mode() -> integer \n\ -\n\ -Get the timestamp mode of the device. \n"); - - static PyObject * alsapcm_set_tstamp_type(alsapcm_t *self, PyObject *args) { @@ -1021,12 +973,6 @@ alsapcm_set_tstamp_type(alsapcm_t *self, PyObject *args) return Py_None; } - -PyDoc_STRVAR(set_tstamp_type_doc, -"set_tstamp_type() -> None\n\ -\n\ -Set the timestamp type of the device. \n"); - static PyObject * alsapcm_get_tstamp_type(alsapcm_t *self, PyObject *args) { @@ -1058,15 +1004,8 @@ alsapcm_get_tstamp_type(alsapcm_t *self, PyObject *args) } -PyDoc_STRVAR(get_tstamp_type_doc, -"get_tstamp_type() -> int \n\ -\n\ -Get the timestamp type of the device. \n"); - - // auxiliary function - static PyObject * alsapcm_getformats(alsapcm_t *self, PyObject *args) { @@ -1097,11 +1036,6 @@ alsapcm_getformats(alsapcm_t *self, PyObject *args) 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) { @@ -1134,11 +1068,6 @@ alsapcm_getratemaxmin(alsapcm_t *self, PyObject *args) 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) { @@ -1187,17 +1116,6 @@ alsapcm_getrates(alsapcm_t *self, PyObject *args) } } -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) { @@ -1236,12 +1154,6 @@ alsapcm_getchannels(alsapcm_t *self,PyObject *args) 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) { @@ -1256,12 +1168,6 @@ alsapcm_pcmtype(alsapcm_t *self, PyObject *args) return PyLong_FromLong(self->pcmtype); } -PyDoc_STRVAR(pcmtype_doc, -"pcmtype() -> int\n\ -\n\ -Returns either PCM_CAPTURE or PCM_PLAYBACK."); - - static PyObject * alsapcm_pcmmode(alsapcm_t *self, PyObject *args) { @@ -1276,15 +1182,6 @@ alsapcm_pcmmode(alsapcm_t *self, PyObject *args) return PyLong_FromLong(self->pcmmode); } -PyDoc_STRVAR(pcmmode_doc, -"pcmmode() -> int\n\ -\n\ -Returns the mode of the PCM object. One of:\n\ - - PCM_NONBLOCK\n\ - - PCM_ASYNC\n\ - - PCM_NORMAL."); - - static PyObject * alsapcm_cardname(alsapcm_t *self, PyObject *args) { @@ -1299,12 +1196,6 @@ alsapcm_cardname(alsapcm_t *self, PyObject *args) return PyUnicode_FromString(self->cardname); } -PyDoc_STRVAR(cardname_doc, -"cardname() -> string\n\ -\n\ -Returns the name of the sound card used by this PCM object."); - - static PyObject * alsapcm_setchannels(alsapcm_t *self, PyObject *args) { @@ -1336,17 +1227,6 @@ alsapcm_setchannels(alsapcm_t *self, PyObject *args) return PyLong_FromLong(self->channels); } -PyDoc_STRVAR(setchannels_doc, -"setchannels(numchannels)\n\ -\n\ -Deprecated since 0.9\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) { @@ -1379,15 +1259,6 @@ alsapcm_setrate(alsapcm_t *self, PyObject *args) return PyLong_FromLong(self->rate); } -PyDoc_STRVAR(setrate_doc, -"setrate(rate)\n\ -\n\ -Deprecated since 0.9\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) { @@ -1420,11 +1291,6 @@ alsapcm_setformat(alsapcm_t *self, PyObject *args) return PyLong_FromLong(self->format); } -PyDoc_STRVAR(setformat_doc, -"setformat(rate)\n\ -\n\ -Deprecated since 0.9"); - static PyObject * alsapcm_setperiodsize(alsapcm_t *self, PyObject *args) { @@ -1461,19 +1327,10 @@ alsapcm_setperiodsize(alsapcm_t *self, PyObject *args) return PyLong_FromLong(self->periodsize); } -PyDoc_STRVAR(setperiodsize_doc, -"setperiodsize(period) -> int\n\ -\n\ -Deprecated since 0.9\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) { + snd_pcm_state_t state; int res; int size = self->framesize * self->periodsize; int sizeout = 0; @@ -1507,14 +1364,13 @@ alsapcm_read(alsapcm_t *self, PyObject *args) buffer = PyBytes_AS_STRING(buffer_obj); #endif - Py_BEGIN_ALLOW_THREADS - res = snd_pcm_readi(self->handle, buffer, self->periodsize); - if (res == -EPIPE) - { - /* EPIPE means overrun */ - snd_pcm_prepare(self->handle); + state = snd_pcm_state(self->handle); + if ((state != SND_PCM_STATE_XRUN && state != SND_PCM_STATE_SETUP) || + (res = snd_pcm_prepare(self->handle)) >= 0) { + Py_BEGIN_ALLOW_THREADS + res = snd_pcm_readi(self->handle, buffer, self->periodsize); + Py_END_ALLOW_THREADS } - Py_END_ALLOW_THREADS if (res != -EPIPE) { @@ -1565,25 +1421,9 @@ alsapcm_read(alsapcm_t *self, PyObject *args) return tuple_obj; } -PyDoc_STRVAR(read_doc, -"read() -> (size, data)\n\ -\n\ -In PCM_NORMAL mode, this function blocks until a full period is\n\ -available, and then returns a tuple (length,data) where length is\n\ -the number of frames of the captured data, and data is the captured sound\n\ -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.\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) { + snd_pcm_state_t state; int res; int datalen; char *data; @@ -1615,16 +1455,13 @@ static PyObject *alsapcm_write(alsapcm_t *self, PyObject *args) return NULL; } - Py_BEGIN_ALLOW_THREADS - res = snd_pcm_writei(self->handle, data, datalen/self->framesize); - if (res == -EPIPE) - { - /* EPIPE means underrun */ - res = snd_pcm_recover(self->handle, res, 1); - if (res >= 0) - res = snd_pcm_writei(self->handle, data, datalen/self->framesize); + state = snd_pcm_state(self->handle); + if ((state != SND_PCM_STATE_XRUN && state != SND_PCM_STATE_SETUP) || + (res = snd_pcm_prepare(self->handle)) >= 0) { + Py_BEGIN_ALLOW_THREADS + res = snd_pcm_writei(self->handle, data, datalen/self->framesize); + Py_END_ALLOW_THREADS } - Py_END_ALLOW_THREADS if (res == -EAGAIN) { rc = PyLong_FromLong(0); @@ -1645,23 +1482,6 @@ static PyObject *alsapcm_write(alsapcm_t *self, PyObject *args) return rc; } -PyDoc_STRVAR(write_doc, -"write(data) -> bytes written\n\ -\n\ -Writes (plays) the sound in data. The length of data must be a multiple\n\ -of the frame size, and should be exactly the size of a period. If less\n\ -than 'period size' frames are provided, the actual playout will not\n\ -happen until more data is written.\n\ -If the device is not in PCM_NONBLOCK mode, this call will block if the\n\ -kernel buffer is full, and until enough sound has been played to allow\n\ -the sound data to be buffered. The call always returns the size of the\n\ -data provided.\n\ -\n\ -In PCM_NONBLOCK mode, the call will return immediately, with a return\n\ -value of zero, if the buffer is full. In this case, the data should be\n\ -written at a later time."); - - static PyObject *alsapcm_pause(alsapcm_t *self, PyObject *args) { int enabled=1, res; @@ -1674,10 +1494,7 @@ static PyObject *alsapcm_pause(alsapcm_t *self, PyObject *args) return NULL; } - Py_BEGIN_ALLOW_THREADS res = snd_pcm_pause(self->handle, enabled); - Py_END_ALLOW_THREADS - if (res < 0) { PyErr_Format(ALSAAudioError, "%s [%s]", snd_strerror(res), @@ -1688,13 +1505,6 @@ static PyObject *alsapcm_pause(alsapcm_t *self, PyObject *args) return PyLong_FromLong(res); } -PyDoc_STRVAR(pause_doc, -"pause(enable=1)\n\ -\n\ -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; @@ -1714,7 +1524,22 @@ static PyObject *alsapcm_drop(alsapcm_t *self) return NULL; } - res = snd_pcm_prepare(self->handle); + return PyLong_FromLong(res); +} + +static PyObject *alsapcm_drain(alsapcm_t *self) +{ + int res; + + if (!self->handle) { + PyErr_SetString(ALSAAudioError, "PCM device is closed"); + return NULL; + } + + Py_BEGIN_ALLOW_THREADS + res = snd_pcm_drain(self->handle); + Py_END_ALLOW_THREADS + if (res < 0) { PyErr_Format(ALSAAudioError, "%s [%s]", snd_strerror(res), @@ -1726,11 +1551,6 @@ static PyObject *alsapcm_drop(alsapcm_t *self) 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) { @@ -1769,6 +1589,7 @@ alsapcm_polldescriptors(alsapcm_t *self, PyObject *args) { PyErr_Format(ALSAAudioError, "Can't get poll descriptors [%s]", self->cardname); + free(fds); return NULL; } @@ -1777,52 +1598,45 @@ alsapcm_polldescriptors(alsapcm_t *self, PyObject *args) PyList_SetItem(result, i, Py_BuildValue("ih", fds[i].fd, fds[i].events)); } + free(fds); return result; } -PyDoc_STRVAR(pcm_polldescriptors_doc, -"polldescriptors() -> List of tuples (fd, eventmask).\n\ -\n\ -Return a list of file descriptors and event masks\n\ -suitable for use with poll."); - - /* ALSA PCM Object Bureaucracy */ 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}, - {"htimestamp", (PyCFunction) alsapcm_htimestamp, METH_VARARGS, htimestamp_doc}, - {"set_tstamp_type", (PyCFunction) alsapcm_set_tstamp_type, METH_VARARGS, set_tstamp_type_doc}, - {"set_tstamp_mode", (PyCFunction) alsapcm_set_tstamp_mode, METH_VARARGS, set_tstamp_mode_doc}, - {"get_tstamp_type", (PyCFunction) alsapcm_get_tstamp_type, METH_VARARGS, get_tstamp_type_doc}, - {"get_tstamp_mode", (PyCFunction) alsapcm_get_tstamp_mode, METH_VARARGS, get_tstamp_mode_doc}, + {"pcmtype", (PyCFunction)alsapcm_pcmtype, METH_VARARGS}, + {"pcmmode", (PyCFunction)alsapcm_pcmmode, METH_VARARGS}, + {"cardname", (PyCFunction)alsapcm_cardname, METH_VARARGS}, + {"setchannels", (PyCFunction)alsapcm_setchannels, METH_VARARGS}, + {"setrate", (PyCFunction)alsapcm_setrate, METH_VARARGS}, + {"setformat", (PyCFunction)alsapcm_setformat, METH_VARARGS}, + {"setperiodsize", (PyCFunction)alsapcm_setperiodsize, METH_VARARGS}, + {"htimestamp", (PyCFunction) alsapcm_htimestamp, METH_VARARGS}, + {"set_tstamp_type", (PyCFunction) alsapcm_set_tstamp_type, METH_VARARGS}, + {"set_tstamp_mode", (PyCFunction) alsapcm_set_tstamp_mode, METH_VARARGS}, + {"get_tstamp_type", (PyCFunction) alsapcm_get_tstamp_type, METH_VARARGS}, + {"get_tstamp_mode", (PyCFunction) alsapcm_get_tstamp_mode, METH_VARARGS}, {"dumpinfo", (PyCFunction)alsapcm_dumpinfo, METH_VARARGS}, - {"info", (PyCFunction)alsapcm_info, METH_VARARGS, pcm_info_doc}, - {"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}, + {"info", (PyCFunction)alsapcm_info, METH_VARARGS}, + {"state", (PyCFunction)alsapcm_state, METH_VARARGS}, + {"getformats", (PyCFunction)alsapcm_getformats, METH_VARARGS}, + {"getratebounds", (PyCFunction)alsapcm_getratemaxmin, METH_VARARGS}, + {"getrates", (PyCFunction)alsapcm_getrates, METH_VARARGS}, + {"getchannels", (PyCFunction)alsapcm_getchannels, METH_VARARGS}, + {"read", (PyCFunction)alsapcm_read, METH_VARARGS}, + {"write", (PyCFunction)alsapcm_write, METH_VARARGS}, + {"pause", (PyCFunction)alsapcm_pause, METH_VARARGS}, + {"drop", (PyCFunction)alsapcm_drop, METH_VARARGS}, + {"drain", (PyCFunction)alsapcm_drain, METH_VARARGS}, + {"close", (PyCFunction)alsapcm_close, METH_VARARGS}, + {"polldescriptors", (PyCFunction)alsapcm_polldescriptors, METH_VARARGS}, {NULL, NULL} }; static PyMethodDef alsa_methods[] = { - {"asoundlib_version", (PyCFunction) alsa_asoundlib_version, METH_VARARGS, asoundlib_version_doc}, + {"asoundlib_version", (PyCFunction) alsa_asoundlib_version, METH_VARARGS}, {NULL, NULL} }; @@ -1904,16 +1718,18 @@ static PyTypeObject ALSAMixerType; #define MIXER_CHANNEL_ALL -1 -int +static int alsamixer_gethandle(char *cardname, snd_mixer_t **handle) { int err; - if ((err = snd_mixer_open(handle, 0)) < 0) return err; - if ((err = snd_mixer_attach(*handle, cardname)) < 0) return err; - if ((err = snd_mixer_selem_register(*handle, NULL, NULL)) < 0) return err; - if ((err = snd_mixer_load(*handle)) < 0) return err; - - return 0; + if ((err = snd_mixer_open(handle, 0)) < 0) + return err; + if ((err = snd_mixer_attach(*handle, cardname)) >= 0 && + (err = snd_mixer_selem_register(*handle, NULL, NULL)) >= 0 && + (err = snd_mixer_load(*handle)) >= 0) + return 0; + snd_mixer_close(*handle); + return err; } static PyObject * @@ -1950,7 +1766,6 @@ alsamixer_list(PyObject *self, PyObject *args, PyObject *kwds) if (err < 0) { PyErr_Format(ALSAAudioError, "%s [%s]", snd_strerror(err), device); - snd_mixer_close(handle); return NULL; } @@ -1970,14 +1785,6 @@ alsamixer_list(PyObject *self, PyObject *args, PyObject *kwds) return result; } -PyDoc_STRVAR(mixers_doc, -"mixers([cardname])\n\ -\n\ -List the available mixers. The optional cardname specifies\n\ -which card should be queried (this is only relevant if you\n\ -have more than one sound card). Omit to use the default sound card."); - - static snd_mixer_elem_t * alsamixer_find_elem(snd_mixer_t *handle, char *control, int id) { @@ -2145,20 +1952,17 @@ alsamixer_close(alsamixer_t *self, PyObject *args) if (!PyArg_ParseTuple(args,":close")) return NULL; - snd_mixer_close(self->handle); - free(self->cardname); - free(self->controlname); - self->handle = 0; + if (self->handle) { + snd_mixer_close(self->handle); + free(self->cardname); + free(self->controlname); + self->handle = 0; + } Py_INCREF(Py_None); return Py_None; } -PyDoc_STRVAR(mixer_close_doc, -"close() -> None\n\ -\n\ -Close a Mixer."); - static PyObject * alsamixer_cardname(alsamixer_t *self, PyObject *args) { @@ -2174,11 +1978,6 @@ alsamixer_cardname(alsamixer_t *self, PyObject *args) return PyUnicode_FromString(self->cardname); } -PyDoc_STRVAR(mixer_cardname_doc, -"cardname() -> string\n\ -\n\ -Returns the name of the sound card used by this Mixer object."); - static PyObject * alsamixer_mixer(alsamixer_t *self, PyObject *args) { @@ -2194,13 +1993,6 @@ alsamixer_mixer(alsamixer_t *self, PyObject *args) return PyUnicode_FromString(self->controlname); } -PyDoc_STRVAR(mixer_doc, -"mixer() -> string\n\ -\n\ -Returns the name of the specific mixer controlled by this object,\n\ -for example 'Master' or 'PCM'"); - - static PyObject * alsamixer_mixerid(alsamixer_t *self, PyObject *args) { @@ -2216,12 +2008,6 @@ alsamixer_mixerid(alsamixer_t *self, PyObject *args) return PyLong_FromLong(self->controlid); } -PyDoc_STRVAR(mixerid_doc, -"mixerid() -> int\n\ -\n\ -Returns the ID of the ALSA mixer controlled by this object."); - - static PyObject * alsamixer_volumecap(alsamixer_t *self, PyObject *args) { @@ -2277,19 +2063,6 @@ alsamixer_volumecap(alsamixer_t *self, PyObject *args) return result; } -PyDoc_STRVAR(volumecap_doc, -"volumecap() -> List of volume capabilities (string)\n\ -\n\ -Returns a list of the volume control capabilities of this mixer.\n\ -Possible values in this list are:\n\ - - 'Volume'\n\ - - 'Joined Volume'\n\ - - 'Playback Volume'\n\ - - 'Joined Playback Mute'\n\ - - 'Capture Volume'\n\ - - 'Joined Capture Volume'"); - - static PyObject * alsamixer_switchcap(alsamixer_t *self, PyObject *args) { @@ -2352,21 +2125,6 @@ alsamixer_switchcap(alsamixer_t *self, PyObject *args) return result; } -PyDoc_STRVAR(switchcap_doc, -"switchcap() -> List of switch capabilities (string)\n\ -\n\ -Returns a list of the switches which are defined by this mixer.\n\ -\n\ -Possible values in this list are:\n\ - - 'Mute'\n\ - - 'Joined Mute'\n\ - - 'Playback Mute'\n\ - - 'Joined Playback Mute'\n\ - - 'Capture Mute'\n\ - - 'Joined Capture Mute'\n\ - - 'Capture Exclusive'\n"); - - static int alsamixer_getpercentage(long min, long max, long value) { /* Convert from number in range to percentage */ @@ -2490,18 +2248,6 @@ alsamixer_getvolume(alsamixer_t *self, PyObject *args, PyObject *kwds) return result; } -PyDoc_STRVAR(getvolume_doc, -"getvolume([pcmtype]) -> List of volume settings (int)\n\ -\n\ -Returns a list with the current volume settings for each channel.\n\ -The list elements are integer percentages.\n\ -\n\ -The optional 'pcmtype' argument can be either PCM_PLAYBACK or\n\ -PCM_CAPTURE, which is relevant if the mixer can control both\n\ -playback and capture volume. The default value is PCM_PLAYBACK\n\ -if the mixer has this capability, otherwise PCM_CAPTURE"); - - static PyObject * alsamixer_getrange(alsamixer_t *self, PyObject *args, PyObject *kwds) { @@ -2606,17 +2352,6 @@ alsamixer_getrange(alsamixer_t *self, PyObject *args, PyObject *kwds) return NULL; } -PyDoc_STRVAR(getrange_doc, -"getrange([pcmtype]) -> List of (min_volume, max_volume)\n\ -\n\ -Returns a list of tuples with the volume range (ints).\n\ -\n\ -The optional 'pcmtype' argument can be either PCM_PLAYBACK or\n\ -PCM_CAPTURE, which is relevant if the mixer can control both\n\ -playback and capture volume. The default value is 'playback'\n\ -if the mixer has this capability, otherwise 'capture'"); - - static PyObject * alsamixer_getenum(alsamixer_t *self, PyObject *args) { @@ -2699,13 +2434,6 @@ alsamixer_getenum(alsamixer_t *self, PyObject *args) return result; } -PyDoc_STRVAR(getenum_doc, -"getenum() -> Tuple of (string, list of strings)\n\ -\n\ -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) { @@ -2752,13 +2480,6 @@ alsamixer_setenum(alsamixer_t *self, PyObject *args) 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) { @@ -2804,15 +2525,6 @@ alsamixer_getmute(alsamixer_t *self, PyObject *args) return result; } -PyDoc_STRVAR(getmute_doc, -"getmute() -> List of mute settings (int)\n\ -\n\ -Return a list indicating the current mute setting for each channel.\n\ -0 means not muted, 1 means muted.\n\ -\n\ -This method will fail if the mixer has no playback switch capabilities."); - - static PyObject * alsamixer_getrec(alsamixer_t *self, PyObject *args) { @@ -2856,14 +2568,6 @@ alsamixer_getrec(alsamixer_t *self, PyObject *args) return result; } -PyDoc_STRVAR(getrec_doc, -"getrec() -> List of record mute settings (int)\n\ -\n\ -Return a list indicating the current record mute setting for each\n\ -channel. 0 means not recording, 1 means recording.\n\ -This method will fail if the mixer has no capture switch capabilities."); - - static PyObject * alsamixer_setvolume(alsamixer_t *self, PyObject *args, PyObject *kwds) { @@ -2973,22 +2677,6 @@ alsamixer_setvolume(alsamixer_t *self, PyObject *args, PyObject *kwds) return Py_None; } -PyDoc_STRVAR(setvolume_doc, -"setvolume(volume[[, channel] [, pcmtype]])\n\ -\n\ -Change the current volume settings for this mixer. The volume argument\n\ -controls the new volume setting as an integer percentage.\n\ -If the optional argument channel is present, the volume is set only for\n\ -this channel. This assumes that the mixer can control the volume for the\n\ -channels independently.\n\ -\n\ -The optional 'pcmtype' argument can be either PCM_PLAYBACK or PCM_CAPTURE.\n\ -It is relevant if the mixer has independent playback and capture volume\n\ -capabilities, and controls which of the volumes will be changed.\n\ -The default is 'playback' if the mixer has this capability, otherwise\n\ -'capture'."); - - static PyObject * alsamixer_setmute(alsamixer_t *self, PyObject *args) { @@ -3036,17 +2724,6 @@ alsamixer_setmute(alsamixer_t *self, PyObject *args) return Py_None; } -PyDoc_STRVAR(setmute_doc, -"setmute(mute [, channel])\n\ -\n\ -Sets the mute flag to a new value. The mute argument is either 0 for\n\ -not muted, or 1 for muted.\n\ -The optional channel argument controls which channel is muted.\n\ -If omitted, the mute flag is set for for all channels.\n\ -\n\ -This method will fail if the mixer has no playback mute capabilities"); - - static PyObject * alsamixer_setrec(alsamixer_t *self, PyObject *args) { @@ -3096,16 +2773,6 @@ alsamixer_setrec(alsamixer_t *self, PyObject *args) return Py_None; } -PyDoc_STRVAR(setrec_doc, -"setrec(capture [, channel])\n\ -\n\ -Sets the capture mute flag to a new value. The capture argument is\n\ -either 0 for no capture, or 1 for capture.\n\ -The optional channel argument controls which channel is changed.\n\ -If omitted, the capture flag is set for all channels.\n\ -\n\ -This method will fail if the mixer has no capture switch capabilities"); - static PyObject * alsamixer_polldescriptors(alsamixer_t *self, PyObject *args) { @@ -3144,6 +2811,7 @@ alsamixer_polldescriptors(alsamixer_t *self, PyObject *args) { PyErr_Format(ALSAAudioError, "Can't get poll descriptors [%s]", self->cardname); + free(fds); return NULL; } @@ -3152,16 +2820,11 @@ alsamixer_polldescriptors(alsamixer_t *self, PyObject *args) PyList_SetItem(result, i, Py_BuildValue("ih", fds[i].fd, fds[i].events)); } + free(fds); return result; } -PyDoc_STRVAR(polldescriptors_doc, -"polldescriptors() -> List of tuples (fd, eventmask).\n\ -\n\ -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) { @@ -3187,38 +2850,24 @@ alsamixer_handleevents(alsamixer_t *self, PyObject *args) 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}, - {"close", (PyCFunction)alsamixer_close, METH_VARARGS, mixer_close_doc}, - {"mixer", (PyCFunction)alsamixer_mixer, METH_VARARGS, mixer_doc}, - {"mixerid", (PyCFunction)alsamixer_mixerid, METH_VARARGS, mixerid_doc}, - {"switchcap", (PyCFunction)alsamixer_switchcap, METH_VARARGS, - switchcap_doc}, - {"volumecap", (PyCFunction)alsamixer_volumecap, METH_VARARGS, - volumecap_doc}, - {"getvolume", (PyCFunction)alsamixer_getvolume, METH_VARARGS | METH_KEYWORDS, - getvolume_doc}, - {"getrange", (PyCFunction)alsamixer_getrange, METH_VARARGS | METH_KEYWORDS, getrange_doc}, - {"getenum", (PyCFunction)alsamixer_getenum, METH_VARARGS, getenum_doc}, - {"getmute", (PyCFunction)alsamixer_getmute, METH_VARARGS, getmute_doc}, - {"getrec", (PyCFunction)alsamixer_getrec, METH_VARARGS, getrec_doc}, - {"setvolume", (PyCFunction)alsamixer_setvolume, METH_VARARGS | METH_KEYWORDS, - 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}, + {"cardname", (PyCFunction)alsamixer_cardname, METH_VARARGS}, + {"close", (PyCFunction)alsamixer_close, METH_VARARGS}, + {"mixer", (PyCFunction)alsamixer_mixer, METH_VARARGS}, + {"mixerid", (PyCFunction)alsamixer_mixerid, METH_VARARGS}, + {"switchcap", (PyCFunction)alsamixer_switchcap, METH_VARARGS}, + {"volumecap", (PyCFunction)alsamixer_volumecap, METH_VARARGS}, + {"getvolume", (PyCFunction)alsamixer_getvolume, METH_VARARGS | METH_KEYWORDS}, + {"getrange", (PyCFunction)alsamixer_getrange, METH_VARARGS | METH_KEYWORDS}, + {"getenum", (PyCFunction)alsamixer_getenum, METH_VARARGS}, + {"getmute", (PyCFunction)alsamixer_getmute, METH_VARARGS}, + {"getrec", (PyCFunction)alsamixer_getrec, METH_VARARGS}, + {"setvolume", (PyCFunction)alsamixer_setvolume, METH_VARARGS | METH_KEYWORDS}, + {"setenum", (PyCFunction)alsamixer_setenum, METH_VARARGS}, + {"setmute", (PyCFunction)alsamixer_setmute, METH_VARARGS}, + {"setrec", (PyCFunction)alsamixer_setrec, METH_VARARGS}, + {"polldescriptors", (PyCFunction)alsamixer_polldescriptors, METH_VARARGS}, + {"handleevents", (PyCFunction)alsamixer_handleevents, METH_VARARGS}, {NULL, NULL} }; @@ -3283,11 +2932,11 @@ 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|METH_KEYWORDS, pcms_doc}, - { "mixers", (PyCFunction)alsamixer_list, METH_VARARGS|METH_KEYWORDS, mixers_doc}, + { "card_indexes", (PyCFunction)alsacard_list_indexes, METH_VARARGS}, + { "card_name", (PyCFunction)alsacard_name, METH_VARARGS}, + { "cards", (PyCFunction)alsacard_list, METH_VARARGS}, + { "pcms", (PyCFunction)alsapcm_list, METH_VARARGS|METH_KEYWORDS}, + { "mixers", (PyCFunction)alsamixer_list, METH_VARARGS|METH_KEYWORDS}, { 0, 0 }, }; @@ -3300,7 +2949,7 @@ static PyMethodDef alsaaudio_methods[] = { static struct PyModuleDef alsaaudio_module = { PyModuleDef_HEAD_INIT, "alsaaudio", - alsaaudio_module_doc, + NULL, /* m_doc */ -1, alsaaudio_methods, 0, /* m_reload */ @@ -3326,10 +2975,12 @@ PyObject *PyInit_alsaaudio(void) ALSAPCMType.tp_new = alsapcm_new; ALSAMixerType.tp_new = alsamixer_new; +#if PY_VERSION_HEX < 0x03090000 PyEval_InitThreads(); +#endif #if PY_MAJOR_VERSION < 3 - m = Py_InitModule3("alsaaudio", alsaaudio_methods, alsaaudio_module_doc); + m = Py_InitModule3("alsaaudio", alsaaudio_methods); if (!m) return; #else @@ -3419,6 +3070,16 @@ PyObject *PyInit_alsaaudio(void) _EXPORT_INT(m, "PCM_FORMAT_DSD_U32_BE", SND_PCM_FORMAT_DSD_U32_BE); #endif + _EXPORT_INT(m, "PCM_STATE_OPEN", SND_PCM_STATE_OPEN); + _EXPORT_INT(m, "PCM_STATE_SETUP", SND_PCM_STATE_SETUP); + _EXPORT_INT(m, "PCM_STATE_PREPARED", SND_PCM_STATE_PREPARED); + _EXPORT_INT(m, "PCM_STATE_RUNNING", SND_PCM_STATE_RUNNING); + _EXPORT_INT(m, "PCM_STATE_XRUN", SND_PCM_STATE_XRUN); + _EXPORT_INT(m, "PCM_STATE_DRAINING", SND_PCM_STATE_DRAINING); + _EXPORT_INT(m, "PCM_STATE_PAUSED", SND_PCM_STATE_PAUSED); + _EXPORT_INT(m, "PCM_STATE_SUSPENDED", SND_PCM_STATE_SUSPENDED); + _EXPORT_INT(m, "PCM_STATE_DISCONNECTED", SND_PCM_STATE_DISCONNECTED); + /* Mixer stuff */ _EXPORT_INT(m, "MIXER_CHANNEL_ALL", MIXER_CHANNEL_ALL); diff --git a/doc/index.rst b/doc/index.rst index 81a1ec9..9160521 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -1,8 +1,3 @@ -.. 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 =================================================== @@ -18,15 +13,13 @@ Download ======== * `Download from pypi `_ - - + Github ====== * `Repository `_ * `Bug tracker `_ - Indices and tables ================== @@ -34,5 +27,3 @@ Indices and tables * :ref:`modindex` * :ref:`search` - - diff --git a/doc/libalsaaudio.rst b/doc/libalsaaudio.rst index 34ead05..9b8f479 100644 --- a/doc/libalsaaudio.rst +++ b/doc/libalsaaudio.rst @@ -5,38 +5,15 @@ .. module:: alsaaudio :platform: Linux - -.. % \declaremodule{builtin}{alsaaudio} % standard library, in C -.. % not standard, in C - .. moduleauthor:: Casper Wilstrup .. moduleauthor:: Lars Immisch - -.. % Author of the module code; - - The :mod:`alsaaudio` module defines functions and classes for using ALSA. -.. % ---- 3.1. ---- -.. % For each function, use a ``funcdesc'' block. This has exactly two -.. % parameters (each parameters is contained in a set of curly braces): -.. % the first parameter is the function name (this automatically -.. % generates an index entry); the second parameter is the function's -.. % argument list. If there are no arguments, use an empty pair of -.. % curly braces. If there is more than one argument, separate the -.. % arguments with backslash-comma. Optional parts of the parameter -.. % list are contained in \optional{...} (this generates a set of square -.. % brackets around its parameter). Arguments are automatically set in -.. % italics in the parameter list. Each argument should be mentioned at -.. % least once in the description; each usage (even inside \code{...}) -.. % should be enclosed in \var{...}. - - .. function:: pcms(pcmtype=PCM_PLAYBACK) List available PCM devices by name. - + Arguments are: * *pcmtype* - can be either :const:`PCM_CAPTURE` or :const:`PCM_PLAYBACK` @@ -62,7 +39,13 @@ The :mod:`alsaaudio` module defines functions and classes for using ALSA. List the available ALSA cards by name. This function is only moderately useful. If you want to see a list of available PCM devices, use :func:`pcms` instead. - + +.. + Omitted by intention due to being superseded by cards(): + + .. function:: card_indexes() + .. function:: card_name() + .. function:: mixers(cardindex=-1, device='default') List the available mixers. The arguments are: @@ -72,12 +55,14 @@ The :mod:`alsaaudio` module defines functions and classes for using ALSA. the `device` keyword argument is ignored. ``0`` is the first hardware sound card. + **Note:** This should not be used, as it bypasses most of ALSA's configuration. + * *device* - the name of the device on which the mixer resides. The default is ``'default'``. **Note:** For a list of available controls, you can also use ``amixer`` on the commandline:: - + $ amixer To elaborate the example, calling :func:`mixers` with the argument @@ -91,7 +76,7 @@ The :mod:`alsaaudio` module defines functions and classes for using ALSA. $ amixer -D foo *Changed in 0.8*: - + - The keyword argument `device` is new and can be used to select virtual devices. As a result, the default behaviour has subtly changed. Since 0.8, this functions returns the mixers for the default @@ -101,6 +86,7 @@ The :mod:`alsaaudio` module defines functions and classes for using ALSA. Return a Python string containing the ALSA version found. + .. _pcm-objects: PCM Objects @@ -110,7 +96,7 @@ PCM objects in :mod:`alsaaudio` can play or capture (record) PCM sound through speakers or a microphone. The PCM constructor takes the following arguments: -.. class:: PCM(type=PCM_PLAYBACK, mode=PCM_NORMAL, rate=44100, channels=2, format=PCM_FORMAT_S16_LE, periodsize=32, device='default', cardindex=-1) +.. class:: PCM(type=PCM_PLAYBACK, mode=PCM_NORMAL, rate=44100, channels=2, format=PCM_FORMAT_S16_LE, periodsize=32, periods=4, device='default', cardindex=-1) This class is used to represent a PCM device (either for playback and recording). The arguments are: @@ -123,7 +109,7 @@ following arguments: * *channels* - the number of channels. The default value is 2 (stereo). * *format* - the data format. This controls how the PCM device interprets data for playback, and how data is encoded in captures. The default value is :const:`PCM_FORMAT_S16_LE`. - + ========================= =============== Format Description ========================= =============== @@ -156,7 +142,11 @@ following arguments: ``PCM_FORMAT_U24_3BE`` Unsigned 24 bit samples for each channel (Big Endian byte order in 3 bytes) ========================= =============== - * *periodsize* - the period size in frames. Each write should consist of *periodsize* frames. The default value is 32. + * *periodsize* - the period size in frames. + Make sure you understand :ref:`the meaning of periods `. + The default value is 32, which is below the actual minimum of most devices, + and will therefore likely be larger in practice. + * *periods* - the number of periods in the buffer. The default value is 4. * *device* - the name of the PCM device that should be used (for example a value from the output of :func:`pcms`). The default value is ``'default'``. @@ -165,14 +155,20 @@ following arguments: the `device` keyword argument is ignored. ``0`` is the first hardware sound card. + **Note:** This should not be used, as it bypasses most of ALSA's configuration. + This will construct a PCM object with the given settings. + *Changed in 0.10:* + + - Added the optional named parameter `periods`. + *Changed in 0.9:* - Added the optional named parameters `rate`, `channels`, `format` and `periodsize`. *Changed in 0.8:* - + - The `card` keyword argument is still supported, but deprecated. Please use `device` instead. @@ -180,8 +176,7 @@ following arguments: The `card` keyword is deprecated because it guesses the real ALSA name of the card. This was always fragile and broke some legitimate usecases. - - + PCM objects have the following methods: .. method:: PCM.info() @@ -235,17 +230,43 @@ PCM objects have the following methods: Returns the type of PCM object. Either :const:`PCM_CAPTURE` or :const:`PCM_PLAYBACK`. - .. method:: PCM.pcmmode() Return the mode of the PCM object. One of :const:`PCM_NONBLOCK`, :const:`PCM_ASYNC`, or :const:`PCM_NORMAL` - .. method:: PCM.cardname() Return the name of the sound card used by this PCM object. +.. + Omitted by intention due to not really fitting the c'tor-based setup concept: + + .. method:: PCM.getchannels() + + Returns list of the device's supported channel counts. + + .. method:: PCM.getratebounds() + + Returns the card's minimum and maximum supported sample rates as + a tuple of integers. + + .. method:: PCM.getrates() + + Returns the sample rates supported by the device. + The returned value can be of one of the following, depending on + the card's properties: + * Card supports only a single rate: returns the rate + * Card supports a continuous range of rates: returns a tuple of + the range's lower and upper bounds (inclusive) + * Card supports a collection of well-known rates: returns a list of + the supported rates + + .. method:: PCM.getformats() + + Returns a dictionary of supported format codes (integers) keyed by + their standard ALSA names (strings). + .. method:: PCM.setchannels(nchannels) .. deprecated:: 0.9 Use the `channels` named argument to :func:`PCM`. @@ -255,13 +276,42 @@ PCM objects have the following methods: .. deprecated:: 0.9 Use the `rate` named argument to :func:`PCM`. .. method:: PCM.setformat(format) - + .. deprecated:: 0.9 Use the `format` named argument to :func:`PCM`. .. method:: PCM.setperiodsize(period) .. deprecated:: 0.9 Use the `periodsize` named argument to :func:`PCM`. +.. method:: PCM.info() + + Returns a dictionary with the PCM object's configured parameters. + + Values are retrieved from the ALSA library if they are available; + otherwise they represent those stored by pyalsaaudio, and their keys + are prefixed with ' (call value) '. + + *New in 0.9.1* + +.. method:: PCM.dumpinfo() + + Dumps the PCM object's configured parameters to stdout. + +.. method:: PCM.state() + + Returs the current state of the stream, which can be one of + :const:`PCM_STATE_OPEN` (this should not actually happen), + :const:`PCM_STATE_SETUP` (after :func:`drop` or :func:`drain`), + :const:`PCM_STATE_PREPARED` (after construction), + :const:`PCM_STATE_RUNNING`, + :const:`PCM_STATE_XRUN`, + :const:`PCM_STATE_DRAINING`, + :const:`PCM_STATE_PAUSED`, + :const:`PCM_STATE_SUSPENDED`, and + :const:`PCM_STATE_DISCONNECTED`. + + *New in 0.10* + .. method:: PCM.read() In :const:`PCM_NORMAL` mode, this function blocks until a full period is @@ -294,17 +344,42 @@ PCM objects have the following methods: return value of zero, if the buffer is full. In this case, the data should be written at a later time. + Note that this call completing means only that the samples were buffered + in the kernel, and playout will continue afterwards. Make sure that the + stream is drained before discarding the PCM handle. .. method:: PCM.pause([enable=True]) If *enable* is :const:`True`, playback or capture is paused. Otherwise, playback/capture is resumed. +.. method:: PCM.drop() + + Stop the stream and drop residual buffered frames. + + *New in 0.9* + +.. method:: PCM.drain() + + For :const:`PCM_PLAYBACK` PCM objects, play residual buffered frames + and then stop the stream. In :const:`PCM_NORMAL` mode, + this function blocks until all pending playback is drained. + + For :const:`PCM_CAPTURE` PCM objects, this function is not very useful. + + *New in 0.10* + +.. method:: PCM.close() + + Closes the PCM device. + + For :const:`PCM_PLAYBACK` PCM objects in :const:`PCM_NORMAL` mode, + this function blocks until all pending playback is drained. .. method:: PCM.polldescriptors() - Returns a tuple of *(file descriptor, eventmask)* that can be used to - wait for changes on the PCM with *select.poll*. + Returns a list of tuples of *(file descriptor, eventmask)* that can be + used to wait for changes on the PCM with *select.poll*. The *eventmask* value is compatible with `poll.register`__ in the Python :const:`select` module. @@ -347,7 +422,7 @@ PCM objects have the following methods: ``PCM_TSTAMP_TYPE_MONOTONIC_RAW`` Monotonic time from an unspecified starting time using only the system clock. ================================= =========================================== - + The timestamp mode is controlled by the tstamp_mode, as described in the table below. ================================= =========================================== @@ -358,9 +433,6 @@ PCM objects have the following methods: update. ================================= =========================================== - -__ 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 @@ -396,11 +468,10 @@ Mixer Objects Mixer objects provides access to the ALSA mixer API. - .. class:: Mixer(control='Master', id=0, cardindex=-1, device='default') Arguments are: - + * *control* - specifies which control to manipulate using this mixer object. The list of available controls can be found with the :mod:`alsaaudio`.\ :func:`mixers` function. The default value is @@ -416,30 +487,27 @@ Mixer objects provides access to the ALSA mixer API. * *device* - the name of the device on which the mixer resides. The default value is ``'default'``. - + *Changed in 0.8*: - + - The keyword argument `device` is new and can be used to select virtual devices. - + Mixer objects have the following methods: .. method:: Mixer.cardname() Return the name of the sound card used by this Mixer object - .. method:: Mixer.mixer() Return the name of the specific mixer controlled by this object, For example ``'Master'`` or ``'PCM'`` - .. method:: Mixer.mixerid() Return the ID of the ALSA mixer controlled by this object. - .. method:: Mixer.switchcap() Returns a list of the switches which are defined by this specific mixer. @@ -460,7 +528,6 @@ Mixer objects have the following methods: To manipulate these switches use the :meth:`setrec` or :meth:`setmute` methods - .. method:: Mixer.volumecap() Returns a list of the volume control capabilities of this @@ -476,7 +543,7 @@ Mixer objects have the following methods: 'Capture Volume' Manipulate sound capture volume 'Joined Capture Volume' Manipulate sound capture volume for all channels at a time ======================== ================ - + .. method:: Mixer.getenum() For enumerated controls, return the currently selected item and the list of @@ -503,48 +570,43 @@ Mixer objects have the following methods: This method will return an empty tuple if the mixer is not an enumerated control. +.. method:: Mixer.setenum(index) -.. method:: Mixer.getmute() + For enumerated controls, sets the currently selected item. + *index* is an index into the list of available enumerated items returned + by :func:`getenum`. - Return a list indicating the current mute setting for each - channel. 0 means not muted, 1 means muted. - - This method will fail if the mixer has no playback switch capabilities. - - -.. method:: Mixer.getrange(pcmtype=PCM_PLAYBACK) +.. method:: Mixer.getrange(pcmtype=PCM_PLAYBACK, units=VOLUME_UNITS_RAW) Return the volume range of the ALSA mixer controlled by this object. + The value is a tuple of integers whose meaning is determined by the + *units* argument. The optional *pcmtype* argument can be either :const:`PCM_PLAYBACK` or :const:`PCM_CAPTURE`, which is relevant if the mixer can control both playback and capture volume. The default value is :const:`PCM_PLAYBACK` if the mixer has playback channels, otherwise it is :const:`PCM_CAPTURE`. + The optional *units* argument can be one of :const:`VOLUME_UNITS_PERCENTAGE`, + :const:`VOLUME_UNITS_RAW`, or :const:`VOLUME_UNITS_DB`. -.. method:: Mixer.getrec() - - Return a list indicating the current record mute setting for each channel. 0 - means not recording, 1 means recording. - - This method will fail if the mixer has no capture switch capabilities. - - -.. method:: Mixer.getvolume(pcmtype=PCM_PLAYBACK) +.. method:: Mixer.getvolume(pcmtype=PCM_PLAYBACK, units=VOLUME_UNITS_PERCENTAGE) Returns a list with the current volume settings for each channel. The list - elements are integer percentages. + elements are integers whose meaning is determined by the *units* argument. The optional *pcmtype* argument can be either :const:`PCM_PLAYBACK` or :const:`PCM_CAPTURE`, which is relevant if the mixer can control both playback and capture volume. The default value is :const:`PCM_PLAYBACK` if the mixer has playback channels, otherwise it is :const:`PCM_CAPTURE`. + The optional *units* argument can be one of :const:`VOLUME_UNITS_PERCENTAGE`, + :const:`VOLUME_UNITS_RAW`, or :const:`VOLUME_UNITS_DB`. -.. method:: Mixer.setvolume(volume, channel=None, pcmtype=PCM_PLAYBACK) +.. method:: Mixer.setvolume(volume, channel=None, pcmtype=PCM_PLAYBACK, units=VOLUME_UNITS_PERCENTAGE) Change the current volume settings for this mixer. The *volume* argument - controls the new volume setting as an integer percentage. + is an integer whose meaning is determined by the *units* argument. If the optional argument *channel* is present, the volume is set only for this channel. This assumes that the mixer can control the @@ -555,6 +617,16 @@ Mixer objects have the following methods: playback and capture volume. The default value is :const:`PCM_PLAYBACK` if the mixer has playback channels, otherwise it is :const:`PCM_CAPTURE`. + The optional *units* argument can be one of :const:`VOLUME_UNITS_PERCENTAGE`, + :const:`VOLUME_UNITS_RAW`, or :const:`VOLUME_UNITS_DB`. + +.. method:: Mixer.getmute() + + Return a list indicating the current mute setting for each channel. + 0 means not muted, 1 means muted. + + This method will fail if the mixer has no playback switch capabilities. + .. method:: Mixer.setmute(mute, [channel]) Sets the mute flag to a new value. The *mute* argument is either 0 for not @@ -565,6 +637,12 @@ Mixer objects have the following methods: This method will fail if the mixer has no playback mute capabilities +.. method:: Mixer.getrec() + + Return a list indicating the current record mute setting for each channel. + 0 means not recording, 1 means recording. + + This method will fail if the mixer has no capture switch capabilities. .. method:: Mixer.setrec(capture, [channel]) @@ -578,20 +656,22 @@ Mixer objects have the following methods: .. method:: Mixer.polldescriptors() - Returns a tuple of *(file descriptor, eventmask)* that can be used to - wait for changes on the mixer with *select.poll*. + Returns a list of tuples 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 + Acknowledge events on the :func:`polldescriptors` file descriptors to prevent subsequent polls from returning the same events again. Returns the number of events that were acknowledged. +.. method:: Mixer.close() + + Closes the Mixer device. + **A rant on the ALSA Mixer API** The ALSA mixer API is extremely complicated - and hardly documented at all. @@ -614,8 +694,6 @@ Unfortunately, I'm not able to create such a HOWTO myself, since I only understand half of the API, and that which I do understand has come from a painful trial and error process. -.. % ==== 4. ==== - .. _pcm-example: @@ -657,6 +735,7 @@ To test PCM playback (on your default soundcard), run:: recordtest.py and playbacktest.py ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + **recordtest.py** and **playbacktest.py** will record and play a raw sound file in CD quality. @@ -678,7 +757,7 @@ Without arguments, **mixertest.py** will list all available *controls* on the default soundcard. The output might look like this:: - + $ ./mixertest.py Available mixer controls: 'Master' @@ -726,9 +805,3 @@ argument:: Capabilities: Playback Volume Playback Mute Channel 0 volume: 61% Channel 1 volume: 61% - -.. 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 diff --git a/doc/pyalsaaudio.rst b/doc/pyalsaaudio.rst index 1ae88b3..42e1126 100644 --- a/doc/pyalsaaudio.rst +++ b/doc/pyalsaaudio.rst @@ -7,33 +7,19 @@ Introduction .. |release| replace:: version -.. % At minimum, give your name and an email address. You can include a -.. % snail-mail address if you like. - -.. % This makes the Abstract go on a separate page in the HTML version; -.. % if a copyright notice is used, it should go immediately after this. -.. % - - .. _front: This software is licensed under the PSF license - the same one used by the majority of the python distribution. Basically you can use it for anything you wish (even commercial purposes). There is no warranty whatsoever. -.. % Copyright statement should go here, if needed. - -.. % The abstract should be a paragraph or two long, and describe the -.. % scope of the document. - - .. topic:: Abstract This package contains wrappers for accessing the ALSA API from Python. It is 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 thegithub issue tracker. + If you find bugs in the wrappers please use the github 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. @@ -64,8 +50,8 @@ More information about ALSA may be found on the project homepage ALSA and Python =============== -The older Linux sound API (OSS) which is now deprecated is well supported from -the standard Python library, through the ossaudiodev module. No native ALSA +The older Linux sound API (OSS) -- which is now deprecated -- is well supported +by the standard Python library, through the ossaudiodev module. No native ALSA support exists in the standard library. There are a few other "ALSA for Python" projects available, including at least @@ -106,6 +92,7 @@ And then as root: --- :: # python setup.py install + ******* Testing ******* @@ -130,7 +117,7 @@ with ``Ctl-C``. Play back the recording with:: - $ python playbacktest.py-d + $ python playbacktest.py -d 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 diff --git a/doc/terminology.rst b/doc/terminology.rst index 370973d..3c16d19 100644 --- a/doc/terminology.rst +++ b/doc/terminology.rst @@ -19,7 +19,7 @@ Sample Musically, the sample size determines the dynamic range. The dynamic range is the difference between the quietest and the - loudest signal that can be resproduced. + loudest signal that can be reproduced. Frame A frame consists of exactly one sample per channel. If there is only one @@ -28,9 +28,9 @@ Frame Frame size This is the size in bytes of each frame. This can vary a lot: if each sample - is 8 bits, and we're handling mono sound, the frame size is one byte. - Similarly in 6 channel audio with 64 bit floating point samples, the frame - size is 48 bytes + is 8 bits, and we're handling mono sound, the frame size is one byte. + For six channel audio with 64 bit floating point samples, the frame size + is 48 bytes. Rate PCM sound consists of a flow of sound frames. The sound rate controls how @@ -38,7 +38,7 @@ Rate means that a new frame is played or captured 8000 times per second. Data rate - This is the number of bytes, which must be recorded or provided per + This is the number of bytes which must be consumed or provided per second at a certain frame size and rate. 8000 Hz mono sound with 8 bit (1 byte) samples has a data rate of @@ -46,24 +46,40 @@ 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). + + If the data rate requirement is not met, an overrun (on capture) or + underrun (on playback) occurs; the term "xrun" is used to refer to + either event. + +.. _term-period: Period - When the hardware processes data this is done in chunks of frames. The time - interval between each processing (A/D or D/A conversion) is known - as the period. - The size of the period has direct implication on the latency of the - sound input or output. For low-latency the period size should be - very small, while low CPU resource usage would usually demand - larger period sizes. With ALSA, the CPU utilization is not impacted - much by the period size, since the kernel layer buffers multiple - periods internally, so each period generates an interrupt and a - memory copy, but userspace can be slower and read or write multiple - periods at the same time. + The CPU processes sample data in chunks of frames, so-called periods + (also called fragments by some systems). The operating system kernel's + sample buffer must hold at least two periods (at any given time, one + is processed by the sound hardware, and one by the CPU). + + The completion of a *period* triggers a CPU interrupt, which causes + processing and context switching overhead. Therefore, a smaller period + size causes higher CPU resource usage at a given data rate. + + A bigger size of the *buffer* improves the system's resilience to xruns. + The buffer being split into a bigger number of smaller periods also does + that, as it allows it to be drained / topped up sooner. + + On the other hand, a bigger size of the *buffer* also increases the + playback latency, that is, the time it takes for a frame from being + sent out by the application to being actually audible. + + Similarly, a bigger *period* size increases the capture latency. + + The trade-off between latency, xrun resilience, and resource usage + must be made depending on the application. Period size - This is the size of each period in Hz. *Not bytes, but Hz!.* In - :mod:`alsaaudio` the period size is set directly, and it is + This is the size of each period in frames. *Not bytes, but frames!* + In :mod:`alsaaudio` the period size is set directly, and it is therefore important to understand the significance of this number. If the period size is configured to for example 32, each write should contain exactly 32 frames of sound data, and each diff --git a/playbacktest.py b/playbacktest.py index 874415b..74232ca 100755 --- a/playbacktest.py +++ b/playbacktest.py @@ -49,5 +49,5 @@ if __name__ == '__main__': while data: out.write(data) data = f.read(320) - + out.close() diff --git a/setup.py b/setup.py index fb24982..05c77ea 100755 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ from setuptools import setup from setuptools.extension import Extension from sys import version -pyalsa_version = '0.9.2' +pyalsa_version = '0.10.0' if __name__ == '__main__': setup(