Lenard Lindstrom avatar Lenard Lindstrom committed 018779f

Merge the buffer branch back into trunk.
1) Extend pygame.mixer.Sound() to load samples from an object with an array struct interface or the new buffer protocol. Exports an array struct interface.
2) Update sndarray to use the new Sound features on numpy arrays, removing the Python array manipulation code.
3) Add a new Type, pygame._view.View as a proxy for exporting an array struct interface.
4) Add the pygame.Surface.get_view() method, which returns a View instance. Besides creating views describing pixel2d, pixels3d, and pixels_alpha arrays, new pixels_red, pixels_green, and pixels_blue views have been added.
5) Update the pixels surfarray methods to use Surface.get_view() for numpy arrays, removing the Python array manipulation code. Add new methods returning red, green, and blue pixels arrays.

Comments (0)

Files changed (29)

 pixelarray src/pixelarray.c $(SDL) $(DEBUG)
 _arraysurfarray src/_arraysurfarray.c $(SDL) $(DEBUG)
 math src/math.c $(SDL) $(DEBUG)
-
+_view src/_view.c $(SDL) $(DEBUG)
 # BREAK = change breaks existing code
 # BUG    = fixed a bug that was (or could have been) crashing
 
+[SVN 2983] Feb 07, 2011
+    Merge the buffer branch back into trunk.
+    1) Extend pygame.mixer.Sound() to load samples from an object with
+       an array struct interface or the new buffer protocol. Exports
+       an array struct interface.
+    2) Update sndarray to use the new Sound features on numpy arrays,
+       removing the Python array manipulation code.
+    3) Add a new Type, pygame._view.View as a proxy for exporting an
+       array struct interface.
+    4) Add the pygame.Surface.get_view() method, which returns a View instance.
+       Besides creating views describing pixel2d, pixels3d, and pixels_alpha
+       arrays, new pixels_red, pixels_green, and pixels_blue views have been
+       added.
+    5) Update the pixels surfarray methods to use Surface.get_view() for
+       numpy arrays, removing the Python array manipulation code. Add new
+       methods returning red, green, and blue pixels arrays.
+
+[SVN 2977] Feb 06, 2011
+    Add Python 3.2 __pycache__ directories to ignore list.
+
+[SVN 2958] Jan 19, 2011
+    Add proof-of-concept pygame.freetype.render_raw method to show one way to
+      correctly calculate text size.
+
+[SVN 2957] Jan 14, 2011
+    Fix freetype.c build bug involving PyFREETYPE_C_API not defined.
+
+[SVN 2952-2954] Jan 05, 2011
+    [bug] GroupSingle memory leak posted in mailing list by Tobias Steinr�cken.
+
+[SVN 2951] Nov 14, 2010
+    Reenable the camera module for linux.
+
+[SVN 2936, 2938, 2944] Sep 28, 2010
+    Pygame now builds for Python 3.2. Capsule objects are used in preference
+    to CObject objects.
+
 [SVN 2937] September 23, 2010
     [BUG] Bugzilla 54: pygame.examples.movieplayer causes
     "PyThreadState_Get: no current thread".
     Movie module now works under Python 3.2.
 
-[SVN 2936] September 22, 2010
-    Use capsule objects in preference to cobject objects.
-    This allows Pygame to build an run from Python 3.2, which has to cobjects.
-
 [SVN 2934] September 16, 2010
     [BUG] Fix some MSVC warnings in hope of solving an error raised in font_test.py.
 

docs/ref/index.html

 <li><a href="surface.html#Surface.get_rect">Surface.get_rect</a> - <font size=-1>get the rectangular area of the Surface</font></li>
 <li><a href="surface.html#Surface.get_shifts">Surface.get_shifts</a> - <font size=-1>the bit shifts needed to convert between a color and a mapped integer</font></li>
 <li><a href="surface.html#Surface.get_size">Surface.get_size</a> - <font size=-1>get the dimensions of the Surface</font></li>
+<li><a href="surface.html#Surface.get_view">Surface.get_view</a> - <font size=-1>return a view of a surface's pixel data.</font></li>
 <li><a href="surface.html#Surface.get_width">Surface.get_width</a> - <font size=-1>get the width of the Surface</font></li>
 <li><a href="surface.html#Surface.lock">Surface.lock</a> - <font size=-1>lock the Surface memory for pixel access</font></li>
 <li><a href="surface.html#Surface.map_rgb">Surface.map_rgb</a> - <font size=-1>convert a color into a mapped color value</font></li>
 <li><a href="surfarray.html#pygame.surfarray.pixels2d">pygame.surfarray.pixels2d</a> - <font size=-1>Reference pixels into a 2d array</font></li>
 <li><a href="surfarray.html#pygame.surfarray.pixels3d">pygame.surfarray.pixels3d</a> - <font size=-1>Reference pixels into a 3d array</font></li>
 <li><a href="surfarray.html#pygame.surfarray.pixels_alpha">pygame.surfarray.pixels_alpha</a> - <font size=-1>Reference pixel alphas into a 2d array</font></li>
+<li><a href="surfarray.html#pygame.surfarray.pixels_blue">pygame.surfarray.pixels_blue</a> - <font size=-1>Reference pixel blue into a 2d array.</font></li>
+<li><a href="surfarray.html#pygame.surfarray.pixels_green">pygame.surfarray.pixels_green</a> - <font size=-1>Reference pixel green into a 2d array.</font></li>
+<li><a href="surfarray.html#pygame.surfarray.pixels_red">pygame.surfarray.pixels_red</a> - <font size=-1>Reference pixel red into a 2d array.</font></li>
 <li><a href="surfarray.html#pygame.surfarray.use_arraytype">pygame.surfarray.use_arraytype</a> - <font size=-1>Sets the array system to be used for surface arrays</font></li>
 </ul>
 <li><a href="tests.html#pygame.tests">pygame.tests</a> - <font size=-1>Pygame unit test suite package</font></li>

docs/ref/mixer.html

   <tt>pygame.mixer.Sound(buffer=buffer): return Sound</tt><br>
   <tt>pygame.mixer.Sound(object): return Sound</tt><br>
   <tt>pygame.mixer.Sound(file=object): return Sound</tt><br>
+  <tt>pygame.mixer.Sound(array=object): return Sound</tt><br>
 <ul><small><table>
   <tr><td><a href="mixer.html#Sound.play">Sound.play</a> - <font size=-1>begin sound playback</font></td><td>begin sound playback</td></tr>
   <tr><td><a href="mixer.html#Sound.stop">Sound.stop</a> - <font size=-1>stop sound playback</font></td><td>stop sound playback</td></tr>
   <tr><td><a href="mixer.html#Sound.get_length">Sound.get_length</a> - <font size=-1>get the length of the Sound</font></td><td>get the length of the Sound</td></tr>
   <tr><td><a href="mixer.html#Sound.get_buffer">Sound.get_buffer</a> - <font size=-1>acquires a buffer object for the sameples of the Sound.</font></td><td>acquires a buffer object for the sameples of the Sound.</td></tr>
 </table></small></ul>
-<p>Load a new sound buffer from a filename, a python file object or a readable buffer object. Limited resampling will be performed to help the sample match the initialize arguments for the mixer. <tt>A</tt> Unicode string can only be a file pathname. <tt>A</tt> Python <tt>2.x</tt> string or a Python <tt>3.x</tt> bytes object can be either a pathname or a buffer object. Use the 'file' or 'buffer' keywords to avoid ambiguity; otherwise Sound may guess wrong. </p>
-<p>The Sound object represents actual sound sample data. Methods that change the state of the Sound object will the all instances of the Sound playback. </p>
+<p>Load a new sound buffer from a filename, a python file object or a readable buffer object. Limited resampling will be performed to help the sample match the initialize arguments for the mixer. <tt>A</tt> Unicode string can only be a file pathname. <tt>A</tt> Python <tt>2.x</tt> string or a Python <tt>3.x</tt> bytes object can be either a pathname or a buffer object. Use the 'file' or 'buffer' keywords to avoid ambiguity; otherwise Sound may guess wrong. If the array keyword is used, the object is expected to export a version 3, <tt>C</tt> level array interface or, for Python <tt>2.6</tt> or later, a new buffer interface (The object is checked for a buffer interface <tt>first.)</tt> </p>
+<p>The Sound object represents actual sound sample data. Methods that change the state of the Sound object will the all instances of the Sound playback. <tt>A</tt> Sound object also exports an array interface, and, for Python <tt>2.6</tt> or later, a new buffer interface. </p>
 <p>The Sound can be loaded from an <tt>OGG</tt> audio file or from an uncompressed <tt>WAV</tt>. </p>
 <p>Note: The buffer will be copied internally, no data will be shared between it and the Sound object. </p>
-<p><tt>pygame.mixer.Sound(buffer)</tt> is new in pygame <tt>1.8</tt> <tt>pygame.mixer.Sound</tt> keyword arguments new in pygame <tt>1.9.2</tt> </p>
+<p>For now buffer and array support is consistent with <tt>sndarray.make_sound</tt> for Numeric arrays, in that sample sign and byte order are ignored. This will change, either by correctly handling sign and byte order, or by raising an exception when different. Also, source samples are truncated to fit the audio sample size. This will not change. </p>
+<p><tt>pygame.mixer.Sound(buffer)</tt> is new in pygame <tt>1.8</tt> <tt>pygame.mixer.Sound</tt> keyword arguments and array interface support new in pygame <tt>1.9.2</tt> </p>
 <!--COMMENTS:pygame.mixer.Sound--> &nbsp;<br> 
 
 

docs/ref/surface.html

   <tr><td><a href="surface.html#Surface.set_shifts">Surface.set_shifts</a> - <font size=-1>sets the bit shifts needed to convert between a color and a mapped integer</font></td><td>sets the bit shifts needed to convert between a color and a mapped integer</td></tr>
   <tr><td><a href="surface.html#Surface.get_losses">Surface.get_losses</a> - <font size=-1>the significant bits used to convert between a color and a mapped integer</font></td><td>the significant bits used to convert between a color and a mapped integer</td></tr>
   <tr><td><a href="surface.html#Surface.get_bounding_rect">Surface.get_bounding_rect</a> - <font size=-1>find the smallest rect containing data</font></td><td>find the smallest rect containing data</td></tr>
