Index: pjproject/trunk/pjmedia/build/Makefile
===================================================================
--- pjproject/trunk/pjmedia/build/Makefile	(revision 416)
+++ pjproject/trunk/pjmedia/build/Makefile	(revision 417)
@@ -67,5 +67,6 @@
 			clock_thread.o codec.o conference.o endpoint.o errno.o \
 			wav_player.o wav_writer.o g711.o jbuf.o \
-			master_port.o null_port.o port.o resample.o \
+			master_port.o null_port.o plc_common.o plc_g711.o \
+			port.o resample.o \
 			resample_port.o rtcp.o rtp.o sdp.o sdp_cmp.o sdp_neg.o \
 			session.o silencedet.o sound_port.o stream.o wave.o \
Index: pjproject/trunk/pjmedia/build/pjmedia.dsp
===================================================================
--- pjproject/trunk/pjmedia/build/pjmedia.dsp	(revision 416)
+++ pjproject/trunk/pjmedia/build/pjmedia.dsp	(revision 417)
@@ -136,4 +136,12 @@
 # Begin Source File
 
+SOURCE=..\src\pjmedia\plc_common.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\plc_g711.c
+# End Source File
+# Begin Source File
+
 SOURCE=..\src\pjmedia\port.c
 # End Source File
@@ -245,4 +253,8 @@
 
 SOURCE=..\include\pjmedia.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\pjmedia\plc.h
 # End Source File
 # Begin Source File
Index: pjproject/trunk/pjmedia/include/pjmedia.h
===================================================================
--- pjproject/trunk/pjmedia/include/pjmedia.h	(revision 416)
+++ pjproject/trunk/pjmedia/include/pjmedia.h	(revision 417)
@@ -35,4 +35,5 @@
 #include <pjmedia/master_port.h>
 #include <pjmedia/null_port.h>
+#include <pjmedia/plc.h>
 #include <pjmedia/port.h>
 #include <pjmedia/resample.h>
Index: pjproject/trunk/pjmedia/include/pjmedia/config.h
===================================================================
--- pjproject/trunk/pjmedia/include/pjmedia/config.h	(revision 416)
+++ pjproject/trunk/pjmedia/include/pjmedia/config.h	(revision 417)
@@ -118,4 +118,14 @@
 
 
+/**
+ * G.711 Appendix I Packet Lost Concealment (PLC).
+ * Enabled only when floating point is enabled.
+ */
+#ifndef PJMEDIA_HAS_G711_PLC
+#   define PJMEDIA_HAS_G711_PLC			PJ_HAS_FLOATING_POINT
+#endif
+
+
+
 #endif	/* __PJMEDIA_CONFIG_H__ */
 
