/* * zyxel-o56k.c * * Coverts voice messages between pvf file format and ADPCM variation * used in ZyXEL Omni 56K modem series. Note that claims in the modem * documentation that they support IMA ADPCM standard are incorrect * and this algorithm is definitely not IMA ADPCM (it stands very * close to the latter but uses different stepsize tables etc.). * * The code has been taken from "Digispeech ADPCM encoding/decoding * programs", version 1.0 (date created: Jan 16, 1992; date revised: * Jan 24, 1992). * * Copyright 1992 by Digispeech Inc. * Author: Yuhang Wu * * Modified for use with vgetty, Const Kaplinsky * * $Id: zyxel-o56k.c,v 1.1 2000/07/22 10:01:01 marcs Exp $ * */ #include #include #include "pvf.h" #define BL 16380 /* ADPCM stepsize table */ static short stepsize[137] = { 16, 17, 18, 19, 20, 22, 23, 24, 25, 27, 28, 30, 32, 33, 35, 37, 39, 41, 44, 46, 49, 51, 54, 57, 61, 64, 67, 71, 75, 79, 84, 88, 93, 98, 104, 109, 116, 122, 129, 136, 143, 151, 159, 168, 178, 187, 198, 209, 220, 232, 245, 258, 273, 288, 304, 320, 338, 357, 376, 397, 419, 442, 466, 492, 519, 548, 578, 609, 643, 678, 716, 755, 797, 841, 887, 936, 987, 1041, 1099, 1159, 1223, 1290, 1361, 1436, 1515, 1598, 1686, 1779, 1877, 1980, 2089, 2204, 2325, 2453, 2588, 2731, 2881, 3039, 3207, 3383, 3569, 3765, 3973, 4191, 4422, 4665, 4921, 5192, 5478, 5779, 6097, 6432, 6786, 7159, 7553, 7968, 8407, 8869, 9357, 9872, 10415, 10987, 11592, 12229, 12902, 13612, 14360, 15150, 15983, 16863, 17790, 18769, 19801, 20890, 22039, 23251, 24530 }; /* Table of stepsize index */ static short indextbl[8] = { -2, -2, -2, -2, 3, 9, 13, 16 }; /* functions */ static char encode_c (short Delta_N, short Dn); static short decode_c (short Delta_N, char c); int pvftozo56k (FILE *fd_in, FILE *fd_out, pvf_header *header_in) { /* variables */ short i, j, k; /* loop index */ short xn; /* input sample */ short xn1; /* signal estimate */ short Delta_N; /* stepsize */ short Dn; /* differential value */ short stepindex; /* index for stepsize table */ char codeword; /* ADPCM codeword */ short xni[5]; /* delayed speech samples */ unsigned char c = 0; /* ADPCM sample */ unsigned char *xo; /* output buffer */ /* memory allocation for output buffer */ xo = (unsigned char *)calloc(BL,sizeof(unsigned char)); if (xo == NULL) return ERROR; /* initial conditions */ xn1 = 0; Delta_N = 16; stepindex = 0; codeword = 0; xni[0] = xni[1] = xni[2] = xni[3] = xni[4] = 0; /* loop until end of file */ while (!feof (fd_in)) { k = 0; for (j = 0; !feof (fd_in) && j < BL*2; j++) { xn = (short)(header_in->read_pvf_data(fd_in) >> 9); if (ferror (fd_in)) break; /* calculate a prediction of input sample */ xn1 = xni[1] + (xni[1] >> 3 ) + (xni[1] >> 4); xn1 -= ( xni[2] >> 3 ) + ( xni[2] >> 6 ); xn1 -= ( xni[3] >> 1 ) - ( xni[3] >> 6 ); xn1 += ( xni[4] >> 2 ); if (xn1 < -16384) xn1 = -16384; else if (xn1 > 16383) xn1 = 16383; /* differential value calculation */ Dn = xn - xn1; /* encode Dn relative to the current stepsize */ codeword = encode_c( Delta_N, Dn ); /* write ADPCM sample to output buffer */ if ( j%2 == 0 ) { c = 0x00F0 & ( codeword << 4 ); } else { c += 0x000F & codeword; xo[k++] = c; } /* decode ADPCM code value to reproduce Dn and accumulates an estimated xn */ xn1 += decode_c(Delta_N, codeword); if (xn1 < -16384) xn1 = -16384; else if (xn1 > 16383) xn1 = 16383; /* shift predictor register */ for (i=1; i<4; i++) xni[i+1] = xni[i]; xni[1] = xn1; /* stepsize adaptation */ stepindex += indextbl[ codeword&7 ]; if ( stepindex < 0 ) stepindex = 0; else if ( stepindex > 136 ) stepindex = 136; Delta_N = stepsize[stepindex]; } /* write ADPCM samples from output buffer to output file */ if (fwrite(xo, 1, k, fd_out) != k) { free(xo); return ERROR; } } free(xo); if (ferror (fd_in)) return ERROR; return OK; } int zo56ktopvf (FILE *fd_in, FILE *fd_out, pvf_header *header_out) { /* variables */ short xn1; /* signal estimate */ short Delta_N; /* stepsize */ short stepindex; /* index for stepsize table */ char codeword; /* ADPCM codeword */ short i, j, k; /* loop index */ short xni[5]; /* delayed speech samples */ short RL; /* sample index */ unsigned char *xn; /* input buffer */ /* initial conditions */ xn1 = 0; Delta_N = 16; stepindex = 0; xni[0] = xni[1] = xni[2] = xni[3] = xni[4] = 0; /* memory allocation for input buffer */ xn = (unsigned char *)calloc(BL, sizeof(unsigned char)); if (xn == NULL) return ERROR; /* loop until end of file */ while (!feof (fd_in)) { RL = fread(xn, sizeof(unsigned char), BL, fd_in); for (j=0; j>4); else codeword = 0x0F & xn[j]; /* calculate a prediction of the speech sample */ xn1 = xni[1] + (xni[1] >> 3 ) + (xni[1] >> 4); xn1 -= ( xni[2] >> 3 ) + ( xni[2] >> 6 ); xn1 -= ( xni[3] >> 1 ) - ( xni[3] >> 6 ); xn1 += ( xni[4] >> 2 ); if (xn1 < -16384) xn1 = -16384; else if (xn1 > 16383) xn1 = 16383; /* decode ADPCM code value to reproduce Dn and accumulates an estimated xn */ xn1 += decode_c(Delta_N, codeword); if (xn1 < -16384) xn1 = -16384; else if (xn1 > 16383) xn1 = 16383; /* shift prediction register */ for (i=1; i<4; i++) xni[i+1] = xni[i]; xni[1] = xn1; /* stepsize adaptation */ stepindex += indextbl[ codeword&7 ]; if ( stepindex < 0 ) stepindex = 0; else if ( stepindex > 136 ) stepindex = 136; Delta_N = stepsize[stepindex]; /* write reproduced sample to output file */ header_out->write_pvf_data(fd_out, (int)xn1 << 9); } } } free(xn); if (ferror (fd_in)) return ERROR; return OK; } /* function to encode the differential value and output an ADPCM codeword function return value: char; ADPCM codeword */ static char encode_c (short Delta_N, short Dn) /* parameters: short Dn : input; the differential value; short Delta_N: input; the stepsize; */ { char c; c = 0; if ( Dn < 0 ) { c = 8; Dn = -Dn; } if ( Dn >= Delta_N ) { c += 4; Dn -= Delta_N; } if ( Dn >= (Delta_N>>1) ) { c += 2; Dn -= Delta_N>>1; } if ( Dn >= (Delta_N>>2) ) c += 1; return ( c ); } /* function to calculate the differential value from an ADPCM codeword function return value: short; reproduced differential vale */ static short decode_c (short Delta_N, char c) /* parameters: char c : input; the ADPCM codeword; short Delta_N: input; the stepsize; */ { short b; b = Delta_N >> 3; b += (c&4) ? Delta_N : 0; b += (c&2) ? Delta_N >> 1 : 0; b += (c&1) ? Delta_N >> 2 : 0; return( (c&8) ? -b : b ); }