"""
Copyright 2010 Olivier Belanger

This file is part of pyo, a python module to help digital signal
processing script creation.

pyo 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 3 of the License, or
(at your option) any later version.

pyo 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 pyo.  If not, see <http://www.gnu.org/licenses/>.
"""
from _core import *
from _maps import *
from _widgets import createGraphWindow, createSndViewTableWindow
from types import ListType
from math import pi

######################################################################
### Tables
######################################################################
class HarmTable(PyoTableObject):
    """
    Harmonic waveform generator.

    Generates composite waveforms made up of weighted sums 
    of simple sinusoids.

    Parentclass: PyoTableObject

    Parameters:

    list : list, optional
        Relative strengths of the fixed harmonic partial numbers 1,2,3, etc. 
        Defaults to [1].
    size : int, optional
        Table size in samples. Defaults to 8192.

    Methods:

    setSize(size) : Change the size of the table. This will erase the 
        previously drawn waveform.
    replace(list) : Redraw the waveform according to the new `list` 
        parameter.

    Attributes:

    list : list, optional
        Relative strengths of the fixed harmonic partial numbers.
    size : int, optional
        Table size in samples.

    Examples:

    >>> s = Server().boot()
    >>> s.start()
    >>> # Square wave up to 9th harmonic
    >>> t = HarmTable([1,0,.33,0,.2,0,.143,0,.111])
    >>> a = Osc(table=t, freq=[199,200], mul=.2).out()

    """
    def __init__(self, list=[1., 0.], size=8192):
        PyoTableObject.__init__(self)
        self._list = list
        self._size = size
        self._base_objs = [HarmTable_base(list, size)]

    def __dir__(self):
        return ['list', 'size']
        
    def setSize(self, size):
        """
        Change the size of the table. This will erase the previously 
        drawn waveform.
        
        Parameters:
        
        size : int
            New table size in samples.
        
        """
        self._size = size
        [obj.setSize(size) for obj in self._base_objs]
    
    def replace(self, list):
        """
        Redraw the waveform according to a new set of harmonics 
        relative strengths.
        
        Parameters:
        
        list : list
            Relative strengths of the fixed harmonic partial 
            numbers 1,2,3, etc.

        """      
        self._list = list
        [obj.replace(list) for obj in self._base_objs]

    @property
    def size(self):
        """int. Table size in samples.""" 
        return self._size
    @size.setter
    def size(self, x): self.setSize(x)

    @property
    def list(self): 
        """list. Relative strengths of the fixed harmonic partial numbers."""
        return self._list
    @list.setter
    def list(self, x): self.replace(x)

class SawTable(PyoTableObject):
    """
    Sawtooth waveform generator.

    Generates sawtooth waveforms made up of fixed number of harmonics.

    Parentclass: PyoTableObject

    Parameters:

    order : int, optional
        Number of harmonics sawtooth is made of. 
        Defaults to 10.
    size : int, optional
        Table size in samples. Defaults to 8192.

    Methods:

    setOrder(x) : Change the `order` attribute and redraw the waveform.
    setSize(size) : Change the size of the table. This will erase the 
        previously drawn waveform.

    Attributes:

    order : int, optional
        Number of harmonics sawtooth is made of.
    size : int, optional
        Table size in samples.

    Examples:

    >>> s = Server().boot()
    >>> s.start()
    >>> t = SawTable(order=12).normalize()
    >>> a = Osc(table=t, freq=[199,200], mul=.2).out()

    """
    def __init__(self, order=10, size=8192):
        PyoTableObject.__init__(self)
        self._order = order
        self._size = size
        list = [1./i for i in range(1,(order+1))]
        self._base_objs = [HarmTable_base(list, size)]

    def __dir__(self):
        return ['order', 'size']
        
    def setSize(self, size):
        """
        Change the size of the table. This will erase the previously 
        drawn waveform.
        
        Parameters:
        
        size : int
            New table size in samples.
        
        """
        self._size = size
        [obj.setSize(size) for obj in self._base_objs]
    
    def setOrder(self, x):
        """
        Change the `order` attribute and redraw the waveform.
        
        Parameters:
        
        x : int
            New number of harmonics

        """      
        self._order = x
        list = [1./i for i in range(1,(self._order+1))]
        [obj.replace(list) for obj in self._base_objs]

    @property
    def size(self):
        """int. Table size in samples.""" 
        return self._size
    @size.setter
    def size(self, x): self.setSize(x)

    @property
    def order(self): 
        """int. Number of harmonics sawtooth is made of."""
        return self._order
    @order.setter
    def order(self, x): self.setOrder(x)

class SquareTable(PyoTableObject):
    """
    Square waveform generator.

    Generates square waveforms made up of fixed number of harmonics.

    Parentclass: PyoTableObject

    Parameters:

    order : int, optional
        Number of harmonics square waveform is made of. The waveform will 
        contains `order` odd harmonics. Defaults to 10.
    size : int, optional
        Table size in samples. Defaults to 8192.

    Methods:

    setOrder(x) : Change the `order` attribute and redraw the waveform.
    setSize(size) : Change the size of the table. This will erase the 
        previously drawn waveform.

    Attributes:

    order : int, optional
        Number of harmonics square waveform is made of.
    size : int, optional
        Table size in samples.

    Examples:

    >>> s = Server().boot()
    >>> s.start()
    >>> t = SquareTable(order=15).normalize()
    >>> a = Osc(table=t, freq=[199,200], mul=.2).out()

    """
    def __init__(self, order=10, size=8192):
        PyoTableObject.__init__(self)
        self._order = order
        self._size = size
        list = []
        for i in range(1,(order*2)):
            if i%2 == 1:
                list.append(1./i)
            else:
                list.append(0.)    
        self._base_objs = [HarmTable_base(list, size)]

    def __dir__(self):
        return ['order', 'size']
        
    def setSize(self, size):
        """
        Change the size of the table. This will erase the previously 
        drawn waveform.
        
        Parameters:
        
        size : int
            New table size in samples.
        
        """
        self._size = size
        [obj.setSize(size) for obj in self._base_objs]
    
    def setOrder(self, x):
        """
        Change the `order` attribute and redraw the waveform.
        
        Parameters:
        
        x : int
            New number of harmonics

        """      
        self._order = x
        list = []
        for i in range(1,(self._order*2)):
            if i%2 == 1:
                list.append(1./i)
            else:
                list.append(0.)    
        [obj.replace(list) for obj in self._base_objs]

    @property
    def size(self):
        """int. Table size in samples.""" 
        return self._size
    @size.setter
    def size(self, x): self.setSize(x)

    @property
    def order(self): 
        """int. Number of harmonics square waveform is made of."""
        return self._order
    @order.setter
    def order(self, x): self.setOrder(x)