Index: pjproject/trunk/pjmedia/include/pjmedia/plc.h
===================================================================
--- pjproject/trunk/pjmedia/include/pjmedia/plc.h	(revision 417)
+++ pjproject/trunk/pjmedia/include/pjmedia/plc.h	(revision 417)
@@ -0,0 +1,102 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#ifndef __PJMEDIA_PLC_H__
+#define __PJMEDIA_PLC_H__
+
+
+/**
+ * @file plc.h
+ * @brief Packet Lost Concealment (PLC) API.
+ */
+#include <pjmedia/types.h>
+
+/**
+ * @defgroup PJMED_PLC Packet Lost Concealment
+ * @ingroup PJMEDIA
+ * @{
+ *
+ */
+
+
+PJ_BEGIN_DECL
+
+
+/**
+ * Opaque declaration for PLC.
+ */
+typedef struct pjmedia_plc pjmedia_plc;
+
+
+
+/**
+ * Create PLC session. This function will select the PLC algorithm to
+ * use based on the arguments.
+ *
+ * @param pool		    Pool to allocate memory for the PLC.
+ * @param clock_rate	    Media sampling rate.
+ * @param samples_per_frame Number of samples per frame.
+ * @param options	    Must be zero for now.
+ * @param p_plc		    Pointer to receive the PLC instance.
+ *
+ * @return		    PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_plc_create( pj_pool_t *pool,
+					 unsigned clock_rate,
+					 unsigned samples_per_frame,
+					 unsigned options,
+					 pjmedia_plc **p_plc);
+
+
+/**
+ * Save a good frame to PLC.
+ *
+ * @param plc		    The PLC session.
+ * @param frame		    The good frame to be stored to PLC. This frame
+ *			    must have the same length as the configured
+ *			    samples per frame.
+ *
+ * @return		    PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_plc_save( pjmedia_plc *plc,
+				       pj_int16_t *frame );
+
+
+/**
+ * Generate a replacement for lost frame.
+ *
+ * @param plc		    The PLC session.
+ * @param frame		    Buffer to receive the generated frame. This buffer
+ *			    must be able to store the frame.
+ *
+ * @return		    PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_plc_generate( pjmedia_plc *plc,
+					   pj_int16_t *frame );
+
+
+
+
+PJ_END_DECL
+
+/**
+ * @}
+ */
+
+#endif	/* __PJMEDIA_PLC_H__ */
+
Index: pjproject/trunk/pjmedia/src/pjmedia/plc_common.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/plc_common.c	(revision 417)
+++ pjproject/trunk/pjmedia/src/pjmedia/plc_common.c	(revision 417)
@@ -0,0 +1,227 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pjmedia/plc.h>
+#include <pjmedia/errno.h>
+#include <pj/assert.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+
+
+static void* plc_replay_create(pj_pool_t*, unsigned c, unsigned f);
+static void  plc_replay_save(void*, pj_int16_t*);
+static void  plc_replay_generate(void*, pj_int16_t*);
+
+static void* noplc_create(pj_pool_t*, unsigned c, unsigned f);
+static void  noplc_save(void*, pj_int16_t*);
+static void  noplc_generate(void*, pj_int16_t*);
+
+extern void* pjmedia_plc_g711_create(pj_pool_t*, unsigned c, unsigned f);
+extern void  pjmedia_plc_g711_save(void*, pj_int16_t*);
+extern void  pjmedia_plc_g711_generate(void*, pj_int16_t*);
+
+
+/**
+ * This struct is used internally to represent a PLC backend.
+ */
+struct plc_alg
+{
+    void* (*plc_create)(pj_pool_t*, unsigned c, unsigned f);
+    void  (*plc_save)(void*, pj_int16_t*);
+    void  (*plc_generate)(void*, pj_int16_t*);
+};
+
+
+#if defined(PJMEDIA_HAS_G711_PLC) && PJMEDIA_HAS_G711_PLC!=0
+static struct plc_alg plc_g711 =
+{
+    &pjmedia_plc_g711_create,
+    &pjmedia_plc_g711_save,
+    &pjmedia_plc_g711_generate
+};
+#endif
+
+
+static struct plc_alg plc_replay =
+{
+    &plc_replay_create,
+    &plc_replay_save,
+    &plc_replay_generate
+};
+
+
+static struct plc_alg no_plc =
+{
+    &noplc_create,
+    &noplc_save,
+    &noplc_generate
+};
+
+
+struct pjmedia_plc
+{
+    void	    *obj;
+    struct plc_alg  *op;
+};
+
+
+/*
+ * Create PLC session. This function will select the PLC algorithm to
+ * use based on the arguments.
+ */
+PJ_DEF(pj_status_t) pjmedia_plc_create( pj_pool_t *pool,
+					unsigned clock_rate,
+					unsigned samples_per_frame,
+					unsigned options,
+					pjmedia_plc **p_plc)
+{
+    pjmedia_plc *plc;
+
+    PJ_ASSERT_RETURN(pool && clock_rate && samples_per_frame && p_plc,
+		     PJ_EINVAL);
+    PJ_ASSERT_RETURN(options == 0, PJ_EINVAL);
+
+    PJ_UNUSED_ARG(options);
+
+    plc = pj_pool_zalloc(pool, sizeof(pjmedia_plc));
+
+    if (0)
+	;
+#if defined(PJMEDIA_HAS_G711_PLC) && PJMEDIA_HAS_G711_PLC!=0
+    else if (clock_rate == 8000)
+	plc->op = &plc_g711;
+#endif
+    else
+	plc->op = &plc_replay;
+
+    plc->obj = plc->op->plc_create(pool, clock_rate, samples_per_frame);
+
+    *p_plc = plc;
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Save a good frame to PLC.
+ */
+PJ_DEF(pj_status_t) pjmedia_plc_save( pjmedia_plc *plc,
+				      pj_int16_t *frame )
+{
+    PJ_ASSERT_RETURN(plc && frame, PJ_EINVAL);
+ 
+    plc->op->plc_save(plc->obj, frame);
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Generate a replacement for lost frame.
+ */
+PJ_DEF(pj_status_t) pjmedia_plc_generate( pjmedia_plc *plc,
+					  pj_int16_t *frame )
+{
+    PJ_ASSERT_RETURN(plc && frame, PJ_EINVAL);
+    
+    plc->op->plc_generate(plc->obj, frame);
+    return PJ_SUCCESS;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+/*
+ * Simple replay based plc
+ */
+struct replay_plc
+{
+    unsigned	size;
+    unsigned	replay_cnt;
+    pj_int16_t *frame;
+};
+
+
+static void* plc_replay_create(pj_pool_t *pool, unsigned clock_rate, 
+			       unsigned samples_per_frame)
+{
+    struct replay_plc *o;
+
+    PJ_UNUSED_ARG(clock_rate);
+
+    o = pj_pool_alloc(pool, sizeof(struct replay_plc));
+    o->size = samples_per_frame * 2;
+    o->replay_cnt = 0;
+    o->frame = pj_pool_zalloc(pool, o->size);
+
+    return o;
+}
+
+static void plc_replay_save(void *plc, pj_int16_t *frame)
+{
+    struct replay_plc *o = plc;
+
+    pj_memcpy(o->frame, frame, o->size);
+    o->replay_cnt = 0;
+}
+
+static void plc_replay_generate(void *plc, pj_int16_t *frame)
+{
+    struct replay_plc *o = plc;
+    unsigned i, count;
+    pj_int16_t *samp;
+
+    ++o->replay_cnt;
+
+    if (o->replay_cnt < 16) {
+	pj_memcpy(frame, o->frame, o->size);
+    
+
+	count = o->size / 2;
+	samp = o->frame;
+	for (i=0; i<count; ++i)
+	    samp[i] = (pj_int16_t)(samp[i] >> 1);
+    } else {
+	pj_memset(frame, 0, o->size);
+    }
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+/*
+ * No PLC
+ */
+static void* noplc_create(pj_pool_t *pool, unsigned clock_rate, 
+			  unsigned samples_per_sec)
+{
+    PJ_UNUSED_ARG(pool);    
+    PJ_UNUSED_ARG(clock_rate);
+    return (void*) samples_per_sec;
+}
+
+static void noplc_save(void *plc, pj_int16_t *frame)
+{
+    PJ_UNUSED_ARG(plc);
+    PJ_UNUSED_ARG(frame);
+}
+
+static void  noplc_generate(void *plc, pj_int16_t *frame)
+{
+    unsigned samples_per_sec = (unsigned)plc;
+    pj_memset(frame, 0, samples_per_sec);
+}
+
Index: pjproject/trunk/pjmedia/src/pjmedia/plc_g711.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/plc_g711.c	(revision 417)
+++ pjproject/trunk/pjmedia/src/pjmedia/plc_g711.c	(revision 417)
@@ -0,0 +1,462 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pjmedia/types.h>
+#include <pj/assert.h>
+#include <pj/pool.h>
+
+
+/*
+ * Only build when PJMEDIA_HAS_G711_PLC is enabled.
+ */
+#if defined(PJMEDIA_HAS_G711_PLC) && PJMEDIA_HAS_G711_PLC!=0
+
+
+#include <math.h>
+
+typedef float Float;
+
+#define PITCH_MIN	40			/* minimum allowed pitch, 200 Hz */
+#define PITCH_MAX	120			/* maximum allowed pitch, 66 Hz */
+#define PITCHDIFF	(PITCH_MAX - PITCH_MIN)
+#define POVERLAPMAX	(PITCH_MAX >> 2)	/* maximum pitch OLA window */
+#define HISTORYLEN	(PITCH_MAX * 3 + POVERLAPMAX) /* history buffer length*/
+#define NDEC		2			/* 2:1 decimation */
+#define CORRLEN		160			/* 20 ms correlation length */
+#define CORRBUFLEN	(CORRLEN + PITCH_MAX)	/* correlation buffer length */
+#define CORRMINPOWER	((Float)250.)		/* minimum power */
+#define EOVERLAPINCR	32			/* end OLA increment per frame, 4 ms */
+#define FRAMESZ		80			/* 10 ms at 8 KHz */
+#define ATTENFAC	((Float).2)		/* attenuation factor per 10 ms frame */
+#define ATTENINCR	(ATTENFAC/FRAMESZ)	/* attenuation per sample */
+
+
+typedef struct LowcFE LowcFE;
+
+static void dofe(LowcFE *sess, short *s); /* synthesize speech for erasure */
+static void addtohistory(LowcFE *sess, short *s); /* add a good frame to history buffer */
+static void scalespeech(LowcFE *sess, short *out);
+static void getfespeech(LowcFE *sess, short *out, int sz);
+static void savespeech(LowcFE *sess, short *s);
+static int findpitch(LowcFE *sess);
+static void overlapadd_r(Float *l, Float *r, Float *o, int cnt);
+static void overlapadd_i(short *l, short *r, short *o, int cnt);
+static void overlapaddatend(LowcFE *sess, short *s, short *f, int cnt);
+static void convertsf(short *f, Float *t, int cnt);
+static void convertfs(Float *f, short *t, int cnt);
+static void copyf(Float *f, Float *t, int cnt);
+static void copys(short *f, short *t, int cnt);
+static void zeros(short *s, int cnt);
+
+struct LowcFE
+{
+    unsigned samples_per_frame;
+
+    int erasecnt;		/* consecutive erased frames */
+    int poverlap;		/* overlap based on pitch */
+    int poffset;		/* offset into pitch period */
+    int pitch;			/* pitch estimate */
+    int pitchblen;		/* current pitch buffer length */
+    Float *pitchbufend;		/* end of pitch buffer */
+    Float *pitchbufstart;	/* start of pitch buffer */
+    Float pitchbuf[HISTORYLEN]; /* buffer for cycles of speech */
+    Float lastq[POVERLAPMAX];	/* saved last quarter wavelength */
+    short history[HISTORYLEN];	/* history buffer */
+};
+
+
+
+static void convertsf(short *f, Float *t, int cnt)
+{
+    int i;
+    for (i = 0; i < cnt; i++)
+	t[i] = (Float)f[i];
+}
+
+static void convertfs(Float *f, short *t, int cnt)
+{
+    int i;
+    for (i = 0; i < cnt; i++)
+	t[i] = (short)f[i];
+}
+
+void copyf(Float *f, Float *t, int cnt)
+{
+    int i;
+    for (i = 0; i < cnt; i++)
+	t[i] = f[i];
+}
+
+void copys(short *f, short *t, int cnt)
+{
+    int i;
+    for (i = 0; i < cnt; i++)
+	t[i] = f[i];
+}
+
+void zeros(short *s, int cnt)
+{
+    int i;
+    for (i = 0; i < cnt; i++)
+	s[i] = 0;
+}
+
+
+void init_LowcFE(LowcFE *sess)
+{
+    sess->erasecnt = 0;
+    sess->pitchbufend = &sess->pitchbuf[HISTORYLEN];
+    zeros(sess->history, HISTORYLEN);
+}
+
+
+/*
+ * Save a frames worth of new speech in the history buffer.
+ * Return the output speech delayed by POVERLAPMAX.
+ */
+static void savespeech(LowcFE *sess, short *s)
+{
+    /* make room for new signal */
+    copys(&sess->history[FRAMESZ], sess->history, HISTORYLEN - FRAMESZ);
+
+    /* copy in the new frame */
+    copys(s, &sess->history[HISTORYLEN - FRAMESZ], FRAMESZ);
+
+    /* copy out the delayed frame */
+    copys(&sess->history[HISTORYLEN - FRAMESZ - POVERLAPMAX], s, FRAMESZ);
+}
+
+
+
+/*
+ * A good frame was received and decoded.
+ * If right after an erasure, do an overlap add with the synthetic signal.
+ * Add the frame to history buffer.
+ */
+static void addtohistory(LowcFE *sess, short *s)
+{
+    if (sess->erasecnt) {
+	short overlapbuf[FRAMESZ];
+
+	/*
+	 * longer erasures require longer overlaps
+	 * to smooth the transition between the synthetic
+	 * and real signal.
+	 */
+	int olen = sess->poverlap + (sess->erasecnt - 1) * EOVERLAPINCR;
+	if (olen > FRAMESZ)
+	    olen = FRAMESZ;
+
+	getfespeech(sess, overlapbuf, olen);
+	overlapaddatend(sess, s, overlapbuf, olen);
+	sess->erasecnt = 0;
+    }
+
+    savespeech(sess, s);
+}
+
+
+
+/*
+ * Generate the synthetic signal.
+ * At the beginning of an erasure determine the pitch, and extract
+ * one pitch period from the tail of the signal. Do an OLA for 1/4
+ * of the pitch to smooth the signal. Then repeat the extracted signal
+ * for the length of the erasure. If the erasure continues for more than
+ * 10 ms, increase the number of periods in the pitchbuffer. At the end
+ * of an erasure, do an OLA with the start of the first good frame.
+ * The gain decays as the erasure gets longer.
+ */
+static void dofe(LowcFE *sess, short *out)
+{
+    if (sess->erasecnt == 0) {
+	convertsf(sess->history, sess->pitchbuf, HISTORYLEN); /* get history */
+	sess->pitch = findpitch(sess); /* find pitch */
+	sess->poverlap = sess->pitch >> 2; /* OLA 1/4 wavelength */
+	/* save original last poverlap samples */
+	copyf(sess->pitchbufend - sess->poverlap, sess->lastq, sess->poverlap);
+	sess->poffset = 0; /* create pitch buffer with 1 period */
+	sess->pitchblen = sess->pitch;
+	sess->pitchbufstart = sess->pitchbufend - sess->pitchblen;
+	overlapadd_r(sess->lastq, sess->pitchbufstart - sess->poverlap,
+		     sess->pitchbufend - sess->poverlap, sess->poverlap);
+	/* update last 1/4 wavelength in history buffer */
+	convertfs(sess->pitchbufend - sess->poverlap, &sess->history[HISTORYLEN-sess->poverlap],
+		  sess->poverlap);
+	getfespeech(sess, out, FRAMESZ); /* get synthesized speech */
+
+    } else if (sess->erasecnt == 1 || sess->erasecnt == 2) {
+	/* tail of previous pitch estimate */
+	short tmp[POVERLAPMAX];
+	int saveoffset = sess->poffset; /* save offset for OLA */
+	getfespeech(sess, tmp, sess->poverlap); /* continue with old pitchbuf */
+	/* add periods to the pitch buffer */
+	sess->poffset = saveoffset;
+	while (sess->poffset > sess->pitch)
+	    sess->poffset -= sess->pitch;
+	sess->pitchblen += sess->pitch; /* add a period */
+	sess->pitchbufstart = sess->pitchbufend - sess->pitchblen;
+	overlapadd_r(sess->lastq, sess->pitchbufstart - sess->poverlap,
+		     sess->pitchbufend - sess->poverlap, sess->poverlap);
+	/* overlap add old pitchbuffer with new */
+	getfespeech(sess, out, FRAMESZ);
+	overlapadd_i(tmp, out, out, sess->poverlap);
+	scalespeech(sess, out);
+    } else if (sess->erasecnt > 5) {
+	zeros(out, FRAMESZ);
+    } else {
+	getfespeech(sess, out, FRAMESZ);
+	scalespeech(sess, out);
+    }
+    sess->erasecnt++;
+    savespeech(sess, out);
+}
+
+
+/*
+ * Estimate the pitch.
+ * l - pointer to first sample in last 20 ms of speech.
+ * r - points to the sample PITCH_MAX before l
+ */
+static int findpitch(LowcFE *sess)
+{
+    int i, j, k;
+    int bestmatch;
+    Float bestcorr;
+    Float corr; /* correlation */
+    Float energy; /* running energy */
+    Float scale; /* scale correlation by average power */
+    Float *rp; /* segment to match */
+    Float *l = sess->pitchbufend - CORRLEN;
+    Float *r = sess->pitchbufend - CORRBUFLEN;
+
+    /* coarse search */
+    rp = r;
+    energy = 0.f;
+    corr = 0.f;
+    for (i = 0; i < CORRLEN; i += NDEC) {
+	energy += rp[i] * rp[i];
+	corr += rp[i] * l[i];
+    }
+    scale = energy;
+    if (scale < CORRMINPOWER)
+	scale = CORRMINPOWER;
+    corr = corr / (Float)sqrt(scale);
+    bestcorr = corr;
+    bestmatch = 0;
+    for (j = NDEC; j <= PITCHDIFF; j += NDEC) {
+	energy -= rp[0] * rp[0];
+	energy += rp[CORRLEN] * rp[CORRLEN];
+	rp += NDEC;
+	corr = 0.f;
+	for (i = 0; i < CORRLEN; i += NDEC)
+	    corr += rp[i] * l[i];
+	scale = energy;
+	if (scale < CORRMINPOWER)
+	    scale = CORRMINPOWER;
+	corr /= (Float)sqrt(scale);
+	if (corr >= bestcorr) {
+	    bestcorr = corr;
+	    bestmatch = j;
+	}
+    }
+    /* fine search */
+    j = bestmatch - (NDEC - 1);
+    if (j < 0)
+	j = 0;
+    k = bestmatch + (NDEC - 1);
+    if (k > PITCHDIFF)
+	k = PITCHDIFF;
+    rp = &r[j];
+    energy = 0.f;
+    corr = 0.f;
+    for (i = 0; i < CORRLEN; i++) {
+	energy += rp[i] * rp[i];
+	corr += rp[i] * l[i];
+    }
+    scale = energy;
+    if (scale < CORRMINPOWER)
+	scale = CORRMINPOWER;
+    corr = corr / (Float)sqrt(scale);
+    bestcorr = corr;
+    bestmatch = j;
+    for (j++; j <= k; j++) {
+	energy -= rp[0] * rp[0];
+	energy += rp[CORRLEN] * rp[CORRLEN];
+	rp++;
+	corr = 0.f;
+	for (i = 0; i < CORRLEN; i++)
+	    corr += rp[i] * l[i];
+	scale = energy;
+	if (scale < CORRMINPOWER)
+	    scale = CORRMINPOWER;
+	corr = corr / (Float)sqrt(scale);
+	if (corr > bestcorr) {
+	    bestcorr = corr;
+	    bestmatch = j;
+	}
+    }
+    return PITCH_MAX - bestmatch;
+}
+
+
+
+/*
+ * Get samples from the circular pitch buffer. Update poffset so
+ * when subsequent frames are erased the signal continues.
+ */
+static void getfespeech(LowcFE *sess, short *out, int sz)
+{
+    while (sz) {
+	int cnt = sess->pitchblen - sess->poffset;
+	if (cnt > sz)
+	    cnt = sz;
+	convertfs(&sess->pitchbufstart[sess->poffset], out, cnt);
+	sess->poffset += cnt;
+	if (sess->poffset == sess->pitchblen)
+	    sess->poffset = 0;
+	out += cnt;
+	sz -= cnt;
+    }
+}
+
+static void scalespeech(LowcFE *sess, short *out)
+{
+    Float g = (Float)1. - (sess->erasecnt - 1) * ATTENFAC;
+    int i;
+    for (i = 0; i < FRAMESZ; i++) {
+	out[i] = (short)(out[i] * g);
+	g -= ATTENINCR;
+    }
+}
+
+
+
+static void overlapadd_r(Float *l, Float *r, Float *o, int cnt)
+{
+    Float incr = (Float)1. / cnt;
+    Float lw = (Float)1. - incr;
+    Float rw = incr;
+    int i;
+    for (i = 0; i < cnt; i++) {
+	Float t = lw * l[i] + rw * r[i];
+	if (t > 32767.)
+	    t = 32767.;
+	else if (t < -32768.)
+	    t = -32768.;
+	o[i] = t;
+	lw -= incr;
+	rw += incr;
+    }
+}
+
+static void overlapadd_i(short *l, short *r, short *o, int cnt)
+{
+    Float incr = (Float)1. / cnt;
+    Float lw = (Float)1. - incr;
+    Float rw = incr;
+    int i;
+    for (i = 0; i < cnt; i++) {
+	Float t = lw * l[i] + rw * r[i];
+	if (t > 32767.)
+	    t = 32767.;
+	else if (t < -32768.)
+	    t = -32768.;
+	o[i] = (short)t;
+	lw -= incr;
+	rw += incr;
+    }
+}
+
+/*
+ * Overlap add the end of the erasure with the start of the first good frame
+ * Scale the synthetic speech by the gain factor before the OLA.
+ */
+static void overlapaddatend(LowcFE *sess, short *s, short *f, int cnt)
+{
+    Float incr = (Float)1. / cnt;
+    Float gain;
+    Float incrg;
+    Float lw;
+    Float rw;
+    int i;
+
+    gain = (Float)1. - (sess->erasecnt - 1) * ATTENFAC;
+    if (gain < 0.)
+	gain = (Float)0.;
+    incrg = incr * gain;
+    lw = ((Float)1. - incr) * gain;
+    rw = incr;
+
+
+    for (i = 0; i < cnt; i++) {
+	Float t = lw * f[i] + rw * s[i];
+	if (t > 32767.)
+	    t = 32767.; 
+	else if (t < -32768.)
+	    t = -32768.;
+	s[i] = (short)t;
+	lw -= incrg;
+	rw += incr;
+    }
+}
+
+
+void* pjmedia_plc_g711_create(pj_pool_t *pool, unsigned clock_rate, 
+			      unsigned samples_per_frame)
+{
+    LowcFE *o;
+
+    pj_assert(clock_rate == 8000 && (samples_per_frame % FRAMESZ) == 0);
+
+    o = pj_pool_zalloc(pool, sizeof(LowcFE));
+    o->samples_per_frame = samples_per_frame;
+    init_LowcFE(o);
+
+    return o;
+}
+
+
+void  pjmedia_plc_g711_save(void *plc, pj_int16_t *frame)
+{
+    LowcFE *o = plc;
+    unsigned pos;
+
+    pos = 0;
+    while (pos < o->samples_per_frame) {
+	addtohistory(o, frame + pos);
+	pos += FRAMESZ;
+    }
+}
+
+
+void  pjmedia_plc_g711_generate(void *plc, pj_int16_t *frame)
+{
+    LowcFE *o = plc;
+    unsigned pos;
+
+    pos = 0;
+    while (pos < o->samples_per_frame) {
+	dofe(o, frame+pos);
+	pos += FRAMESZ;
+    }
+}
+
+
+#endif	/* PJMEDIA_HAS_G711_PLC */
+
Index: pjproject/trunk/pjmedia/src/pjmedia/sound_port.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/sound_port.c	(revision 416)
+++ pjproject/trunk/pjmedia/src/pjmedia/sound_port.c	(revision 417)
@@ -19,4 +19,5 @@
 #include <pjmedia/sound_port.h>
 #include <pjmedia/errno.h>
+#include <pjmedia/plc.h>
 #include <pj/assert.h>
 #include <pj/log.h>
@@ -25,5 +26,5 @@
 
 
-//#define SIMULATE_LOST_PCT   10
+//#define SIMULATE_LOST_PCT   20
 
 
@@ -35,6 +36,6 @@
 };
 
-#define DEFAULT_OPTIONS	PJMEDIA_PLC_ENABLED
-
+//#define DEFAULT_OPTIONS	PJMEDIA_PLC_ENABLED
+#define DEFAULT_OPTIONS	    0
 
 
@@ -48,7 +49,5 @@
     unsigned		 options;
 
-    void		*last_frame;
-    unsigned		 last_frame_size;
-    unsigned		 last_replay_count;
+    pjmedia_plc		*plc;
 
     unsigned		 clock_rate;
@@ -104,14 +103,6 @@
 #endif
 
-    /* Keep frame if PLC is enabled. */
-    if (snd_port->options & PJMEDIA_PLC_ENABLED) {
-	/* Must have the same length as last_frame_size */
-	pj_assert(frame.size == snd_port->last_frame_size);
-
-	/* Copy frame to last_frame */
-	pj_memcpy(snd_port->last_frame, output, snd_port->last_frame_size);
-
-	snd_port->last_replay_count = 0;
-    }
+    if (snd_port->plc)
+	pjmedia_plc_save(snd_port->plc, output);
 
     return PJ_SUCCESS;
@@ -119,39 +110,11 @@
 no_frame:
 
-    /* Replay last frame if PLC is enabled */
-    if ((snd_port->options & PJMEDIA_PLC_ENABLED) &&
-	snd_port->last_replay_count < 8) 
-    {
-
-	/* Must have the same length as last_frame_size */
-	pj_assert(size == snd_port->last_frame_size);
-
-	/* Replay last frame */
-	pj_memcpy(output, snd_port->last_frame, snd_port->last_frame_size);
-
-	/* Reduce replay frame signal level to half */
-	if (snd_port->bits_per_sample == 16) {
-	    unsigned i, count;
-	    pj_int16_t *samp;
-
-	    count = snd_port->last_frame_size / 2;
-	    samp = (pj_int16_t *) snd_port->last_frame;
-
-	    for (i=0; i<count; ++i)
-		samp[i] = (pj_int16_t) (samp[i] >> 2);
-
-	}
-
+    /* Apply PLC */
+    if (snd_port->plc) {
+
+	pjmedia_plc_generate(snd_port->plc, output);
 #ifdef SIMULATE_LOST_PCT
-	PJ_LOG(4,(THIS_FILE, "Frame replayed"));
+	PJ_LOG(4,(THIS_FILE, "Lost frame generated"));
 #endif
-
-	++snd_port->last_replay_count;
-
-    } else {
-
-	/* Just zero the frame */
-	pj_memset(output, 0, size);
-
     }
 
@@ -255,10 +218,10 @@
 	(snd_port->options & PJMEDIA_PLC_ENABLED)) 
     {
-
-	snd_port->last_frame_size = snd_port->samples_per_frame *
-				    snd_port->channel_count *
-				    snd_port->bits_per_sample / 8;
-	snd_port->last_frame = pj_pool_zalloc(pool, 
-					      snd_port->last_frame_size);
+	status = pjmedia_plc_create(pool, snd_port->clock_rate, 
+				    snd_port->samples_per_frame * 
+					snd_port->channel_count,
+				    0, &snd_port->plc);
+	if (status != PJ_SUCCESS)
+	    snd_port->plc = NULL;
     }
 
