/*
 * Do anything required when you stop reading samples.  
 * Don't close input file! 
 */
int st_vol_stop(eff_t effp)
{
    vol_t vol = (vol_t) effp->priv;
    if (vol->limited)
    {
        st_warn("VOL limited %d values (%d percent).", 
             vol->limited, (int) (vol->limited * 100.0 / vol->totalprocessed));
    }
    if (vol->clipped) 
    {
        st_warn("VOL clipped %d values, amplitude gain=%f too high...", 
             vol->clipped, vol->gain);
    }
    return ST_SUCCESS;
}
/*
 * Process tail of input samples.
 */
int st_resample_drain(eff_t effp, st_sample_t *obuf, st_size_t *osamp)
{
	resample_t r = (resample_t) effp->priv;
	long isamp_res, osamp_res;
	st_sample_t *Obuf;
	int rc;

	/* fprintf(stderr,"Xoff %d, Xt %d  <--- DRAIN\n",r->Xoff, r->Xt); */

	/* stuff end with Xoff zeros */
	isamp_res = r->Xoff;
	osamp_res = *osamp;
	Obuf = obuf;
	while (isamp_res>0 && osamp_res>0) {
		st_sample_t Isamp, Osamp;
		Isamp = isamp_res;
		Osamp = osamp_res;
		rc = st_resample_flow(effp, NULL, Obuf, (st_size_t *)&Isamp, (st_size_t *)&Osamp);
		if (rc)
		    return rc;
	  /* fprintf(stderr,"DRAIN isamp,osamp  (%d,%d) -> (%d,%d)\n",
		     isamp_res,osamp_res,Isamp,Osamp); */
		Obuf += Osamp;
		osamp_res -= Osamp;
		isamp_res -= Isamp;
	}
	*osamp -= osamp_res;
	/* fprintf(stderr,"DRAIN osamp %d\n", *osamp); */
	if (isamp_res)
		st_warn("drain overran obuf by %d\n", isamp_res); fflush(stderr);
	return (ST_SUCCESS);
}
/*
 * Drain out remaining samples if the effect generates any.
 */
int st_fade_drain(eff_t effp, st_sample_t *obuf, st_size_t *osamp)
{
    fade_t fade = (fade_t) effp->priv;
    int len, t_chan = 0;

    len = *osamp;
    *osamp = 0;

    if (fade->do_out && fade->samplesdone < fade->out_stop &&
        !(fade->endpadwarned))
    { /* Warning about padding silence into end of sample */
        st_warn("Fade: warning: End time passed end-of-file. Padding with silence");
        fade->endpadwarned = 1;
    } /* endif endpadwarned */

    for (;len && (fade->do_out &&
                  fade->samplesdone < fade->out_stop); len--)
    {
        *obuf = 0;
        obuf++;
        *osamp += 1;

        t_chan++;
        if (t_chan >= effp->ininfo.channels)
        {
            fade->samplesdone += 1;
            t_chan = 0;
        } /* endif channels */
    } /* endfor */

    if (fade->do_out && fade->samplesdone >= fade->out_stop)
        return ST_EOF;
    else
        return ST_SUCCESS;
}
/*
 * Do anything required when you stop reading samples.  
 * Don't close input file! 
 */
int st_pitch_stop(eff_t effp)
{
    pitch_t pitch = (pitch_t) effp->priv;

    free(pitch->fade);
    free(pitch->tmp);
    free(pitch->acc);
    free(pitch->buf);

    if (pitch->clipped)
        st_warn("PITCH clipped %d values... adjust volume with -v option maybe?", 
             pitch->clipped);

    return ST_SUCCESS;
}
int st_wvestopwrite(ft_t ft)
{

        /* Call before seeking to flush buffer */
        return st_rawstopwrite(ft);

        if (!ft->seekable)
        {
            st_warn("Header will be have invalid file length since file is not seekable");
            return ST_SUCCESS;
        }

        if (st_seeki(ft, 0L, 0) != 0)
        {
                st_fail_errno(ft,errno,"Can't rewind output file to rewrite Psion header.");
                return(ST_EOF);
        }
        wvewriteheader(ft);
}
/* Do window management once we have a complete window, including mangling
 * the current window. */
