[svn-commits] jdixon: branch jdixon/chan_usbradio-1.4 r140399 - /team/jdixon/chan_usbradio-...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Thu Aug 28 14:36:52 CDT 2008


Author: jdixon
Date: Thu Aug 28 14:36:52 2008
New Revision: 140399

URL: http://svn.digium.com/view/asterisk?view=rev&rev=140399
Log:
Added support for IRLP IMA ADPCM

Modified:
    team/jdixon/chan_usbradio-1.4/codecs/codec_adpcm.c

Modified: team/jdixon/chan_usbradio-1.4/codecs/codec_adpcm.c
URL: http://svn.digium.com/view/asterisk/team/jdixon/chan_usbradio-1.4/codecs/codec_adpcm.c?view=diff&rev=140399&r1=140398&r2=140399
==============================================================================
--- team/jdixon/chan_usbradio-1.4/codecs/codec_adpcm.c (original)
+++ team/jdixon/chan_usbradio-1.4/codecs/codec_adpcm.c Thu Aug 28 14:36:52 2008
@@ -8,6 +8,7 @@
  * All rights reserved.
  *
  * Karl Sackett <krs at linux-support.net>, 2001-03-21
+ * Jim Dixon <jim at lambdatel.com>, 2008-08-28 (Added IRLP Functionality)
  *
  * See http://www.asterisk.org for more information about
  * the Asterisk project. Please do not directly contact
@@ -23,9 +24,17 @@
 /*! \file
  *
  * \brief codec_adpcm.c - translate between signed linear and Dialogic ADPCM
+ * or IMA PCM (for IRLP).
+ *
+ * This sends the 3 byte ADPCM state info (and expects to receive it too)
+ * from the silly IRLP system at the end of the data block. In other words,
+ * instead of the datalen being samples / 2 (encoded), its 
+ * (samples / 2) + 3, with the state info added to the end of the data block.
  * 
  * \ingroup codecs
  */
+
+#define	ADPCM_IRLP
 
 #include "asterisk.h"
 
@@ -48,10 +57,343 @@
 #include "asterisk/channel.h"
 #include "asterisk/utils.h"
 