+  <tr><td><a href="surface.html#Surface.get_view">Surface.get_view</a> - <font size=-1>return a view of a surface's pixel data.</font></td><td>return a view of a surface's pixel data.</td></tr>
   <tr><td><a href="surface.html#Surface.get_buffer">Surface.get_buffer</a> - <font size=-1>acquires a buffer object for the pixels of the Surface.</font></td><td>acquires a buffer object for the pixels of the Surface.</td></tr>
 </table></small></ul>
 <p><tt>A</tt> pygame Surface is used to represent any image. The Surface has a fixed resolution and pixel format. Surfaces with 8bit pixels use a color palette to map to 24bit color. </p>
 <br></ul>
 
 
+<a name="Surface.get_view">
+<big><b>Surface.get_view</big></b><br><ul>
+  <i>return a view of a surface's pixel data.</i><br>
+  <tt>Surface.get_view(kind='2'): return <view></tt><br>
+<p>Return an object which exposes a surface's internal pixel buffer to a NumPy array. For now a custom object with an array struct interface is returned. <tt>A</tt> Python memoryview may be returned in the future. The buffer is writeable. </p>
+<p>The kind argument is the length 1 string '2', '3', 'r', 'g', 'b', or 'a'. The letters are case insensitive; <tt>'A'</tt> will work as well. The argument can be either a Unicode or byte (char) string. The default is '2'. </p>
+<p><tt>A</tt> kind '2' view is a (surface-width, surface-height) array of raw pixels. The pixels are surface bytesized unsigned integers. The pixel format is surface specific. It is unavailable for 24-bit surfaces. </p>
+<p>'3' returns a (surface-width, surface-height, 3) view of <tt>RGB</tt> color components. Each of the red, green, and blue components are unsigned bytes. Only 24-bit and 32-bit surfaces are supported. The color components must be in either <tt>RGB</tt> or <tt>BGR</tt> order within the pixel. </p>
+<p>'r' for red, 'g' for green, 'b' for blue, and 'a' for alpha return a (surface-width, surface-height) view of a single color component within a surface: a color plane. Color components are unsigned bytes. Both 24-bit and 32-bit surfaces support 'r', 'g', and 'b'. Only 32-bit surfaces with <tt>SRCALPHA</tt> support 'a'. </p>
+<p>This method implicitly locks the Surface. The lock will be released, once the returned view object is deleted. </p>
+<p>New in pygame <tt>1.9.2</tt>. </p>
+<!--COMMENTS:Surface.get_view--> &nbsp;<br> 
+<br></ul>
+
+
 <a name="Surface.get_buffer">
 <big><b>Surface.get_buffer</big></b><br><ul>
   <i>acquires a buffer object for the pixels of the Surface.</i><br>

docs/ref/surfarray.html

   <tr><td><a href="surfarray.html#pygame.surfarray.pixels3d">pygame.surfarray.pixels3d</a> - <font size=-1>Reference pixels into a 3d array</font></td><td>Reference pixels into a 3d array</td></tr>
   <tr><td><a href="surfarray.html#pygame.surfarray.array_alpha">pygame.surfarray.array_alpha</a> - <font size=-1>Copy pixel alphas into a 2d array</font></td><td>Copy pixel alphas into a 2d array</td></tr>
   <tr><td><a href="surfarray.html#pygame.surfarray.pixels_alpha">pygame.surfarray.pixels_alpha</a> - <font size=-1>Reference pixel alphas into a 2d array</font></td><td>Reference pixel alphas into a 2d array</td></tr>
+  <tr><td><a href="surfarray.html#pygame.surfarray.pixels_red">pygame.surfarray.pixels_red</a> - <font size=-1>Reference pixel red into a 2d array.</font></td><td>Reference pixel red into a 2d array.</td></tr>
+  <tr><td><a href="surfarray.html#pygame.surfarray.pixels_green">pygame.surfarray.pixels_green</a> - <font size=-1>Reference pixel green into a 2d array.</font></td><td>Reference pixel green into a 2d array.</td></tr>
+  <tr><td><a href="surfarray.html#pygame.surfarray.pixels_blue">pygame.surfarray.pixels_blue</a> - <font size=-1>Reference pixel blue into a 2d array.</font></td><td>Reference pixel blue into a 2d array.</td></tr>
   <tr><td><a href="surfarray.html#pygame.surfarray.array_colorkey">pygame.surfarray.array_colorkey</a> - <font size=-1>Copy the colorkey values into a 2d array</font></td><td>Copy the colorkey values into a 2d array</td></tr>
   <tr><td><a href="surfarray.html#pygame.surfarray.make_surface">pygame.surfarray.make_surface</a> - <font size=-1>Copy an array to a new surface</font></td><td>Copy an array to a new surface</td></tr>
   <tr><td><a href="surfarray.html#pygame.surfarray.blit_array">pygame.surfarray.blit_array</a> - <font size=-1>Blit directly from a array values</font></td><td>Blit directly from a array values</td></tr>
 <br></ul>
 
 
+<a name="pygame.surfarray.pixels_red">
+<big><b>pygame.surfarray.pixels_red</big></b><br><ul>
+  <i>Reference pixel red into a 2d array.</i><br>
+  <tt>pygame.surfarray.pixels_red (Surface): return array</tt><br>
+<p>Create a new <tt>2D</tt> array that directly references the red values in a Surface. Any changes to the array will affect the pixels in the Surface. This is a fast operation since no data is copied. </p>
+<p>This can only work on 24-bit or 32-bit Surfaces. </p>
+<p>The Surface this array references will remain locked for the lifetime of the array. </p>
+<!--COMMENTS:pygame.surfarray.pixels_red--> &nbsp;<br> 
+<br></ul>
+
+
+<a name="pygame.surfarray.pixels_green">
+<big><b>pygame.surfarray.pixels_green</big></b><br><ul>
+  <i>Reference pixel green into a 2d array.</i><br>
+  <tt>pygame.surfarray.pixels_green (Surface): return array</tt><br>
+<p>Create a new <tt>2D</tt> array that directly references the green values in a Surface. Any changes to the array will affect the pixels in the Surface. This is a fast operation since no data is copied. </p>
+<p>This can only work on 24-bit or 32-bit Surfaces. </p>
+<p>The Surface this array references will remain locked for the lifetime of the array. </p>
+<!--COMMENTS:pygame.surfarray.pixels_green--> &nbsp;<br> 
+<br></ul>
+
+
+<a name="pygame.surfarray.pixels_blue">
+<big><b>pygame.surfarray.pixels_blue</big></b><br><ul>
+  <i>Reference pixel blue into a 2d array.</i><br>
+  <tt>pygame.surfarray.pixels_blue (Surface): return array</tt><br>
+<p>Create a new <tt>2D</tt> array that directly references the blue values in a Surface. Any changes to the array will affect the pixels in the Surface. This is a fast operation since no data is copied. </p>
+<p>This can only work on 24-bit or 32-bit Surfaces. </p>
+<p>The Surface this array references will remain locked for the lifetime of the array. </p>
+<!--COMMENTS:pygame.surfarray.pixels_blue--> &nbsp;<br> 
+<br></ul>
+
+
 <a name="pygame.surfarray.array_colorkey">
 <big><b>pygame.surfarray.array_colorkey</big></b><br><ul>
   <i>Copy the colorkey values into a 2d array</i><br>

lib/_numpysndarray.py

 import pygame.mixer as mixer 
 import numpy
 