static int process_window(reddata_t data, int chan_num, int num_chans,
                          st_sample_t *obuf, int len) {
    int j;
    float* nextwindow;
    int use = min(len, WINDOWSIZE)-(WINDOWSIZE/2);
    chandata_t *chan = &(data->chandata[chan_num]);
    int first = (chan->lastwindow == NULL);

    nextwindow = (float*)calloc(WINDOWSIZE, sizeof(float));
    memcpy(nextwindow, chan->window+WINDOWSIZE/2,
           sizeof(float)*(WINDOWSIZE/2));

    reduce_noise(chan, chan->window, data->threshold);
        
    if (!first) {
        for (j = 0; j < use; j ++) {
            float s = chan->window[j] + chan->lastwindow[WINDOWSIZE/2 + j];
            if (s < -1 || s > 1) {
                float news;
                if (s > 1)
                    news = 1;
                else
                    news = -1;

                st_warn("noisered: Output clipped from %f to %f.\n",
                        s, news);
            }
            obuf[chan_num + num_chans * j] =
                ST_FLOAT_DWORD_TO_SAMPLE(s);
        }
        free(chan->lastwindow);
    } else {
        for (j = 0; j < use; j ++) {
            assert(chan->window[j] >= -1 && chan->window[j] <= 1);
            obuf[chan_num + num_chans * j] =
                ST_FLOAT_DWORD_TO_SAMPLE(chan->window[j]);
        }
    }
    chan->lastwindow = chan->window;
    chan->window = nextwindow;

    return use;
}
/*
 * Start processing
 */
int st_vol_start(eff_t effp)
{
    vol_t vol = (vol_t) effp->priv;
    
    if (effp->outinfo.channels != effp->ininfo.channels)
    {
        st_warn("VOL cannot handle different channels (in=%d, out=%d)"
             " use avg or pan", effp->ininfo.channels, effp->outinfo.channels);
    }

    if (effp->outinfo.rate != effp->ininfo.rate)
    {
        st_fail("VOL cannot handle different rates (in=%ld, out=%ld)"
             " use resample or rate", effp->ininfo.rate, effp->outinfo.rate);
        return ST_EOF;
    }

    vol->clipped = 0;
    vol->limited = 0;
    vol->totalprocessed = 0;

    return ST_SUCCESS;
}
/*
 * Prepare for processing.
 */