class ChebyTable(PyoTableObject):
    """
    Chebyshev polynomials of the first kind.

    Uses Chebyshev coefficients to generate stored polynomial functions 
    which, under waveshaping, can be used to split a sinusoid into 
    harmonic partials having a pre-definable spectrum. 

    Parentclass: PyoTableObject

    Parameters:

    list : list, optional
        Relative strengths of partials numbers 1,2,3, ..., 12 that will 
        result when a sinusoid of amplitude 1 is waveshaped using this 
        function table. Up to 12 partials can be specified. Defaults to [1].
    size : int, optional
        Table size in samples. Defaults to 8192.

    Methods:

    setSize(size) : Change the size of the table. This will erase the 
        previously drawn waveform.
    replace(list) : Redraw the waveform according to the new `list` 
        parameter.

    Attributes:

    list : list, optional
        Relative strengths of the fixed harmonic partial numbers.
    size : int, optional
        Table size in samples.

    Examples:

    >>> s = Server().boot()
    >>> s.start()
    >>> t = ChebyTable([1,0,.33,0,.2,0,.143,0,.111])
    >>> lfo = Sine(freq=.25, mul=0.45, add=0.5)
    >>> a = Sine(freq=[200,201], mul=lfo)
    >>> b = Lookup(table=t, index=a, mul=1-lfo).out()

    """
    def __init__(self, list=[1., 0.], size=8192):
        PyoTableObject.__init__(self)
        self._list = list
        self._size = size
        self._base_objs = [ChebyTable_base(list, size)]

    def __dir__(self):
        return ['list', 'size']
        
    def setSize(self, size):
        """
        Change the size of the table. This will erase the previously 
        drawn waveform.
        
        Parameters:
        
        size : int
            New table size in samples.
        
        """
        self._size = size
        [obj.setSize(size) for obj in self._base_objs]
    
    def replace(self, list):
        """
        Redraw the waveform according to a new set of harmonics 
        relative strengths that will result when a sinusoid of 
        amplitude 1 is waveshaped using this function table.
        
        Parameters:
        
        list : list
            Relative strengths of the fixed harmonic partial 
            numbers 1,2,3, ..., 12. Up to 12 partials can be specified.

        """      
        self._list = list
        [obj.replace(list) for obj in self._base_objs]

    @property
    def size(self):
        """int. Table size in samples.""" 
        return self._size
    @size.setter
    def size(self, x): self.setSize(x)

    @property
    def list(self): 
        """list. Relative strengths of the fixed harmonic partial numbers."""
        return self._list
    @list.setter
    def list(self, x): self.replace(x)
        
class HannTable(PyoTableObject):
    """
    Generates Hanning window function. 

    Parentclass: PyoTableObject

    Parameters:

    size : int, optional
        Table size in samples. Defaults to 8192.

    Methods:

    setSize(size) : Change the size of the table. This will redraw the envelope.

    Attributes:

    size : int
        Table size in samples.

    Examples:

    >>> s = Server().boot()
    >>> s.start()
    >>> # Hanning envelope
    >>> t = HannTable()
    >>> a = Osc(table=t, freq=2, mul=.2)
    >>> b = Sine(freq=[299,300], mul=a).out()

    """
    def __init__(self, size=8192):
        PyoTableObject.__init__(self)
        self._size = size
        self._base_objs = [HannTable_base(size)]

    def __dir__(self):
        return ['size']

    def setSize(self, size):
        """
        Change the size of the table. This will redraw the envelope.
        
        Parameters:
        
        size : int
            New table size in samples.
        
        """
        self._size = size
        [obj.setSize(size) for obj in self._base_objs]

    @property
    def size(self): 
        """int. Table size in samples."""
        return self._size
    @size.setter
    def size(self, x): self.setSize(x)

class SincTable(PyoTableObject):
    """
    Generates sinc window function. 

    Parentclass: PyoTableObject

    Parameters:

    freq : float, optional
        Frequency, in radians, of the sinc function. Defaults to pi*2.
    windowed : boolean, optional
        If True, an hanning window is applied on the sinc function. Defaults to False.
    size : int, optional
        Table size in samples. Defaults to 8192.

    Methods:

    setFreq(x) : Change the frequency of the sinc function. This will redraw the envelope.
    setWindowed(x) : Change the windowed flag. This will redraw the envelope.
    setSize(size) : Change the size of the table. This will redraw the envelope.

    Attributes:

    freq : float
        Frequency, in radians, of the sinc function.
    windowed : boolean
        If True, an hanning window is applied on the sinc function.
    size : int
        Table size in samples.

    Examples:

    >>> import math
    >>> s = Server().boot()
    >>> s.start()
    >>> t = SincTable(freq=math.pi*6, windowed=True)
    >>> a = Osc(t, freq=[199,200], mul=.2).out()

    """
    def __init__(self, freq=pi*2, windowed=False, size=8192):
        PyoTableObject.__init__(self)
        self._freq = freq
        self._windowed = windowed
        self._size = size
        self._base_objs = [SincTable_base(freq, windowed, size)]

    def __dir__(self):
        return ['freq', 'windowed', 'size']

    def setFreq(self, x):
        """
        Change the frequency of the sinc function. This will redraw the envelope.

        Parameters:

        x : float
            New frequency in radians.

        """
        self._freq = x
        [obj.setFreq(x) for obj in self._base_objs]

    def setWindowed(self, x):
        """
        Change the windowed flag. This will redraw the envelope.

        Parameters:

        x : boolean
            New windowed flag.

        """
        self._windowed = x
        [obj.setWindowed(x) for obj in self._base_objs]

    def setSize(self, size):
        """
        Change the size of the table. This will redraw the envelope.

        Parameters:

        size : int
            New table size in samples.

        """
        self._size = size
        [obj.setSize(size) for obj in self._base_objs]

    @property
    def freq(self): 
        """float. Frequency of the sinc function."""
        return self._freq
    @freq.setter
    def freq(self, x): self.setFreq(x)
    @property
    def windowed(self): 
        """boolean. Windowed flag."""
        return self._windowed
    @windowed.setter
    def windowed(self, x): self.setWindowed(x)
    @property
    def size(self): 
        """int. Table size in samples."""
        return self._size
    @size.setter
    def size(self, x): self.setSize(x)

