/*
 * FILE:    codec_decoder.c - Version 1.0
 * PROGRAM: RAT
 * AUTHOR:  Isidor Kouvelas + Orion Hodson
 * 
 * $Revision: 1.1.1.1 $
 * $Date: 1998/08/15 19:16:48 $
 * 
 * Copyright (c) 1995,1996 University College London
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, is permitted, for non-commercial use only, provided
 * that the following conditions are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by the Computer Science
 *      Department at University College London
 * 4. Neither the name of the University nor of the Department may be used
 *    to endorse or promote products derived from this software without
 *    specific prior written permission.
 * Use of this software for commercial purposes is explicitly forbidden
 * unless prior written permission is obtained from the authors.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include "bat_include.h"

int dvi_adpcm_decoder(char *indata, char *outdata, int len);

static void
cosine_blend(sample *src1, sample *src2, sample *dst, int len)
{
/*        int   i = 0;
        register float c;
        assert(len>0);
        while(i<len) {
                c = cos(0.5*M_PI*(double)i/(double)len);
                dst[i] = c * src1[i] + (1-c) * src2[i];
                      i++;
        }
        */      
}


static void
post_lpc_blend_chk(rx_queue_element_struct *u)
{
        rx_queue_element_struct *pu;
        short                    tmp_buf[POST_LPC_BLEND_LENGTH];
        
        if (u->interval->prev_ptr != NULL) {
                if ((pu = get_participant_unit(u->interval->prev_ptr, u->dbe_source[0])) != NULL) {
                        if (pu->comp_format[0].scheme == LPC) {
                                lpc_extend_synthesize(tmp_buf,
                                                        POST_LPC_BLEND_LENGTH,
                                                        &(u->dbe_source[0]->lpc_state));
                                cosine_blend(tmp_buf,
                                             u->decomp_data,
                                             u->decomp_data,
                                             POST_LPC_BLEND_LENGTH);
                        }
                }
        }
}

static sample *
get_comp_data (rx_queue_element_struct *u, int type)
{
         int i;
         assert (u != NULL);
         for (i=0; i < u->comp_count; i++) {
                 if (u->comp_format[i].scheme == type) {
                         return (u->comp_data[i]);
                 }
         }
         return (NULL);
}

static int
align_pitch_position(sample *x, int pitch, int framelen)
{
        int i, mi, dx, mdx;
        mi = mdx = 0;
        for(i = 1; i < framelen; i++) {
                dx = x[i] - x[i-1];
                if (abs(dx)>mdx) {
                        mi = i;
                        mdx = abs(dx);
                }
        }
        mi = mi - framelen;
        while (mi < 0) {
                mi += pitch;
        }
        return (mi);
}

static void
decode_lpc_unit(rx_queue_element_struct *u)
{
        rx_queue_element_struct *pu;       /* previous participant unit       */
        lpcparams_t             *pp, *p;   /* previous params, current params */
        lpcstate_t              *s;        /* state pointer                   */
#ifndef ucacoxh
        int                      i;
        short                    tmp_buf[SAMPLES_PER_UNIT];
#endif
        s = &(u->dbe_source[0]->lpc_state);
        p = (lpcparams_t*)u->comp_data[0];
        
        if (u->interval->prev_ptr == NULL) {
                        /* talkspurt start */
                memset(s, 0, sizeof(lpcstate_t));
        } else if ((pu = get_participant_unit(u->interval->prev_ptr, u->dbe_source[0])) == NULL) {
                        /* talkspurt start for particpant, but not others */
                memset(s, 0, sizeof(lpcstate_t));
        } else if (pu->comp_format[0].scheme == LPC){
                        /* do nothing */
        } else if ((pp = (lpcparams_t*) get_comp_data(pu, LPC)) == NULL) {
                if (pu->decomp_data == NULL) {
                        return;
                }
#ifndef ucacoxh
                pu->comp_data[pu->comp_count] = (sample*)xmalloc(LPCRECSIZE);
                pp = (lpcparams_t*) pu->comp_data[pu->comp_count];
                lpc_analyze(pu->decomp_data, pp);
                pu->comp_count++;

                s->Oldper = (double)pp->period / 256.;
                s->OldG   = (double)pp->gain / 256.;
                for(i = 1; i <= LPC_FILTORDER; i++) {
                        s->Oldk[i] = (double)pp->k[i] / 128.;
                }
                s->OldG  /= sqrt(SAMPLES_PER_UNIT / (s->Oldper == 0 ? 3.0 : s->Oldper));
                lpc_synthesize(tmp_buf, pp, s);                
                s->pitchctr = (s->Oldper == 0) ? 0 : align_pitch_position(pu->decomp_data, s->Oldper, SAMPLES_PER_UNIT);
                cosine_blend(tmp_buf+SAMPLES_PER_UNIT-11,
                             pu->decomp_data+SAMPLES_PER_UNIT-11,
                             pu->decomp_data+SAMPLES_PER_UNIT-11,
                             10);
#else
                        /* double decode to get filter state in right place*/
                        lpc_synthesize(u->decomp_data, p, s);
                        s->pitchctr = (s->Oldper == 0) ? 0 : align_pitch_position(pu->decomp_data, s->Oldper, SAMPLES_PER_UNIT);
#endif /*ucacoxh*/
        } else {
                        /* there was lpc data in the previous participant unit */
                        /* but it was not used.                                */
                        /* set state as if it was decoded and align pitch.     */
                        /* WE should never get this far                        */
        }
        lpc_synthesize(u->decomp_data, p, s);
}