-def _array_samples(sound, raw):
-    # Info is a (freq, format, stereo) tuple
-    info = mixer.get_init ()
-    if not info:
-        raise pygame.error("Mixer not initialized")
-    fmtbytes = (abs (info[1]) & 0xff) >> 3
-    channels = info[2]
-    if raw:
-        data = sound.get_buffer ().raw
-    else:
-        data = sound.get_buffer ()
-
-    shape = (len (data) // fmtbytes, )
-    if channels > 1:
-        shape = (shape[0] // channels, channels)
-
-    # mixer.init () does not support different formats from the ones below,
-    # so MSB/LSB stuff is silently ignored.
-    typecode = { 8 : numpy.uint8,   # AUDIO_U8
-                 16 : numpy.uint16, # AUDIO_U16 / AUDIO_U16SYS
-                 -8 : numpy.int8,   # AUDIO_S8
-                 -16 : numpy.int16  # AUDUI_S16 / AUDIO_S16SYS
-                 }[info[1]]
-                 
-    array = numpy.fromstring (data, typecode)
-    array.shape = shape
-    return array
 
 def array (sound):
     """pygame._numpysndarray.array(Sound): return array
     array will always be in the format returned from
     pygame.mixer.get_init().
     """
-    return _array_samples(sound, True)
+
+    return numpy.array (sound, copy=True)
 
 def samples (sound):
     """pygame._numpysndarray.samples(Sound): return array
     object. Modifying the array will change the Sound. The array will
     always be in the format returned from pygame.mixer.get_init().
     """
-    # Info is a (freq, format, stereo) tuple
-    info = pygame.mixer.get_init ()
-    if not info:
-        raise pygame.error("Mixer not initialized")
-    fmtbytes = (abs (info[1]) & 0xff) >> 3
-    channels = info[2]
-    data = sound.get_buffer ()
 
-    shape = (data.length // fmtbytes, )
-    if channels > 1:
-        shape = (shape[0] // channels, channels)
-        
-    # mixer.init () does not support different formats from the ones below,
-    # so MSB/LSB stuff is silently ignored.
-    typecode = { 8 : numpy.uint8,   # AUDIO_U8
-                 16 : numpy.uint16, # AUDIO_U16
-                 -8 : numpy.int8,   # AUDIO_S8
-                 -16 : numpy.int16  # AUDUI_S16
-                 }[info[1]]
-
-    array = numpy.frombuffer (data, typecode)
-    array.shape = shape
-    return array
+    return numpy.array (sound, copy=False)
 
 def make_sound (array):
     """pygame._numpysndarray.make_sound(array): return Sound
     must be initialized and the array format must be similar to the mixer
     audio format.
     """
-    # Info is a (freq, format, stereo) tuple
-    info = pygame.mixer.get_init ()
-    if not info:
-        raise pygame.error("Mixer not initialized")
-    channels = info[2]
+    
+    return mixer.Sound (array=array)
 
-    shape = array.shape
-    if channels == 1:
-        if len (shape) != 1:
-            raise ValueError("Array must be 1-dimensional for mono mixer")
-    else:
-        if len (shape) != 2:
-            raise ValueError("Array must be 2-dimensional for stereo mixer")
-        elif shape[1] != channels:
-            raise ValueError("Array depth must match number of mixer channels")
-    return mixer.Sound (buffer=array)

lib/_numpysurfarray.py

 from pygame.compat import bytes_
 from pygame._arraysurfarray import blit_array
 import numpy
-
+from numpy import array as numpy_array
 
 def array2d (surface):
     """pygame.numpyarray.array2d (Surface): return array
     the array (see the Surface.lock - lock the Surface memory for pixel
     access method).
     """
-    bpp = surface.get_bytesize ()
-    if bpp == 3 or bpp < 1 or bpp > 4:
-        raise ValueError("unsupported bit depth for 2D reference array")
 
-    typecode = (numpy.uint8, numpy.uint16, None, numpy.int32)[bpp - 1]
-    array = numpy.frombuffer (surface.get_buffer (), typecode)
-    array.shape = surface.get_height (), surface.get_pitch () / bpp
-
-    # Padding correction for certain depth due to padding bytes.
-    array = array[:,:surface.get_width ()]
-    array = numpy.transpose (array)
-    return array
+    return numpy_array(surface.get_view('2'), copy=False)
 
 def array3d (surface):
     """pygame.numpyarray.array3d (Surface): return array
     the array (see the Surface.lock - lock the Surface memory for pixel
     access method).
     """
-    bpp = surface.get_bytesize ()
-    if bpp < 3 or bpp > 4:
-        raise ValueError("unsupported bit depth for 3D reference array")
-    lilendian = pygame.get_sdl_byteorder () == pygame.LIL_ENDIAN
 
-    start = 0
-    step = 0
-
-    # Check for RGB or BGR surface.
-    shifts = surface.get_shifts ()
-    if shifts[0] == 16 and shifts[1] == 8 and shifts[2] == 0:
-        # RGB 
-        if lilendian:
-            start = 2
-            step = -1
-        else:
-            start = 0
-            step = 1
-    elif shifts[2] == 16 and shifts[1] == 8 and shifts[0] == 0:
-        # BGR
-        if lilendian:
-            start = 0
-            step = 1
-        else:
-            start = 2
-            step = -1
-    else:
-        raise ValueError("unsupported colormasks for 3D reference array")
-
-    if bpp == 4 and not lilendian:
-        start += 1
-
-    array = numpy.ndarray \
-            (shape=(surface.get_width (), surface.get_height (), 3),
-             dtype=numpy.uint8, buffer=surface.get_buffer (),
-             offset=start, strides=(bpp, surface.get_pitch (),step))
-    return array
+    return numpy_array(surface.get_view('3'), copy=False)
 
 def array_alpha (surface):
     """pygame.numpyarray.array_alpha (Surface): return array
     The Surface this array references will remain locked for the
     lifetime of the array.
     """
-    if surface.get_bytesize () != 4:
-        raise ValueError("unsupported bit depth for alpha reference array")
-    lilendian = pygame.get_sdl_byteorder () == pygame.LIL_ENDIAN
 
-    # ARGB surface.
-    start = 0
-    
-    if surface.get_shifts ()[3] == 24 and lilendian:
-        # RGBA surface.
-        start = 3
-    elif surface.get_shifts ()[3] == 0 and not lilendian:
-        start = 3
-    else:
-        raise ValueError("unsupported colormasks for alpha reference array")
+    return numpy.array (surface.get_view('a'), copy=False)
 
-    array = numpy.ndarray \
-            (shape=(surface.get_width (), surface.get_height ()),
-             dtype=numpy.uint8, buffer=surface.get_buffer (),
-             offset=start, strides=(4, surface.get_pitch ()))
-    return array
+def pixels_red (surface):
+    """pygame.surfarray.pixels_red (Surface): return array
+
+    Reference pixel red into a 2d array.
+
+    Create a new 2D array that directly references the red values
+    in a Surface. Any changes to the array will affect the pixels
+    in the Surface. This is a fast operation since no data is copied.
+
+    This can only work on 24-bit or 32-bit Surfaces.
+
+    The Surface this array references will remain locked for the
+    lifetime of the array.
+    """
+
+    return numpy.array (surface.get_view('r'), copy=False)
+
+def pixels_green (surface):
+    """pygame.surfarray.pixels_green (Surface): return array
+
+    Reference pixel green into a 2d array.
+
+    Create a new 2D array that directly references the green values
+    in a Surface. Any changes to the array will affect the pixels
+    in the Surface. This is a fast operation since no data is copied.
+
+    This can only work on 24-bit or 32-bit Surfaces.
+
+    The Surface this array references will remain locked for the
+    lifetime of the array.
+    """
+
+    return numpy.array (surface.get_view('g'), copy=False)
+
+def pixels_blue (surface):
+    """pygame.surfarray.pixels_blue (Surface): return array
+
+    Reference pixel blue into a 2d array.
+
+    Create a new 2D array that directly references the blue values
+    in a Surface. Any changes to the array will affect the pixels
+    in the Surface. This is a fast operation since no data is copied.
+
+    This can only work on 24-bit or 32-bit Surfaces.
+
+    The Surface this array references will remain locked for the
+    lifetime of the array.
+    """
+
+    return numpy.array (surface.get_view('b'), copy=False)
 
 def array_colorkey (surface):
     """pygame.numpyarray.array_colorkey (Surface): return array

lib/surfarray.doc

 the array.
 <END>
 
+pixels_red
+Reference pixel red into a 2d array.
+pygame.surfarray.pixels_red (Surface): return array
+
+Create a new 2D array that directly references the red values
+in a Surface. Any changes to the array will affect the pixels
+in the Surface. This is a fast operation since no data is copied.
+
+This can only work on 24-bit or 32-bit Surfaces.
+
+The Surface this array references will remain locked for the
+lifetime of the array.
+<END>
+
+pixels_green
+Reference pixel green into a 2d array.
+pygame.surfarray.pixels_green (Surface): return array
+
+Create a new 2D array that directly references the green values
+in a Surface. Any changes to the array will affect the pixels
+in the Surface. This is a fast operation since no data is copied.
+
+This can only work on 24-bit or 32-bit Surfaces.
+
+The Surface this array references will remain locked for the
+lifetime of the array.
+<END>
+
+pixels_blue
+Reference pixel blue into a 2d array.
+pygame.surfarray.pixels_blue (Surface): return array
+
+Create a new 2D array that directly references the blue values
+in a Surface. Any changes to the array will affect the pixels
+in the Surface. This is a fast operation since no data is copied.
+
+This can only work on 24-bit or 32-bit Surfaces.
+
+The Surface this array references will remain locked for the
+lifetime of the array.
+<END>
+
 array_colorkey
 Copy the colorkey values into a 2d array
 pygame.surfarray.array_colorkey(Surface): return array
         return numpysf.pixels_alpha (surface)
     raise NotImplementedError("surface arrays are not supported")
 
+def pixels_red (surface):
+    """pygame.surfarray.pixels_red (Surface): return array
+
+    Reference pixel red into a 2d array.
+
+    Create a new 2D array that directly references the red values
+    in a Surface. Any changes to the array will affect the pixels
+    in the Surface. This is a fast operation since no data is copied.
+
+    This can only work on 24-bit or 32-bit Surfaces.
+
+    The Surface this array references will remain locked for the
+    lifetime of the array.
+    """
+    if __arraytype == "numpy":
+        return numpysf.pixels_red (surface)
+    raise NotImplementedError("surface arrays are not supported")
+
+def pixels_green (surface):
+    """pygame.surfarray.pixels_green (Surface): return array
+
+    Reference pixel green into a 2d array.
+
+    Create a new 2D array that directly references the green values
+    in a Surface. Any changes to the array will affect the pixels
+    in the Surface. This is a fast operation since no data is copied.
+
+    This can only work on 24-bit or 32-bit Surfaces.
+
+    The Surface this array references will remain locked for the
+    lifetime of the array.
+    """
+    if __arraytype == "numpy":
+        return numpysf.pixels_green (surface)
+    raise NotImplementedError("surface arrays are not supported")
+
+def pixels_blue (surface):
+    """pygame.surfarray.pixels_blue (Surface): return array
+
+    Reference pixel blue into a 2d array.
+
+    Create a new 2D array that directly references the blue values
+    in a Surface. Any changes to the array will affect the pixels
+    in the Surface. This is a fast operation since no data is copied.
+
+    This can only work on 24-bit or 32-bit Surfaces.
+
+    The Surface this array references will remain locked for the
+    lifetime of the array.
+    """
+    if __arraytype == "numpy":
+        return numpysf.pixels_blue (surface)
+    raise NotImplementedError("surface arrays are not supported")
+
 def array_colorkey (surface):
     """pygame.surfarray.array_colorkey (Surface): return array
 

src/_arraysurfarray.c

                 PyErr_SetString(PyExc_ValueError,
                                 "no C-struct array interface");
         }
-        return 0;
+        return -1;
     }
 
 #if PG_HAVE_COBJECT
         inter->two != 2  ) {
         Py_DECREF(cobj);
         PyErr_SetString(PyExc_ValueError, "invalid array interface");
-        return 0;
+        return -1;
     }
 
     *cobj_p = cobj;
     *inter_p = inter;
-    return 1;
+    return 0;
 }
 
 /*macros used to blit arrays*/
     surf = PySurface_AsSurface(surfobj);
     format = surf->format;
     