class WinTable(PyoTableObject):
    """
    Generates different kind of windowing functions. 

    Parentclass: PyoTableObject

    Parameters:

    type : int, optional
        Windowing function. Defaults to 2.
        Possible choices are:
            0 : Rectangular (no window)
            1 : Hamming
            2 : Hanning
            3 : Bartlett (triangular)
            4 : Blackman 3-term
            5 : Blackman-Harris 4-term
            6 : Blackman-Harris 7-term
            7 : Tuckey (alpha = 0.66)
            8 : Sine (half-sine window)
    size : int, optional
        Table size in samples. Defaults to 8192.

    Methods:

    setType(x) : Sets the windowing function.
    setSize(size) : Change the size of the table. This will redraw the envelope.

    Attributes:
  
    type : int
        Windowing function.
    size : int
        Table size in samples.

    Examples:

    >>> s = Server().boot()
    >>> s.start()
    >>> # Triangular envelope
    >>> t = WinTable(type=3)
    >>> a = Osc(table=t, freq=2, mul=.2)
    >>> b = SineLoop(freq=[199,200], feedback=0.05, mul=a).out()

    """
    def __init__(self, type=2, size=8192):
        PyoTableObject.__init__(self)
        self._type = type
        self._size = size
        self._base_objs = [WinTable_base(type, size)]

    def __dir__(self):
        return ['type', 'size']

    def setType(self, type):
        """
        Sets the windowing function.

        Parameters:

        type : int {0 -> 8}
        Windowing function.
        """
        self._type = type
        [obj.setType(type) for obj in self._base_objs]


    def setSize(self, size):
        """
        Change the size of the table. This will redraw the envelope.
        
        Parameters:
        
        size : int
            New table size in samples.
        
        """
        self._size = size
        [obj.setSize(size) for obj in self._base_objs]

    @property
    def type(self): 
        """int. Windowing function."""
        return self._type
    @type.setter
    def type(self, x): self.setType(x)

    @property
    def size(self): 
        """int. Table size in samples."""
        return self._size
    @size.setter
    def size(self, x): self.setSize(x)

class ParaTable(PyoTableObject):
    """
    Generates parabola window function. 

    The parabola is a conic section, the intersection of a right circular conical 
    surface and a plane parallel to a generating straight line of that surface.

    Parentclass: PyoTableObject

    Parameters:

    size : int, optional
        Table size in samples. Defaults to 8192.

    Methods:

    setSize(size) : Change the size of the table. This will redraw the envelope.

    Attributes:

    size : int
        Table size in samples.

    Examples:

    >>> s = Server().boot()
    >>> s.start()
    >>> # Parabola envelope
    >>> t = ParaTable()
    >>> a = Osc(table=t, freq=2, mul=.2)
    >>> b = SineLoop(freq=[299,300], feedback=0.05, mul=a).out()

    """
    def __init__(self, size=8192):
        PyoTableObject.__init__(self)
        self._size = size
        self._base_objs = [ParaTable_base(size)]

    def __dir__(self):
        return ['size']

    def setSize(self, size):
        """
        Change the size of the table. This will redraw the envelope.

        Parameters:

        size : int
            New table size in samples.

        """
        self._size = size
        [obj.setSize(size) for obj in self._base_objs]

    @property
    def size(self): 
        """int. Table size in samples."""
        return self._size
    @size.setter
    def size(self, x): self.setSize(x)

class LinTable(PyoTableObject):
    """
    Construct a table from segments of straight lines in breakpoint fashion.

    Parentclass: PyoTableObject

    Parameters:

    list : list, optional
        List of tuples indicating location and value of each points 
        in the table. The default, [(0,0.), (8191, 1.)], creates a 
        straight line from 0.0 at location 0 to 1.0 at the end of the 
        table (size - 1). Location must be an integer.
    size : int, optional
        Table size in samples. Defaults to 8192.

    Methods:

    setSize(size) : Change the size of the table and rescale the envelope.
    replace(list) : Draw a new envelope according to the `list` parameter.
    loadRecFile(filename, tolerance) : Import an automation recording file.
    graph(yrange, title, wxnoserver) : Opens a grapher window to control 
        the shape of the envelope.

    Notes:

    Locations in the list must be in increasing order. If the last value 
    is less than size, the rest of the table will be filled with zeros. 

    Attributes:

    list : list
        List of tuples [(location, value), ...].
    size : int, optional
        Table size in samples.

    Examples:

    >>> s = Server().boot()
    >>> s.start()
    >>> # Sharp attack envelope
    >>> t = LinTable([(0,0), (100,1), (1000,.25), (8191,0)])
    >>> a = Osc(table=t, freq=2, mul=.25)
    >>> b = SineLoop(freq=[299,300], feedback=0.05, mul=a).out()

    """
    def __init__(self, list=[(0, 0.), (8191, 1.)], size=8192):
        PyoTableObject.__init__(self)
        self._size = size
        self._base_objs = [LinTable_base(list, size)]

    def __dir__(self):
        return ['list', 'size']
        
    def setSize(self, size):
        """
        Change the size of the table and rescale the envelope.
        
        Parameters:
        
        size : int
            New table size in samples.
        
        """
        self._size = size
        [obj.setSize(size) for obj in self._base_objs]
    
    def replace(self, list):
        """
        Draw a new envelope according to the new `list` parameter.
        
        Parameters:
        
        list : list
            List of tuples indicating location and value of each points 
            in the table. Location must be integer.

        """ 
        self._list = list
        [obj.replace(list) for obj in self._base_objs]

    def loadRecFile(self, filename, tolerance=0.02):
        """
        Import an automation recording file in the table.
        
        loadRecFile takes a recording file, usually from a ControlRec object,
        as `filename` parameter, applies a filtering pre-processing to eliminate
        redundancies and loads the result in the table as a list of points. 
        Filtering process can be controled with the `tolerance` parameter. 
        
        Parameters:
        
        filename : string
            Full path of an automation recording file.
        tolerance : float, optional
            Tolerance of the filter. A higher value will eliminate more points.
            Defaults to 0.02.

        """
        _path, _name = os.path.split(filename)
        # files = sorted([f for f in os.listdir(_path) if _name+"_" in f])
        # if _name not in files: files.append(_name)
        files = [filename]
        for i, obj in enumerate(self._base_objs):
            p = os.path.join(_path, wrap(files,i))
            f = open(p, "r")
            values = [(float(l.split()[0]), float(l.split()[1])) for l in f.readlines()]
            scl = self._size / values[-1][0]
            values = [(int(v[0]*scl), v[1]) for v in values]
            f.close()
            values = reducePoints(values, tolerance=tolerance)
            self._list = values
            obj.replace(values)

    def getPoints(self):
        return self._base_objs[0].getPoints()

    def graph(self, yrange=(0.0, 1.0), title=None, wxnoserver=False):
        """
        Opens a grapher window to control the shape of the envelope.

        When editing the grapher with the mouse, the new set of points
        will be send to the object on mouse up. 
        
        Ctrl+C with focus on the grapher will copy the list of points to the 
        clipboard, giving an easy way to insert the new shape in a script.

        Parameters:

        yrange : tuple, optional
            Set the min and max values of the Y axis of the graph.
            Defaults to (0.0, 1.0).
        title : string, optional
            Title of the window. If none is provided, the name of the 
            class is used.
        wxnoserver : boolean, optional
            With wxPython graphical toolkit, if True, tells the 
            interpreter that there will be no server window and not 
            to wait for it before showing the controller window. 
            Defaults to False.

        """
        createGraphWindow(self, 0, self._size, yrange, title, wxnoserver)

    @property
    def size(self):
        """int. Table size in samples.""" 
        return self._size
    @size.setter
    def size(self, x): self.setSize(x)

    @property
    def list(self):
        """list. List of tuples indicating location and value of each points in the table.""" 
        return self.getPoints()
    @list.setter
    def list(self, x): self.replace(x)

