//  ------------------------------------------------------------------------
//
//  Copyright (C) 2011-2012 Fons Adriaensen <fons@linuxaudio.org>
//
//  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 3 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, see <http://www.gnu.org/licenses/>.
//
//  ------------------------------------------------------------------------


#ifndef __DELAYPROC_H
#define __DELAYPROC_H


#include <string.h>


// Audio delay class.
// 
//   maxdel = maximum delay in samples.
//   period = number of samples guaranteed to be available for
//            linear reading or writing, i.e. without wraparound. 
//
// To read or write: 
//    * Get pointer.
//    * Read or write up to 'period' samples.
//    * Commit number of samples used.

class Delayproc
{
public:

    Delayproc (int maxdel, int period);
    ~Delayproc (void);

    // Clear buffer.
    void reset (void);

    // Delay must be in the range 0...'maxdel'.
    void set_delay (int k)
    {
	_rdind = _wrind - k;
	if (_rdind < 0) _rdind += _length;
    }

    // Get write or read pointer. Either can be used as
    // the base of an array of size 'period' samples.
    float *wr_ptr (void) const { return _data + _wrind; }
    float *rd_ptr (void) const { return _data + _rdind; }

    // Commit writing of k samples.
    void wr_commit (int k)
    {
	int    a, b, n;
	float  *p, *q;

        // This ensures that the first and last period sized
        // blocks remain identical, allowing linear access
        // for both reading and writing.
	a = _wrind;
	b = a + k;
        n = b - _length;
	if (n >= 0)
	{
	    _wrind = n;
	    if (n > 0)
	    {
                p = _data;
                q = p + _length;
                memcpy (p, q, n * sizeof (float));
	    }
	}
	else
	{
	    _wrind = b;
	    n = _period - a;
	    if (n > 0)
	    {
                q = _data + a;
                p = q + _length;
                if (n > k) n = k;
                memcpy (p, q, n * sizeof (float));
	    }
	}
    }

    // Commit reading of k samples.
    void rd_commit (int k)
    {
	_rdind += k;
	if (_rdind >= _length) _rdind -= _length;
    }

private:

    int     _length;
    int     _period;
    int     _wrind;
    int     _rdind;
    float  *_data;
};


#endif
