pyalsaaudio-0.8.4/0002755000175000001470000000000013054110315013571 5ustar larsdev00000000000000pyalsaaudio-0.8.4/LICENSE0000644000175000001470000000473612267770727014636 0ustar larsdev00000000000000PyAlsaAudio is released under the same conditions as Python itself. The original wording of this license can be found below. PSF LICENSE AGREEMENT FOR PYTHON 2.4 ------------------------------------ 1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and the Individual or Organization ("Licensee") accessing and otherwise using Python 2.4 software in source or binary form and its associated documentation. 2. Subject to the terms and conditions of this License Agreement, PSF hereby grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use Python 2.4 alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of copyright, i.e., "Copyright (c) 2001, 2002, 2003, 2004 Python Software Foundation; All Rights Reserved" are retained in Python 2.4 alone or in any derivative version prepared by Licensee. 3. In the event Licensee prepares a derivative work that is based on or incorporates Python 2.4 or any part thereof, and wants to make the derivative work available to others as provided herein, then Licensee hereby agrees to include in any such work a brief summary of the changes made to Python 2.4. 4. PSF is making Python 2.4 available to Licensee on an "AS IS" basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 2.4 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON 2.4 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 2.4, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. 6. This License Agreement will automatically terminate upon a material breach of its terms and conditions. 7. Nothing in this License Agreement shall be deemed to create any relationship of agency, partnership, or joint venture between PSF and Licensee. This License Agreement does not grant permission to use PSF trademarks or trade name in a trademark sense to endorse or promote products or services of Licensee, or any third party. 8. By copying, installing or otherwise using Python 2.4, Licensee agrees to be bound by the terms and conditions of this License Agreement. pyalsaaudio-0.8.4/isine.py0000644000175000001470000000410613053677677015305 0ustar larsdev00000000000000''' Use this example from an interactive python session, for example: >>> from isine import change >>> change(880) ''' from __future__ import print_function from threading import Thread from queue import Queue, Empty from math import pi, sin import struct import alsaaudio sampling_rate = 44100.0 format = alsaaudio.PCM_FORMAT_S16_LE framesize = 2 # bytes per frame for the values above def digitize(s): if s > 1.0 or s < -1.0: raise ValueError return struct.pack('h', int(s * 32767)) def generate(frequency): # generate a buffer with a sine wave of frequency size = sampling_rate / frequency buffer = bytes() for i in range(size): buffer = buffer + digitize(sin(i/size)/(2 * pi)) return buffer class SinePlayer(Thread): def __init__(self, frequency = 440.0): Thread.__init__(self) self.setDaemon(True) self.device = alsaaudio.PCM() self.device.setchannels(1) self.device.setformat(format) self.device.setrate(sampling_rate) self.queue = Queue() self.change(frequency) def change(self, frequency): '''This is called outside of the player thread''' # we generate the buffer in the calling thread for less # latency when switching frequencies # More than 100 writes/s are pushing it - play multiple buffers # for higher frequencies factor = int(frequency/100.0) if factor == 0: factor = 1 buf = generate(frequency) * factor print('factor: %d, frames: %d' % (factor, len(buf) / framesize)) self.queue.put( buf) def run(self): buffer = None while True: try: buffer = self.queue.get(False) self.device.setperiodsize(int(len(buffer) / framesize)) self.device.write(buffer) except Empty: if buffer: self.device.write(buffer) isine = SinePlayer() isine.start() def change(f): isine.change(f) pyalsaaudio-0.8.4/alsaaudio.c0000644000175000001470000021204413054023402015700 0ustar larsdev00000000000000/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /* * alsaaudio -- Python interface to ALSA (Advanced Linux Sound Architecture). * The standard audio API for Linux since kernel 2.6 * * Contributed by Unispeed A/S (http://www.unispeed.com) * Author: Casper Wilstup (cwi@aves.dk) and Lars Immisch (lars@ibp.de) * * License: Python Software Foundation License * */ #include "Python.h" #if PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION < 6 #include "stringobject.h" #define PyUnicode_AS_DATA PyString_FromString #define PyUnicode_FromString PyString_FromString #define PyUnicode_Check PyString_Check #define PyLong_Check PyInt_Check #define PyLong_AS_LONG PyInt_AS_LONG #endif #if PY_MAJOR_VERSION < 3 #define PyLong_FromLong PyInt_FromLong #endif #include #include #include 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; int pcmmode; char *cardname; snd_pcm_t *handle; // Configurable parameters int channels; int rate; int format; snd_pcm_uframes_t periodsize; int framesize; } alsapcm_t; typedef struct { PyObject_HEAD; /* Mixer identification */ char *cardname; char *controlname; int controlid; /* Capabilities */ unsigned int volume_cap; unsigned int switch_cap; unsigned int pchannels; unsigned int cchannels; /* min and max values for playback and capture volumes */ long pmin; long pmax; long cmin; long cmax; snd_mixer_t *handle; } alsamixer_t; /******************************************/ /* PCM object wrapper */ /******************************************/ static PyTypeObject ALSAPCMType; static PyObject *ALSAAudioError; static long get_pcmtype(PyObject *obj) { if (!obj || (obj == Py_None)) { return SND_PCM_STREAM_PLAYBACK; } #if PY_MAJOR_VERSION > 2 if (PyLong_Check(obj)) { long pcmtype = PyLong_AS_LONG(obj); if (pcmtype == SND_PCM_STREAM_PLAYBACK || pcmtype == SND_PCM_STREAM_CAPTURE) { return pcmtype; } } #else if (PyInt_Check(obj)) { long pcmtype = PyInt_AS_LONG(obj); if (pcmtype == SND_PCM_STREAM_PLAYBACK || pcmtype == SND_PCM_STREAM_CAPTURE) { return pcmtype; } } #endif if (PyUnicode_Check(obj)) { 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; } PyErr_SetString(ALSAAudioError, "PCM type must be PCM_PLAYBACK (0) " "or PCM_CAPTURE (1)"); return -1; } static PyObject * alsacard_list(PyObject *self, PyObject *args) { int rc; int card = -1; snd_ctl_card_info_t *info; snd_ctl_t *handle; PyObject *result = NULL; if (!PyArg_ParseTuple(args,":cards")) return NULL; snd_ctl_card_info_alloca(&info); result = PyList_New(0); for (rc = snd_card_next(&card); !rc && (card >= 0); rc = snd_card_next(&card)) { char name[32]; int err; PyObject *item; /* One would be tempted to think that snd_card_get_name returns a name that is actually meaningful for any further operations. Not in ALSA land. Here we need the id, not the name */ sprintf(name, "hw:%d", card); if ((err = snd_ctl_open(&handle, name, 0)) < 0) { PyErr_Format(ALSAAudioError, "%s [%s]", snd_strerror(err), name); return NULL; } if ((err = snd_ctl_card_info(handle, info)) < 0) { PyErr_Format(ALSAAudioError, "%s [%s]", snd_strerror(err), name); snd_ctl_close(handle); Py_DECREF(result); return NULL; } item = PyUnicode_FromString(snd_ctl_card_info_get_id(info)); PyList_Append(result, item); Py_DECREF(item); snd_ctl_close(handle); } return result; } PyDoc_STRVAR(cards_doc, "cards()\n\ \n\ List the available card ids."); static PyObject * alsacard_list_indexes(PyObject *self, PyObject *args) { int rc; int card = -1; PyObject *result = NULL; if (!PyArg_ParseTuple(args,":card_indexes")) return NULL; result = PyList_New(0); for (rc = snd_card_next(&card); !rc && (card >= 0); rc = snd_card_next(&card)) { PyObject *item = PyLong_FromLong(card); PyList_Append(result, item); Py_DECREF(item); } return result; } PyDoc_STRVAR(card_indexes_doc, "card_indexes()\n\ \n\ List the available card indexes."); static PyObject * alsacard_name(PyObject *self, PyObject *args) { int err, card; PyObject *result = NULL; char *name = NULL, *longname = NULL; if (!PyArg_ParseTuple(args,"i:card_name", &card)) return NULL; err = snd_card_get_name(card, &name); if (err < 0) { PyErr_Format(ALSAAudioError, "%s [%d]", snd_strerror(err), card); goto exit; } err = snd_card_get_longname(card, &longname); if (err < 0) { PyErr_Format(ALSAAudioError, "%s [%d]", snd_strerror(err), card); goto exit; } result = PyTuple_New(2); PyTuple_SetItem(result, 0, PyUnicode_FromString(name)); PyTuple_SetItem(result, 1, PyUnicode_FromString(longname)); exit: free(name); free(longname); return result; } PyDoc_STRVAR(card_name_doc, "card_name(card_index) -> Tuple of (name, longname)\n\ \n\ Return the card name and long name for card 'card_index'."); static PyObject * alsapcm_list(PyObject *self, PyObject *args) { PyObject *pcmtypeobj = NULL; long pcmtype; PyObject *result = NULL; PyObject *item; void **hints, **n; char *name, *io; const char *filter; if (!PyArg_ParseTuple(args,"|O:pcms", &pcmtypeobj)) return NULL; pcmtype = get_pcmtype(pcmtypeobj); if (pcmtype < 0) { return NULL; } result = PyList_New(0); if (snd_device_name_hint(-1, "pcm", &hints) < 0) return result; n = hints; filter = pcmtype == SND_PCM_STREAM_CAPTURE ? "Input" : "Output"; while (*n != NULL) { name = snd_device_name_get_hint(*n, "NAME"); io = snd_device_name_get_hint(*n, "IOID"); if (io != NULL && strcmp(io, filter) != 0) goto __end; item = PyUnicode_FromString(name); PyList_Append(result, item); Py_DECREF(item); __end: if (name != NULL) free(name); if (io != NULL) free(io); n++; } snd_device_name_free_hint(hints); 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, 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; 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, &pcmtypeobj, &pcmmode, &device, &cardidx, &card)) return NULL; if (cardidx >= 0) { if (cardidx < 32) { snprintf(hw_device, sizeof(hw_device), "hw:%d", cardidx); device = hw_device; } else { PyErr_Format(ALSAAudioError, "Invalid card number %d", cardidx); return NULL; } } else if (card) { // The card kw argument is deprecated PyErr_WarnEx(PyExc_DeprecationWarning, "The `card` keyword argument is deprecated. " "Please use `device` instead", 1); // If we find a colon, we assume it is a real ALSA cardname if (strchr(card, ':')) { device = card; } snprintf(hw_device, sizeof(hw_device), "default:CARD=%s", card); device = hw_device; } pcmtype = get_pcmtype(pcmtypeobj); if (pcmtype < 0) { 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); } else { if (self->handle) { snd_pcm_close(self->handle); self->handle = 0; } PyErr_Format(ALSAAudioError, "%s [%s]", snd_strerror(res), device); return NULL; } return (PyObject *)self; } static void alsapcm_dealloc(alsapcm_t *self) { if (self->handle) { snd_pcm_drain(self->handle); snd_pcm_close(self->handle); } free(self->cardname); PyObject_Del(self); } static PyObject * alsapcm_close(alsapcm_t *self, PyObject *args) { if (!PyArg_ParseTuple(args,":close")) return NULL; if (self->handle) { Py_BEGIN_ALLOW_THREADS snd_pcm_drain(self->handle); snd_pcm_close(self->handle); Py_END_ALLOW_THREADS self->handle = 0; } Py_INCREF(Py_None); 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_format_t fmt; int dir; snd_pcm_uframes_t frames; snd_pcm_hw_params_t *hwparams; snd_pcm_hw_params_alloca(&hwparams); snd_pcm_hw_params_current(self->handle,hwparams); if (!PyArg_ParseTuple(args,":dumpinfo")) return NULL; if (!self->handle) { PyErr_SetString(ALSAAudioError, "PCM device is closed"); return NULL; } printf("PCM handle name = '%s'\n", snd_pcm_name(self->handle)); printf("PCM state = %s\n", snd_pcm_state_name(snd_pcm_state(self->handle))); snd_pcm_hw_params_get_access(hwparams, (snd_pcm_access_t *) &val); printf("access type = %s\n", snd_pcm_access_name((snd_pcm_access_t)val)); snd_pcm_hw_params_get_format(hwparams, &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); printf("subformat = '%s' (%s)\n", snd_pcm_subformat_name((snd_pcm_subformat_t)val), snd_pcm_subformat_description((snd_pcm_subformat_t)val)); snd_pcm_hw_params_get_channels(hwparams, &val); printf("channels = %d\n", val); snd_pcm_hw_params_get_rate(hwparams, &val, &dir); printf("rate = %d bps\n", val); snd_pcm_hw_params_get_period_time(hwparams, &val, &dir); printf("period time = %d us\n", val); snd_pcm_hw_params_get_period_size(hwparams, &frames, &dir); printf("period size = %d frames\n", (int)frames); snd_pcm_hw_params_get_buffer_time(hwparams, &val, &dir); printf("buffer time = %d us\n", val); snd_pcm_hw_params_get_buffer_size(hwparams, (snd_pcm_uframes_t *) &val); printf("buffer size = %d frames\n", val); snd_pcm_hw_params_get_periods(hwparams, &val, &dir); printf("periods per buffer = %d frames\n", val); snd_pcm_hw_params_get_rate_numden(hwparams, &val, &val2); printf("exact rate = %d/%d bps\n", val, val2); val = snd_pcm_hw_params_get_sbits(hwparams); printf("significant bits = %d\n", val); snd_pcm_hw_params_get_period_time(hwparams, &val, &dir); printf("period time = %d us\n", val); val = snd_pcm_hw_params_is_batch(hwparams); printf("is batch = %d\n", val); val = snd_pcm_hw_params_is_block_transfer(hwparams); printf("is block transfer = %d\n", val); val = snd_pcm_hw_params_is_double(hwparams); printf("is double = %d\n", val); val = snd_pcm_hw_params_is_half_duplex(hwparams); printf("is half duplex = %d\n", val); val = snd_pcm_hw_params_is_joint_duplex(hwparams); printf("is joint duplex = %d\n", val); val = snd_pcm_hw_params_can_overrange(hwparams); printf("can overrange = %d\n", val); val = snd_pcm_hw_params_can_mmap_sample_resolution(hwparams); printf("can mmap = %d\n", val); val = snd_pcm_hw_params_can_pause(hwparams); printf("can pause = %d\n", val); val = snd_pcm_hw_params_can_resume(hwparams); printf("can resume = %d\n", val); val = snd_pcm_hw_params_can_sync_start(hwparams); printf("can sync start = %d\n", val); Py_INCREF(Py_None); return Py_None; } static PyObject * alsapcm_pcmtype(alsapcm_t *self, PyObject *args) { if (!PyArg_ParseTuple(args,":pcmtype")) return NULL; if (!self->handle) { PyErr_SetString(ALSAAudioError, "PCM device is closed"); return NULL; } 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) { if (!PyArg_ParseTuple(args,":pcmmode")) return NULL; if (!self->handle) { PyErr_SetString(ALSAAudioError, "PCM device is closed"); return NULL; } 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) { if (!PyArg_ParseTuple(args,":cardname")) return NULL; if (!self->handle) { PyErr_SetString(ALSAAudioError, "PCM device is closed"); return NULL; } 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) { 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; } if (!PyArg_ParseTuple(args,":read")) return NULL; if (!self->handle) { PyErr_SetString(ALSAAudioError, "PCM device is closed"); return NULL; } if (self->pcmtype != SND_PCM_STREAM_CAPTURE) { PyErr_Format(ALSAAudioError, "Cannot read from playback PCM [%s]", self->cardname); return NULL; } Py_BEGIN_ALLOW_THREADS res = snd_pcm_readi(self->handle, buffer, self->periodsize); if (res == -EPIPE) { /* EPIPE means overrun */ snd_pcm_prepare(self->handle); } Py_END_ALLOW_THREADS if (res != -EPIPE) { if (res == -EAGAIN) { res = 0; } else if (res < 0) { PyErr_Format(ALSAAudioError, "%s [%s]", snd_strerror(res), self->cardname); return NULL; } } #if PY_MAJOR_VERSION < 3 return Py_BuildValue("is#", res, buffer, res*self->framesize); #else return Py_BuildValue("iy#", res, buffer, res*self->framesize); #endif } 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) { int res; int datalen; char *data; PyObject *rc = NULL; #if PY_MAJOR_VERSION < 3 if (!PyArg_ParseTuple(args,"s#:write", &data, &datalen)) return NULL; #else Py_buffer buf; if (!PyArg_ParseTuple(args,"y*:write", &buf)) return NULL; data = buf.buf; datalen = buf.len; #endif if (!self->handle) { PyErr_SetString(ALSAAudioError, "PCM device is closed"); return NULL; } if (datalen % self->framesize) { PyErr_SetString(ALSAAudioError, "Data size must be a multiple of framesize"); 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); } Py_END_ALLOW_THREADS if (res == -EAGAIN) { rc = PyLong_FromLong(0); } else if (res < 0) { PyErr_Format(ALSAAudioError, "%s [%s]", snd_strerror(res), self->cardname); } else { rc = PyLong_FromLong(res); } #if PY_MAJOR_VERSION >= 3 PyBuffer_Release(&buf); #endif 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; if (!PyArg_ParseTuple(args,"|i:pause", &enabled)) return NULL; if (!self->handle) { PyErr_SetString(ALSAAudioError, "PCM device is closed"); 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), self->cardname); return NULL; } 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_polldescriptors(alsapcm_t *self, PyObject *args) { int i, count, rc; PyObject *result; struct pollfd *fds; if (!PyArg_ParseTuple(args,":polldescriptors")) return NULL; if (!self->handle) { PyErr_SetString(ALSAAudioError, "PCM device is closed"); return NULL; } count = snd_pcm_poll_descriptors_count(self->handle); if (count < 0) { PyErr_Format(ALSAAudioError, "Can't get poll descriptor count [%s]", self->cardname); return NULL; } fds = (struct pollfd*)calloc(count, sizeof(struct pollfd)); if (!fds) { PyErr_Format(PyExc_MemoryError, "Out of memory [%s]", self->cardname); return NULL; } result = PyList_New(count); rc = snd_pcm_poll_descriptors(self->handle, fds, (unsigned int)count); if (rc != count) { PyErr_Format(ALSAAudioError, "Can't get poll descriptors [%s]", self->cardname); return NULL; } for (i = 0; i < count; ++i) { PyList_SetItem(result, i, Py_BuildValue("II", fds[i].fd, fds[i].events)); } 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}, {"dumpinfo", (PyCFunction)alsapcm_dumpinfo, METH_VARARGS}, {"read", (PyCFunction)alsapcm_read, METH_VARARGS, read_doc}, {"write", (PyCFunction)alsapcm_write, METH_VARARGS, write_doc}, {"pause", (PyCFunction)alsapcm_pause, METH_VARARGS, pause_doc}, {"close", (PyCFunction)alsapcm_close, METH_VARARGS, pcm_close_doc}, {"polldescriptors", (PyCFunction)alsapcm_polldescriptors, METH_VARARGS, pcm_polldescriptors_doc}, {NULL, NULL} }; #if PY_VERSION_HEX < 0x02020000 static PyObject * alsapcm_getattr(alsapcm_t *self, char *name) { return Py_FindMethod(alsapcm_methods, (PyObject *)self, name); } #endif static PyTypeObject ALSAPCMType = { #if PY_MAJOR_VERSION < 3 PyObject_HEAD_INIT(&PyType_Type) 0, /* ob_size */ #else PyVarObject_HEAD_INIT(&PyType_Type, 0) #endif "alsaaudio.PCM", /* tp_name */ sizeof(alsapcm_t), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor) alsapcm_dealloc, /* tp_dealloc */ 0, /* print */ #if PY_VERSION_HEX < 0x02020000 (getattrfunc)alsapcm_getattr, /* tp_getattr */ #else 0, /* tp_getattr */ #endif 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ #if PY_VERSION_HEX >= 0x02020000 PyObject_GenericGetAttr, /* tp_getattro */ #else 0, /* tp_getattro */ #endif 0, /* tp_setattro */ 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 */ }; /******************************************/ /* Mixer object wrapper */ /******************************************/ static PyTypeObject ALSAMixerType; #define MIXER_CAP_VOLUME 0x0001 #define MIXER_CAP_VOLUME_JOINED 0x0002 #define MIXER_CAP_PVOLUME 0x0004 #define MIXER_CAP_PVOLUME_JOINED 0x0008 #define MIXER_CAP_CVOLUME 0x0010 #define MIXER_CAP_CVOLUME_JOINED 0x0020 #define MIXER_CAP_SWITCH 0x0001 #define MIXER_CAP_SWITCH_JOINED 0x0002 #define MIXER_CAP_PSWITCH 0x0004 #define MIXER_CAP_PSWITCH_JOINED 0x0008 #define MIXER_CAP_CSWITCH 0x0010 #define MIXER_CAP_CSWITCH_JOINED 0x0020 #define MIXER_CAP_CSWITCH_EXCLUSIVE 0x0040 #define MIXER_CHANNEL_ALL -1 int alsamixer_gethandle(char *cardname, snd_mixer_t **handle) { int err; if ((err = snd_mixer_open(handle, 0)) < 0) return err; if ((err = snd_mixer_attach(*handle, cardname)) < 0) return err; if ((err = snd_mixer_selem_register(*handle, NULL, NULL)) < 0) return err; if ((err = snd_mixer_load(*handle)) < 0) return err; return 0; } static PyObject * alsamixer_list(PyObject *self, PyObject *args, PyObject *kwds) { snd_mixer_t *handle; snd_mixer_selem_id_t *sid; snd_mixer_elem_t *elem; int err; int cardidx = -1; char hw_device[32]; char *device = "default"; PyObject *result; char *kw[] = { "cardindex", "device", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|is", kw, &cardidx, &device)) return NULL; if (cardidx >= 0) { if (cardidx < 32) { snprintf(hw_device, sizeof(hw_device), "hw:%d", cardidx); device = hw_device; } else { PyErr_Format(ALSAAudioError, "Invalid card number %d", cardidx); return NULL; } } snd_mixer_selem_id_alloca(&sid); err = alsamixer_gethandle(device, &handle); if (err < 0) { PyErr_Format(ALSAAudioError, "%s [%s]", snd_strerror(err), device); snd_mixer_close(handle); return NULL; } result = PyList_New(0); for (elem = snd_mixer_first_elem(handle); elem; elem = snd_mixer_elem_next(elem)) { PyObject *mixer; snd_mixer_selem_get_id(elem, sid); mixer = PyUnicode_FromString(snd_mixer_selem_id_get_name(sid)); PyList_Append(result,mixer); Py_DECREF(mixer); } snd_mixer_close(handle); 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) { snd_mixer_selem_id_t *sid; snd_mixer_selem_id_alloca(&sid); snd_mixer_selem_id_set_index(sid, id); snd_mixer_selem_id_set_name(sid, control); return snd_mixer_find_selem(handle, sid); } static PyObject * alsamixer_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { alsamixer_t *self; int err; char *control = "Master"; char *device = "default"; char hw_device[32]; int cardidx = -1; int id = 0; snd_mixer_elem_t *elem; int channel; char *kw[] = { "control", "id", "cardindex", "device", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|siis", kw, &control, &id, &cardidx, &device)) { return NULL; } if (cardidx >= 0) { if (cardidx < 32) { snprintf(hw_device, sizeof(hw_device), "hw:%d", cardidx); device = hw_device; } else { PyErr_Format(ALSAAudioError, "Invalid card number %d", cardidx); return NULL; } } if (!(self = (alsamixer_t *)PyObject_New(alsamixer_t, &ALSAMixerType))) return NULL; self->handle = 0; err = alsamixer_gethandle(device, &self->handle); if (err < 0) { PyErr_Format(ALSAAudioError, "%s [%s]", snd_strerror(err), device); return NULL; } self->cardname = strdup(device); self->controlname = strdup(control); self->controlid = id; elem = alsamixer_find_elem(self->handle,control, id); if (!elem) { snd_mixer_close(self->handle); PyErr_Format(ALSAAudioError, "Unable to find mixer control %s,%i [%s]", self->controlname, self->controlid, self->cardname); free(self->cardname); free(self->controlname); return NULL; } /* Determine mixer capabilities */ self->volume_cap = self->switch_cap = 0; if (snd_mixer_selem_has_common_volume(elem)) { self->volume_cap |= MIXER_CAP_VOLUME; if (snd_mixer_selem_has_playback_volume_joined(elem)) self->volume_cap |= MIXER_CAP_VOLUME_JOINED; } else { if (snd_mixer_selem_has_playback_volume(elem)) { self->volume_cap |= MIXER_CAP_PVOLUME; if (snd_mixer_selem_has_playback_volume_joined(elem)) self->volume_cap |= MIXER_CAP_PVOLUME_JOINED; } if (snd_mixer_selem_has_capture_volume(elem)) { self->volume_cap |= MIXER_CAP_CVOLUME; if (snd_mixer_selem_has_capture_volume_joined(elem)) self->volume_cap |= MIXER_CAP_CVOLUME_JOINED; } } if (snd_mixer_selem_has_common_switch(elem)) { self->switch_cap |= MIXER_CAP_SWITCH; if (snd_mixer_selem_has_playback_switch_joined(elem)) self->switch_cap |= MIXER_CAP_SWITCH_JOINED; } else { if (snd_mixer_selem_has_playback_switch(elem)) { self->switch_cap |= MIXER_CAP_PSWITCH; if (snd_mixer_selem_has_playback_switch_joined(elem)) self->switch_cap |= MIXER_CAP_PSWITCH_JOINED; } if (snd_mixer_selem_has_capture_switch(elem)) { self->switch_cap |= MIXER_CAP_CSWITCH; if (snd_mixer_selem_has_capture_switch_joined(elem)) self->switch_cap |= MIXER_CAP_CSWITCH_JOINED; if (snd_mixer_selem_has_capture_switch_exclusive(elem)) self->switch_cap |= MIXER_CAP_CSWITCH_EXCLUSIVE; } } self->pchannels = 0; if (self->volume_cap | MIXER_CAP_PVOLUME || self->switch_cap | MIXER_CAP_PSWITCH) { if (snd_mixer_selem_is_playback_mono(elem)) self->pchannels = 1; else { for (channel=0; channel <= SND_MIXER_SCHN_LAST; channel++) { if (snd_mixer_selem_has_playback_channel(elem, channel)) self->pchannels++; else break; } } } self->cchannels = 0; if (self->volume_cap | MIXER_CAP_CVOLUME || self->switch_cap | MIXER_CAP_CSWITCH) { if (snd_mixer_selem_is_capture_mono(elem)) self->cchannels = 1; else { for (channel=0; channel <= SND_MIXER_SCHN_LAST; channel++) { if (snd_mixer_selem_has_capture_channel(elem, channel)) self->cchannels++; else break; } } } snd_mixer_selem_get_playback_volume_range(elem, &self->pmin, &self->pmax); snd_mixer_selem_get_capture_volume_range(elem, &self->cmin, &self->cmax); return (PyObject *)self; } static void alsamixer_dealloc(alsamixer_t *self) { if (self->handle) { snd_mixer_close(self->handle); free(self->cardname); free(self->controlname); self->handle = 0; } PyObject_Del(self); } static PyObject * alsamixer_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; 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) { if (!PyArg_ParseTuple(args,":cardname")) return NULL; if (!self->handle) { PyErr_SetString(ALSAAudioError, "Mixer is closed"); return NULL; } 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) { if (!PyArg_ParseTuple(args,":mixer")) return NULL; if (!self->handle) { PyErr_SetString(ALSAAudioError, "Mixer is closed"); return NULL; } 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) { if (!PyArg_ParseTuple(args,":mixerid")) return NULL; if (!self->handle) { PyErr_SetString(ALSAAudioError, "Mixer is closed"); return NULL; } 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) { PyObject *result; PyObject *item; if (!PyArg_ParseTuple(args,":volumecap")) return NULL; if (!self->handle) { PyErr_SetString(ALSAAudioError, "Mixer is closed"); return NULL; } result = PyList_New(0); if (self->volume_cap & MIXER_CAP_VOLUME) { item = PyUnicode_FromString("Volume"); PyList_Append(result, item); Py_DECREF(item); } if (self->volume_cap & MIXER_CAP_VOLUME_JOINED) { item = PyUnicode_FromString("Joined Volume"); PyList_Append(result, item); Py_DECREF(item); } if (self->volume_cap & MIXER_CAP_PVOLUME) { item = PyUnicode_FromString("Playback Volume"); PyList_Append(result, item); Py_DECREF(item); } if (self->volume_cap & MIXER_CAP_PVOLUME_JOINED) { item = PyUnicode_FromString("Joined Playback Volume"); PyList_Append(result, item); Py_DECREF(item); } if (self->volume_cap & MIXER_CAP_CVOLUME) { item = PyUnicode_FromString("Capture Volume"); PyList_Append(result, item); Py_DECREF(item); } if (self->volume_cap & MIXER_CAP_CVOLUME_JOINED) { item = PyUnicode_FromString("Joined Capture Volume"); PyList_Append(result, item); Py_DECREF(item); } 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) { PyObject *result; PyObject *item; if (!PyArg_ParseTuple(args,":switchcap")) return NULL; if (!self->handle) { PyErr_SetString(ALSAAudioError, "Mixer is closed"); return NULL; } result = PyList_New(0); if (self->volume_cap & MIXER_CAP_SWITCH) { item = PyUnicode_FromString("Mute"); PyList_Append(result, item); Py_DECREF(item); } if (self->volume_cap & MIXER_CAP_SWITCH_JOINED) { item = PyUnicode_FromString("Joined Mute"); PyList_Append(result, item); Py_DECREF(item); } if (self->volume_cap & MIXER_CAP_PSWITCH) { item = PyUnicode_FromString("Playback Mute"); PyList_Append(result, item); Py_DECREF(item); } if (self->volume_cap & MIXER_CAP_PSWITCH_JOINED) { item = PyUnicode_FromString("Joined Playback Mute"); PyList_Append(result, item); Py_DECREF(item); } if (self->volume_cap & MIXER_CAP_CSWITCH) { item = PyUnicode_FromString("Capture Mute"); PyList_Append(result, item); Py_DECREF(item); } if (self->volume_cap & MIXER_CAP_CSWITCH_JOINED) { item = PyUnicode_FromString("Joined Capture Mute"); PyList_Append(result, item); Py_DECREF(item); } if (self->volume_cap & MIXER_CAP_CSWITCH_EXCLUSIVE) { item = PyUnicode_FromString("Capture Exclusive"); PyList_Append(result, item); Py_DECREF(item); } 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 */ int range = max - min; int tmp; if (range == 0) return 0; value -= min; tmp = rint((double)value/(double)range * 100); return tmp; } static long alsamixer_getphysvolume(long min, long max, int percentage) { /* Convert from percentage to number in range */ int range = max - min; int tmp; if (range == 0) return 0; tmp = rint((double)range * ((double)percentage*.01)) + min; return tmp; } static PyObject * alsamixer_getvolume(alsamixer_t *self, PyObject *args) { snd_mixer_elem_t *elem; int channel; long ival; PyObject *pcmtypeobj = NULL; long pcmtype; PyObject *result; PyObject *item; if (!PyArg_ParseTuple(args,"|O:getvolume", &pcmtypeobj)) return NULL; if (!self->handle) { PyErr_SetString(ALSAAudioError, "Mixer is closed"); return NULL; } pcmtype = get_pcmtype(pcmtypeobj); if (pcmtype < 0) { return NULL; } elem = alsamixer_find_elem(self->handle,self->controlname,self->controlid); result = PyList_New(0); for (channel = 0; channel <= SND_MIXER_SCHN_LAST; channel++) { if (pcmtype == SND_PCM_STREAM_PLAYBACK && snd_mixer_selem_has_playback_channel(elem, channel)) { snd_mixer_selem_get_playback_volume(elem, channel, &ival); item = PyLong_FromLong(alsamixer_getpercentage(self->pmin, self->pmax, ival)); PyList_Append(result, item); Py_DECREF(item); } else if (pcmtype == SND_PCM_STREAM_CAPTURE && snd_mixer_selem_has_capture_channel(elem, channel) && snd_mixer_selem_has_capture_volume(elem)) { snd_mixer_selem_get_capture_volume(elem, channel, &ival); item = PyLong_FromLong(alsamixer_getpercentage(self->cmin, self->cmax, ival)); PyList_Append(result, item); Py_DECREF(item); } } 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) { snd_mixer_elem_t *elem; PyObject *pcmtypeobj = NULL; long pcmtype; if (!PyArg_ParseTuple(args,"|O:getrange", &pcmtypeobj)) { return NULL; } if (!self->handle) { PyErr_SetString(ALSAAudioError, "Mixer is closed"); return NULL; } pcmtype = get_pcmtype(pcmtypeobj); if (pcmtype < 0) { return NULL; } elem = alsamixer_find_elem(self->handle, self->controlname, self->controlid); if (!pcmtypeobj || (pcmtypeobj == Py_None)) { if (self->pchannels) { pcmtype = SND_PCM_STREAM_PLAYBACK; } else { pcmtype = SND_PCM_STREAM_CAPTURE; } } if (pcmtype == SND_PCM_STREAM_PLAYBACK) { if (snd_mixer_selem_has_playback_channel(elem, 0)) { return Py_BuildValue("[ii]", self->pmin, self->pmax); } PyErr_Format(ALSAAudioError, "Mixer %s,%d has no playback channel [%s]", self->controlname, self->controlid, self->cardname); return NULL; } else { if (snd_mixer_selem_has_capture_channel(elem, 0) && snd_mixer_selem_has_capture_volume(elem)) { return Py_BuildValue("[ii]", self->cmin, self->cmax); } PyErr_Format(ALSAAudioError, "Mixer %s,%d has no capture channel " "or capture volume [%s]", self->controlname, self->controlid, self->cardname); return NULL; } // Unreached statement PyErr_SetString(ALSAAudioError, "Huh?"); 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 'direction' 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) { snd_mixer_elem_t *elem; PyObject *elems; int i, count, rc; unsigned int index; char name[32]; PyObject *result; if (!PyArg_ParseTuple(args, ":getenum")) return NULL; if (!self->handle) { PyErr_SetString(ALSAAudioError, "Mixer is closed"); return NULL; } elem = alsamixer_find_elem(self->handle,self->controlname,self->controlid); if (!snd_mixer_selem_is_enumerated(elem)) { // Not an enumerated control, return an empty tuple return PyTuple_New(0); } count = snd_mixer_selem_get_enum_items(elem); if (count < 0) { PyErr_Format(ALSAAudioError, "%s [%s]", snd_strerror(count), self->cardname); return NULL; } result = PyTuple_New(2); if (!result) return NULL; rc = snd_mixer_selem_get_enum_item(elem, 0, &index); if (rc) { PyErr_Format(ALSAAudioError, "%s [%s]", snd_strerror(rc), self->cardname); return NULL; } rc = snd_mixer_selem_get_enum_item_name(elem, index, sizeof(name)-1, name); if (rc) { Py_DECREF(result); PyErr_Format(ALSAAudioError, "%s [%s]", snd_strerror(rc), self->cardname); return NULL; } PyTuple_SetItem(result, 0, PyUnicode_FromString(name)); elems = PyList_New(count); if (!elems) { Py_DECREF(result); return NULL; } for (i = 0; i < count; ++i) { rc = snd_mixer_selem_get_enum_item_name(elem, i, sizeof(name)-1, name); if (rc) { Py_DECREF(elems); Py_DECREF(result); PyErr_Format(ALSAAudioError, "%s [%s]", snd_strerror(rc), self->cardname); return NULL; } PyList_SetItem(elems, i, PyUnicode_FromString(name)); } PyTuple_SetItem(result, 1, elems); 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) { snd_mixer_elem_t *elem; int index, count, rc; if (!PyArg_ParseTuple(args, "i:setenum", &index)) return NULL; if (!self->handle) { PyErr_SetString(ALSAAudioError, "Mixer is closed"); return NULL; } elem = alsamixer_find_elem(self->handle,self->controlname,self->controlid); if (!snd_mixer_selem_is_enumerated(elem)) { PyErr_SetString(ALSAAudioError, "Not an enumerated control"); return NULL; } count = snd_mixer_selem_get_enum_items(elem); if (count < 0) { PyErr_Format(ALSAAudioError, "%s [%s]", snd_strerror(count), self->cardname); return NULL; } if (index < 0 || index >= count) { PyErr_Format(ALSAAudioError, "Enum index out of range 0 <= %d < %d", index, count); return NULL; } rc = snd_mixer_selem_set_enum_item(elem, 0, index); if (rc) { PyErr_Format(ALSAAudioError, "%s [%s]", snd_strerror(rc), self->cardname); return NULL; } Py_RETURN_NONE; } PyDoc_STRVAR(setenum_doc, "setenum(index) -> None\n\ \n\ Sets the value of the enum, where 'index' is an index into the list of\n\ available enumerated items returned by getenum()."); static PyObject * alsamixer_getmute(alsamixer_t *self, PyObject *args) { snd_mixer_elem_t *elem; int i; int ival; PyObject *result; PyObject *item; if (!PyArg_ParseTuple(args,":getmute")) return NULL; if (!self->handle) { PyErr_SetString(ALSAAudioError, "Mixer is closed"); return NULL; } elem = alsamixer_find_elem(self->handle, self->controlname, self->controlid); if (!snd_mixer_selem_has_playback_switch(elem)) { PyErr_Format(ALSAAudioError, "Mixer %s,%d has no playback switch capabilities, [%s]", self->controlname, self->controlid, self->cardname); return NULL; } result = PyList_New(0); for (i = 0; i <= SND_MIXER_SCHN_LAST; i++) { if (snd_mixer_selem_has_playback_channel(elem, i)) { snd_mixer_selem_get_playback_switch(elem, i, &ival); item = PyLong_FromLong(!ival); PyList_Append(result, item); Py_DECREF(item); } } 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) { snd_mixer_elem_t *elem; int i; int ival; PyObject *result; PyObject *item; if (!PyArg_ParseTuple(args,":getrec")) return NULL; if (!self->handle) { PyErr_SetString(ALSAAudioError, "Mixer is closed"); return NULL; } elem = alsamixer_find_elem(self->handle, self->controlname, self->controlid); if (!snd_mixer_selem_has_capture_switch(elem)) { PyErr_Format(ALSAAudioError, "Mixer %s,%d has no capture switch capabilities [%s]", self->controlname, self->controlid, self->cardname); return NULL; } result = PyList_New(0); for (i = 0; i <= SND_MIXER_SCHN_LAST; i++) { if (snd_mixer_selem_has_capture_channel(elem, i)) { snd_mixer_selem_get_capture_switch(elem, i, &ival); item = PyLong_FromLong(ival); PyList_Append(result, item); Py_DECREF(item); } } 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) { snd_mixer_elem_t *elem; int i; long volume; int physvolume; PyObject *pcmtypeobj = NULL; long pcmtype; int channel = MIXER_CHANNEL_ALL; int done = 0; if (!PyArg_ParseTuple(args,"l|iO:setvolume", &volume, &channel, &pcmtypeobj)) return NULL; if (volume < 0 || volume > 100) { PyErr_SetString(ALSAAudioError, "Volume must be between 0 and 100"); return NULL; } pcmtype = get_pcmtype(pcmtypeobj); if (pcmtype < 0) { return NULL; } if (!self->handle) { PyErr_SetString(ALSAAudioError, "Mixer is closed"); return NULL; } elem = alsamixer_find_elem(self->handle,self->controlname,self->controlid); if (!pcmtypeobj || (pcmtypeobj == Py_None)) { if (self->pchannels) pcmtype = SND_PCM_STREAM_PLAYBACK; else pcmtype = SND_PCM_STREAM_CAPTURE; } for (i = 0; i <= SND_MIXER_SCHN_LAST; i++) { if (channel == -1 || channel == i) { if (pcmtype == SND_PCM_STREAM_PLAYBACK && snd_mixer_selem_has_playback_channel(elem, i)) { physvolume = alsamixer_getphysvolume(self->pmin, self->pmax, volume); snd_mixer_selem_set_playback_volume(elem, i, physvolume); done++; } else if (pcmtype == SND_PCM_STREAM_CAPTURE && snd_mixer_selem_has_capture_channel(elem, i) && snd_mixer_selem_has_capture_volume(elem)) { physvolume = alsamixer_getphysvolume(self->cmin, self->cmax, volume); snd_mixer_selem_set_capture_volume(elem, i, physvolume); done++; } } } if(!done) { PyErr_Format(ALSAAudioError, "No such channel [%s]", self->cardname); return NULL; } Py_INCREF(Py_None); 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 direction 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) { snd_mixer_elem_t *elem; int i; int mute = 0; int done = 0; int channel = MIXER_CHANNEL_ALL; if (!PyArg_ParseTuple(args,"i|i:setmute", &mute, &channel)) return NULL; if (!self->handle) { PyErr_SetString(ALSAAudioError, "Mixer is closed"); return NULL; } elem = alsamixer_find_elem(self->handle,self->controlname,self->controlid); if (!snd_mixer_selem_has_playback_switch(elem)) { PyErr_Format(ALSAAudioError, "Mixer %s,%d has no playback switch capabilities [%s]", self->controlname, self->controlid, self->cardname); return NULL; } for (i = 0; i <= SND_MIXER_SCHN_LAST; i++) { if (channel == MIXER_CHANNEL_ALL || channel == i) { if (snd_mixer_selem_has_playback_channel(elem, i)) { snd_mixer_selem_set_playback_switch(elem, i, !mute); done++; } } } if (!done) { PyErr_Format(ALSAAudioError, "Invalid channel number [%s]", self->cardname); return NULL; } Py_INCREF(Py_None); 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) { snd_mixer_elem_t *elem; int i; int rec = 0; int done = 0; int channel = MIXER_CHANNEL_ALL; if (!PyArg_ParseTuple(args,"i|i:setrec", &rec, &channel)) return NULL; if (!self->handle) { PyErr_SetString(ALSAAudioError, "Mixer is closed"); return NULL; } elem = alsamixer_find_elem(self->handle, self->controlname, self->controlid); if (!snd_mixer_selem_has_capture_switch(elem)) { PyErr_Format(ALSAAudioError, "Mixer %s,%d has no record switch capabilities [%s]", self->controlname, self->controlid, self->cardname); return NULL; } for (i = 0; i <= SND_MIXER_SCHN_LAST; i++) { if (channel == MIXER_CHANNEL_ALL || channel == i) { if (snd_mixer_selem_has_capture_channel(elem, i)) { snd_mixer_selem_set_capture_switch(elem, i, rec); done++; } } } if (!done) { PyErr_Format(ALSAAudioError, "Invalid channel number [%s]", self->cardname); return NULL; } Py_INCREF(Py_None); 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) { int i, count, rc; PyObject *result; struct pollfd *fds; if (!PyArg_ParseTuple(args,":polldescriptors")) return NULL; if (!self->handle) { PyErr_SetString(ALSAAudioError, "Mixer is closed"); return NULL; } count = snd_mixer_poll_descriptors_count(self->handle); if (count < 0) { PyErr_Format(ALSAAudioError, "Can't get poll descriptor count [%s]", self->cardname); return NULL; } fds = (struct pollfd*)calloc(count, sizeof(struct pollfd)); if (!fds) { PyErr_Format(PyExc_MemoryError, "Out of memory [%s]", self->cardname); return NULL; } result = PyList_New(count); rc = snd_mixer_poll_descriptors(self->handle, fds, (unsigned int)count); if (rc != count) { PyErr_Format(ALSAAudioError, "Can't get poll descriptors [%s]", self->cardname); return NULL; } for (i = 0; i < count; ++i) { PyList_SetItem(result, i, Py_BuildValue("II", fds[i].fd, fds[i].events)); } 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) { int handled; if (!PyArg_ParseTuple(args,":handleevents")) return NULL; if (!self->handle) { PyErr_SetString(ALSAAudioError, "Mixer is closed"); return NULL; } handled = snd_mixer_handle_events(self->handle); if (handled < 0) { PyErr_Format(ALSAAudioError, "%s [%s]", snd_strerror(handled), self->cardname); return NULL; } return PyLong_FromLong(handled); } PyDoc_STRVAR(handleevents_doc, "handleevents() -> int\n\ \n\ Acknowledge events on the polldescriptors() file descriptors\n\ to prevent subsequent polls from returning the same events again.\n\ Returns the number of events that were acknowledged."); static PyMethodDef alsamixer_methods[] = { {"cardname", (PyCFunction)alsamixer_cardname, METH_VARARGS, mixer_cardname_doc}, {"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, getvolume_doc}, {"getrange", (PyCFunction)alsamixer_getrange, METH_VARARGS, 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, setvolume_doc}, {"setenum", (PyCFunction)alsamixer_setenum, METH_VARARGS, setenum_doc}, {"setmute", (PyCFunction)alsamixer_setmute, METH_VARARGS, setmute_doc}, {"setrec", (PyCFunction)alsamixer_setrec, METH_VARARGS, setrec_doc}, {"polldescriptors", (PyCFunction)alsamixer_polldescriptors, METH_VARARGS, polldescriptors_doc}, {"handleevents", (PyCFunction)alsamixer_handleevents, METH_VARARGS, handleevents_doc}, {NULL, NULL} }; #if PY_VERSION_HEX < 0x02020000 static PyObject * alsamixer_getattr(alsapcm_t *self, char *name) { return Py_FindMethod(alsamixer_methods, (PyObject *)self, name); } #endif static PyTypeObject ALSAMixerType = { #if PY_MAJOR_VERSION < 3 PyObject_HEAD_INIT(&PyType_Type) 0, /* ob_size */ #else PyVarObject_HEAD_INIT(&PyType_Type, 0) #endif "alsaaudio.Mixer", /* tp_name */ sizeof(alsamixer_t), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor) alsamixer_dealloc, /* tp_dealloc */ 0, /* print */ #if PY_VERSION_HEX < 0x02020000 (getattrfunc)alsamixer_getattr, /* tp_getattr */ #else 0, /* tp_getattr */ #endif 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ #if PY_VERSION_HEX >= 0x02020000 PyObject_GenericGetAttr, /* tp_getattro*/ #else 0, /* tp_getattro*/ #endif 0, /* tp_setattro*/ 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 */ }; /******************************************/ /* Module initialization */ /******************************************/ static PyMethodDef alsaaudio_methods[] = { { "card_indexes", (PyCFunction)alsacard_list_indexes, METH_VARARGS, card_indexes_doc}, { "card_name", (PyCFunction)alsacard_name, METH_VARARGS, card_name_doc}, { "cards", (PyCFunction)alsacard_list, METH_VARARGS, cards_doc}, { "pcms", (PyCFunction)alsapcm_list, METH_VARARGS, pcms_doc}, { "mixers", (PyCFunction)alsamixer_list, METH_VARARGS|METH_KEYWORDS, mixers_doc}, { 0, 0 }, }; #if PY_MAJOR_VERSION >= 3 #define _EXPORT_INT(mod, name, value) \ if (PyModule_AddIntConstant(mod, name, (long) value) == -1) return NULL; static struct PyModuleDef alsaaudio_module = { PyModuleDef_HEAD_INIT, "alsaaudio", alsaaudio_module_doc, -1, alsaaudio_methods, 0, /* m_reload */ 0, /* m_traverse */ 0, /* m_clear */ 0, /* m_free */ }; #else #define _EXPORT_INT(mod, name, value) \ if (PyModule_AddIntConstant(mod, name, (long) value) == -1) return; #endif // 3.0 #if PY_MAJOR_VERSION < 3 void initalsaaudio(void) #else PyObject *PyInit_alsaaudio(void) #endif { PyObject *m; ALSAPCMType.tp_new = alsapcm_new; ALSAMixerType.tp_new = alsamixer_new; PyEval_InitThreads(); #if PY_MAJOR_VERSION < 3 m = Py_InitModule3("alsaaudio", alsaaudio_methods, alsaaudio_module_doc); if (!m) return; #else m = PyModule_Create(&alsaaudio_module); if (!m) return NULL; #endif ALSAAudioError = PyErr_NewException("alsaaudio.ALSAAudioError", NULL, NULL); if (!ALSAAudioError) #if PY_MAJOR_VERSION < 3 return; #else return NULL; #endif /* Each call to PyModule_AddObject decrefs it; compensate: */ Py_INCREF(&ALSAPCMType); PyModule_AddObject(m, "PCM", (PyObject *)&ALSAPCMType); Py_INCREF(&ALSAMixerType); PyModule_AddObject(m, "Mixer", (PyObject *)&ALSAMixerType); Py_INCREF(ALSAAudioError); PyModule_AddObject(m, "ALSAAudioError", ALSAAudioError); _EXPORT_INT(m, "PCM_PLAYBACK",SND_PCM_STREAM_PLAYBACK); _EXPORT_INT(m, "PCM_CAPTURE",SND_PCM_STREAM_CAPTURE); _EXPORT_INT(m, "PCM_NORMAL",0); _EXPORT_INT(m, "PCM_NONBLOCK",SND_PCM_NONBLOCK); _EXPORT_INT(m, "PCM_ASYNC",SND_PCM_ASYNC); /* PCM Formats */ _EXPORT_INT(m, "PCM_FORMAT_S8",SND_PCM_FORMAT_S8); _EXPORT_INT(m, "PCM_FORMAT_U8",SND_PCM_FORMAT_U8); _EXPORT_INT(m, "PCM_FORMAT_S16_LE",SND_PCM_FORMAT_S16_LE); _EXPORT_INT(m, "PCM_FORMAT_S16_BE",SND_PCM_FORMAT_S16_BE); _EXPORT_INT(m, "PCM_FORMAT_U16_LE",SND_PCM_FORMAT_U16_LE); _EXPORT_INT(m, "PCM_FORMAT_U16_BE",SND_PCM_FORMAT_U16_BE); _EXPORT_INT(m, "PCM_FORMAT_S24_LE",SND_PCM_FORMAT_S24_LE); _EXPORT_INT(m, "PCM_FORMAT_S24_BE",SND_PCM_FORMAT_S24_BE); _EXPORT_INT(m, "PCM_FORMAT_U24_LE",SND_PCM_FORMAT_U24_LE); _EXPORT_INT(m, "PCM_FORMAT_U24_BE",SND_PCM_FORMAT_U24_BE); _EXPORT_INT(m, "PCM_FORMAT_S32_LE",SND_PCM_FORMAT_S32_LE); _EXPORT_INT(m, "PCM_FORMAT_S32_BE",SND_PCM_FORMAT_S32_BE); _EXPORT_INT(m, "PCM_FORMAT_U32_LE",SND_PCM_FORMAT_U32_LE); _EXPORT_INT(m, "PCM_FORMAT_U32_BE",SND_PCM_FORMAT_U32_BE); _EXPORT_INT(m, "PCM_FORMAT_FLOAT_LE",SND_PCM_FORMAT_FLOAT_LE); _EXPORT_INT(m, "PCM_FORMAT_FLOAT_BE",SND_PCM_FORMAT_FLOAT_BE); _EXPORT_INT(m, "PCM_FORMAT_FLOAT64_LE",SND_PCM_FORMAT_FLOAT64_LE); _EXPORT_INT(m, "PCM_FORMAT_FLOAT64_BE",SND_PCM_FORMAT_FLOAT64_BE); _EXPORT_INT(m, "PCM_FORMAT_MU_LAW",SND_PCM_FORMAT_MU_LAW); _EXPORT_INT(m, "PCM_FORMAT_A_LAW",SND_PCM_FORMAT_A_LAW); _EXPORT_INT(m, "PCM_FORMAT_IMA_ADPCM",SND_PCM_FORMAT_IMA_ADPCM); _EXPORT_INT(m, "PCM_FORMAT_MPEG",SND_PCM_FORMAT_MPEG); _EXPORT_INT(m, "PCM_FORMAT_GSM",SND_PCM_FORMAT_GSM); /* DSD sample formats are included in ALSA 1.0.29 and higher * define OVERRIDE_DSD_COMPILE to include DSD sample support * if you use a patched ALSA lib version */ #if SND_LIB_VERSION >= 0x1001d || defined OVERRIDE_DSD_COMPILE _EXPORT_INT(m, "PCM_FORMAT_DSD_U8", SND_PCM_FORMAT_DSD_U8); _EXPORT_INT(m, "PCM_FORMAT_DSD_U16_LE", SND_PCM_FORMAT_DSD_U16_LE); _EXPORT_INT(m, "PCM_FORMAT_DSD_U32_LE", SND_PCM_FORMAT_DSD_U32_LE); _EXPORT_INT(m, "PCM_FORMAT_DSD_U32_BE", SND_PCM_FORMAT_DSD_U32_BE); #endif /* Mixer stuff */ _EXPORT_INT(m, "MIXER_CHANNEL_ALL", MIXER_CHANNEL_ALL); #if 0 // Omit for now - use case unknown _EXPORT_INT(m, "MIXER_SCHN_UNKNOWN", SND_MIXER_SCHN_UNKNOWN); _EXPORT_INT(m, "MIXER_SCHN_FRONT_LEFT", SND_MIXER_SCHN_FRONT_LEFT); _EXPORT_INT(m, "MIXER_SCHN_FRONT_RIGHT", SND_MIXER_SCHN_FRONT_RIGHT); _EXPORT_INT(m, "MIXER_SCHN_REAR_LEFT", SND_MIXER_SCHN_REAR_LEFT); _EXPORT_INT(m, "MIXER_SCHN_REAR_RIGHT", SND_MIXER_SCHN_REAR_RIGHT); _EXPORT_INT(m, "MIXER_SCHN_FRONT_CENTER", SND_MIXER_SCHN_FRONT_CENTER); _EXPORT_INT(m, "MIXER_SCHN_WOOFER", SND_MIXER_SCHN_WOOFER); _EXPORT_INT(m, "MIXER_SCHN_SIDE_LEFT", SND_MIXER_SCHN_SIDE_LEFT); _EXPORT_INT(m, "MIXER_SCHN_SIDE_RIGHT", SND_MIXER_SCHN_SIDE_RIGHT); _EXPORT_INT(m, "MIXER_SCHN_REAR_CENTER", SND_MIXER_SCHN_REAR_CENTER); _EXPORT_INT(m, "MIXER_SCHN_MONO", SND_MIXER_SCHN_MONO); #endif #if PY_MAJOR_VERSION >= 3 return m; #endif } pyalsaaudio-0.8.4/playwav.py0000755000175000001470000000303513054110172015631 0ustar larsdev00000000000000#!/usr/bin/env python # Simple test script that plays (some) wav files from __future__ import print_function import sys import wave import getopt import alsaaudio def play(device, f): print('%d channels, %d sampling rate\n' % (f.getnchannels(), f.getframerate())) # Set attributes device.setchannels(f.getnchannels()) device.setrate(f.getframerate()) # 8bit is unsigned in wav files if f.getsampwidth() == 1: device.setformat(alsaaudio.PCM_FORMAT_U8) # Otherwise we assume signed data, little endian elif f.getsampwidth() == 2: device.setformat(alsaaudio.PCM_FORMAT_S16_LE) elif f.getsampwidth() == 3: device.setformat(alsaaudio.PCM_FORMAT_S24_LE) elif f.getsampwidth() == 4: device.setformat(alsaaudio.PCM_FORMAT_S32_LE) else: raise ValueError('Unsupported format') periodsize = f.getframerate() / 8 device.setperiodsize(periodsize) data = f.readframes(periodsize) while data: # Read data from stdin device.write(data) data = f.readframes(periodsize) def usage(): print('usage: playwav.py [-d ] ', file=sys.stderr) sys.exit(2) if __name__ == '__main__': device = 'default' opts, args = getopt.getopt(sys.argv[1:], 'd:') for o, a in opts: if o == '-d': device = a if not args: usage() f = wave.open(args[0], 'rb') device = alsaaudio.PCM(device=device) play(device, f) f.close() pyalsaaudio-0.8.4/CHANGES0000644000175000001470000000403712601246475014604 0ustar larsdev00000000000000Version 0.8.2: - fix #3 (we cannot get the revision from git for pip installs) Version 0.8.1: - document changes (this file) Version 0.8: - 'PCM()' has new 'device' and 'cardindex' keyword arguments. The keyword 'device' allows to select virtual devices, 'cardindex' can be used to select hardware cards by index (as with 'mixers()' and 'Mixer()'). The 'card' keyword argument is still supported, but deprecated. The reason for this change is that the 'card' keyword argument guessed a device name from the card name, but this only works sometimes, and breaks opening virtual devices. - new function 'pcms()' to list available PCM devices. - mixers() and Mixer() take an additional 'device' keyword argument. This allows to list or open virtual devices. - The default behaviour of Mixer() without any arguments has changed. Now Mixer() will try to open the 'default' Mixer instead of the Mixer that is associated with card 0. - fix a memory leak under Python 3.x - some more memory leaks in error conditions fixed. Version 0.7: - fixed several memory leaks (patch 3372909), contributed by Erik Kulyk) Version 0.6: - mostly reverted patch 2594366: alsapcm_setup did not do complete error checking for good reasons; some ALSA functions in alsapcm_setup may fail without rendering the device unusable Version 0.5: - applied patch 2777035: Fixed setrec method in alsaaudio.c This included a mixertest with more features - fixed/applied patch 2594366: alsapcm_setup does not do any error checking Version 0.4: - API changes: mixers() and Mixer() now take a card index instead of a card name as optional parameter. - Support for Python 3.0 - Documentation converted to reStructuredText; use Sphinx instead of LaTeX. - added cards() - added PCM.close() - added Mixer.close() - added mixer.getenum() Version 0.3: - wrapped blocking calls with Py_BEGIN_ALLOW_THREADS/Py_END_ALLOW_THREADS - added pause Version 0.2: - Many bugfixes related to playback in particular - Module documentation in the doc subdirectory Version 0.1: - Initial version pyalsaaudio-0.8.4/test.py0000755000175000001470000001002513053413154015127 0ustar larsdev00000000000000#!/usr/bin/env python # These are internal tests. They shouldn't fail, but they don't cover all # of the ALSA API. Most importantly PCM.read and PCM.write are missing. # These need to be tested by playbacktest.py and recordtest.py # In case of a problem, run these tests. If they fail, file a bug report on # http://github.com/larsimmisch/pyalsaaudio/issues import unittest import alsaaudio import warnings # we can't test read and write well - these are tested otherwise PCMMethods = [('pcmtype', None), ('pcmmode', None), ('cardname', None), ('setchannels', (2,)), ('setrate', (44100,)), ('setformat', (alsaaudio.PCM_FORMAT_S8,)), ('setperiodsize', (320,))] # A clever test would look at the Mixer capabilities and selectively run the # omitted tests, but I am too tired for that. MixerMethods = [('cardname', None), ('mixer', None), ('mixerid', None), ('switchcap', None), ('volumecap', None), ('getvolume', None), ('getrange', None), ('getenum', None), # ('getmute', None), # ('getrec', None), # ('setvolume', (60,)), # ('setmute', (0,)) # ('setrec', (0')), ] class MixerTest(unittest.TestCase): """Test Mixer objects""" def testMixer(self): """Open the default Mixers and the Mixers on every card""" for c in alsaaudio.card_indexes(): mixers = alsaaudio.mixers(cardindex=c) for m in mixers: mixer = alsaaudio.Mixer(m, cardindex=c) mixer.close() def testMixerAll(self): "Run common Mixer methods on an open object" mixers = alsaaudio.mixers() mixer = alsaaudio.Mixer(mixers[0]) for m, a in MixerMethods: f = alsaaudio.Mixer.__dict__[m] if a is None: f(mixer) else: f(mixer, *a) mixer.close() def testMixerClose(self): """Run common Mixer methods on a closed object and verify it raises an error""" mixers = alsaaudio.mixers() mixer = alsaaudio.Mixer(mixers[0]) mixer.close() for m, a in MixerMethods: f = alsaaudio.Mixer.__dict__[m] if a is None: self.assertRaises(alsaaudio.ALSAAudioError, f, mixer) else: self.assertRaises(alsaaudio.ALSAAudioError, f, mixer, *a) class PCMTest(unittest.TestCase): """Test PCM objects""" def testPCM(self): "Open a PCM object on every card" for c in alsaaudio.card_indexes(): pcm = alsaaudio.PCM(cardindex=c) pcm.close() def testPCMAll(self): "Run all PCM methods on an open object" pcm = alsaaudio.PCM() for m, a in PCMMethods: f = alsaaudio.PCM.__dict__[m] if a is None: f(pcm) else: f(pcm, *a) pcm.close() def testPCMClose(self): "Run all PCM methods on a closed object and verify it raises an error" pcm = alsaaudio.PCM() pcm.close() for m, a in PCMMethods: f = alsaaudio.PCM.__dict__[m] if a is None: self.assertRaises(alsaaudio.ALSAAudioError, f, pcm) else: self.assertRaises(alsaaudio.ALSAAudioError, f, pcm, *a) def testPCMDeprecated(self): with warnings.catch_warnings(record=True) as w: # Cause all warnings to always be triggered. warnings.simplefilter("always") try: pcm = alsaaudio.PCM(card='default') except alsaaudio.ALSAAudioError: pass # Verify we got a DepreciationWarning assert len(w) == 1 assert issubclass(w[-1].category, DeprecationWarning) if __name__ == '__main__': unittest.main() pyalsaaudio-0.8.4/playbacktest.py0000755000175000001470000000257113053406056016650 0ustar larsdev00000000000000#!/usr/bin/env python ## playbacktest.py ## ## This is an example of a simple sound playback script. ## ## The script opens an ALSA pcm for sound playback. Set ## various attributes of the device. It then reads data ## from stdin and writes it to the device. ## ## To test it out do the following: ## python recordtest.py out.raw # talk to the microphone ## python playbacktest.py out.raw from __future__ import print_function import sys import time import getopt import alsaaudio def usage(): print('usage: playbacktest.py [-d ] ', file=sys.stderr) sys.exit(2) if __name__ == '__main__': device = 'default' opts, args = getopt.getopt(sys.argv[1:], 'd:') for o, a in opts: if o == '-d': device = a if not args: usage() f = open(args[0], 'rb') # Open the device in playback mode. out = alsaaudio.PCM(alsaaudio.PCM_PLAYBACK, device=device) # Set attributes: Mono, 44100 Hz, 16 bit little endian frames out.setchannels(1) out.setrate(44100) out.setformat(alsaaudio.PCM_FORMAT_S16_LE) # The period size controls the internal number of frames per period. # The significance of this parameter is documented in the ALSA api. out.setperiodsize(160) # Read data from stdin data = f.read(320) while data: out.write(data) data = f.read(320) pyalsaaudio-0.8.4/MANIFEST.in0000644000175000001470000000021112267770727015347 0ustar larsdev00000000000000include *.py include CHANGES include TODO include LICENSE recursive-include doc *.html *.gif *.png *.css *.py *.rst *.js *.json Makefilepyalsaaudio-0.8.4/doc/0002755000175000001470000000000013054110315014336 5ustar larsdev00000000000000pyalsaaudio-0.8.4/doc/html/0002755000175000001470000000000013054110315015302 5ustar larsdev00000000000000pyalsaaudio-0.8.4/doc/html/py-modindex.html0000644000175000001470000000706113054110214020425 0ustar larsdev00000000000000 Python Module Index — alsaaudio 0.8.4 documentation