class CosTable(PyoTableObject):
    """
    Construct a table from cosine interpolated segments.

    Parentclass: PyoTableObject

    Parameters:

    list : list, optional
        List of tuples indicating location and value of each points 
        in the table. The default, [(0,0.), (8191, 1.)], creates a 
        cosine line from 0.0 at location 0 to 1.0 at the end of the 
        table (size - 1). Location must be an integer.
    size : int, optional
        Table size in samples. Defaults to 8192.

    Methods:

    setSize(size) : Change the size of the table and rescale the envelope.
    replace(list) : Draw a new envelope according to the `list` parameter.
    loadRecFile(filename, tolerance) : Import an automation recording file.
    graph(yrange, title, wxnoserver) : Opens a grapher window to control 
        the shape of the envelope.

    Notes:

    Locations in the list must be in increasing order. If the last value 
    is less than size, the rest of the table will be filled with zeros. 

    Attributes:

    list : list
        List of tuples [(location, value), ...].
    size : int, optional
        Table size in samples.

    Examples:

    >>> s = Server().boot()
    >>> s.start()
    >>> # Sharp attack envelope
    >>> t = CosTable([(0,0), (100,1), (1000,.25), (8191,0)])
    >>> a = Osc(table=t, freq=2, mul=.25)
    >>> b = SineLoop(freq=[299,300], feedback=0.05, mul=a).out()

    """
    def __init__(self, list=[(0, 0.), (8191, 1.)], size=8192):
        PyoTableObject.__init__(self)
        self._size = size
        self._base_objs = [CosTable_base(list, size)]

    def __dir__(self):
        return ['list', 'size']
        
    def setSize(self, size):
        """
        Change the size of the table and rescale the envelope.
        
        Parameters:
        
        size : int
            New table size in samples.
        
        """
        self._size = size
        [obj.setSize(size) for obj in self._base_objs]
    
    def replace(self, list):
        """
        Draw a new envelope according to the new `list` parameter.
        
        Parameters:
        
        list : list
            List of tuples indicating location and value of each points 
            in the table. Location must be integer.

        """      
        self._list = list
        [obj.replace(list) for obj in self._base_objs]

    def loadRecFile(self, filename, tolerance=0.02):
        """
        Import an automation recording file in the table.

        loadRecFile takes a recording file, usually from a ControlRec object,
        as `filename` parameter, applies a filtering pre-processing to eliminate
        redundancies and loads the result in the table as a list of points. 
        Filtering process can be controled with the `tolerance` parameter. 

        Parameters:

        filename : string
            Full path of an automation recording file.
        tolerance : float, optional
            Tolerance of the filter. A higher value will eliminate more points.
            Defaults to 0.02.

        """
        _path, _name = os.path.split(filename)
        # files = sorted([f for f in os.listdir(_path) if _name+"_" in f])
        # if _name not in files: files.append(_name)
        files = [filename]
        for i, obj in enumerate(self._base_objs):
            p = os.path.join(_path, wrap(files,i))
            f = open(p, "r")
            values = [(float(l.split()[0]), float(l.split()[1])) for l in f.readlines()]
            scl = self._size / values[-1][0]
            values = [(int(v[0]*scl), v[1]) for v in values]
            f.close()
            values = reducePoints(values, tolerance=tolerance)
            self._list = values
            obj.replace(values)

    def getPoints(self):
        return self._base_objs[0].getPoints()

    def graph(self, yrange=(0.0, 1.0), title=None, wxnoserver=False):
        """
        Opens a grapher window to control the shape of the envelope.

        When editing the grapher with the mouse, the new set of points
        will be send to the object on mouse up. 

        Ctrl+C with focus on the grapher will copy the list of points to the 
        clipboard, giving an easy way to insert the new shape in a script.

        Parameters:

        yrange : tuple, optional
            Set the min and max values of the Y axis of the graph.
            Defaults to (0.0, 1.0).
        title : string, optional
            Title of the window. If none is provided, the name of the 
            class is used.
        wxnoserver : boolean, optional
            With wxPython graphical toolkit, if True, tells the 
            interpreter that there will be no server window and not 
            to wait for it before showing the controller window. 
            Defaults to False.

        """
        createGraphWindow(self, 1, self._size, yrange, title, wxnoserver)
        
    @property
    def size(self):
        """int. Table size in samples.""" 
        return self._size
    @size.setter
    def size(self, x): self.setSize(x)

    @property
    def list(self):
        """list. List of tuples indicating location and value of each points in the table.""" 
        return self.getPoints()
    @list.setter
    def list(self, x): self.replace(x)

