/*
  The mediastreamer library aims at providing modular media processing and I/O
	for linphone, but also for any telephony application.
  Copyright (C) 2001  Simon MORLAT simon.morlat@linphone.org
  										
  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include <config.h>

#ifdef HAVE_ILBC


#include "msilbcdec.h"
#include "msilbcenc.h"
#include "mscodec.h"
#include <stdlib.h>
#include <stdio.h>



extern MSFilter * ms_ilbc_encoder_new(void);

MSCodecInfo ilbc_info={
	{
		"iLBC codec",
		0,
		MS_FILTER_AUDIO_CODEC,
		ms_ilbc_encoder_new,
		"A speech codec suitable for robust voice communication over IP"
	},
	ms_ilbc_encoder_new,
	ms_ilbc_decoder_new,
	0,	/* not applicable, 2 modes */
	0, /* not applicable, 2 modes */
	15200,
	8000,
	97,
	"iLBC",
	1,
	1,
};


void ms_ilbc_codec_init()
{
	ms_filter_register(MS_FILTER_INFO(&ilbc_info));
}



static MSILBCDecoderClass *ms_ilbc_decoder_class=NULL;

MSFilter * ms_ilbc_decoder_new(void)
{
	MSILBCDecoder *r;
	
	r=g_new(MSILBCDecoder,1);
	ms_ilbc_decoder_init(r);
	if (ms_ilbc_decoder_class==NULL)
	{
		ms_ilbc_decoder_class=g_new(MSILBCDecoderClass,1);
		ms_ilbc_decoder_class_init(ms_ilbc_decoder_class);
	}
	MS_FILTER(r)->klass=MS_FILTER_CLASS(ms_ilbc_decoder_class);
	return(MS_FILTER(r));
}
	

int ms_ilbc_decoder_set_property(MSILBCDecoder *obj, MSFilterProperty prop, char *value)
{
	switch(prop){
		case MS_FILTER_PROPERTY_FMTP:
			if (!value) return;
			if (strstr(value,"ptime=20")!=NULL) obj->ms_per_frame=20;
			else if (strstr(value,"ptime=30")!=NULL) obj->ms_per_frame=30;
			else g_warning("Unrecognized fmtp parameter for ilbc encoder!");
		break;
	}
	return 0;
}
int ms_ilbc_decoder_get_property(MSILBCDecoder *obj, MSFilterProperty prop, char *value)
{
	switch(prop){
		case MS_FILTER_PROPERTY_FMTP:
			if (obj->ms_per_frame==20) strncpy(value,"ptime=20",MS_FILTER_PROPERTY_STRING_MAX_SIZE);
			if (obj->ms_per_frame==30) strncpy(value,"ptime=30",MS_FILTER_PROPERTY_STRING_MAX_SIZE);
		break;
	}
	return 0;
}

void ms_ilbc_decoder_setup(MSILBCDecoder *r) 
{
	MSFilterClass *klass = NULL;
	switch (r->ms_per_frame) {
	case 20:
		r->samples_per_frame = BLOCKL_20MS;
		r->bytes_per_compressed_frame = NO_OF_BYTES_20MS;
		break;
	case 30:
		r->samples_per_frame = BLOCKL_30MS;
		r->bytes_per_compressed_frame = NO_OF_BYTES_30MS;
		break;
	default:
		g_error("ms_ilbc_decoder_setup: Bad value for ptime (%i)",r->ms_per_frame);
	}
	g_message("Using ilbc decoder with %i ms frames mode.",r->ms_per_frame);
	initDecode(&r->ilbc_dec, r->ms_per_frame /* ms frames */, /* user enhancer */ 0);
}


/* FOR INTERNAL USE*/
void ms_ilbc_decoder_init(MSILBCDecoder *r)
{
	/* default bitrate */
	r->bitrate = 15200;
	r->ms_per_frame = 30;
	r->samples_per_frame = BLOCKL_20MS;
	r->bytes_per_compressed_frame = NO_OF_BYTES_20MS;

	ms_filter_init(MS_FILTER(r));
	MS_FILTER(r)->inqueues=r->q_inputs;
	MS_FILTER(r)->outfifos=r->f_outputs;
	memset(r->q_inputs,0,sizeof(MSFifo*)*MSILBCDECODER_MAX_INPUTS);
	memset(r->f_outputs,0,sizeof(MSFifo*)*MSILBCDECODER_MAX_INPUTS);
}

void ms_ilbc_decoder_class_init(MSILBCDecoderClass *klass)
{
	ms_filter_class_init(MS_FILTER_CLASS(klass));
	ms_filter_class_set_name(MS_FILTER_CLASS(klass),"ILBCDec");
	MS_FILTER_CLASS(klass)->max_qinputs=MSILBCDECODER_MAX_INPUTS;
	MS_FILTER_CLASS(klass)->max_foutputs=MSILBCDECODER_MAX_INPUTS;
	MS_FILTER_CLASS(klass)->w_maxgran= ILBC_MAX_SAMPLES_PER_FRAME*2;
	MS_FILTER_CLASS(klass)->destroy=(MSFilterDestroyFunc)ms_ilbc_decoder_destroy;
	MS_FILTER_CLASS(klass)->set_property=(MSFilterPropertyFunc)ms_ilbc_decoder_set_property;
	MS_FILTER_CLASS(klass)->get_property=(MSFilterPropertyFunc)ms_ilbc_decoder_get_property;
	MS_FILTER_CLASS(klass)->setup=(MSFilterSetupFunc)ms_ilbc_decoder_setup;
	MS_FILTER_CLASS(klass)->process=(MSFilterProcessFunc)ms_ilbc_decoder_process;
	MS_FILTER_CLASS(klass)->info=(MSFilterInfo*)&ilbc_info;
}
	
void ms_ilbc_decoder_process(MSILBCDecoder *r)
{
	MSFifo *fo;
	MSQueue *qi;
	int err1;
	void *dst=NULL;
	float speech[ILBC_MAX_SAMPLES_PER_FRAME];
	MSMessage *m;

	qi=r->q_inputs[0];
	fo=r->f_outputs[0];
	m=ms_queue_get(qi);
	
	ms_fifo_get_write_ptr(fo, r->samples_per_frame*2, &dst);
	if (dst!=NULL){
		if (m->data!=NULL){
			if (m->size<r->bytes_per_compressed_frame) {
				g_warning("Invalid ilbc frame ?");
			}
			iLBC_decode(speech, m->data, &r->ilbc_dec, /* mode */1);
		}else{
			iLBC_decode(speech,NULL, &r->ilbc_dec,0);
		}
		ilbc_write_16bit_samples((gint16*)dst, speech, r->samples_per_frame);
	}
	ms_message_destroy(m);
}

void ms_ilbc_decoder_uninit(MSILBCDecoder *obj)
{
}

void ms_ilbc_decoder_destroy( MSILBCDecoder *obj)
{
	ms_ilbc_decoder_uninit(obj);
	g_free(obj);
}

#endif
