/*
	adpcm.c
	
	Adaptive Pulse Code Modulation Compressor/Decompressor for sound
	Follows the IMA specification.
	
	Check out IMA Proceedings, Digital Audio Special Edition, Vol 2, Issue 2, May 1992,
	pg. 94 for Intel ADPCM code.
*/

/*
	Speed improvements:
	2) read as a long instead of a short
	3) only write to memory once per long, instead of 2.
*/

#ifndef __myth_defs_h__
#include "myth_defs.h"
#endif

#include <assert.h>

#include "adpcm.h"

typedef short PCM_16;		/* This typedef is platform dependent!!! */
typedef long  PCM_32;		/* This typedef is platform dependent!!! */

/* ------- constants */
enum
{
	NUMBER_OF_IMA_SAMPLES_PER_PACKET= 64
};

const int indextab[]=
{
	-1,-1,-1,-1, 2, 4, 6, 8,
	-1,-1,-1,-1, 2, 4, 6, 8
};

const int steptab[89]=
{ 	
	7, 8, 9, 10, 11, 12, 13, 14,
	16,  17,  19,  21,  23,  25,  28,
	31,  34,  37,  41,  45,  50,  55,
	60,  66,  73,  80,  88,  97, 107,
	118, 130, 143, 157, 173, 190, 209,
	230, 253, 279, 307, 337, 371, 408,
	449, 494, 544, 598, 658, 724, 796,
	876, 963,1060,1166,1282,1411,1552,
	1707,1878,
	2066,2272,2499,2749,3024,3327,3660,4026,
	4428,4871,5358,5894,6484,7132,7845,8630,
	9493,10442,11487,12635,13899,15289,16818,
	18500,20350,22385,24623,27086,29794,32767
};

/* ------- local prototypes */

static void stereo_adpcm_compress_with_index_and_predictor(
	short *source,
	unsigned int number_of_samples,
	char *destination,
	short *index,
	short *predicted
	);
	
static void adpcm_compress_with_index_and_predictor(
	short *source,
	unsigned int number_of_samples,
	char *destination,
	short *index,
	short *predicted
	);

static void stereo_adpcm_decompress_by_state( async_adpcm_decompressor_state *state );
static void adpcm_decompress_by_state( async_adpcm_decompressor_state *state );

/* ------- code */

#ifdef OBSOLETE
void adpcm_decompress(
	char *buffer,
	unsigned int number_of_samples,  // should be length of buffer *2 (2 samples per char)
	short *destination
	) // destination must be number_of_samples*sizeof(short) 
{
	PCM_32 predsample = 0;
	int step = 7;
	int index = 0;
	int codebuf = 0; // just a reminder. (read first time through)
	unsigned int sample;

	for( sample=0; sample < number_of_samples; sample++ )
	{
		PCM_32 diff;
		int code;
	
		// cache every other sample.
		if( sample & 1 )							/* two samples per inbuf char */
		{
			code = (codebuf>>4) & 0xf;
		}
		else
		{
			codebuf = buffer[sample>>1];		/* buffer two ADPCM nibbles */
			code = codebuf & 0xf;
		}

		/* compute new sample estimate predsample */
		diff = 0;
		if( code & 4 ) 
		{
			diff += step;
		}
		
		if( code & 2 ) 
		{
			diff += step >> 1;
		}
		
		if( code & 1 ) 
		{
			diff += step >> 2;
		}

		diff += step >> 3;
		if( code & 8 )
		{
			diff = -diff;
		}
		
		predsample = PIN( predsample + diff, -32768, 32767 );

	    destination[sample] = predsample;		/* store estimate to output buffer */

	    /* compute new stepsize step */
		index = PIN( index + indextab[code], 0, 88 );
	    step = steptab[index];
	}
	
	return;
}

void adpcm_compress(
	short *source,
	unsigned int number_of_samples,  // should be length of source/2 (1 sample per short)
	char *destination) // should be (number_of_samples/2 + number_of_samples&1)*sizeof(char)
{
	short index, predicted;
	
	index = predicted = 0;
	
	adpcm_compress_with_index_and_predictor(
		source,
		number_of_samples,
		destination,
		&index,
		&predicted
		);
	
	return;
}
#endif // OBSOLETE

void initialize_adpcm_decompressor_state(
	char *source,
	int number_of_samples,
	struct async_adpcm_decompressor_state *state)
{
	memset( state, 0, sizeof(async_adpcm_decompressor_state) );
	
	state->source_samples = source;
	state->number_of_samples = number_of_samples;
	state->predsample = 0;
	state->index = 0;
}

void apple_adpcm_compress(
	short *source,
	unsigned int number_of_samples, 
	char *destination, 
	short *compression_index, 
	short *predicted)
{
	adpcm_compress_with_index_and_predictor(
		source,
		number_of_samples,
		destination,
		compression_index,
		predicted
		);
}