class CurveTable(PyoTableObject):
    """
    Construct a table from curve interpolated segments.

    CurveTable uses Hermite interpolation (sort of cubic interpolation)
    to calculate each points of the curve. This algorithm allows tension
    and biasing controls. Tension can be used to tighten up the curvature 
    at the known points. The bias is used to twist the curve about the 
    known points.

    Parentclass: PyoTableObject

    Parameters:

    list : list, optional
        List of tuples indicating location and value of each points 
        in the table. The default, [(0,0.), (8191, 1.)], creates a 
        curved line from 0.0 at location 0 to 1.0 at the end of the 
        table (size - 1). Location must be an integer.
    tension : float, optional
        Curvature at the known points. 1 is high, 0 normal, -1 is low.
        Defaults to 0.
    bias : float, optional
        Curve attraction (for each segments) toward bundary points.
        0 is even, positive is towards first point, negative is towards 
        the second point. Defaults to 0.
    size : int, optional
        Table size in samples. Defaults to 8192.

    Methods:

    setSize(size) : Change the size of the table and rescale the envelope.
    setTension(x) : Replace the `tension` attribute.
    setTension(x) : Replace the `bias` attribute.
    replace(list) : Draw a new envelope according to the `list` parameter.
    loadRecFile(filename, tolerance) : Import an automation recording file.
    graph(yrange, title, wxnoserver) : Opens a grapher window to control 
        the shape of the envelope.

    Notes:

    Locations in the list must be in increasing order. If the last value 
    is less than size, the rest of the table will be filled with zeros.

    High tension or bias values can create unstable or very loud table,
    use normalize method to keep the curve between -1 and 1.

    Attributes:

    list : list
        List of tuples [(location, value), ...].
    tension : float
        Curvature tension.
    bias : float
        Curve attraction.
    size : int, optional
        Table size in samples.

    Examples:

    >>> s = Server().boot()
    >>> s.start()
    >>> t = CurveTable([(0,0),(2048,.5),(4096,.2),(6144,.5),(8192,0)], 0, 20)
    >>> t.normalize()
    >>> a = Osc(table=t, freq=2, mul=.25)
    >>> b = SineLoop(freq=[299,300], feedback=0.05, mul=a).out()

    """
    def __init__(self, list=[(0, 0.), (8191, 1.)], tension=0, bias=0, size=8192):
        PyoTableObject.__init__(self)
        self._size = size
        self._tension = tension
        self._bias = bias
        self._base_objs = [CurveTable_base(list, tension, bias, size)]

    def __dir__(self):
        return ['list', 'tension', 'bias', 'size']
        
    def setSize(self, size):
        """
        Change the size of the table and rescale the envelope.
        
        Parameters:
        
        size : int
            New table size in samples.
        
        """
        self._size = size
        [obj.setSize(size) for obj in self._base_objs]

    def setTension(self, x):
        """
        Replace the `tension` attribute.
        
        1 is high, 0 normal, -1 is low.
        
        Parameters:
        
        x : float
            New `tension` attribute.
        
        """
        self._tension = x
        [obj.setTension(x) for obj in self._base_objs]

    def setBias(self, x):
        """
        Replace the `bias` attribute.
        
        0 is even, positive is towards first point, negative is towards 
        the second point.
        
        Parameters:
        
        x : float
            New `bias` attribute.
        
        """
        self._bias = x
        [obj.setBias(x) for obj in self._base_objs]
     
    def replace(self, list):
        """
        Draw a new envelope according to the new `list` parameter.
        
        Parameters:
        
        list : list
            List of tuples indicating location and value of each points 
            in the table. Location must be integer.

        """      
        self._list = list
        [obj.replace(list) for obj in self._base_objs]

    def loadRecFile(self, filename, tolerance=0.02):
        """
        Import an automation recording file in the table.

        loadRecFile takes a recording file, usually from a ControlRec object,
        as `filename` parameter, applies a filtering pre-processing to eliminate
        redundancies and loads the result in the table as a list of points. 
        Filtering process can be controled with the `tolerance` parameter. 

        Parameters:

        filename : string
            Full path of an automation recording file.
        tolerance : float, optional
            Tolerance of the filter. A higher value will eliminate more points.
            Defaults to 0.02.

        """
        _path, _name = os.path.split(filename)
        # files = sorted([f for f in os.listdir(_path) if _name+"_" in f])
        # if _name not in files: files.append(_name)
        files = [filename]
        for i, obj in enumerate(self._base_objs):
            p = os.path.join(_path, wrap(files,i))
            f = open(p, "r")
            values = [(float(l.split()[0]), float(l.split()[1])) for l in f.readlines()]
            scl = self._size / values[-1][0]
            values = [(int(v[0]*scl), v[1]) for v in values]
            f.close()
            values = reducePoints(values, tolerance=tolerance)
            self._list = values
            obj.replace(values)
        
    def getPoints(self):
        return self._base_objs[0].getPoints()

    def graph(self, yrange=(0.0, 1.0), title=None, wxnoserver=False):
        """
        Opens a grapher window to control the shape of the envelope.

        When editing the grapher with the mouse, the new set of points
        will be send to the object on mouse up. 

        Ctrl+C with focus on the grapher will copy the list of points to the 
        clipboard, giving an easy way to insert the new shape in a script.

        Parameters:

        yrange : tuple, optional
            Set the min and max values of the Y axis of the graph.
            Defaults to (0.0, 1.0).
        title : string, optional
            Title of the window. If none is provided, the name of the 
            class is used.
        wxnoserver : boolean, optional
            With wxPython graphical toolkit, if True, tells the 
            interpreter that there will be no server window and not 
            to wait for it before showing the controller window. 
            Defaults to False.

        """
        createGraphWindow(self, 3, self._size, yrange, title, wxnoserver)
        
    @property
    def size(self):
        """int. Table size in samples.""" 
        return self._size
    @size.setter
    def size(self, x): self.setSize(x)

    @property
    def tension(self):
        """float. Curvature tension.""" 
        return self._tension
    @tension.setter
    def tension(self, x): self.setTension(x)

    @property
    def bias(self):
        """float. Curve Attraction.""" 
        return self._bias
    @bias.setter
    def bias(self, x): self.setBias(x)

    @property
    def list(self):
        """list. List of tuples indicating location and value of each points in the table.""" 
        return self.getPoints()
    @list.setter
    def list(self, x): self.replace(x)

class ExpTable(PyoTableObject):
    """
    Construct a table from exponential interpolated segments.

    Parentclass: PyoTableObject

    Parameters:

    list : list, optional
        List of tuples indicating location and value of each points 
        in the table. The default, [(0,0.), (8192, 1.)], creates a 
        exponential line from 0.0 at location 0 to 1.0 at the end of 
        the table. Location must be an integer.
    exp : float, optional
        Exponent factor. Used to control the slope of the curve.
        Defaults to 10.
    inverse : boolean, optional
        If True, downward slope will be inversed. Useful to create 
        biexponential curves. Defaults to True.
    size : int, optional
        Table size in samples. Defaults to 8192.

    Methods:

    setSize(size) : Change the size of the table and rescale the envelope.
    setExp(x) : Replace the `exp` attribute.
    setInverse(x) : Replace the `inverse` attribute.
    replace(list) : Draw a new envelope according to the `list` parameter.
    loadRecFile(filename, tolerance) : Import an automation recording file.
    graph(yrange, title, wxnoserver) : Opens a grapher window to control 
        the shape of the envelope.

    Notes:

    Locations in the list must be in increasing order. If the last value 
    is less than size, the rest of the table will be filled with zeros.

    Attributes:

    list : list
        List of tuples [(location, value), ...].
    exp : float
        Exponent factor.
    inverse : boolean
        Inversion of downward slope.
    size : int, optional
        Table size in samples.

    Examples:

    >>> s = Server().boot()
    >>> s.start()
    >>> t = ExpTable([(0,0),(4096,1),(8192,0)], exp=5, inverse=True)
    >>> a = Osc(table=t, freq=2, mul=.3)
    >>> b = Sine(freq=[299,300], mul=a).out()

    """
    def __init__(self, list=[(0, 0.), (8192, 1.)], exp=10, inverse=True, size=8192):
        PyoTableObject.__init__(self)
        self._size = size
        self._exp = exp
        self._inverse = inverse
        self._base_objs = [ExpTable_base(list, exp, inverse, size)]

    def __dir__(self):
        return ['list', 'exp', 'inverse', 'size']
        
    def setSize(self, size):
        """
        Change the size of the table and rescale the envelope.
        
        Parameters:
        
        size : int
            New table size in samples.
        
        """
        self._size = size
        [obj.setSize(size) for obj in self._base_objs]

    def setExp(self, x):
        """
        Replace the `exp` attribute.
        
        Parameters:
        
        x : float
            New `exp` attribute.
        
        """
        self._exp = x
        [obj.setExp(x) for obj in self._base_objs]

    def setInverse(self, x):
        """
        Replace the `inverse` attribute.
        
        Parameters:
        
        x : boolean
            New `inverse` attribute.
        
        """
        self._inverse = x
        [obj.setInverse(x) for obj in self._base_objs]
  
    def replace(self, list):
        """
        Draw a new envelope according to the new `list` parameter.
        
        Parameters:
        
        list : list
            List of tuples indicating location and value of each points 
            in the table. Location must be integer.

        """
        self._list = list
        [obj.replace(list) for obj in self._base_objs]

    def loadRecFile(self, filename, tolerance=0.02):
        """
        Import an automation recording file in the table.

        loadRecFile takes a recording file, usually from a ControlRec object,
        as `filename` parameter, applies a filtering pre-processing to eliminate
        redundancies and loads the result in the table as a list of points. 
        Filtering process can be controled with the `tolerance` parameter. 

        Parameters:

        filename : string
            Full path of an automation recording file.
        tolerance : float, optional
            Tolerance of the filter. A higher value will eliminate more points.
            Defaults to 0.02.

        """
        _path, _name = os.path.split(filename)
        # files = sorted([f for f in os.listdir(_path) if _name+"_" in f])
        # if _name not in files: files.append(_name)
        files = [filename]
        for i, obj in enumerate(self._base_objs):
            p = os.path.join(_path, wrap(files,i))
            f = open(p, "r")
            values = [(float(l.split()[0]), float(l.split()[1])) for l in f.readlines()]
            scl = self._size / values[-1][0]
            values = [(int(v[0]*scl), v[1]) for v in values]
            f.close()
            values = reducePoints(values, tolerance=tolerance)
            self._list = values
            obj.replace(values)
        
    def getPoints(self):
        return self._base_objs[0].getPoints()

    def graph(self, yrange=(0.0, 1.0), title=None, wxnoserver=False):
        """
        Opens a grapher window to control the shape of the envelope.

        When editing the grapher with the mouse, the new set of points
        will be send to the object on mouse up. 

        Ctrl+C with focus on the grapher will copy the list of points to the 
        clipboard, giving an easy way to insert the new shape in a script.

        Parameters:

        yrange : tuple, optional
            Set the min and max values of the Y axis of the graph.
            Defaults to (0.0, 1.0).
        title : string, optional
            Title of the window. If none is provided, the name of the 
            class is used.
        wxnoserver : boolean, optional
            With wxPython graphical toolkit, if True, tells the 
            interpreter that there will be no server window and not 
            to wait for it before showing the controller window. 
            Defaults to False.

        """
        createGraphWindow(self, 2, self._size, yrange, title, wxnoserver)

    @property
    def size(self):
        """int. Table size in samples.""" 
        return self._size
    @size.setter
    def size(self, x): self.setSize(x)
    @property
    def exp(self):
        """float. Exponent factor.""" 
        return self._exp
    @exp.setter
    def exp(self, x): self.setExp(x)
    @property
    def inverse(self):
        """boolean. Inverse factor.""" 
        return self._inverse
    @inverse.setter
    def inverse(self, x): self.setInverse(x)
    @property
    def list(self):
        """list. List of tuples indicating location and value of each points in the table.""" 
        return self.getPoints()
    @list.setter
    def list(self, x): self.replace(x)

