Import of libpvf

This commit is contained in:
dcp1990 2005-06-14 02:11:33 +00:00
parent 7790a48779
commit 01eeffa5e0
11 changed files with 4479 additions and 0 deletions

564
lib/libpvf/au.c Normal file
View File

@ -0,0 +1,564 @@
/*
* au.c
*
* Conversion pvf <--> au.
*
* $Id: au.c,v 1.6 2001/05/14 09:52:30 marcs Exp $
*
*/
#include "../include/voice.h"
typedef long Word; /* must be 32 bits */
typedef struct
{
Word magic; /* magic number SND_MAGIC */
Word dataLocation; /* offset or pointer to the data */
Word dataSize; /* number of bytes of data */
Word dataFormat; /* the data format code */
Word samplingRate; /* the sampling rate */
Word channelCount; /* the number of channels */
Word info; /* optional text information */
} SNDSoundStruct;
#define SND_MAGIC (0x2e736e64L)
#define SND_HEADER_SIZE 28
#define SND_UNKNOWN_SIZE ((Word)(-1))
#ifdef PRINT_INFO
static char sound_format[][30] =
{
"unspecified",
"uLaw_8",
"linear_8",
"linear_16",
"linear_24",
"linear_32",
"float",
"double",
"fragmented",
"aLaw_8",
};
#endif
/*
* This routine converts from linear 16 bit to 8 bit ulaw.
*
* Craig Reese: IDA/Supercomputing Research Center
* Joe Campbell: Department of Defense
* 29 September 1989
*
* References:
* 1) CCITT Recommendation G.711 (very difficult to follow)
* 2) "A New Digital Technique for Implementation of Any
* Continuous PCM Companding Law," Villeret, Michel,
* et al. 1973 IEEE Int. Conf. on Communications, Vol 1,
* 1973, pg. 11.12-11.17
* 3) MIL-STD-188-113,"Interoperability and Performance Standards
* for Analog-to_Digital Conversion Techniques,"
* 17 February 1987
*
* Input: Signed 16 bit linear sample
* Output: 8 bit ulaw sample
*/
#define ZEROTRAP /* turn on the trap as per the MIL-STD */
#undef ZEROTRAP
#define BIAS 0x84 /* define the add-in bias for 16 bit samples */
#define CLIP 32635
unsigned char linear2ulaw (int sample)
{
static int exp_lut[256] =
{
0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 ,6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
};
int sign;
int exponent;
int mantissa;
unsigned char ulawbyte;
/*
* Get the sample into sign-magnitude.
*/
sign = (sample >> 8) & 0x80; /* set aside the sign */
if (sign != 0) /* get magnitude */
sample = -sample;
if (sample > CLIP) /* clip the magnitude */
sample = CLIP;
/*
* Convert from 16 bit linear to ulaw.
*/
sample = sample + BIAS;
exponent = exp_lut[(sample >> 7 ) & 0xff];
mantissa = (sample >> (exponent + 3)) & 0x0f;
ulawbyte = ~(sign | (exponent << 4) | mantissa);
#ifdef ZEROTRAP
if (ulawbyte == 0)
ulawbyte = 0x02; /* optional CCITT trap */
#endif
return(ulawbyte);
}
/*
* This routine converts from ulaw to 16 bit linear.
*
* Craig Reese: IDA/Supercomputing Research Center
* 29 September 1989
*
* References:
* 1) CCITT Recommendation G.711 (very difficult to follow)
* 2) MIL-STD-188-113,"Interoperability and Performance Standards
* for Analog-to_Digital Conversion Techniques,"
* 17 February 1987
*
* Input: 8 bit ulaw sample
* Output: signed 16 bit linear sample
*/
int ulaw2linear (unsigned char ulawbyte)
{
static int exp_lut[8] = {0, 132, 396, 924, 1980, 4092, 8316, 16764};
int sign;
int exponent;
int mantissa;
int sample;
ulawbyte = ~ulawbyte;
sign = (ulawbyte & 0x80);
exponent = (ulawbyte >> 4) & 0x07;
mantissa = ulawbyte & 0x0f;
sample = exp_lut[exponent] + (mantissa << (exponent + 3));
if (sign != 0)
sample = -sample;
return(sample);
}
/* The following routines convert 8bit ulaw/alaw to 8bit alaw/ulaw
* This is merely a copy of the stuff in the ISDN4Linux kernel driver
*/
unsigned char ulaw2alaw (unsigned char ulawbyte)
{
static unsigned char ulaw_to_alaw[] =
{
0xab, 0x55, 0xd5, 0x15, 0x95, 0x75, 0xf5, 0x35,
0xb5, 0x45, 0xc5, 0x05, 0x85, 0x65, 0xe5, 0x25,
0xa5, 0x5d, 0xdd, 0x1d, 0x9d, 0x7d, 0xfd, 0x3d,
0xbd, 0x4d, 0xcd, 0x0d, 0x8d, 0x6d, 0xed, 0x2d,
0xad, 0x51, 0xd1, 0x11, 0x91, 0x71, 0xf1, 0x31,
0xb1, 0x41, 0xc1, 0x01, 0x81, 0x61, 0xe1, 0x21,
0x59, 0xd9, 0x19, 0x99, 0x79, 0xf9, 0x39, 0xb9,
0x49, 0xc9, 0x09, 0x89, 0x69, 0xe9, 0x29, 0xa9,
0xd7, 0x17, 0x97, 0x77, 0xf7, 0x37, 0xb7, 0x47,
0xc7, 0x07, 0x87, 0x67, 0xe7, 0x27, 0xa7, 0xdf,
0x9f, 0x7f, 0xff, 0x3f, 0xbf, 0x4f, 0xcf, 0x0f,
0x8f, 0x6f, 0xef, 0x2f, 0x53, 0x13, 0x73, 0x33,
0xb3, 0x43, 0xc3, 0x03, 0x83, 0x63, 0xe3, 0x23,
0xa3, 0x5b, 0xdb, 0x1b, 0x9b, 0x7b, 0xfb, 0x3b,
0xbb, 0xbb, 0x4b, 0x4b, 0xcb, 0xcb, 0x0b, 0x0b,
0x8b, 0x8b, 0x6b, 0x6b, 0xeb, 0xeb, 0x2b, 0x2b,
0xab, 0x54, 0xd4, 0x14, 0x94, 0x74, 0xf4, 0x34,
0xb4, 0x44, 0xc4, 0x04, 0x84, 0x64, 0xe4, 0x24,
0xa4, 0x5c, 0xdc, 0x1c, 0x9c, 0x7c, 0xfc, 0x3c,
0xbc, 0x4c, 0xcc, 0x0c, 0x8c, 0x6c, 0xec, 0x2c,
0xac, 0x50, 0xd0, 0x10, 0x90, 0x70, 0xf0, 0x30,
0xb0, 0x40, 0xc0, 0x00, 0x80, 0x60, 0xe0, 0x20,
0x58, 0xd8, 0x18, 0x98, 0x78, 0xf8, 0x38, 0xb8,
0x48, 0xc8, 0x08, 0x88, 0x68, 0xe8, 0x28, 0xa8,
0xd6, 0x16, 0x96, 0x76, 0xf6, 0x36, 0xb6, 0x46,
0xc6, 0x06, 0x86, 0x66, 0xe6, 0x26, 0xa6, 0xde,
0x9e, 0x7e, 0xfe, 0x3e, 0xbe, 0x4e, 0xce, 0x0e,
0x8e, 0x6e, 0xee, 0x2e, 0x52, 0x12, 0x72, 0x32,
0xb2, 0x42, 0xc2, 0x02, 0x82, 0x62, 0xe2, 0x22,
0xa2, 0x5a, 0xda, 0x1a, 0x9a, 0x7a, 0xfa, 0x3a,
0xba, 0xba, 0x4a, 0x4a, 0xca, 0xca, 0x0a, 0x0a,
0x8a, 0x8a, 0x6a, 0x6a, 0xea, 0xea, 0x2a, 0x2a
};
return ulaw_to_alaw[ulawbyte];
}
unsigned char alaw2ulaw (unsigned char alawbyte)
{
static unsigned char alaw_to_ulaw[] =
{
0xab, 0x2b, 0xe3, 0x63, 0x8b, 0x0b, 0xc9, 0x49,
0xba, 0x3a, 0xf6, 0x76, 0x9b, 0x1b, 0xd7, 0x57,
0xa3, 0x23, 0xdd, 0x5d, 0x83, 0x03, 0xc1, 0x41,
0xb2, 0x32, 0xeb, 0x6b, 0x93, 0x13, 0xcf, 0x4f,
0xaf, 0x2f, 0xe7, 0x67, 0x8f, 0x0f, 0xcd, 0x4d,
0xbe, 0x3e, 0xfe, 0x7e, 0x9f, 0x1f, 0xdb, 0x5b,
0xa7, 0x27, 0xdf, 0x5f, 0x87, 0x07, 0xc5, 0x45,
0xb6, 0x36, 0xef, 0x6f, 0x97, 0x17, 0xd3, 0x53,
0xa9, 0x29, 0xe1, 0x61, 0x89, 0x09, 0xc7, 0x47,
0xb8, 0x38, 0xf2, 0x72, 0x99, 0x19, 0xd5, 0x55,
0xa1, 0x21, 0xdc, 0x5c, 0x81, 0x01, 0xbf, 0x3f,
0xb0, 0x30, 0xe9, 0x69, 0x91, 0x11, 0xce, 0x4e,
0xad, 0x2d, 0xe5, 0x65, 0x8d, 0x0d, 0xcb, 0x4b,
0xbc, 0x3c, 0xfa, 0x7a, 0x9d, 0x1d, 0xd9, 0x59,
0xa5, 0x25, 0xde, 0x5e, 0x85, 0x05, 0xc3, 0x43,
0xb4, 0x34, 0xed, 0x6d, 0x95, 0x15, 0xd1, 0x51,
0xac, 0x2c, 0xe4, 0x64, 0x8c, 0x0c, 0xca, 0x4a,
0xbb, 0x3b, 0xf8, 0x78, 0x9c, 0x1c, 0xd8, 0x58,
0xa4, 0x24, 0xde, 0x5e, 0x84, 0x04, 0xc2, 0x42,
0xb3, 0x33, 0xec, 0x6c, 0x94, 0x14, 0xd0, 0x50,
0xb0, 0x30, 0xe8, 0x68, 0x90, 0x10, 0xce, 0x4e,
0xbf, 0x3f, 0xfe, 0x7e, 0xa0, 0x20, 0xdc, 0x5c,
0xa8, 0x28, 0xe0, 0x60, 0x88, 0x08, 0xc6, 0x46,
0xb7, 0x37, 0xf0, 0x70, 0x98, 0x18, 0xd4, 0x54,
0xaa, 0x2a, 0xe2, 0x62, 0x8a, 0x0a, 0xc8, 0x48,
0xb9, 0x39, 0xf4, 0x74, 0x9a, 0x1a, 0xd6, 0x56,
0xa2, 0x22, 0xdd, 0x5d, 0x82, 0x02, 0xc0, 0x40,
0xb1, 0x31, 0xea, 0x6a, 0x92, 0x12, 0xcf, 0x4f,
0xae, 0x2e, 0xe6, 0x66, 0x8e, 0x0e, 0xcc, 0x4c,
0xbd, 0x3d, 0xfc, 0x7c, 0x9e, 0x1e, 0xda, 0x5a,
0xa6, 0x26, 0xdf, 0x5f, 0x86, 0x06, 0xc4, 0x44,
0xb5, 0x35, 0xee, 0x6e, 0x96, 0x16, 0xd2, 0x52
};
return alaw_to_ulaw[alawbyte];
}
static Word read_word (FILE *in)
{
Word w;
w = getc(in);
w = (w << 8) | getc(in);
w = (w << 8) | getc(in);
w = (w << 8) | getc(in);
return(w);
}
static void write_word (Word w, FILE *out)
{
putc((w & 0xff000000) >> 24, out);
putc((w & 0x00ff0000) >> 16, out);
putc((w & 0x0000ff00) >> 8, out);
putc((w & 0x000000ff), out);
}
int pvftoau (FILE *fd_in, FILE *fd_out, pvf_header *header_in,
int dataFormat)
{
SNDSoundStruct hdr;
int sample;
hdr.magic = SND_MAGIC;
hdr.dataLocation = SND_HEADER_SIZE;
hdr.dataSize = SND_UNKNOWN_SIZE;
hdr.dataFormat = dataFormat;
hdr.samplingRate = header_in->speed;
hdr.channelCount = 1;
hdr.info = 0;
write_word(hdr.magic, fd_out);
write_word(hdr.dataLocation, fd_out);
write_word(hdr.dataSize, fd_out);
write_word(hdr.dataFormat, fd_out);
write_word(hdr.samplingRate, fd_out);
write_word(hdr.channelCount, fd_out);
write_word(hdr.info, fd_out);
switch (hdr.dataFormat)
{
case SND_FORMAT_MULAW_8:
while (1)
{
sample = header_in->read_pvf_data(fd_in) >> 8;
if (feof(fd_in))
break;
if (sample > 0x7fff)
sample = 0x7fff;
if (sample < -0x8000)
sample = -0x8000;
putc(linear2ulaw(sample) & 0xff, fd_out);
}
break;
case SND_FORMAT_ALAW_8:
while (1)
{
sample = header_in->read_pvf_data(fd_in) >> 8;
if (feof(fd_in))
break;
if (sample > 0x7fff)
sample = 0x7fff;
if (sample < -0x8000)
sample = -0x8000;
putc(ulaw2alaw(linear2ulaw(sample) & 0xff), fd_out);
}
break;
case SND_FORMAT_LINEAR_8:
while (1)
{
sample = header_in->read_pvf_data(fd_in) >> 16;
if (feof(fd_in))
break;
if (sample > 0x7f)
sample = 0x7f;
if (sample < -0x80)
sample = -0x80;
putc(sample & 0xff, fd_out);
}
break;
case SND_FORMAT_LINEAR_16:
while (1)
{
sample = header_in->read_pvf_data(fd_in) >> 8;
if (feof(fd_in))
break;
if (sample > 0x7fff)
sample = 0x7fff;
if (sample < -0x8000)
sample = -0x8000;
putc((sample >> 8) & 0xff, fd_out);
putc(sample & 0xff, fd_out);
}
break;
default:
fprintf(stderr, "%s: unsupported sound file format requested",
program_name);
return(ERROR);
}
return(OK);
}
int autopvf (FILE *fd_in, FILE *fd_out, pvf_header *header_out)
{
SNDSoundStruct hdr;
int i;
int sample;
hdr.magic = read_word(fd_in);
hdr.dataLocation = read_word(fd_in);
hdr.dataSize = read_word(fd_in);
hdr.dataFormat = read_word(fd_in);
hdr.samplingRate = read_word(fd_in);
hdr.channelCount = read_word(fd_in);
/* hdr.info = read_word(fd_in); */ /* this is sometimes missing */
if (hdr.magic != SND_MAGIC)
{
fprintf(stderr, "%s: illegal magic number for an .au file",
program_name);
return(ERROR);
}
#ifdef PRINT_INFO
{
Word fmt=hdr.dataFormat;
if ((hdr.dataFormat >= 0) && (hdr.dataFormat < (sizeof(sound_format) /
sizeof(sound_format[0]))))
printf("%s: Data format: %s\n", program_name,
sound_format[hdr.dataFormat]);
else
printf("%s: Data format unknown, code=%ld\n", prgoram_name,
(long) hdr.dataFormat);
fprintf(stderr, "Sampling rate: %ld\n", (long) hdr.samplingRate);
fprintf(stderr, "Number of channels: %ld\n", (long) hdr.channelCount);
fprintf(stderr, "Data location: %ld\n", (long) hdr.dataLocation);
fprintf(stderr, "Data size: %ld\n", (long) hdr.dataSize);
#endif
if (hdr.channelCount != 1)
{
fprintf(stderr, "%s: number of channels (%ld) is not 1\n",
program_name, hdr.channelCount);
return(ERROR);
}
header_out->speed = hdr.samplingRate;
if (write_pvf_header(fd_out, header_out) != OK)
{
fprintf(stderr, "%s: could not write pvf header\n",
program_name);
return(ERROR);
};
for (i = ftell(fd_in); i < hdr.dataLocation; i++)
if (getc(fd_in) == EOF)
{
fprintf(stderr, "%s: unexpected end of file\n",
program_name);
return(ERROR);
}
switch (hdr.dataFormat)
{
case SND_FORMAT_MULAW_8:
while ((sample = getc(fd_in)) != EOF)
header_out->write_pvf_data(fd_out,
ulaw2linear(sample) << 8);
break;
case SND_FORMAT_ALAW_8:
while ((sample = getc(fd_in)) != EOF)
header_out->write_pvf_data(fd_out,
ulaw2linear(alaw2ulaw(sample)) << 8);
break;
case SND_FORMAT_LINEAR_8:
while ((sample = getc(fd_in)) != EOF)
{
sample &= 0xff;
if (sample > 0x7f)
sample -= 0x100;
header_out->write_pvf_data(fd_out, (sample << 16));
}
break;
case SND_FORMAT_LINEAR_16:
while ((sample = getc(fd_in)) != EOF)
{
sample &= 0xffff;
if (sample > 0x7fff)
sample -= 0x10000;
header_out->write_pvf_data(fd_out, (sample << 8));
}
break;
default:
fprintf(stderr, "%s: unsupported or illegal sound encoding\n",
program_name);
return(ERROR);
}
return(OK);
}
int pvftoulaw(FILE *fd_in, FILE *fd_out, pvf_header *header_in)
{
int sample;
if (header_in->speed != 8000)
{
fprintf(stderr, "%s: sample speed (%d) must be 8000\n",
program_name, header_in->speed);
return(ERROR);
};
while (1)
{
sample = header_in->read_pvf_data(fd_in) >> 8;
if (feof(fd_in))
break;
putc(linear2ulaw(sample), fd_out);
}
return(OK);
}
int ulawtopvf(FILE *fd_in, FILE *fd_out, pvf_header *header_out)
{
int sample;
if (header_out->speed != 8000)
{
fprintf(stderr, "%s: sample speed (%d) must be 8000\n",
program_name, header_out->speed);
return(ERROR);
};
while ((sample = getc(fd_in)) != EOF)
header_out->write_pvf_data(fd_out, ulaw2linear(sample) << 8);
return(OK);
}
int pvftoalaw(FILE *fd_in, FILE *fd_out, pvf_header *header_in)
{
int sample;
if (header_in->speed != 8000)
{
fprintf(stderr, "%s: sample speed (%d) must be 8000\n",
program_name, header_in->speed);
return(ERROR);
};
while (1)
{
sample = header_in->read_pvf_data(fd_in) >> 8;
if (feof(fd_in))
break;
putc(ulaw2alaw(linear2ulaw(sample)), fd_out);
}
return(OK);
}
int alawtopvf(FILE *fd_in, FILE *fd_out, pvf_header *header_out)
{
int sample;
if (header_out->speed != 8000)
{
fprintf(stderr, "%s: sample speed (%d) must be 8000\n",
program_name, header_out->speed);
return(ERROR);
};
while ((sample = getc(fd_in)) != EOF)
header_out->write_pvf_data(fd_out, ulaw2linear(alaw2ulaw(sample)) << 8);
return(OK);
}

204
lib/libpvf/fft.c Normal file
View File

@ -0,0 +1,204 @@
/*
* fft.c
*
* Original code by ulrich@Gaston.westfalen.de (Heinz Ulrich Stille).
*
* $Id: fft.c,v 1.5 1999/03/16 09:59:19 marcs Exp $
*
*/
#include "../include/voice.h"
static void fft (float *real, float *imag, int n)
{
float a;
float tr;
float ti;
float wr;
float wi;
int mr = 0;
int l;
int istep;
int m;
int i;
int j;
for (m = 1; m <= (n - 1); m++)
{
l = n / 2;
while ((mr + l) > (n - 1))
l /= 2;
mr = (mr % l) + l;
if (mr > m)
{
tr = real[m];
real[m] = real[mr];
real[mr] = tr;
ti = imag[m];
imag[m] = imag[mr];
imag[mr] = ti;
}
}
for (l = 1; l < n; l = istep)
{
istep = 2 * l;
for (m = 1; m <= l; m++)
{
a = -M_PI * (m - 1.0) / l;
wr = cos(a);
wi = sin(a);
i = m - 1;
do
{
j = i + l;
tr = wr * real[j] - wi * imag[j];
ti = wr * imag[j] + wi * real[j];
real[j] = real[i] - tr;
imag[j] = imag[i] - ti;
real[i] += tr;
imag[i] += ti;
i += istep;
}
while (i < n);
}
}
}
int pvffft (FILE *fd_in, pvf_header *header_in, int skip, int sample_size,
double threshold, int vgetty_pid, int display)
{
/*
* Read pvf data, transform it into amplitude data and
* compute an index for the distribution of energy over the
* frequencies.
*/
int i;
int data;
float val;
float sum = 0.0;
float max = 0.0;
float *real;
float *imag;
for (i = sample_size; i > 0; i >>= 1)
{
if (((i & 0x01) != 0) && (i != 1))
{
fprintf(stderr, "%s: sample size (%d) must be a power of 2\n",
program_name, sample_size);
return(ERROR);
}
}
real = malloc((sample_size + 8) * sizeof(float));
imag = malloc((sample_size + 8) * sizeof(float));
if ((real == NULL) || (imag == NULL))
{
fprintf(stderr, "%s: not enough memory\n", program_name);
return(ERROR);
}
for(i = 0; i < sample_size; i++)
{
real[i] = 0;
imag[i] = 0;
}
/*
* skip the first few seconds
*/
for (i = 0; i < skip; i++)
data = header_in->read_pvf_data(fd_in);
/*
* store a few seconds' worth of data
*/
for (i = 0; i < sample_size; i++)
{
real[i] = (float) header_in->read_pvf_data(fd_in);
if (feof(fd_in))
{
/*
* assume it isn't data
*/
fprintf(stderr, "%s: not enough samples available\n",
program_name);
return(ERROR);
}
}
/*
* tell the calling process that it can stop writing
*/
if (vgetty_pid > 0)
kill(vgetty_pid, SIGPIPE);
/*
* note that it will get another sigpipe once this is
* really finished - a close won't do since other processes
* (i.e. the calling shell) will still have the fd opened.
*/
fft(real, imag, sample_size);
for (i = 10; i < sample_size; i++)
{
val = sqrt(real[i] * real[i] + imag[i] * imag[i]);
sum += val;
if (val > max)
max = val;
}
sum /= sample_size;
if (max > 1e-10)
sum /= max;
if (vgetty_pid > 0)
{
if (sum < threshold)
kill(vgetty_pid, SIGUSR2);
return(OK);
}
if (display)
{
for (i = 10; i < (sample_size >> 1); i++)
printf("%f %f\n", ((double) i / sample_size) *
header_in->speed, sqrt(real[i] * real[i] +
imag[i] * imag[i]) / max);
return(OK);
};
printf("%s: FFT level is %g\n", program_name, sum);
return(OK);
}

430
lib/libpvf/lib.c Normal file
View File

@ -0,0 +1,430 @@
/*
* lib.c
*
* Contains some basic functions for reading and writing files
*
* $Id: lib.c,v 1.4 1998/09/09 21:07:00 gert Exp $
*
*/
#include "../include/voice.h"
rmd_header init_rmd_header = {"RMD1", "", 0x0000, 0, 0, {0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00}};
state_t init_state = {0x0000, 0};
int bitmask[17] = {0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f,
0x007f, 0x00ff, 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff,
0xffff};
int read_bits (FILE *fd_in, state_t *state, int nbits)
{
static int data_new;
while(state->nleft < nbits)
{
if ((data_new = getc(fd_in)) == EOF)
return(EOF);
state->data = (state->data << 8) | data_new;
state->nleft += 8;
}
state->nleft -= nbits;
return(state->data >> state->nleft) & bitmask[nbits];
}
void write_bits (FILE *fd_out, state_t *state, int nbits, int data)
{
state->data = (state->data << nbits) | (data & bitmask[nbits]);
state->nleft += nbits;
while (state->nleft >= 8)
{
putc((state->data >> (state->nleft - 8)) & 0xff, fd_out);
state->nleft -= 8;
}
}
int read_bits_reverse (FILE *fd_in, state_t *state, int nbits)
{
static int data;
static int data_new;
while(state->nleft < nbits)
{
if ((data_new = getc(fd_in)) != EOF)
return(EOF);
state->data |= (data_new << state->nleft);
state->nleft += 8;
}
data = state->data & bitmask[nbits];
state->nleft -= nbits;
state->data >>= nbits;
return(data);
}
void write_bits_reverse (FILE *fd_out, state_t *state, int nbits, int data)
{
state->data |= ((data & bitmask[nbits]) << state->nleft);
state->nleft += nbits;
while(state->nleft >= 8)
{
putc(state->data & 0xff, fd_out);
state->nleft -= 8;
state->data >>= 8;
}
}
int read_rmd_header(FILE *fd_in, rmd_header *header_in)
{
*header_in = init_rmd_header;
if (fread(header_in, sizeof(rmd_header), 1, fd_in) != 1)
{
fprintf(stderr, "%s: Could not read rmd header\n", program_name);
return(FAIL);
};
if (strncmp(header_in->magic, "RMD1", 4) != 0)
{
fprintf(stderr, "%s: No rmd (raw modem data) header found\n",
program_name);
return(FAIL);
};
return(OK);
}
int write_rmd_header(FILE *fd_out, rmd_header *header_out)
{
if (fwrite(header_out, sizeof(rmd_header), 1, fd_out) != 1)
{
fprintf(stderr, "%s: Could not write rmd header\n", program_name);
return(FAIL);
};
return(OK);
}
static int read_pvf_data_8_ascii(FILE *fd_in)
{
static int data_new;
if (fscanf(fd_in, "%d", &data_new) != 1)
return(EOF);
if (data_new < -0x80)
data_new = -0x80;
if (data_new > 0x7f)
data_new = 0x7f;
return(data_new << 16);
}
static int read_pvf_data_16_ascii(FILE *fd_in)
{
static int data_new;
if (fscanf(fd_in, "%d", &data_new) != 1)
return(EOF);
if (data_new < -0x8000)
data_new = -0x8000;
if (data_new > 0x7fff)
data_new = 0x7fff;
return(data_new << 8);
}
static int read_pvf_data_32_ascii(FILE *fd_in)
{
static int data_new;
if (fscanf(fd_in, "%d", &data_new) != 1)
return(EOF);
return(data_new);
}
static int read_pvf_data_8(FILE *fd_in)
{
static signed char data_new;
if (fread(&data_new, 1, 1, fd_in) != 1)
return(EOF);
return(data_new << 16);
}
static int read_pvf_data_16(FILE *fd_in)
{
static signed short data_new;
if (fread(&data_new, 2, 1, fd_in) != 1)
return(EOF);
data_new = ntohs(data_new);
return(data_new << 8);
}
static int read_pvf_data_32(FILE *fd_in)
{
static int data_new;
if (fread(&data_new, 4, 1, fd_in) != 1)
return(EOF);
return(ntohl(data_new));
}
static void write_pvf_data_8_ascii(FILE *fd_out, int data)
{
data >>= 16;
if (data > 0x7f)
data = 0x7f;
if (data < -0x80)
data = -0x80;
fprintf(fd_out, "%d\n", data);
}
static void write_pvf_data_16_ascii(FILE *fd_out, int data)
{
data >>= 8;
if (data > 0x7fff)
data = 0x7fff;
if (data < -0x8000)
data = -0x8000;
fprintf(fd_out, "%d\n", data);
}
static void write_pvf_data_32_ascii(FILE *fd_out, int data)
{
fprintf(fd_out, "%d\n", data);
}
static void write_pvf_data_8(FILE *fd_out, int data)
{
data >>= 16;
if (data > 0x7f)
data = 0x7f;
if (data < -0x80)
data = -0x80;
putc(data, fd_out);
}
static void write_pvf_data_16(FILE *fd_out, int data)
{
static signed short out;
data >>= 8;
if (data > 0x7fff)
data = 0x7fff;
if (data < -0x8000)
data = -0x8000;
out = htons((short) data);
fwrite(&out, 2, 1, fd_out);
}
static void write_pvf_data_32(FILE *fd_out, int data)
{
static signed int out;
out = htonl((long) data);
fwrite(&out, 4, 1, fd_out);
}
int read_pvf_header(FILE *fd_in, pvf_header *header_in)
{
char buffer[VOICE_BUF_LEN];
int i;
*header_in = init_pvf_header;
if (fread(&buffer, 5, 1, fd_in) != 1)
{
fprintf(stderr, "%s: Could not read pvf header\n", program_name);
return(FAIL);
};
if (strncmp(buffer, "PVF1\n", 5) == 0)
header_in->ascii = FALSE;
else if (strncmp(buffer, "PVF2\n", 5) == 0)
header_in->ascii = TRUE;
else
{
fprintf(stderr, "%s: No pvf (portable voice format) header found\n",
program_name);
return(FAIL);
};
for (i = 0; i < VOICE_BUF_LEN; i++)
{
if (fread(&buffer[i], 1, 1, fd_in) != 1)
{
fprintf(stderr, "%s: Could not read pvf header\n", program_name);
return(FAIL);
};
if (buffer[i] == '\n')
break;
};
buffer[i] = 0x00;
sscanf(buffer, "%d %d %d", &header_in->channels, &header_in->speed,
&header_in->nbits);
if ((header_in->channels < 1) || (32 < header_in->channels))
{
fprintf(stderr, "%s: Invalid number of channels (%d)\n",
program_name, header_in->channels);
return(FAIL);
};
if ((header_in->speed < 0) || (50000 < header_in->speed))
{
fprintf(stderr, "%s: Invalid sample speed (%d)\n", program_name,
header_in->speed);
return(FAIL);
};
if ((header_in->nbits != 8) && (header_in->nbits != 16) &&
(header_in->nbits != 32))
{
fprintf(stderr, "%s: Invalid number of bits (%d) per sample\n",
program_name, header_in->nbits);
return(FAIL);
};
if (header_in->ascii)
{
switch (header_in->nbits)
{
case 8:
header_in->read_pvf_data = &read_pvf_data_8_ascii;
break;
case 16:
header_in->read_pvf_data = &read_pvf_data_16_ascii;
break;
case 32:
header_in->read_pvf_data = &read_pvf_data_32_ascii;
break;
default:
fprintf(stderr, "%s: Illegal bit size for pvf input\n",
program_name);
return(FAIL);
};
}
else
{
switch (header_in->nbits)
{
case 8:
header_in->read_pvf_data = &read_pvf_data_8;
break;
case 16:
header_in->read_pvf_data = &read_pvf_data_16;
break;
case 32:
header_in->read_pvf_data = &read_pvf_data_32;
break;
default:
fprintf(stderr, "%s: Illegal bit size for pvf input\n",
program_name);
return(FAIL);
};
};
return(OK);
}
int write_pvf_header(FILE *fd_out, pvf_header *header_out)
{
char buffer[VOICE_BUF_LEN];
if (header_out->ascii)
{
sprintf(buffer, "PVF2\n%d %d %d\n", header_out->channels,
header_out->speed, header_out->nbits);
switch (header_out->nbits)
{
case 8:
header_out->write_pvf_data = &write_pvf_data_8_ascii;
break;
case 16:
header_out->write_pvf_data = &write_pvf_data_16_ascii;
break;
case 32:
header_out->write_pvf_data = &write_pvf_data_32_ascii;
break;
default:
fprintf(stderr, "%s: Illegal bit size for pvf output\n",
program_name);
return(FAIL);
};
}
else
{
sprintf(buffer, "PVF1\n%d %d %d\n", header_out->channels,
header_out->speed, header_out->nbits);
switch (header_out->nbits)
{
case 8:
header_out->write_pvf_data = &write_pvf_data_8;
break;
case 16:
header_out->write_pvf_data = &write_pvf_data_16;
break;
case 32:
header_out->write_pvf_data = &write_pvf_data_32;
break;
default:
fprintf(stderr, "%s: Illegal bit size for pvf output\n",
program_name);
return(FAIL);
};
};
if (fwrite(&buffer, strlen(buffer), 1, fd_out) != 1)
{
fprintf(stderr, "%s: Could not write pvf header\n", program_name);
return(FAIL);
};
return(OK);
}
pvf_header init_pvf_header = {FALSE, 1, 8000, 32, &read_pvf_data_32,
&write_pvf_data_32};

82
lib/libpvf/linear.c Normal file
View File

@ -0,0 +1,82 @@
/*
* linear.c
*
* Converts pvf <--> linear.
*
* $Id: linear.c,v 1.5 1999/03/16 09:59:20 marcs Exp $
*
*/
#include "../include/voice.h"
int pvftolin (FILE *fd_in, FILE *fd_out, pvf_header *header_in, int is_signed,
int bits16, int intel)
{
int data;
while (1)
{
data = header_in->read_pvf_data(fd_in) >> 8;
if (feof(fd_in))
break;
if (data > 0x7fff)
data = 0x7fff;
if (data < -0x8000)
data = -0x8000;
if (!is_signed)
data += 0x8000;
if (bits16 && intel)
putc(data & 0xff, fd_out);
putc((data >> 8), fd_out);
if (bits16 && !intel)
putc(data & 0xff, fd_out);
}
return(OK);
}
int lintopvf (FILE *fd_in, FILE *fd_out, pvf_header *header_out,
int is_signed, int bits16, int intel)
{
int data;
while ((data = getc(fd_in)) != EOF)
{
if (bits16)
{
if (intel)
data |= (getc(fd_in) << 8);
else
{
data <<= 8;
data |= (getc(fd_in));
}
}
else
data = (data << 8);
if (is_signed)
{
if (data > 0x7fff)
data -= 0x10000;
}
else
data -= 0x8000;
header_out->write_pvf_data(fd_out, ((data) << 8));
}
return(OK);
}

313
lib/libpvf/multitech.c Normal file
View File

@ -0,0 +1,313 @@
/*
* multitech.c
*
* Covert pvf <-> IMA ADPCM
*
* Modified for use with vgetty, Russell King (rmk@ecs.soton.ac.uk)
*
* $Id: multitech.c,v 1.3 1998/09/09 21:07:02 gert Exp $
*
*/
#include "../include/voice.h"
/***********************************************************
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.
*/
#include "../include/adpcm.h"
#include <stdio.h> /*DBG*/
#ifndef __STDC__
#define signed
#endif
/* 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
};
static void
adpcm_coder(indata, outdata, len, state)
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 = 0; /* 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;
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 & 0x0f;
} else {
*outp++ = ((delta << 4) & 0xf0) | outputbuffer;
}
bufferstep = !bufferstep;
}
/* Output last step, if needed */
if ( !bufferstep )
*outp++ = outputbuffer;
state->valprev = valpred;
state->index = index;
}
static void
adpcm_decoder(indata, outdata, len, state)
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 = 0; /* 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;
for ( ; len > 0 ; len-- ) {
/* Step 1 - get the delta value */
if ( bufferstep ) {
delta = (inputbuffer >> 4) & 0xf;
} else {
inputbuffer = *inp++;
delta = inputbuffer & 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;
if ( delta & 4 ) vpdiff += step << 3;
if ( delta & 2 ) vpdiff += step << 2;
if ( delta & 1 ) vpdiff += step << 1;
if ( sign )
valpred -= vpdiff >> 3;
else
valpred += vpdiff >> 3;
/* 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;
}
#define SAMPLES 512
static short sbuffer[SAMPLES*2];
static char cbuffer[SAMPLES/2];
static struct adpcm_state state;
int
pvftoimaadpcm (FILE *fd_in, FILE *fd_out, pvf_header *header_in)
{
int i/*, j*/;
while (!feof (fd_in)) {
for (i = 0; !feof (fd_in) && i < SAMPLES; i++)
sbuffer[i] = header_in->read_pvf_data(fd_in) >> 8;
if (ferror (fd_in))
break;
adpcm_coder (sbuffer, cbuffer, i, &state);
if (fwrite (cbuffer, 1, i / 2, fd_out) != i /2)
return ERROR;
}
if (ferror (fd_in))
return ERROR;
return OK;
}
int
imaadpcmtopvf (FILE *fd_in, FILE *fd_out, pvf_header *header_out)
{
int i, o/*, j*/;
while (!feof (fd_in)) {
i = fread (cbuffer, 1, SAMPLES / 2, fd_in);
adpcm_decoder (cbuffer, sbuffer, i * 2, &state);
for (o = 0; o < i * 2; o++)
header_out->write_pvf_data(fd_out, sbuffer[o] << 8);
if (ferror (fd_in))
break;
}
if (ferror (fd_in))
return ERROR;
return OK;
}