+#define BUFFER_SAMPLES   8096	/* size for the translation buffers */
+
+#ifdef	ADPCM_IRLP
+
+/***********************************************************
+For IMA ADPCM Codec, for IRLP use:
+
+Copyright 1992 by Stichting Mathematisch Centrum, Amsterdam, The
+Netherlands.
+
+                        All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its 
+documentation for any purpose and without fee is hereby granted, 
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in 
+supporting documentation, and that the names of Stichting Mathematisch
+Centrum or CWI not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior permission.
+
+STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
+FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+******************************************************************/
+
+/*
+** Intel/DVI ADPCM coder/decoder.
+**
+** The algorithm for this coder was taken from the IMA Compatability Project
+** proceedings, Vol 2, Number 2; May 1992.
+**
+** Version 1.2, 18-Dec-92.
+**
+** Change log:
+** - Fixed a stupid bug, where the delta was computed as
+**   stepsize*code/4 in stead of stepsize*(code+0.5)/4.
+** - There was an off-by-one error causing it to pick
+**   an incorrect delta once in a blue moon.
+** - The NODIVMUL define has been removed. Computations are now always done
+**   using shifts, adds and subtracts. It turned out that, because the standard
+**   is defined using shift/add/subtract, you needed bits of fixup code
+**   (because the div/mul simulation using shift/add/sub made some rounding
+**   errors that real div/mul don't make) and all together the resultant code
+**   ran slower than just using the shifts all the time.
+** - Changed some of the variable names to be more meaningful.
+*/
+
+struct adpcm_state {
+    short	valprev;	/* Previous output value */
+    char	index;		/* Index into stepsize table */
+};
+
+/* Intel ADPCM step variation table */
+static int indexTable[16] = {
+    -1, -1, -1, -1, 2, 4, 6, 8,
+    -1, -1, -1, -1, 2, 4, 6, 8,
+};
+
+static int stepsizeTable[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
+};
+
+#include "slin_adpcm_ima_ex.h"
+#include "adpcm_ima_slin_ex.h"
+
+static void
+adpcm_coder(short *indata, char *outdata, int len, struct adpcm_state *state)
+{
+    short *inp;			/* Input buffer pointer */
+    signed char *outp;		/* output buffer pointer */
+    int val;			/* Current input sample value */
+    int sign;			/* Current adpcm sign bit */
+    int delta;			/* Current adpcm output value */
+    int diff;			/* Difference between val and valprev */
+    int step;			/* Stepsize */
+    int valpred;		/* Predicted output value */
+    int vpdiff;			/* Current change to valpred */
+    int index;			/* Current step change index */
+    int outputbuffer;		/* place to keep previous 4-bit value */
+    int bufferstep;		/* toggle between outputbuffer/output */
+
+    outp = (signed char *)outdata;
+    inp = indata;
+
+    valpred = state->valprev;
+    index = state->index;
+    step = stepsizeTable[index];
+    
+    bufferstep = 1;
+    outputbuffer = 0;
+
+    for ( ; len > 0 ; len-- ) {
+	val = *inp++;
+
+	/* Step 1 - compute difference with previous value */
+	diff = val - valpred;
+	sign = (diff < 0) ? 8 : 0;
+	if ( sign ) diff = (-diff);
+
+	/* Step 2 - Divide and clamp */
+	/* Note:
+	** This code *approximately* computes:
+	**    delta = diff*4/step;
+	**    vpdiff = (delta+0.5)*step/4;
+	** but in shift step bits are dropped. The net result of this is
+	** that even if you have fast mul/div hardware you cannot put it to
+	** good use since the fixup would be too expensive.
+	*/
+	delta = 0;
+	vpdiff = (step >> 3);
+	
+	if ( diff >= step ) {
+	    delta = 4;
+	    diff -= step;
+	    vpdiff += step;
+	}
+	step >>= 1;
+	if ( diff >= step  ) {
+	    delta |= 2;
+	    diff -= step;
+	    vpdiff += step;
+	}
+	step >>= 1;
+	if ( diff >= step ) {
+	    delta |= 1;
+	    vpdiff += step;
+	}
+
+	/* Step 3 - Update previous value */
+	if ( sign )
+	  valpred -= vpdiff;
+	else
+	  valpred += vpdiff;
+
+	/* Step 4 - Clamp previous value to 16 bits */
+	if ( valpred > 32767 )
+	  valpred = 32767;
+	else if ( valpred < -32768 )
+	  valpred = -32768;
+
+	/* Step 5 - Assemble value, update index and step values */
+	delta |= sign;
+	
+	index += indexTable[delta];
+	if ( index < 0 ) index = 0;
+	if ( index > 88 ) index = 88;
+	step = stepsizeTable[index];
+
+	/* Step 6 - Output value */
+	if ( bufferstep ) {
+	    outputbuffer = (delta << 4) & 0xf0;
+	} else {
+	    *outp++ = (delta & 0x0f) | outputbuffer;
+	}
+	bufferstep = !bufferstep;
+    }
+
+    /* Output last step, if needed */
+    if ( !bufferstep )
+      *outp++ = outputbuffer;
+    
+    state->valprev = valpred;
+    state->index = index;
+}
+
+static void
+adpcm_decoder(char *indata, short *outdata, int len, struct adpcm_state *state)
+{
+    signed char *inp;		/* Input buffer pointer */
+    short *outp;		/* output buffer pointer */
+    int sign;			/* Current adpcm sign bit */
+    int delta;			/* Current adpcm output value */
+    int step;			/* Stepsize */
+    int valpred;		/* Predicted value */
+    int vpdiff;			/* Current change to valpred */
+    int index;			/* Current step change index */
+    int inputbuffer;		/* place to keep next 4-bit value */
+    int bufferstep;		/* toggle between inputbuffer/input */
+
+    outp = outdata;
+    inp = (signed char *)indata;
+
+    valpred = state->valprev;
+    index = state->index;
+    step = stepsizeTable[index];
+
+    bufferstep = 0;
+    inputbuffer = 0;
+    
+    for ( ; len > 0 ; len-- ) {
+	
+	/* Step 1 - get the delta value */
+	if ( bufferstep ) {
+	    delta = inputbuffer & 0xf;
+	} else {
+	    inputbuffer = *inp++;
+	    delta = (inputbuffer >> 4) & 0xf;
+	}
+	bufferstep = !bufferstep;
+
+	/* Step 2 - Find new index value (for later) */
+	index += indexTable[delta];
+	if ( index < 0 ) index = 0;
+	if ( index > 88 ) index = 88;
+
+	/* Step 3 - Separate sign and magnitude */
+	sign = delta & 8;
+	delta = delta & 7;
+
+	/* Step 4 - Compute difference and new predicted value */
+	/*
+	** Computes 'vpdiff = (delta+0.5)*step/4', but see comment
+	** in adpcm_coder.
+	*/
+	vpdiff = step >> 3;
+	if ( delta & 4 ) vpdiff += step;
+	if ( delta & 2 ) vpdiff += step>>1;
+	if ( delta & 1 ) vpdiff += step>>2;
+
+	if ( sign )
+	  valpred -= vpdiff;
+	else
+	  valpred += vpdiff;
+
+	/* Step 5 - clamp output value */
+	if ( valpred > 32767 )
+	  valpred = 32767;
+	else if ( valpred < -32768 )
+	  valpred = -32768;
+
+	/* Step 6 - Update step value */
+	step = stepsizeTable[index];
+
+	/* Step 7 - Output value */
+	*outp++ = valpred;
+    }
+
+    state->valprev = valpred;
+    state->index = index;
+}
+
+/*----------------- Asterisk-codec glue ------------*/
+
+/*! \brief Workspace for translating signed linear signals to ADPCM. */
+struct adpcm_encoder_pvt {
+	struct adpcm_state state;
+	int16_t inbuf[BUFFER_SAMPLES];	/* Unencoded signed linear values */
+};
+
+/*! \brief Workspace for translating ADPCM signals to signed linear. */
+struct adpcm_decoder_pvt {
+	struct adpcm_state state;
+};
+
+/*! \brief decode 4-bit adpcm frame data and store in output buffer */
+static int adpcmtolin_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
+{
+	struct adpcm_decoder_pvt *tmp = pvt->pvt;
+	int16_t *dst = (int16_t *)pvt->outbuf + pvt->samples;
+	int x = f->datalen;
+	char *cp = f->data;	
+
+	if (x > (f->samples / 2))
+	{
+		cp += (x - 3);
+		tmp->state.valprev = (cp[0] << 8) + cp[1];
+		tmp->state.index = cp[2];
+	}	
+	adpcm_decoder(f->data,dst,f->samples,&tmp->state);
+	pvt->samples += f->samples;
+	pvt->datalen += f->samples * 2;
+	return 0;
+}
+
+/*! \brief fill input buffer with 16-bit signed linear PCM values. */
+static int lintoadpcm_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
+{
+	struct adpcm_encoder_pvt *tmp = pvt->pvt;
+
+	memcpy(&tmp->inbuf[pvt->samples], f->data, f->datalen);
+	pvt->samples += f->samples;
+	return 0;
+}
+
+/*! \brief convert inbuf and store into frame */
+static struct ast_frame *lintoadpcm_frameout(struct ast_trans_pvt *pvt)
+{
+	struct adpcm_encoder_pvt *tmp = pvt->pvt;
+	struct ast_frame *f;
+	int samples = pvt->samples;	/* save original number */
+	int x;
+	char *cp;
+	struct adpcm_state istate;
+  
+	if (samples < 2)
+		return NULL;
+
+	pvt->samples &= ~1; /* atomic size is 2 samples */
+	istate = tmp->state;
+	adpcm_coder(tmp->inbuf, pvt->outbuf, pvt->samples,&tmp->state);
+	x = pvt->samples / 2;
+	cp = pvt->outbuf;
+	cp[x] = (istate.valprev & 0xff00) >> 8;
+	cp[x + 1] = istate.valprev & 0xff;
+	cp[x + 2] = istate.index; 
+	f = ast_trans_frameout(pvt, x + 3, 0);
+
+	/*
+	 * If there is a left over sample, move it to the beginning
+	 * of the input buffer.
+	 */
+
+	if (samples & 1) {	/* move the leftover sample at beginning */
+		tmp->inbuf[0] = tmp->inbuf[samples - 1];
+		pvt->samples = 1;
+	}
+	return f;
+}
+
+#else /* ADPCM_IRLP */
+
 /* define NOT_BLI to use a faster but not bit-level identical version */
 /* #define NOT_BLI */
 
-#define BUFFER_SAMPLES   8096	/* size for the translation buffers */
 
 /* Sample frame data */
 
@@ -294,6 +636,8 @@
 	return f;
 }
 
+#endif /* ADPCM_IRLP */
+
 
 /*! \brief AdpcmToLin_Sample */
 static struct ast_frame *adpcmtolin_sample(void)




More information about the svn-commits mailing list