Anonymous avatar Anonymous committed 6d536aa

Added openal Sources and Listener support.
Buffers, Sources and Listener need to know about their context.

Comments (0)

Files changed (11)

 * freetype: description/markup language for rendering text (like e.g. pango
    has)?
 
+* openal: check integrity and references for sources, buffers and listeners.
+
 Things to ADD:
 ==============
 * (re)add tests

doc/create_cref.py

     for l in lines:
         l = l.strip ().replace ("::", "")
         if l.startswith ("|"):
-            # Peserve spacings.
+            # Preserve spacings.
             l = l.replace (":const:", "       ")
             l = l.replace (":class:", "       ")
             l = l.replace (":meth:", "      ")
     Module ("openal.base",
         sources = [ "src/openal/openalmod.c",
                     "src/openal/buffers.c",
+                    "src/openal/sources.c",
+                    "src/openal/listener.c",
                     "src/openal/context.c",
                     "src/openal/device.c" ],
         depends = [ 'openal' ],

src/openal/buffers.c

 */
 #define PYGAME_OPENALBUFFERS_INTERNAL
 
-#include "pgbase.h"
 #include "openalmod.h"
 #include "pgopenal.h"
 
 static void _buffers_dealloc (PyBuffers *self);
 static PyObject* _buffers_repr (PyObject *self);
 
-typedef enum
-{
-    INVALID,     /* invalid type */
-    INT,         /* 'i'  */
-    FLOAT,       /* 'f'  */
-    INT3,        /* 'i3' */
-    FLOAT3,      /* 'f3' */ 
-    INTARRAY,    /* 'ia' */
-    FLOATARRAY   /* 'fa' */
-} PropType;
-static PropType _getproptype_from_str (char *name);
-
 static PyObject* _buffers_setprop (PyObject *self, PyObject *args);
 static PyObject* _buffers_getprop (PyObject *self, PyObject *args);
+static PyObject* _buffers_bufferdata (PyObject *self, PyObject *args);
+
+static PyObject* _buffers_getcount (PyObject* self, void *closure);
+static PyObject* _buffers_getbuffers (PyObject* self, void *closure);
 
 /**
  */
 static PyMethodDef _buffers_methods[] = {
     { "set_prop", _buffers_setprop, METH_VARARGS, NULL },
     { "get_prop", _buffers_getprop, METH_VARARGS, NULL },
+    { "buffer_data", _buffers_bufferdata, METH_VARARGS, NULL },
     { NULL, NULL, 0, NULL }
 };
 
 /**
  */
 static PyGetSetDef _buffers_getsets[] = {
+    { "count", _buffers_getcount, NULL, NULL, NULL },
+    { "buffers", _buffers_getbuffers, NULL, NULL, NULL },
     { NULL, NULL, NULL, NULL, NULL }
 };
 
 static void
 _buffers_dealloc (PyBuffers *self)
 {
-    alDeleteBuffers (self->count, self->buffers);
+    int switched = 0;
+
+    if (self->buffers != NULL)
+    {
+        ALCcontext *ctxt = alcGetCurrentContext ();
+        if (ctxt != PyContext_AsContext (self->context))
+        {
+            /* Switch the current context to release the correct buffers. */
+            alcMakeContextCurrent (PyContext_AsContext (self->context));
+            switched = 1;
+        }
+        alDeleteBuffers (self->count, self->buffers);
+        if (switched)
+            alcMakeContextCurrent (ctxt);
+    }
     self->buffers = NULL;
+    self->count = 0;
+
     ((PyObject*)self)->ob_type->tp_free ((PyObject *) self);
 }
 
 }
 
 /* Buffers getters/setters */
+static PyObject*
+_buffers_getcount (PyObject* self, void *closure)
+{
+    return PyInt_FromLong ((long)(((PyBuffers*)self)->count));
+}
+
+static PyObject*
+_buffers_getbuffers (PyObject* self, void *closure)
+{
+    PyBuffers *buffers = (PyBuffers*)self;
+    PyObject *tuple, *item;
+    ALsizei i;
+
+    tuple = PyTuple_New ((Py_ssize_t)(buffers->count));
+    if (!tuple)
+        return NULL;
+
+    for (i = 0; i < buffers->count; i++)
+    {
+        item = PyInt_FromLong ((long)buffers->buffers[i]);
+        if (!item)
+        {
+            Py_DECREF (tuple);
+            return NULL;
+        }
+        PyTuple_SET_ITEM (tuple, (Py_ssize_t)i, item);
+    }
+    return tuple;
+}
+
 
 /* Buffers methods */