766
lib/libpvf/rockwell.c Normal file
View File

@ -0,0 +1,766 @@
/*
*
* rockwell.c
*
* Conversion subroutines for:
*
* signed linear 16 bit audio words <--> Rockwell 2, 3, or 4 bit ADPCM
*
* -----------------------------------------------------------------------
*
* Floating point version by
* Torsten Duwe <duwe@informatik.uni-erlangen.de>
*
* very remotely derived from Rockwell's d.asm.
* Converted to C and simplified by Torsten Duwe 1995
*
* -----------------------------------------------------------------------
*
* Floating point version was removed on Fri Jan 17 13:23:42 1997
*
* -----------------------------------------------------------------------
*
* Fixed point version
* by Peter Jaeckel <atmpj@ermine.ox.ac.uk> [1996-1997].
*
*
* Marc Eberhard dubbed this file the "Rocky Horror Picture Show".
* And he is right.
* PJ, Tue Jan 14 17:04:13 1997
*
*
* The fixed point version is a complete rewrite, I reused only some
* tiny fragments of the floating point code.
*
* The fixed point version actually manages to get output that is
* identical to that of the DOS executables distributed by Rockwell. It
* is noticeably better than that of the floating point version.
* Rockwell uses all sorts of dirty tricks in their fixed point code
* which I don't necessarily understand as I am not an expert on
* compression formats. Read the comments throughout the code for
* further explanation.
*
* Technical Note : The compressor does not do any silence deletion
* which is exactly what the Rockwell sources do as well. The
* decompressor detects silence codewords and inserts the required
* silence period if RV_HONOUR_SILENCE_CODEWORDS is defined. Otherwise,
* the silence codeword and the following integer word determining the
* length of the silence are just gobbled up as compressed data and may
* lead to some temporarily increased noise level after that.
*
* The Rockwell sources were taken from http://www.nb.rockwell.com/ref/adpcm.html .
*
* Peter Jaeckel, Fri Jan 17 14:40:32 1997
*
* -----------------------------------------------------------------------
*
* $Id: rockwell.c,v 1.6 1999/03/16 09:59:20 marcs Exp $
*
*/
#define RV_HONOUR_SILENCE_CODEWORDS
#include "../include/voice.h"
/*
PJ:
A first attempt to implement basically all that the original
Rockwell D.ASM code does in C. I never had to deal with assembler
before, so be lenient when you judge me, please...
NB: The guts of this code are not very pretty to look at.
RV_ stands for Rockwell Voice.
*/
#define RV_PNT98 32113 /* 0.98 */
#define RV_PNT012 393 /* 0.012 */
#define RV_PNT006 197 /* 0.006 */
#define RV_QDLMN 0x1F /* QDLMN = IQDLMN = 2.87 mV. */
#define RV_DEMPCF 0x3333 /* 0.4 */
#define RV_PDEMPCF 0x3333 /* 0.4 */
#define RV_QORDER 8 /* Total delay line length for the pole and zero delay lines */
/*
Design Notes: Relation of QDataIndex to position in QData Buffer.
the rotation is done by the QDataIndex. This variable always
points to the data to be multiplied by the coefficient a1. The
coefficients, a1..a2 and b1..b6, stay in the same relative
position in the coefficient array. Updates to these values are
done in place. Illustration belows shows the value of QDataIndex
and the Delay Line data in relation to the coefficient array.
Position
in Qdata Start 2nd 3rd 4th
-----------------------------------------
0 a1 Y(n-1) Y(n-2) Q(n-1) Q(n-2)
1 a2 Y(n-2) Q(n-1) Q(n-2) Q(n-3)
2 b1 Q(n-1) Q(n-2) Q(n-3) Q(n-4)
3 b2 Q(n-2) Q(n-3) Q(n-4) Q(n-5)
4 b3 Q(n-3) Q(n-4) Q(n-5) Q(n-6)
5 b4 Q(n-4) Q(n-5) Q(n-6) Y(n-1)
6 b5 Q(n-5) Q(n-6) Y(n-1) Y(n-2)
7 b6 Q(n-6) Y(n-1) Y(n-2) Q(n-1)
-----------------------------------------
QDataIndex 0 7 6 5
-----------------------------------------
*/
static vgetty_s_int16 RV_pzTable[8]; /* Coefficient Table for the pole and zero linear predictor. */
/* a1 a2 b1 b2 b3 b4 b5 b6 */
static vgetty_s_int16 RV_QdataIndex = 0; /* Delay line pointer to the coefficient a1. */
static vgetty_s_int16 RV_Qdata[RV_QORDER]; /* Delay line. */
#ifdef POSTFILTER /* DON'T USE THIS */
/*
The POSTFILTER code is in Rockwell's original D.ASM, too.
They too, don't use it in their distributed executables
though. I have no idea under what circumstances it might be
useful, I just left the code in here as I went through the
effort of writing it before I realised that it appears to be
useless here.
*/
static vgetty_s_int16 RV_QPdata[RV_QORDER];
static vgetty_s_int16 RV_QPPdata[RV_QORDER];
#endif
static vgetty_s_int16 RV_LastNu = 0; /* Last Nu value. */
static vgetty_s_int16 RV_Dempz = 0; /* De-emphasis filter delay line (one element). */
static vgetty_s_int16 RV_NewQdata = 0; /* Adaptive quantizer output. */
static vgetty_s_int16 RV_NewAppData = 0; /* Temporay data storage */
/* ML2bps, ML3bps, and ML4bps are combined in mul[][], just like Torsten suggested */
static vgetty_s_int16 RV_mul[3][16] =
{ /* Multiplier table to calculate new Nu for 2/3/4 BPS. */
{0x3333, 0x199A, 0x199A, 0x3333},
{0x3800, 0x2800, 0x1CCD, 0x1CCD, 0x1CCD, 0x1CCD, 0x2800, 0x3800},
{0x4CCD, 0x4000, 0x3333, 0x2666, 0x1CCD, 0x1CCD, 0x1CCD, 0x1CCD, 0x1CCD, 0x1CCD, 0x1CCD, 0x1CCD, 0x2666, 0x3333, 0x4000, 0x4CCD}
};
/* Zeta2bps, Zeta3bps, and Zeta4bps are combined in Zeta[][],
just like Torsten suggested */
static vgetty_u_int16 RV_Zeta[3][16] =
{ /* Multiplier table for 2/3/4 BPS to calculate inverse */
/* quantizer output. This number, index by the code */
/* word, times Nu is the inverse quantizer output. */
{0xCFAE, 0xF183, 0x0E7D, 0x3052},
{0xBB23, 0xD4FE, 0xE7CF, 0xF828, 0x07D8, 0x1831, 0x2B02, 0x44DD},
{0xA88B, 0xBDCB, 0xCC29, 0xD7CF, 0xE1D8, 0xEAFB, 0xF395, 0xFBE4, 0x041C, 0x0C6B, 0x1505, 0x1E28, 0x2831, 0x33C7, 0x4235, 0x5775}
};
static vgetty_s_int16 *RV_mul_p;
static vgetty_s_int16 *RV_Zeta_p;
static vgetty_u_int16 RV_silence_words[3] = {0x13ec, 0x23de, 0xc11c};
static vgetty_u_int16 RV_silence_word;
/* Maximum limit for Nu. Changes based on 2, 3, or 4 BPS selected.
Initialization routine updates this value. */
static vgetty_s_int16 RV_QDelayMX = 0;
/* Array index by BPS-2 for updating QDelayMX */
static vgetty_s_int16 RV_QDelayTable[3] = {0x54C4,0x3B7A,0x2ED5}; /* 2.01V, 1.41V, 1.11V */
/*
Macro definitions used in the decompression, interpolation, and
compression functions.
*/
static vgetty_s_int32 RV_max_local_int16,RV_min_local_int16;
static vgetty_s_int64 RV_max_local_int32,RV_min_local_int32;
#define RV_clip_16(a) ((a)<RV_min_local_int16?RV_min_local_int16:(a)>RV_max_local_int16?RV_max_local_int16:(a))
#define RV_clip_32(a) ((a)<RV_min_local_int32?RV_min_local_int32:(a)>RV_max_local_int32?RV_max_local_int32:(a))
#define HIWORD(x) (((vgetty_u_int32)x) >> 16)
#define LOWORD(x) ((vgetty_u_int32)(((unsigned int)x) & 0xffff))
#define LOBYTE(x) ((unsigned int)(((unsigned int)x) & 0xff))
#define RV_round_32_into_16(x) (vgetty_s_int16)((((vgetty_u_int32)x)>>16)+((((vgetty_u_int32)x)>>15)&0x0001))
/* In order to stay as close as possible to the original assembler
(a kludge, I know), we simulate the system's register(s) below */
static vgetty_s_int16 RV_di;
/* Utilities.
Routines that both the decompressor and the compressor use. */
/*
pzPred
Linear predictor coefficient update routine. Local to this module.
Inputs:
CX = Q(n), i.e. WORD PTR NewQData
Output: DI points to (QDataIndex+7)%8.
*/
static void RV_pzPred(vgetty_s_int16 cx){
/*
A little explanation is required here. Rockwell uses 16bit
integers to represent numbers in [-1,1). They take 0x8000 to be -1
and 0x7fff to be 0.999969482, i.e. the ints -32768 to 32767 are
normalised over 32768 into the required interval. The product of
two such numbers is supposed to be, again, in the range [-1,1).
I know that this is mathematically incorrect, but that's how they
do it, just read on.
The "adjustment" that is mentioned in D.ASM can be understood by
the following example: Assume you want to multiply -1 with -0.5. In
integers, that's imul 0x8000, 0xc000 (I know, it does actulally
require a register), i.e. -32768*-16384. The result is 0x20000000.
They only want to keep a 16 bit result, thus they need to round.
First, however, an adjustment for the moved decimal point is
required. The upper 16 bit of 0x20000000 are 0x2000 which
corresponds only to 8192/32768=0.25 ! Thus, all the bits need to be
left shifted by one place and the result will be 0x4000 which
correctly corresponds to 0.5 now. This confusion is due to the fact
that the original two numbers in total have two bits representing
two different signs and the result, which is again represented by a
total of 32 bits, needs only one sign bit. Thus, the result in 32
bits has effectively one more data bit available than the total of
the two multiplicands. The nature of integer arithmetics feeds that
bit in from the left behind the sign bit. A consequence of this is
that the two leftmost bits of the resulting 32 bit integer are
always equal, apart from one case, namely 0x8000*0x8000, or
-32768*-32768, which results in 0x40000000. Arithmetically, we
would expect this to be decimal-point-adjusted to the 16 bit
representation 0x7fff. The Rockwell assembler code, however, just
maps this to 0x8000, i.e. -1, by ignoring the special case of
0x8000*0x8000. This explains the cryptic warnings like
; Determine sign of Q(n) * Y(n-1)
;
; Do not change the sign determination method!
in D.ASM. Personally, this is the first time ever I have seen
anyone using arithmetics like -1 * -1 = -1 whilst -1 * -0.99 = 0.99 ;-).
So, after this type of decimal-point-adjustment they then round off
the lower 16 bit and just take the upper 16 to be the result.
*/
static vgetty_s_int32 x,y; /* local accumulator(s) */
static vgetty_s_int16 di;
static int i;
di = RV_QdataIndex;
/* Calculate coefficients a1 a2 b1 b2 b3 b4 b5 b6 . */
for (i = 0; i < 8; i++)
{
x = RV_pzTable[i] * ((vgetty_s_int32) RV_PNT98);
x <<= 1; /*
Rockwell-adjust for decimal point shift, then round off
lower 16 bits to obtain a 16 bit representation of the
result.
*/
x = RV_round_32_into_16 (x);
/* cx contains the NewQdata=Q(n) */
y = ((vgetty_s_int32) cx) * ((vgetty_s_int32) RV_Qdata[di]);
y <<= 1; /* Rockwell-adjust for decimal point shift. */
y = RV_round_32_into_16 (y);
x += (y < 0 ? -1 : 1) * (i < 2 ? RV_PNT012 : RV_PNT006);
/* i<2 ? The a's get RV_PNT012. All b's get RV_PNT006. */
/*
The result of a multiplication needs adjusting & rounding.
The result of an addition/subtraction needs clipping.
*/
RV_pzTable[i] = RV_clip_16 (x);
di++;
di %= 8;
}
}
/*
Taken from D.ASM:
Design Notes: Sum of Multiplications.
Multiplications are 16-bit signed numbers producing a signed 32-bit
result. The two operands are usually numbers less than one; this
requires a 32-bit shift by the macro "adjust" to bring the decimal
point in line. The 32-bit addition is two 16-bit additions with
carry. The "clip" macro checks for overflow and limits the result of
the addition to 0x7fffffff or 0x80000000 (for 32-bit results), or
0x7fff or 0x8000 (for 16-bit results). Note that the "clip" macro
depends on the flags being set because of an addition; the 80x86
processor does not update these flags because of a move operation.
*/
static vgetty_s_int32 RV_XpzCalc(vgetty_s_int16 cx){
/*
Linear pole and zero predictor calculate. CX,BX register pair is the
32 bit accumulator. Local to this module.
Input: CX = Initial Value. BX set to zero.
Output: CX,BX contains the result of the sum of products.
Also, DI points to (QDataIndex+7)%8.
*/
static vgetty_s_int32 x; /* local accumulator */
static vgetty_s_int64 sum;
int i;
RV_di = RV_QdataIndex;
sum = ((vgetty_s_int32) cx) << 16;
for (i = 0; i < 8; i++)
{
x = ((vgetty_s_int32) RV_pzTable[i]) * ((vgetty_s_int32) RV_Qdata[RV_di]);
x <<= 1; /* Rockwell-adjust for decimal point shift. */
sum += x;
sum = RV_clip_32 (sum);
RV_di++;
RV_di %= 8;
}
RV_di = (RV_QdataIndex + 7) % 8;
return (vgetty_s_int32) sum;
}
static void RV_Reset(int bps){
int i;
vgetty_u_int16 tmp_int16 = 0;
vgetty_u_int32 tmp_int32 = 0;
tmp_int16 = ~tmp_int16;
tmp_int16 >>= 1;
RV_max_local_int16 = tmp_int16;
RV_min_local_int16 = tmp_int16;
RV_min_local_int16 = -RV_min_local_int16;
RV_min_local_int16--;
tmp_int32 = ~tmp_int32;
tmp_int32 >>= 1;
RV_max_local_int32 = tmp_int32;
RV_min_local_int32 = tmp_int32;
RV_min_local_int32 = -RV_min_local_int32;
RV_min_local_int32--;
RV_QdataIndex = 0;
for (i = 0; i < RV_QORDER; i++)
{
RV_Qdata[i] = 0;
#ifdef POSTFILTER
RV_QPdata[i] = 0;
RV_QPPdata[i] = 0;
#endif
}
RV_Dempz = 0;
RV_NewQdata = 0;
RV_NewAppData = 0;
RV_LastNu = RV_QDLMN;
RV_QDelayMX = RV_QDelayTable[bps-2];
RV_silence_word = RV_silence_words[bps-2];
RV_mul_p = RV_mul[bps-2];
RV_Zeta_p = (vgetty_s_int16 *)RV_Zeta[bps-2];
}
#ifdef POSTFILTER
static vgetty_s_int16 RV_P8Table[6] = /* Adaptive post filter number 1 coefficients */
{0x6666, 0x51eb, 0x4189, 0x346d, 0x29f1, 0x218e};
static vgetty_s_int16 RV_PM5Table[6] = /* Adaptive post filter number 2 coefficients */
{0xc000, 0xe000, 0xf000, 0xf800, 0xfc00, 0xfe00};
static vgetty_s_int32 RV_App1Calc(vgetty_s_int16 cx){
/* Adaptive post filter number 1 */
/*
Load pointers to the predictor table and the pointer
to the coefficient a1.
*/
static vgetty_s_int32 x; /* local accumulator */
static vgetty_s_int64 sum;
static vgetty_s_int16 di;
int i;
di = RV_QdataIndex;
RV_NewAppData = cx;
sum = 0;
for (i = 0; i < 8; i++)
{
x = ((vgetty_s_int32) RV_pzTable[i]) * ((vgetty_s_int32) RV_P8Table[i]);
x <<= 1; /*
Rockwell-adjust for decimal point shift, then round off
lower 16 bits to obtain a 16 bit representation of the
result.
*/
x = ((vgetty_s_int32)
(RV_round_32_into_16 (x))) * ((vgetty_s_int32) RV_QPdata[di]);
x <<= 1; /* Rockwell-adjust for decimal point shift. */
sum += x;
sum = RV_clip_32 (sum);
di++;
di %= 8;
}
cx = HIWORD(sum);
di = (RV_QdataIndex + 7) % 8;
RV_QPdata[di] = cx; /* drop b6, now a1 Qdata */
di += 2;
di %= 8;
RV_QPdata[di] = RV_NewAppData; /* drop a2, now b1 Qdata */
return (vgetty_s_int32) sum;
}
static vgetty_s_int32 RV_App2Calc(vgetty_s_int16 cx){
/* Adaptive post filter number 2 */
/*
Load pointers to the predictor table and the pointer
to the coefficient a1.
*/
static vgetty_s_int32 x; /* local accumulator */
static vgetty_s_int64 sum;
static vgetty_s_int16 di;
int i;
di = RV_QdataIndex;
RV_NewAppData = cx;
sum = 0;
for (i = 0; i < 8; i++)
{
x = ((vgetty_s_int32) RV_pzTable[i]) * ((vgetty_s_int32) RV_PM5Table[i]);
x <<= 1; /*
Rockwell-adjust for decimal point shift, then round off
lower 16 bits to obtain a 16 bit representation of the
result.
*/
x = ((vgetty_s_int32) RV_round_32_into_16 (x)) * ((vgetty_s_int32) RV_QPPdata[di]);
x <<= 1; /* Rockwell-adjust for decimal point shift. */
sum += x;
sum = RV_clip_32 (sum);
di++;
di %= 8;
}
cx = HIWORD(sum);
di = (RV_QdataIndex + 7) % 8;
RV_QPPdata[di] = cx; /* drop b6, now a1 Qdata */
di += 2;
di %= 8;
RV_QPPdata[di] = RV_NewAppData; /* drop a2, now b1 Qdata */
return (vgetty_s_int32) sum;
}
#endif
static vgetty_s_int16 RV_DecomOne(vgetty_s_int16 ax, vgetty_s_int16 bx){
/*
RVDecomOne
Decode a code word. Local to this module.
Inputs:
AX = ML, adaptive multiplier for Nu.
BX = Zeta, base inverse quantizer value, modified by Nu.
Also, updates QdataIndex to (QdataIndex+7)%8 .
*/
static vgetty_s_int16 si;
static vgetty_s_int32 LastNu_bak;
static vgetty_s_int32 x; /* local accumulator */
static vgetty_s_int64 sum;
LastNu_bak = RV_LastNu;
x = ((vgetty_s_int32) ax) * ((vgetty_s_int32) RV_LastNu);
x <<= 1; /* Rockwell-adjust for decimal point shift. */
/* Round and Multiply by 4 */
x = (RV_round_32_into_16 (x) * ((vgetty_s_int32)4));
RV_LastNu = RV_clip_16 (x);
if (RV_LastNu < RV_QDLMN)
RV_LastNu = RV_QDLMN;
else if (RV_LastNu > RV_QDelayMX)
RV_LastNu = RV_QDelayMX;
x = bx * LastNu_bak; /* Zeta * LastNu */
x <<= 1; /* Rockwell-adjust for decimal point shift. */
x = (RV_round_32_into_16 (x) * ((vgetty_s_int32)4));
RV_NewQdata = RV_clip_16 (x);
sum = RV_XpzCalc (RV_NewQdata); /* Compute (Xp+z)(n) + Q(n) */
si = HIWORD(sum); /* Y(n) done, save in SI for later */
#ifdef POSTFILTER
sum = RV_App1Calc ((vgetty_s_int16)(HIWORD(sum)));
sum = RV_App2Calc ((vgetty_s_int16)(HIWORD(sum)));
#endif
/* Use a de-emphasis filter on Y(n) to remove the effects */
/* of the emphasis filter used during compression. */
x = RV_DEMPCF * RV_Dempz;
x <<= 1; /* Rockwell-adjust for decimal point shift. */
sum += x;
sum = RV_clip_32 (sum);
RV_Dempz = HIWORD(sum);
/* Update predictor filter coefficients. */
RV_pzPred (RV_NewQdata);
RV_Qdata[RV_di] = si; /* drop b6, now a1 Qdata */
/* Update delay line at the a1(n) table entry position. */
RV_QdataIndex = RV_di;
RV_di += 2;
RV_di %= 8;
RV_Qdata[RV_di] = RV_NewQdata; /* drop a2, now b1 Qdata */
return RV_Dempz;
}
/* Compression!! */
/* cd2x, cd3x, and cd4x are combined in cd[][], just like Torsten suggested */
static vgetty_s_int16 RV_cdx[3][16] =
{
{0x1F69},
{0x1005, 0x219A, 0x37F0},
{0x0843, 0x10B8, 0x1996, 0x232B, 0x2DFC, 0x3B02, 0x4CD6}
};
static vgetty_s_int16 RV_cdx_length[3] = { 1,3,7 };
static vgetty_s_int16 *RV_cd;
static vgetty_s_int16 RV_cd_length;
static vgetty_s_int32 RV_ComOne(vgetty_s_int16 ax)
{
/*
RVComOne
Code a word into a bps bit codeword. Local to this module.
Inputs:
AX = X(n)
*/
int i;
static vgetty_s_int16 adc16z1 = 0; /* X(n-1) */
static vgetty_s_int16 NewXpz = 0; /* Xp+z(n) */
static vgetty_s_int16 bx,cx,dx;
static vgetty_s_int32 x, y; /* local accumulator */
static vgetty_s_int64 sum;
sum = RV_XpzCalc ((vgetty_s_int16)0); /* Compute Xp+z(n) */
NewXpz = HIWORD(sum);
x = ((vgetty_s_int32) adc16z1) * RV_PDEMPCF;
x <<= 1; /* Rockwell-adjust for decimal point shift. */
sum += x;
sum = RV_clip_32 (sum);
cx = HIWORD(sum);
/* Optimise any of this at your own peril ! */
x = cx;
x -= ax;
cx = RV_clip_16 (x);
bx = cx;
if (bx<0) bx = -bx;
cx = -cx;
adc16z1 = ax;
y = RV_LastNu; /*
Optimise any of this at your own peril !
Ideally, the important variables
here should all be declared volatile.
If you change any of this, your
optimiser might break it !
*/
for (i=0;i<RV_cd_length;i++){
x = ((vgetty_s_int32)RV_cd[i]) * y;
x <<= 1; /* Rockwell-adjust for decimal point shift. */
/* Round and Multiply by 4 */
x = (RV_round_32_into_16 (x) * ((vgetty_s_int32)4));
dx = RV_clip_16 (x);
if (bx<dx) break;
}
i++;
if (cx<0){
i -= RV_cd_length;
i--;
i = -i;
} else i+= RV_cd_length;
ax = i;
x = ((vgetty_s_int32)RV_mul_p[i]) * y;
x <<= 1; /* Rockwell-adjust for decimal point shift. */
/* Round and Multiply by 4 */
x = (RV_round_32_into_16 (x) * ((vgetty_s_int32)4));
RV_LastNu = RV_clip_16 (x);
if (RV_LastNu < RV_QDLMN)
RV_LastNu = RV_QDLMN;
else if (RV_LastNu > RV_QDelayMX)
RV_LastNu = RV_QDelayMX;
/* Make a new inverse quantizer value. */
x = ((vgetty_s_int32)RV_Zeta_p[i]) * y;
x <<= 1; /* Rockwell-adjust for decimal point shift. */
/* Round and Multiply by 4 */
x = (RV_round_32_into_16 (x) * ((vgetty_s_int32)4));
cx = RV_clip_16 (x);
/* Update predictor filter coefficients. */
RV_pzPred (cx);
/* Update delay line at the a1(n) table entry position. */
RV_QdataIndex += 7;
RV_QdataIndex %= 8;
x = NewXpz;
x += cx;
RV_Qdata[RV_QdataIndex] = RV_clip_16 (x); /* drop b6, now a1 Qdata. */
RV_Qdata[(RV_QdataIndex+2)%8] = cx; /* drop a2, now b1 Qdata. */
return (vgetty_s_int32) ax;
}
#ifdef RV_HONOUR_SILENCE_CODEWORDS /* Honour silence codewords and insert the requested silence */
static void put_silence(vgetty_s_int32 num_samples, FILE * out)
{
/* Write num_samples 16 bit ints of value 0 */
num_samples *= 2;
while (num_samples && (putc (0, out) != EOF))
num_samples--;
if (num_samples)
{
perror ("write silence");
exit (1);
}
}
static int getcodeword(FILE * in, vgetty_s_int32 *codeword){
/*
Rockwell modems always pass on 16bit ints in little-endian format.
Therefore, we have to read the data the same way if we don't want
to miss a silence codeword.
*/
static int c;
if ((c = getc (in)) == EOF) return 0;
*codeword = c;
if ((c = getc (in)) == EOF) return 8;
*codeword |= (c<<8);
return 16;
}
int rockwelltopvf (FILE *fd_in, FILE *fd_out, int nbits, pvf_header *header_out)
{
vgetty_s_int32 w; /* packed compressed codewords */
int c; /* single compressed codeword */
vgetty_s_int32 mask=(1<<nbits)-1; /* bitmask for the decompression */
vgetty_s_int32 a = 0; /* local accumulator */
int valbits = 0; /* number of bits valid in accumulator */
/* The pvf header should have been written by now, start with the decompression */
/* Reset the coefficient table for the pole and zero linear predictor. */
for (w = 0; w < 8; w++) RV_pzTable[w] = 0;
RV_Reset (nbits);
/*
The algorithm below (copied from Torsten Duwe's code)
takes care of bit concatenation.
*/
while ((c=getcodeword(fd_in,&w)))
/*
Not using the pvf library generic read_bits interface because
Rockwell modems always pass on 16bit ints in little-endian format.
Therefore, we have to read the data the same way if we don't want
to miss a silence codeword.
*/
{
if (w == RV_silence_word)
{
getcodeword(fd_in,&w);
put_silence (w, fd_out);
RV_Reset (nbits);
valbits = 0;
continue;
}
a |= w<<valbits;
valbits += c;
while (valbits >= nbits)
{
c = a & mask;
w = RV_DecomOne(RV_mul_p[c],RV_Zeta_p[c]);
w <<= 8; /* The pvf routines expect a 24bit int */
header_out->write_pvf_data(fd_out, w);
a >>= nbits;
valbits -= nbits;
}
}
return(OK);
}
#else /* Silence codewords are just gobbled up as data */
int rockwelltopvf (FILE *fd_in, FILE *fd_out, int nbits, pvf_header *header_out)
{
state_t s = init_state;
int c; /* single compressed codeword or EOF */
/* The pvf header should have been written by now, start with the decompression */
/* Reset the coefficient table for the pole and zero linear predictor. */
for (c = 0; c < 8; c++) RV_pzTable[c] = 0;
RV_Reset (nbits);
while ((c = read_bits_reverse(fd_in,&s,nbits)) != EOF)
header_out->write_pvf_data(fd_out, /* The pvf routines expect a 24bit int */
((vgetty_s_int32)RV_DecomOne(RV_mul_p[c],RV_Zeta_p[c]))<<8 );
return(OK);
}
#endif
int pvftorockwell (FILE *fd_in, FILE *fd_out, int nbits, pvf_header *header_in)
{
state_t s = init_state;
vgetty_s_int32 w; /* uncompressed audio word */
/* Reset the coefficient table for the pole and zero linear predictor. */
for (w = 0; w < 8; w++) RV_pzTable[w] = 0;
RV_Reset (nbits);
RV_cd = RV_cdx[nbits-2];
RV_cd_length = RV_cdx_length[nbits-2];
/* The rmd header should have been written by now. Do the compression. */
while (1)
{
w = header_in->read_pvf_data(fd_in);
if (feof(fd_in))
break;
/* The pvf routines work on 24bit ints */
write_bits_reverse(fd_out, &s, nbits, (int)RV_ComOne((vgetty_s_int16)(w>>8)) );
}
if (s.nleft > 0) write_bits_reverse(fd_out, &s, 8 - s.nleft, 0x00);
return(OK);
}
/*
* I borrowed this code from wav.c - it assumes the pvf is a certain size and
* I'm not sure this is a good assumption -- Bill Nugent <whn@topelo.lopi.com>
*/
int rockwellpcmtopvf (FILE *fd_in, FILE *fd_out, int nbits, pvf_header *header_out)
{
int d;
/* 8 bit PCM */
while ((d = getc(fd_in)) != EOF)
{
d -= 0x80;
d <<= 16;
header_out->write_pvf_data(fd_out, d);
}
return(OK);
}
int pvftorockwellpcm (FILE *fd_in, FILE *fd_out, int nbits, pvf_header *header_in)
{
int data;
/* 8 bit PCM */
while((data = header_in->read_pvf_data(fd_in)) != EOF)
{
data >>=16;
if (data > 0x7f)
data = 0x7f;
if (data < -0x80)
data = -0x80;
putc(data+0x80,fd_out);
};
return(OK);
}

