Files
fs2open.github.com/code/vcodec/codec1.cpp
T
2004-07-26 20:47:56 +00:00

1995 lines
68 KiB
C++

// Codec1.cpp
//
// Contains C-style-C++ implementation of VoiCTech (Voice Communications
// Technology) voice encoder/decoder.
//
// Written by Matthew F. Storch, Ph.D., copyright (c) 1998 Volition Inc.
#include "globalincs/pstypes.h"
#ifdef _WIN32
#include <windows.h>
#endif
#include <math.h>
#include <assert.h>
#include "vcodec/codec1.h"
//////////////////////////////////////////////////////////////////////////////
//
// Introduction to the VoiCTech encoder
// ------------------------------------
// The VoiCTech (short for Voice Communication Technology, pronounced
// "voice-tech") audio codec uses two separate algorithm suites: Codec1
// and LPC-10. Codec1 does compression without using traditional signal
// processing algorithms (expcept for a simple FIR low-pass filter). As
// a result, it is extremely fast, and yields moderate-quality voice at a
// compression ratio of between 4-to-1 and 10-to-1, assuming 11KHz sampled
// data. LPC-10 does significantly more analysis and is therefore slower
// (approximately 3 times slower than Codec1), but achieves substantially
// better compression (25-1) at a comparable quality.
//
// The externally-callable interface for both algorithm suites is through
// a simple, generic front-end that is prototyped in codec1.h.
//
// This file contains the implementions of both the generic interface and
// Codec1. LPC-10 is implemented in a set of files in the LPC10 subdirectory.
//
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// Low pass filter stuff.
//
// Number of points to convolve with. Larger number means a better but
// slower low-pass filter. Number should always be odd. Useful range is
// 7 -> 31.
const int LPF_NUM_POINTS = 11;
double Encoder_LPF_Coef[LPF_NUM_POINTS]; // convolution coefficents (weights)
// lookup table for coefficents * every possible sample value
char Encoder_LPF_CoefTimesSample[LPF_NUM_POINTS][256];
double Decoder_LPF_Coef[LPF_NUM_POINTS]; // convolution coefficents (weights)
// lookup table for coefficents * every possible sample value
char Decoder_LPF_CoefTimesSample[LPF_NUM_POINTS][256];
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// Function prototypes for functions internal to Codec1
static void InitEncoder1(int QoS);
static void Smooth1(t_Sample* bufIn, t_Sample* bufOut, int size);
static double AutoGain1(t_Sample* bufIn, t_Sample* bufOut, int size);
static void UnAutoGain1(t_Sample* bufIn, t_Sample* bufOut, int size,
double gain);
static void Decode1(t_Sample* bufIn, t_Sample* bufOut, int size, int sizeOut);
#if defined(CODEC_DEMO)
static int Encode1(t_Sample* bufIn, t_Sample* bufOut, int sizeIn, int sizeOut,
t_Sample* levels, int* modes, int samples[9], int storage[9]);
#else
static int Encode1(t_Sample* bufIn, t_Sample* bufOut, int sizeIn, int sizeOut);
#endif
static void SkipEveryOther(t_Sample* bufIn, t_Sample* bufOut, int size);
static void InterpolateEveryOther(t_Sample* bufIn, t_Sample* bufOut, int size);
#if defined(CODEC_DEMO)
static int DoEncode(int mode, BOOL& packetPos, t_Sample*& in, t_Sample*& out,
int& level, t_Sample*& levels, int*& modes,
int samples[9], int storage[9]);
#else
static int DoEncode(int mode, BOOL& packetPos, t_Sample*& in, t_Sample*& out,
int& level);
#endif
static void DecodeRL(BOOL packetPos, t_Sample*& p, t_Sample*& q,
t_Sample* bufEnd, t_Sample* bufOutEnd);
static void DecodeHF(BOOL packetPos, t_Sample*& p, t_Sample*& q,
t_Sample* bufOutEnd);
static void DecodeNom(BOOL packetPos, t_Sample*& p, t_Sample*& q,
unsigned int mode, t_Sample* bufOutEnd);
static void DecodeMF(BOOL packetPos, t_Sample*& p, t_Sample*& q,
t_Sample* bufOutEnd);
static void InitLowPassFilter(int QoS, double LPF_Coef[LPF_NUM_POINTS],
char LPF_CoefTimesSample[LPF_NUM_POINTS][256]);
static void LowPassFilter(t_Sample* bufIn, t_Sample* bufOut, int size,
char LPF_CoefTimesSample[LPF_NUM_POINTS][256]);
#if defined(USE_LPC10)
static void ConvertToLPC10(t_Sample* bufIn, t_Sample* bufOut, int size);
static void ConvertFromLPC10(t_Sample* bufIn, t_Sample* bufOut, int size);
extern "C"
{
// the following three functions are defined in lpc10\lpc10.c
void lpc10init();
int lpc10encode(unsigned char *in, unsigned char *out, int inlen);
int lpc10decode(unsigned char *in, unsigned char *out, int inlen);
}
#define AssertLPC10Available()
#else
#define ConvertToLPC10(bufIn, bufOut, size)
#define ConvertFromLPC10(bufIn, bufOut, size)
#define lpc10init()
#define lpc10encode(in, out, inlen) 0
#define lpc10decode(in, out, inlen) 0
#define AssertLPC10Available() assert(0)
#endif // defined(USE_LPC10)
//
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Generic high-level codec interface.
t_Code EncodeMode = e_cLPC10;
t_Sample* TempDecoderBuf = NULL;
t_Sample* TempEncoderBuf1 = NULL;
t_Sample* TempEncoderBuf2 = NULL;
///////////////////////////////
// The following compile-time switches are applicable to Codec1 only.
// If SKIP_EVERY_OTHER is defined, every other sample will be thrown out
// after the low-pass filter has massaged the data but before the encoder
// goes at it. The missing samples are then interpolated after decoding.
#define SKIP_EVERY_OTHER
// Don't define this ;)
//#define REMOVE_DC_BIAS
// Define this to use logarithmic gain (but linear gain works better).
//#define LOGARITHMIC_GAIN
// Define this to enable the low-pass filter.
#define USE_LOWPASS_FILTER
// End of compile-time switches
///////////////////////////////
#if defined(SKIP_EVERY_OTHER)
const int SKIP_FACTOR = 2;
#else
const int SKIP_FACTOR = 1;
#endif
void InitDecoder(int QoS, t_Sample* tempBuf)
{
QoS = QoS; // just to shut compiler up
TempDecoderBuf = tempBuf;
lpc10init(); // call unconditionally because we don't know what kind of
// coded packets we might receive
InitLowPassFilter(8, Decoder_LPF_Coef, Decoder_LPF_CoefTimesSample);
}
void Decode(t_CodeInfo* ci, t_Sample* bufIn, t_Sample* bufOut,
int encodeSize, int decodeSize)
{
if (ci->Code == e_cCodec1)
{
Decode1(bufIn, TempDecoderBuf, encodeSize, decodeSize/SKIP_FACTOR);
#if defined(SKIP_EVERY_OTHER)
UnAutoGain1(TempDecoderBuf, TempDecoderBuf, decodeSize/SKIP_FACTOR, ci->Gain);
InterpolateEveryOther(TempDecoderBuf, bufOut, decodeSize/SKIP_FACTOR);
#else
UnAutoGain1(TempDecoderBuf, bufOut, decodeSize, ci->Gain);
#endif
LowPassFilter(bufOut, bufOut, decodeSize,Decoder_LPF_CoefTimesSample);
}
else
{
AssertLPC10Available();
lpc10decode(bufIn, bufOut, encodeSize);
ConvertFromLPC10(bufOut, bufOut, decodeSize);
}
}
void InitEncoder(t_Code code, int QoS, t_Sample* tempBuf1, t_Sample* tempBuf2)
{
TempEncoderBuf1 = tempBuf1;
TempEncoderBuf2 = tempBuf2;
EncodeMode = code;
if (code == e_cCodec1)
{
InitEncoder1(QoS);
InitLowPassFilter(QoS, Encoder_LPF_Coef, Encoder_LPF_CoefTimesSample);
}
}
#if defined(CODEC_DEMO)
#define EXTRA_CODEC_DEMO_ARGS4 , levels, modes, samples, storage
int Encode(t_Sample* bufIn, t_Sample* bufOut, int sizeIn, int sizeOut,
t_CodeInfo* codeInfo,
t_Sample* levels, int* modes, int samples[9], int storage[9])
#else
#define EXTRA_CODEC_DEMO_ARGS4
int Encode(t_Sample* bufIn, t_Sample* bufOut, int sizeIn, int sizeOut,
t_CodeInfo* codeInfo)
#endif
{
int encodeSize;
codeInfo->Code = EncodeMode;
if (EncodeMode == e_cCodec1)
{
#if defined(SKIP_EVERY_OTHER)
LowPassFilter(bufIn, TempEncoderBuf1, sizeIn,
Encoder_LPF_CoefTimesSample);
SkipEveryOther(TempEncoderBuf1, TempEncoderBuf1, sizeIn);
#else
LowPassFilter(bufIn, TempEncoderBuf1, sizeIn,
Encoder_LPF_CoefTimesSample);
#endif
Smooth1(TempEncoderBuf1, TempEncoderBuf2, sizeIn/SKIP_FACTOR);
codeInfo->Gain = AutoGain1(TempEncoderBuf2, TempEncoderBuf2,
sizeIn/SKIP_FACTOR);
#if defined(CODEC_DEMO)
encodeSize = Encode1(TempEncoderBuf2, bufOut, sizeIn/SKIP_FACTOR,
sizeOut, levels, modes, samples, storage);
#else
encodeSize = Encode1(TempEncoderBuf2, bufOut, sizeIn/SKIP_FACTOR,
sizeOut);
#endif
}
else
{
AssertLPC10Available();
ConvertToLPC10(bufIn, TempEncoderBuf2, sizeIn);
encodeSize = lpc10encode(TempEncoderBuf2, bufOut, sizeIn);
}
return encodeSize;
}
//
//////////////////////////////////////////////////////////////////////////////
// ********* EVERYTHING FROM THIS POINT ON IS SPECIFIC TO CODEC1. ************
//////////////////////////////////////////////////////////////////////////////
//
// Introduction to Codec1
// ----------------------
// Codec1 uses multiple encoders, including a highly modal primary
// encoder, to achieve a substantial degree of compression while
// accurately preserving the low-to-mid frequency components of human
// voice. Higher frequency components are preserved less well, but
// usually acceptably. The encoder works best with samples taken at
// low-to-moderate microphone volumes, though an additonal logarithmic
// compression pass has been added to give better performance at high
// volumes.
//
// The interface is very simple. The externally-visible functions are:
// InitEncoder1 -- initialize the encoder
// LowPassFilter -- get rid of unwanted high frequency data
// SkipEveryOther -- throw out every other sample (optional)
// Smooth1 -- smooth little bumps so run-length encoding works better
// AutoGain1 -- reduce problematic high-volume samples
// Encode1 -- primary encoder
// ~~~~~~ transmission to remote machine ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Decode1 -- primary decoder
// UnAutoGain1 -- restore true volume
// InterpolateEveryOther -- bring back missing samples
//
// All encoder and decoder algorithms have O(N) running time with
// respect to the number of samples (otherwise it would be hopelessly
// slow). Decoding is extremely fast and should not have a
// significant impact on a modern computer. The C implementation is
// most likely fine for production code. The encoding is quite fast
// compared with other voice encoders, but it is of course substantially
// slower than the decoding. Because of the "bit twiddling" nature of
// the algorithms, I would expect that a good assembly language
// implementation could easily provide 2X-3X the execution time performance
// of this C implementation. As of 12/97, I have implemented one key
// routine in assembly that increased performance substantially.
//
// The encoded format is novel and somewhat complex because of the
// desire for very high degrees of compression (4:1 to 10:1, nominally
// 8:1) with respectable sound quality. In order to achieve such
// extraordinarily high compression rates, the exact format is
// (unfortunately) specific to 8-bit samples. Ideas very similar to
// those used for the 8-bit codec could be used to develop 16 or
// 24-bit codecs, but the details would differ enough to require a
// mostly new implementation.
//
// A sequence of samples in the original data is encoded as a
// "packet". A single packet may describe anywhere from a few samples
// to several thousand samples depending on the type of packet. There
// are currently 4 major categories of packets: nominal packets,
// run-length packets, high-frequency packets, and medium-frequency
// packets. An additional category, low-frequency packets, may be
// added later.
//
// The centerpiece of the encoding strategy is the category of nominal
// packets. Nominal packets encode 9 contiguous 8-bit samples in 12
// bits, for a compression ratio of 6:1 and very high fidelity. The
// encoded value of each sample is relative to the value of the
// previous sample, so this technique can only be effectively applied
// when contiguous samples have values that are not too different
// (i.e. for samples that contain only low-to-medium frequency
// components). Detailed examination of actual voice samples revealed
// that monotonic upward (or downward) runs of several samples were
// common, so I decided that the encoding strategy would be as
// follows. Each bit in a nominal packet corresponds to one sample,
// and the value of the bit determines what offset is added to the
// previous sample value to define the current sample value. The
// actual offset values depend on the mode of the packet. For
// example, in a mode 3 packet, a data bit of 0 means the offset is
// +1, while a data bit of 1 means the offset is +3. On the other
// hand, in a mode 5 packet, a data bit of 0 means the offset is -1
// and a data bit of 1 means the offset is +1.
//
// The 12-bit packet size presents some challenges given that the
// natural data sizes of 80x86 processors are 8, 16, and 32 bits.
// However, the challenge is justified because none of the natural
// data sizes provided encoding opportunites as good. The
// alternatives to the scheme I chose were to either encode 6 samples
// in 8 bits, or 12 samples in 16 bits. 6 samples in 8 bits leaves
// only 2 bits to assign the mode, which is inadequate. 12 bits in 16
// samples leaves plenty of mode bits (4) but then we are committed to
// the same mode for 12 bits, which I felt is too long for the voice
// data I examined. So I chose the 9-in-12 "sweet spot" granularity
// as the basis for the encoding strategy.
//
// The 12-bit packets are encoded in pairs; each pair occupies 3
// bytes. If all packets were nominal packets, a single 3-byte
// "packet pair" data structure could be used, and such a structure is
// in fact used when nominal packets are back-to-back. However, all
// the samples in a typical voice sample buffer cannot be encoded
// using nominal packets, so other encoding schemes are used. The
// 3 mode bits available in the 9-in-12 scheme allow us 8 modes.
// Modes 1 through 6 are used for nominal packets, while mode 0 is an
// escape to either run-length or high-frequency mode, and mode 7 is an
// escape to medium-frequency mode.
//
// The data structures used for run-length (RL), high-frequency (HF),
// and medium-frequency (MF) modes are different depending on whether
// the previous packet is the first packet in a nominal packet pair
// (case 0), or is any of the following (case 1):
// - the second packet in a nominal packet pair
// - a run-length, high-frequency, or medium-frequency packet
// - the first packet in the buffer
// In case 0, the RL, HF, or MF packet is 12 bits, while in case 1 it
// is 16 bits (that is, we don't use 12 bit encoding for these modes
// unless we are forced to, because 16 bit packets are better all the
// way around for RL, HF, and MF modes. Both the encoder and decoder
// maintain knowlege of case 0 vs. case 1 as a state variable called
// "packetPos".
//
// The only identical-value runs that occur frequently enough to be
// worth worrying about have a value of 0 (silence), so run length
// encoding uses 0 as the implicit data value.
//
// In high-frequency mode each sample is *not* relative to the
// previous sample (as it is in the nominal modes). Instead it is an
// absolute value that is multiplied by a multiplier that is part of
// the HF packet.
//
// In medium-frequency mode, a sequence of 4 samples in the original
// data is approximated by a straight line. This mode can also encode
// low-frequency data reasonably well, but not as well as the nominal
// modes, so the latter are used where possible. However, medium
// frequency mode can be used when the total rise (or fall) of 4
// samples is up to 32, whereas the best total rise (or fall) the
// nominal modes can achieve over 4 samples is 3*4 = 12.
//
// Finally, there is a special "literal" mode that is used only for
// the very first sample in the buffer (which is implicitly literal
// mode) and the last few samples (signaled by a special "run length"
// packet that has a length of 0). It adds a very slight inefficiency
// (since it does no compression) but it greatly simplifies dealing
// with the edge cases, which would otherwise be problematic.
//
//////////////////////////////////////////////////////////////////////////////
//
// Some details of the encoding scheme are summarized below:
//
// Encoding Modes
// --------------
// Mode number Description
// 0 run-length or high-frequency mode
// 1 0, +1
// 2 0, -1
// 3 +1, +3
// 4 -1, -3
// 5 +1, -1
// 6 +2, -2
// 7 medium-frequency mode
//
// Packet Layout
// -------------
// Run-length/high-frequency in first packet of packet pair (case 1)
// 0000 nnnn nnnn nnnn -- run length, n = length of run (1st nibble low-order)
// 0001 mmmm xxxx yyyy -- high frequency, m = multiplier
// x, y and succeeding nibbles until 0 byte
// = absolute sample values
//
// Run-length/high-frequency in second packet of packet pair (case 0)
// ---- ---- ---- 0000 nnnn nnnn -- run length, n = length of run
// ---- ---- ---- 0001 mmmm xxxx -- high frequency, m = multiplier
// x, y and succeeding nibbles until 0 byte
// = absolute sample values
//
// mmmm = 0 is not a useful value so we can use it as an escape indicator for
// other modes
//
//////////////////////////////////////////////////////////////////////////////
//
// Misc notes
//
// CODEC_DEMO
// ----
// CODEC_DEMO is a compile-time flag that adds/alters code for use with the demo/
// experiment program. If CODEC_DEMO is not defined, the codec routines will be
// compiled for general-purpose use outside of the demo/experiment program.
// (Like in a GAME maybe...what a NOVEL concept!)
//
// Packing Problems
// ----------------
// Even with #pragma pack(1), sizeof returns 4 for 3 byte structures. In
// some places in the code, structure sizes had to be hardwired to get around
// this problem.
///////////////////////////////////////////////////////////////////////////////
// Data structures
// Not sure if there is a point in using unions somehow to make one big struct;
// for now just forget it and cast to totally different structs which is
// theoretically evil but is not really all that dangerous in practice...
// disable compiler padding of structures
#if defined _MSC_VER
#pragma pack(push, packet_declarations)
#endif
#pragma pack(1)
// most general notion of a packet pair
struct t_PacketPair
{
unsigned long Mode1 : 3;
unsigned long Mode1Ex : 1;
unsigned long Data1 : 8;
unsigned long Mode0 : 3;
unsigned long Mode0Ex : 1;
unsigned long Data0 : 8;
};
// nominal packet pair
struct t_PacketPairNom
{
unsigned long Mode1 : 3;
unsigned long Data1 : 9;
unsigned long Mode0 : 3;
unsigned long Data0 : 9;
};
// run-length packet, case 1
struct t_PacketRL1
{
unsigned short Mode : 3;
unsigned short ModeEx : 1; // extra mode bit to distinguish RL & HF
unsigned short Length : 12;
};
// high-frequency packet, case 1
struct t_PacketHF1
{
unsigned short Mode : 3;
unsigned short ModeEx : 1; // extra mode bit to distinguish RL & HF
unsigned short Table : 3; // lookup table number
unsigned short Data2 : 3; // absolute sample data
unsigned short Data1 : 3; // absolute sample data
unsigned short Data0 : 3; // absolute sample data
};
// high-frequency data packet, only used immediately after an HF packet, or
// after another HF data packet
struct t_PacketHFData
{
unsigned short Table : 1; // 1 ==> DataT is lookup table number
unsigned short Data3 : 3; // absolute sample data
unsigned short Data2 : 3; // absolute sample data
unsigned short Data1 : 3; // absolute sample data
unsigned short Data0 : 3; // absolute sample data
unsigned short DataT : 3; // absolute sample data or lookup table number
};
// run-length packet, case 0
struct t_PacketRL0
{
unsigned long Mode1 : 3; // mode of previous packet in pair
unsigned long Data1 : 9; // data of previous packet in pair
unsigned long Mode0 : 3; // mode of this packet (always 0)
unsigned long Mode0Ex : 1; // extra bit to distinguish RL & HF (always 0)
unsigned long Length : 8; // length of run
};
// high-frequency packet, case 0
struct t_PacketHF0
{
unsigned long Mode1 : 3; // mode of previous packet in pair
unsigned long Data1 : 9; // data of previous packet in pair
unsigned long Mode0 : 3; // mode of this packet (always 0)
unsigned long Mode0Ex : 1; // extra bit to distinguish RL & HF (always 1)
unsigned long Table : 1; // 1 ==> DataT is lookup table number
unsigned long Data0 : 3; // absolute sample data
unsigned long DataT : 3; // absolute sample data or lookup table number
unsigned long Unused : 1;
};
// medium-frequency packet, case 1
struct t_PacketMF1
{
unsigned short Mode : 3; // mode of this packet (always 7)
unsigned short Mult : 1; // 0 ==> mult data by 1, 1 ==> mult data by 2
short Data1 : 6; // total rise or fall over current 4 samples
short Data0 : 6; // total rise or fall over next 4 samples
};
// medium-frequency packet, case 0
struct t_PacketMF0
{
unsigned long Mode1 : 3; // mode of previous packet in pair
unsigned long Data1 : 9; // data of previous packet in pair
unsigned long Mode0 : 3; // mode of this packet (always 7)
unsigned long Mult : 1; // 0 ==> mult data by 1, 1 ==> mult data by 2
long DataX : 2; // not currently used
long Data0 : 6; // total rise or fall over next 4 samples
};
// restore state of compiler padding of structures
#if defined _MSC_VER
#pragma pack(pop, packet_declarations)
#elif defined __GNUC__
#pragma pack()
#else
#error unknown compiler
#endif
////////////////////////////////////////////////////////////////////////////////
// Constants, enums, and data tables
//
// These are not all delclared "const", but all are constant after the code is
// initialized.
// For the second packet of a packet pair, the breakeven point for run length
// vs. normal encoding is 9 bits, because in that case we are using exactly
// the packet in both cases (we resolve the tie in favor of RL because it's
// encoding and decoding is faster). For the first packet, RL uses a minimum
// of 16 bits whereas normal encoding commits us to 12 bits, so the breakeven
// point is with somewhat longer runs. Using the compression ratio as the
// figure of merit, breakeven is at x/16 = 9/12, or x = 12 bits.
const int MIN_RUN_LEN[2] = { 9, 12 };
const int ZERO = 128; // the value of silence ;)
// some quality-of-service tuning parameters
int BIG_MOVE2; // useful values 4..32
int FUDGE; // useful values 1..5
// some tuning parameters to help decide which mode to use when
const int BIG_MOVE = 8;
const int SMALL_MOVE1 = 2;
const int SMALL_MOVE2 = 2;
const int SMALL_MOVE3 = 2;
// enum DecodeState { e_dsFirst, e_dsSecond };
enum EncodeMode
{
e_emRL_HF = 0, // run-length/high-frequency
e_emZ_P1 = 1, // nominal: 0 or +1
e_em0_N1 = 2, // nominal: 0 or -1
e_emP1_P3 = 3, // nominal: +1 or +3
e_emN1_N3 = 4, // nominal: -1 or -3
e_emP1_N1 = 5, // nominal: +1 or -1
e_emP2_N2 = 6, // nominal: +2 or -2
e_emMF = 7 // medium-frequency
};
// offsets for modes 1 through 6 (bogus first entry)
const int Deltas[7][2] =
{
{ 0, 0 },
{ 0, +1 },
{ -1, 0 },
{ +1, +3 },
{ -3, -1 },
{ -1, +1 },
{ -2, +2 }
};
double Log2[256]; // lookup table for log-base-2 (i.e. log(x) / log(2))
// HF encoding table
#if 0
unsigned int EncTable[8][256] =
{
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
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, 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
},
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 3, 3, 3, 3, 3, 3, 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,
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
},
};
#else
unsigned int EncTable[8][256];
#endif
// HF decoding table
int DecTable[8][8] =
{
{ -12, -6, -3, -1, 1, 3, 6, 12 },
{ -24, -12, -6, -3, 3, 6, 12, 24 },
{ -36, -18, -9, -5, 5, 9, 18, 36 },
{ -48, -24, -12, -6, 6, 12, 24, 48 },
{ -60, -30, -15, -7, 7, 15, 30, 60 },
{ -72, -36, -18, -9, 9, 18, 36, 72 },
{ -84, -42, -21, -10, 10, 21, 42, 84 },
{ -96, -48, -24, -12, 12, 24, 48, 96 }
};
///////////////////////////////////////////////////////////////////////////////
// Decoder
// Decode1 is the main decoder entry point.
// Input:
// bufIn - encoded data to decode
// bufOut - empty buffer in which to place decoded data
// size - size of bufIn in bytes
// sizeOut - size of bufOut in bytes
// Output:
// bufOut - decoded data written here
// returns - nothing
//
static void Decode1(t_Sample* bufIn, t_Sample* bufOut, int size, int sizeOut)
{
unsigned int mode, modeEx;
BOOL packetPos = 1; // 1 = first packet of packet pair, 0 = second
t_Sample* bufEnd = bufIn + size - 1;
t_Sample* bufOutEnd = bufOut + sizeOut - 1;
t_Sample* p = bufIn; // current position in input buffer
t_Sample* q = bufOut; // current position in output buffer
*q++ = *p++; // first sample is in literal mode
// Main decoding loop. Look at the mode of the packet and call the
// appropriate decoder.
while (p <= bufEnd && q <= bufOutEnd)
{
if (packetPos)
{
mode = ((t_PacketPair*)p)->Mode1;
modeEx = ((t_PacketPair*)p)->Mode1Ex;
}
else
{
mode = ((t_PacketPair*)p)->Mode0;
modeEx = ((t_PacketPair*)p)->Mode0Ex;
}
switch (mode)
{
case e_emRL_HF:
if (modeEx) // modeEx differentiates HF vs RL modes
DecodeHF(packetPos, p, q, bufOutEnd);
else
DecodeRL(packetPos, p, q, bufEnd, bufOutEnd);
packetPos = 1;
break;
case e_emZ_P1: case e_em0_N1: case e_emP1_P3: case e_emN1_N3:
case e_emP1_N1: case e_emP2_N2:
DecodeNom(packetPos, p, q, mode, bufOutEnd);
packetPos = !packetPos;
break;
case e_emMF:
DecodeMF(packetPos, p, q, bufOutEnd);
packetPos = 1;
break;
}
}
//assert(p == bufEnd && q == bufOutEnd);
#if defined(CODEC_DEMO)
// Do some extra error checking in CODEC_DEMO mode.
if (abs(bufEnd - p) > 1 || abs(bufOutEnd - q) > 1)
{
int leftIn = bufEnd - p,
leftOut = bufOutEnd - q;
char str[80];
sprintf(str, "%d bytes left in source, %d bytes left in dest",
leftIn, leftOut);
AfxMessageBox(str);
}
#endif
}
// Run-length decoder. Very straightforward.
static void DecodeRL(BOOL packetPos, t_Sample*& p, t_Sample*& q,
t_Sample* bufEnd, t_Sample* bufOutEnd)
{
int len;
if (packetPos)
{
len = ((t_PacketRL1*)p)->Length;
p += sizeof(t_PacketRL1);
}
else
{
len = ((t_PacketRL0*)p)->Length;
p += 3; //sizeof(t_PacketRL0); // see "Packing Problems" comment above
}
// q + len-1 is where the last sample in this run will be written
if (q + len-1 > bufOutEnd)
{
q = bufOutEnd + 1; // cause decoder to immediately abort
return;
}
for (int i = 0; i < len; i++)
*q++ = ZERO;
if (len == 0) // check for literal-mode marker near end of data
{
// remaining data is in literal mode
for (int remaining = *p++; remaining > 0 && q<=bufOutEnd; remaining--)
*q++ = *p++;
// assert(p == bufEnd+1);
}
}
// High-frequency decoder. Each sample is the data value for that sample
// multiplied by the current multiplier.
static void DecodeHF(BOOL packetPos, t_Sample*& p, t_Sample*& q,
t_Sample* bufOutEnd)
{
static unsigned int table = 1;
int data;
// t_Sample* pstart = p;
if (q > bufOutEnd - 3)
{
q = bufOutEnd + 1; // cause decoder to immediately abort
return;
}
if (packetPos)
{
table = ((t_PacketHF1*)p)->Table;
data = ((t_PacketHF1*)p)->Data2;
*q++ = t_Sample(DecTable[table][data] + ZERO);
data = ((t_PacketHF1*)p)->Data1;
*q++ = t_Sample(DecTable[table][data] + ZERO);
data = ((t_PacketHF1*)p)->Data0;
*q++ = t_Sample(DecTable[table][data] + ZERO);
p += sizeof(t_PacketHF1);
}
else
{
if (((t_PacketHF0*)p)->Table == 1)
table = ((t_PacketHF0*)p)->DataT;
data = ((t_PacketHF0*)p)->Data0;
*q++ = t_Sample(DecTable[table][data] + ZERO);
if (((t_PacketHF0*)p)->Table == 0)
{
data = ((t_PacketHF0*)p)->DataT;
*q++ = t_Sample(DecTable[table][data] + ZERO);
}
p += 3; //sizeof(t_PacketHF0); // see "Packing Problems" comment above
}
while (*(char*)p != 0)
{
if (q > bufOutEnd - 5)
{
q = bufOutEnd + 1; // cause decoder to immediately abort
return;
}
if (((t_PacketHFData*)p)->Table == 1)
table = ((t_PacketHFData*)p)->DataT;
data = ((t_PacketHFData*)p)->Data3;
*q++ = t_Sample(DecTable[table][data] + ZERO);
data = ((t_PacketHFData*)p)->Data2;
*q++ = t_Sample(DecTable[table][data] + ZERO);
data = ((t_PacketHFData*)p)->Data1;
*q++ = t_Sample(DecTable[table][data] + ZERO);
data = ((t_PacketHFData*)p)->Data0;
*q++ = t_Sample(DecTable[table][data] + ZERO);
if (((t_PacketHFData*)p)->Table == 0)
{
data = ((t_PacketHFData*)p)->DataT;
*q++ = t_Sample(DecTable[table][data] + ZERO);
}
p += sizeof(t_PacketHFData);
}
p += sizeof(char); //sizeof(t_PacketHFData); // to cover final "0" packet
}
// Nominal packet decoder. Each sample is equal to the previous sample
// +/- an offset.
static void DecodeNom(BOOL packetPos, t_Sample*& p, t_Sample*& q,
unsigned int mode, t_Sample* bufOutEnd)
{
unsigned int data;
t_Sample delta0 = t_Sample(Deltas[mode][0]), // offset for "0" bits
delta1 = t_Sample(Deltas[mode][1]), // offset for "1" bits
last = *(q-1);
if (q > bufOutEnd - 9)
{
q = bufOutEnd + 1;
return;
}
if (packetPos)
{
data = ((t_PacketPairNom*)p)->Data1;
}
else
{
data = ((t_PacketPairNom*)p)->Data0;
p += 3; // sizeof(t_PacketPairNom);
}
for (int i = 0; i < 9; i++)
{
if (data & 0x100)
{
last = t_Sample(last + delta1); // using += causes level 4 warning
*q++ = last;
}
else
{
last = t_Sample(last + delta0); // using += causes level 4 warning
*q++ = last;
}
data <<= 1;
}
}
// Medium-frequency decoder. Uses a straight line to approximate 4
// consecutive samples.
static void DecodeMF(BOOL packetPos, t_Sample*& p, t_Sample*& q,
t_Sample* bufOutEnd)
{
unsigned int mult, data;
t_Sample level = *(q-1);
if (q > bufOutEnd - 4)
{
q = bufOutEnd + 1;
return;
}
if (packetPos)
{
mult = ((t_PacketMF1*)p)->Mult; // currently unused
data = ((t_PacketMF1*)p)->Data1;
// Make each of the 4 points is computed in a way equivalent to that
// used in the encoder.
level = t_Sample(level + data / 4); *q++ = level;
data -= data / 4; level = t_Sample(level + data / 3); *q++ = level;
data -= data / 3; level = t_Sample(level + data / 2); *q++ = level;
data -= data / 2; level = t_Sample(level + data ); *q++ = level;
data = ((t_PacketMF1*)p)->Data0;
p += sizeof(t_PacketMF1);
}
else
{
mult = ((t_PacketMF0*)p)->Mult; // currently unused
data = ((t_PacketMF0*)p)->Data0;
p += sizeof(t_PacketMF0);
}
level = t_Sample(level + data / 4); *q++ = level;
data -= data / 4; level = t_Sample(level + data / 3); *q++ = level;
data -= data / 3; level = t_Sample(level + data / 2); *q++ = level;
data -= data / 2; level = t_Sample(level + data ); *q++ = level;
}
///////////////////////////////////////////////////////////////////////////////
// Encoder
int QualityOfService;
// InitEncoder1 must be called once to initialize the encoder. May safely be
// called again to change tuning parameters.
// Input:
// QoS - Quality of Service: 1..10, 1 = highest compression/lowest
// quality, 10 = worst compression/best quality
// Output:
// initializes global variables
//
static void InitEncoder1(int QoS)
{
int i, j, table, in, ip, vn, vp;
QualityOfService = QoS;
// There is nothing magic about the folowing 2 formulas, they're just one
// way to translate from a semantic notion of "quality of service" to
// reasonable values of the tuning parameters. Each parameter is
// orthogonal to the others, so the only reason to tie them to a single
// input parameter is simplicity.
BIG_MOVE2 = 8 + (10 - QoS) * 2;
FUDGE = (12 - QoS) / 2;
// initilize log-base-2 lookup table
for (i = 1; i < 256; i++)
Log2[i] = log((double)i) / log(2.0);
for (table = 7; table >= 0; table--)
{
vn = 3, vp = 4;
in = ZERO-1, ip = ZERO;
for (i = 0; i < 16 * (table+1)/8.0; i++)
EncTable[table][in--] = vn, EncTable[table][ip++] = vp;
for (j = 1; j < 4; j++)
{
vn--, vp++;
for (i = 0; i < pow(2.0, 3.0+j) * (table+1)/8.0; i++)
EncTable[table][in--] = vn, EncTable[table][ip++] = vp;
}
while (in >= 0)
EncTable[table][in--] = vn, EncTable[table][ip++] = vp;
}
table = 0;
#if 0
for (table = 0; table < 8; table++)
for (i = 0; i < 256; i++)
{
s = abs(i - ZERO);
if (s < 128*(table+1)/8.0)
EncTable[table][i] = (unsigned int)log(s*8.0/(table+1))
/ log(2.0);
else
EncTable[table][i] = (unsigned int)log(128*8.0/(table+1));
}
for (table = 0; table < 8; table++)
for (i = 0; i < 8; i++)
DecTable[table][i] = pow(2.0, i);
#endif
}
// Encode1 is the main Encoder entry point.
// Input:
// bufIn - encoded data to decode
// bufOut - empty buffer in which to place decoded data
// size - size of bufIn in bytes
// sizeOut - size of bufOut in bytes
// Output:
// bufOut - encoded data written here
// returns - number of bytes written to bufOut
//
#if defined(CODEC_DEMO)
#define EXTRA_CODEC_DEMO_ARGS4 , levels, modes, samples, storage
static int Encode1(t_Sample* bufIn, t_Sample* bufOut, int size, int sizeOut,
t_Sample* levels, int* modes, int samples[9], int storage[9])
#else
#define EXTRA_CODEC_DEMO_ARGS4
static int Encode1(t_Sample* bufIn, t_Sample* bufOut, int size, int sizeOut)
#endif
{
const int CUTOFF = 4;
t_Sample *in = bufIn, // current position in input buffer
*out = bufOut, // current position in output buffer
*p, // temporary pointer
*end = bufIn + size, // end of input buffer
*endOut = bufOut + sizeOut; // end of output buffer
int i, // loop counter
level, // current "true" sample value
s0, // first of two samples
s1, // second of two samples
smin, smax, // min and max of current set of samples
hist[CUTOFF*2 + 1], // histogram of sample-to-sample deltas
upMove, downMove; // total up/down deltas
BOOL packetPos = 1; // 1 = first packet of packet pair, 0 = second
int tableNum = 0;
assert(sizeof(t_PacketRL1) == 2);
assert(sizeof(t_PacketHF1) == 2);
assert(sizeof(t_PacketHFData) == 2);
assert(sizeof(t_PacketHF0) == 4);
// First byte in encoded data is unencoded (literal mode) initial level.
level = *out++ = *in++;
#if defined(CODEC_DEMO)
// Remove *levels++ to make level 1 pixel offset.
*levels++ = t_Sample(level);
// For statistics, pretend that literal mode is high-frequency mode.
*modes++ = 8;
samples[8] += 1; storage[8] += 2*sizeof(t_Sample);
#endif
// Main encoding loop. General strategy is to compute a histogram and
// some other stats about the next 9 samples, then decide which is the
// best encoding mode (that's the hard part), and finally encode the
// data. Note that if we decide on run-length or high-frequency mode,
// we may encode more than 9 samples before going around this loop
// again.
while (in < end && out < endOut)
{
// Use current level rather than last sample for histogram etc.
// because level is where we are really starting from. In the case
// where we are starting with significant error (e.g. just coming out
// of HF mode), we may very well pick a different mode starting from
// level than we would starting from the last sample.
//smin = smax = s0 = *(in-1);
smin = smax = s0 = level;
//int sstart = (s0 + *in) / 2,
// send = (in[7] + in[8]) / 2;
// Compute histogram.
// Do 9 bits at a time, ensuring we don't go off end of bufIn.
for (i = 0; i < CUTOFF*2 + 1; i++)
hist[i] = 0;
upMove = downMove = 0;
for (i = 0; i < 9 && in+i < end; i++)
{
s1 = in[i];
if (s1 < smin) smin = s1;
if (s1 > smax) smax = s1;
int diff = s1 - s0;
if (diff < 0) downMove -= diff;
if (diff > 0) upMove += diff;
if (diff > CUTOFF) diff = CUTOFF;
if (diff < -CUTOFF) diff = -CUTOFF;
hist[diff+CUTOFF]++;
s0 = s1;
}
// it's faster to do this in the loop above...
//for (i = 0, downMove = 0; i < CUTOFF; i++)
// downMove += hist[i] * (CUTOFF-i);
//for (i = CUTOFF+1, upMove = 0; i < CUTOFF*2 + 1; i++)
// upMove += hist[i] * (i-CUTOFF);
short delta = short(in[3] - level); // for medium-frequency mode
// First check if we need to switch to literal mode.
if (in+9 >= end)
{
// We are near the end of the data. Put marker indicating
// the final mode switch to literal mode. The marker is a
// run-length mode packet of length 0. Literal mode
// encoding is a single byte indicating the number of
// literal mode samples followed by the original samples.
if (packetPos)
{
t_PacketRL1 packet;
packet.Mode = e_emRL_HF;
packet.ModeEx = 0;
packet.Length = 0;
*(t_PacketRL1*)out = packet;
out += sizeof(t_PacketRL1);
}
else
{
t_PacketRL0 packet = *(t_PacketRL0*)out;
packet.Mode0 = e_emRL_HF;
packet.Mode0Ex = 0;
packet.Length = 0;
*(t_PacketRL0*)out = packet;
out += 3; //sizeof(t_PacketRL0); // see "Packing Problems"
}
i = *out++ = t_Sample(end - in);
#if defined(CODEC_DEMO)
int remaining = i;
while (i-- > 0)
*levels++ = *out++ = *in++;
// For statistical purposes, pretend that literal mode is high-
// frequency mode since high frequency mode is the most similar
// in terms of bits per sample. In any case, literal mode will
// have very little impact on any of the statistics.
for (i = remaining; i > 0; i--) *modes++ = 8;
samples[8] += remaining; storage[8] += 2*remaining;
#else
while (i-- > 0)
*out++ = *in++;
#endif
}
// Check if low frequency mode applies; if so, use it because it
// is the most efficient.
else if (upMove <= SMALL_MOVE3 && downMove <= SMALL_MOVE3)
{ // low frequency mode
BOOL runLengthMode = FALSE;
if (hist[CUTOFF] == 9)
{ // possible 0-run starting; check ahead
// packet1 can handle runs of 2^12-1 = 4095 samples
// packet0 can handle runs of 2^8-1 = 255 samples
int len, maxLen = packetPos ? 4095 : 255;
for (p = &in[9], len = 9; p < end && len < maxLen; p++, len++)
if (*p != ZERO)
break;
if (len > MIN_RUN_LEN[packetPos])
{ // encode region in-to-p as run-length
runLengthMode = TRUE;
if (packetPos)
{
t_PacketRL1 packet;
packet.Mode = e_emRL_HF;
packet.ModeEx = 0;
packet.Length = (unsigned short)len;
*(t_PacketRL1*)out = packet;
out += sizeof packet;
// packetPos remains at 1
#if defined(CODEC_DEMO)
samples[0] += len; storage[0] += 2*sizeof packet;
#endif
}
else
{
t_PacketRL0 packet = *(t_PacketRL0*)out;
packet.Mode0 = e_emRL_HF;
packet.Mode0Ex = 0;
packet.Length = len;
*(t_PacketRL0*)out = packet;
out += 3; // sizeof packet;
packetPos = 1;
#if defined(CODEC_DEMO)
samples[0] += len; storage[0] += 3; // sizeof packet;
#endif
}
in += len;
#if defined(CODEC_DEMO)
for (int j = 0; j < len; j++)
*levels++ = ZERO, *modes++ = 0;
#endif
}
}
if (!runLengthMode)
{ // normal low-frequency mode
//!!! Need to implement a low-frequency mode for real. In the
// meantime, use the appropriate nominal mode in its place.
if (upMove > 0 && downMove > 0)
{
out += DoEncode(e_emP1_N1, packetPos, in, out, level
EXTRA_CODEC_DEMO_ARGS4);
}
else if (upMove > 0)
{
out += DoEncode(e_emZ_P1, packetPos, in, out, level
EXTRA_CODEC_DEMO_ARGS4);
}
else
{
out += DoEncode(e_em0_N1, packetPos, in, out, level
EXTRA_CODEC_DEMO_ARGS4);
}
}
}
// Check if nominal modes apply; if so, use one of them.
// Given 9 bits with a maximum per-bit delta of 3, we can keep up
// with an end-to-end delta of 3 * 9 in nominal mode, plus we add
// in a little fudge factor because we would really rather avoid
// medium or high-frequency modes if possible.
//else if (hist[CUTOFF*2] <= 2 && hist[0] <= 2 &&
// upMove < 3 * 9 + 5 && downMove < 3 * 9 + 5)
else if (hist[CUTOFF*2] + hist[0] <= 2 &&
upMove < 3 * 9 + FUDGE && downMove < 3 * 9 + FUDGE)
{
if (upMove >= BIG_MOVE)
{ // lot of up movement, use mode 3 or 6
// mode 3 unless nontrivial down move
if (downMove <= SMALL_MOVE1)
out += DoEncode(e_emP1_P3, packetPos, in, out, level
EXTRA_CODEC_DEMO_ARGS4);
else
out += DoEncode(e_emP2_N2, packetPos, in, out,
level EXTRA_CODEC_DEMO_ARGS4);
}
else if (downMove >= BIG_MOVE)
{ // lot of down movement, use mode 4 or 6
if (upMove <= SMALL_MOVE1) // mode 4 unless nontrivial up move
out += DoEncode(e_emN1_N3, packetPos, in, out, level
EXTRA_CODEC_DEMO_ARGS4);
else
out += DoEncode(e_emP2_N2, packetPos, in, out, level
EXTRA_CODEC_DEMO_ARGS4);
}
else if (upMove >= SMALL_MOVE2 && downMove >= SMALL_MOVE2)
{
out += DoEncode(e_emP1_N1, packetPos, in, out, level
EXTRA_CODEC_DEMO_ARGS4);
}
else if (upMove >= SMALL_MOVE2)
{
out += DoEncode(e_emZ_P1, packetPos, in, out, level
EXTRA_CODEC_DEMO_ARGS4);
}
else
{
out += DoEncode(e_em0_N1, packetPos, in, out, level
EXTRA_CODEC_DEMO_ARGS4);
}
}
// Last chance to avoid dreaded high-frequency mode. In medium-
// frequency mode, a sequence of 4 samples is encoded as a straight-
// line approximation. There are 6 bits available to encode the
// total rise/fall of the line, so we can track a total rise of
// +(2^5-1) = +31 and a total fall of -(2^5) = -32.
//else if (delta <= 31 && delta >= -32)
else if ((upMove < BIG_MOVE2 || downMove < BIG_MOVE2)
&& delta <= 31 && delta >= -32)
{ // medium frequency mode
#if defined(CODEC_DEMO)
int temp1;
#endif
int temp2;
in += 4;
if (packetPos)
{
t_PacketMF1 packet;
packet.Mode = e_emMF;
packet.Mult = 0; //!!! should implement .Mult
packet.Data1 = delta;
temp2 = level;
level += packet.Data1;
delta = short(in[3] - level);
if (delta > 31) packet.Data0 = 31;
else if (delta < -32) packet.Data0 = -32;
else packet.Data0 = delta;
*(t_PacketMF1*)out = packet;
out += sizeof packet;
in += 4;
#if defined(CODEC_DEMO)
temp1 = packet.Data1; *levels++ = t_Sample(temp2 += temp1 / 4);
temp1 -= temp1 / 4; *levels++ = t_Sample(temp2 += temp1 / 3);
temp1 -= temp1 / 3; *levels++ = t_Sample(temp2 += temp1 / 2);
temp1 -= temp1 / 2; *levels++ = t_Sample(temp2 += temp1);
assert(temp2 == level);
temp1 = packet.Data0; *levels++ = t_Sample(temp2 += temp1 / 4);
temp1 -= temp1 / 4; *levels++ = t_Sample(temp2 += temp1 / 3);
temp1 -= temp1 / 3; *levels++ = t_Sample(temp2 += temp1 / 2);
temp1 -= temp1 / 2; *levels++ = t_Sample(temp2 += temp1);
assert(temp2 == level + packet.Data0);
for (i = 0; i < 8; i++) *modes++ = 7;
samples[7] += 8; storage[7] += 2*sizeof packet;
#endif
level += packet.Data0;
}
else
{
t_PacketMF0 packet = *(t_PacketMF0*)out;
packet.Mode0 = e_emMF;
packet.DataX = 0;
packet.Data0 = delta;
*(t_PacketMF0*)out = packet;
out += sizeof packet;
#if defined(CODEC_DEMO)
temp2 = level;
temp1 = packet.Data0; *levels++ = t_Sample(temp2 += temp1 / 4);
temp1 -= temp1 / 4; *levels++ = t_Sample(temp2 += temp1 / 3);
temp1 -= temp1 / 3; *levels++ = t_Sample(temp2 += temp1 / 2);
temp1 -= temp1 / 2; *levels++ = t_Sample(temp2 += temp1);
assert(temp2 == level + packet.Data0);
for (i = 0; i < 4; i++) *modes++ = 7;
samples[7] += 4; storage[7] += 3; // sizeof packet;
#endif
level += packet.Data0;
}
packetPos = 1;
}
else // No choice but to use bit-gobbling high frequency mode.
{ // high frequency mode
// need abs because smax is not guaranteed to be > ZERO and smin
// is not guaranteed to be < ZERO
int temp1 = abs((smax - ZERO) / 16),
temp2 = abs((ZERO - smin - 1) / 16);
// "table" is our new preferred table
int table = temp1 > temp2 ? temp1 : temp2;
int data4, data3, data2, data1, data0, datat, val3, val2, val1, val0;
int max;
int finalPacketData;
if (packetPos)
{
t_PacketHF1 packet;
packet.Mode = e_emRL_HF;
packet.ModeEx = 1;
// HF1 packets include table number unconditionally
packet.Table = (unsigned short)(tableNum = table);
packet.Data2 = (unsigned short)(EncTable[table][*in++]);
packet.Data1 = (unsigned short)(EncTable[table][*in++]);
finalPacketData =
packet.Data0 = (unsigned short)(EncTable[table][*in]);
data0 = *in++;
*(t_PacketHF1*)out = packet;
out += sizeof packet;
#if defined(CODEC_DEMO)
*levels++ = t_Sample(DecTable[table][packet.Data2] + ZERO);
*levels++ = t_Sample(DecTable[table][packet.Data1] + ZERO);
*levels++ = t_Sample(DecTable[table][packet.Data0] + ZERO);
*modes++ = 8;
*modes++ = 8;
*modes++ = 8;
samples[8] += 3; storage[8] += 2*sizeof packet;
#endif
}
else
{
t_PacketHF0 packet = *(t_PacketHF0*)out;
packet.Mode0 = e_emRL_HF;
packet.Mode0Ex = 1;
finalPacketData = packet.Data0 = EncTable[table][*in++];
// "tableNum-1" in order to introduce a little hysteresis in
// going to a smaller table. No such trick in the other
// direction since the consequences of using a table that is
// too small is worse than using one that is to large.
if (table > tableNum || table < tableNum-1)
{
packet.Table = 1;
packet.DataT = tableNum = table;
}
else // use previous table
{
packet.Table = 0;
finalPacketData = packet.DataT = EncTable[table = tableNum][*in++];
}
*(t_PacketHF0*)out = packet;
out += 3; // sizeof packet;
#if defined(CODEC_DEMO)
*levels++ = t_Sample(DecTable[table][packet.Data0] + ZERO);
*modes++ = 8;
samples[8] += 1; storage[8] += 3; // sizeof packet;
if (packet.Table == 0)
{
*levels++ = t_Sample(DecTable[table][packet.DataT] + ZERO);
*modes++ = 8;
samples[8] += 1;
}
#endif
}
t_PacketHFData packet;
for (;;)
{
if (in+5 >= end)
{
*(char*)out = 0;
out += sizeof(char);
#if defined(CODEC_DEMO)
storage[8] += 2*sizeof(char);
#endif
}
data4 = *(in-1) - ZERO;
data3 = *in++ - ZERO;
data2 = *in++ - ZERO;
data1 = *in++ - ZERO;
data0 = *in++ - ZERO;
datat = *in - ZERO;
max = abs(data3) > abs(data2) ? abs(data3) : abs(data2);
max = abs(data1) > max ? abs(data1) : max;
max = abs(data0) > max ? abs(data0) : max;
max = abs(datat) > max ? abs(datat) : max;
table = (max-1) / 16;
if (table > tableNum || table < tableNum-1)
{
packet.Table = 1;
packet.DataT = (unsigned short)(table);
}
else // use previous table
{
packet.Table = 0;
table = tableNum;
packet.DataT = (unsigned short)(EncTable[table][datat+ZERO]);
in++;
}
packet.Data3 = (unsigned short)(val3 = EncTable[table][data3+ZERO]);
packet.Data2 = (unsigned short)(val2 = EncTable[table][data2+ZERO]);
packet.Data1 = (unsigned short)(val1 = EncTable[table][data1+ZERO]);
packet.Data0 = (unsigned short)(val0 = EncTable[table][data0+ZERO]);
// break if the data is relatively smooth or if we encounter
// data (two 0's) that would cause us to encode a packet that
// looks like a HF termination byte
if ((abs(data2 - data3) + abs(data1 - data2) +
abs(data0 - data1) + abs(data3 - data4) <= 12) ||
(val3 == 0 && val2 == 0))
{
*(char*)out = 0;
out += sizeof(char);
#if defined(CODEC_DEMO)
storage[8] += 2*sizeof(char);
#endif
if (packet.Table == 1)
in -= 4;
else
in -= 5;
break;
}
// now that we know for certain that this packet, will be
// used, set persistent state to indicate the last used
// table & last encoded data value
finalPacketData = packet.Table ? packet.Data0 : packet.DataT;
tableNum = table;
*(t_PacketHFData*)out = packet;
out += sizeof packet;
#if defined(CODEC_DEMO)
*levels++ = t_Sample(DecTable[table][packet.Data3] + ZERO);
*levels++ = t_Sample(DecTable[table][packet.Data2] + ZERO);
*levels++ = t_Sample(DecTable[table][packet.Data1] + ZERO);
*levels++ = t_Sample(DecTable[table][packet.Data0] + ZERO);
*modes++ = 8; *modes++ = 8; *modes++ = 8; *modes++ = 8;
samples[8] += 4; storage[8] += 2*sizeof packet;
if (packet.Table == 0)
{
*levels++ = t_Sample(DecTable[table][packet.DataT] + ZERO);
*modes++ = 8;
samples[8] += 1;
}
#endif
}
// be sure to set level here so it stays in sync
level = DecTable[tableNum][finalPacketData] + ZERO;
packetPos = 1;
}
}
return out - bufOut;
}
/*
#if defined(CODEC_DEMO)
static int ComputeNomData(t_Sample*& in, const int deltas[], int& level,
t_Sample*& levels)
#else
static int ComputeNomData(t_Sample*& in, const int deltas[], int& level)
#endif
{
int data = 0;
for (int i = 0; i < 9; i++, in++)
{
if (level+deltas[0] < *in)
{
data = (data << 1) | 1;
level += deltas[1];
}
else
{
data <<= 1;
level += deltas[0];
}
#if defined(CODEC_DEMO)
*levels++ = t_Sample(level);
#endif
}
return data;
}
*/
// ecx i
// eax temp
// edx level
// ebx deltas[0]
// edi deltas[1]
// esi in
// ebp data
#if defined(_WIN32)
#if defined(CODEC_DEMO)
static int ComputeNomDataF(t_Sample*& inp, const int deltas[], int& level,
t_Sample*& levels)
#else
static int ComputeNomDataF(t_Sample*& inp, const int deltas[], int& level)
#endif
{
int data;
#if defined _MSC_VER
__asm
{
push ebp
mov esi, [inp] // esi = &inp
mov esi, [esi] // esi = inp
mov ebx, deltas[0] // ebx = &deltas[0]
mov edi, [ebx+4] // edi = deltas[1]
mov ebx, [ebx] // ebx = deltas[0]
mov edx, [level] // edx = &level
mov edx, [edx] // edx = level
mov ebp, 0
mov eax, edx // temp = level
mov ecx, 9 // loop count = 9
DO_9_SAMPLES:
add eax, ebx // temp += deltas[0]
cmp al, byte ptr [esi] // level+deltas[0] < *in ?
rcl ebp, 1 // shift right bit into data
jge SUM_GE_IN // if (level+deltas[0] >= *in) goto SUM_GE_IN
add edx, edi // level+deltas[0] < *in -> level += deltas[1]
mov eax, edx // temp = level (for next iteration)
jmp SUM_LT_IN
SUM_GE_IN:
mov edx, eax // level = temp
SUM_LT_IN:
#if defined(CODEC_DEMO)
push eax // ran out of registers
push ebp // we will need the real ebp to get at levels
mov ebp,[esp+8] // get real (frame ptr) value of ebp
mov eax, levels // eax = &levels (implicitly uses ebp)
//mov al, byte ptr ss:[levels]
// eax = &levels
mov ebp, eax // ebp = &levels
mov eax, [eax] // eax = levels
mov [eax], dl // *levels = level
inc eax // eax = levels + 1
mov [ebp], eax // levels = eax
pop ebp
pop eax
#endif
inc esi
loop DO_9_SAMPLES
mov ebx, ebp
pop ebp
mov eax, [level] // eax = &level
mov [eax], edx // level = edx
mov eax, [inp] // eax = &inp
mov [eax], esi // inp = esi
mov [data], ebx
}
#elif defined __GNUC__
#warning asm not implemented
Error(LOCATION, "asm not implemented");
#else
#error unknown compiler
#endif
return data;
}
#else // if defined(_WIN32)
#if defined(CODEC_DEMO)
int ComputeNomDataF(t_Sample*& inp, const int deltas[], int& level,
t_Sample*& levels);
#else
int ComputeNomDataF(t_Sample*& inp, const int deltas[], int& level);
#endif
#endif // if defined(_WIN32)
#define VERIFY_ASM
// Nominal packet encoder. Each encoded sample is equal to the previous sample
// +/- an offset. The mode has been decided by the routine that calls DoEncode
// so the mode is passed in.
#if defined(CODEC_DEMO)
#define EXTRA_CODEC_DEMO_ARGS1 , levels
static int DoEncode(int mode, BOOL& packetPos, t_Sample*& in, t_Sample*& out,
int& level, t_Sample*& levels, int*& modes,
int samples[9], int storage[9])
#else
#define EXTRA_CODEC_DEMO_ARGS1
static int DoEncode(int mode, BOOL& packetPos, t_Sample*& in, t_Sample*& out,
int& level)
#endif
{
t_PacketPairNom packet;
int advanceOutput = 0;
if (packetPos)
{
#if defined(VERIFY_ASM) && defined(CODEC_DEMO)
t_Sample *inT1 = in, *inT2 = in, *levelsT = levels;
int levelT1 = level, levelT2 = level;
#endif
packet.Mode1 = mode;
packet.Data1 = ComputeNomDataF(in,Deltas[mode], level EXTRA_CODEC_DEMO_ARGS1);
#if defined(VERIFY_ASM) && defined(CODEC_DEMO)
levels = levelsT;
unsigned int dataT = ComputeNomData(inT1, Deltas[mode], levelT1 EXTRA_CODEC_DEMO_ARGS1);
if (dataT != packet.Data1)
{
inT1 = inT2, levelT1 = levelT2;
levels = levelsT;
dataT = ComputeNomData(inT1, Deltas[mode], levelT1 EXTRA_CODEC_DEMO_ARGS1);
levels = levelsT;
dataT = ComputeNomDataF(inT2, Deltas[mode], levelT2 EXTRA_CODEC_DEMO_ARGS1);
}
#endif
}
else
{
packet = *(t_PacketPairNom*)out;
packet.Mode0 = mode;
packet.Data0 = ComputeNomDataF(in,Deltas[mode], level EXTRA_CODEC_DEMO_ARGS1);
advanceOutput = 3; // sizeof packet;
}
*(t_PacketPairNom*)out = packet;
packetPos = !packetPos;
#if defined(CODEC_DEMO)
for (int i = 0; i < 9; i++) *modes++ = mode;
samples[mode] += 9; storage[mode] += 3; // sizeof packet;
#endif
return advanceOutput;
}
static void Smooth1(t_Sample* bufIn, t_Sample* bufOut, int size)
{
int i;
bufOut[0] = bufIn[0];
bufOut[size-1] = bufIn[size-1];
for (i = 2; i < size; i++)
{
int s0 = bufIn[i-2], s1 = bufIn[i-1], s2 = bufIn[i];
if (s0 == s2 && abs(s1 - s0) < 3)
bufOut[i-1] = bufIn[i];
else
bufOut[i-1] = bufIn[i-1];
if (abs(s0 - ZERO) < 2 && abs(s1 - ZERO) < 3)
bufOut[i-2] = ZERO;
}
}
// can operate in place
static double AutoGain1(t_Sample* bufIn, t_Sample* bufOut, int size)
{
register int i, s;
int hist[17], sum, silenceSize = 0;
const int TARGET_MAX = 32, BUCKET_SIZE = 3;
double x;
for (i = 0; i < 17; i++)
hist[i] = 0;
for (i = 0; i < size; i++)
{
s = abs(bufIn[i] - ZERO);
hist[s >> BUCKET_SIZE]++;
if (s == 0 || s == 1 || s == -1)
silenceSize++;
}
// ignore silence data when computing significant_data_above_this
for (i = 15, sum = hist[16]; i >= 0 && sum < ((size - silenceSize) >> 4);
i--)
sum += hist[i];
int significant_data_above_this = i << BUCKET_SIZE;
#if LOGARITHMIC_GAIN
if (significant_data_above_this > TARGET_MAX)
{
x = ((double)Log2[significant_data_above_this]) / TARGET_MAX;
for (i = 0; i < size; i++)
{
s = abs(bufIn[i] - ZERO);
s = int(Log2[s] / x);
bufOut[i] = bufIn[i] < ZERO ? t_Sample(ZERO - s) : t_Sample(s + ZERO);
}
}
else
x = 0.0;
return x;
#else
// linearly scale data such that a value of significant_data_above_this
// will become a value of TARGET_MAX
if (significant_data_above_this > TARGET_MAX)
{
x = ((double)TARGET_MAX) / significant_data_above_this;
for (i = 0; i < size; i++)
{
s = abs(bufIn[i] - ZERO);
s = int(((double)s) * x + .5);
bufOut[i] = bufIn[i] < ZERO ? t_Sample(ZERO - s) : t_Sample(s + ZERO);
}
x = 1 / x; // inverse for UnAutoGain() (no danger of x being 0)
}
else if (significant_data_above_this > 0)
x = ((double)TARGET_MAX) / significant_data_above_this;
else
x = 4.0;
// Do a little extra volume boost for low QoS situations. There is
// nothing magic about the constants in the following, they're just
// reasonable heuristic values.
if (QualityOfService <= 3)
x = (x * 6) / (QualityOfService+2);
if (QualityOfService == 1)
x *= 1.5;
return x;
#endif
}
// can operate in place
static void UnAutoGain1(t_Sample* bufIn, t_Sample* bufOut, int size,
double gain)
{
register int i;
if (gain == 0.0)
{
if (bufIn != bufOut)
for (i = 0; i < size; i++)
bufOut[i] = bufIn[i];
return;
}
for (i = 0; i < size; i++)
{
int s = abs(bufIn[i] - ZERO);
#if defined(LOGARITHMIC_GAIN)
s = int(pow(2.0, s * gain)); //!!! could use some optimization
#else
s = int(((double)s) * gain + .5);
#endif
if (s > 127) s = 127;
bufOut[i] = bufIn[i] < ZERO ? t_Sample(ZERO - s) : t_Sample(s + ZERO);
}
}
static void InitLowPassFilter(int QoS, double LPF_Coef[],
char LPF_CoefTimesSample[LPF_NUM_POINTS][256])
{
// QoS==10 ==> cutoff frequency is 5500Hz (filter is a noop)
// QoS==1 ==> cutoff frequency is 550Hz
int N = 19, K = 2 * QoS - 1, kshift = LPF_NUM_POINTS/2, k;
for (k = 0; k < LPF_NUM_POINTS; k++)
{
if (k != kshift)
LPF_Coef[k] = sin(PI * (k-kshift) * K / (double)N) /
( sin(PI * (k-kshift) / (double)N) * N );
else
LPF_Coef[k] = ((double)K) / N;
for (int i = 0; i < 256; i++)
LPF_CoefTimesSample[k][i] = (char)(LPF_Coef[k] * i + .5);
}
}
// can now operate in place
static void LowPassFilter(t_Sample* bufIn, t_Sample* bufOut, int size,
char LPF_CoefTimesSample[LPF_NUM_POINTS][256])
{
register int j;
#if !defined(USE_LOWPASS_FILTER)
if (bufIn != bufOut)
for (j = 0; j < size; j++)
bufOut[j] = bufIn[j];
return;
#endif
for (j = LPF_NUM_POINTS-1; j < size; j++)
{
register int i, sum = 0;
for (i = 0; i < LPF_NUM_POINTS; i++)
{
int temp1 = bufIn[ i + (j-(LPF_NUM_POINTS-1)) ]; // 0 -> 255
temp1 -= ZERO; // -128 -> 127
// lookup based on abs value
int temp2 = LPF_CoefTimesSample[i][abs(temp1)];
if (temp1 < 0)
sum -= temp2;
else
sum += temp2;
}
// depending on the exact values of the coefficients, there is a
// chance that sum may become slightly larger (or smaller) than the
// maximum (mimimum) allowable values, so truncate it before it does
// any damage
if (sum > 127) sum = 127;
if (sum < -128) sum = -128;
bufOut[j-(LPF_NUM_POINTS-1)] = t_Sample(sum + ZERO);
}
#if 0
//for (j = 0; j < LPF_NUM_POINTS-1; j++)
// bufOut[j] = 0; // clear unused buffer entries
// fade in the samples to avoid pops & clicks
int level = bufOut[LPF_NUM_POINTS-1];
for (j = LPF_NUM_POINTS-2; j >= 0; j--)
bufOut[j] = level = ((level-ZERO) >> 1) + ZERO;
#else
// fade out the samples to avoid pops & clicks
int level = bufOut[size - LPF_NUM_POINTS];
for (j = size-(LPF_NUM_POINTS+1); j < size; j++)
bufOut[j] = t_Sample(level = ((level-ZERO) >> 1) + ZERO);
#endif
#if defined(REMOVE_DC_BIAS)
int sum, bias;
// remove DC bias
for (j = 0, sum = 0; j < size; j++)
sum += bufOut[j] - ZERO;
bias = sum / size;
for (j = 0; j < size; j++)
bufOut[j] -= bias;
#endif
}
// can operate in place (i.e. bufIn == bufOut is ok)
static void SkipEveryOther(t_Sample* bufIn, t_Sample* bufOut, int size)
{
for (int i = 0; i < size; i += 2)
bufOut[i/2] = bufIn[i];
}
static void InterpolateEveryOther(t_Sample* bufIn, t_Sample* bufOut, int size)
{
for (int i = 0; i < size; i++)
{
*bufOut++ = *bufIn;
*bufOut++ = t_Sample((*bufIn + *(bufIn+1)) / 2);
bufIn++;
}
}
#if defined(USE_LPC10)
extern "C"
{
#include "lpc10\ulaw.h" // it's the LAW
};
// can operate in place
static void ConvertToLPC10(t_Sample* bufIn, t_Sample* bufOut, int size)
{
#if 0
unsigned short s;
for (int i = 0; i < size; i++)
{
s = bufIn[i]-ZERO;
s <<= 8;
s >>= 3;
bufOut[i] = s2u[s];
}
#else
for (int i = 0; i < size; i++)
bufOut[i] = audio_c2u(bufIn[i]);
#endif
}
// can operate in place
static void ConvertFromLPC10(t_Sample* bufIn, t_Sample* bufOut, int size)
{
for (int i = 0; i < size; i++)
bufOut[i] = t_Sample(audio_u2c(bufIn[i]));
}
#endif // defined(USE_LPC10)
//////////////////////////////////////////////////////////////////////////////
// Only unused junk below. Being kept around for reference only.
#if 0
for (i = 0; i < 256; i++)
{
s = abs(i - ZERO);
if (s > 22)
s = (log((double)s) / log(2.0)) * 5 + .5;
s = i < ZERO ? ZERO - s : s + ZERO;
unsigned char junk = s;
if (junk > ZERO+35 || junk < ZERO-36)
junk = 127;
}
static void Log1(t_Sample* bufIn, t_Sample* bufOut, int size)
{
register int i, s;
for (i = 0; i < size; i++)
{
s = abs(bufIn[i] - ZERO);
if (s > 22)
s = Log2[s] * 5.0 + .5;
bufOut[i] = bufIn[i] < ZERO ? ZERO - s : s + ZERO;
if (bufOut[i] > ZERO+35 || bufOut[i] < ZERO-36)
bufOut[i] = 127;
}
}
static void UnLog1(t_Sample* bufIn, t_Sample* bufOut, int size)
{
int i;
for (i = 0; i < size; i++)
{
int s = abs(bufIn[i] - ZERO);
if (s > 22)
s = pow(2.0, s / 5.0); //!!! could use some optimization
bufOut[i] = bufIn[i] < ZERO ? ZERO - s : s + ZERO;
}
}
// from InitLowPassFilter():
//int K = LPF_NUM_POINTS;
//double cutoffFrequency = 1100.0*QoS,
// bandwidthPerBucket = cutoffFrequency / (K/2);
//int N = (int)(11000.0 / bandwidthPerBucket), k,
// kshift = K/2;
// cutoff fraction = cutoff freq / sample freq
// ==> cutoff fraction = LPF_NUM_POINTS / N
// ==> N = LPF_NUM_POINTS / cutoff fraction
//int i, k, N = (int)(LPF_NUM_POINTS / ((1100.0*QoS) / 11000.0)),
// kshift = LPF_NUM_POINTS/2;
#endif