Python Module Index

a
 
a
alsaaudio (Linux)
pyalsaaudio-0.8.4/doc/html/pyalsaaudio.html0000644000175000001470000002401413054110213020477 0ustar larsdev00000000000000 Introduction — alsaaudio 0.8.4 documentation

Introduction

Author:Casper Wilstrup <cwi@aves.dk>
Author:Lars Immisch <lars@ibp.de>

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.

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. 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.

What is ALSA

The Advanced Linux Sound Architecture (ALSA) provides audio and MIDI functionality to the Linux operating system.

Logically ALSA consists of these components:

  • A set of kernel drivers. — These drivers are responsible for handling the physical sound hardware from within the Linux kernel, and have been the standard sound implementation in Linux since kernel version 2.5
  • A kernel level API for manipulating the ALSA devices.
  • A user-space C library for simplified access to the sound hardware from userspace applications. This library is called libasound and is required by all ALSA capable applications.

More information about ALSA may be found on the project homepage http://www.alsa-project.org

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 support exists in the standard library.

There are a few other “ALSA for Python” projects available, including at least two different projects called pyAlsa. Neither of these seem to be under active 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 the ALSA Mixer API.

MIDI support is not available, and since I don’t own any MIDI hardware, it’s difficult for me to implement it. Volunteers to work on this would be greatly appreciated.