-static PropType
-_getproptype_from_str (char *name)
-{
-    size_t len;
-    if (!name)
-        return INVALID;
-
-    len = strlen (name);
-
-    if (len == 1)
-    {
-        if (name[0] == 'i')
-            return INT;
-        else if (name[0] == 'f')
-            return FLOAT;
-        return INVALID;
-    }
-
-    if (len == 2)
-    {
-        if (name[0] == 'i')
-        {
-            if (name[1] == 'a')
-                return INTARRAY;
-            if (name[1] == '3')
-                return INT3;
-        }
-        else if (name[0] == 'f')
-        {
-            if (name[1] == 'a')
-                return FLOATARRAY;
-            if (name[1] == '3')
-                return FLOAT3;
-        }
-    }
-    return INVALID;
-}
-
-
 static PyObject*
 _buffers_setprop (PyObject *self, PyObject *args)
 {
     char *type;
     PropType ptype = INVALID;
 
-    CLEAR_ERROR_STATE ();
-    
+    if (!CONTEXT_IS_CURRENT (((PyBuffers*)self)->context))
+    {
+        PyErr_SetString (PyExc_PyGameError, "buffer context is not current");
+        return NULL;
+    }
+
     if (!PyArg_ParseTuple (args, "llO|s:set_prop", &bufnum, &param, &values,
             &type))
         return NULL;
 
     if (type)
     {
-        ptype = _getproptype_from_str (type);
+        ptype = GetPropTypeFromStr (type);
         if (ptype == INVALID)
         {
             PyErr_SetString (PyExc_RuntimeError,
     int size = 0, cnt;
     PropType ptype = INVALID;
 
+    if (!CONTEXT_IS_CURRENT (((PyBuffers*)self)->context))
+    {
+        PyErr_SetString (PyExc_PyGameError, "buffer context is not current");
+        return NULL;
+    }
+
     if (!PyArg_ParseTuple (args, "lls", &bufnum, &param, &type))
     {
         PyErr_Clear ();
         return NULL;
     }
 
-    ptype = _getproptype_from_str (type);
+    ptype = GetPropTypeFromStr (type);
     CLEAR_ERROR_STATE ();
     switch (ptype)
     {
         for (cnt = 0; cnt < size; cnt++)
         {
             item = PyLong_FromLong ((long)val[cnt]);
-            if (!item || PyTuple_SET_ITEM (tuple, (Py_ssize_t) cnt, item) != 0)
+            if (!item)
             {
                 PyMem_Free (val);
                 Py_DECREF (tuple);
                 return NULL;
             }
+            PyTuple_SET_ITEM (tuple, (Py_ssize_t) cnt, item);
         }
         return tuple;
     }
             if (!item || PyTuple_SET_ITEM (tuple, (Py_ssize_t) cnt, item) != 0)
             {
                 PyMem_Free (val);
+                Py_XDECREF (item);
                 Py_DECREF (tuple);
                 return NULL;
             }
         PyErr_SetString (PyExc_ValueError, "invalid type specifier");
         return NULL;
     }
+}
 
+static PyObject*
+_buffers_bufferdata (PyObject *self, PyObject *args)
+{
+    long bufnum;
+    ALenum format;
+    const char *buf;
+    Py_ssize_t len;
+    ALsizei freq;
+
+    if (!CONTEXT_IS_CURRENT (((PyBuffers*)self)->context))
+    {
+        PyErr_SetString (PyExc_PyGameError, "buffer context is not current");
+        return NULL;
+    }
+
+    if (!PyArg_ParseTuple (args, "lls#l", &bufnum, &format, &buf, &len, &freq))
+        return NULL;
+    
+    /* TODO */
+    if (bufnum < 0 || bufnum > ((PyBuffers*)self)->count)
+    {
+        PyErr_SetString (PyExc_ValueError, "buffer index out of range");
+        return NULL;
+    }
+    CLEAR_ERROR_STATE ();
+    alBufferData ((ALuint) bufnum, format, (const void*) buf, (ALsizei)len,
+        freq);
     if (SetALErrorException (alGetError ()))
         return NULL;
-    /* TODO */
-    return NULL;
+    Py_RETURN_NONE;
 }
 
 /* C API */
 PyObject*
-PyBuffers_New (ALsizei count)
+PyBuffers_New (PyObject *context, ALsizei count)
 {
     ALuint *buf;
     PyObject *buffers;
 
+    if (!context || !PyContext_Check (context))
+    {
+        PyErr_SetString (PyExc_TypeError, "context is not a valid Context");
+        return NULL;
+    }
+
     if (count < 1)
     {
         PyErr_SetString (PyExc_ValueError, "cannot create less than 1 buffer");
     buffers = PyBuffers_Type.tp_new (&PyBuffers_Type, NULL, NULL);
     if (!buffers)
         return NULL;
+    ((PyBuffers*)buffers)->context = context;
     ((PyBuffers*)buffers)->count = count;
     ((PyBuffers*)buffers)->buffers = NULL;
     
         Py_DECREF (buffers);
         return NULL;
     }
+    CLEAR_ERROR_STATE ();
+    alGenBuffers (count, buf);
+    if (SetALErrorException (alGetError ()))
+    {
+        Py_DECREF (buffers);
+        return NULL;
+    }
+
     ((PyBuffers*)buffers)->buffers = buf;
+
     
     return buffers;
 }

src/openal/context.c

 #include "openalmod.h"
 #include "pgopenal.h"
 
-#define CONTEXT_IS_CURRENT(x) \
-    (alcGetCurrentContext () == PyContext_AsContext (x))
-
-#define ASSERT_CONTEXT_IS_CURRENT(x,ret)                                \
-    if (alcGetCurrentContext () != PyContext_AsContext (x))             \
-    {                                                                   \
-        PyErr_SetString (PyExc_PyGameError, "Context is not current");  \
-        return (ret);                                                   \
-    }
-
 static int _context_init (PyObject *self, PyObject *args, PyObject *kwds);
 static void _context_dealloc (PyContext *self);
 static PyObject* _context_repr (PyObject *self);
 
 static PyObject* _context_createbuffers (PyObject *self, PyObject *args);
+static PyObject* _context_createsources (PyObject *self, PyObject *args);
 static PyObject* _context_makecurrent (PyObject *self);
 static PyObject* _context_suspend (PyObject *self);
 static PyObject* _context_process (PyObject *self);
 static PyObject* _context_isenabled (PyObject *self, PyObject *args);
 
 static PyObject* _context_iscurrent (PyObject* self, void *closure);
+static PyObject* _context_getlistener (PyObject* self, void *closure);
 static PyObject* _context_getdevice (PyObject* self, void *closure);
 
 /**
 static PyMethodDef _context_methods[] = {
     { "make_current", (PyCFunction)_context_makecurrent, METH_NOARGS, NULL },
     { "create_buffers", (PyCFunction) _context_createbuffers, METH_O, NULL },
+    { "create_sources", (PyCFunction) _context_createsources, METH_O, NULL },
     { "suspend", (PyCFunction) _context_suspend, METH_NOARGS, NULL },
     { "process", (PyCFunction) _context_process, METH_NOARGS, NULL },
     { "enable", (PyCFunction) _context_enable, METH_O, "" },
 static PyGetSetDef _context_getsets[] = {
     { "is_current", _context_iscurrent, NULL, NULL, NULL },
     { "device", _context_getdevice, NULL, NULL, NULL },
+    { "listener", _context_getlistener, NULL, NULL, NULL },
     { NULL, NULL, NULL, NULL, NULL }
 };
 
         alcDestroyContext (self->context);
     }
     Py_XDECREF (self->device);
+    Py_XDECREF (self->listener);
+    self->listener = NULL;
     self->device = NULL;
     self->context = NULL;
     ((PyObject*)self)->ob_type->tp_free ((PyObject *) self);
 _context_repr (PyObject *self)
 {
     /* TODO */
-    return Text_FromUTF8 ("<alcContext>");
+    return Text_FromUTF8 ("<Context>");
 }
 
 /* Context getters/setters */
     return ctxt->device;
 }
 
+static PyObject*
+_context_getlistener (PyObject* self, void *closure)
+{
+    PyContext *ctxt = (PyContext*)self;
+
+    if (!ctxt->listener)
+    {
+        ctxt->listener = PyListener_New (self);
+        if (!ctxt->listener)
+            return NULL;
+    }
+    Py_INCREF (ctxt->listener);
+    return ctxt->listener;
+}
+
 /* Context methods */
 static PyObject*
 _context_makecurrent (PyObject *self)
 _context_createbuffers (PyObject *self, PyObject *args)
 {
     unsigned int bufnum;
-    PyBuffers *buffers;
 
     ASSERT_CONTEXT_IS_CURRENT(self, NULL);
-
     if (!UintFromObj (args, &bufnum))
         return NULL;
-    CLEAR_ERROR_STATE ();
+    return PyBuffers_New (self, (ALsizei) bufnum);
+}
 
-    buffers = (PyBuffers*) PyBuffers_New ((ALsizei) bufnum);
-    if (!buffers)
+static PyObject*
+_context_createsources (PyObject *self, PyObject *args)
+{
+    unsigned int bufnum;
+
+    ASSERT_CONTEXT_IS_CURRENT(self, NULL);
+    if (!UintFromObj (args, &bufnum))
         return NULL;
-
-    alGenBuffers ((ALsizei)bufnum, buffers->buffers);
-    if (SetALErrorException (alGetError ()))
-    {
-        Py_DECREF (buffers);
-        return NULL;
-    }
-    Py_RETURN_NONE;
+    return PySources_New (self, (ALsizei) bufnum);
 }
 
 static PyObject*