int
decode_unit(rx_queue_element_struct *u)
{
	int i = 0;
	sample	*s, *d;
	u_char	*sc;

	u->decomp_format.scheme = L16;

        switch (u->comp_format[0].scheme) {
	case L16:
                d = u->decomp_data = (sample*)block_alloc(BYTES_PER_SAMPLE * SAMPLES_PER_UNIT);
                s = u->comp_data[0];
		for (i=0; i < SAMPLES_PER_UNIT; i++) {
			*d++ = ntohs(*s);
			s++;
		}
		break;
	case PCM:
                d = u->decomp_data = (sample*)block_alloc(BYTES_PER_SAMPLE * SAMPLES_PER_UNIT);
		sc = (u_char *)u->comp_data[0];
		for(i = 0; i < SAMPLES_PER_UNIT; i++) {
			*d++ = u2s(*sc);
			sc++;
		}
		break;
	case DVI_WITH_STATE:
		((struct adpcm_state *)u->comp_data[0])->valprev = ntohs(((struct adpcm_state *)u->comp_data[0])->valprev);
		memcpy(&u->dbe_source[0]->dvi_state, u->comp_data[0], DVI_STATE_SIZE);
	case DVI:
                d = u->decomp_data = (sample*)block_alloc(BYTES_PER_SAMPLE * SAMPLES_PER_UNIT);
		adpcm_decoder((char*)u->comp_data[0] +
			      ((u->comp_format[0].scheme == DVI_WITH_STATE)? DVI_STATE_SIZE : 0),
			      u->decomp_data, SAMPLES_PER_UNIT,
			      &u->dbe_source[0]->dvi_state);
		break;
	case LPC:
                d = u->decomp_data = (sample*)block_alloc(BYTES_PER_SAMPLE * SAMPLES_PER_UNIT);
                decode_lpc_unit(u);
		break;
	case GSM:
                d = u->decomp_data = (sample*)block_alloc(BYTES_PER_SAMPLE * SAMPLES_PER_UNIT);
		if (u->dbe_source[0]->rx_gsm_state == NULL) {
			u->dbe_source[0]->rx_gsm_state = gsm_create();
			assert(u->dbe_source[0]->rx_gsm_state != NULL);
			assert(u->dbe_source[0]->rx_gsm_state->nrp == 40);    /* 40 is from gsm_create() */
		}
		assert(u->dbe_source[0]->rx_gsm_state != NULL);
		gsm_decode(u->dbe_source[0]->rx_gsm_state,
		   	   (gsm_byte*)u->comp_data[0],
		   	   (gsm_signal*)u->decomp_data);
		break;
	default:
		/* No codecs in here yet */
		u->decomp_format.scheme = -1;
		return (FALSE);
	}
        if (u->comp_format[0].scheme != LPC) {
                post_lpc_blend_chk(u);
        }
	return (TRUE);
}