Installation

Note: the wrappers link with the alsasound library (from the alsa-lib package) and need the ALSA headers for compilation. Verify that you have /usr/lib/libasound.so and /usr/include/alsa (or similar paths) before building.

On Debian (and probably Ubuntu), install libasound2-dev.

Naturally you also need to use a kernel with proper ALSA support. This is the default in Linux kernel 2.6 and later. If you are using kernel version 2.4 you may need to install the ALSA patches yourself - although most distributions ship with ALSA kernels.

To install, execute the following: —

$ python setup.py build

And then as root: —

# python setup.py install

Testing

First of all, run:

$ python test.py

This is a small test suite that mostly performs consistency tests. If it fails, please file a bug report.

To test PCM recordings (on your default soundcard), verify your microphone works, then do:

$ python recordtest.py <filename>

Speak into the microphone, and interrupt the recording at any time with Ctl-C.

Play back the recording with:

$ python playbacktest.py <filename>

Table Of Contents

Previous topic

alsaaudio documentation

Next topic

PCM Terminology and Concepts

This Page

pyalsaaudio-0.8.4/doc/html/terminology.html0000644000175000001470000001732113054110213020537 0ustar larsdev00000000000000 PCM Terminology and Concepts — alsaaudio 0.8.4 documentation

PCM Terminology and Concepts

