diff --git a/lib/libpvf/au.c b/lib/libpvf/au.c new file mode 100644 index 0000000..feab170 --- /dev/null +++ b/lib/libpvf/au.c @@ -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); + } diff --git a/lib/libpvf/fft.c b/lib/libpvf/fft.c new file mode 100644 index 0000000..5d412ad --- /dev/null +++ b/lib/libpvf/fft.c @@ -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); + } diff --git a/lib/libpvf/lib.c b/lib/libpvf/lib.c new file mode 100644 index 0000000..5305b8a --- /dev/null +++ b/lib/libpvf/lib.c @@ -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}; diff --git a/lib/libpvf/linear.c b/lib/libpvf/linear.c new file mode 100644 index 0000000..aa9a895 --- /dev/null +++ b/lib/libpvf/linear.c @@ -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); + } diff --git a/lib/libpvf/multitech.c b/lib/libpvf/multitech.c new file mode 100644 index 0000000..5da01cc --- /dev/null +++ b/lib/libpvf/multitech.c @@ -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 /*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; +} diff --git a/lib/libpvf/rockwell.c b/lib/libpvf/rockwell.c new file mode 100644 index 0000000..a3c9d93 --- /dev/null +++ b/lib/libpvf/rockwell.c @@ -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 + * + * 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 [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_max_local_int16?RV_max_local_int16:(a)) +#define RV_clip_32(a) ((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_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) + { + 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 + */ + +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); +} diff --git a/lib/libpvf/usr.c b/lib/libpvf/usr.c new file mode 100644 index 0000000..8a8288f --- /dev/null +++ b/lib/libpvf/usr.c @@ -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); +} diff --git a/lib/libpvf/voc.c b/lib/libpvf/voc.c new file mode 100644 index 0000000..0a9fca7 --- /dev/null +++ b/lib/libpvf/voc.c @@ -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); + } diff --git a/lib/libpvf/wav.c b/lib/libpvf/wav.c new file mode 100644 index 0000000..41d13df --- /dev/null +++ b/lib/libpvf/wav.c @@ -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); + } + diff --git a/lib/libpvf/zyxel-o56k.c b/lib/libpvf/zyxel-o56k.c new file mode 100644 index 0000000..e887bea --- /dev/null +++ b/lib/libpvf/zyxel-o56k.c @@ -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 + * + * $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>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 ); +} + diff --git a/lib/libpvf/zyxel.c b/lib/libpvf/zyxel.c new file mode 100644 index 0000000..306525a --- /dev/null +++ b/lib/libpvf/zyxel.c @@ -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); + }