-    if (!_get_array_interface(arrayobj, &cobj, &inter)) {
+    if (_get_array_interface(arrayobj, &cobj, &inter)) {
         return 0;
     }
 
+/*
+  pygame - Python Game Library
+  Module adapted from bufferproxy.c, Copyright (C) 2007  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
+
+*/
+
+/*
+  This module exports an object which provides a C level interface to
+  another object's buffer. For now only an array structure -
+  __array_struct__ - interface is exposed. When memoryview is ready it
+  will replace the View object.
+ */
+
+#define NO_PYGAME_C_API
+#include "pygame.h"
+#include "pgcompat.h"
+#include "pgview.h"
+
+typedef struct {
+    PyObject_HEAD
+    PyObject *capsule;            /* Wrapped array struct            */
+    PyObject *parent;             /* Object responsible for the view */
+    PgView_Destructor destructor; /* Optional release callback       */
+    PyObject *pydestructor;       /* Python callable destructor      */
+    PyObject *weakrefs;           /* There can be reference cycles   */
+} PgViewObject;
+
+/**
+ * Helper functions.
+ */
+static void
+_view_default_destructor(PyObject *view) {
+    PgViewObject *v = (PgViewObject *)view;
+    PyObject *rvalue;
+
+    if (v->pydestructor) {
+        rvalue = PyObject_CallFunctionObjArgs(v->pydestructor,
+                                              v->capsule,
+                                              v->parent,
+                                              0);
+        PyErr_Clear();
+        Py_XDECREF(rvalue);
+    }
+}
+
+static PyObject *
+_view_new_from_type(PyTypeObject *type,
+                    PyObject *capsule,
+                    PyObject *parent,
+                    PgView_Destructor destructor,
+                    PyObject *pydestructor)
+{
+    PgViewObject *self = (PgViewObject *)type->tp_alloc(type, 0);
+
+    if (!self) {
+        return 0;
+    }
+    self->weakrefs = 0;
+    Py_INCREF(capsule);
+    self->capsule = capsule;
+    if (!parent) {
+        parent = Py_None;
+    }
+    Py_INCREF(parent);
+    self->parent = parent;
+    self->destructor = destructor ? destructor : _view_default_destructor;
+    Py_XINCREF(pydestructor);
+    self->pydestructor = pydestructor;
+    return (PyObject *)self;
+}
+
+/**
+ * Creates a new PgViewObject.
+ */
+static PyObject *
+_view_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+    PyObject *capsule;
+    PyObject *parent = 0;
+    PyObject *pydestructor = 0;
+    char *keywords[] = {"capsule", "parent", "destructor", 0};
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OO:View", keywords,
+                                     &capsule, &parent, &pydestructor)) {
+        return 0;
+    }
+    if (pydestructor == Py_None) {
+        pydestructor = 0;
+    }
+    return _view_new_from_type(type,
+                               capsule,
+                               parent,
+                               0,
+                               pydestructor);
+}
+
+/**
+ * Deallocates the PgViewObject and its members.
+ */
+static void
+_view_dealloc(PgViewObject *self)
+{
+    PgView_Destructor destructor = self->destructor;
+
+    /* Guard against recursion */
+    self->destructor = 0;
+    if (!destructor) {
+        return;
+    }
+
+    destructor((PyObject *)self);
+    Py_DECREF(self->capsule);
+    Py_DECREF(self->parent);
+    Py_XDECREF(self->pydestructor);
+    if (self->weakrefs) {
+        PyObject_ClearWeakRefs((PyObject *)self);
+    }
+    Py_TYPE(self)->tp_free(self);
+}
+
+/**** Getter and setter access ****/
+
+static PyObject *
+_view_get_arraystruct(PgViewObject *self, PyObject *closure)
+{
+    Py_INCREF(self->capsule);
+    return self->capsule;
+}
+
+static PyObject *
+_view_get_parent(PgViewObject *self, PyObject *closure)
+{
+    if (!self->parent) {
+        Py_RETURN_NONE;
+    }
+    Py_INCREF(self->parent);
+    return self->parent;
+}
+
+/**** Methods ****/
+
+/**
+ * Representation method.
+ */
+static PyObject *
+_view_repr (PgViewObject *self)
+{
+    return Text_FromFormat("<%s(%p)>", Py_TYPE(self)->tp_name, self->capsule);
+}
+
+/**
+ * Getters and setters for the PgViewObject.
+ */
+static PyGetSetDef _view_getsets[] =
+{
+    {"__array_struct__", (getter)_view_get_arraystruct, 0, 0, 0},
+    {"parent", (getter)_view_get_parent, 0, 0, 0},
+    {0, 0, 0, 0, 0}
+};
+
+
+static PyTypeObject PgView_Type =
+{
+    TYPE_HEAD (NULL, 0)
+    "pygame._view.View",        /* tp_name */
+    sizeof (PgViewObject),      /* tp_basicsize */
+    0,                          /* tp_itemsize */
+    (destructor)_view_dealloc,  /* tp_dealloc */
+    0,                          /* tp_print */
+    0,                          /* tp_getattr */
+    0,                          /* tp_setattr */
+    0,                          /* tp_compare */
+    (reprfunc)_view_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_HAVE_WEAKREFS | Py_TPFLAGS_BASETYPE |
+     Py_TPFLAGS_HAVE_CLASS),
+    "Object view as an array struct\n",
+    0,                          /* tp_traverse */
+    0,                          /* tp_clear */
+    0,                          /* tp_richcompare */
+    offsetof(PgViewObject, weakrefs),    /* tp_weaklistoffset */
+    0,                          /* tp_iter */
+    0,                          /* tp_iternext */
+    0,                          /* tp_methods */
+    0,                          /* tp_members */
+    _view_getsets,              /* tp_getset */
+    0,                          /* tp_base */
+    0,                          /* tp_dict */
+    0,                          /* tp_descr_get */
+    0,                          /* tp_descr_set */
+    0,                          /* tp_dictoffset */
+    0,                          /* tp_init */
+    0,                          /* tp_alloc */
+    _view_new,                  /* tp_new */
+    0,                          /* tp_free */
+#ifndef __SYMBIAN32__
+    0,                          /* tp_is_gc */
+    0,                          /* tp_bases */
+    0,                          /* tp_mro */
+    0,                          /* tp_cache */
+    0,                          /* tp_subclasses */
+    0,                          /* tp_weaklist */
+    0                           /* tp_del */
+#endif
+};
+
+
+/**** Public C api ***/
+
+static PyObject *
+PgView_New(PyObject *capsule,
+           PyObject *parent,
+           PgView_Destructor destructor)
+{
+    if (!capsule) {
+        PyErr_SetString(PyExc_TypeError, "the capsule argument is required");
+        return 0;
+    }
+    return _view_new_from_type(&PgView_Type, capsule, parent, destructor, 0);
+}
+
+static PyObject *
+PgView_GetCapsule(PyObject *view)
+{
+    PyObject *capsule = ((PgViewObject *)view)->capsule;
+
+    Py_INCREF(capsule);
+    return capsule;
+}
+
+static PyObject *
+PgView_GetParent(PyObject *view)
+{
+    PyObject *parent = ((PgViewObject *)view)->parent;
+
+    Py_INCREF(parent);
+    return parent;
+}
+
+/*DOC*/ static char _view_doc[] =
+/*DOC*/    "exports View, a generic wrapper object for an array "
+/*DOC*/    "struct capsule";
+
+MODINIT_DEFINE(_view)
+{
+    PyObject *module;
+    PyObject *apiobj;
+    static void* c_api[PYGAMEAPI_VIEW_NUMSLOTS];
+
+#if PY3
+    static struct PyModuleDef _module = {
+        PyModuleDef_HEAD_INIT,
+        "_view",
+        _view_doc,
+        -1,
+        NULL, NULL, NULL, NULL, NULL
+    };
+#endif
+
+    if (PyType_Ready(&PgView_Type) < 0) {
+        MODINIT_ERROR;
+    }
+
+    /* create the module */
+#if PY3
+    module = PyModule_Create(&_module);
+#else
+    module = Py_InitModule3(MODPREFIX "_view", NULL, _view_doc);
+#endif
+
+    Py_INCREF(&PgView_Type);
+    if (PyModule_AddObject(module, "View", (PyObject *)&PgView_Type)) {
+        Py_DECREF(&PgView_Type);
+        DECREF_MOD(module);
+        MODINIT_ERROR;
+    }
+#if PYGAMEAPI_VIEW_NUMSLOTS != 4
+#error export slot count mismatch
+#endif
+    c_api[0] = &PgView_Type;
+    c_api[1] = PgView_New;
+    c_api[2] = PgView_GetCapsule;
+    c_api[3] = PgView_GetParent;
+    apiobj = encapsulate_api(c_api, "_view");
+    if (apiobj == NULL) {
+        DECREF_MOD(module);
+        MODINIT_ERROR;
+    }
+    if (PyModule_AddObject(module, PYGAMEAPI_LOCAL_ENTRY, apiobj)) {
+        Py_DECREF(apiobj);
+        DECREF_MOD(module);
+        MODINIT_ERROR;
+    }
+    MODINIT_RETURN(module);
+}

src/doc/math_doc.h

 
 #define DOC_ ""
 
-#define DOC_ ""
-
 
 
 /* Docs in a comments... slightly easier to read. */
 
 
 
-
- 
-
-
-
 */
 

src/doc/midi_doc.h

 
 #define DOC_PYGAMEMIDITIME "pygame.midi.time(): return time\nreturns the current time in ms of the PortMidi timer"
 
-#define DOC_ ""
-
 
 
 /* Docs in a comments... slightly easier to read. */
 
 
 
-
- 
-
-
-
 */
 

src/doc/mixer_doc.h

 
 #define DOC_PYGAMEMIXERGETBUSY "pygame.mixer.get_busy(): return bool\ntest if any sound is being mixed"
 
-#define DOC_PYGAMEMIXERSOUND "pygame.mixer.Sound(filename): return Sound\npygame.mixer.Sound(file=filename): return Sound\npygame.mixer.Sound(buffer): return Sound\npygame.mixer.Sound(buffer=buffer): return Sound\npygame.mixer.Sound(object): return Sound\npygame.mixer.Sound(file=object): return Sound\nCreate a new Sound object from a file or buffer object"
+#define DOC_PYGAMEMIXERSOUND "pygame.mixer.Sound(filename): return Sound\npygame.mixer.Sound(file=filename): return Sound\npygame.mixer.Sound(buffer): return Sound\npygame.mixer.Sound(buffer=buffer): return Sound\npygame.mixer.Sound(object): return Sound\npygame.mixer.Sound(file=object): return Sound\npygame.mixer.Sound(array=object): return Sound\nCreate a new Sound object from a file or buffer object"
 
 #define DOC_SOUNDPLAY "Sound.play(loops=0, maxtime=0, fade_ms=0): return Channel\nbegin sound playback"
 
 pygame.mixer.Sound(buffer=buffer): return Sound
 pygame.mixer.Sound(object): return Sound
 pygame.mixer.Sound(file=object): return Sound
+pygame.mixer.Sound(array=object): return Sound
 Create a new Sound object from a file or buffer object
 
 

src/doc/surface_doc.h

 
 #define DOC_SURFACEGETBOUNDINGRECT "Surface.get_bounding_rect(min_alpha = 1): return Rect\nfind the smallest rect containing data"
 
+#define DOC_SURFACEGETVIEW "Surface.get_view(kind='2'): return <view>\nreturn a view of a surface's pixel data."
+
 #define DOC_SURFACEGETBUFFER "Surface.get_buffer(): return BufferProxy\nacquires a buffer object for the pixels of the Surface."
 
 
 
 
 
+Surface.get_view
+ Surface.get_view(kind='2'): return <view>
+return a view of a surface's pixel data.
+
+
+
 Surface.get_buffer
  Surface.get_buffer(): return BufferProxy
 acquires a buffer object for the pixels of the Surface.