994
lib/libpvf/usr.c Normal file
View File

@ -0,0 +1,994 @@
/*
* usr.c
*
* Conversion pvf <--> USR GSM and ADPCM formats.
*
* $Id: usr.c,v 1.5 2001/12/22 19:44:09 marcs Exp $
*
*/
#include "../include/voice.h"
/* Forward defs of the format-specific routines
*/
static int pvftousrgsm (FILE *fd_in, FILE *fd_out, pvf_header *header_in);
static int pvftousradpcm (FILE *fd_in, FILE *fd_out, pvf_header *header_in);
static int usrgsmtopvf (FILE *fd_in, FILE *fd_out, pvf_header *header_out);
static int usradpcmtopvf (FILE *fd_in, FILE *fd_out, pvf_header *header_out);
int pvftousr(FILE *fd_in, FILE *fd_out, int compression,
pvf_header *header_in) {
switch (compression) {
case 1:
return (pvftousrgsm(fd_in, fd_out, header_in));
case 4:
return (pvftousradpcm(fd_in, fd_out, header_in));
default:
return -1;
}
}
int usrtopvf (FILE *fd_in, FILE *fd_out, int compression,
pvf_header *header_out) {
switch (compression) {
case 1:
return (usrgsmtopvf(fd_in, fd_out, header_out));
case 4:
return (usradpcmtopvf(fd_in, fd_out, header_out));
default:
return -1;
}
}
/*****************
** GSM SECTION **
*****************/
#include "../libmgsm/gsm.h"
/* USR's GSM data format consists of 38-byte frames of data where the
* first two bytes of the frame (usually "0xFE 0xFE" for valid data and
* "0xB6 0xB6" for silence, and 3 bytes of trailer ("0x0 0xA5 0xA5") can
* be discarded, giving 33 bytes of useful data.
* Newer models can also generate frames with raw data (without the
* trailing and leading bytes).
* The decoding function tries to detect the frame type and pass the
* 33 bytes of data to a garden variety GSM decode process.
* In my case, I used GSM 06.10 from Technische
* Universitaet Berlin ftp://ftp.cs.tu-berlin.de/pub/local/kbs/tubmik/gsm/
*
* The pvftousrgsm function just generates the old type of frame
* since it can be played on both new and old models.
*/
unsigned char gsm_head[2] = { 0xfe, 0xfe };
unsigned char gsm_tail[3] = { 0x0, 0xa5, 0xa5 };
static int pvftousrgsm (FILE *fd_in, FILE *fd_out, pvf_header *header_in)
{
gsm r;
gsm_signal s[ 160 ];
gsm_frame d;
int opt_fast = 0;
int opt_verbose = 0;
int opt_ltp_cut = 0;
int i;
int sample = 0;
if (!(r = gsm_create())) {
perror("gsm_create");
return -1;
}
(void)gsm_option(r, GSM_OPT_FAST, &opt_fast);
(void)gsm_option(r, GSM_OPT_VERBOSE, &opt_verbose);
(void)gsm_option(r, GSM_OPT_LTP_CUT, &opt_ltp_cut);
/* GSM operates on frames of 160 samples each, so we may run up against
* the end of the file and be forced to backfill with zeroes.
*/
while (!feof(fd_in)) {
for (i=0;i<160;i++) {
sample = header_in->read_pvf_data(fd_in);
if (feof(fd_in)) {
memset((char *)(&s[i]), 0, sizeof(gsm_signal)*(160-i));
} else {
sample >>= 8;
if (sample > 0x7fff)
sample = 0x7fff;
if (sample < -0x8000)
sample = -0x8000;
s[i] = sample;
}
}
gsm_encode(r, s, d);
fwrite((char *)gsm_head, 2, 1, fd_out);
fwrite((char *)d, sizeof(d), 1, fd_out);
fwrite((char *)gsm_tail, 3, 1, fd_out);
}
gsm_destroy(r);
return(OK);
}
static int usrgsmtopvf (FILE *fd_in, FILE *fd_out, pvf_header *header_out)
{
unsigned char inbuf[38];
gsm r;
gsm_byte *s;
gsm_signal d[ 160 ];
int opt_fast = 0;
int opt_verbose = 0;
int i, sample, bytes2read, chunksread;
if (!(r = gsm_create())) {
perror("gsm_create");
return -1;
}
(void)gsm_option(r, GSM_OPT_FAST, &opt_fast);
(void)gsm_option(r, GSM_OPT_VERBOSE, &opt_verbose);
/*
* read the first frame to see if it has an
* header or is raw data
*/
if ((chunksread=fread(inbuf, 33, 1, fd_in)) > 0) {
if ((inbuf[0] == inbuf[1]) &&
((inbuf[0] == 0xfe) || (inbuf[0] == 0xb6))) {
/*
* has an header
*/
fread(&inbuf[33], 5, 1, fd_in);
s=&inbuf[2];
bytes2read=38;
} else
{
/*
* raw data
*/
s=&inbuf[0];
bytes2read=33;
}
}
while (chunksread > 0) {
/* --- MNI_p/JoSch --->
* I don'n know how this (now redundant to leave libmgsm untouched
* -> see ../libmgsm/decode.c line 20) control for GSM_MAGIC
* works with other USR- modems. Do they realy produce wrong
* bytes so that it is necessary to control it?
* For my US Robotics Vi 28.8 Faxmodem (gr) with Personal Voice
* Mail (speed 33600) this seems to work.
* May be there is a modem-setting I don't know (I don't know
* any voice-settig-switches for this modem) that switches the
* modem to a workable compression. At this time this patch works
* for me until I get informations from the USR-Support.
*/
if ((((*s >> 4) & 0x0F) != GSM_MAGIC) || (((*s >> 4) & 0x0F) != 0))
*s |= (GSM_MAGIC << 4);
/* <--- MNI_p/JoSch --- */
if (gsm_decode(r, s, d)) {
fprintf(stderr, "%s: bad frame in input\n", program_name);
gsm_destroy(r);
return(ERROR);
}
for (i=0;i<160;i++) {
sample = d[i];
if (sample > 0x7fff) {
sample -= 0x10000;
}
header_out->write_pvf_data(fd_out, sample << 8);
}
chunksread=fread(inbuf, bytes2read, 1, fd_in);
}
return(OK);
}
/*******************
** ADPCM SECTION **
*******************/
/* This ADPCM code was released by Sun Microsystems, Inc. to the public domain
* as a part of the CCITT (International Telegraph and Telephone Consultative
* Committee) reference implementation of the G.711, G.721 and G.723 voice
* compression standards.
*
* The following files from the original distribution have been concatenated
* in this section:
*
* g72x.h header file for g721.c, g723_24.c and g723_40.c
* g72x.c common denominator of G.721 and G.723 ADPCM codes
* g721.c CCITT G.721 32Kbps ADPCM coder (with g72x.c)
*
* Below this, the pvf conversion routines follow.
*/
/*
* This source code is a product of Sun Microsystems, Inc. and is provided
* for unrestricted use. Users may copy or modify this source code without
* charge.
*
* SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING
* THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
* PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
*
* Sun source code is provided with no support and without any obligation on
* the part of Sun Microsystems, Inc. to assist in its use, correction,
* modification or enhancement.
*
* SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
* INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE
* OR ANY PART THEREOF.
*
* In no event will Sun Microsystems, Inc. be liable for any lost revenue
* or profits or other special, indirect and consequential damages, even if
* Sun has been advised of the possibility of such damages.
*
* Sun Microsystems, Inc.
* 2550 Garcia Avenue
* Mountain View, California 94043
*/
/*
* g72x.h
*
* Header file for CCITT conversion routines.
*
*/
#ifndef _G72X_H
#define _G72X_H
#define AUDIO_ENCODING_LINEAR (3) /* PCM 2's-complement (0-center) */
/*
* The following is the definition of the state structure
* used by the G.721/G.723 encoder and decoder to preserve their internal
* state between successive calls. The meanings of the majority
* of the state structure fields are explained in detail in the
* CCITT Recommendation G.721. The field names are essentially indentical
* to variable names in the bit level description of the coding algorithm
* included in this Recommendation.
*/
struct g72x_state {
long yl; /* Locked or steady state step size multiplier. */
short yu; /* Unlocked or non-steady state step size multiplier. */
short dms; /* Short term energy estimate. */
short dml; /* Long term energy estimate. */
short ap; /* Linear weighting coefficient of 'yl' and 'yu'. */
short a[2]; /* Coefficients of pole portion of prediction filter. */
short b[6]; /* Coefficients of zero portion of prediction filter. */
short pk[2]; /*
* Signs of previous two samples of a partially
* reconstructed signal.
*/
short dq[6]; /*
* Previous 6 samples of the quantized difference
* signal represented in an internal floating point
* format.
*/
short sr[2]; /*
* Previous 2 samples of the quantized difference
* signal represented in an internal floating point
* format.
*/
char td; /* delayed tone detect, new in 1988 version */
};
#endif /* !_G72X_H */
/*
* This source code is a product of Sun Microsystems, Inc. and is provided
* for unrestricted use. Users may copy or modify this source code without
* charge.
*
* SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING
* THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
* PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
*
* Sun source code is provided with no support and without any obligation on
* the part of Sun Microsystems, Inc. to assist in its use, correction,
* modification or enhancement.
*
* SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
* INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE
* OR ANY PART THEREOF.
*
* In no event will Sun Microsystems, Inc. be liable for any lost revenue
* or profits or other special, indirect and consequential damages, even if
* Sun has been advised of the possibility of such damages.
*
* Sun Microsystems, Inc.
* 2550 Garcia Avenue
* Mountain View, California 94043
*/
/*
* g72x.c
*
* Common routines for G.721 and G.723 conversions.
*/
static short power2[15] = {1, 2, 4, 8, 0x10, 0x20, 0x40, 0x80,
0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000};
/*
* quan()
*
* quantizes the input val against the table of size short integers.
* It returns i if table[i - 1] <= val < table[i].
*
* Using linear search for simple coding.
*/
static int
quan(
int val,
short *table,
int size)
{
int i;
for (i = 0; i < size; i++)
if (val < *table++)
break;
return (i);
}
/*
* fmult()
*
* returns the integer product of the 14-bit integer "an" and
* "floating point" representation (4-bit exponent, 6-bit mantessa) "srn".
*/
static int
fmult(
int an,
int srn)
{
short anmag, anexp, anmant;
short wanexp, /* wanmag, */ wanmant;
short retval;
anmag = (an > 0) ? an : ((-an) & 0x1FFF);
anexp = quan(anmag, power2, 15) - 6;
anmant = (anmag == 0) ? 32 :
(anexp >= 0) ? anmag >> anexp : anmag << -anexp;
wanexp = anexp + ((srn >> 6) & 0xF) - 13;
wanmant = (anmant * (srn & 077) + 0x30) >> 4;
retval = (wanexp >= 0) ? ((wanmant << wanexp) & 0x7FFF) :
(wanmant >> -wanexp);
return (((an ^ srn) < 0) ? -retval : retval);
}
/*
* g72x_init_state()
*
* This routine initializes and/or resets the g72x_state structure
* pointed to by 'state_ptr'.
* All the initial state values are specified in the CCITT G.721 document.
*/
static void
g72x_init_state(
struct g72x_state *state_ptr)
{
int cnta;
state_ptr->yl = 34816;
state_ptr->yu = 544;
state_ptr->dms = 0;
state_ptr->dml = 0;
state_ptr->ap = 0;
for (cnta = 0; cnta < 2; cnta++) {
state_ptr->a[cnta] = 0;
state_ptr->pk[cnta] = 0;
state_ptr->sr[cnta] = 32;
}
for (cnta = 0; cnta < 6; cnta++) {
state_ptr->b[cnta] = 0;
state_ptr->dq[cnta] = 32;
}
state_ptr->td = 0;
}
/*
* predictor_zero()
*
* computes the estimated signal from 6-zero predictor.
*
*/
static int
predictor_zero(
struct g72x_state *state_ptr)
{
int i;
int sezi;
sezi = fmult(state_ptr->b[0] >> 2, state_ptr->dq[0]);
for (i = 1; i < 6; i++) /* ACCUM */
sezi += fmult(state_ptr->b[i] >> 2, state_ptr->dq[i]);
return (sezi);
}
/*
* predictor_pole()
*
* computes the estimated signal from 2-pole predictor.
*
*/
static int
predictor_pole(
struct g72x_state *state_ptr)
{
return (fmult(state_ptr->a[1] >> 2, state_ptr->sr[1]) +
fmult(state_ptr->a[0] >> 2, state_ptr->sr[0]));
}
/*
* step_size()
*
* computes the quantization step size of the adaptive quantizer.
*
*/
static int
step_size(
struct g72x_state *state_ptr)
{
int y;
int dif;
int al;
if (state_ptr->ap >= 256)
return (state_ptr->yu);
else {
y = state_ptr->yl >> 6;
dif = state_ptr->yu - y;
al = state_ptr->ap >> 2;
if (dif > 0)
y += (dif * al) >> 6;
else if (dif < 0)
y += (dif * al + 0x3F) >> 6;
return (y);
}
}
/*
* quantize()
*
* Given a raw sample, 'd', of the difference signal and a
* quantization step size scale factor, 'y', this routine returns the
* ADPCM codeword to which that sample gets quantized. The step
* size scale factor division operation is done in the log base 2 domain
* as a subtraction.
*/
static int
quantize(
int d, /* Raw difference signal sample */
int y, /* Step size multiplier */
short *table, /* quantization table */
int size) /* table size of short integers */
{
short dqm; /* Magnitude of 'd' */
short exp; /* Integer part of base 2 log of 'd' */
short mant; /* Fractional part of base 2 log */
short dl; /* Log of magnitude of 'd' */
short dln; /* Step size scale factor normalized log */
int i;
/*
* LOG
*
* Compute base 2 log of 'd', and store in 'dl'.
*/
dqm = abs(d);
exp = quan(dqm >> 1, power2, 15);
mant = ((dqm << 7) >> exp) & 0x7F; /* Fractional portion. */
dl = (exp << 7) + mant;
/*
* SUBTB
*
* "Divide" by step size multiplier.
*/
dln = dl - (y >> 2);
/*
* QUAN
*
* Obtain codword i for 'd'.
*/
i = quan(dln, table, size);
if (d < 0) /* take 1's complement of i */
return ((size << 1) + 1 - i);
else if (i == 0) /* take 1's complement of 0 */
return ((size << 1) + 1); /* new in 1988 */
else
return (i);
}
/*
* reconstruct()
*
* Returns reconstructed difference signal 'dq' obtained from
* codeword 'i' and quantization step size scale factor 'y'.
* Multiplication is performed in log base 2 domain as addition.
*/
static int
reconstruct(
int sign, /* 0 for non-negative value */
int dqln, /* G.72x codeword */
int y) /* Step size multiplier */
{
short dql; /* Log of 'dq' magnitude */
short dex; /* Integer part of log */
short dqt;
short dq; /* Reconstructed difference signal sample */
dql = dqln + (y >> 2); /* ADDA */
if (dql < 0) {
return ((sign) ? -0x8000 : 0);
} else { /* ANTILOG */
dex = (dql >> 7) & 15;
dqt = 128 + (dql & 127);
dq = (dqt << 7) >> (14 - dex);
return ((sign) ? (dq - 0x8000) : dq);
}
}
/*
* update()
*
* updates the state variables for each output code
*/
static void
update(
int code_size, /* distinguish 723_40 with others */
int y, /* quantizer step size */
int wi, /* scale factor multiplier */
int fi, /* for long/short term energies */
int dq, /* quantized prediction difference */
int sr, /* reconstructed signal */
int dqsez, /* difference from 2-pole predictor */
struct g72x_state *state_ptr) /* coder state pointer */
{
int cnt;
short mag, exp /* , mant */ ; /* Adaptive predictor, FLOAT A */
short a2p = 0; /* LIMC */
short a1ul; /* UPA1 */
short /* ua2, */ pks1; /* UPA2 */
short /* uga2a, */ fa1;
/* short uga2b; */
char tr; /* tone/transition detector */
short ylint, thr2, dqthr;
short ylfrac, thr1;
short pk0;
pk0 = (dqsez < 0) ? 1 : 0; /* needed in updating predictor poles */
mag = dq & 0x7FFF; /* prediction difference magnitude */
/* TRANS */
ylint = state_ptr->yl >> 15; /* exponent part of yl */
ylfrac = (state_ptr->yl >> 10) & 0x1F; /* fractional part of yl */
thr1 = (32 + ylfrac) << ylint; /* threshold */
thr2 = (ylint > 9) ? 31 << 10 : thr1; /* limit thr2 to 31 << 10 */
dqthr = (thr2 + (thr2 >> 1)) >> 1; /* dqthr = 0.75 * thr2 */
if (state_ptr->td == 0) /* signal supposed voice */
tr = 0;
else if (mag <= dqthr) /* supposed data, but small mag */
tr = 0; /* treated as voice */
else /* signal is data (modem) */
tr = 1;
/*
* Quantizer scale factor adaptation.
*/
/* FUNCTW & FILTD & DELAY */
/* update non-steady state step size multiplier */
state_ptr->yu = y + ((wi - y) >> 5);
/* LIMB */
if (state_ptr->yu < 544) /* 544 <= yu <= 5120 */
state_ptr->yu = 544;
else if (state_ptr->yu > 5120)
state_ptr->yu = 5120;
/* FILTE & DELAY */
/* update steady state step size multiplier */
state_ptr->yl += state_ptr->yu + ((-state_ptr->yl) >> 6);
/*
* Adaptive predictor coefficients.
*/
if (tr == 1) { /* reset a's and b's for modem signal */
state_ptr->a[0] = 0;
state_ptr->a[1] = 0;
state_ptr->b[0] = 0;
state_ptr->b[1] = 0;
state_ptr->b[2] = 0;
state_ptr->b[3] = 0;
state_ptr->b[4] = 0;
state_ptr->b[5] = 0;
} else { /* update a's and b's */
pks1 = pk0 ^ state_ptr->pk[0]; /* UPA2 */
/* update predictor pole a[1] */
a2p = state_ptr->a[1] - (state_ptr->a[1] >> 7);
if (dqsez != 0) {
fa1 = (pks1) ? state_ptr->a[0] : -state_ptr->a[0];
if (fa1 < -8191) /* a2p = function of fa1 */
a2p -= 0x100;
else if (fa1 > 8191)
a2p += 0xFF;
else
a2p += fa1 >> 5;
if (pk0 ^ state_ptr->pk[1])
/* LIMC */
if (a2p <= -12160)
a2p = -12288;
else if (a2p >= 12416)
a2p = 12288;
else
a2p -= 0x80;
else if (a2p <= -12416)
a2p = -12288;
else if (a2p >= 12160)
a2p = 12288;
else
a2p += 0x80;
}
/* TRIGB & DELAY */
state_ptr->a[1] = a2p;
/* UPA1 */
/* update predictor pole a[0] */
state_ptr->a[0] -= state_ptr->a[0] >> 8;
if (dqsez != 0) {
if (pks1 == 0) {
state_ptr->a[0] += 192;
}
else {
state_ptr->a[0] -= 192;
}
}
/* LIMD */
a1ul = 15360 - a2p;
if (state_ptr->a[0] < -a1ul)
state_ptr->a[0] = -a1ul;
else if (state_ptr->a[0] > a1ul)
state_ptr->a[0] = a1ul;
/* UPB : update predictor zeros b[6] */
for (cnt = 0; cnt < 6; cnt++) {
if (code_size == 5) /* for 40Kbps G.723 */
state_ptr->b[cnt] -= state_ptr->b[cnt] >> 9;
else /* for G.721 and 24Kbps G.723 */
state_ptr->b[cnt] -= state_ptr->b[cnt] >> 8;
if (dq & 0x7FFF) { /* XOR */
if ((dq ^ state_ptr->dq[cnt]) >= 0)
state_ptr->b[cnt] += 128;
else
state_ptr->b[cnt] -= 128;
}
}
}
for (cnt = 5; cnt > 0; cnt--)
state_ptr->dq[cnt] = state_ptr->dq[cnt-1];
/* FLOAT A : convert dq[0] to 4-bit exp, 6-bit mantissa f.p. */
if (mag == 0) {
state_ptr->dq[0] = (dq >= 0) ? 0x20 : 0xFC20;
} else {
exp = quan(mag, power2, 15);
state_ptr->dq[0] = (dq >= 0) ?
(exp << 6) + ((mag << 6) >> exp) :
(exp << 6) + ((mag << 6) >> exp) - 0x400;
}
state_ptr->sr[1] = state_ptr->sr[0];
/* FLOAT B : convert sr to 4-bit exp., 6-bit mantissa f.p. */
if (sr == 0) {
state_ptr->sr[0] = 0x20;
} else if (sr > 0) {
exp = quan(sr, power2, 15);
state_ptr->sr[0] = (exp << 6) + ((sr << 6) >> exp);
} else if (sr > -32768) {
mag = -sr;
exp = quan(mag, power2, 15);
state_ptr->sr[0] = (exp << 6) + ((mag << 6) >> exp) - 0x400;
} else
state_ptr->sr[0] = (unsigned short) 0xFC20;
/* DELAY A */
state_ptr->pk[1] = state_ptr->pk[0];
state_ptr->pk[0] = pk0;
/* TONE */
if (tr == 1) /* this sample has been treated as data */
state_ptr->td = 0; /* next one will be treated as voice */
else if (a2p < -11776) /* small sample-to-sample correlation */
state_ptr->td = 1; /* signal may be data */
else /* signal is voice */
state_ptr->td = 0;
/*
* Adaptation speed control.
*/
state_ptr->dms += (fi - state_ptr->dms) >> 5; /* FILTA */
state_ptr->dml += (((fi << 2) - state_ptr->dml) >> 7); /* FILTB */
if (tr == 1)
state_ptr->ap = 256;
else if (y < 1536) /* SUBTC */
state_ptr->ap += (0x200 - state_ptr->ap) >> 4;
else if (state_ptr->td == 1)
state_ptr->ap += (0x200 - state_ptr->ap) >> 4;
else if (abs((state_ptr->dms << 2) - state_ptr->dml) >=
(state_ptr->dml >> 3))
state_ptr->ap += (0x200 - state_ptr->ap) >> 4;
else
state_ptr->ap += (-state_ptr->ap) >> 4;
}
/*
* This source code is a product of Sun Microsystems, Inc. and is provided
* for unrestricted use. Users may copy or modify this source code without
* charge.
*
* SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING
* THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
* PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
*
* Sun source code is provided with no support and without any obligation on
* the part of Sun Microsystems, Inc. to assist in its use, correction,
* modification or enhancement.
*
* SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
* INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE
* OR ANY PART THEREOF.
*
* In no event will Sun Microsystems, Inc. be liable for any lost revenue
* or profits or other special, indirect and consequential damages, even if
* Sun has been advised of the possibility of such damages.
*
* Sun Microsystems, Inc.
* 2550 Garcia Avenue
* Mountain View, California 94043
*/
/*
* g721.c
*
* Description:
*
* g721_encoder(), g721_decoder()
*
* These routines comprise an implementation of the CCITT G.721 ADPCM
* coding algorithm. Essentially, this implementation is identical to
* the bit level description except for a few deviations which
* take advantage of work station attributes, such as hardware 2's
* complement arithmetic and large memory. Specifically, certain time
* consuming operations such as multiplications are replaced
* with lookup tables and software 2's complement operations are
* replaced with hardware 2's complement.
*
* The deviation from the bit level specification (lookup tables)
* preserves the bit level performance specifications.
*
* As outlined in the G.721 Recommendation, the algorithm is broken
* down into modules. Each section of code below is preceded by
* the name of the module which it is implementing.
*
*/
static short qtab_721[7] = {-124, 80, 178, 246, 300, 349, 400};
/*
* Maps G.721 code word to reconstructed scale factor normalized log
* magnitude values.
*/
static short _dqlntab[16] = {-2048, 4, 135, 213, 273, 323, 373, 425,
425, 373, 323, 273, 213, 135, 4, -2048};
/* Maps G.721 code word to log of scale factor multiplier. */
static short _witab[16] = {-12, 18, 41, 64, 112, 198, 355, 1122,
1122, 355, 198, 112, 64, 41, 18, -12};
/*
* Maps G.721 code words to a set of values whose long and short
* term averages are computed and then compared to give an indication
* how stationary (steady state) the signal is.
*/
static short _fitab[16] = {0, 0, 0, 0x200, 0x200, 0x200, 0x600, 0xE00,
0xE00, 0x600, 0x200, 0x200, 0x200, 0, 0, 0};
/*
* g721_encoder()
*
* Encodes the input vale of linear PCM, A-law or u-law data sl and returns
* the resulting code. -1 is returned for unknown input coding value.
*/
static int
g721_encoder(
int sl,
int in_coding,
struct g72x_state *state_ptr)
{
short sezi, se, sez; /* ACCUM */
short d; /* SUBTA */
short sr; /* ADDB */
short y; /* MIX */
short dqsez; /* ADDC */
short dq, i;
switch (in_coding) { /* linearize input sample to 14-bit PCM */
case AUDIO_ENCODING_LINEAR:
sl >>= 2; /* 14-bit dynamic range */
break;
default:
return (-1);
}
sezi = predictor_zero(state_ptr);
sez = sezi >> 1;
se = (sezi + predictor_pole(state_ptr)) >> 1; /* estimated signal */
d = sl - se; /* estimation difference */
/* quantize the prediction difference */
y = step_size(state_ptr); /* quantizer step size */
i = quantize(d, y, qtab_721, 7); /* i = ADPCM code */
dq = reconstruct(i & 8, _dqlntab[i], y); /* quantized est diff */
sr = (dq < 0) ? se - (dq & 0x3FFF) : se + dq; /* reconst. signal */
dqsez = sr + sez - se; /* pole prediction diff. */
update(4, y, _witab[i] << 5, _fitab[i], dq, sr, dqsez, state_ptr);
return (i);
}
/*
* g721_decoder()
*
* Description:
*
* Decodes a 4-bit code of G.721 encoded data of i and
* returns the resulting linear PCM, A-law or u-law value.
* return -1 for unknown out_coding value.
*/
static int
g721_decoder(
int i,
int out_coding,
struct g72x_state *state_ptr)
{
short sezi, sei, sez, se; /* ACCUM */
short y; /* MIX */
short sr; /* ADDB */
short dq;
short dqsez;
i &= 0x0f; /* mask to get proper bits */
sezi = predictor_zero(state_ptr);
sez = sezi >> 1;
sei = sezi + predictor_pole(state_ptr);
se = sei >> 1; /* se = estimated signal */
y = step_size(state_ptr); /* dynamic quantizer step size */
dq = reconstruct(i & 0x08, _dqlntab[i], y); /* quantized diff. */
sr = (dq < 0) ? (se - (dq & 0x3FFF)) : se + dq; /* reconst. signal */
dqsez = sr - se + sez; /* pole prediction diff. */
update(4, y, _witab[i] << 5, _fitab[i], dq, sr, dqsez, state_ptr);
switch (out_coding) {
case AUDIO_ENCODING_LINEAR:
return (sr << 2); /* sr was 14-bit dynamic range */
default:
return (-1);
}
}
static int
pack_output(
unsigned code,
int bits,
FILE* fd_out)
{
static unsigned int out_buffer = 0;
static int out_bits = 0;
unsigned char out_byte;
out_buffer |= (code << out_bits);
out_bits += bits;
if (out_bits >= 8) {
out_byte = out_buffer & 0xff;
out_bits -= 8;
out_buffer >>= 8;
fwrite(&out_byte, sizeof (char), 1, fd_out);
}
return (out_bits > 0);
}
static int
unpack_input(
unsigned char *code,
int bits,
FILE* fd_in)
{
static unsigned int in_buffer = 0;
static int in_bits = 0;
unsigned char in_byte;
if (in_bits < bits) {
if (fread(&in_byte, sizeof (char), 1, fd_in) != 1) {
*code = 0;
return (-1);
}
in_buffer |= (in_byte << in_bits);
in_bits += 8;
}
*code = in_buffer & ((1 << bits) - 1);
in_buffer >>= bits;
in_bits -= bits;
return (in_bits > 0);
}
int pvftousradpcm (FILE *fd_in, FILE *fd_out, pvf_header *header_in)
{
struct g72x_state state;
int r = 0;
int sample = 0;
unsigned char code = 0;
g72x_init_state(&state);
/* GSM operates on frames of 160 samples each, so we may run up against
* the end of the file and be forced to backfill with zeroes.
*/
while (!feof(fd_in)) {
sample = header_in->read_pvf_data(fd_in);
sample >>= 8;
if (sample > 0x7fff)
sample = 0x7fff;
if (sample < -0x8000)
sample = -0x8000;
code = g721_encoder(sample, AUDIO_ENCODING_LINEAR, &state);
r = pack_output(code, 4, fd_out);
}
while (r) {
r = pack_output(0, 4, fd_out);
}
return(OK);
}
static int usradpcmtopvf (FILE *fd_in, FILE *fd_out, pvf_header *header_out)
{
int sample;
struct g72x_state state;
unsigned char code = 0;
while (unpack_input(&code, 4, fd_in) >= 0) {
sample = g721_decoder(code, AUDIO_ENCODING_LINEAR, &state);
header_out->write_pvf_data(fd_out, sample << 8);
}
return(OK);
}