class SndTable(PyoTableObject):
    """
    Transfers data from a soundfile into a function table.

    If `chnl` is None, the table will contain as many table streams as 
    necessary to read all channels of the loaded sound.

    Parentclass: PyoTableObject

    Parameters:

    path : string
        Full path name of the sound.
    chnl : int, optional
        Channel number to read in. Available at initialization time only.
        The default (None) reads all channels.
    start : float, optional
        Begins reading at `start` seconds into the file. Available at 
        initialization time only. Defaults to 0.
    stop : float, optional
        Stops reading at `stop` seconds into the file. Available at 
        initialization time only. The default (None) means the end of 
        the file.

    Methods:

    setSound(path, start, stop) : Load a new sound in the table.
    append(path, crossfade, start, stop) : Append a sound in the table 
        with crossfade.
    insert(path, pos, crossfade, start, stop) : Insert a sound in the 
        table with crossfade.
    getDur(all) : Return the duration of the sound in seconds.
    getSize(all) : Return the size of the table in samples.
    getRate() : Return the frequency in cps at which the sound will be 
        read at its original pitch.
    getEnvelope(points) : Return the amplitude envelope of the table.
    view(title, wxnoserver, mouse_callback) : Opens a window showing the 
        contents of the table.

    Attributes:

    sound : Sound path loaded in the table.

    Examples:

    >>> s = Server().boot()
    >>> s.start()
    >>> snd_path = SNDS_PATH + '/transparent.aif'
    >>> t = SndTable(snd_path)
    >>> freq = t.getRate()
    >>> a = Osc(table=t, freq=[freq, freq*.995], mul=.3).out()

    """
    def __init__(self, path, chnl=None, start=0, stop=None, initchnls=1):
        PyoTableObject.__init__(self)
        self._path = path
        self._chnl = chnl
        self._start = start
        self._stop = stop
        self._size = []
        self._dur = []
        self._base_objs = []
        path, lmax = convertArgsToLists(path)
        if self._path == None:
            self._base_objs = [SndTable_base("", 0, 0) for i in range(initchnls)]
        else:
            for p in path:
                _size, _dur, _snd_sr, _snd_chnls, _format, _type = sndinfo(p)
                if chnl == None:
                    if stop == None:
                        self._base_objs.extend([SndTable_base(p, i, start) for i in range(_snd_chnls)])
                    else:
                        self._base_objs.extend([SndTable_base(p, i, start, stop) for i in range(_snd_chnls)])
                else:
                    if stop == None:
                        self._base_objs.append(SndTable_base(p, chnl, start))
                    else:
                        self._base_objs.append(SndTable_base(p, chnl, start, stop))
                self._size.append(self._base_objs[-1].getSize())
                self._dur.append(self._size[-1] / float(_snd_sr))
            if lmax == 1:
                self._size = self._base_objs[-1].getSize()
                self._dur = self._size / float(_snd_sr)

    def __dir__(self):
        return ['path', 'chnl', 'start', 'stop']

    def setSound(self, path, start=0, stop=None):
        """
        Load a new sound in the table.
        
        Keeps the number of channels of the sound loaded at initialization.
        If the new sound has less channels, it will wrap around and load 
        the same channels many times. If the new sound has more channels, 
        the extra channels will be skipped.
        
        Parameters:
        
        path : string
            Full path of the new sound.
        start : float, optional
            Begins reading at `start` seconds into the file. Defaults to 0.
        stop : float, optional
            Stops reading at `stop` seconds into the file. The default (None) 
            means the end of the file.

        """
        self._path = path
        if type(path) == ListType:
            self._size = []
            self._dur = []
            path, lmax = convertArgsToLists(path)
            for i, obj in enumerate(self._base_objs):
                p = path[i%lmax]
                _size, _dur, _snd_sr, _snd_chnls, _format, _type = sndinfo(p)
                self._size.append(_size)
                self._dur.append(_dur)
                if stop == None:
                    obj.setSound(p, 0, start)
                else:
                    obj.setSound(p, 0, start, stop)
        else:
            _size, _dur, _snd_sr, _snd_chnls, _format, _type = sndinfo(path)
            self._size = _size
            self._dur = _dur
            if stop == None:
                [obj.setSound(path, (i%_snd_chnls), start) for i, obj in enumerate(self._base_objs)]
            else:
                [obj.setSound(path, (i%_snd_chnls), start, stop) for i, obj in enumerate(self._base_objs)]

    def append(self, path, crossfade=0, start=0, stop=None):
        """
        Append a sound to the one already in the table with crossfade.

        Keeps the number of channels of the sound loaded at initialization.
        If the new sound has less channels, it will wrap around and load 
        the same channels many times. If the new sound has more channels, 
        the extra channels will be skipped.

        Parameters:

        path : string
            Full path of the new sound.
        crossfade : float, optional
            Crossfade time, in seconds, between the sound already in the table 
            and the new one. Defaults to 0.
        start : float, optional
            Begins reading at `start` seconds into the file. Defaults to 0.
        stop : float, optional
            Stops reading at `stop` seconds into the file. The default, None, 
            means the end of the file.

        """
        if type(path) == ListType:
            self._size = []
            self._dur = []
            path, lmax = convertArgsToLists(path)
            for i, obj in enumerate(self._base_objs):
                p = path[i%lmax]
                _size, _dur, _snd_sr, _snd_chnls, _format, _type = sndinfo(p)
                self._size.append(_size)
                self._dur.append(_dur)
                if stop == None:
                    obj.append(p, crossfade, 0, start)
                else:
                    obj.append(p, crossfade, 0, start, stop)
        else:
            _size, _dur, _snd_sr, _snd_chnls, _format, _type = sndinfo(path)
            self._size = _size
            self._dur = _dur
            if stop == None:
                [obj.append(path, crossfade, (i%_snd_chnls), start) for i, obj in enumerate(self._base_objs)]
            else:
                [obj.append(path, crossfade, (i%_snd_chnls), start, stop) for i, obj in enumerate(self._base_objs)]

    def insert(self, path, pos=0, crossfade=0, start=0, stop=None):
        """
        Insert a sound into the one already in the table with crossfade.

        Insert a sound at position `pos`, specified in seconds, 
        with crossfading at the beginning and the end of the insertion.
        
        Keeps the number of channels of the sound loaded at initialization.
        If the new sound has less channels, it will wrap around and load 
        the same channels many times. If the new sound has more channels, 
        the extra channels will be skipped.

        Parameters:

        path : string
            Full path of the new sound.
        pos : float, optional
            Position in the table, in seconds, where to insert the new sound.
            Defaults to 0.
        crossfade : float, optional
            Crossfade time, in seconds, between the sound already in the table 
            and the new one. Defaults to 0.
        start : float, optional
            Begins reading at `start` seconds into the file. Defaults to 0.
        stop : float, optional
            Stops reading at `stop` seconds into the file. The default, None, 
            means the end of the file.

        """
        if type(path) == ListType:
            self._size = []
            self._dur = []
            path, lmax = convertArgsToLists(path)
            for i, obj in enumerate(self._base_objs):
                p = path[i%lmax]
                _size, _dur, _snd_sr, _snd_chnls, _format, _type = sndinfo(p)
                self._size.append(_size)
                self._dur.append(_dur)
                if stop == None:
                    obj.insert(p, pos, crossfade, 0, start)
                else:
                    obj.insert(p, pos, crossfade, 0, start, stop)
        else:
            _size, _dur, _snd_sr, _snd_chnls, _format, _type = sndinfo(path)
            self._size = _size
            self._dur = _dur
            if stop == None:
                [obj.insert(path, pos, crossfade, (i%_snd_chnls), start) for i, obj in enumerate(self._base_objs)]
            else:
                [obj.insert(path, pos, crossfade, (i%_snd_chnls), start, stop) for i, obj in enumerate(self._base_objs)]

    def getRate(self):
        """
        Return the frequency in cps at which the sound will be read at its 
        original pitch.
        
        """
        if type(self._path) == ListType:
            return [obj.getRate() for obj in self._base_objs]
        else:
            return self._base_objs[0].getRate()

    def getDur(self, all=True):
        """
        Return the duration of the sound in seconds.
        
        Parameters:
        
        all : boolean
            If the table contains more than one sound and `all` is True,
            returns a list of all durations. Otherwise, returns only the
            first duration as a float.
        
        """
        if type(self._path) == ListType:
            _dur = [1./obj.getRate() for obj in self._base_objs]
        else:    
            _dur = 1./self._base_objs[0].getRate()

        if all:
            return _dur
        else:
            if type(_dur) == ListType:
                return _dur[0]
            else:
                return _dur

    def getSize(self, all=True):
        """
        Return the size of the table in samples.

        Parameters:
        
        all : boolean
            If the table contains more than one sound and `all` is True,
            returns a list of all sizes. Otherwise, returns only the
            first size as an int.

        """
        if len(self._base_objs) > 1:
            _size = [obj.getSize() for obj in self._base_objs]
        else:
            _size = self._base_objs[0].getSize()

        if all:
            return _size
        else:
            if type(_size) == ListType:
                return _size[0]
            else:
                return _size

    def getEnvelope(self, points):
        """
        Return the amplitude envelope of the table.
        
        Return a list, of length `chnl`, of lists of length `points` filled 
        with the amplitude envelope of the table.
        
        Parameters:
        
        points : int
            Number of points of the amplitude analysis.

        """
        return [obj.getEnvelope(points) for obj in self._base_objs]

    def view(self, title="Sound waveform", wxnoserver=False, mouse_callback=None):
        """
        Opens a window showing the contents of the table.

        Parameters:

        title : string, optional
            Window title. Defaults to "Table waveform". 
        wxnoserver : boolean, optional
            With wxPython graphical toolkit, if True, tells the 
            interpreter that there will be no server window and not 
            to wait for it before showing the table window. 
            Defaults to False.
        mouse_callback : callable
            If provided, this function will be called with the mouse 
            position, inside the frame, as argument. Defaults to None.

        """
        createSndViewTableWindow(self, title, wxnoserver, self.__class__.__name__, mouse_callback)

    @property
    def sound(self):
        """string. Full path of the sound.""" 
        return self._path
    @sound.setter
    def sound(self, x): self.setSound(x)
    @property
    def path(self):
        """string. Full path of the sound.""" 
        return self._path
    @path.setter
    def path(self, x): self.setSound(x)
    @property
    def chnl(self):
        """int. Channel to read in.""" 
        return self._chnl
    @chnl.setter
    def chnl(self, x): print "'chnl' attribute is read-only."
    @property
    def start(self):
        """float. Start point, in seconds, to read into the file.""" 
        return self._start
    @start.setter
    def start(self, x): print "'start' attribute is read-only."
    @property
    def stop(self):
        """float. Stop point, in seconds, to read into the file.""" 
        return self._stop
    @stop.setter
    def stop(self, x): print "'stop' attribute is read-only."

