Mercurial > hg > audiostuff
diff spandsp-0.0.3/spandsp-0.0.3/src/ima_adpcm.c @ 5:f762bf195c4b
import spandsp-0.0.3
author | Peter Meerwald <pmeerw@cosy.sbg.ac.at> |
---|---|
date | Fri, 25 Jun 2010 16:00:21 +0200 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/spandsp-0.0.3/spandsp-0.0.3/src/ima_adpcm.c Fri Jun 25 16:00:21 2010 +0200 @@ -0,0 +1,387 @@ +/* + * SpanDSP - a series of DSP components for telephony + * + * ima_adpcm.c - Conversion routines between linear 16 bit PCM data and + * IMA/DVI/Intel ADPCM format. + * + * Written by Steve Underwood <steveu@coppice.org> + * + * Copyright (C) 2001, 2004 Steve Underwood + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: ima_adpcm.c,v 1.18 2006/11/30 15:41:47 steveu Exp $ + */ + +/*! \file */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdlib.h> +#include <inttypes.h> +#include <string.h> +#if defined(HAVE_TGMATH_H) +#include <tgmath.h> +#endif +#if defined(HAVE_MATH_H) +#include <math.h> +#endif + +#include "spandsp/telephony.h" +#include "spandsp/dc_restore.h" +#include "spandsp/ima_adpcm.h" + +/* + * Intel/DVI ADPCM coder/decoder. + * + * The algorithm for this coder was taken from the IMA Compatability Project + * proceedings, Vol 2, Number 2; May 1992. + * + * The RTP payload specs. reference a variant of DVI, called VDVI. This attempts to + * further compresses, in a variable bit rate manner, by expressing the 4 bit codes + * from the DVI codec as: + * + * 0 00 + * 1 010 + * 2 1100 + * 3 11100 + * 4 111100 + * 5 1111100 + * 6 11111100 + * 7 11111110 + * 8 10 + * 9 011 + * 10 1101 + * 11 11101 + * 12 111101 + * 13 1111101 + * 14 11111101 + * 15 11111111 + * + * Any left over bits in the last octet of an encoded burst are set to one. + */ + +/* Intel ADPCM step variation table */ +static const int step_size[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 +}; + +static const int step_adjustment[8] = +{ + -1, -1, -1, -1, 2, 4, 6, 8 +}; + +static const struct +{ + uint8_t code; + uint8_t bits; +} vdvi_encode[] = +{ + {0x00, 2}, + {0x02, 3}, + {0x0C, 4}, + {0x1C, 5}, + {0x3C, 6}, + {0x7C, 7}, + {0xFC, 8}, + {0xFE, 8}, + {0x02, 2}, + {0x03, 3}, + {0x0D, 4}, + {0x1D, 5}, + {0x3D, 6}, + {0x7D, 7}, + {0xFD, 8}, + {0xFF, 8} +}; + +static const struct +{ + uint16_t code; + uint16_t mask; + uint8_t bits; +} vdvi_decode[] = +{ + {0x0000, 0xC000, 2}, + {0x4000, 0xE000, 3}, + {0xC000, 0xF000, 4}, + {0xE000, 0xF800, 5}, + {0xF000, 0xFC00, 6}, + {0xF800, 0xFE00, 7}, + {0xFC00, 0xFF00, 8}, + {0xFE00, 0xFF00, 8}, + {0x8000, 0xC000, 2}, + {0x6000, 0xE000, 3}, + {0xD000, 0xF000, 4}, + {0xE800, 0xF800, 5}, + {0xF400, 0xFC00, 6}, + {0xFA00, 0xFE00, 7}, + {0xFD00, 0xFF00, 8}, + {0xFF00, 0xFF00, 8} +}; + +static int16_t decode(ima_adpcm_state_t *s, uint8_t adpcm) +{ + int e; + int ss; + int16_t linear; + + /* e = (adpcm+0.5)*step/4 */ + + ss = step_size[s->step_index]; + e = ss >> 3; + if (adpcm & 0x01) + e += (ss >> 2); + /*endif*/ + if (adpcm & 0x02) + e += (ss >> 1); + /*endif*/ + if (adpcm & 0x04) + e += ss; + /*endif*/ + if (adpcm & 0x08) + e = -e; + /*endif*/ + linear = saturate(s->last + e); + s->last = linear; + s->step_index += step_adjustment[adpcm & 0x07]; + if (s->step_index < 0) + s->step_index = 0; + else if (s->step_index > 88) + s->step_index = 88; + /*endif*/ + return linear; +} +/*- End of function --------------------------------------------------------*/ + +static uint8_t encode(ima_adpcm_state_t *s, int16_t linear) +{ + int e; + int ss; + int adpcm; + int diff; + int initial_e; + + ss = step_size[s->step_index]; + initial_e = + e = linear - s->last; + diff = ss >> 3; + adpcm = (uint8_t) 0x00; + if (e < 0) + { + adpcm = (uint8_t) 0x08; + e = -e; + } + /*endif*/ + if (e >= ss) + { + adpcm |= (uint8_t) 0x04; + e -= ss; + } + /*endif*/ + ss >>= 1; + if (e >= ss) + { + adpcm |= (uint8_t) 0x02; + e -= ss; + } + /*endif*/ + ss >>= 1; + if (e >= ss) + { + adpcm |= (uint8_t) 0x01; + e -= ss; + } + /*endif*/ + + if (initial_e < 0) + diff = -(diff - initial_e - e); + else + diff = diff + initial_e - e; + /*endif*/ + s->last = saturate(diff + s->last); + s->step_index += step_adjustment[adpcm & 0x07]; + if (s->step_index < 0) + s->step_index = 0; + else if (s->step_index > 88) + s->step_index = 88; + /*endif*/ + return (uint8_t) adpcm; +} +/*- End of function --------------------------------------------------------*/ + +ima_adpcm_state_t *ima_adpcm_init(ima_adpcm_state_t *s, int variant) +{ + if (s == NULL) + { + if ((s = (ima_adpcm_state_t *) malloc(sizeof(*s))) == NULL) + return NULL; + } + /*endif*/ + memset(s, 0, sizeof(*s)); + s->variant = variant; + return s; +} +/*- End of function --------------------------------------------------------*/ + +int ima_adpcm_release(ima_adpcm_state_t *s) +{ + free(s); + return 0; +} +/*- End of function --------------------------------------------------------*/ + +int ima_adpcm_decode(ima_adpcm_state_t *s, + int16_t amp[], + const uint8_t ima_data[], + int ima_bytes) +{ + int i; + int j; + int samples; + uint16_t code; + + samples = 0; + if (s->variant == IMA_ADPCM_VDVI) + { + code = 0; + s->bits = 0; + for (i = 0; ; ) + { + if (s->bits <= 8) + { + if (i >= ima_bytes) + break; + /*endif*/ + code |= ((uint16_t) ima_data[i++] << (8 - s->bits)); + s->bits += 8; + } + /*endif*/ + for (j = 0; j < 8; j++) + { + if ((vdvi_decode[j].mask & code) == vdvi_decode[j].code) + break; + if ((vdvi_decode[j + 8].mask & code) == vdvi_decode[j + 8].code) + { + j += 8; + break; + } + /*endif*/ + } + /*endfor*/ + amp[samples++] = decode(s, (uint8_t) j); + code <<= vdvi_decode[j].bits; + s->bits -= vdvi_decode[j].bits; + } + /*endfor*/ + /* Use up the remanents of the last octet */ + while (s->bits > 0) + { + for (j = 0; j < 8; j++) + { + if ((vdvi_decode[j].mask & code) == vdvi_decode[j].code) + break; + /*endif*/ + if ((vdvi_decode[j + 8].mask & code) == vdvi_decode[j + 8].code) + { + j += 8; + break; + } + /*endif*/ + } + /*endfor*/ + if (vdvi_decode[j].bits > s->bits) + break; + /*endif*/ + amp[samples++] = decode(s, (uint8_t) j); + code <<= vdvi_decode[j].bits; + s->bits -= vdvi_decode[j].bits; + } + /*endfor*/ + } + else + { + for (i = 0; i < ima_bytes; i++) + { + amp[samples++] = decode(s, ima_data[i] & 0xF); + amp[samples++] = decode(s, (ima_data[i] >> 4) & 0xF); + } + /*endwhile*/ + } + /*endif*/ + return samples; +} +/*- End of function --------------------------------------------------------*/ + +int ima_adpcm_encode(ima_adpcm_state_t *s, + uint8_t ima_data[], + const int16_t amp[], + int len) +{ + int i; + int bytes; + uint8_t code; + + bytes = 0; + if (s->variant == IMA_ADPCM_VDVI) + { + s->bits = 0; + for (i = 0; i < len; i++) + { + code = encode(s, amp[i]); + s->ima_byte = (s->ima_byte << vdvi_encode[code].bits) | vdvi_encode[code].code; + s->bits += vdvi_encode[code].bits; + if (s->bits >= 8) + { + s->bits -= 8; + ima_data[bytes++] = (uint8_t) (s->ima_byte >> s->bits); + } + /*endif*/ + } + /*endfor*/ + if (s->bits) + { + ima_data[bytes++] = (uint8_t) (((s->ima_byte << 8) | 0xFF) >> s->bits); + } + /*endif*/ + } + else + { + for (i = 0; i < len; i++) + { + s->ima_byte = (uint8_t) ((s->ima_byte >> 4) | (encode(s, amp[i]) << 4)); + if ((s->bits++ & 1)) + ima_data[bytes++] = (uint8_t) s->ima_byte; + /*endif*/ + } + /*endfor*/ + } + /*endif*/ + return bytes; +} +/*- End of function --------------------------------------------------------*/ +/*- End of file ------------------------------------------------------------*/