183
lib/libpvf/voc.c Normal file
View File

@ -0,0 +1,183 @@
/*
* voc.c
*
* Converts pvf <--> voc.
*
* $Id: voc.c,v 1.4 1998/09/09 21:07:05 gert Exp $
*
*/
#include "../include/voice.h"
static char voc_hdr[32] =
{
'C','r','e','a','t','i','v','e',' ',
'V','o','i','c','e',' ','F','i','l','e',
0x1a,0x1a,0x00,0x0a,0x01,0x29,0x11,
0x01,(unsigned char) 0x82,0x70,0x00,(unsigned char) 0x98,0x00
};
/*
* static char *voc_type[] =
* {
* "8 bit",
* "4 bit",
* "2.6 bit",
* "2 bit",
* "Multi DAC, 1 channel",
* "Multi DAC, 2 channels",
* "Multi DAC, 3 channels",
* "Multi DAC, 4 channels",
* "unknown"
* };
*/
int pvftovoc (FILE *fd_in, FILE *fd_out, pvf_header *header_in)
{
int blocksize = 0x7080;
int count;
long rate = header_in->speed;
static unsigned char voc_blk[4] = {0x02, 0x80, 0x70, 0x00};
int data;
voc_hdr[30] = 256 - ((long) 1000000 / rate);
fwrite(voc_hdr, 1, sizeof(voc_hdr), fd_out);
count = blocksize;
while (!feof(fd_in))
{
data = header_in->read_pvf_data(fd_in) >> 16;
if (data > 0x7f)
data = 0x7f;
if (data < -0x80)
data = -0x80;
putc(data + 0x80, fd_out);
count--;
if (!count)
{
count = blocksize;
fwrite(voc_blk, 1, 4, fd_out);
};
};
while (count--)
putc(0x7f, fd_out);
putc(0x00, fd_out);
return(OK);
}
int voctopvf (FILE *fd_in, FILE *fd_out, pvf_header *header_out)
{
char hdr[32];
int data_offset;
int type;
long count;
long blocksize;
header_out->speed = -1;
fread(hdr, 1, 0x1a, fd_in);
if (strncmp(hdr, voc_hdr, 0x14))
{
fprintf(stderr, "%s: not a VOC file", program_name);
return(ERROR);
};
data_offset = hdr[0x14] | (hdr[0x15] << 8);
if (hdr[0x17] != 1)
{
fprintf(stderr, "%s: unsupported VOC major version %d",
program_name, hdr[0x17]);
return(ERROR);
};
for (count = 0x20; count < data_offset; count++)
getc(fd_in);
/*
* read the data blocks
*/
blocksize = 0;
count = 0;
while (TRUE)
{
type = getc(fd_in);
if (type == 0)
{
/*
* terminator
*/
return(OK);
}
else
{
blocksize = getc(fd_in);
blocksize |= (getc(fd_in) << 8);
blocksize |= (getc(fd_in) << 16);
count = blocksize;
if (type > 2)
fprintf(stderr,
"%s: unknown block type %d, skipping...\n", program_name,
type);
if (type == 1)
{
long sample_rate = 1000000L / (long) (256 - getc(fd_in));
int data_type = getc(fd_in);
if (header_out->speed == -1)
{
header_out->speed = sample_rate;
write_pvf_header(fd_out, header_out);
}
else
if (header_out->speed != sample_rate)
{
fprintf(stderr,
"%s: unsupported sample rate change",
program_name);
return(ERROR);
};
if (data_type != 0)
{
fprintf(stderr, "%s: unsupported data type %d",
program_name, data_type);
return(ERROR);
};
count -= 2;
};
while (count--)
{
int d = getc(fd_in);
if (feof(fd_in))
return(OK);
if (type <= 2)
header_out->write_pvf_data(fd_out, (d - 0x80) << 16);
};
};
};
return(OK);
}

