..
  Audio Tools, a module and set of tools for manipulating audio data
  Copyright (C) 2007-2015  Brian Langenberger

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program 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 General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

:mod:`audiotools.pcm` --- the PCM FrameList Module
==================================================

.. module:: audiotools.pcm
   :synopsis: the PCM FrameList Module


The :mod:`audiotools.pcm` module contains the FrameList and FloatFrameList
classes for handling blobs of raw data.
These classes are immutable and list-like, but provide several additional
methods and attributes to aid in processing PCM data.

.. function:: empty_framelist(channels, bits_per_sample)

   Returns an empty :class:`FrameList` with the given parameters.

.. function:: from_list(list, channels, bits_per_sample, is_signed)

   Given a list of integer values, a number of channels,
   the amount of bits-per-sample and whether the samples are signed,
   returns a new :class:`FrameList` object with those values.
   Raises :exc:`ValueError` if a :class:`FrameList` cannot be built
   from those values.

   >>> f = from_list([-1,0,1,2],2,16,True)
   >>> list(f)
   [-1, 0, 1, 2]

.. function:: from_frames(frame_list)

   Given a list of :class:`FrameList` objects, returns a new
   :class:`FrameList` whose values are built from those objects.
   Raises :exc:`ValueError` if any of the objects are longer than
   1 PCM frame, their number of channels are not consistent
   or their bits_per_sample are not consistent.

   >>> l = [from_list([-1,0],2,16,True),
   ...      from_list([ 1,2],2,16,True)]
   >>> f = from_frames(l)
   >>> list(f)
   [-1, 0, 1, 2]

.. function:: from_channels(frame_list)

   Given a list of :class:`FrameList` objects, returns a new
   :class:`FrameList` whose values are built from those objects.
   Raises :exc:`ValueError` if any of the objects are wider than
   1 channel, their number of frames are not consistent
   or their bits_per_sample are not consistent.

   >>> l = [from_list([-1,1],1,16,True),
   ...      from_list([ 0,2],1,16,True)]
   >>> f = from_channels(l)
   >>> list(f)
   [-1, 0, 1, 2]

.. function:: empty_float_framelist(channels)

   Returns an empty :class:`FloatFrameList` with the given parameters.

.. function:: from_float_frames(float_frame_list)

   Given a list of :class:`FloatFrameList` objects, returns a new
   :class:`FloatFrameList` whose values are built from those objects.
   Raises :exc:`ValueError` if any of the objects are longer than
   1 PCM frame or their number of channels are not consistent.

   >>> l = [FloatFrameList([-1.0,0.0],2),
   ...      FloatFrameList([ 0.5,1.0],2)]
   >>> f = from_float_frames(l)
   >>> list(f)
   [-1.0, 0.0, 0.5, 1.0]

.. function:: from_float_channels(float_frame_list)

   Given a list of :class:`FloatFrameList` objects, returns a new
   :class:`FloatFrameList` whose values are built from those objects.
   Raises :exc:`ValueError` if any of the objects are wider than
   1 channel or their number of frames are not consistent.

   >>> l = [FloatFrameList([-1.0,0.5],1),
   ...      FloatFrameList([ 0.0,1.0],1)]
   >>> f = from_float_channels(l)
   >>> list(f)
   [-1.0, 0.0, 0.5, 1.0]


FrameList Objects
-----------------

.. class:: FrameList(string, channels, bits_per_sample, is_big_endian, is_signed)

   This class implements a PCM FrameList, which can be envisioned as a
   2D array of signed integers where
   each row represents a PCM frame of samples and
   each column represents a channel.

   During initialization, ``string`` is a collection of raw bytes,
   ``bits_per_sample`` is an integer and ``is_big_endian`` and ``is_signed``
   are booleans.
   This provides a convenient way to transforming raw data from
   file-like objects into :class:`FrameList` objects.
   Once instantiated, a :class:`FrameList` object is immutable.

.. data:: FrameList.frames

   The amount of PCM frames within this object, as a non-negative integer.

.. data:: FrameList.channels

   The amount of channels within this object, as a positive integer.

.. data:: FrameList.bits_per_sample

   The size of each sample in bits, as a positive integer.

.. method:: FrameList.frame(frame_number)

   Given a non-negative ``frame_number`` integer,
   returns the samples at the given frame as a new :class:`FrameList` object.
   This new FrameList will be a single frame long, but have the same
   number of channels and bits_per_sample as the original.
   Raises :exc:`IndexError` if one tries to get a frame number outside
   this FrameList's boundaries.

.. method:: FrameList.channel(channel_number)

   Given a non-negative ``channel_number`` integer,
   returns the samples at the given channel as a new :class:`FrameList` object.
   This new FrameList will be a single channel wide, but have the same
   number of frames and bits_per_sample as the original.
   Raises :exc:`IndexError` if one tries to get a channel number outside
   this FrameList's boundaries.

.. method:: FrameList.split(frame_count)

   Returns a pair of :class:`FrameList` objects.
   The first contains up to ``frame_count`` number of PCM frames.
   The second contains the remainder.
   If ``frame_count`` is larger than the number of frames in the FrameList,
   the first will contain all of the frames and the second will be empty.

.. method:: FrameList.to_float()

   Converts this object's values to a new :class:`FloatFrameList` object
   by transforming all samples to the range -1.0 to 1.0.

.. method:: FrameList.to_bytes(is_big_endian, is_signed)

   Given ``is_big_endian`` and ``is_signed`` booleans,
   returns a plain string of raw PCM data.
   This is much like the inverse of :class:`FrameList`'s initialization
   routine.

.. method:: FrameList.frame_count(bytes)

   A convenience method which converts a given byte count to the
   maximum number of frames those bytes could contain, or a minimum of 1.

   >>> FrameList("",2,16,False,True).frame_count(8)
   2

FloatFrameList Objects
----------------------

.. class:: FloatFrameList(floats, channels)

   This class implements a FrameList of floating point samples,
   which can be envisioned as a 2D array of signed floats where
   each row represents a PCM frame of samples,
   each column represents a channel and each value is
   within the range of -1.0 to 1.0.

   During initialization, ``floats`` is a list of float values
   and ``channels`` is an integer number of channels.

.. data:: FloatFrameList.frames

   The amount of PCM frames within this object, as a non-negative integer.

.. data:: FloatFrameList.channels

   The amount of channels within this object, as a positive integer.

.. method:: FloatFrameList.frame(frame_number)

   Given a non-negative ``frame_number`` integer,
   returns the samples at the given frame as a new :class:`FloatFrameList`
   object.
   This new FloatFrameList will be a single frame long, but have the same
   number of channels as the original.
   Raises :exc:`IndexError` if one tries to get a frame number outside
   this FloatFrameList's boundaries.

.. method:: FloatFrameList.channel(channel_number)

   Given a non-negative ``channel_number`` integer,
   returns the samples at the given channel as a new :class:`FloatFrameList`
   object.
   This new FloatFrameList will be a single channel wide, but have the same
   number of frames as the original.
   Raises :exc:`IndexError` if one tries to get a channel number outside
   this FloatFrameList's boundaries.

.. method:: FloatFrameList.split(frame_count)

   Returns a pair of :class:`FloatFrameList` objects.
   The first contains up to ``frame_count`` number of PCM frames.
   The second contains the remainder.
   If ``frame_count`` is larger than the number of frames in the
   FloatFrameList, the first will contain all of the frames and the
   second will be empty.

.. method:: FloatFrameList.to_int(bits_per_sample)

   Given a ``bits_per_sample`` integer, converts this object's
   floating point values to a new :class:`FrameList` object.
