/*
 * Copyright (c) 2001-2003 Shiman Associates Inc. All Rights Reserved.
 * 
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use, copy,
 * modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <math.h>
#include "mas/mas_dpi.h"
#include "funcparam.h"

char* funcstring[] = { "sine wave",
                       "triangle wave",
                       "square wave",
                       "sawtooth wave",
                       "white noise",
                       "pink noise",
                       "" };

struct mas_data*
gen_sine_wave( struct func_params* fp, int seglen )
{
    struct mas_data* data;
    int i;

    data = MAS_NEW( data );
    masc_setup_data( data, seglen );
    if ( data == NULL ) return 0;
    
    data->length = seglen;

    for (i=0; i<fp->segsize; i++)
    {
	((int16*)data->segment)[2*i] = fp->A *
	    sin(fp->inside * (float)fp->samplenum);
	((int16*)data->segment)[2*i + 1] = ((int16*)data->segment)[2*i];
	fp->samplenum = (++(fp->samplenum)) % fp->samples_in_period;

        /* see if we have to recompute the inside to change the
           frequency.  This allows a sinewave to finish its period before
           changing the frequency, making frequency changes smooth.*/
        if ( fp->samplenum == 0 && fp->recompute)
        {
            fp->recompute = FALSE;
            fp->freq = fp->nextfreq;
            fp->inside = 2.0 * M_PI * fp->freq / (float)fp->rate;
            fp->samples_in_period = (int)(fp->rate/fp->freq);
            fp->samplenum = 0;
        }
    }

    return data;
}

struct mas_data*
gen_triangle_wave( struct func_params* fp, int seglen )
{
    struct mas_data* data;
    int i;
    
    data = MAS_NEW( data );
    masc_setup_data( data, seglen );
    if ( data == NULL ) return 0;
    
    data->length = seglen;

    for (i=0; i<fp->segsize; i++)
    {
        if ( fp->samplenum < fp->samples_in_period/2 )
            ((int16*)data->segment)[2*i] = fp->A - fp->inside * fp->samplenum;
        else
            ((int16*)data->segment)[2*i] = fp->inside * fp->samplenum - 3 * fp->A;
        ((int16*)data->segment)[2*i + 1] = ((int16*)data->segment)[2*i];
	fp->samplenum = (++(fp->samplenum)) % fp->samples_in_period;

        /* see if we have to recompute the inside to change the
           frequency.  This allows a sinewave to finish its period before
           changing the frequency, making frequency changes smooth.*/
        if ( fp->samplenum == 0 && fp->recompute)
        {
            fp->recompute = FALSE;
            fp->freq = fp->nextfreq;
            fp->inside = 4.0 * fp->A * fp->freq / (float)fp->rate;
            fp->samples_in_period = (int)(fp->rate/fp->freq);
            fp->samplenum = 0;
        }
    }

    return data;
}

struct mas_data*
gen_sawtooth_wave( struct func_params* fp, int seglen )
{
    struct mas_data* data;
    int i;
    
    data = MAS_NEW( data );
    masc_setup_data( data, seglen );
    if ( data == NULL ) return 0;
    
    data->length = seglen;

    for (i=0; i<fp->segsize; i++)
    {
        ((int16*)data->segment)[2*i] = fp->inside * fp->samplenum - fp->A;
        
        ((int16*)data->segment)[2*i + 1] = ((int16*)data->segment)[2*i];
	fp->samplenum = (++(fp->samplenum)) % fp->samples_in_period;

        /* see if we have to recompute the inside to change the
           frequency.  This allows a sinewave to finish its period before
           changing the frequency, making frequency changes smooth.*/
        if ( fp->samplenum == 0 && fp->recompute)
        {
            fp->recompute = FALSE;
            fp->freq = fp->nextfreq;
            fp->inside = 2.0 * fp->A * fp->freq / (float)fp->rate;
            fp->samples_in_period = (int)(fp->rate/fp->freq);
            fp->samplenum = 0;
        }
    }

    return data;
}

struct mas_data*
gen_square_wave( struct func_params* fp, int seglen )
{
    struct mas_data* data;
    int i;
    int16 val;

    data = MAS_NEW( data );
    masc_setup_data( data, seglen );
    if ( data == NULL ) return 0;
    
    data->length = seglen;

    for (i=0; i<fp->segsize; i++)
    {
        /* square wave is +A for first half of period and -A for
           second half. */
        if ( fp->samplenum == 0 )
            val = fp->A; /* avoid divide by zero */
        else if ( fp->samples_in_period / fp->samplenum > 2 )
            val = fp->A;
        else val = -fp->A;
        
	((int16*)data->segment)[2*i] = val;
        ((int16*)data->segment)[2*i + 1] = val;
	fp->samplenum = (++(fp->samplenum)) % fp->samples_in_period;

        /* see if we have to change the frequency.  This allows the
           square wave to finish its period before changing the
           frequency, making frequency changes smooth (although, it is
           a square wave).  (the weird compare is because they're both
           floats) */
        if ( fp->samplenum == 0 && fp->recompute )
        {
            fp->recompute = FALSE;
            fp->freq = fp->nextfreq;
            fp->samples_in_period = (int)(fp->rate/fp->freq);
            fp->samplenum = 0;
        }
    }

    return data;
}