574
lib/libpvf/wav.c Normal file
View File

@ -0,0 +1,574 @@
/*
* wav.c
*
* Converts pvf <--> wav.
* it was written by Karlo Gross kg@orion.ddorf.rhein-ruhr.de
* by using parts from Rick Richardson and Lance Norskog's
* wav.c, found in sox-11-gamma. Thank you for some funtions.
* This is the 1. alpha release from 1997/2/14
*
* $Id: wav.c,v 1.6 2000/07/22 09:57:46 marcs Exp $
*/
#include "../include/voice.h"
char *sizes[] =
{
"NONSENSE!",
"bytes",
"shorts",
"NONSENSE",
"longs",
"32-bit floats",
"64-bit floats",
"IEEE floats"
};
/* Private data for .wav file */
typedef struct wavstuff
{
long samples;
int second_header; /* non-zero on second header write */
} *wav_t;
/* wave file characteristics */
unsigned short wFormatTag; /* data format */
unsigned short wChannels; /* number of channels */
unsigned long wSamplesPerSecond; /* samples per second per channel */
unsigned long wAvgBytesPerSec; /* estimate of bytes per second needed */
unsigned short wBlockAlign; /* byte alignment of a basic sample block */
unsigned short wBitsPerSample; /* bits per sample */
unsigned long data_length; /* length of sound data in bytes */
unsigned long bytespersample; /* bytes per sample (per channel) */
static char *wav_format_str();
/* Read short, little-endian: little end first. VAX/386 style. */
unsigned short rlshort(ft_t ft)
{
unsigned char uc, uc2;
uc = getc(ft->fp);
uc2 = getc(ft->fp);
return (uc2 << 8) | uc;
}
/* Read long, little-endian: little end first. VAX/386 style. */
unsigned long rllong(ft_t ft)
{
unsigned char uc, uc2, uc3, uc4;
/* if (feof(ft->fp))
fprintf(stderr,readerr); No worky! */
uc = getc(ft->fp);
uc2 = getc(ft->fp);
uc3 = getc(ft->fp);
uc4 = getc(ft->fp);
return ((long)uc4 << 24) | ((long)uc3 << 16) | ((long)uc2 << 8) | (long)uc;
}
/* Write long, little-endian: little end first. VAX/386 style. */
int wllong(ft_t ft, unsigned long ul)
{
int datum;
datum = (ul) & 0xff;
putc(datum, ft->fp);
datum = (ul >> 8) & 0xff;
putc(datum, ft->fp);
datum = (ul >> 16) & 0xff;
putc(datum, ft->fp);
datum = (ul >> 24) & 0xff;
putc(datum, ft->fp);
if (ferror(ft->fp))
return 1;
return 0;
}
/* Write short, little-endian: little end first. VAX/386 style. */
int wlshort(ft_t ft, unsigned short us)
{
putc(us, ft->fp);
putc(us >> 8, ft->fp);
if (ferror(ft->fp))
return 1;
return 0;
}
int wavstartread(ft_t ft)
{
wav_t wav = (wav_t) ft->priv;
char magic[4];
unsigned int len;
int littlendian = 1;
char *endptr;
ft->info.rate = 0;
ft->info.size = -1;
ft->info.style = -1;
ft->info.channels = -1;
ft->comment = NULL;
ft->swap = 0;
endptr = (char *) &littlendian;
if (!*endptr) ft->swap = 1;
/* If you need to seek around the input file. */
if (0 && ! ft->seekable)
fprintf(stderr, "Sorry, .wav input file must be a file, not a pipe");
if ( fread(magic, 1, 4, ft->fp) != 4
|| strncmp("RIFF", magic, 4))
{
fprintf(stderr, "Sorry, not a RIFF file");
return ERROR;
}
len = rllong(ft);
if ( fread(magic, 1, 4, ft->fp) != 4
|| strncmp("WAVE", magic, 4))
{
fprintf(stderr, "Sorry, not a WAVE file");
return ERROR;
}
/* Now look for the format chunk */
for (;;)
{
if ( fread(magic, 1, 4, ft->fp) != 4 )
{
fprintf(stderr, "Sorry, missing fmt spec");
return ERROR;
}
len = rllong(ft);
if (strncmp("fmt ", magic, 4) == 0)
break; /* Found the format chunk */
while (len > 0 && !feof(ft->fp)) /* skip to next chunk */
{
getc(ft->fp);
len--;
}
}
if ( len < 16 )
fprintf(stderr, "Sorry, fmt chunk is too short");
wFormatTag = rlshort(ft);
switch (wFormatTag)
{
case WAVE_FORMAT_UNKNOWN:
fprintf(stderr, "Sorry, this WAV file is in Microsoft Official Unknown format.");
return ERROR;
case WAVE_FORMAT_PCM: /* this one, at least, I can handle */
break;
case WAVE_FORMAT_ADPCM:
fprintf(stderr, "Sorry, this WAV file is in Microsoft ADPCM format.");
return ERROR;
case WAVE_FORMAT_ALAW: /* Think I can handle this */
ft->info.style = ALAW;
break;
case WAVE_FORMAT_MULAW: /* Think I can handle this */
ft->info.style = ULAW;
break;
case WAVE_FORMAT_OKI_ADPCM:
fprintf(stderr, "Sorry, this WAV file is in OKI ADPCM format.");
return ERROR;
case WAVE_FORMAT_DIGISTD:
fprintf(stderr, "Sorry, this WAV file is in Digistd format.");
return ERROR;
case WAVE_FORMAT_DIGIFIX:
fprintf(stderr, "Sorry, this WAV file is in Digifix format.");
return ERROR;
case IBM_FORMAT_MULAW:
fprintf(stderr, "Sorry, this WAV file is in IBM U-law format.");
return ERROR;
case IBM_FORMAT_ALAW:
fprintf(stderr, "Sorry, this WAV file is in IBM A-law format.");
return ERROR;
case IBM_FORMAT_ADPCM:
fprintf(stderr, "Sorry, this WAV file is in IBM ADPCM format.");
return ERROR;
default:
fprintf(stderr, "Sorry, don't understand format");
return ERROR;
}
wChannels = rlshort(ft);
ft->info.channels = wChannels;
wSamplesPerSecond = rllong(ft);
ft->info.rate = wSamplesPerSecond;
wAvgBytesPerSec = rllong(ft); /* Average bytes/second */
wBlockAlign = rlshort(ft); /* Block align */
wBitsPerSample = rlshort(ft); /* bits per sample per channel */
bytespersample = (wBitsPerSample + 7)/8;
switch (bytespersample)
{
case 1:
ft->info.size = BYTE;
break;
case 2:
ft->info.size = WORD;
break;
case 4:
ft->info.size = LONG;
break;
default:
fprintf(stderr, "Sorry, don't understand .wav size");
return ERROR;
}
len -= 16;
while (len > 0 && !feof(ft->fp))
{
getc(ft->fp);
len--;
}
/* Now look for the wave data chunk */
for (;;)
{
if ( fread(magic, 1, 4, ft->fp) != 4 )
{
fprintf(stderr, "Sorry, missing data chunk");
return ERROR;
}
len = rllong(ft);
if (strncmp("data", magic, 4) == 0)
break; /* Found the data chunk */
while (len > 0 && !feof(ft->fp)) /* skip to next chunk */
{
getc(ft->fp);
len--;
}
}
data_length = len;
wav->samples = data_length/ft->info.size; /* total samples */
fprintf(stderr, "Reading Wave file: %s format, %d channel%s, %ld samp/sec\n",
wav_format_str(wFormatTag), wChannels,
wChannels == 1 ? "" : "s", wSamplesPerSecond);
fprintf(stderr, "%ld byte/sec, %d block align, %d bits/samp, %lu data bytes\n",
wAvgBytesPerSec, wBlockAlign, wBitsPerSample, data_length);
return OK;
}
int wavwritehdr(ft_t ft,long data_size)
{
switch (ft->info.size)
{
case BYTE:
wBitsPerSample = 8;
if (ft->info.style == -1 || ft->info.style == UNSIGNED)
ft->info.style = UNSIGNED;
else if (ft->info.style != ALAW && ft->info.style != ULAW)
fprintf(stderr, "User options overiding style written to .wav header");
break;
case WORD:
wBitsPerSample = 16;
if (ft->info.style == -1 || ft->info.style == SIGN2)
ft->info.style = SIGN2;
break;
case LONG:
wBitsPerSample = 32;
if (ft->info.style == -1 || ft->info.style == SIGN2)
ft->info.style = SIGN2;
break;
default:
wBitsPerSample = 32;
if (ft->info.style == -1)
ft->info.style = SIGN2;
break;
}
switch (ft->info.style)
{
case UNSIGNED:
wFormatTag = WAVE_FORMAT_PCM;
if (wBitsPerSample != 8 )
fprintf(stderr, "Warning - writing bad .wav file using unsigned data and %d bits/sample",wBitsPerSample);
break;
case SIGN2:
wFormatTag = WAVE_FORMAT_PCM;
if (wBitsPerSample == 8 )
fprintf(stderr, "Warning - writing bad .wav file using signed data and %d bits/sample",wBitsPerSample);
break;
case ALAW:
wFormatTag = WAVE_FORMAT_ALAW;
if (wBitsPerSample != 8 )
fprintf(stderr, "Warning - writing bad .wav file using A-law data and %d bits/sample",wBitsPerSample);
break;
case ULAW:
wFormatTag = WAVE_FORMAT_MULAW;
if (wBitsPerSample != 8 )
fprintf(stderr, "Warning - writing bad .wav file using U-law data and %d bits/sample",wBitsPerSample);
break;
}
wSamplesPerSecond = ft->info.rate;
bytespersample = (wBitsPerSample + 7)/8;
wAvgBytesPerSec = ft->info.rate * ft->info.channels * bytespersample;
wChannels = ft->info.channels;
wBlockAlign = ft->info.channels * bytespersample;
data_length = data_size;
/* figured out header info, so write it */
fputs("RIFF", ft->fp);
wllong(ft, data_length + 8+16+12+1); /* Waveform chunk size: FIXUP(4) */
/* die 1 ist von mir karlo */
fputs("WAVE", ft->fp);
fputs("fmt ", ft->fp);
wllong(ft, (long)16); /* fmt chunk size */
wlshort(ft, wFormatTag);
wlshort(ft, wChannels);
wllong(ft, wSamplesPerSecond);
wllong(ft, wAvgBytesPerSec);
wlshort(ft, wBlockAlign);
wlshort(ft, wBitsPerSample);
fputs("data", ft->fp);
wllong(ft, data_length); /* data chunk size: FIXUP(40) */
fprintf(stderr, "Writing Wave file: %s format, %d channel%s, %ld samp/sec",
wav_format_str(wFormatTag), wChannels,
wChannels == 1 ? "" : "s", wSamplesPerSecond);
fprintf(stderr, " %ld byte/sec, %d block align, %d bits/samp\n",
wAvgBytesPerSec, wBlockAlign, wBitsPerSample);
return OK;
}
int wavstartwrite(ft_t ft,long data_size)
{
wav_t wav = (wav_t) ft->priv;
int littlendian = 1;
char *endptr;
endptr = (char *) &littlendian;
if (!*endptr) ft->swap = 1;
wav->samples = 0;
wav->second_header = 0;
if (wavwritehdr(ft,data_size) != OK)
return ERROR;
return OK;
}
/*
* Return a string corresponding to the wave format type.
*/
static char * wav_format_str(unsigned wFormatTag)
{
switch (wFormatTag)
{
case WAVE_FORMAT_UNKNOWN:
return "Microsoft Official Unknown";
case WAVE_FORMAT_PCM:
return "Microsoft PCM";
case WAVE_FORMAT_ADPCM:
return "Microsoft ADPCM";
case WAVE_FORMAT_ALAW:
return "Microsoft A-law";
case WAVE_FORMAT_MULAW:
return "Microsoft U-law";
case WAVE_FORMAT_OKI_ADPCM:
return "OKI ADPCM format.";
case WAVE_FORMAT_DIGISTD:
return "Digistd format.";
case WAVE_FORMAT_DIGIFIX:
return "Digifix format.";
case IBM_FORMAT_MULAW:
return "IBM U-law format.";
case IBM_FORMAT_ALAW:
return "IBM A-law";
case IBM_FORMAT_ADPCM:
return "IBM ADPCM";
default:
return "Unknown";
}
}
int pvftowav (FILE *fd_in, FILE *fd_out, pvf_header *header_in, int wav_bits)
{
int bytespersample;
long data_size = 0;
int buffer_size = 0;
int *buffer = NULL;
int data,*ptr;
int voice_samples = 0;
struct soundstream s;
bytespersample = (wav_bits + 7) / 8;
switch (bytespersample)
{
case 1:
s.info.size = BYTE;
break;
case 2:
s.info.size = WORD;
break;
case 4:
s.info.size = LONG;
break;
default:
fprintf(stderr, "sorry, don't understand .wav size");
return(ERROR);
}
s.info.rate = header_in->speed;
s.info.style = -1;
s.info.channels = header_in->channels;
s.comment = NULL;
s.swap = 0;
s.filetype = (char *) 0;
s.fp = fd_out;
s.seekable = 0;
while(!feof(fd_in))
{
data = header_in->read_pvf_data(fd_in);
if (voice_samples >= buffer_size)
{
buffer_size += BLOCK_SIZE;
buffer = (int *) realloc(buffer, buffer_size * sizeof(int));
if (buffer == NULL)
{
fprintf(stderr, "%s: out of memory in pvftowav", program_name);
free(buffer);
exit(99);
};
}
buffer[voice_samples++] = data;
data_size++;
}
if (wavstartwrite(&s,data_size) != OK)
{
free(buffer);
return ERROR;
}
ptr = buffer;
switch (s.info.size)
{
case BYTE:
while (data_size--)
{
*ptr >>=16;
if (*ptr > 0x7f)
*ptr = 0x7f;
if (*ptr < -0x80)
*ptr = -0x80;
putc(*ptr+0x80,fd_out);
ptr++;
};
break;
case WORD:
while (data_size--)
{
*ptr >>=8;
if (*ptr > 0x7fff)
*ptr = 0x7fff;
if (*ptr < -0x8000)
*ptr = -0x8000;
putc(*ptr, fd_out);
putc(*ptr++ >> 8, fd_out);
};
break;
case LONG:
while (data_size--)
{
putc(*ptr, fd_out);
putc(*ptr >> 8, fd_out);
putc(*ptr >> 16, fd_out);
putc(*ptr++ >> 24, fd_out);
}
break;
}
free(buffer);
return(OK);
}
int wavtopvf (FILE *fd_in, FILE *fd_out, pvf_header *header_out)
{
struct soundstream s;
int d;
s.fp = fd_in;
if (wavstartread(&s) != OK)
return ERROR;
header_out->channels = (int) wChannels;
header_out->speed = (int) wSamplesPerSecond;
write_pvf_header(fd_out, header_out);
while (data_length--)
{
if (feof(fd_in))
return(OK);
switch (wBitsPerSample)
{
case 8:
d = getc(fd_in) - 0x80;
d <<= 16;
break;
case 16:
d = getc(fd_in) & 0xFF;
d += (getc(fd_in) << 8);
if (d & 0x8000)
d |= 0xFFFF0000;
d <<= 8;
break;
default:
fprintf(stderr,
"%s: unsupported number of bits per sample (%d)\n",
program_name, wBitsPerSample);
return(ERROR);
};
header_out->write_pvf_data(fd_out, d);
};
return(OK);
}