In order to use PCM devices it is useful to be familiar with some concepts and terminology.

Sample

PCM audio, whether it is input or output, consists of samples. A single sample represents the amplitude of one channel of sound at a certain point in time. A lot of individual samples are necessary to represent actual sound; for CD audio, 44100 samples are taken every second.

Samples can be of many different sizes, ranging from 8 bit to 64 bit precision. The specific format of each sample can also vary - they can be big endian byte integers, little endian byte integers, or floating point numbers.

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.

Frame
A frame consists of exactly one sample per channel. If there is only one channel (Mono sound) a frame is simply a single sample. If the sound is stereo, each frame consists of two samples, etc.
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
Rate
PCM sound consists of a flow of sound frames. The sound rate controls how often the current frame is replaced. For example, a rate of 8000 Hz 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 second at a certain frame size and rate.

8000 Hz mono sound with 8 bit (1 byte) samples has a data rate of 8000 * 1 * 1 = 8 kb/s or 64kbit/s. This is typically used for telephony.

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)

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.
Period size
This is the size of each period in Hz. Not bytes, but Hz!. In 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 read will return either 32 frames of data or nothing at all.

Once you understand these concepts, you will be ready to use the PCM API. Read on.

Previous topic

Introduction

Next topic

alsaaudio

This Page

