Import of libpvf
This commit is contained in:
parent
7790a48779
commit
01eeffa5e0
564
lib/libpvf/au.c
Normal file
564
lib/libpvf/au.c
Normal 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
204
lib/libpvf/fft.c
Normal 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
430
lib/libpvf/lib.c
Normal 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
82
lib/libpvf/linear.c
Normal 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
313
lib/libpvf/multitech.c
Normal 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
766
lib/libpvf/rockwell.c
Normal 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
994
lib/libpvf/usr.c
Normal 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
183
lib/libpvf/voc.c
Normal 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
574
lib/libpvf/wav.c
Normal 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
249
lib/libpvf/zyxel-o56k.c
Normal 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
120
lib/libpvf/zyxel.c
Normal 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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user