int st_chorus_start(eff_t effp)
{
        chorus_t chorus = (chorus_t) effp->priv;
        int i;
        float sum_in_volume;

        chorus->maxsamples = 0;

        if ( chorus->in_gain < 0.0 )
        {
                st_fail("chorus: gain-in must be positive!\n");
                return (ST_EOF);
        }
        if ( chorus->in_gain > 1.0 )
        {
                st_fail("chorus: gain-in must be less than 1.0!\n");
                return (ST_EOF);
        }
        if ( chorus->out_gain < 0.0 )
        {
                st_fail("chorus: gain-out must be positive!\n");
                return (ST_EOF);
        }
        for ( i = 0; i < chorus->num_chorus; i++ ) {
                chorus->samples[i] = (int) ( ( chorus->delay[i] + 
                        chorus->depth[i] ) * effp->ininfo.rate / 1000.0);
                chorus->depth_samples[i] = (int) (chorus->depth[i] * 
                        effp->ininfo.rate / 1000.0);

                if ( chorus->delay[i] < 20.0 )
                {
                        st_fail("chorus: delay must be more than 20.0 msec!\n");
                        return (ST_EOF);
                }
                if ( chorus->delay[i] > 100.0 )
                {
                        st_fail("chorus: delay must be less than 100.0 msec!\n");
                        return (ST_EOF);
                }
                if ( chorus->speed[i] < 0.1 )
                {
                        st_fail("chorus: speed must be more than 0.1 Hz!\n");
                        return (ST_EOF);
                }
                if ( chorus->speed[i] > 5.0 )
                {
                        st_fail("chorus: speed must be less than 5.0 Hz!\n");
                        return (ST_EOF);
                }
                if ( chorus->depth[i] < 0.0 )
                {
                        st_fail("chorus: delay must be more positive!\n");
                        return (ST_EOF);
                }
                if ( chorus->depth[i] > 10.0 )
                {
                    st_fail("chorus: delay must be less than 10.0 msec!\n");
                    return (ST_EOF);
                }
                if ( chorus->decay[i] < 0.0 )
                {
                        st_fail("chorus: decay must be positive!\n" );
                        return (ST_EOF);
                }
                if ( chorus->decay[i] > 1.0 )
                {
                        st_fail("chorus: decay must be less that 1.0!\n" );
                        return (ST_EOF);
                }
                chorus->length[i] = effp->ininfo.rate / chorus->speed[i];
                if (! (chorus->lookup_tab[i] = 
                        (int *) malloc(sizeof (int) * chorus->length[i])))
                {
                        st_fail("chorus: Cannot malloc %d bytes!\n", 
                                sizeof(int) * chorus->length[i]);
                        return (ST_EOF);
                }
                if ( chorus->modulation[i] == MOD_SINE )
                        st_sine(chorus->lookup_tab[i], chorus->length[i], 
                                chorus->depth_samples[i] - 1,
                                chorus->depth_samples[i]);
                else
                        st_triangle(chorus->lookup_tab[i], chorus->length[i], 
                                chorus->samples[i] - 1,
                                chorus->depth_samples[i]);
                chorus->phase[i] = 0;

                if ( chorus->samples[i] > chorus->maxsamples )
                        chorus->maxsamples = chorus->samples[i];
        }

        /* Be nice and check the hint with warning, if... */
        sum_in_volume = 1.0;
        for ( i = 0; i < chorus->num_chorus; i++ )
                sum_in_volume += chorus->decay[i];
        if ( chorus->in_gain * ( sum_in_volume ) > 1.0 / chorus->out_gain )
        st_warn("chorus: warning >>> gain-out can cause saturation or clipping of output <<<");


        if (! (chorus->chorusbuf = 
                (float *) malloc(sizeof (float) * chorus->maxsamples)))
        {
                st_fail("chorus: Cannot malloc %d bytes!\n", 
                        sizeof(float) * chorus->maxsamples);
                return (ST_EOF);
        }
        for ( i = 0; i < chorus->maxsamples; i++ )
                chorus->chorusbuf[i] = 0.0;

        chorus->counter = 0;
        chorus->fade_out = chorus->maxsamples;
        return (ST_SUCCESS);
}
ft_t st_open_read(const char *path, const st_signalinfo_t *info,
                  const char *filetype)
{
    ft_t ft;

    ft = (ft_t)calloc(sizeof(struct st_soundstream), 1);

    if (!ft )
        return NULL;

    ft->filename = strdup(path);

    /* Let auto effect do the work if user is not overriding. */
    if (!filetype)
        ft->filetype = strdup("auto");
    else
        ft->filetype = strdup(filetype);

    if (!ft->filename || !ft->filetype)
        goto input_error;

    if (st_gettype(ft) != ST_SUCCESS)
    {
        st_warn("Unknown input file format for '%s':  %s", 
                ft->filename, 
                ft->st_errstr);
        goto input_error;
    }

    ft->info.size = -1;
    ft->info.encoding = -1;
    ft->info.channels = -1;
    if (info)
        ft->info = *info;
    /* FIXME: Remove ft->swap from code */
    ft->swap = ft->info.swap;
    ft->mode = 'r';

    if (!(ft->h->flags & ST_FILE_NOSTDIO))
    {
        /* Open file handler based on input name.  Used stdin file handler
         * if the filename is "-"
         */
        if (!strcmp(ft->filename, "-"))
            ft->fp = stdin;
        else if ((ft->fp = fopen(ft->filename, "rb")) == NULL)
        {
            st_warn("Can't open input file '%s': %s", ft->filename,
                    strerror(errno));
            goto input_error;
        }

        /* See if this file is seekable or not */
        ft->seekable = is_seekable(ft);
    }

    /* Read and write starters can change their formats. */
    if ((*ft->h->startread)(ft) != ST_SUCCESS)
    {
        st_warn("Failed reading %s: %s", ft->filename, ft->st_errstr);
        goto input_error;
    }

    /* Go a head and assume 1 channel audio if nothing is detected.
     * This is because libst usually doesn't set this for mono file
     * formats (for historical reasons).
     */
    if (ft->info.channels == -1)
        ft->info.channels = 1;

    if (st_checkformat(ft) )
    {
        st_fail("bad input format for file %s: %s", ft->filename,
                ft->st_errstr);
        goto input_error;
    }

    return ft;

input_error:

    if (ft->filename)
        free(ft->filename);
    if (ft->filetype)
        free(ft->filetype);
    free(ft);
    return NULL;
}
ft_t st_open_write_instr(const char *path, const st_signalinfo_t *info,
                         const char *filetype, const char *comment,
                         const st_instrinfo_t *instr,
                         const st_loopinfo_t *loops)
{
    ft_t ft;
    int i;

    ft = (ft_t)calloc(sizeof(struct st_soundstream), 1);

    if (!ft )
        return NULL;

    ft->filename = strdup(path);

    /* Let auto effect do the work if user is not overriding. */
    if (!filetype)
    {
        char *chop;
        int len;

        len = strlen(ft->filename);

        /* Use filename extension to determine audio type. */
        chop = ft->filename + len;
        while (chop > ft->filename && *chop != LASTCHAR)
            chop--;

        while (chop < ft->filename+len && *chop != '.')
            chop++;

        if (*chop == '.')
        {
            chop++;
            ft->filetype = strdup(chop);
        }
    }
    else
        ft->filetype = strdup(filetype);

    if (!ft->filename || !ft->filetype)
        goto output_error;

    if (st_gettype(ft) != ST_SUCCESS)
    {
        st_warn("Unknown output file format for '%s':  %s", 
                ft->filename, 
                ft->st_errstr);
        goto output_error;
    }

    ft->info.size = -1;
    ft->info.encoding = -1;
    ft->info.channels = -1;
    if (info)
        ft->info = *info;
    ft->mode = 'w';

    if (!(ft->h->flags & ST_FILE_NOSTDIO))
    {
        /* Open file handler based on input name.  Used stdin file handler
         * if the filename is "-"
         */
        if (!strcmp(ft->filename, "-"))
        {
            ft->fp = stdout;

        }
        else if ((ft->fp = fopen(ft->filename, "wb")) == NULL)
        {
            st_warn("Can't open output file '%s': %s", ft->filename,
                    strerror(errno));
            goto output_error;
        }

        /* stdout tends to be line-buffered.  Override this */
        /* to be Full Buffering. */
        /* FIXME: Use buffer size from ft structure */
        if (setvbuf (ft->fp, NULL, _IOFBF, sizeof(char)*ST_BUFSIZ))
        {
            st_warn("Can't set write buffer");
            goto output_error;
        }

        /* See if this file is seekable or not */
        ft->seekable = is_seekable(ft);
    }

    if (ft->comment == NULL && comment != NULL)
        ft->comment = strdup(comment);
    else
        ft->comment = strdup("Processed by SoX");

    if (loops)
    {
        for (i = 0; i < ST_MAX_NLOOPS; i++)
        {
            ft->loops[i] = loops[i];
        }
    }

    /* leave SMPTE # alone since it's absolute */
    if (instr)
        ft->instr = *instr;

    /* FIXME: Remove ft->swap from code */
    ft->swap = ft->info.swap;

    /* Read and write starters can change their formats. */
    if ((*ft->h->startwrite)(ft) != ST_SUCCESS)
    {
        st_warn("Failed writing %s: %s", ft->filename, ft->st_errstr);
        goto output_error;
    }

    if (st_checkformat(ft) )
    {
        st_fail("bad output format for file %s: %s", ft->filename,
                ft->st_errstr);
        goto output_error;
    }

    return ft;

output_error:

    if (ft->filename)
        free(ft->filename);
    if (ft->filetype)
        free(ft->filetype);
    free(ft);
    return NULL;
}