pyalsaaudio-0.8.4/doc/html/search.html0000644000175000001470000000664513054110214017444 0ustar larsdev00000000000000 Search — alsaaudio 0.8.4 documentation

Search

Please activate JavaScript to enable the search functionality.

From here you can search these documents. Enter your search words into the box below and click "search". Note that the search function will automatically search for all of the words. Pages containing fewer words won't appear in the result list.

pyalsaaudio-0.8.4/doc/html/genindex.html0000644000175000001470000002077313054110214017776 0ustar larsdev00000000000000 Index — alsaaudio 0.8.4 documentation pyalsaaudio-0.8.4/doc/html/_static/0002755000175000001470000000000013054110315016730 5ustar larsdev00000000000000pyalsaaudio-0.8.4/doc/html/_static/down-pressed.png0000644000175000001470000000056012062620216022053 0ustar larsdev00000000000000PNG  IHDRasRGBbKGDC pHYs B(xtIME -vF#IDAT8!OAJ, ++@I vbÿ@W7F HN#48646TMvv޼7Dsax1U q;< E-f)j%po4xF78G>)- EYm4%7YTk-Qa"NWAo-yeq,) Ypt\hqmszG]Nar߶s^l vh\2%0EeRvIENDB`pyalsaaudio-0.8.4/doc/html/_static/comment-bright.png0000644000175000001470000000665412062620216022372 0ustar larsdev00000000000000PNG  IHDRa OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3-bKGD pHYs  tIME 6 B\<IDAT8˅Kh]es1mA`jh[-E(FEaA!bIȐ*BX"؁4)NURZ!Mhjssm؋^-\gg ]o|Ҭ[346>zd ]#8Oݺt{5uIXN!I=@Vf=v1}e>;fvnvxaHrʪJF`D¹WZ]S%S)WAb |0K=So7D~\~q-˟\aMZ,S'*} F`Nnz674U= 0; }; // Invoke a method (with arguments) on every item in a collection. _.invoke = function(obj, method) { var args = slice.call(arguments, 2); var isFunc = _.isFunction(method); return _.map(obj, function(value) { return (isFunc ? method : value[method]).apply(value, args); }); }; // Convenience version of a common use case of `map`: fetching a property. _.pluck = function(obj, key) { return _.map(obj, _.property(key)); }; // Convenience version of a common use case of `filter`: selecting only objects // containing specific `key:value` pairs. _.where = function(obj, attrs) { return _.filter(obj, _.matches(attrs)); }; // Convenience version of a common use case of `find`: getting the first object // containing specific `key:value` pairs. _.findWhere = function(obj, attrs) { return _.find(obj, _.matches(attrs)); }; // Return the maximum element (or element-based computation). _.max = function(obj, iteratee, context) { var result = -Infinity, lastComputed = -Infinity, value, computed; if (iteratee == null && obj != null) { obj = obj.length === +obj.length ? obj : _.values(obj); for (var i = 0, length = obj.length; i < length; i++) { value = obj[i]; if (value > result) { result = value; } } } else { iteratee = _.iteratee(iteratee, context); _.each(obj, function(value, index, list) { computed = iteratee(value, index, list); if (computed > lastComputed || computed === -Infinity && result === -Infinity) { result = value; lastComputed = computed; } }); } return result; }; // Return the minimum element (or element-based computation). _.min = function(obj, iteratee, context) { var result = Infinity, lastComputed = Infinity, value, computed; if (iteratee == null && obj != null) { obj = obj.length === +obj.length ? obj : _.values(obj); for (var i = 0, length = obj.length; i < length; i++) { value = obj[i]; if (value < result) { result = value; } } } else { iteratee = _.iteratee(iteratee, context); _.each(obj, function(value, index, list) { computed = iteratee(value, index, list); if (computed < lastComputed || computed === Infinity && result === Infinity) { result = value; lastComputed = computed; } }); } return result; }; // Shuffle a collection, using the modern version of the // [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle). _.shuffle = function(obj) { var set = obj && obj.length === +obj.length ? obj : _.values(obj); var length = set.length; var shuffled = Array(length); for (var index = 0, rand; index < length; index++) { rand = _.random(0, index); if (rand !== index) shuffled[index] = shuffled[rand]; shuffled[rand] = set[index]; } return shuffled; }; // Sample **n** random values from a collection. // If **n** is not specified, returns a single random element. // The internal `guard` argument allows it to work with `map`. _.sample = function(obj, n, guard) { if (n == null || guard) { if (obj.length !== +obj.length) obj = _.values(obj); return obj[_.random(obj.length - 1)]; } return _.shuffle(obj).slice(0, Math.max(0, n)); }; // Sort the object's values by a criterion produced by an iteratee. _.sortBy = function(obj, iteratee, context) { iteratee = _.iteratee(iteratee, context); return _.pluck(_.map(obj, function(value, index, list) { return { value: value, index: index, criteria: iteratee(value, index, list) }; }).sort(function(left, right) { var a = left.criteria; var b = right.criteria; if (a !== b) { if (a > b || a === void 0) return 1; if (a < b || b === void 0) return -1; } return left.index - right.index; }), 'value'); }; // An internal function used for aggregate "group by" operations. var group = function(behavior) { return function(obj, iteratee, context) { var result = {}; iteratee = _.iteratee(iteratee, context); _.each(obj, function(value, index) { var key = iteratee(value, index, obj); behavior(result, value, key); }); return result; }; }; // Groups the object's values by a criterion. Pass either a string attribute // to group by, or a function that returns the criterion. _.groupBy = group(function(result, value, key) { if (_.has(result, key)) result[key].push(value); else result[key] = [value]; }); // Indexes the object's values by a criterion, similar to `groupBy`, but for // when you know that your index values will be unique. _.indexBy = group(function(result, value, key) { result[key] = value; }); // Counts instances of an object that group by a certain criterion. Pass // either a string attribute to count by, or a function that returns the // criterion. _.countBy = group(function(result, value, key) { if (_.has(result, key)) result[key]++; else result[key] = 1; }); // Use a comparator function to figure out the smallest index at which // an object should be inserted so as to maintain order. Uses binary search. _.sortedIndex = function(array, obj, iteratee, context) { iteratee = _.iteratee(iteratee, context, 1); var value = iteratee(obj); var low = 0, high = array.length; while (low < high) { var mid = low + high >>> 1; if (iteratee(array[mid]) < value) low = mid + 1; else high = mid; } return low; }; // Safely create a real, live array from anything iterable. _.toArray = function(obj) { if (!obj) return []; if (_.isArray(obj)) return slice.call(obj); if (obj.length === +obj.length) return _.map(obj, _.identity); return _.values(obj); }; // Return the number of elements in an object. _.size = function(obj) { if (obj == null) return 0; return obj.length === +obj.length ? obj.length : _.keys(obj).length; }; // Split a collection into two arrays: one whose elements all satisfy the given // predicate, and one whose elements all do not satisfy the predicate. _.partition = function(obj, predicate, context) { predicate = _.iteratee(predicate, context); var pass = [], fail = []; _.each(obj, function(value, key, obj) { (predicate(value, key, obj) ? pass : fail).push(value); }); return [pass, fail]; }; // Array Functions // --------------- // Get the first element of an array. Passing **n** will return the first N // values in the array. Aliased as `head` and `take`. The **guard** check // allows it to work with `_.map`. _.first = _.head = _.take = function(array, n, guard) { if (array == null) return void 0; if (n == null || guard) return array[0]; if (n < 0) return []; return slice.call(array, 0, n); }; // Returns everything but the last entry of the array. Especially useful on // the arguments object. Passing **n** will return all the values in // the array, excluding the last N. The **guard** check allows it to work with // `_.map`. _.initial = function(array, n, guard) { return slice.call(array, 0, Math.max(0, array.length - (n == null || guard ? 1 : n))); }; // Get the last element of an array. Passing **n** will return the last N // values in the array. The **guard** check allows it to work with `_.map`. _.last = function(array, n, guard) { if (array == null) return void 0; if (n == null || guard) return array[array.length - 1]; return slice.call(array, Math.max(array.length - n, 0)); }; // Returns everything but the first entry of the array. Aliased as `tail` and `drop`. // Especially useful on the arguments object. Passing an **n** will return // the rest N values in the array. The **guard** // check allows it to work with `_.map`. _.rest = _.tail = _.drop = function(array, n, guard) { return slice.call(array, n == null || guard ? 1 : n); }; // Trim out all falsy values from an array. _.compact = function(array) { return _.filter(array, _.identity); }; // Internal implementation of a recursive `flatten` function. var flatten = function(input, shallow, strict, output) { if (shallow && _.every(input, _.isArray)) { return concat.apply(output, input); } for (var i = 0, length = input.length; i < length; i++) { var value = input[i]; if (!_.isArray(value) && !_.isArguments(value)) { if (!strict) output.push(value); } else if (shallow) { push.apply(output, value); } else { flatten(value, shallow, strict, output); } } return output; }; // Flatten out an array, either recursively (by default), or just one level. _.flatten = function(array, shallow) { return flatten(array, shallow, false, []); }; // Return a version of the array that does not contain the specified value(s). _.without = function(array) { return _.difference(array, slice.call(arguments, 1)); }; // Produce a duplicate-free version of the array. If the array has already // been sorted, you have the option of using a faster algorithm. // Aliased as `unique`. _.uniq = _.unique = function(array, isSorted, iteratee, context) { if (array == null) return []; if (!_.isBoolean(isSorted)) { context = iteratee; iteratee = isSorted; isSorted = false; } if (iteratee != null) iteratee = _.iteratee(iteratee, context); var result = []; var seen = []; for (var i = 0, length = array.length; i < length; i++) { var value = array[i]; if (isSorted) { if (!i || seen !== value) result.push(value); seen = value; } else if (iteratee) { var computed = iteratee(value, i, array); if (_.indexOf(seen, computed) < 0) { seen.push(computed); result.push(value); } } else if (_.indexOf(result, value) < 0) { result.push(value); } } return result; }; // Produce an array that contains the union: each distinct element from all of // the passed-in arrays. _.union = function() { return _.uniq(flatten(arguments, true, true, [])); }; // Produce an array that contains every item shared between all the // passed-in arrays. _.intersection = function(array) { if (array == null) return []; var result = []; var argsLength = arguments.length; for (var i = 0, length = array.length; i < length; i++) { var item = array[i]; if (_.contains(result, item)) continue; for (var j = 1; j < argsLength; j++) { if (!_.contains(arguments[j], item)) break; } if (j === argsLength) result.push(item); } return result; }; // Take the difference between one array and a number of other arrays. // Only the elements present in just the first array will remain. _.difference = function(array) { var rest = flatten(slice.call(arguments, 1), true, true, []); return _.filter(array, function(value){ return !_.contains(rest, value); }); }; // Zip together multiple lists into a single array -- elements that share // an index go together. _.zip = function(array) { if (array == null) return []; var length = _.max(arguments, 'length').length; var results = Array(length); for (var i = 0; i < length; i++) { results[i] = _.pluck(arguments, i); } return results; }; // Converts lists into objects. Pass either a single array of `[key, value]` // pairs, or two parallel arrays of the same length -- one of keys, and one of // the corresponding values. _.object = function(list, values) { if (list == null) return {}; var result = {}; for (var i = 0, length = list.length; i < length; i++) { if (values) { result[list[i]] = values[i]; } else { result[list[i][0]] = list[i][1]; } } return result; }; // Return the position of the first occurrence of an item in an array, // or -1 if the item is not included in the array. // If the array is large and already in sort order, pass `true` // for **isSorted** to use binary search. _.indexOf = function(array, item, isSorted) { if (array == null) return -1; var i = 0, length = array.length; if (isSorted) { if (typeof isSorted == 'number') { i = isSorted < 0 ? Math.max(0, length + isSorted) : isSorted; } else { i = _.sortedIndex(array, item); return array[i] === item ? i : -1; } } for (; i < length; i++) if (array[i] === item) return i; return -1; }; _.lastIndexOf = function(array, item, from) { if (array == null) return -1; var idx = array.length; if (typeof from == 'number') { idx = from < 0 ? idx + from + 1 : Math.min(idx, from + 1); } while (--idx >= 0) if (array[idx] === item) return idx; return -1; }; // Generate an integer Array containing an arithmetic progression. A port of // the native Python `range()` function. See // [the Python documentation](http://docs.python.org/library/functions.html#range). _.range = function(start, stop, step) { if (arguments.length <= 1) { stop = start || 0; start = 0; } step = step || 1; var length = Math.max(Math.ceil((stop - start) / step), 0); var range = Array(length); for (var idx = 0; idx < length; idx++, start += step) { range[idx] = start; } return range; }; // Function (ahem) Functions // ------------------ // Reusable constructor function for prototype setting. var Ctor = function(){}; // Create a function bound to a given object (assigning `this`, and arguments, // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if // available. _.bind = function(func, context) { var args, bound; if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); if (!_.isFunction(func)) throw new TypeError('Bind must be called on a function'); args = slice.call(arguments, 2); bound = function() { if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments))); Ctor.prototype = func.prototype; var self = new Ctor; Ctor.prototype = null; var result = func.apply(self, args.concat(slice.call(arguments))); if (_.isObject(result)) return result; return self; }; return bound; }; // Partially apply a function by creating a version that has had some of its // arguments pre-filled, without changing its dynamic `this` context. _ acts // as a placeholder, allowing any combination of arguments to be pre-filled. _.partial = function(func) { var boundArgs = slice.call(arguments, 1); return function() { var position = 0; var args = boundArgs.slice(); for (var i = 0, length = args.length; i < length; i++) { if (args[i] === _) args[i] = arguments[position++]; } while (position < arguments.length) args.push(arguments[position++]); return func.apply(this, args); }; }; // Bind a number of an object's methods to that object. Remaining arguments // are the method names to be bound. Useful for ensuring that all callbacks // defined on an object belong to it. _.bindAll = function(obj) { var i, length = arguments.length, key; if (length <= 1) throw new Error('bindAll must be passed function names'); for (i = 1; i < length; i++) { key = arguments[i]; obj[key] = _.bind(obj[key], obj); } return obj; }; // Memoize an expensive function by storing its results. _.memoize = function(func, hasher) { var memoize = function(key) { var cache = memoize.cache; var address = hasher ? hasher.apply(this, arguments) : key; if (!_.has(cache, address)) cache[address] = func.apply(this, arguments); return cache[address]; }; memoize.cache = {}; return memoize; }; // Delays a function for the given number of milliseconds, and then calls // it with the arguments supplied. _.delay = function(func, wait) { var args = slice.call(arguments, 2); return setTimeout(function(){ return func.apply(null, args); }, wait); }; // Defers a function, scheduling it to run after the current call stack has // cleared. _.defer = function(func) { return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1))); }; // Returns a function, that, when invoked, will only be triggered at most once // during a given window of time. Normally, the throttled function will run // as much as it can, without ever going more than once per `wait` duration; // but if you'd like to disable the execution on the leading edge, pass // `{leading: false}`. To disable execution on the trailing edge, ditto. _.throttle = function(func, wait, options) { var context, args, result; var timeout = null; var previous = 0; if (!options) options = {}; var later = function() { previous = options.leading === false ? 0 : _.now(); timeout = null; result = func.apply(context, args); if (!timeout) context = args = null; }; return function() { var now = _.now(); if (!previous && options.leading === false) previous = now; var remaining = wait - (now - previous); context = this; args = arguments; if (remaining <= 0 || remaining > wait) { clearTimeout(timeout); timeout = null; previous = now; result = func.apply(context, args); if (!timeout) context = args = null; } else if (!timeout && options.trailing !== false) { timeout = setTimeout(later, remaining); } return result; }; }; // Returns a function, that, as long as it continues to be invoked, will not // be triggered. The function will be called after it stops being called for // N milliseconds. If `immediate` is passed, trigger the function on the // leading edge, instead of the trailing. _.debounce = function(func, wait, immediate) { var timeout, args, context, timestamp, result; var later = function() { var last = _.now() - timestamp; if (last < wait && last > 0) { timeout = setTimeout(later, wait - last); } else { timeout = null; if (!immediate) { result = func.apply(context, args); if (!timeout) context = args = null; } } }; return function() { context = this; args = arguments; timestamp = _.now(); var callNow = immediate && !timeout; if (!timeout) timeout = setTimeout(later, wait); if (callNow) { result = func.apply(context, args); context = args = null; } return result; }; }; // Returns the first function passed as an argument to the second, // allowing you to adjust arguments, run code before and after, and // conditionally execute the original function. _.wrap = function(func, wrapper) { return _.partial(wrapper, func); }; // Returns a negated version of the passed-in predicate. _.negate = function(predicate) { return function() { return !predicate.apply(this, arguments); }; }; // Returns a function that is the composition of a list of functions, each // consuming the return value of the function that follows. _.compose = function() { var args = arguments; var start = args.length - 1; return function() { var i = start; var result = args[start].apply(this, arguments); while (i--) result = args[i].call(this, result); return result; }; }; // Returns a function that will only be executed after being called N times. _.after = function(times, func) { return function() { if (--times < 1) { return func.apply(this, arguments); } }; }; // Returns a function that will only be executed before being called N times. _.before = function(times, func) { var memo; return function() { if (--times > 0) { memo = func.apply(this, arguments); } else { func = null; } return memo; }; }; // Returns a function that will be executed at most one time, no matter how // often you call it. Useful for lazy initialization. _.once = _.partial(_.before, 2); // Object Functions // ---------------- // Retrieve the names of an object's properties. // Delegates to **ECMAScript 5**'s native `Object.keys` _.keys = function(obj) { if (!_.isObject(obj)) return []; if (nativeKeys) return nativeKeys(obj); var keys = []; for (var key in obj) if (_.has(obj, key)) keys.push(key); return keys; }; // Retrieve the values of an object's properties. _.values = function(obj) { var keys = _.keys(obj); var length = keys.length; var values = Array(length); for (var i = 0; i < length; i++) { values[i] = obj[keys[i]]; } return values; }; // Convert an object into a list of `[key, value]` pairs. _.pairs = function(obj) { var keys = _.keys(obj); var length = keys.length; var pairs = Array(length); for (var i = 0; i < length; i++) { pairs[i] = [keys[i], obj[keys[i]]]; } return pairs; }; // Invert the keys and values of an object. The values must be serializable. _.invert = function(obj) { var result = {}; var keys = _.keys(obj); for (var i = 0, length = keys.length; i < length; i++) { result[obj[keys[i]]] = keys[i]; } return result; }; // Return a sorted list of the function names available on the object. // Aliased as `methods` _.functions = _.methods = function(obj) { var names = []; for (var key in obj) { if (_.isFunction(obj[key])) names.push(key); } return names.sort(); }; // Extend a given object with all the properties in passed-in object(s). _.extend = function(obj) { if (!_.isObject(obj)) return obj; var source, prop; for (var i = 1, length = arguments.length; i < length; i++) { source = arguments[i]; for (prop in source) { if (hasOwnProperty.call(source, prop)) { obj[prop] = source[prop]; } } } return obj; }; // Return a copy of the object only containing the whitelisted properties. _.pick = function(obj, iteratee, context) { var result = {}, key; if (obj == null) return result; if (_.isFunction(iteratee)) { iteratee = createCallback(iteratee, context); for (key in obj) { var value = obj[key]; if (iteratee(value, key, obj)) result[key] = value; } } else { var keys = concat.apply([], slice.call(arguments, 1)); obj = new Object(obj); for (var i = 0, length = keys.length; i < length; i++) { key = keys[i]; if (key in obj) result[key] = obj[key]; } } return result; }; // Return a copy of the object without the blacklisted properties. _.omit = function(obj, iteratee, context) { if (_.isFunction(iteratee)) { iteratee = _.negate(iteratee); } else { var keys = _.map(concat.apply([], slice.call(arguments, 1)), String); iteratee = function(value, key) { return !_.contains(keys, key); }; } return _.pick(obj, iteratee, context); }; // Fill in a given object with default properties. _.defaults = function(obj) { if (!_.isObject(obj)) return obj; for (var i = 1, length = arguments.length; i < length; i++) { var source = arguments[i]; for (var prop in source) { if (obj[prop] === void 0) obj[prop] = source[prop]; } } return obj; }; // Create a (shallow-cloned) duplicate of an object. _.clone = function(obj) { if (!_.isObject(obj)) return obj; return _.isArray(obj) ? obj.slice() : _.extend({}, obj); }; // Invokes interceptor with the obj, and then returns obj. // The primary purpose of this method is to "tap into" a method chain, in // order to perform operations on intermediate results within the chain. _.tap = function(obj, interceptor) { interceptor(obj); return obj; }; // Internal recursive comparison function for `isEqual`. var eq = function(a, b, aStack, bStack) { // Identical objects are equal. `0 === -0`, but they aren't identical. // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal). if (a === b) return a !== 0 || 1 / a === 1 / b; // A strict comparison is necessary because `null == undefined`. if (a == null || b == null) return a === b; // Unwrap any wrapped objects. if (a instanceof _) a = a._wrapped; if (b instanceof _) b = b._wrapped; // Compare `[[Class]]` names. var className = toString.call(a); if (className !== toString.call(b)) return false; switch (className) { // Strings, numbers, regular expressions, dates, and booleans are compared by value. case '[object RegExp]': // RegExps are coerced to strings for comparison (Note: '' + /a/i === '/a/i') case '[object String]': // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is // equivalent to `new String("5")`. return '' + a === '' + b; case '[object Number]': // `NaN`s are equivalent, but non-reflexive. // Object(NaN) is equivalent to NaN if (+a !== +a) return +b !== +b; // An `egal` comparison is performed for other numeric values. return +a === 0 ? 1 / +a === 1 / b : +a === +b; case '[object Date]': case '[object Boolean]': // Coerce dates and booleans to numeric primitive values. Dates are compared by their // millisecond representations. Note that invalid dates with millisecond representations // of `NaN` are not equivalent. return +a === +b; } if (typeof a != 'object' || typeof b != 'object') return false; // Assume equality for cyclic structures. The algorithm for detecting cyclic // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. var length = aStack.length; while (length--) { // Linear search. Performance is inversely proportional to the number of // unique nested structures. if (aStack[length] === a) return bStack[length] === b; } // Objects with different constructors are not equivalent, but `Object`s // from different frames are. var aCtor = a.constructor, bCtor = b.constructor; if ( aCtor !== bCtor && // Handle Object.create(x) cases 'constructor' in a && 'constructor' in b && !(_.isFunction(aCtor) && aCtor instanceof aCtor && _.isFunction(bCtor) && bCtor instanceof bCtor) ) { return false; } // Add the first object to the stack of traversed objects. aStack.push(a); bStack.push(b); var size, result; // Recursively compare objects and arrays. if (className === '[object Array]') { // Compare array lengths to determine if a deep comparison is necessary. size = a.length; result = size === b.length; if (result) { // Deep compare the contents, ignoring non-numeric properties. while (size--) { if (!(result = eq(a[size], b[size], aStack, bStack))) break; } } } else { // Deep compare objects. var keys = _.keys(a), key; size = keys.length; // Ensure that both objects contain the same number of properties before comparing deep equality. result = _.keys(b).length === size; if (result) { while (size--) { // Deep compare each member key = keys[size]; if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break; } } } // Remove the first object from the stack of traversed objects. aStack.pop(); bStack.pop(); return result; }; // Perform a deep comparison to check if two objects are equal. _.isEqual = function(a, b) { return eq(a, b, [], []); }; // Is a given array, string, or object empty? // An "empty" object has no enumerable own-properties. _.isEmpty = function(obj) { if (obj == null) return true; if (_.isArray(obj) || _.isString(obj) || _.isArguments(obj)) return obj.length === 0; for (var key in obj) if (_.has(obj, key)) return false; return true; }; // Is a given value a DOM element? _.isElement = function(obj) { return !!(obj && obj.nodeType === 1); }; // Is a given value an array? // Delegates to ECMA5's native Array.isArray _.isArray = nativeIsArray || function(obj) { return toString.call(obj) === '[object Array]'; }; // Is a given variable an object? _.isObject = function(obj) { var type = typeof obj; return type === 'function' || type === 'object' && !!obj; }; // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp. _.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) { _['is' + name] = function(obj) { return toString.call(obj) === '[object ' + name + ']'; }; }); // Define a fallback version of the method in browsers (ahem, IE), where // there isn't any inspectable "Arguments" type. if (!_.isArguments(arguments)) { _.isArguments = function(obj) { return _.has(obj, 'callee'); }; } // Optimize `isFunction` if appropriate. Work around an IE 11 bug. if (typeof /./ !== 'function') { _.isFunction = function(obj) { return typeof obj == 'function' || false; }; } // Is a given object a finite number? _.isFinite = function(obj) { return isFinite(obj) && !isNaN(parseFloat(obj)); }; // Is the given value `NaN`? (NaN is the only number which does not equal itself). _.isNaN = function(obj) { return _.isNumber(obj) && obj !== +obj; }; // Is a given value a boolean? _.isBoolean = function(obj) { return obj === true || obj === false || toString.call(obj) === '[object Boolean]'; }; // Is a given value equal to null? _.isNull = function(obj) { return obj === null; }; // Is a given variable undefined? _.isUndefined = function(obj) { return obj === void 0; }; // Shortcut function for checking if an object has a given property directly // on itself (in other words, not on a prototype). _.has = function(obj, key) { return obj != null && hasOwnProperty.call(obj, key); }; // Utility Functions // ----------------- // Run Underscore.js in *noConflict* mode, returning the `_` variable to its // previous owner. Returns a reference to the Underscore object. _.noConflict = function() { root._ = previousUnderscore; return this; }; // Keep the identity function around for default iteratees. _.identity = function(value) { return value; }; _.constant = function(value) { return function() { return value; }; }; _.noop = function(){}; _.property = function(key) { return function(obj) { return obj[key]; }; }; // Returns a predicate for checking whether an object has a given set of `key:value` pairs. _.matches = function(attrs) { var pairs = _.pairs(attrs), length = pairs.length; return function(obj) { if (obj == null) return !length; obj = new Object(obj); for (var i = 0; i < length; i++) { var pair = pairs[i], key = pair[0]; if (pair[1] !== obj[key] || !(key in obj)) return false; } return true; }; }; // Run a function **n** times. _.times = function(n, iteratee, context) { var accum = Array(Math.max(0, n)); iteratee = createCallback(iteratee, context, 1); for (var i = 0; i < n; i++) accum[i] = iteratee(i); return accum; }; // Return a random integer between min and max (inclusive). _.random = function(min, max) { if (max == null) { max = min; min = 0; } return min + Math.floor(Math.random() * (max - min + 1)); }; // A (possibly faster) way to get the current timestamp as an integer. _.now = Date.now || function() { return new Date().getTime(); }; // List of HTML entities for escaping. var escapeMap = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''', '`': '`' }; var unescapeMap = _.invert(escapeMap); // Functions for escaping and unescaping strings to/from HTML interpolation. var createEscaper = function(map) { var escaper = function(match) { return map[match]; }; // Regexes for identifying a key that needs to be escaped var source = '(?:' + _.keys(map).join('|') + ')'; var testRegexp = RegExp(source); var replaceRegexp = RegExp(source, 'g'); return function(string) { string = string == null ? '' : '' + string; return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string; }; }; _.escape = createEscaper(escapeMap); _.unescape = createEscaper(unescapeMap); // If the value of the named `property` is a function then invoke it with the // `object` as context; otherwise, return it. _.result = function(object, property) { if (object == null) return void 0; var value = object[property]; return _.isFunction(value) ? object[property]() : value; }; // Generate a unique integer id (unique within the entire client session). // Useful for temporary DOM ids. var idCounter = 0; _.uniqueId = function(prefix) { var id = ++idCounter + ''; return prefix ? prefix + id : id; }; // By default, Underscore uses ERB-style template delimiters, change the // following template settings to use alternative delimiters. _.templateSettings = { evaluate : /<%([\s\S]+?)%>/g, interpolate : /<%=([\s\S]+?)%>/g, escape : /<%-([\s\S]+?)%>/g }; // When customizing `templateSettings`, if you don't want to define an // interpolation, evaluation or escaping regex, we need one that is // guaranteed not to match. var noMatch = /(.)^/; // Certain characters need to be escaped so that they can be put into a // string literal. var escapes = { "'": "'", '\\': '\\', '\r': 'r', '\n': 'n', '\u2028': 'u2028', '\u2029': 'u2029' }; var escaper = /\\|'|\r|\n|\u2028|\u2029/g; var escapeChar = function(match) { return '\\' + escapes[match]; }; // JavaScript micro-templating, similar to John Resig's implementation. // Underscore templating handles arbitrary delimiters, preserves whitespace, // and correctly escapes quotes within interpolated code. // NB: `oldSettings` only exists for backwards compatibility. _.template = function(text, settings, oldSettings) { if (!settings && oldSettings) settings = oldSettings; settings = _.defaults({}, settings, _.templateSettings); // Combine delimiters into one regular expression via alternation. var matcher = RegExp([ (settings.escape || noMatch).source, (settings.interpolate || noMatch).source, (settings.evaluate || noMatch).source ].join('|') + '|$', 'g'); // Compile the template source, escaping string literals appropriately. var index = 0; var source = "__p+='"; text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { source += text.slice(index, offset).replace(escaper, escapeChar); index = offset + match.length; if (escape) { source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; } else if (interpolate) { source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; } else if (evaluate) { source += "';\n" + evaluate + "\n__p+='"; } // Adobe VMs need the match returned to produce the correct offest. return match; }); source += "';\n"; // If a variable is not specified, place data values in local scope. if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; source = "var __t,__p='',__j=Array.prototype.join," + "print=function(){__p+=__j.call(arguments,'');};\n" + source + 'return __p;\n'; try { var render = new Function(settings.variable || 'obj', '_', source); } catch (e) { e.source = source; throw e; } var template = function(data) { return render.call(this, data, _); }; // Provide the compiled source as a convenience for precompilation. var argument = settings.variable || 'obj'; template.source = 'function(' + argument + '){\n' + source + '}'; return template; }; // Add a "chain" function. Start chaining a wrapped Underscore object. _.chain = function(obj) { var instance = _(obj); instance._chain = true; return instance; }; // OOP // --------------- // If Underscore is called as a function, it returns a wrapped object that // can be used OO-style. This wrapper holds altered versions of all the // underscore functions. Wrapped objects may be chained. // Helper function to continue chaining intermediate results. var result = function(obj) { return this._chain ? _(obj).chain() : obj; }; // Add your own custom functions to the Underscore object. _.mixin = function(obj) { _.each(_.functions(obj), function(name) { var func = _[name] = obj[name]; _.prototype[name] = function() { var args = [this._wrapped]; push.apply(args, arguments); return result.call(this, func.apply(_, args)); }; }); }; // Add all of the Underscore functions to the wrapper object. _.mixin(_); // Add all mutator Array functions to the wrapper. _.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { var method = ArrayProto[name]; _.prototype[name] = function() { var obj = this._wrapped; method.apply(obj, arguments); if ((name === 'shift' || name === 'splice') && obj.length === 0) delete obj[0]; return result.call(this, obj); }; }); // Add all accessor Array functions to the wrapper. _.each(['concat', 'join', 'slice'], function(name) { var method = ArrayProto[name]; _.prototype[name] = function() { return result.call(this, method.apply(this._wrapped, arguments)); }; }); // Extracts the result from a wrapped and chained object. _.prototype.value = function() { return this._wrapped; }; // AMD registration happens at the end for compatibility with AMD loaders // that may not enforce next-turn semantics on modules. Even though general // practice for AMD registration is to be anonymous, underscore registers // as a named module because, like jQuery, it is a base library that is // popular enough to be bundled in a third party lib, but not be part of // an AMD load request. Those cases could generate an error when an // anonymous define() is called outside of a loader request. if (typeof define === 'function' && define.amd) { define('underscore', [], function() { return _; }); } }.call(this)); pyalsaaudio-0.8.4/doc/html/_static/default.css0000644000175000001470000000771013054110215021070 0ustar larsdev00000000000000/* * default.css_t * ~~~~~~~~~~~~~ * * Sphinx stylesheet -- default theme. * * :copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS. * :license: BSD, see LICENSE for details. * */ @import url("basic.css"); /* -- page layout ----------------------------------------------------------- */ body { font-family: sans-serif; font-size: 100%; background-color: #11303d; color: #000; margin: 0; padding: 0; } div.document { background-color: #1c4e63; } div.documentwrapper { float: left; width: 100%; } div.bodywrapper { margin: 0 0 0 230px; } div.body { background-color: #ffffff; color: #000000; padding: 0 20px 30px 20px; } div.footer { color: #ffffff; width: 100%; padding: 9px 0 9px 0; text-align: center; font-size: 75%; } div.footer a { color: #ffffff; text-decoration: underline; } div.related { background-color: #133f52; line-height: 30px; color: #ffffff; } div.related a { color: #ffffff; } div.sphinxsidebar { } div.sphinxsidebar h3 { font-family: 'Trebuchet MS', sans-serif; color: #ffffff; font-size: 1.4em; font-weight: normal; margin: 0; padding: 0; } div.sphinxsidebar h3 a { color: #ffffff; } div.sphinxsidebar h4 { font-family: 'Trebuchet MS', sans-serif; color: #ffffff; font-size: 1.3em; font-weight: normal; margin: 5px 0 0 0; padding: 0; } div.sphinxsidebar p { color: #ffffff; } div.sphinxsidebar p.topless { margin: 5px 10px 10px 10px; } div.sphinxsidebar ul { margin: 10px; padding: 0; color: #ffffff; } div.sphinxsidebar a { color: #98dbcc; } div.sphinxsidebar input { border: 1px solid #98dbcc; font-family: sans-serif; font-size: 1em; } /* -- hyperlink styles ------------------------------------------------------ */ a { color: #355f7c; text-decoration: none; } a:visited { color: #355f7c; text-decoration: none; } a:hover { text-decoration: underline; } /* -- body styles ----------------------------------------------------------- */ div.body h1, div.body h2, div.body h3, div.body h4, div.body h5, div.body h6 { font-family: 'Trebuchet MS', sans-serif; background-color: #f2f2f2; font-weight: normal; color: #20435c; border-bottom: 1px solid #ccc; margin: 20px -20px 10px -20px; padding: 3px 0 3px 10px; } div.body h1 { margin-top: 0; font-size: 200%; } div.body h2 { font-size: 160%; } div.body h3 { font-size: 140%; } div.body h4 { font-size: 120%; } div.body h5 { font-size: 110%; } div.body h6 { font-size: 100%; } a.headerlink { color: #c60f0f; font-size: 0.8em; padding: 0 4px 0 4px; text-decoration: none; } a.headerlink:hover { background-color: #c60f0f; color: white; } div.body p, div.body dd, div.body li { text-align: justify; line-height: 130%; } div.admonition p.admonition-title + p { display: inline; } div.admonition p { margin-bottom: 5px; } div.admonition pre { margin-bottom: 5px; } div.admonition ul, div.admonition ol { margin-bottom: 5px; } div.note { background-color: #eee; border: 1px solid #ccc; } div.seealso { background-color: #ffc; border: 1px solid #ff6; } div.topic { background-color: #eee; } div.warning { background-color: #ffe4e4; border: 1px solid #f66; } p.admonition-title { display: inline; } p.admonition-title:after { content: ":"; } pre { padding: 5px; background-color: #eeffcc; color: #333333; line-height: 120%; border: 1px solid #ac9; border-left: none; border-right: none; } tt { background-color: #ecf0f3; padding: 0 1px 0 1px; font-size: 0.95em; } th { background-color: #ede; } .warning tt { background: #efc2c2; } .note tt { background: #d6d6d6; } .viewcode-back { font-family: sans-serif; } div.viewcode-block:target { background-color: #f4debf; border-top: 1px solid #ac9; border-bottom: 1px solid #ac9; }pyalsaaudio-0.8.4/doc/html/_static/comment.png0000644000175000001470000000656512062620216021116 0ustar larsdev00000000000000PNG  IHDRa OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3-bKGD pHYs  tIME 1;VIDAT8ukU?sg4h`G1 RQܸp%Bn"bЍXJ .4V iZ##T;m!4bP~7r>ιbwc;m;oӍAΆ ζZ^/|s{;yR=9(rtVoG1w#_ө{*E&!(LVuoᲵ‘D PG4 :&~*ݳreu: S-,U^E&JY[P!RB ŖޞʖR@_ȐdBfNvHf"2T]R j'B1ddAak/DIJD D2H&L`&L $Ex,6|~_\P $MH`I=@Z||ttvgcЕWTZ'3rje"ܵx9W> mb|byfFRx{w%DZC$wdցHmWnta(M<~;9]C/_;Տ#}o`zSڷ_>:;x컓?yݩ|}~wam-/7=0S5RP"*֯ IENDB`pyalsaaudio-0.8.4/doc/html/_static/websupport.js0000644000175000001470000006123612331063777021526 0ustar larsdev00000000000000/* * websupport.js * ~~~~~~~~~~~~~ * * sphinx.websupport utilties for all documentation. * * :copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS. * :license: BSD, see LICENSE for details. * */ (function($) { $.fn.autogrow = function() { return this.each(function() { var textarea = this; $.fn.autogrow.resize(textarea); $(textarea) .focus(function() { textarea.interval = setInterval(function() { $.fn.autogrow.resize(textarea); }, 500); }) .blur(function() { clearInterval(textarea.interval); }); }); }; $.fn.autogrow.resize = function(textarea) { var lineHeight = parseInt($(textarea).css('line-height'), 10); var lines = textarea.value.split('\n'); var columns = textarea.cols; var lineCount = 0; $.each(lines, function() { lineCount += Math.ceil(this.length / columns) || 1; }); var height = lineHeight * (lineCount + 1); $(textarea).css('height', height); }; })(jQuery); (function($) { var comp, by; function init() { initEvents(); initComparator(); } function initEvents() { $('a.comment-close').live("click", function(event) { event.preventDefault(); hide($(this).attr('id').substring(2)); }); $('a.vote').live("click", function(event) { event.preventDefault(); handleVote($(this)); }); $('a.reply').live("click", function(event) { event.preventDefault(); openReply($(this).attr('id').substring(2)); }); $('a.close-reply').live("click", function(event) { event.preventDefault(); closeReply($(this).attr('id').substring(2)); }); $('a.sort-option').live("click", function(event) { event.preventDefault(); handleReSort($(this)); }); $('a.show-proposal').live("click", function(event) { event.preventDefault(); showProposal($(this).attr('id').substring(2)); }); $('a.hide-proposal').live("click", function(event) { event.preventDefault(); hideProposal($(this).attr('id').substring(2)); }); $('a.show-propose-change').live("click", function(event) { event.preventDefault(); showProposeChange($(this).attr('id').substring(2)); }); $('a.hide-propose-change').live("click", function(event) { event.preventDefault(); hideProposeChange($(this).attr('id').substring(2)); }); $('a.accept-comment').live("click", function(event) { event.preventDefault(); acceptComment($(this).attr('id').substring(2)); }); $('a.delete-comment').live("click", function(event) { event.preventDefault(); deleteComment($(this).attr('id').substring(2)); }); $('a.comment-markup').live("click", function(event) { event.preventDefault(); toggleCommentMarkupBox($(this).attr('id').substring(2)); }); } /** * Set comp, which is a comparator function used for sorting and * inserting comments into the list. */ function setComparator() { // If the first three letters are "asc", sort in ascending order // and remove the prefix. if (by.substring(0,3) == 'asc') { var i = by.substring(3); comp = function(a, b) { return a[i] - b[i]; }; } else { // Otherwise sort in descending order. comp = function(a, b) { return b[by] - a[by]; }; } // Reset link styles and format the selected sort option. $('a.sel').attr('href', '#').removeClass('sel'); $('a.by' + by).removeAttr('href').addClass('sel'); } /** * Create a comp function. If the user has preferences stored in * the sortBy cookie, use those, otherwise use the default. */ function initComparator() { by = 'rating'; // Default to sort by rating. // If the sortBy cookie is set, use that instead. if (document.cookie.length > 0) { var start = document.cookie.indexOf('sortBy='); if (start != -1) { start = start + 7; var end = document.cookie.indexOf(";", start); if (end == -1) { end = document.cookie.length; by = unescape(document.cookie.substring(start, end)); } } } setComparator(); } /** * Show a comment div. */ function show(id) { $('#ao' + id).hide(); $('#ah' + id).show(); var context = $.extend({id: id}, opts); var popup = $(renderTemplate(popupTemplate, context)).hide(); popup.find('textarea[name="proposal"]').hide(); popup.find('a.by' + by).addClass('sel'); var form = popup.find('#cf' + id); form.submit(function(event) { event.preventDefault(); addComment(form); }); $('#s' + id).after(popup); popup.slideDown('fast', function() { getComments(id); }); } /** * Hide a comment div. */ function hide(id) { $('#ah' + id).hide(); $('#ao' + id).show(); var div = $('#sc' + id); div.slideUp('fast', function() { div.remove(); }); } /** * Perform an ajax request to get comments for a node * and insert the comments into the comments tree. */ function getComments(id) { $.ajax({ type: 'GET', url: opts.getCommentsURL, data: {node: id}, success: function(data, textStatus, request) { var ul = $('#cl' + id); var speed = 100; $('#cf' + id) .find('textarea[name="proposal"]') .data('source', data.source); if (data.comments.length === 0) { ul.html('
  • No comments yet.
  • '); ul.data('empty', true); } else { // If there are comments, sort them and put them in the list. var comments = sortComments(data.comments); speed = data.comments.length * 100; appendComments(comments, ul); ul.data('empty', false); } $('#cn' + id).slideUp(speed + 200); ul.slideDown(speed); }, error: function(request, textStatus, error) { showError('Oops, there was a problem retrieving the comments.'); }, dataType: 'json' }); } /** * Add a comment via ajax and insert the comment into the comment tree. */ function addComment(form) { var node_id = form.find('input[name="node"]').val(); var parent_id = form.find('input[name="parent"]').val(); var text = form.find('textarea[name="comment"]').val(); var proposal = form.find('textarea[name="proposal"]').val(); if (text == '') { showError('Please enter a comment.'); return; } // Disable the form that is being submitted. form.find('textarea,input').attr('disabled', 'disabled'); // Send the comment to the server. $.ajax({ type: "POST", url: opts.addCommentURL, dataType: 'json', data: { node: node_id, parent: parent_id, text: text, proposal: proposal }, success: function(data, textStatus, error) { // Reset the form. if (node_id) { hideProposeChange(node_id); } form.find('textarea') .val('') .add(form.find('input')) .removeAttr('disabled'); var ul = $('#cl' + (node_id || parent_id)); if (ul.data('empty')) { $(ul).empty(); ul.data('empty', false); } insertComment(data.comment); var ao = $('#ao' + node_id); ao.find('img').attr({'src': opts.commentBrightImage}); if (node_id) { // if this was a "root" comment, remove the commenting box // (the user can get it back by reopening the comment popup) $('#ca' + node_id).slideUp(); } }, error: function(request, textStatus, error) { form.find('textarea,input').removeAttr('disabled'); showError('Oops, there was a problem adding the comment.'); } }); } /** * Recursively append comments to the main comment list and children * lists, creating the comment tree. */ function appendComments(comments, ul) { $.each(comments, function() { var div = createCommentDiv(this); ul.append($(document.createElement('li')).html(div)); appendComments(this.children, div.find('ul.comment-children')); // To avoid stagnating data, don't store the comments children in data. this.children = null; div.data('comment', this); }); } /** * After adding a new comment, it must be inserted in the correct * location in the comment tree. */ function insertComment(comment) { var div = createCommentDiv(comment); // To avoid stagnating data, don't store the comments children in data. comment.children = null; div.data('comment', comment); var ul = $('#cl' + (comment.node || comment.parent)); var siblings = getChildren(ul); var li = $(document.createElement('li')); li.hide(); // Determine where in the parents children list to insert this comment. for(i=0; i < siblings.length; i++) { if (comp(comment, siblings[i]) <= 0) { $('#cd' + siblings[i].id) .parent() .before(li.html(div)); li.slideDown('fast'); return; } } // If we get here, this comment rates lower than all the others, // or it is the only comment in the list. ul.append(li.html(div)); li.slideDown('fast'); } function acceptComment(id) { $.ajax({ type: 'POST', url: opts.acceptCommentURL, data: {id: id}, success: function(data, textStatus, request) { $('#cm' + id).fadeOut('fast'); $('#cd' + id).removeClass('moderate'); }, error: function(request, textStatus, error) { showError('Oops, there was a problem accepting the comment.'); } }); } function deleteComment(id) { $.ajax({ type: 'POST', url: opts.deleteCommentURL, data: {id: id}, success: function(data, textStatus, request) { var div = $('#cd' + id); if (data == 'delete') { // Moderator mode: remove the comment and all children immediately div.slideUp('fast', function() { div.remove(); }); return; } // User mode: only mark the comment as deleted div .find('span.user-id:first') .text('[deleted]').end() .find('div.comment-text:first') .text('[deleted]').end() .find('#cm' + id + ', #dc' + id + ', #ac' + id + ', #rc' + id + ', #sp' + id + ', #hp' + id + ', #cr' + id + ', #rl' + id) .remove(); var comment = div.data('comment'); comment.username = '[deleted]'; comment.text = '[deleted]'; div.data('comment', comment); }, error: function(request, textStatus, error) { showError('Oops, there was a problem deleting the comment.'); } }); } function showProposal(id) { $('#sp' + id).hide(); $('#hp' + id).show(); $('#pr' + id).slideDown('fast'); } function hideProposal(id) { $('#hp' + id).hide(); $('#sp' + id).show(); $('#pr' + id).slideUp('fast'); } function showProposeChange(id) { $('#pc' + id).hide(); $('#hc' + id).show(); var textarea = $('#pt' + id); textarea.val(textarea.data('source')); $.fn.autogrow.resize(textarea[0]); textarea.slideDown('fast'); } function hideProposeChange(id) { $('#hc' + id).hide(); $('#pc' + id).show(); var textarea = $('#pt' + id); textarea.val('').removeAttr('disabled'); textarea.slideUp('fast'); } function toggleCommentMarkupBox(id) { $('#mb' + id).toggle(); } /** Handle when the user clicks on a sort by link. */ function handleReSort(link) { var classes = link.attr('class').split(/\s+/); for (var i=0; iThank you! Your comment will show up ' + 'once it is has been approved by a moderator.'); } // Prettify the comment rating. comment.pretty_rating = comment.rating + ' point' + (comment.rating == 1 ? '' : 's'); // Make a class (for displaying not yet moderated comments differently) comment.css_class = comment.displayed ? '' : ' moderate'; // Create a div for this comment. var context = $.extend({}, opts, comment); var div = $(renderTemplate(commentTemplate, context)); // If the user has voted on this comment, highlight the correct arrow. if (comment.vote) { var direction = (comment.vote == 1) ? 'u' : 'd'; div.find('#' + direction + 'v' + comment.id).hide(); div.find('#' + direction + 'u' + comment.id).show(); } if (opts.moderator || comment.text != '[deleted]') { div.find('a.reply').show(); if (comment.proposal_diff) div.find('#sp' + comment.id).show(); if (opts.moderator && !comment.displayed) div.find('#cm' + comment.id).show(); if (opts.moderator || (opts.username == comment.username)) div.find('#dc' + comment.id).show(); } return div; } /** * A simple template renderer. Placeholders such as <%id%> are replaced * by context['id'] with items being escaped. Placeholders such as <#id#> * are not escaped. */ function renderTemplate(template, context) { var esc = $(document.createElement('div')); function handle(ph, escape) { var cur = context; $.each(ph.split('.'), function() { cur = cur[this]; }); return escape ? esc.text(cur || "").html() : cur; } return template.replace(/<([%#])([\w\.]*)\1>/g, function() { return handle(arguments[2], arguments[1] == '%' ? true : false); }); } /** Flash an error message briefly. */ function showError(message) { $(document.createElement('div')).attr({'class': 'popup-error'}) .append($(document.createElement('div')) .attr({'class': 'error-message'}).text(message)) .appendTo('body') .fadeIn("slow") .delay(2000) .fadeOut("slow"); } /** Add a link the user uses to open the comments popup. */ $.fn.comment = function() { return this.each(function() { var id = $(this).attr('id').substring(1); var count = COMMENT_METADATA[id]; var title = count + ' comment' + (count == 1 ? '' : 's'); var image = count > 0 ? opts.commentBrightImage : opts.commentImage; var addcls = count == 0 ? ' nocomment' : ''; $(this) .append( $(document.createElement('a')).attr({ href: '#', 'class': 'sphinx-comment-open' + addcls, id: 'ao' + id }) .append($(document.createElement('img')).attr({ src: image, alt: 'comment', title: title })) .click(function(event) { event.preventDefault(); show($(this).attr('id').substring(2)); }) ) .append( $(document.createElement('a')).attr({ href: '#', 'class': 'sphinx-comment-close hidden', id: 'ah' + id }) .append($(document.createElement('img')).attr({ src: opts.closeCommentImage, alt: 'close', title: 'close' })) .click(function(event) { event.preventDefault(); hide($(this).attr('id').substring(2)); }) ); }); }; var opts = { processVoteURL: '/_process_vote', addCommentURL: '/_add_comment', getCommentsURL: '/_get_comments', acceptCommentURL: '/_accept_comment', deleteCommentURL: '/_delete_comment', commentImage: '/static/_static/comment.png', closeCommentImage: '/static/_static/comment-close.png', loadingImage: '/static/_static/ajax-loader.gif', commentBrightImage: '/static/_static/comment-bright.png', upArrow: '/static/_static/up.png', downArrow: '/static/_static/down.png', upArrowPressed: '/static/_static/up-pressed.png', downArrowPressed: '/static/_static/down-pressed.png', voting: false, moderator: false }; if (typeof COMMENT_OPTIONS != "undefined") { opts = jQuery.extend(opts, COMMENT_OPTIONS); } var popupTemplate = '\
    \

    \ Sort by:\ best rated\ newest\ oldest\

    \
    Comments
    \
    \ loading comments...
    \
      \
      \

      Add a comment\ (markup):

      \
      \ reStructured text markup: *emph*, **strong**, \ ``code``, \ code blocks: :: and an indented block after blank line
      \
      \ \

      \ \ Propose a change ▹\ \ \ Propose a change ▿\ \

      \ \ \ \ \
      \
      \
      '; var commentTemplate = '\
      \
      \
      \ \ \ \ \ \ \
      \
      \ \ \ \ \ \ \
      \
      \
      \

      \ <%username%>\ <%pretty_rating%>\ <%time.delta%>\

      \
      <#text#>
      \

      \ \ reply ▿\ proposal ▹\ proposal ▿\ \ \

      \
      \
      <#proposal_diff#>\
              
      \
        \
        \
        \
        \ '; var replyTemplate = '\
      • \
        \
        \ \ \ \ \ \
        \
        \
      • '; $(document).ready(function() { init(); }); })(jQuery); $(document).ready(function() { // add comment anchors for all paragraphs that are commentable $('.sphinx-has-comment').comment(); // highlight search words in search results $("div.context").each(function() { var params = $.getQueryParameters(); var terms = (params.q) ? params.q[0].split(/\s+/) : []; var result = $(this); $.each(terms, function() { result.highlightText(this.toLowerCase(), 'highlighted'); }); }); // directly open comment window if requested var anchor = document.location.hash; if (anchor.substring(0, 9) == '#comment-') { $('#ao' + anchor.substring(9)).click(); document.location.hash = '#s' + anchor.substring(9); } }); pyalsaaudio-0.8.4/doc/html/_static/ajax-loader.gif0000644000175000001470000000124112062620216021606 0ustar larsdev00000000000000GIF89aU|NU|l!Created with ajaxload.info! ! NETSCAPE2.0,30Ikc:Nf E1º.`q-[9ݦ9 JkH! ,4N!  DqBQT`1 `LE[|ua C%$*! ,62#+AȐ̔V/cNIBap ̳ƨ+Y2d! ,3b%+2V_ ! 1DaFbR]=08,Ȥr9L! ,2r'+JdL &v`\bThYB)@<&,ȤR! ,3 9tڞ0!.BW1  sa50 m)J! ,2 ٜU]qp`a4AF0` @1Α! ,20IeBԜ) q10ʰPaVڥ ub[;pyalsaaudio-0.8.4/doc/html/_static/up-pressed.png0000644000175000001470000000056412062620216021534 0ustar larsdev00000000000000PNG  IHDRasRGBbKGDC pHYs B(xtIME ,ZeIDAT8͓jA*WKk-,By@- و/`cXYh!6jf GrOlXvvfk2!p!GOOԲ &zf 6|M~%`]* ΛM]K ZĆ1Er%ȶcm1`0 var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 var s_v = "^(" + C + ")?" + v; // vowel in stem this.stemWord = function (w) { var stem; var suffix; var firstch; var origword = w; if (w.length < 3) return w; var re; var re2; var re3; var re4; firstch = w.substr(0,1); if (firstch == "y") w = firstch.toUpperCase() + w.substr(1); // Step 1a re = /^(.+?)(ss|i)es$/; re2 = /^(.+?)([^s])s$/; if (re.test(w)) w = w.replace(re,"$1$2"); else if (re2.test(w)) w = w.replace(re2,"$1$2"); // Step 1b re = /^(.+?)eed$/; re2 = /^(.+?)(ed|ing)$/; if (re.test(w)) { var fp = re.exec(w); re = new RegExp(mgr0); if (re.test(fp[1])) { re = /.$/; w = w.replace(re,""); } } else if (re2.test(w)) { var fp = re2.exec(w); stem = fp[1]; re2 = new RegExp(s_v); if (re2.test(stem)) { w = stem; re2 = /(at|bl|iz)$/; re3 = new RegExp("([^aeiouylsz])\\1$"); re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); if (re2.test(w)) w = w + "e"; else if (re3.test(w)) { re = /.$/; w = w.replace(re,""); } else if (re4.test(w)) w = w + "e"; } } // Step 1c re = /^(.+?)y$/; if (re.test(w)) { var fp = re.exec(w); stem = fp[1]; re = new RegExp(s_v); if (re.test(stem)) w = stem + "i"; } // Step 2 re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; if (re.test(w)) { var fp = re.exec(w); stem = fp[1]; suffix = fp[2]; re = new RegExp(mgr0); if (re.test(stem)) w = stem + step2list[suffix]; } // Step 3 re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; if (re.test(w)) { var fp = re.exec(w); stem = fp[1]; suffix = fp[2]; re = new RegExp(mgr0); if (re.test(stem)) w = stem + step3list[suffix]; } // Step 4 re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; re2 = /^(.+?)(s|t)(ion)$/; if (re.test(w)) { var fp = re.exec(w); stem = fp[1]; re = new RegExp(mgr1); if (re.test(stem)) w = stem; } else if (re2.test(w)) { var fp = re2.exec(w); stem = fp[1] + fp[2]; re2 = new RegExp(mgr1); if (re2.test(stem)) w = stem; } // Step 5 re = /^(.+?)e$/; if (re.test(w)) { var fp = re.exec(w); stem = fp[1]; re = new RegExp(mgr1); re2 = new RegExp(meq1); re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) w = stem; } re = /ll$/; re2 = new RegExp(mgr1); if (re.test(w) && re2.test(w)) { re = /.$/; w = w.replace(re,""); } // and turn initial Y back to y if (firstch == "y") w = firstch.toLowerCase() + w.substr(1); return w; } } /** * Simple result scoring code. */ var Scorer = { // Implement the following function to further tweak the score for each result // The function takes a result array [filename, title, anchor, descr, score] // and returns the new score. /* score: function(result) { return result[4]; }, */ // query matches the full name of an object objNameMatch: 11, // or matches in the last dotted part of the object name objPartialMatch: 6, // Additive scores depending on the priority of the object objPrio: {0: 15, // used to be importantResults 1: 5, // used to be objectResults 2: -5}, // used to be unimportantResults // Used when the priority is not in the mapping. objPrioDefault: 0, // query found in title title: 15, // query found in terms term: 5 }; /** * Search Module */ var Search = { _index : null, _queued_query : null, _pulse_status : -1, init : function() { var params = $.getQueryParameters(); if (params.q) { var query = params.q[0]; $('input[name="q"]')[0].value = query; this.performSearch(query); } }, loadIndex : function(url) { $.ajax({type: "GET", url: url, data: null, dataType: "script", cache: true, complete: function(jqxhr, textstatus) { if (textstatus != "success") { document.getElementById("searchindexloader").src = url; } }}); }, setIndex : function(index) { var q; this._index = index; if ((q = this._queued_query) !== null) { this._queued_query = null; Search.query(q); } }, hasIndex : function() { return this._index !== null; }, deferQuery : function(query) { this._queued_query = query; }, stopPulse : function() { this._pulse_status = 0; }, startPulse : function() { if (this._pulse_status >= 0) return; function pulse() { var i; Search._pulse_status = (Search._pulse_status + 1) % 4; var dotString = ''; for (i = 0; i < Search._pulse_status; i++) dotString += '.'; Search.dots.text(dotString); if (Search._pulse_status > -1) window.setTimeout(pulse, 500); } pulse(); }, /** * perform a search for something (or wait until index is loaded) */ performSearch : function(query) { // create the required interface elements this.out = $('#search-results'); this.title = $('

        ' + _('Searching') + '

        ').appendTo(this.out); this.dots = $('').appendTo(this.title); this.status = $('

        ').appendTo(this.out); this.output = $('