src/doc/surfarray_doc.h

 
 #define DOC_PYGAMESURFARRAYPIXELSALPHA "pygame.surfarray.pixels_alpha(Surface): return array\nReference pixel alphas into a 2d array"
 
+#define DOC_PYGAMESURFARRAYPIXELSRED "pygame.surfarray.pixels_red (Surface): return array\nReference pixel red into a 2d array."
+
+#define DOC_PYGAMESURFARRAYPIXELSGREEN "pygame.surfarray.pixels_green (Surface): return array\nReference pixel green into a 2d array."
+
+#define DOC_PYGAMESURFARRAYPIXELSBLUE "pygame.surfarray.pixels_blue (Surface): return array\nReference pixel blue into a 2d array."
+
 #define DOC_PYGAMESURFARRAYARRAYCOLORKEY "pygame.surfarray.array_colorkey(Surface): return array\nCopy the colorkey values into a 2d array"
 
 #define DOC_PYGAMESURFARRAYMAKESURFACE "pygame.surfarray.make_surface(array): return Surface\nCopy an array to a new surface"
 
 
 
+pygame.surfarray.pixels_red
+ pygame.surfarray.pixels_red (Surface): return array
+Reference pixel red into a 2d array.
+
+
+
+pygame.surfarray.pixels_green
+ pygame.surfarray.pixels_green (Surface): return array
+Reference pixel green into a 2d array.
+
+
+
+pygame.surfarray.pixels_blue
+ pygame.surfarray.pixels_blue (Surface): return array
+Reference pixel blue into a 2d array.
+
+
+
 pygame.surfarray.array_colorkey
  pygame.surfarray.array_colorkey(Surface): return array
 Copy the colorkey values into a 2d array
 #include "pgcompat.h"
 #include "doc/mixer_doc.h"
 #include "mixer.h"
+#include "pgarrinter.h"
 
+#define EXPORT_BUFFER 0 /* Don't export a buffer interface until the new */
+                        /* buffer protocol is formalized (Python 3.3 ?). */
+
+#if !defined(EXPORT_BUFFER)
+#define EXPORT_BUFFER HAVE_NEW_BUFPROTO
+#endif
+
+/* The SDL audio format constants are not defined for anything larger
+   than 2 byte samples. Define our own. Low two bytes gives sample
+   size in bytes. Higher bytes are flags.
+*/
+typedef Uint32 PG_sample_format_t;
+const PG_sample_format_t PG_SAMPLE_SIGNED = 0x10000u;
+const PG_sample_format_t PG_SAMPLE_NATIVE_ENDIAN = 0x20000u;
+#if SDL_BYTEORDER == SDL_LIL_ENDIAN
+const PG_sample_format_t PG_SAMPLE_LITTLE_ENDIAN = 0x20000u;
+const PG_sample_format_t PG_SAMPLE_BIG_ENDIAN = 0;
+#else
+const PG_sample_format_t PG_SAMPLE_LITTLE_ENDIAN = 0;
+const PG_sample_format_t PG_SAMPLE_BIG_ENDIAN = 0x20000u;
+#endif
+#define PG_SAMPLE_SIZE(sf) ((sf) & 0x0ffffu)
+#define PG_IS_SAMPLE_SIGNED(sf) ((sf) & PG_SAMPLE_SIGNED != 0)
+#define PG_IS_SAMPLE_NATIVE_ENDIAN(sf) ((sf) & PG_SAMPLE_NATIVE_ENDIAN != 0)
+#define PG_IS_SAMPLE_LITTLE_ENDIAN(sf) \
+    ((sf) & PG_SAMPLE_LITTLE_ENDIAN == PG_SAMPLE_LITTLE_ENDIAN)
+#define PG_IS_SAMPLE_BIG_ENDIAN(sf) \
+    ((sf) & PG_SAMPLE_BIG_ENDIAN == PG_SAMPLE_BIG_ENDIAN)
+#if (char)-1l > 0
+const PG_sample_format_t PG_SAMPLE_CHAR_SIGN = 0;
+#else
+const PG_sample_format_t PG_SAMPLE_CHAR_SIGN = 0x10000;
+#endif
 
 /* Since they are documented, the default init values are defined here
    rather than taken from SDL_mixer. It also means that the default
 Mix_Music** current_music;
 Mix_Music** queue_music;
 
+static int
+_format_itemsize(Uint16 format)
+{
+    int size = -1;
+
+    switch (format) {
+
+    case AUDIO_U8:
+    case AUDIO_S8:
+        size = 1;
+        break;
+
+    case AUDIO_U16LSB:
+    case AUDIO_U16MSB:
+    case AUDIO_S16LSB:
+    case AUDIO_S16MSB:
+        size = 2;
+        break;
+
+    default:
+        PyErr_Format(PyExc_SystemError,
+                     "Pygame bug (mixer.Sound): unknown mixer format %d",
+                     (int)format);
+    }
+    return size;
+}
+
+static int
+_get_array_interface(PyObject *obj,
+                     PyObject **cobj_p,
+                     PyArrayInterface **inter_p)
+{
+    PyObject *cobj = PyObject_GetAttrString(obj, "__array_struct__");
+    PyArrayInterface *inter = NULL;
+
+    if (cobj == NULL) {
+        if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
+                PyErr_Clear();
+                PyErr_SetString(PyExc_ValueError,
+                                "no C-struct array interface");
+        }
+        return -1;
+    }
+
+#if PG_HAVE_COBJECT
+    if (PyCObject_Check(cobj)) {
+        inter = (PyArrayInterface *)PyCObject_AsVoidPtr(cobj);
+    }
+#endif
+#if PG_HAVE_CAPSULE
+    if (PyCapsule_IsValid(cobj, NULL)) {
+        inter = (PyArrayInterface *)PyCapsule_GetPointer(cobj, NULL);
+    }
+#endif
+    if (inter == NULL ||   /* conditional or */
+        inter->two != 2  ) {
+        Py_DECREF(cobj);
+        PyErr_SetString(PyExc_ValueError, "invalid array interface");
+        return -1;
+    }
+
+    *cobj_p = cobj;
+    *inter_p = inter;
+    return 0;
+}
+
+static PG_sample_format_t
+_format_inter_to_audio(PyArrayInterface *inter)
+{
+    PG_sample_format_t format = 0;
+    int itemsize = inter->itemsize;
+
+    switch (inter->typekind) {
+        case 'u':
+        break;
+
+        case 'i':
+        format |= PG_SAMPLE_SIGNED;
+        break;
+
+        default:
+        PyErr_Format(PyExc_ValueError,
+                     "Array has unsupported item format '%c'",
+                     (int)inter->typekind);
+        return 0;
+    }
+    if (itemsize <= 0 || itemsize > 0xFFFFl) {
+        PyErr_Format(PyExc_ValueError,
+                     "Array has unsupported integer size %d", itemsize);
+        return 0;
+    }
+    format += itemsize;
+    format |= inter->flags & PAI_NOTSWAPPED ? PG_SAMPLE_NATIVE_ENDIAN : 0;
+    return format;
+}
+
+#if HAVE_NEW_BUFPROTO
+static PG_sample_format_t
+_format_view_to_audio(Py_buffer *view)
+{
+    int fstr_len;
+    int native_size = 0;
+    int index = 0;
+    PG_sample_format_t format = 0;
+
+    if (!view->format) {
+        /* Assume unsigned byte */
+        return (PG_sample_format_t)sizeof(unsigned char);
+    }
+    fstr_len = strlen(view->format);
+    if (fstr_len < 1 || fstr_len > 2) {
+        RAISE(PyExc_ValueError, "Array has unsupported item format");
+        return 0;
+    }
+    if (fstr_len == 1) {
+        format |= PG_SAMPLE_NATIVE_ENDIAN;
+        native_size = 1;
+    }
+    else {
+        switch (view->format[index]) {
+
+        case '@':
+            native_size = 1;
+            format |= PG_SAMPLE_NATIVE_ENDIAN;
+            break;
+
+        case '=':
+            format |= PG_SAMPLE_NATIVE_ENDIAN;
+            break;
+
+        case '<':
+            format |= PG_SAMPLE_LITTLE_ENDIAN;
+            break;
+
+        case '>':
+        case '!':
+            format |= PG_SAMPLE_BIG_ENDIAN;
+            break;
+
+        default:
+            RAISE(PyExc_ValueError, "Array has unsupported item format");
+            return 0;
+        }
+        ++index;
+    }
+    switch (view->format[index]) {
+
+    case 'c':
+        format |= PG_SAMPLE_CHAR_SIGN;
+        format += native_size ? sizeof(char) : 1;
+        break;
+
+    case 'b':
+        format |= PG_SAMPLE_SIGNED;
+        format += native_size ? sizeof(signed char) : 1;
+        break;
+
+    case 'B':
+        format += native_size ? sizeof(unsigned char) : 1;
+        break;
+
+    case 'h':
+        format |= PG_SAMPLE_SIGNED;
+        format += native_size ? sizeof(short int) : 2;
+        break;
+
+    case 'H':
+        format += native_size ? sizeof(unsigned short int) : 2;
+        break;
+
+    case 'l':
+        format |= PG_SAMPLE_SIGNED;
+        format += native_size ? sizeof(long int) : 4;
+        break;
+
+    case 'L':
+        format += native_size ? sizeof(unsigned long int) : 4;
+        break;
+
+    case 'q':
+        format |= PG_SAMPLE_SIGNED;
+        format += native_size ? sizeof(long long int) : 8;
+        break;
+
+    case 'Q':
+        format += native_size ? sizeof(unsigned long long int) : 8;
+        break;
+
+    default:
+        PyErr_Format(PyExc_ValueError,
+                     "Array has unsupported item format '%s'",
+                     view->format);
+        return 0;
+    }
+    if (view->itemsize && PG_SAMPLE_SIZE(format) != view->itemsize) {
+        PyErr_Format(PyExc_ValueError,
+                     "Array item size %d does not match format '%s'",
+                     (int)view->itemsize, view->format);
+        return 0;
+    }
+    return format;
+}
+#endif
 
 static void
 endsound_callback (int channel)
 {
     if (channeldata)
     {
-	if (channeldata[channel].endevent && SDL_WasInit (SDL_INIT_VIDEO))
-	{
-	    SDL_Event e;
-	    memset (&e, 0, sizeof(e));
-	    e.type = channeldata[channel].endevent;
+    if (channeldata[channel].endevent && SDL_WasInit (SDL_INIT_VIDEO))
+    {
+        SDL_Event e;
+        memset (&e, 0, sizeof(e));
+        e.type = channeldata[channel].endevent;
             if (e.type >= SDL_USEREVENT && e.type < SDL_NUMEVENTS)
                 e.user.code = channel;
-	    SDL_PushEvent (&e);
-	}
-	if (channeldata[channel].queue)
-	{
-	    int channelnum;
-	    Mix_Chunk* sound = PySound_AsChunk (channeldata[channel].queue);
-	    Py_XDECREF (channeldata[channel].sound);
-	    channeldata[channel].sound = channeldata[channel].queue;
-	    channeldata[channel].queue = NULL;
-	    channelnum = Mix_PlayChannelTimed (channel, sound, 0, -1);
-	    if (channelnum != -1)
-	    	Mix_GroupChannel (channelnum, (intptr_t)sound);
-	}
-	else
-	{
-	    Py_XDECREF (channeldata[channel].sound);
-	    channeldata[channel].sound = NULL;
-	}
+        SDL_PushEvent (&e);
+    }
+    if (channeldata[channel].queue)
+    {
+        int channelnum;
+        Mix_Chunk* sound = PySound_AsChunk (channeldata[channel].queue);
+        Py_XDECREF (channeldata[channel].sound);
+        channeldata[channel].sound = channeldata[channel].queue;
+        channeldata[channel].queue = NULL;
+        channelnum = Mix_PlayChannelTimed (channel, sound, 0, -1);
+        if (channelnum != -1)
+            Mix_GroupChannel (channelnum, (intptr_t)sound);
+    }
+    else
+    {
+        Py_XDECREF (channeldata[channel].sound);
+        channeldata[channel].sound = NULL;
+    }
     }
 }
 
     int i;
 
     if (!freq) {
-	freq = request_frequency;
+    freq = request_frequency;
     }
     if (!size) {
-	size = request_size;
+    size = request_size;
     }
     if (!stereo) {
-	stereo = request_stereo;
+    stereo = request_stereo;
     }
     if (!chunk) {
-	chunk = request_chunksize;
+    chunk = request_chunksize;
     }
     if (stereo >= 2)
         stereo = 2;
 
     switch (size) {
     case 8:
-	fmt = AUDIO_U8;
-	break;
+    fmt = AUDIO_U8;
+    break;
     case -8:
-	fmt = AUDIO_S8;
-	break;
+    fmt = AUDIO_S8;
+    break;
     case 16:
-	fmt = AUDIO_U16SYS;
-	break;
+    fmt = AUDIO_U16SYS;
+    break;
     case -16:
-	fmt = AUDIO_S16SYS;
-	break;
+    fmt = AUDIO_S16SYS;
+    break;
     default:
-	PyErr_Format(PyExc_ValueError, "unsupported size %i", size);
-	return NULL;
+    PyErr_Format(PyExc_ValueError, "unsupported size %i", size);
+    return NULL;
     }
 
     /* printf("size:%d:\n", size); */
         /* A bug in sdl_mixer where the stereo is reversed for 8 bit.
            So we use this CPU hogging effect to reverse it for us.
            Hopefully this bug is fixed in SDL_mixer 1.2.9
-        printf("MIX_MAJOR_VERSION :%d: MIX_MINOR_VERSION :%d: MIX_PATCHLEVEL :%d: \n", 
+        printf("MIX_MAJOR_VERSION :%d: MIX_MINOR_VERSION :%d: MIX_PATCHLEVEL :%d: \n",
                MIX_MAJOR_VERSION, MIX_MINOR_VERSION, MIX_PATCHLEVEL);
         */
 
 
     static char *kwids[] = {"frequency", "size", "channels", "buffer", NULL};
     if (!PyArg_ParseTupleAndKeywords (args, keywds, "|iiii", kwids,
-				      &freq, &size, &stereo, &chunk)) {
-	return NULL;
+                      &freq, &size, &stereo, &chunk)) {
+    return NULL;
     }
     result = _init (freq, size, stereo, chunk);
     if (!result)
     request_stereo = 0;
     request_chunksize = 0;
     if (!PyArg_ParseTupleAndKeywords (args, keywds, "|iiii", kwids,
-				      &request_frequency, &request_size,
-				      &request_stereo, &request_chunksize))
+                      &request_frequency, &request_size,
+                      &request_stereo, &request_chunksize))
         return NULL;
     if (!request_frequency) {
-	request_frequency = PYGAME_MIXER_DEFAULT_FREQUENCY;
+    request_frequency = PYGAME_MIXER_DEFAULT_FREQUENCY;
     }
     if (!request_size) {
-	request_size = PYGAME_MIXER_DEFAULT_SIZE;
+    request_size = PYGAME_MIXER_DEFAULT_SIZE;
     }
     if (!request_stereo) {
-	request_stereo = PYGAME_MIXER_DEFAULT_CHANNELS;
+    request_stereo = PYGAME_MIXER_DEFAULT_CHANNELS;
     }
     if (!request_chunksize) {
-	request_chunksize = PYGAME_MIXER_DEFAULT_CHUNKSIZE;
+    request_chunksize = PYGAME_MIXER_DEFAULT_CHUNKSIZE;
     }
     Py_RETURN_NONE;
 }
     char *kwids[] = { "loops", "maxtime", "fade_ms", NULL };
     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|iii", kwids, &loops, &playtime, &fade_ms))
        return NULL;