class NewTable(PyoTableObject):
    """
    Create an empty table ready for recording. 

    See `TableRec` to write samples in the table.

    Parentclass: PyoTableObject

    Parameters:

    length : float
        Length of the table in seconds.
    chnls : int, optional
        Number of channels that will be handled by the table. 
        Defaults to 1.
    init : list of floats, optional
        Initial table. List of list can match the number of channels,
        otherwise, the list will be loaded in all tablestreams. 
        Defaults to None.
    feedback : float, optional
        Amount of old data to mix with a new recording. Defaults to 0.0.

    Methods:

    getSize() : Returns the length of the table in samples.
    getLength() : Returns the length of the table in seconds.
    getDur() : Returns the length of the table in seconds.
    getRate() : Returns the frequency (cycle per second) to give 
        to an oscillator to read the sound at its original pitch.
    replace() : Replaces the actual table.
    setFeedback() : Replace the `feedback` attribute.

    Attributes:

    feedback : float. Amount of old data to mix with a new recording.

    See also: DataTable, TableRec

    Examples:

    >>> s = Server(duplex=1).boot()
    >>> s.start()
    >>> t = NewTable(length=2, chnls=1)
    >>> a = Input(0)
    >>> b = TableRec(a, t, .01)
    >>> amp = Iter(b["trig"], [.5])
    >>> freq = t.getRate()
    >>> c = Osc(table=t, freq=[freq, freq*.99], mul=amp).out()
    >>> # to record in the empty table, call:
    >>> # b.play()

    """
    def __init__(self, length, chnls=1, init=None, feedback=0.0):
        PyoTableObject.__init__(self)
        self._length = length
        self._chnls = chnls
        self._init = init
        self._feedback = feedback
        if init == None:
            self._base_objs = [NewTable_base(length, feedback=feedback) for i in range(chnls)]
        else:
            if type(init[0]) != ListType: 
                init = [init]
            self._base_objs = [NewTable_base(length, wrap(init,i), feedback) for i in range(chnls)]

    def __dir__(self):
        return ['length', 'chnls', 'init', 'feedback']

    def replace(self, x):
        """
        Replaces the actual table.
        
        Parameters:
        
        x : list of floats
            New table. Must be of the same size as the actual table.
            List of list can match the number of channels, otherwise, 
            the list will be loaded in all tablestreams.

        """
        if type(x[0]) != ListType: 
            x = [x]
        [obj.setTable(wrap(x,i)) for i, obj in enumerate(self._base_objs)]

    def setFeedback(self, x):
        """
        Replaces the`feedback` attribute.

        Parameters:

        x : float
            New `feedback` value.

        """
        self._feedback = x
        [obj.setFeedback(x) for i, obj in enumerate(self._base_objs)]
        
    def getSize(self):
        """
        Returns the length of the table in samples.
        
        """
        return self._base_objs[0].getSize()

    def getLength(self):
        """
        Returns the length of the table in seconds.
        
        """
        return self._base_objs[0].getLength()

    def getDur(self):
        """
        Returns the length of the table in seconds.
        
        """
        return self._base_objs[0].getLength()
        
    def getRate(self):
        """
        Returns the frequency (cycle per second) to give to an 
        oscillator to read the sound at its original pitch.
        
        """
        return self._base_objs[0].getRate()

    def view(self, title="Sound waveform", wxnoserver=False, mouse_callback=None):
        """
        Opens a window showing the contents of the table.

        Parameters:

        title : string, optional
            Window title. Defaults to "Table waveform". 
        wxnoserver : boolean, optional
            With wxPython graphical toolkit, if True, tells the 
            interpreter that there will be no server window and not 
            to wait for it before showing the table window. 
            Defaults to False.
        mouse_callback : callable
            If provided, this function will be called with the mouse 
            position, inside the frame, as argument. Defaults to None.

        """
        createSndViewTableWindow(self, title, wxnoserver, self.__class__.__name__, mouse_callback)

    @property
    def length(self):
        """float. Length of the table in seconds.""" 
        return self._length
    @length.setter
    def length(self, x): print "'length' attribute is read-only."
    @property
    def chnls(self):
        """int. Number of channels that will be handled by the table.""" 
        return self._chnls
    @chnls.setter
    def chnls(self, x): print "'chnls' attribute is read-only."
    @property
    def init(self):
        """list of floats. Initial table.""" 
        return self._init
    @init.setter
    def init(self, x): print "'init' attribute is read-only."
    @property
    def feedback(self):
        """float. Amount of old data to mix with a new recording.""" 
        return self._feedback
    @feedback.setter
    def feedback(self, x): self.setFeedback(x)