struct mas_data*
gen_white_noise( struct func_params* fp, int seglen )
{
    struct mas_data* data;
    int i;

    data = MAS_NEW( data );
    masc_setup_data( data, seglen );
    if ( data == NULL ) return 0;

    data->length = seglen;

    fp->recompute = FALSE;
    for (i=0; i<fp->segsize; i++)
    {
        /* left and right channels are duplicated */
	((int16*)data->segment)[2*i] = (int16) fp->A - (2*fp->A*rand()/(RAND_MAX+1.0));
	((int16*)data->segment)[2*i + 1] = ((int16*)data->segment)[2*i];
    }
    
    return data;
}

/*
 * From http://shoko.calarts.edu/~glmrboy/musicdsp/sourcecode/pink.txt
 *
 * Filter to make pink noise from white  (updated March 2000)
 * ------------------------------------
 * 
 * This is an approximation to a -10dB/decade filter using a weighted
 * sum of first order filters. It is accurate to within +/-0.05dB
 * above 9.2Hz (44100Hz sampling rate). Unity gain is at Nyquist, but
 * can be adjusted by scaling the numbers at the end of each line.
 * 
 * If 'white' consists of uniform random numbers, such as those
 * generated by the rand() function, 'pink' will have an almost
 * gaussian level distribution.
 *
 *
 * b0 = 0.99886 * b0 + white * 0.0555179;
 * b1 = 0.99332 * b1 + white * 0.0750759;
 * b2 = 0.96900 * b2 + white * 0.1538520;
 * b3 = 0.86650 * b3 + white * 0.3104856;
 * b4 = 0.55000 * b4 + white * 0.5329522;
 * b5 = -0.7616 * b5 - white * 0.0168980;
 * pink = b0 + b1 + b2 + b3 + b4 + b5 + b6 + white * 0.5362;
 * b6 = white * 0.115926;
 *
 *
 * An 'economy' version with accuracy of +/-0.5dB is also available.
 *
 * b0 = 0.99765 * b0 + white * 0.0990460;
 * b1 = 0.96300 * b1 + white * 0.2965164;
 * b2 = 0.57000 * b2 + white * 1.0526913;
 * pink = b0 + b1 + b2 + white * 0.1848;
 *
 * ---
 * paul.kellett@maxim.abel.co.uk
 * http://www.abel.co.uk/~maxim/
 *
 */

struct mas_data*
gen_pink_noise( struct func_params* fp, int seglen )
{
    struct mas_data* data;
    int i;
    int white, pink;

    fp->recompute = FALSE;
    
    /* start by generating white noise */
    data = gen_white_noise( fp, seglen );
    if ( data == 0 ) return data;

    /* this doesn't really work.... */
    for (i=0; i<fp->segsize; i++)
    {
        white = ((int16*)data->segment)[2*i];
        fp->pf.b0 = 0.99886 * fp->pf.b0 + white * 0.0555179;
        fp->pf.b1 = 0.99332 * fp->pf.b1 + white * 0.0750759;
        fp->pf.b2 = 0.96900 * fp->pf.b2 + white * 0.1538520;
        fp->pf.b3 = 0.86650 * fp->pf.b3 + white * 0.3104856;
        fp->pf.b4 = 0.55000 * fp->pf.b4 + white * 0.5329522;
        fp->pf.b5 = -0.7616 * fp->pf.b5 - white * 0.0168980;
        pink = fp->pf.b0 + fp->pf.b1 + fp->pf.b2 + fp->pf.b3 + fp->pf.b4 + fp->pf.b5 + fp->pf.b6 + white * 0.5362;
        fp->pf.b6 = white * 0.115926;

        pink >>= 2;
        /* set the noise to the new pink value */
        /* left and right channels are duplicated */
	((int16*)data->segment)[2*i] = (int16) pink;
	((int16*)data->segment)[2*i + 1] = ((int16*)data->segment)[2*i];
    }
    
    return data;
}

int32
fadeout( struct func_params* fp, struct mas_data* data )
{
    int i;
    
    if ( data == 0 ) return mas_error(MERR_INVALID);
    if ( data->segment == 0 ) return mas_error(MERR_INVALID);
    
    for (i=0; i<fp->segsize; i++)
    {
	((int16*)data->segment)[2*i] *= (float)(fp->segsize - i)/fp->segsize;
	((int16*)data->segment)[2*i + 1] *= (float)(fp->segsize - i)/fp->segsize;
        printf("%d ", ((int16*)data->segment)[2*i] );
    }

    return 0;
}

int32
cutout( struct func_params* fp, struct mas_data* data )
{
    int i;
    int16 a, b;
    int cut=0;

    if ( data == 0 ) return mas_error(MERR_INVALID);
    if ( data->segment == 0 ) return mas_error(MERR_INVALID);
    
    for (i=1; i<fp->segsize; i++)
    {
        if (!cut)
        {
            a = ((int16*)data->segment)[2*(i - 1)];
            b = ((int16*)data->segment)[2*i];

            /* find a zero crossing */
            if ( a >= 0 && b <= 0 ) cut = TRUE;
            if ( a <= 0 && b >= 0 ) cut = TRUE;
        }
        else /* set both channels to zero */
        {
            ((int16*)data->segment)[2*i] = 0;
            ((int16*)data->segment)[2*i + 1] = 0;
        }
        
    }

    return 0;
}

