WSS File Format: Difference between revisions

From Bohemia Interactive Community
Jump to navigation Jump to search
m (removed double borders)
Line 72: Line 72:
==Decompression==
==Decompression==


Bis '''optionally''' use Delta8 (byte) or Delta4 (nibble) 'Lossy' Compression for mono pcm data only.
The facility exists to handle stereo compression but it isn't employed.
===Byte Compression===


Compression consists of single encoded byte 'samples' versus uncompressed short 'samples'.
Compression consists of single encoded byte 'samples' versus uncompressed short 'samples'.
Line 135: Line 141:
}
}
</syntaxhighlight>
</syntaxhighlight>
===Nibble Compression===
Although by no means the same thing, Nibble compression is an adaptation of IMA ADPCM compression. Each nibble represents a SHORT of PCMdata. Thus the resultant output size will be four times larger (in bytes).
===C++ Code===
//
// all honor and glory to T_D
//
static short PCMIndex[] = {-8192, -4096, -2048, -1024, -512, -256, -64, 0, 64, 256, 512, 1024, 2048, 4096, 8192};
static short limits(short PCM)
{
  if(PCM > SHRT_MAX) PCM = SHRT_MAX;
  else if(PCM < SHRT_MIN) PCM = SHRT_MIN;
  return PCM;
}
short * DeCompressNibble(const char *NibbleData,int len,short seed=0)//len in bytes
{
  short  *snap,*pcmData;
  if (!(snap=pcmData=new short[2*len]))  throw GENERR_NOMEM;
  delta=seed;
  while (len--)
  { 
    byte nibble=*NibbleData++;
    *pcmData++ = limits(delta += PCMIndex[nibble >> 4]);
    *pcmData++ = limits(delta += PCMIndex[nibble & 0x0F]);
  }
  return snap;
}
[[Category:BIS_File_Formats]]
[[Category:BIS_File_Formats]]

Revision as of 13:19, 21 February 2012

Introduction

WSS files are used since OFP times to store sound data and its file format is pretty much like the RIFF WAVE file format used for .wav files.

For the purposes of description here, 'Sound' in both wav or wss is recorded as PCM data in 16 bit 'samples'. The quality of that sound is determined by the frequency of those 'samples' per second, referred to as the SampleRate.

Given this: a few important paramaters are define here:

  • BitsPerSample: 16
  • BytesPerSample: 2 (Inferred BitsPerSample/8)


File Format

Offset Datatype Content Description
0 char[4] "WSS0" file signature
4 ulong CompressionType 0 == none. 8== compressed PCM data
8 ushort format Always 1 (WAVE_FORMAT_PCM)
10 ushort nChannels 1=mono, 2=stereo
12 ulong SampleRate e.g. 44100Hz
16 ulong BytesPerSecond SampleRate * BlockAlign
20 ushort BlockAlign nChannels * BytesPerSample
22 ushort BitsPerSecond usually 16
24 ushort <unknown> unknown value, not always present
26/24 byte[fileSize-26/24] <soundData> here the PCM data of the sound is stored

Decompression

Bis optionally use Delta8 (byte) or Delta4 (nibble) 'Lossy' Compression for mono pcm data only.

The facility exists to handle stereo compression but it isn't employed.

Byte Compression

Compression consists of single encoded byte 'samples' versus uncompressed short 'samples'. Each byte is, effectively, extrapolated to a short, thus making the compressed BYTE array, and the resulting decompressed SHORT array the same number of elements(length). The length is the remaining file length (in bytes) after the header.

C++ code

Function returns a short array, same size as it's compressed byte equivalent

#define LOG10	2.3025850929940456840	//ln(10)
#define LOG2	1.4426950408889634070	//log2(e)
#define MAGIC_NUMBER	((LOG10*LOG2)/28.12574042515172)

short* DeCompress(const char *CompressedData,int len)
{
        short *snap,*OutputData;

	if (!(snap=OutputData=new short[len]))  return 0;
	short LastVal=0;
	for (;len--;CompressedData++)
	{
		if (*CompressedData)
		{
			double asFloat = abs(*CompressedData) *MAGIC_NUMBER;
			double rnd = Round(asFloat);
			asFloat = pow(2.0, asFloat - rnd) * pow(2, rnd);// mantissa -
			if (*CompressedData < 0) asFloat *= -1;
			int asInt = Round(asFloat)+LastVal;
			if (asInt > SHRT_MAX ) asInt = SHRT_MAX ;
			if (asInt < SHRT_MIN) asInt = SHRT_MIN;
			LastVal=(short)asInt;
		 }
		*OutputData++ = LastVal;
	}
	return snap;
}

C# code

If the <soundData> is compressed the following (C#) code can be used for decompression:

PCMData = new Int16[soundData.Length];
for (int j = 0; j < PCMData.Length; j++)
{
  SByte srcSample = (SByte)soundData[j];
  if (srcSample != 0)
  {
    double asFloat = Math.Abs(srcSample) / 28.12574042515172;
    asFloat *= 2.3025850929940456840; //ln(10)
    asFloat *= 1.4426950408889634070; //log2(e)
    double rnd = Math.Round(asFloat);
    double mantisse = Math.Pow(2.0, asFloat - rnd);
    asFloat = mantisse * Math.Pow(2, rnd);
    if (srcSample < 0) asFloat *= -1;
    Int32 asInt = (int)Math.Round(asFloat);
    asInt = (j == 0) ? asInt : (asInt + PCMData[j - 1]);
    if (asInt > short.MaxValue) asInt = short.MaxValue;
    if (asInt < short.MinValue) asInt = short.MinValue;
    PCMData[j] = (Int16)asInt;
  }
  else PCMData[j] = (j == 0) ? (Int16)0 : PCMData[j - 1];
}

Nibble Compression

Although by no means the same thing, Nibble compression is an adaptation of IMA ADPCM compression. Each nibble represents a SHORT of PCMdata. Thus the resultant output size will be four times larger (in bytes).

C++ Code

//
// all honor and glory to T_D
//
static short PCMIndex[] = {-8192, -4096, -2048, -1024, -512, -256, -64, 0, 64, 256, 512, 1024, 2048, 4096, 8192};
static short limits(short PCM)
{
 if(PCM > SHRT_MAX) PCM = SHRT_MAX;
 else if(PCM < SHRT_MIN) PCM = SHRT_MIN;
 return PCM;
}
short * DeCompressNibble(const char *NibbleData,int len,short seed=0)//len in bytes
{
  short  *snap,*pcmData;
  if (!(snap=pcmData=new short[2*len]))  throw GENERR_NOMEM;
  delta=seed;
  while (len--)
  {   
    byte nibble=*NibbleData++;
   *pcmData++ = limits(delta += PCMIndex[nibble >> 4]);
   *pcmData++ = limits(delta += PCMIndex[nibble & 0x0F]);
  }
  return snap;
}