class DataTable(PyoTableObject):
    """
    Create an empty table ready for data recording.

    See `TableRec` to write samples in the table.

    Parentclass: PyoTableObject

    Parameters:

    size : int
        Size of the table in samples.
    chnls : int, optional
        Number of channels that will be handled by the table. 
        Defaults to 1.
    init : list of floats, optional
        Initial table. List of list can match the number of channels,
        otherwise, the list will be loaded in all tablestreams. 
        Defaults to None.

    Methods:

    getSize() : Returns the length of the table in samples.
    getRate() : Returns the frequency (cycle per second) to give 
        to an oscillator to read the sound at its original pitch.
    replace() : Replaces the actual table.

    See also: NewTable, TableRec

    Examples:

    >>> s = Server().boot()
    >>> s.start()
    >>> import random
    >>> notes = [midiToHz(random.randint(48,72)) for i in range(10)]
    >>> tab = DataTable(size=10, init=notes)
    >>> ind = RandInt(10, 8)
    >>> pit = TableIndex(tab, ind)
    >>> a = SineLoop(freq=[pit,pit*0.99], feedback = 0.07, mul=.2).out()

    """
    def __init__(self, size, chnls=1, init=None):
        PyoTableObject.__init__(self)
        self._size = size
        self._chnls = chnls
        self._init = init
        if init == None:
            self._base_objs = [DataTable_base(size) for i in range(chnls)]
        else:
            if type(init[0]) != ListType:
                init = [init]
            self._base_objs = [DataTable_base(size, wrap(init,i)) for i in range(chnls)]


    def __dir__(self):
        return ['size', 'chnls', 'init']

    def replace(self, x):
        """
        Replaces the actual table.

        Parameters:

        x : list of floats
            New table. Must be of the same size as the actual table.
            List of list can match the number of channels, otherwise, 
            the list will be loaded in all tablestreams.

        """
        if type(x[0]) != ListType: 
            x = [x]
        [obj.setTable(wrap(x,i)) for i, obj in enumerate(self._base_objs)]

    def getSize(self):
        """
        Returns the length of the table in samples.

        """
        return self._base_objs[0].getSize()

    def getRate(self):
        """
        Returns the frequency (cycle per second) to give to an 
        oscillator to read the sound at its original pitch.

        """
        return self._base_objs[0].getRate()

    @property
    def size(self):
        """int. Length of the table in samples.""" 
        return self._size
    @size.setter
    def size(self, x): print "'size' attribute is read-only."
    @property
    def chnls(self):
        """int. Number of channels that will be handled by the table.""" 
        return self._chnls
    @chnls.setter
    def chnls(self, x): print "'chnls' attribute is read-only."
    @property
    def init(self):
        """list of floats. Initial table.""" 
        return self._init
    @init.setter
    def init(self, x): print "'init' attribute is read-only."