-    
+
     if (fade_ms > 0)
-    {	
-    	channelnum = Mix_FadeInChannelTimed (-1, chunk, loops, fade_ms, playtime);    	
+    {
+        channelnum = Mix_FadeInChannelTimed (-1, chunk, loops, fade_ms, playtime);
     }
     else
     {
-    	channelnum = Mix_PlayChannelTimed (-1, chunk, loops, playtime);
+        channelnum = Mix_PlayChannelTimed (-1, chunk, loops, playtime);
     }
     if (channelnum == -1)
         Py_RETURN_NONE;
     return buffer;
 }
 
+#if PY3
+static void
+snd_arraystruct_capsule_destr(PyObject *capsule)
+{
+    PyArrayInterface *inter =
+        (PyArrayInterface *)PyCapsule_GetPointer(capsule, 0);
+
+    PyMem_Free(inter->shape);
+    PyMem_Free(inter);
+
+    /* The context may be NULL in the unlike case PyCapsule_SetContext failed.
+     */
+    Py_XDECREF((PyObject *)PyCapsule_GetContext(capsule));
+}
+#else
+static void
+snd_arraystruct_cobject_destr(void *inter_vp, void *desc_vp)
+{
+    PyArrayInterface *inter = (PyArrayInterface *)inter_vp;
+
+    PyMem_Free(inter->shape);
+    PyMem_Free(inter);
+    Py_DECREF((PyObject *)desc_vp);
+}
+#endif
+
+static PyObject *
+snd_get_arraystruct(PyObject *self, void *closure)
+{
+    Mix_Chunk *chunk = PySound_AsChunk(self);
+    PyArrayInterface *inter;
+    int nd;
+    Py_intptr_t *shape;
+    Py_intptr_t *strides;
+    int freq = 0;
+    int channels;
+    Py_ssize_t len;
+    Uint16 format;
+    PyObject *desc;
+    PyObject *cobj;
+
+    MIXER_INIT_CHECK();
+    desc = Py_BuildValue("sO", "PyArrayInterface Version 3", self);
+    if (!desc) {
+        return NULL;
+    }
+    inter = PyMem_New(PyArrayInterface, 1);
+    if (!inter) {
+        Py_DECREF(desc);
+        return PyErr_NoMemory();
+    }
+    Mix_QuerySpec(&freq, &format, &channels);
+    nd = channels > 1 ? 2 : 1;
+    len = chunk->alen;
+    inter->two = 2;
+    inter->nd = nd;
+    switch (format) {
+        case AUDIO_U8:
+        inter->typekind = 'u';
+        inter->itemsize = 1;
+        break;
+
+        case AUDIO_S8:
+        inter->typekind = 'i';
+        inter->itemsize = 1;
+        break;
+
+        case AUDIO_U16SYS:
+        inter->typekind = 'u';
+        inter->itemsize = 2;
+        break;
+
+        default:
+        inter->typekind = 'i';
+        inter->itemsize = 2;
+        break;
+    }
+    inter->flags = PAI_CONTIGUOUS | PAI_ALIGNED |
+                   PAI_NOTSWAPPED | PAI_WRITEABLE;
+    shape = PyMem_New(Py_intptr_t, 2 * nd);
+    if (!shape) {
+        Py_DECREF(desc);
+        PyMem_Free(inter);
+        return PyErr_NoMemory();
+    }
+    strides = shape + nd;
+    strides[0] = inter->itemsize * channels;
+    shape[0] = len / strides[0];
+    if (nd == 2) {
+        shape[1] = channels;
+        strides[1] = inter->itemsize;
+    }
+    inter->shape = shape;
+    inter->strides = strides;
+    inter->data = chunk->abuf;
+    inter->descr = 0;
+#if PY3
+    cobj = PyCapsule_New(inter, 0, snd_arraystruct_capsule_destr);
+#else
+    cobj = PyCObject_FromVoidPtrAndDesc(inter,
+                                        desc,
+                                        snd_arraystruct_cobject_destr);
+#endif
+    if (!cobj) {
+        Py_DECREF(desc);
+        PyMem_Free(inter->shape);
+        PyMem_Free(inter);
+        return NULL;
+    }
+#if PY3
+    else if (PyCapsule_SetContext(cobj, desc)) {
+        Py_DECREF(cobj);
+        return NULL;
+    }
+#endif
+    return cobj;
+}
+
 static PyMethodDef sound_methods[] =
 {
     { "play", (PyCFunction) snd_play, METH_VARARGS | METH_KEYWORDS,
     { NULL, NULL, 0, NULL }
 };
 
+static PyGetSetDef sound_getset[] =
+{
+    {"__array_struct__", snd_get_arraystruct, NULL, "Version 3", NULL},
+    {NULL, NULL, NULL, NULL, NULL}
+};
+
+
+/*buffer protocol*/
+
+#if EXPORT_BUFFER
+static int
+snd_buffer_iteminfo(char **format, Py_ssize_t *itemsize, int *channels)
+{
+    static char fmt_AUDIO_U8[] = "B";
+    static char fmt_AUDIO_S8[] = "b";
+    static char fmt_AUDIO_U16SYS[] = "=H";
+    static char fmt_AUDIO_S16SYS[] = "=h";
+    int freq = 0;
+    Uint16 mixer_format = 0;
+
+    Mix_QuerySpec(&freq, &mixer_format, channels);
+
+    switch (mixer_format) {
+        case AUDIO_U8:
+        *format = fmt_AUDIO_U8;
+        *itemsize = 1;
+        return 0;
+
+        case AUDIO_S8:
+        *format = fmt_AUDIO_S8;
+        *itemsize = 1;
+        return 0;
+
+        case AUDIO_U16SYS:
+        *format = fmt_AUDIO_U16SYS;
+        *itemsize = 2;
+        return 0;
+
+        case AUDIO_S16SYS:
+        *format = fmt_AUDIO_S16SYS;
+        *itemsize = 2;
+        return 0;
+    }
+
+    PyErr_Format(PyExc_SystemError,
+                 "Pygame bug (mixer.Sound): unknown mixer format %d",
+                 (int)mixer_format);
+    return -1;
+}
+
+static int
+snd_getbuffer(PyObject *obj, Py_buffer *view, int flags)
+{
+    Mix_Chunk *chunk = PySound_AsChunk(obj);
+    int channels;
+    char *format = '\0';
+    int ndim = 1;
+    Py_ssize_t *shape = 0;
+    Py_ssize_t *strides = 0;
+    Py_ssize_t itemsize = 0;
+    Py_ssize_t samples;
+    int fortran_order = (flags & PyBUF_F_CONTIGUOUS) == PyBUF_F_CONTIGUOUS;
+
+    if (flags != PyBUF_SIMPLE) {
+        if (snd_buffer_iteminfo(&format, &itemsize, &channels)) {
+            return -1;
+        }
+        if ((flags & PyBUF_FORMAT) != PyBUF_FORMAT) {
+            format = 0;
+        }
+        if ((flags & PyBUF_ND) == PyBUF_ND) {
+            ndim = channels > 1 ? 2 : 1;
+            samples = chunk->alen / (itemsize * channels);
+            shape = PyMem_New(Py_ssize_t, 2 * ndim);
+            if (!shape) {
+                PyErr_NoMemory();
+                return -1;
+            }
+            if (fortran_order) {
+                shape[0] = channels;
+                shape[ndim - 1] = samples;
+            }
+            else {
+                shape[ndim - 1] = channels;
+                shape[0] = samples;
+            }
+        }
+        if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES) {
+            strides = shape + ndim;
+            if (fortran_order) {
+                strides[ndim - 1] = itemsize * channels;
+                strides[0] = itemsize;
+            }
+            else {
+                strides[0] = itemsize * channels;
+                strides[ndim - 1] = itemsize;
+            }
+        }
+    }
+
+    if (PyBuffer_FillInfo(view, obj, chunk->abuf, chunk->alen, 0, flags)) {
+        return -1;
+    }
+    if (flags != PyBUF_SIMPLE) {
+        view->format = format;
+        view->ndim = ndim;
+        view->shape = shape;
+        view->strides = strides;
+        view->itemsize = itemsize;
+    }
+    return 0;
+}
+
+static void
+snd_releasebuffer(PyObject *obj, Py_buffer *view)
+{
+    if (view->shape) {
+        PyMem_Free(view->shape);
+        view->shape = 0;
+    }
+}
+
+#if HAVE_OLD_BUFPROTO
+#define snd_getreadbuffer 0
+#define snd_getwritebuffer 0
+#define snd_getsegcount 0
+#define snd_getcharbuffer 0
+#endif
+
+static PyBufferProcs sound_as_buffer[] =
+{
+    {
+#if HAVE_OLD_BUFPROTO
+        snd_getreadbuffer,
+        snd_getwritebuffer,
+        snd_getsegcount,
+        snd_getcharbuffer,
+#endif
+        snd_getbuffer,
+        snd_releasebuffer
+    }
+};
+#else
+#define sound_as_buffer 0
+#endif /* #if EXPORT_BUFFER */
+
 
 /*sound object internals*/
 static void
     (destructor)sound_dealloc,
     0,
     0,