249
lib/libpvf/zyxel-o56k.c Normal file
View File

@ -0,0 +1,249 @@
/*
* 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 <const@ce.cctpu.edu.ru>
*
* $Id: zyxel-o56k.c,v 1.1 2000/07/22 10:01:01 marcs Exp $
*
*/
#include "../include/voice.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<RL; j++)
{
for (k=0; k<2; k++)
{
/* extract ADPCM codeword from input buffer */
if ( k==0 ) codeword = 0x0F & (xn[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 );
}

120
lib/libpvf/zyxel.c Normal file
View File

@ -0,0 +1,120 @@
/*
* zyxel.c
*
* Converts the ZyXEL 1496 2, 3 and 4 bit voice format to the pvf format
* and the other way around. The conversion algorithm is based on the
* ZyXEL vcnvt program.
*
* The ZyXEL 2864 and the ISDN4Linux driver can also store voice data in
* this format.
*
* $Id: zyxel.c,v 1.4 1998/09/09 21:07:06 gert Exp $
*
*/
#include "../include/voice.h"
static int Mx[3][8] =
{
{0x3800, 0x5600, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
{0x399a, 0x3a9f, 0x4d14, 0x6607, 0x0000, 0x0000, 0x0000, 0x0000},
{0x3556, 0x3556, 0x399A, 0x3A9F, 0x4200, 0x4D14, 0x6607, 0x6607}
};
int zyxeltopvf (FILE *fd_in, FILE *fd_out, int nbits, pvf_header *header_out)
{
state_t s;
int a = 0;
int d = 5;
int sign;
int e;
if (nbits == 30)
nbits = 3;
if ((nbits != 2) && (nbits != 3) && (nbits != 4))
return(FAIL);
s = init_state;
while ((e = read_bits(fd_in, &s, nbits)) != EOF)
{
if ((nbits == 4) && (e == 0))
d = 4;
sign = (e >> (nbits - 1)) ? (-1) : (1);
e = e & bitmask[nbits - 1];
a = (a * 4093 + 2048) >> 12;
a += sign * ((e << 1) + 1) * d >> 1;
if (d & 1)
a++;
header_out->write_pvf_data(fd_out, a << 10);
d = (d * Mx[nbits - 2][e] + 0x2000) >> 14;
if (d < 5)
d = 5;
};
return(OK);
}
int pvftozyxel (FILE *fd_in, FILE *fd_out, int nbits, pvf_header *header_in)
{
int a = 0;
int d = 5;
state_t s;
int e;
int nmax;
int sign;
int delta;
int data_new;
s = init_state;
while (!feof(fd_in))
{
data_new = header_in->read_pvf_data(fd_in);
e = 0;
nmax = 1 << (nbits - 1);
delta = (data_new >> 10) - a;
if (delta < 0)
{
e = nmax;
delta = -delta;
};
while((--nmax) && (delta > d))
{
delta -= d;
e++;
};
if ((nbits == 4) && ((e & 0x0f) == 0))
e = 0x08;
write_bits(fd_out, &s, nbits, e);
a = (a * 4093 + 2048) >> 12;
sign = (e >> (nbits - 1)) ? (-1) : (1);
e = e & bitmask[nbits - 1];
a += sign * ((e << 1) + 1) * d >> 1;
if (d & 1)
a++;
d = (d * Mx[nbits - 2][e] + 0x2000) >> 14;
if (d < 5)
d = 5;
};
if (s.nleft > 0)
write_bits(fd_out, &s, 8 - s.nleft, 0x00);
return(OK);
}