src/openal/device.c

     PyObject *retval;
     const ALCchar *name = alcGetString (PyDevice_AsDevice (self),
         ALC_DEVICE_SPECIFIER);
-    /* <Device ('')> == 13 */
-    size_t len = strlen ((const char*) name) + 14;
+    /* Device('') == 10 */
+    size_t len = strlen ((const char*) name) + 11;
     char *str = malloc (len);
     if (!str)
         return NULL;
 
-    snprintf (str, len, "<Device ('%s')>", (const char*) name);
+    snprintf (str, len, "Device('%s')", (const char*) name);
     retval = Text_FromUTF8 (str);
     free (str);
     return retval;

src/openal/listener.c

+/*
+  pygame - Python Game Library
+  Copyright (C) 2010 Marcus von Appen
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Library General Public
+  License as published by the Free Software Foundation; either
+  version 2 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Library General Public License for more details.
+
+  You should have received a copy of the GNU Library General Public
+  License along with this library; if not, write to the Free
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+*/
+#define PYGAME_OPENALLISTENER_INTERNAL
+
+#include "openalmod.h"
+#include "pgopenal.h"
+
+static int _listener_init (PyObject *self, PyObject *args, PyObject *kwds);
+static void _listener_dealloc (PyListener *self);
+static PyObject* _listener_repr (PyObject *self);
+
+static PyObject* _listener_setprop (PyObject *self, PyObject *args);
+static PyObject* _listener_getprop (PyObject *self, PyObject *args);
+
+/**
+ */
+static PyMethodDef _listener_methods[] = {
+    { "set_prop", _listener_setprop, METH_VARARGS, NULL },
+    { "get_prop", _listener_getprop, METH_VARARGS, NULL },
+    { NULL, NULL, 0, NULL }
+};
+
+/**
+ */
+static PyGetSetDef _listener_getsets[] = {
+    { NULL, NULL, NULL, NULL, NULL }
+};
+
+/**
+ */
+PyTypeObject PyListener_Type =
+{
+    TYPE_HEAD(NULL, 0)
+    "base.Listener",            /* tp_name */
+    sizeof (PyListener),        /* tp_basicsize */
+    0,                          /* tp_itemsize */
+    (destructor) _listener_dealloc, /* tp_dealloc */
+    0,                          /* tp_print */
+    0,                          /* tp_getattr */
+    0,                          /* tp_setattr */
+    0,                          /* tp_compare */
+    (reprfunc)_listener_repr,   /* tp_repr */
+    0,                          /* tp_as_number */
+    0,                          /* tp_as_sequence */
+    0,                          /* tp_as_mapping */
+    0,                          /* tp_hash */
+    0,                          /* tp_call */
+    0,                          /* tp_str */
+    0,                          /* tp_getattro */
+    0,                          /* tp_setattro */
+    0,                          /* tp_as_buffer */
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+    0/*DOC_BASE_DEVICE*/,
+    0,                          /* tp_traverse */
+    0,                          /* tp_clear */
+    0,                          /* tp_richcompare */
+    0,                          /* tp_weaklistoffset */
+    0,                          /* tp_iter */
+    0,                          /* tp_iternext */
+    _listener_methods,          /* tp_methods */
+    0,                          /* tp_members */
+    _listener_getsets,          /* tp_getset */
+    0,                          /* tp_base */
+    0,                          /* tp_dict */
+    0,                          /* tp_descr_get */
+    0,                          /* tp_descr_set */
+    0,                          /* tp_dictoffset */
+    (initproc) _listener_init,  /* tp_init */
+    0,                          /* tp_alloc */
+    0,                          /* tp_new */
+    0,                          /* tp_free */
+    0,                          /* tp_is_gc */
+    0,                          /* tp_bases */
+    0,                          /* tp_mro */
+    0,                          /* tp_cache */
+    0,                          /* tp_subclasses */
+    0,                          /* tp_weaklist */
+    0,                          /* tp_del */
+#if PY_VERSION_HEX >= 0x02060000
+    0                           /* tp_version_tag */
+#endif
+};
+
+static void
+_listener_dealloc (PyListener *self)
+{
+    ((PyObject*)self)->ob_type->tp_free ((PyObject *) self);
+}
+
+static int
+_listener_init (PyObject *self, PyObject *args, PyObject *kwds)
+{
+    PyErr_SetString (PyExc_NotImplementedError,
+        "Listener cannot be created dirrectly - use the Context instead");
+    return -1;
+}
+
+static PyObject*
+_listener_repr (PyObject *self)
+{
+    return Text_FromUTF8 ("<Listener>");
+}
+
+/* Listener getters/setters */
+
+/* Listener methods */
+static PyObject*
+_listener_setprop (PyObject *self, PyObject *args)
+{
+    ALenum param;
+    PyObject *values;
+    char *type;
+    PropType ptype = INVALID;
+
+    if (!CONTEXT_IS_CURRENT (((PyListener*)self)->context))
+    {
+        PyErr_SetString (PyExc_PyGameError, "buffer context is not current");
+        return NULL;
+    }
+
+    if (!PyArg_ParseTuple (args, "lO|s:set_prop", &param, &values, &type))
+        return NULL;
+
+    if (type)
+    {
+        ptype = GetPropTypeFromStr (type);
+        if (ptype == INVALID)
+        {
+            PyErr_SetString (PyExc_RuntimeError,
+                "passing a sequence requires passing a type specifier");
+            return NULL;
+        }
+    }
+
+    if (PySequence_Check (values))
+    {
+        Py_ssize_t size, cnt;
+        if (!type)
+        {
+            PyErr_SetString (PyExc_RuntimeError,
+                "passing a sequence requires passing a type specifier");
+            return NULL;
+        }
+        if (ptype == INT || ptype == FLOAT)
+        {
+            PyErr_SetString (PyExc_TypeError,
+                "cannot use single value type and sequence together");
+            return NULL;
+        }
+
+        size = PySequence_Size (values);
+        switch (ptype)
+        {
+        case INT3:
+        case INTARRAY:
+        {
+            ALint *vals;
+            int tmp;
+            if (ptype == INT3 && size < 3)
+            {
+                PyErr_SetString (PyExc_ValueError,
+                    "sequence too small for 'i3'");
+                return NULL;
+            }
+            vals = PyMem_New (ALint, size);
+            if (!vals)
+                return NULL;
+            for (cnt = 0; cnt < size; cnt++)
+            {
+                if (!IntFromSeqIndex (values, cnt, &tmp))
+                {
+                    PyMem_Free (vals);
+                    return NULL;
+                }
+                vals[cnt] = (ALint) tmp;
+            }
+
+            CLEAR_ERROR_STATE ();
+            if (ptype == INT3)
+                alListener3i (param, vals[0], vals[1], vals[2]);
+            else
+                alListeneriv (param, vals);
+            PyMem_Free (vals);
+            /* Error will be set at the end */
+            break;
+        }
+        case FLOAT3:
+        case FLOATARRAY:
+        {
+            ALfloat *vals;
+            double tmp;
+            if (ptype == FLOAT3 && size < 3)
+            {
+                PyErr_SetString (PyExc_ValueError,
+                    "sequence too small for 'f3'");
+                return NULL;
+            }
+            vals = PyMem_New (ALfloat, size);
+            if (!vals)
+                return NULL;
+            for (cnt = 0; cnt < size; cnt++)
+            {
+                if (!DoubleFromSeqIndex (values, cnt, &tmp))
+                {
+                    PyMem_Free (vals);
+                    return NULL;
+                }
+                vals[cnt] = (ALfloat) tmp;
+            }
+
+            CLEAR_ERROR_STATE ();
+            if (ptype == FLOAT3)
+                alListener3f (param, vals[0], vals[1], vals[2]);
+            else
+                alListenerfv (param, vals);
+            PyMem_Free (vals);
+            /* Error will be set at the end */
+            break;
+        }
+        default:
+            PyErr_SetString (PyExc_TypeError, "unsupported value");
+            return NULL;
+        }
+    }
+    else
+    {
+        int ival = 0;
+        double fval = 0;
+
+        if (!type)
+        {
+            if (IntFromObj (values, &ival))
+                ptype = INT;
+            else
+                PyErr_Clear ();
+            if (DoubleFromObj (values, &fval))
+                ptype = FLOAT;
+            else
+            {
+                PyErr_Clear ();
+                PyErr_SetString (PyExc_TypeError, "unsupported value");
+                return NULL;
+            }
+        }
+        
+        switch (ptype)
+        {
+        case INT:
+            if (!IntFromObj (values, &ival))
+                return NULL;
+            CLEAR_ERROR_STATE ();
+            alListeneri (param, (ALint) ival);
+            break;
+        case FLOAT:
+            if (!DoubleFromObj (values, &fval))
+                return NULL;
+            CLEAR_ERROR_STATE ();
+            alListenerf (param, (ALfloat) fval);
+            break;
+        default:
+            PyErr_SetString (PyExc_TypeError, "value type mismatch");
+            return NULL;
+        }
+    }
+
+    if (SetALErrorException (alGetError ()))
+        return NULL;
+    Py_RETURN_NONE;
+}
+
+static PyObject*
+_listener_getprop (PyObject *self, PyObject *args)
+{
+    ALenum param;
+    char *type;
+    int size = 0, cnt;
+    PropType ptype = INVALID;
+
+    if (!CONTEXT_IS_CURRENT (((PyListener*)self)->context))
+    {
+        PyErr_SetString (PyExc_PyGameError, "buffer context is not current");
+        return NULL;
+    }
+
+    if (!PyArg_ParseTuple (args, "ls", &param, &type))
+    {
+        PyErr_Clear ();
+        if (!PyArg_ParseTuple (args, "l|si", &param, &type, &size))
+            return NULL;
+        if (size <= 0)
+        {
+            PyErr_SetString (PyExc_ValueError, "size must not smaller than 0");
+            return NULL;
+        }
+    }
+
+    ptype = GetPropTypeFromStr (type);
+    CLEAR_ERROR_STATE ();
+    switch (ptype)
+    {
+    case INT:
+    {
+        ALint val;
+        alGetListeneri (param, &val);
+        if (SetALErrorException (alGetError ()))
+            return NULL;
+        return PyLong_FromLong ((long)val);
+    }
+    case FLOAT:
+    {
+        ALfloat val;
+        alGetListenerf (param, &val);
+        if (SetALErrorException (alGetError ()))
+            return NULL;
+        return PyFloat_FromDouble ((double)val);
+    }
+    case INT3:
+    {
+        ALint val[3];
+        alGetListener3i (param, &val[0], &val[1], &val[2]);
+        if (SetALErrorException (alGetError ()))
+            return NULL;
+        return Py_BuildValue ("(lll)", (long)val[0], (long)val[1],
+            (long)val[2]);
+    }
+    case FLOAT3:
+    {
+        ALfloat val[3];
+        alGetListener3f (param, &val[0], &val[1], &val[2]);
+        if (SetALErrorException (alGetError ()))
+            return NULL;
+        return Py_BuildValue ("(ddd)", (double)val[0], (double)val[1],
+            (double)val[2]);
+    }
+    case INTARRAY:
+    {
+        PyObject *tuple, *item;
+        ALint* val = PyMem_New (ALint, size);
+        if (!val)
+            return NULL;
+        alGetListeneriv (param, val);
+        if (SetALErrorException (alGetError ()))
+        {
+            PyMem_Free (val);
+            return NULL;
+        }
+        tuple = PyTuple_New ((Py_ssize_t) size);
+        if (!tuple)
+            return NULL;
+        for (cnt = 0; cnt < size; cnt++)
+        {
+            item = PyLong_FromLong ((long)val[cnt]);
+            if (!item)
+            {
+                PyMem_Free (val);
+                Py_DECREF (tuple);
+                return NULL;
+            }
+            PyTuple_SET_ITEM (tuple, (Py_ssize_t) cnt, item);
+        }
+        return tuple;
+    }
+    case FLOATARRAY:
+    {
+        PyObject *tuple, *item;
+        ALfloat* val = PyMem_New (ALfloat, size);
+        if (!val)
+            return NULL;
+        alGetListenerfv (param, val);
+        if (SetALErrorException (alGetError ()))
+        {
+            PyMem_Free (val);
+            return NULL;
+        }
+        tuple = PyTuple_New ((Py_ssize_t) size);
+        if (!tuple)
+            return NULL;
+        for (cnt = 0; cnt < size; cnt++)
+        {
+            item = PyFloat_FromDouble ((double)val[cnt]);
+            if (!item || PyTuple_SET_ITEM (tuple, (Py_ssize_t) cnt, item) != 0)
+            {
+                PyMem_Free (val);
+                Py_XDECREF (item);
+                Py_DECREF (tuple);
+                return NULL;
+            }
+        }
+        return tuple;
+    }
+    default:
+        PyErr_SetString (PyExc_ValueError, "invalid type specifier");
+        return NULL;
+    }
+}
+
+/* C API */
+PyObject*
+PyListener_New (PyObject *context)
+{
+    PyObject *listener;
+
+    if (!context || !PyContext_Check (context))
+    {
+        PyErr_SetString (PyExc_TypeError, "context is not a valid Context");
+        return NULL;
+    }
+
+    listener = PyListener_Type.tp_new (&PyListener_Type, NULL, NULL);
+    if (!listener)
+        return NULL;
+    ((PyListener*)listener)->context = context;
+    return listener;
+}
+
+void
+listener_export_capi (void **capi)
+{
+    capi[PYGAME_OPENALLISTENER_FIRSTSLOT] = &PyListener_Type;
+}