-    0,					/*setattr*/
-    0,					/*compare*/
-    0,					/*repr*/
-    0,					/*as_number*/
-    0,					/*as_sequence*/
-    0,					/*as_mapping*/
-    (hashfunc)NULL, 		/*hash*/
-    (ternaryfunc)NULL,		/*call*/
-    (reprfunc)NULL, 		/*str*/
-    0,                                  /* tp_getattro */
-    0,                                  /* tp_setattro */
-    0,                                  /* tp_as_buffer */
-    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
-    DOC_PYGAMEMIXERSOUND, /* Documentation string */
-    0,					/* tp_traverse */
-    0,					/* tp_clear */
-    0,					/* tp_richcompare */
-    offsetof(PySoundObject, weakreflist),    /* tp_weaklistoffset */
-    0,					/* tp_iter */
-    0,					/* tp_iternext */
-    sound_methods,			        /* tp_methods */
-    0,				        /* tp_members */
-    0,				        /* tp_getset */
-    0,					/* tp_base */
-    0,					/* tp_dict */
-    0,					/* tp_descr_get */
-    0,					/* tp_descr_set */
-    0,					/* tp_dictoffset */
-    sound_init,			/* tp_init */
-    0,					/* tp_alloc */
-    0,	                /* tp_new */
+    0,                          /* setattr */
+    0,                          /* compare */
+    0,                          /* repr */
+    0,                          /* as_number */
+    0,                          /* as_sequence */
+    0,                          /* as_mapping */
+    (hashfunc)NULL,             /* hash */
+    (ternaryfunc)NULL,          /* call */
+    (reprfunc)NULL,             /* str */
+    0,                          /* tp_getattro */
+    0,                          /* tp_setattro */
+    sound_as_buffer,            /* tp_as_buffer */
+    (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
+    Py_TPFLAGS_HAVE_NEWBUFFER), /* tp_flags */
+    DOC_PYGAMEMIXERSOUND,       /* Documentation string */
+    0,                          /* tp_traverse */
+    0,                          /* tp_clear */
+    0,                          /* tp_richcompare */
+    offsetof(PySoundObject, weakreflist),
+                                /* tp_weaklistoffset */
+    0,                          /* tp_iter */
+    0,                          /* tp_iternext */
+    sound_methods,              /* tp_methods */
+    0,                          /* tp_members */
+    sound_getset,               /* tp_getset */
+    0,                          /* tp_base */
+    0,                          /* tp_dict */
+    0,                          /* tp_descr_get */
+    0,                          /* tp_descr_set */
+    0,                          /* tp_dictoffset */
+    sound_init,                 /* tp_init */
+    0,                          /* tp_alloc */
+    0,                          /* tp_new */
 };
 
-//PyType_GenericNew,	                /* tp_new */
+//PyType_GenericNew,                    /* tp_new */
 
 /* channel object methods */
 static PyObject*
     int loops = 0, playtime = -1, fade_ms = 0;
 
     char *kwids[] = { "Sound", "loops", "maxtime", "fade_ms", NULL };
-    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|iii", kwids, &PySound_Type, &sound, 
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|iii", kwids, &PySound_Type, &sound,
                                      &loops, &playtime, &fade_ms))
        return NULL;
     chunk = PySound_AsChunk (sound);
 
         if(!Mix_SetPanning(channelnum, left, right)) {
             return RAISE (PyExc_SDLError, Mix_GetError());
-        } 
+        }
     }
     else
     {
     0,                          /* tp_clear */
     0,                          /* tp_richcompare */
     0,                          /* tp_weaklistoffset */
-    0,	                        /* tp_iter */
+    0,                          /* tp_iter */
     0,                          /* tp_iternext */
     channel_methods,            /* tp_methods */
     0,                          /* tp_members */
     0,                          /* tp_descr_set */
     0,                          /* tp_dictoffset */
     0,                          /* tp_init */
-    0,				/* tp_alloc */
-    0,			        /* tp_new */
+    0,                          /* tp_alloc */
+    0,                          /* tp_new */
 };
 
 /*mixer module methods*/
 }
 
 static int
