/* pitch.c - change the pitch of a sound file
**
** Copyright (C) 1989 by Jef Poskanzer.
**
** Permission to use, copy, modify, and distribute this software and its
** documentation for any purpose and without fee is hereby granted, provided
** that the above copyright notice appear in all copies and that both that
** copyright notice and this permission notice appear in supporting
** documentation.  This software is provided "as is" without express or
** implied warranty.
*/

#include <stdio.h>
#include "libst.h"

#define MYBUFSIZ 256

#define SAMPLES_PER_CHUNK 1024
#define LOG2_SPC 10
#define HALF_SPC 512

#define CHUNK_USE_FRAC 0.333333  /* only use the middle 1/3 of each chunk */

main( argc, argv )
int argc;
char *argv[];
    {
    FILE *f;
    char mybuf[MYBUFSIZ];
    float scale;
    double atof();
    int c, i, j, k, pstart, pend, plen;
    int linbuf[SAMPLES_PER_CHUNK];
    float xr[SAMPLES_PER_CHUNK], xi[SAMPLES_PER_CHUNK];

    if ( argc < 2 || argc > 3 )
	{
	fprintf( stderr, "usage:  %s <factor> [<file>]\n", argv[0] );
	exit( 1 );
	}

    scale = atof( argv[1] );
    if ( scale == 0.0 )
	{
	fprintf( stderr, "%s: scale factor required\n", argv[0] );
	exit( 1 );
	}

    if ( argc == 2 )
	f = stdin;
    else if ( argc == 3 )
	{
	f = fopen( argv[2], "r" );
	if ( f == NULL )
	    {
	    perror( argv[2] );
	    exit( 1 );
	    }
	}
    initfft( LOG2_SPC );

    i = 0;
    pstart = 0;
    pend = SAMPLES_PER_CHUNK * 0.5 + SAMPLES_PER_CHUNK * CHUNK_USE_FRAC * 0.5;
    while ( (c = getc( f )) != EOF )
	{
	linbuf[i] = st_ulaw_to_linear( c );
	++i;
	if ( i == SAMPLES_PER_CHUNK )
	    {
	    shiftchunk( linbuf, i, scale, pstart, pend );
	    if ( pstart == 0 )
		{
		pstart = SAMPLES_PER_CHUNK * 0.5 - SAMPLES_PER_CHUNK * CHUNK_USE_FRAC * 0.5;
		plen = pend - pstart;
		}
	    for ( j = 0, k = j + plen; k < SAMPLES_PER_CHUNK; j++, k++ )
		linbuf[j] = linbuf[k];
	    i -= plen;
	    }
	}
    if ( i > 0 )
	{
	pend = i;
	shiftchunk( linbuf, i, scale, pstart, pend );
	}

    exit( 0 );
    }

shiftchunk( linbuf, i, scale, pstart, pend )
int linbuf[SAMPLES_PER_CHUNK];
float scale;
int i, pstart, pend;
    {
    register int j, k;
    float xr[SAMPLES_PER_CHUNK], xi[SAMPLES_PER_CHUNK];
    float sxr[SAMPLES_PER_CHUNK], sxi[SAMPLES_PER_CHUNK];

    for ( j = 0; j < i; ++j )
	{
	xr[j] = linbuf[j];
	xi[j] = 0.0;
	}
    for ( ; j < SAMPLES_PER_CHUNK; ++j )
	{
	xr[j] = 0.0;
	xi[j] = 0.0;
	}

    fft( xr, xi, 0 );

    for ( j = 0; j < SAMPLES_PER_CHUNK; ++j )
	{
	k = (int) ( ( j - HALF_SPC ) * scale + 0.5 ) + HALF_SPC;
	if ( k >= 0 && k < SAMPLES_PER_CHUNK )
	    {
	    sxr[j] = xr[k];
	    sxi[j] = xi[k];
	    }
	else
	    {
	    sxr[j] = 0.0;
	    sxi[j] = 0.0;
	    }
	}

    fft( sxr, sxi, 1 );

    for ( j = pstart; j < pend; ++j )
	putchar( st_linear_to_ulaw( (int) sxr[j] ) );
    }