src/openal/openalmod.c

 static PyObject* _openal_getdefaultoutputdevicename (PyObject *self);
 static PyObject* _openal_getdefaultcapturedevicename (PyObject *self);
 
-
 static PyMethodDef _openal_methods[] = {
     { "init", (PyCFunction)_openal_init, METH_NOARGS, ""/*DOC_BASE_INIT*/ },
     { "quit", (PyCFunction)_openal_quit, METH_NOARGS, ""/*DOC_BASE_QUIT*/ },
     }
 }
 
+PropType
+GetPropTypeFromStr (char *name)
+{
+    size_t len;
+    if (!name)
+        return INVALID;
+
+    len = strlen (name);
+
+    if (len == 1)
+    {
+        if (name[0] == 'i')
+            return INT;
+        else if (name[0] == 'f')
+            return FLOAT;
+        return INVALID;
+    }
+
+    if (len == 2)
+    {
+        if (name[0] == 'i')
+        {
+            if (name[1] == 'a')
+                return INTARRAY;
+            if (name[1] == '3')
+                return INT3;
+        }
+        else if (name[0] == 'f')
+        {
+            if (name[1] == 'a')
+                return FLOATARRAY;
+            if (name[1] == '3')
+                return FLOAT3;
+        }
+    }
+    return INVALID;
+}
+
+
 #ifdef IS_PYTHON_3
 PyMODINIT_FUNC PyInit_base (void)
 #else
     PyBuffers_Type.tp_new = PyType_GenericNew;
     if (PyType_Ready (&PyBuffers_Type) < 0)
         goto fail;