void apple_adpcm_packet_decompress(
	short state,
	char *samples,
	short *destination,
	long samples_to_decompress
	)
{
	async_adpcm_decompressor_state adpcm_state;

	assert( destination );
	assert( samples_to_decompress >= 0 && samples_to_decompress <= NUMBER_OF_IMA_SAMPLES_PER_PACKET );

	// apple has 64 samples per packet..
	initialize_adpcm_decompressor_state( samples, NUMBER_OF_IMA_SAMPLES_PER_PACKET, &adpcm_state );
	adpcm_state.destination = destination;
	adpcm_state.number_of_samples_to_decompress = samples_to_decompress;
	
	// override the defaults...
	adpcm_state.index = (state & 0x7f); // 7 bits
	adpcm_state.predsample = ((long)state & 0xffffff80);
	adpcm_decompress_by_state( &adpcm_state );

	return;
}

/* ------- local code */

static void adpcm_compress_with_index_and_predictor(
	short *source, 
	unsigned int number_of_samples, 
	char *destination, 
	short *compression_index, 
	short *predicted
	)
{
	PCM_32 predsample = *predicted;
	int index = *compression_index;
	int step = steptab[index];
	int codebuf = 0; // unnecessary, but makes me remember this has to be scoped here.
	unsigned int sample;
	
	for( sample = 0; sample < number_of_samples; sample++ )
	{
		int i, mask, code, tempstep;
		PCM_32 diff= (PCM_32)source[sample] - predsample;	/* difference may require 17 bits! */

		if( diff >= 0 )  			/* set sign bit */
		{
			code = 0;
		}
		else
		{
			code = 8;
			diff = -diff;
		}

		mask = 4;
		tempstep = step;
		
		for( i = 0; i < 3; i++ ) /* quantize diff sample */
		{		   
			if( diff >= tempstep )
			{
				code |= mask;
				diff -= tempstep;
			}
			
			tempstep >>= 1;
			mask >>= 1;
		}

		// cache the writes
		if( sample & 1 )	
		{
			destination[sample >> 1] = code << 4 | codebuf;
		}
		else
		{
			codebuf = code & 0xf;		/* buffer for write with next nibble */
		}

		/* compute new sample estimate predsample */
		diff = 0;
		if( code & 4 )
		{
			diff += step;
		}
		
		if( code & 2 )
		{
			diff += step >> 1;
		}
		
		if( code & 1 )
		{
			diff += step >> 2;
		}
		
		diff += step >> 3;
		if( code & 8 )
		{
			diff = -diff;
		}
		
		predsample = MIN( MAX( predsample + diff, -32768 ), 32768 );
//		predsample = PIN( predsample+diff, -32768, 32767 );

		/* compute new stepsize step */
		index = MIN( MAX( index + indextab[code], 0 ), 88 );
//		index = PIN( index + indextab[code], 0, 88 );		
		step = steptab[index];
	}

	*predicted = predsample;
	*compression_index = index;
	
	return;
}

static void adpcm_decompress_by_state( async_adpcm_decompressor_state *state )
{
	unsigned int sample;
	PCM_32 predsample;
	int step, codebuf, index;
	short *destination = state->destination;

	assert( state && state->source_samples && state->destination );
	predsample = state->predsample;
	index = state->index;
	codebuf = 0; // just a reminder. (read first time through)

	// reset the step..
	step = steptab[index];

	if( state->source_offset & 1 )
	{
		// starting on odd boundary, must make codebuf valid.
		codebuf = state->source_samples[(state->source_offset - 1) >> 1];
	}

	for( sample = state->source_offset; sample < state->source_offset + state->number_of_samples_to_decompress; sample++ )
	{
		PCM_32 diff;
		int code;
	
		// cache every other sample.
		if( sample & 1 )							/* two samples per inbuf char */
		{
			code = (codebuf >> 4) & 0xf;
		}
		else
		{
			codebuf = state->source_samples[sample >> 1];		/* buffer two ADPCM nibbles */
			code = codebuf & 0xf;
		}

		/* compute new sample estimate predsample */
		diff = 0;
		if( code & 4 )
		{
			diff += step;
		}
		
		if( code & 2 )
		{
			diff += step >> 1;
		}
		
		if( code & 1 )
		{
			diff += step >> 2;
		}

		diff += step >> 3;
		if( code & 8 )
		{
			diff = -diff;
		}
		
		predsample = MIN( MAX( predsample + diff, -32768 ), 32768 );
//		predsample = PIN( predsample + diff, -32768, 32767 );

	    *destination = predsample;		/* store estimate to output buffer */
	    destination++;

	    /* compute new stepsize step */
		index = MIN( MAX( index + indextab[code], 0 ), 88 );
//		index = PIN( index + indextab[code], 0, 88 );
	    step = steptab[index];
	}

	state->predsample = predsample;
	state->index = index;
	state->source_offset += state->number_of_samples_to_decompress;
}


