mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-10 04:31:34 -10:00
1995 lines
68 KiB
C++
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
|
|
|