Mercurial > hg > audiostuff
diff spandsp-0.0.6pre17/src/oki_adpcm.c @ 4:26cd8f1ef0b1
import spandsp-0.0.6pre17
author | Peter Meerwald <pmeerw@cosy.sbg.ac.at> |
---|---|
date | Fri, 25 Jun 2010 15:50:58 +0200 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/spandsp-0.0.6pre17/src/oki_adpcm.c Fri Jun 25 15:50:58 2010 +0200 @@ -0,0 +1,387 @@ +/* + * SpanDSP - a series of DSP components for telephony + * + * oki_adpcm.c - Conversion routines between linear 16 bit PCM data and + * OKI (Dialogic) ADPCM format. Supports with the 32kbps + * and 24kbps variants used by Dialogic. + * + * 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 Lesser General Public License version 2.1, + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * The actual OKI ADPCM encode and decode method is derived from freely + * available code, whose exact origins seem uncertain. + * + * $Id: oki_adpcm.c,v 1.32 2009/02/10 13:06:46 steveu Exp $ + */ + +/*! \file */ + +#if defined(HAVE_CONFIG_H) +#include "config.h" +#endif + +#include <stdlib.h> +#include <inttypes.h> +#include <string.h> + +#include "spandsp/telephony.h" +#include "spandsp/oki_adpcm.h" +#include "spandsp/private/oki_adpcm.h" + +/* Routines to convert 12 bit linear samples to the Oki ADPCM coding format, + widely used in CTI, because Dialogic use it. */ + +/* OKI ADPCM step variation table */ +static const int16_t step_size[49] = +{ + 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 +}; + +static const int16_t step_adjustment[8] = +{ + -1, -1, -1, -1, 2, 4, 6, 8 +}; + +/* Band limiting filter, to allow sample rate conversion to and + from 6k samples/second. */ +static const float cutoff_coeffs[] = +{ + -3.648392e-4f, + 5.062391e-4f, + 1.206247e-3f, + 1.804452e-3f, + 1.691750e-3f, + 4.083405e-4f, + -1.931085e-3f, + -4.452107e-3f, + -5.794821e-3f, + -4.778489e-3f, + -1.161266e-3f, + 3.928504e-3f, + 8.259786e-3f, + 9.500425e-3f, + 6.512800e-3f, + 2.227856e-4f, + -6.531275e-3f, + -1.026843e-2f, + -8.718062e-3f, + -2.280487e-3f, + 5.817733e-3f, + 1.096777e-2f, + 9.634404e-3f, + 1.569301e-3f, + -9.522632e-3f, + -1.748273e-2f, + -1.684408e-2f, + -6.100054e-3f, + 1.071206e-2f, + 2.525209e-2f, + 2.871779e-2f, + 1.664411e-2f, + -7.706268e-3f, + -3.331083e-2f, + -4.521249e-2f, + -3.085962e-2f, + 1.373653e-2f, + 8.089593e-2f, + 1.529060e-1f, + 2.080487e-1f, + 2.286834e-1f, + 2.080487e-1f, + 1.529060e-1f, + 8.089593e-2f, + 1.373653e-2f, + -3.085962e-2f, + -4.521249e-2f, + -3.331083e-2f, + -7.706268e-3f, + 1.664411e-2f, + 2.871779e-2f, + 2.525209e-2f, + 1.071206e-2f, + -6.100054e-3f, + -1.684408e-2f, + -1.748273e-2f, + -9.522632e-3f, + 1.569301e-3f, + 9.634404e-3f, + 1.096777e-2f, + 5.817733e-3f, + -2.280487e-3f, + -8.718062e-3f, + -1.026843e-2f, + -6.531275e-3f, + 2.227856e-4f, + 6.512800e-3f, + 9.500425e-3f, + 8.259786e-3f, + 3.928504e-3f, + -1.161266e-3f, + -4.778489e-3f, + -5.794821e-3f, + -4.452107e-3f, + -1.931085e-3f, + 4.083405e-4f, + 1.691750e-3f, + 1.804452e-3f, + 1.206247e-3f, + 5.062391e-4f, + -3.648392e-4f +}; + +static int16_t decode(oki_adpcm_state_t *s, uint8_t adpcm) +{ + int16_t e; + int16_t ss; + int16_t linear; + + /* Doing the next part as follows: + * + * x = adpcm & 0x07; + * e = (step_size[s->step_index]*(x + x + 1)) >> 3; + * + * Seems an obvious improvement on a modern machine, but remember + * the truncation errors do not come out the same. It would + * not, therefore, be an exact match for what this code is doing. + * + * Just what a Dialogic card does, I do not know! + */ + + 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 = s->last + e; + + /* Saturate the values to +/- 2^11 (supposed to be 12 bits) */ + if (linear > 2047) + linear = 2047; + else if (linear < -2048) + linear = -2048; + /*endif*/ + + s->last = linear; + s->step_index += step_adjustment[adpcm & 0x07]; + if (s->step_index < 0) + s->step_index = 0; + else if (s->step_index > 48) + s->step_index = 48; + /*endif*/ + /* Note: the result here is a 12 bit value */ + return linear; +} +/*- End of function --------------------------------------------------------*/ + +static uint8_t encode(oki_adpcm_state_t *s, int16_t linear) +{ + int16_t e; + int16_t ss; + uint8_t adpcm; + + ss = step_size[s->step_index]; + e = (linear >> 4) - s->last; + adpcm = (uint8_t) 0x00; + if (e < 0) + { + adpcm = (uint8_t) 0x08; + e = -e; + } + /*endif*/ + if (e >= ss) + { + adpcm |= (uint8_t) 0x04; + e -= ss; + } + /*endif*/ + if (e >= (ss >> 1)) + { + adpcm |= (uint8_t) 0x02; + e -= ss; + } + /*endif*/ + if (e >= (ss >> 2)) + adpcm |= (uint8_t) 0x01; + /*endif*/ + + /* Use the decoder to set the estimate of the last sample. */ + /* It also will adjust the step_index for us. */ + s->last = decode(s, adpcm); + return adpcm; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(oki_adpcm_state_t *) oki_adpcm_init(oki_adpcm_state_t *s, int bit_rate) +{ + if (bit_rate != 32000 && bit_rate != 24000) + return NULL; + if (s == NULL) + { + if ((s = (oki_adpcm_state_t *) malloc(sizeof(*s))) == NULL) + return NULL; + } + memset(s, 0, sizeof(*s)); + s->bit_rate = bit_rate; + + return s; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(int) oki_adpcm_release(oki_adpcm_state_t *s) +{ + return 0; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(int) oki_adpcm_free(oki_adpcm_state_t *s) +{ + free(s); + return 0; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(int) oki_adpcm_decode(oki_adpcm_state_t *s, + int16_t amp[], + const uint8_t oki_data[], + int oki_bytes) +{ + int i; + int x; + int l; + int n; + int samples; + float z; + +#if (_MSC_VER >= 1400) + __analysis_assume(s->phase >= 0 && s->phase <= 4); +#endif + samples = 0; + if (s->bit_rate == 32000) + { + for (i = 0; i < oki_bytes; i++) + { + amp[samples++] = decode(s, (oki_data[i] >> 4) & 0xF) << 4; + amp[samples++] = decode(s, oki_data[i] & 0xF) << 4; + } + /*endwhile*/ + } + else + { + n = 0; + for (i = 0; i < oki_bytes; ) + { + /* 6k to 8k sample/second conversion */ + if (s->phase) + { + s->history[s->ptr++] = + decode(s, (n++ & 1) ? (oki_data[i++] & 0xF) : ((oki_data[i] >> 4) & 0xF)) << 4; + s->ptr &= (32 - 1); + } + /*endif*/ + z = 0.0f; + for (l = 80 - 3 + s->phase, x = s->ptr - 1; l >= 0; l -= 4, x--) + z += cutoff_coeffs[l]*s->history[x & (32 - 1)]; + amp[samples++] = (int16_t) (z*4.0f); + if (++s->phase > 3) + s->phase = 0; + /*endif*/ + } + /*endfor*/ + } + /*endif*/ + return samples; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(int) oki_adpcm_encode(oki_adpcm_state_t *s, + uint8_t oki_data[], + const int16_t amp[], + int len) +{ + int x; + int l; + int n; + int bytes; + float z; + + bytes = 0; + if (s->bit_rate == 32000) + { + for (n = 0; n < len; n++) + { + s->oki_byte = (s->oki_byte << 4) | encode(s, amp[n]); + if ((s->mark++ & 1)) + oki_data[bytes++] = s->oki_byte; + /*endif*/ + } + /*endfor*/ + } + else + { + n = 0; + for (;;) + { + /* 8k to 6k sample/second conversion */ + if (s->phase > 2) + { + s->history[s->ptr++] = amp[n]; + s->ptr &= (32 - 1); + s->phase = 0; + if (++n >= len) + break; + /*endif*/ + } + /*endif*/ + s->history[s->ptr++] = amp[n]; + s->ptr &= (32 - 1); + z = 0.0f; + for (l = 80 - s->phase, x = s->ptr - 1; l >= 0; l -= 3, x--) + z += cutoff_coeffs[l]*s->history[x & (32 - 1)]; + /*endfor*/ + s->oki_byte = (s->oki_byte << 4) | encode(s, (int16_t) (z*3.0f)); + if ((s->mark++ & 1)) + oki_data[bytes++] = s->oki_byte; + /*endif*/ + s->phase++; + if (++n >= len) + break; + /*endif*/ + } + /*endfor*/ + } + /*endif*/ + return bytes; +} +/*- End of function --------------------------------------------------------*/ +/*- End of file ------------------------------------------------------------*/