/*---------------------------------------------------------------------------*\

    This program analyses audio data for DTMF tones.


         Voicetronix Voice Processing Board (VPB) Software

         Copyright (C) 1999-2018 Voicetronix www.voicetronix.com.au

         This library is free software; you can redistribute it and/or
         modify it under the terms of the GNU Lesser General Public
         License version 2.1 as published by the Free Software Foundation.

         This library 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
         Lesser General Public License for more details.

         You should have received a copy of the GNU Lesser General Public
         License along with this library; if not, write to the Free Software
         Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
         MA  02110-1301  USA

\*---------------------------------------------------------------------------*/

#include "vpbapi.h"
#include "dtmf.h"

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <climits>


static int arg_exists(int argc, char *argv[], const char *arg)
{ //{{{
	for(int i = 0; i < argc; ++i)
		if(strcmp(argv[i],arg) == 0) return i;

	return 0;
} //}}}

static void usage(const char *argv0)
{ //{{{
	printf("usage: %s [--count] [--block size] file\n\n", argv0);
	printf("  --count       Return the number of digits detected in the exit code.\n");
	printf("  --gain  db    Apply a digital gain stage before the DTMF detector.\n");
	printf("  --block size  Granularity of analysis.  Default is 16000 samples (2sec)\n");
} //}}}

static double dBFactor( double db ) { return exp10( db / 20.0 ); }

static inline void LinearGain( double gain, int16_t *buf, size_t len )
{ //{{{

	// This is the maximum scale factor that lets us safely use
	// an int instead of a float as our temporary computed value.
	const unsigned int scalemax = INT_MAX / 32768;

	if( gain > scalemax ) gain = scalemax;
	for(int16_t *end = buf + len; buf != end; ++buf)
	{
	    int tmp = lrintf(*buf * gain);

	    if(__builtin_expect(tmp > 32767,0))       *buf = 32767;
	    else if(__builtin_expect(tmp < -32768,0)) *buf = -32768;
	    else                                      *buf = tmp;
	}

} //}}}

int main(int argc, char *argv[])
{ //{{{
	if(argc < 2) {
		usage(argv[0]);
		exit(EXIT_FAILURE);
	}

	size_t          NBUF = 16000;
	double          gain = 1.0;
	int             arg;

	if((arg = arg_exists(argc,argv,"--block")) != 0) {
		NBUF = atoi( argv[arg+1] );
		if( NBUF > 16384 ) {
			fprintf(stderr, "Invalid --block size, %u > 16384\n\n", (unsigned)NBUF);
			exit(EXIT_FAILURE);
		}
	}
	if((arg = arg_exists(argc,argv,"--gain")) != 0)
		gain = dBFactor( atof( argv[arg+1] ) );

	int16_t         buf[NBUF];
	char            keys[16];
	size_t          nbytes   = NBUF * sizeof(int16_t);
	int             keydown  = 0;
	unsigned int    start    = 0;
	WFILE          *wav;

	vpb_wave_open_read(&wav, argv[argc - 1]);

	size_t          size     = vpb_wave_get_size(wav);
	void           *dtmf_dec = dtmf_init();
	int             count    = 0;

	printf("Reading %u bytes in %u sample blocks ...\n", (unsigned)size, (unsigned)NBUF);
	while( size > 1 ) {
		size_t n = size > nbytes ? nbytes : size;

		n = vpb_wave_read(wav, (char*)buf, n );
		size -= n;

		// Don't pass through the gain stage for less than about +/-0.01 dB
		if( gain < 0.999 || gain > 1.001 )
			LinearGain( gain, buf, n / sizeof(int16_t));

		int digits_ret = dtmf_decoder(dtmf_dec, keys, sizeof(keys),
							buf, n / sizeof(int16_t),
							&keydown);

		printf("%7u %6.2fs: Detected ", start, start / 16000.0);
		start += n;
		count += digits_ret;

		if( digits_ret ) {
			for( int i = 0; i < digits_ret; ++i ) printf("%c ", keys[i]);
			if( keydown ) printf("(%c)\n", keydown);
			else          printf("\n");
		}
		else if( keydown ) {
			printf("(%c)\n", keydown);
			++count;
		}
		else printf("no digits\n");
	}

	vpb_wave_close_read(wav);
	dtmf_close(dtmf_dec);

	if( arg_exists(argc,argv,"--count") ) return count;
	return EXIT_SUCCESS;
} //}}}