+    PySources_Type.tp_new = PyType_GenericNew;
+    if (PyType_Ready (&PySources_Type) < 0)
+        goto fail;
+    PyListener_Type.tp_new = PyType_GenericNew;
+    if (PyType_Ready (&PyListener_Type) < 0)
+        goto fail;
     
     ADD_OBJ_OR_FAIL (mod, "Device", PyDevice_Type, fail);
     ADD_OBJ_OR_FAIL (mod, "Context", PyContext_Type, fail);
     ADD_OBJ_OR_FAIL (mod, "Buffers", PyBuffers_Type, fail);
+    ADD_OBJ_OR_FAIL (mod, "Sources", PySources_Type, fail);
+    ADD_OBJ_OR_FAIL (mod, "Listener", PyListener_Type, fail);
 
     device_export_capi (c_api);
     context_export_capi (c_api);
     buffers_export_capi (c_api);
+    sources_export_capi (c_api);
+    listener_export_capi (c_api);
 
     c_api_obj = PyCObject_FromVoidPtr ((void *) c_api, NULL);
     if (c_api_obj)

src/openal/openalmod.h

 #define PYGAME_OPENALDEVICE_INTERNAL
 #define PYGAME_OPENALCONTEXT_INTERNAL
 #define PYGAME_OPENALBUFFERS_INTERNAL
+#define PYGAME_OPENALSOURCES_INTERNAL
+#define PYGAME_OPENALLISTENER_INTERNAL
 
 #if defined(IS_MSYS) || defined(IS_WIN32)
 #include <al.h>
 
 extern PyTypeObject PyBuffers_Type;
 #define PyBuffers_Check(x) (PyObject_TypeCheck (x, &PyBuffers_Type))
-PyObject* PyBuffers_New (ALsizei count);
+PyObject* PyBuffers_New (PyObject *context, ALsizei count);
+
+extern PyTypeObject PySources_Type;
+#define PySources_Check(x) (PyObject_TypeCheck (x, &PySources_Type))
+PyObject* PySources_New (PyObject *context, ALsizei count);
+
+extern PyTypeObject PyListener_Type;
+#define PyListener_Check(x) (PyObject_TypeCheck (x, &PyListener_Type))
+PyObject* PyListener_New (PyObject *context);
+
+typedef enum
+{
+    INVALID,     /* invalid type */
+    INT,         /* 'i'  */
+    FLOAT,       /* 'f'  */
+    INT3,        /* 'i3' */
+    FLOAT3,      /* 'f3' */ 
+    INTARRAY,    /* 'ia' */
+    FLOATARRAY   /* 'fa' */
+} PropType;
+PropType GetPropTypeFromStr (char *name);
 
 int SetALErrorException (ALenum error);
 int SetALCErrorException (ALCenum error);
 void device_export_capi (void **capi);
 void context_export_capi (void **capi);
 void buffers_export_capi (void **capi);
+void sources_export_capi (void **capi);
+void listener_export_capi (void **capi);
 
 #endif /* _PYGAME_OPENALMOD_H_ */

src/openal/pgopenal.h

 #include <AL/alext.h>
 #endif
 
-#include "pgcompat.h"
-#include "pgdefines.h"
+#include "pgbase.h"
 
 #ifdef __cplusplus
 extern "C" {
     PyObject_HEAD
     ALCcontext *context;
     PyObject   *device;
+    PyObject   *listener;
 } PyContext;
 #define PyContext_AsContext(x) (((PyContext*)x)->context)
+#define CONTEXT_IS_CURRENT(x) \
+    (alcGetCurrentContext () == PyContext_AsContext (x))
+#define ASSERT_CONTEXT_IS_CURRENT(x,ret)                                \
+    if (alcGetCurrentContext () != PyContext_AsContext (x))             \
+    {                                                                   \
+        PyErr_SetString (PyExc_PyGameError, "Context is not current");  \
+        return (ret);                                                   \
+    }
 #define PYGAME_OPENALCONTEXT_FIRSTSLOT                                  \
     (PYGAME_OPENALDEVICE_FIRSTSLOT + PYGAME_OPENALDEVICE_NUMSLOTS)
 #define PYGAME_OPENALCONTEXT_NUMSLOTS 1
 typedef struct
 {
     PyObject_HEAD
-    ALsizei  count;
-    ALuint  *buffers;
+    PyObject *context;
+    ALsizei   count;
+    ALuint   *buffers;
 } PyBuffers;
-
 #define PyBuffers_AsBuffers(x) (((PyBuffers*)x)->buffers)
 #define PYGAME_OPENALBUFFERS_FIRSTSLOT                                  \
     (PYGAME_OPENALCONTEXT_FIRSTSLOT + PYGAME_OPENALCONTEXT_NUMSLOTS)
         (PyTypeObject*)PyGameOpenAL_C_API[PYGAME_OPENALBUFFERS_FIRSTSLOT+0]))
 #endif /* PYGAME_OPENALBUFFERS_INTERNAL */
 