+_chunk_from_buf(const void *buf, Py_ssize_t len, Mix_Chunk **chunk, Uint8 **mem)
+{
+    Uint8 *m = (Uint8 *)PyMem_Malloc((size_t)len);
+
+    if (!m)
+    {
+        PyErr_NoMemory();
+        return -1;
+    }
+    *chunk = Mix_QuickLoad_RAW(m, (Uint32)len);
+    if (!*chunk) {
+        PyMem_Free(m);
+        PyErr_NoMemory();
+        return -1;
+    }
+    memcpy(m, (Uint8 *)buf, (size_t)len);
+    *mem = m;
+    return 0;
+}
+
+static int
+_chunk_from_array(void *buf, PG_sample_format_t view_format, int ndim,
+                  Py_ssize_t *shape, Py_ssize_t *strides,
+                  Mix_Chunk **chunk, Uint8 **mem)
+{
+    /* TODO: This is taken from _numericsndarray without additions.
+     * So this should be extended to properly handle integer sign
+     * and byte order. These changes will not be backward compatible.
+     */
+    int freq;
+    Uint16 format;
+    int channels;
+    int itemsize;
+    int view_itemsize = PG_SAMPLE_SIZE(view_format);
+    Uint8 *src, *dst;
+    Py_ssize_t memsize;
+    Py_ssize_t loop1, loop2, step1, step2, length, length2=0;
+
+    if (!Mix_QuerySpec(&freq, &format, &channels)) {
+        RAISE(PyExc_SDLError, "Mixer not initialized");
+        return -1;
+    }
+
+    /* Check for compatible values.
+     */
+    if (channels == 1) {
+        if (ndim != 1) {
+            RAISE(PyExc_ValueError,
+                 "Array must be 1-dimensional for mono mixer");
+            return -1;
+        }
+    }
+    else {
+        if (ndim != 2) {
+            RAISE(PyExc_ValueError,
+                  "Array must be 2-dimensional for stereo mixer");
+            return -1;
+        }
+        if (shape[1] != channels) {
+            RAISE(PyExc_ValueError,
+                  "Array depth must match number of mixer channels");
+            return -1;
+        }
+    }
+    itemsize = _format_itemsize(format);
+    /*
+    printf("!! itemsize: %d\n", itemsize);
+    */
+    if (itemsize < 0) {
+        return -1;
+    }
+    if (view_itemsize != 1 && view_itemsize != 2 && view_itemsize != 4) {
+        PyErr_Format(PyExc_ValueError,
+                     "Unsupported integer size %d", view_itemsize);
+        return -1;
+    }
+    length = shape[0];
+    step1 = strides ? strides[0] : view_itemsize * channels;
+    length2 = ndim;
+    if (ndim == 2) {
+        step2 = strides ? strides[1] : view_itemsize;
+    }
+    else {
+        step2 = step1;
+    }
+    memsize = length * channels * itemsize;
+    /*
+    printf("memsize: %d\n", (int)memsize);
+    */
+
+    /* Create chunk.
+     */
+    dst = (Uint8 *)PyMem_Malloc((size_t)memsize);
+    if (!dst) {
+        PyErr_NoMemory();
+        return -1;
+    }
+    *chunk = Mix_QuickLoad_RAW(dst, (Uint32)memsize);
+    if (!*chunk) {
+        PyMem_Free(dst);
+        PyErr_NoMemory();
+        return -1;
+    }
+    *mem = dst;
+
+    /*
+    printf("!! step1: %d, step2: %d, view_itemsize: %d, length: %d\n",
+           step1, step2, view_itemsize, length);
+    */
+    /* Copy samples.
+     */
+    if (step1 == itemsize * channels && step2 == itemsize) {
+        /*OPTIMIZATION: in these cases, we don't need to loop through
+         *the samples individually, because the bytes are already layed
+         *out correctly*/
+        memcpy(dst, buf, memsize);
+    }
+    else if (itemsize == 1) {
+        for (loop1 = 0; loop1 < length; loop1++) {
+            src = (Uint8*)buf + (loop1 * step1);
+            switch (view_itemsize) {
+
+            case 1:
+                for (loop2=0; loop2<length2; loop2++, dst+=1, src+=step2) {
+                    *(Uint8*)dst = (Uint8)*((Uint8*)src);
+                }
+                break;
+            case 2:
+                for (loop2=0; loop2<length2; loop2++, dst+=1, src+=step2) {
+                    *(Uint8*)dst = (Uint8)*((Uint16*)src);
+                }
+                break;
+            case 4:
+                for (loop2=0; loop2<length2; loop2++, dst+=1, src+=step2) {
+                    *(Uint8*)dst = (Uint8)*((Uint32*)src);
+                }
+                break;
+            }
+        }
+    }
+    else {
+        for (loop1 = 0; loop1 < length; loop1++)
+        {
+            src = (Uint8*)buf + (loop1 *step1);
+            switch (view_itemsize) {
+
+            case 1:
+                for (loop2=0; loop2<length2; loop2++, dst+=2, src+=step2) {
+                    *(Uint16*)dst = (Uint16)(*((Uint8*)src)<<8);
+                }
+                break;
+            case 2:
+                for (loop2=0; loop2<length2; loop2++, dst+=2, src+=step2) {
+                    *(Uint16*)dst = (Uint16)*((Uint16*)src);
+                }
+                break;
+            case 4:
+                for (loop2=0; loop2<length2; loop2++, dst+=2, src+=step2) {
+                    *(Uint16*)dst = (Uint16)*((Uint32*)src);
+                }
+                break;
+            }
+        }
+    }
+
+    return 0;
+}
+
+static int
 sound_init(PyObject *self, PyObject *arg, PyObject *kwarg)
 {
     static const char arg_cnt_err_msg[] =
     PyObject *obj = NULL;
     PyObject *file = NULL;
     PyObject *buffer = NULL;
+    PyObject *array = NULL;
     PyObject *keys;
     PyObject *kencoded;
     SDL_RWops *rw;
-    const void *buf = NULL;
-    Py_ssize_t buflen = 0;
     Mix_Chunk *chunk = NULL;
-    Uint8 *mem;
-    
+    Uint8 *mem = NULL;
+
     ((PySoundObject *)self)->chunk = NULL;
     ((PySoundObject *)self)->mem = NULL;
 
             return -1;
         }
         obj = PyTuple_GET_ITEM(arg, 0);
-        
+
         if (PyUnicode_Check(obj)) {
             file = obj;
             obj = NULL;
             return -1;
         }
         if ((file = PyDict_GetItemString(kwarg, "file")) == NULL &&
-            (buffer = PyDict_GetItemString(kwarg, "buffer")) == NULL) {
+            (buffer = PyDict_GetItemString(kwarg, "buffer")) == NULL &&
+            (array = PyDict_GetItemString(kwarg, "array")) == NULL     ) {
             keys = PyDict_Keys(kwarg);
             if (keys == NULL) {
                 return -1;
     else {
         RAISE(PyExc_TypeError, arg_cnt_err_msg);
         return -1;
-    }  
+    }
 
     if (file != NULL) {
         rw = RWopsFromObject(file);
         }
     }
 
+#if HAVE_NEW_BUFPROTO
+#if PY2
+    if (!chunk && buffer && /* conditional and */
+        PyObject_CheckBuffer(buffer)) {
+#else
+    if (!chunk && buffer) {
+#endif
+        Py_buffer view;
+        int rcode;
+
+        view.obj = 0;
+        if (PyObject_GetBuffer(buffer, &view, PyBUF_SIMPLE)) {
+            if (obj != NULL) {
+                PyErr_Clear();
+            }
+            else {
+                PyErr_Format(PyExc_TypeError,
+                             "Expected object with buffer interface: got a %s",
+                             Py_TYPE(buffer)->tp_name);
+                return -1;
+            }
+        }
+        else {
+            rcode = _chunk_from_buf(view.buf, view.len, &chunk, &mem);
+            PyBuffer_Release(&view);
+            if (rcode) {
+                return -1;
+            }
+            ((PySoundObject *)self)->mem = mem;
+        }
+    }
+#endif
+
+#if PY2
     if (chunk == NULL && buffer != NULL) {
+        const void *buf = NULL;
+        Py_ssize_t buflen = 0;
+
         if (PyObject_AsReadBuffer(buffer, &buf, &buflen)) {
             if (obj != NULL) {
                 PyErr_Clear();
             }
         }
         else {
-            mem = PyMem_Malloc((size_t)buflen);
-            if (mem == NULL)
-            {
-                PyErr_NoMemory ();
-                return -1;
-            }
-            chunk = Mix_QuickLoad_RAW(mem, (Uint32)buflen);
-            if (chunk == NULL) {
-                SDL_ClearError();
-                PyMem_Free(mem);
-                PyErr_NoMemory();
+            if (_chunk_from_buf(buf, buflen, &chunk, &mem)) {
                 return -1;
             }
             ((PySoundObject *)self)->mem = mem;
-            memcpy(mem, buf, (size_t)buflen);
         }
     }
+#endif
+
+#if HAVE_NEW_BUFPROTO
+    if (array != NULL && /* conditional and */
+        PyObject_CheckBuffer(array)) {
+        Py_buffer view;
+        PG_sample_format_t view_format;
+        int rcode;
+
+        view.itemsize = 0;
+        view.obj = 0;
+        if (PyObject_GetBuffer(array, &view, PyBUF_FORMAT | PyBUF_ND)) {
+            return -1;
+        }
+        view_format = _format_view_to_audio(&view);
+        if (!view_format) {
+            PyBuffer_Release(&view);
+            return -1;
+        }
+        rcode = _chunk_from_array(view.buf, view_format, view.ndim,
+                                  view.shape, view.strides,
+                                  &chunk, &mem);
+        PyBuffer_Release(&view);
+        if (rcode) {
+            return -1;
+        }
+        ((PySoundObject *)self)->mem = mem;
+    }
+#endif
+
+    if (chunk == NULL && array != NULL) {
+        PyArrayInterface *inter = 0;
+        PyObject *cobj = 0;
+        PG_sample_format_t array_format;
+        int rcode;
+
+        if (_get_array_interface(array, &cobj, &inter)) {
+            return -1;
+        }
+        if (!(inter->flags & PAI_CONTIGUOUS)) {
+            RAISE(PyExc_ValueError,
+                  "Array is discontiguous");
+            Py_DECREF(cobj);
+            return -1;
+        }
+        if (inter->nd > 1 && inter->flags & PAI_FORTRAN) {
+            RAISE(PyExc_ValueError, "Array is channel first");
+            Py_DECREF(cobj);
+            return -1;
+        }
+        array_format = _format_inter_to_audio(inter);
+        if (!array_format) {
+            Py_DECREF(cobj);
+            return -1;
+        }
+        rcode = _chunk_from_array(inter->data, array_format, inter->nd,
+                                  inter->shape, inter->strides,
+                                  &chunk, &mem);
+        Py_DECREF(cobj);
+        if (rcode) {
+            return -1;
+        }
+        ((PySoundObject *)self)->mem = mem;
+    }
 
     if (chunk == NULL) {
         PyErr_Format(PyExc_TypeError,
                      Py_TYPE(obj)->tp_name);
         return -1;
     }
-        
+
     ((PySoundObject *)self)->chunk = chunk;
     return 0;
 }
         }
         _dict = PyModule_GetDict (music);
         ptr = PyDict_GetItemString (_dict, "_MUSIC_POINTER");
-        current_music = 
+        current_music =
             (Mix_Music**)PyCapsule_GetPointer (ptr, "pygame.music_mixer."
                                                     "_MUSIC_POINTER");
         ptr = PyDict_GetItemString (_dict, "_QUEUE_POINTER");
-        queue_music = 
+        queue_music =
             (Mix_Music**)PyCapsule_GetPointer (ptr, "pygame.music_mixer."
                                                     "_QUEUE_POINTER");
     }
 pygame.mixer.Sound(buffer=buffer): return Sound
 pygame.mixer.Sound(object): return Sound
 pygame.mixer.Sound(file=object): return Sound
+pygame.mixer.Sound(array=object): return Sound
 
 Load a new sound buffer from a filename, a python file object or a
 readable buffer object. Limited resampling will be performed to help the
 can only be a file pathname. A Python 2.x string or a Python 3.x
 bytes object can be either a pathname or a buffer object. Use the
 'file' or 'buffer' keywords to avoid ambiguity; otherwise Sound may guess wrong.
+If the array keyword is used, the object is expected to export a version 3, C level
+array interface or, for Python 2.6 or later, a new buffer interface (The object is checked
+for a buffer interface first.)
 
 The Sound object represents actual sound sample data. Methods that change
 the state of the Sound object will the all instances of the Sound playback.
+A Sound object also exports an array interface, and, for Python 2.6 or later,
+a new buffer interface.
 
 The Sound can be loaded from an OGG audio file or from an uncompressed
 WAV.
 Note: The buffer will be copied internally, no data will be shared
 between it and the Sound object.
 
+For now buffer and array support is consistent with sndarray.make_sound for
+Numeric arrays, in that sample sign and byte order are ignored.
+This will change, either by correctly handling sign and byte order,
+or by raising an exception when different.</