+typedef struct
+{
+    PyObject_HEAD
+    PyObject *context;
+    ALsizei   count;
+    ALuint   *sources;
+} PySources;
+#define PySources_AsSources(x) (((PySources*)x)->sources)
+#define PYGAME_OPENALSOURCES_FIRSTSLOT                                  \
+    (PYGAME_OPENALBUFFERS_FIRSTSLOT + PYGAME_OPENALBUFFERS_NUMSLOTS)
+#define PYGAME_OPENALSOURCES_NUMSLOTS 1
+#ifndef PYGAME_OPENALSOURCES_INTERNAL
+#define PySources_Type                                                  \
+    (*(PyTypeObject*)PyGameOpenAL_C_API[PYGAME_OPENALSOURCES_FIRSTSLOT+0])
+#define PySources_Check(x)                                              \
+    (PyObject_TypeCheck(x,                                              \
+        (PyTypeObject*)PyGameOpenAL_C_API[PYGAME_OPENALSOURCES_FIRSTSLOT+0]))
+#endif /* PYGAME_OPENALSOURCES_INTERNAL */
+
+typedef struct
+{
+    PyObject_HEAD
+    PyObject *context;
+} PyListener;
+#define PYGAME_OPENALLISTENER_FIRSTSLOT                                 \
+    (PYGAME_OPENALSOURCES_FIRSTSLOT + PYGAME_OPENALSOURCES_NUMSLOTS)
+#define PYGAME_OPENALLISTENER_NUMSLOTS 1
+#ifndef PYGAME_OPENALLISTENER_INTERNAL
+#define PyListener_Type                                                 \
+    (*(PyTypeObject*)PyGameOpenAL_C_API[PYGAME_OPENALLISTENER_FIRSTSLOT+0])
+#define PyListener_Check(x)                                             \
+    (PyObject_TypeCheck(x,                                              \
+        (PyTypeObject*)PyGameOpenAL_C_API[PYGAME_OPENALLISTENER_FIRSTSLOT+0]))
+#endif /* PYGAME_OPENALLISTENER_INTERNAL */
 
 /**
  * C API export.
 #endif
 
 #define PYGAME_OPENAL_SLOTS \
-    (PYGAME_OPENALBUFFERS_FIRSTSLOT + PYGAME_OPENALBUFFERS_NUMSLOTS)
+    (PYGAME_OPENALLISTENER_FIRSTSLOT + PYGAME_OPENALLISTENER_NUMSLOTS)
 #define PYGAME_OPENAL_ENTRY "_PYGAME_OPENAL_CAPI"
 
 static int

src/openal/sources.c

+/*
+  pygame - Python Game Library
+  Copyright (C) 2010 Marcus von Appen
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Library General Public
+  License as published by the Free Software Foundation; either
+  version 2 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Library General Public License for more details.
+
+  You should have received a copy of the GNU Library General Public
+  License along with this library; if not, write to the Free
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+*/
+#define PYGAME_OPENALSOURCES_INTERNAL
+
+#include "openalmod.h"
+#include "pgopenal.h"
+
+static int _sources_init (PyObject *self, PyObject *args, PyObject *kwds);
+static void _sources_dealloc (PySources *self);
+static PyObject* _sources_repr (PyObject *self);
+
+typedef enum
+{
+    PLAY,
+    PAUSE,
+    STOP,
+    REWIND
+} SourcesAction;
+static PyObject* _sources_action (PyObject *self, PyObject *args,
+    SourcesAction action);
+
+
+static PyObject* _sources_setprop (PyObject *self, PyObject *args);
+static PyObject* _sources_getprop (PyObject *self, PyObject *args);
+static PyObject* _sources_play (PyObject *self, PyObject *args);
+static PyObject* _sources_pause (PyObject *self, PyObject *args);
+static PyObject* _sources_stop (PyObject *self, PyObject *args);
+static PyObject* _sources_rewind (PyObject *self, PyObject *args);
+
+static PyObject* _sources_getcount (PyObject* self, void *closure);
+static PyObject* _sources_getsources (PyObject* self, void *closure);
+
+/**
+ */
+static PyMethodDef _sources_methods[] = {
+    { "set_prop", _sources_setprop, METH_VARARGS, NULL },
+    { "get_prop", _sources_getprop, METH_VARARGS, NULL },
+    { "play", _sources_play, METH_O, NULL },
+    { "pause", _sources_pause, METH_O, NULL },
+    { "stop", _sources_stop, METH_O, NULL },
+    { "rewind", _sources_rewind, METH_O, NULL },
+    { NULL, NULL, 0, NULL }
+};
+
+/**
+ */
+static PyGetSetDef _sources_getsets[] = {
+    { "count", _sources_getcount, NULL, NULL, NULL },
+    { "sources", _sources_getsources, NULL, NULL, NULL },
+    { NULL, NULL, NULL, NULL, NULL }
+};
+
+/**
+ */
+PyTypeObject PySources_Type =
+{
+    TYPE_HEAD(NULL, 0)
+    "base.Sources",              /* tp_name */
+    sizeof (PySources),          /* tp_basicsize */
+    0,                          /* tp_itemsize */
+    (destructor) _sources_dealloc, /* tp_dealloc */
+    0,                          /* tp_print */
+    0,                          /* tp_getattr */
+    0,                          /* tp_setattr */
+    0,                          /* tp_compare */
+    (reprfunc)_sources_repr,     /* tp_repr */
+    0,                          /* tp_as_number */
+    0,                          /* tp_as_sequence */
+    0,                          /* tp_as_mapping */
+    0,                          /* tp_hash */
+    0,                          /* tp_call */
+    0,                          /* tp_str */
+    0,                          /* tp_getattro */
+    0,                          /* tp_setattro */
+    0,                          /* tp_as_buffer */
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+    0/*DOC_BASE_DEVICE*/,
+    0,                          /* tp_traverse */
+    0,                          /* tp_clear */
+    0,                          /* tp_richcompare */
+    0,                          /* tp_weaklistoffset */
+    0,                          /* tp_iter */
+    0,                          /* tp_iternext */
+    _sources_methods,            /* tp_methods */
+    0,                          /* tp_members */
+    _sources_getsets,            /* tp_getset */
+    0,                          /* tp_base */
+    0,                          /* tp_dict */
+    0,                          /* tp_descr_get */
+    0,                          /* tp_descr_set */
+    0,                          /* tp_dictoffset */
+    (initproc) _sources_init,   /* tp_init */
+    0,                          /* tp_alloc */
+    0,                          /* tp_new */
+    0,                          /* tp_free */
+    0,                          /* tp_is_gc */
+    0,                          /* tp_bases */
+    0,                          /* tp_mro */
+    0,                          /* tp_cache */
+    0,                          /* tp_subclasses */
+    0,                          /* tp_weaklist */
+    0,                          /* tp_del */
+#if PY_VERSION_HEX >= 0x02060000
+    0                           /* tp_version_tag */
+#endif
+};
+
+static void
+_sources_dealloc (PySources *self)
+{
+    int switched = 0;
+
+    if (self->sources != NULL)
+    {
+        ALCcontext *ctxt = alcGetCurrentContext ();
+        if (ctxt != PyContext_AsContext (self->context))
+        {
+            /* Switch the current context to release the correct buffers. */
+            alcMakeContextCurrent (PyContext_AsContext (self->context));
+            switched = 1;
+        }
+        alDeleteSources (self->count, self->sources);
+        if (switched)
+            alcMakeContextCurrent (ctxt);
+    }
+    self->sources = NULL;
+    self->count = 0;
+
+    ((PyObject*)self)->ob_type->tp_free ((PyObject *) self);
+}
+
+static int
+_sources_init (PyObject *self, PyObject *args, PyObject *kwds)
+{
+    PyErr_SetString (PyExc_NotImplementedError,
+        "Sources cannot be created dirrectly - use the Context instead");
+    return -1;
+}
+
+static PyObject*
+_sources_repr (PyObject *self)
+{
+    return Text_FromUTF8 ("<Sources>");
+}
+
+/* Sources getters/setters */
+static PyObject*
+_sources_getcount (PyObject* self, void *closure)
+{
+    return PyInt_FromLong ((long)(((PySources*)self)->count));
+}
+
+static PyObject*
+_sources_getsources (PyObject* self, void *closure)
+{
+    PySources *sources = (PySources*)self;
+    PyObject *tuple, *item;
+    ALsizei i;
+
+    tuple = PyTuple_New ((Py_ssize_t)(sources->count));
+    if (!tuple)
+        return NULL;
+
+    for (i = 0; i < sources->count; i++)
+    {
+        item = PyInt_FromLong ((long)sources->sources[i]);
+        if (!item)
+        {
+            Py_DECREF (tuple);
+            return NULL;
+        }
+        PyTuple_SET_ITEM (tuple, (Py_ssize_t)i, item);
+    }
+    return tuple;
+}
+
+/* Sources methods */
+static PyObject*
+_sources_setprop (PyObject *self, PyObject *args)
+{
+    long bufnum;
+    ALenum param;
+    PyObject *values;
+    char *type;
+    PropType ptype = INVALID;
+
+    if (!CONTEXT_IS_CURRENT (((PySources*)self)->context))
+    {
+        PyErr_SetString (PyExc_PyGameError, "source context is not current");
+        return NULL;
+    }
+
+    if (!PyArg_ParseTuple (args, "llO|s:set_prop", &bufnum, &param, &values,
+            &type))
+        return NULL;
+
+    if (bufnum < 0 || bufnum > ((PySources*)self)->count)
+    {
+        PyErr_SetString (PyExc_ValueError, "source index out of range");
+        return NULL;
+    }
+
+    if (type)
+    {
+        ptype = GetPropTypeFromStr (type);
+        if (ptype == INVALID)
+        {
+            PyErr_SetString (PyExc_RuntimeError,
+                "passing a sequence requires passing a type specifier");
+            return NULL;
+        }
+    }
+
+    if (PySequence_Check (values))
+    {
+        Py_ssize_t size, cnt;
+        if (!type)
+        {
+            PyErr_SetString (PyExc_RuntimeError,
+                "passing a sequence requires passing a type specifier");
+            return NULL;
+        }
+        if (ptype == INT || ptype == FLOAT)
+        {
+            PyErr_SetString (PyExc_TypeError,
+                "cannot use single value type and sequence together");
+            return NULL;
+        }
+
+        size = PySequence_Size (values);
+        switch (ptype)
+        {
+        case INT3:
+        case INTARRAY:
+        {
+            ALint *vals;
+            int tmp;
+            if (ptype == INT3 && size < 3)
+            {
+                PyErr_SetString (PyExc_ValueError,
+                    "sequence too small for 'i3'");
+                return NULL;
+            }
+            vals = PyMem_New (ALint, size);
+            if (!vals)
+                return NULL;
+            for (cnt = 0; cnt < size; cnt++)
+            {
+                if (!IntFromSeqIndex (values, cnt, &tmp))
+                {
+                    PyMem_Free (vals);
+                    return NULL;
+                }
+                vals[cnt] = (ALint) tmp;
+            }
+
+            CLEAR_ERROR_STATE ();
+            if (ptype == INT3)
+                alSource3i ((ALuint)bufnum, param, vals[0], vals[1], vals[2]);
+            else
+                alSourceiv ((ALuint)bufnum, param, vals);
+            PyMem_Free (vals);
+            /* Error will be set at the end */
+            break;
+        }
+        case FLOAT3:
+        case FLOATARRAY:
+        {
+            ALfloat *vals;
+            double tmp;
+            if (ptype == FLOAT3 && size < 3)
+            {
+                PyErr_SetString (PyExc_ValueError,
+                    "sequence too small for 'f3'");
+                return NULL;
+            }
+            vals = PyMem_New (ALfloat, size);
+            if (!vals)
+                return NULL;
+            for (cnt = 0; cnt < size; cnt++)
+            {
+                if (!DoubleFromSeqIndex (values, cnt, &tmp))
+                {
+                    PyMem_Free (vals);
+                    return NULL;
+                }
+                vals[cnt] = (ALfloat) tmp;
+            }
+
+            CLEAR_ERROR_STATE ();
+            if (ptype == FLOAT3)
+                alSource3f ((ALuint)bufnum, param, vals[0], vals[1], vals[2]);
+            else
+                alSourcefv ((ALuint)bufnum, param, vals);
+            PyMem_Free (vals);
+            /* Error will be set at the end */
+            break;
+        }
+        default:
+            PyErr_SetString (PyExc_TypeError, "unsupported value");
+            return NULL;
+        }
+    }
+    else
+    {
+        int ival = 0;
+        double fval = 0;
+
+        if (!type)
+        {
+            if (IntFromObj (values, &ival))
+                ptype = INT;
+            else
+                PyErr_Clear ();
+            if (DoubleFromObj (values, &fval))
+                ptype = FLOAT;
+            else
+            {
+                PyErr_Clear ();
+                PyErr_SetString (PyExc_TypeError, "unsupported value");
+                return NULL;
+            }
+        }
+        
+        switch (ptype)
+        {
+        case INT:
+            if (!IntFromObj (values, &ival))
+                return NULL;
+            CLEAR_ERROR_STATE ();
+            alSourcei ((ALuint)bufnum, param, (ALint) ival);
+            break;
+        case FLOAT:
+            if (!DoubleFromObj (values, &fval))
+                return NULL;
+            CLEAR_ERROR_STATE ();
+            alSourcef ((ALuint)bufnum, param, (ALfloat) fval);
+            break;
+        default:
+            PyErr_SetString (PyExc_TypeError, "value type mismatch");
+            return NULL;
+        }
+    }
+
+    if (SetALErrorException (alGetError ()))
+        return NULL;
+    Py_RETURN_NONE;
+}
+
+static PyObject*
+_sources_getprop (PyObject *self, PyObject *args)
+{
+    long bufnum;
+    ALenum param;
+    char *type;
+    int size = 0, cnt;
+    PropType ptype = INVALID;
+
+    if (!CONTEXT_IS_CURRENT (((PySources*)self)->context))
+    {
+        PyErr_SetString (PyExc_PyGameError, "source context is not current");
+        return NULL;
+    }
+
+    if (!PyArg_ParseTuple (args, "lls", &bufnum, &param, &type))
+    {
+        PyErr_Clear ();
+        if (!PyArg_ParseTuple (args, "ll|si", &bufnum, &param, &type, &size))
+            return NULL;
+        if (size <= 0)
+        {
+            PyErr_SetString (PyExc_ValueError, "size must not smaller than 0");
+            return NULL;
+        }
+    }
+
+    if (bufnum < 0 || bufnum > ((PySources*)self)->count)
+    {
+        PyErr_SetString (PyExc_ValueError, "source index out of range");
+        return NULL;
+    }
+
+    ptype = GetPropTypeFromStr (type);
+    CLEAR_ERROR_STATE ();
+    switch (ptype)
+    {
+    case INT:
+    {
+        ALint val;
+        alGetSourcei ((ALuint)bufnum, param, &val);
+        if (SetALErrorException (alGetError ()))
+            return NULL;
+        return PyLong_FromLong ((long)val);
+    }
+    case FLOAT:
+    {
+        ALfloat val;
+        alGetSourcef ((ALuint)bufnum, param, &val);
+        if (SetALErrorException (alGetError ()))
+            return NULL;
+        return PyFloat_FromDouble ((double)val);
+    }
+    case INT3:
+    {
+        ALint val[3];
+        alGetSource3i ((ALuint)bufnum, param, &val[0], &val[1], &val[2]);
+        if (SetALErrorException (alGetError ()))
+            return NULL;
+        return Py_BuildValue ("(lll)", (long)val[0], (long)val[1],
+            (long)val[2]);
+    }
+    case FLOAT3:
+    {
+        ALfloat val[3];
+        alGetSource3f ((ALuint)bufnum, param, &val[0], &val[1], &val[2]);
+        if (SetALErrorException (alGetError ()))
+            return NULL;
+        return Py_BuildValue ("(ddd)", (double)val[0], (double)val[1],
+            (double)val[2]);
+    }
+    case INTARRAY:
+    {
+        PyObject *tuple, *item;
+        ALint* val = PyMem_New (ALint, size);
+        if (!val)
+            return NULL;
+        alGetSourceiv ((ALuint)bufnum, param, val);
+        if (SetALErrorException (alGetError ()))
+        {
+            PyMem_Free (val);
+            return NULL;
+        }
+        tuple = PyTuple_New ((Py_ssize_t) size);
+        if (!tuple)
+            return NULL;
+        for (cnt = 0; cnt < size; cnt++)
+        {
+            item = PyLong_FromLong ((long)val[cnt]);
+            if (!item)
+            {
+                PyMem_Free (val);
+                Py_DECREF (tuple);
+                return NULL;
+            }
+            PyTuple_SET_ITEM (tuple, (Py_ssize_t) cnt, item);
+        }
+        return tuple;
+    }
+    case FLOATARRAY:
+    {
+        PyObject *tuple, *item;
+        ALfloat* val = PyMem_New (ALfloat, size);
+        if (!val)
+            return NULL;
+        alGetSourcefv ((ALuint)bufnum, param, val);
+        if (SetALErrorException (alGetError ()))
+        {
+            PyMem_Free (val);
+            return NULL;
+        }
+        tuple = PyTuple_New ((Py_ssize_t) size);
+        if (!tuple)
+            return NULL;
+        for (cnt = 0; cnt < size; cnt++)
+        {
+            item = PyFloat_FromDouble ((double)val[cnt]);
+            if (!item || PyTuple_SET_ITEM (tuple, (Py_ssize_t) cnt, item) != 0)
+            {
+                PyMem_Free (val);
+                Py_XDECREF (item);
+                Py_DECREF (tuple);
+                return NULL;
+            }
+        }
+        return tuple;
+    }
+    default:
+        PyErr_SetString (PyExc_ValueError, "invalid type specifier");
+        return NULL;
+    }
+}
+
+static PyObject*
+_sources_action (PyObject *self, PyObject *args, SourcesAction action)
+{
+    unsigned int source;
+    
+    if (!CONTEXT_IS_CURRENT (((PySources*)self)->context))
+    {
+        PyErr_SetString (PyExc_PyGameError, "source context is not current");
+        return NULL;
+    }
+
+    if (PySequence_Check (args))
+    {
+        ALuint *sources;
+        Py_ssize_t i;
+        Py_ssize_t len = PySequence_Size (args);
+
+        if (len > ((PySources*)self)->count)
+        {
+            PyErr_SetString (PyExc_ValueError,
+                "sequence size exceeds the available sources");
+            return NULL;
+        }
+        sources = PyMem_New (ALuint, (ALsizei) len);
+        if (!sources)
+            return NULL;
+        for (i = 0; i < len; i++)
+        {
+            if (!UintFromSeqIndex (args, i, &source))
+            {
+                PyMem_Free (sources);
+                return NULL;
+            }
+            sources[i] = source;
+        }
+        CLEAR_ERROR_STATE ();
+        switch (action)
+        {
+        case PLAY:
+            alSourcePlayv ((ALsizei) len, sources);
+            break;
+        case PAUSE:
+            alSourcePausev ((ALsizei) len, sources);
+            break;
+        case STOP:
+            alSourceStopv ((ALsizei) len, sources);
+            break;
+        case REWIND:
+            alSourceRewindv ((ALsizei) len, sources);
+            break;
+        default:
+            break;
+        }
+        PyMem_Free (sources);
+        if (SetALErrorException (alGetError ()))
+            return NULL;
+        Py_RETURN_NONE;
+    }
+    else if (UintFromObj (args, &source))
+    {
+        CLEAR_ERROR_STATE ();
+        switch (action)
+        {
+        case PLAY:
+            alSourcePlay ((ALuint)source);
+            break;
+        case PAUSE:
+            alSourcePause ((ALuint)source);
+            break;
+        case STOP:
+            alSourceStop ((ALuint)source);
+            break;
+        case REWIND:
+            alSourceRewind ((ALuint)source);
+            break;
+        }
+        if (SetALErrorException (alGetError ()))
+            return NULL;
+        Py_RETURN_NONE;
+    }
+
+    PyErr_SetString (PyExc_TypeError,
+        "argument must be a sequence or positive integer");
+    return NULL;
+}
+
+static PyObject*
+_sources_play (PyObject *self, PyObject *args)
+{
+    return _sources_action (self, args, PLAY);
+}
+
+static PyObject*
+_sources_pause (PyObject *self, PyObject *args)
+{
+    return _sources_action (self, args, PAUSE);
+}
+
+static PyObject*
+_sources_stop (PyObject *self, PyObject *args)
+{
+    return _sources_action (self, args, STOP);
+}
+
+static PyObject*
+_sources_rewind (PyObject *self, PyObject *args)
+{
+    return _sources_action (self, args, REWIND);
+}
+
+/* C API */
+PyObject*
+PySources_New (PyObject *context, ALsizei count)
+{
+    ALuint *buf;
+    PyObject *sources;
+
+    if (!context || !PyContext_Check (context))
+    {
+        PyErr_SetString (PyExc_TypeError, "context is not a valid Context");
+        return NULL;
+    }
+
+    if (count < 1)
+    {
+        PyErr_SetString (PyExc_ValueError, "cannot create less than 1 sources");
+        return NULL;
+    }
+
+    sources = PySources_Type.tp_new (&PySources_Type, NULL, NULL);
+    if (!sources)
+        return NULL;
+    ((PySources*)sources)->context = context;
+    ((PySources*)sources)->count = count;
+    ((PySources*)sources)->sources = NULL;
+    
+    buf = PyMem_New (ALuint, count);
+    if (!buf)
+    {
+        Py_DECREF (sources);
+        return NULL;
+    }
+    CLEAR_ERROR_STATE ();
+    alGenSources (count, buf);
+    if (SetALErrorException (alGetError ()))
+    {
+        Py_DECREF (sources);
+        return NULL;
+    }
+
+    ((PySources*)sources)->sources = buf;
+    return sources;
+}
+
+void
+sources_export_capi (void **capi)
+{
+    capi[PYGAME_OPENALSOURCES_FIRSTSLOT] = &PySources_Type;
+}
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.