#include "uc.h" /*****************************************************************************/ /* UNC.C This is the C version of the ACOMP decompressor. Compare it to */ /* the assembly language version of the same, in UC.ASM. The */ /* assembly version of the decompressor is 2.4 times faster than the */ /* C version. Probabably some hand optimization of either would */ /* change these statistics. */ /* It is very imporant to note that you could deburst this algorithm */ /* so that it could decompress portions of the audio, as a background*/ /* task, thus allowing you to play back and decompress digitized */ /* sound, simultaneously! This would allow you to play back large */ /* digitized sound samples while using a very limited amount of core */ /* memory. The UCOMP decompressor could easily be put into a */ /* hardware based system, and it's performance characteristics far */ /* exceed current hardware based audio decompression systems such as */ /* CVSD and LPC encoding. */ /* To give you some performance characteristics look at the following*/ /* On my 20mhz 386 system, it took 0.1141 seconds to decompress 58k */ /* of audio. (0.3451 seconds using the C version.) That comes */ /* out to 600k per second! The amount of CPU overhead to */ /* decompress the audio, as it is played back, is truely */ /* negligible. */ /*****************************************************************************/ #define SQLCH 0x40 // Squelch byte flag #define RESYNC 0x80 // Resync byte flag. #define DELTAMOD 0x30 // Delta modulation bits. #define ONEBIT 0x10 // One bit delta modulate #define TWOBIT 0x20 // Two bit delta modulate #define FOURBIT 0x30 // four bit delta modulate #define MULTIPLIER 0x0F // Bottom nibble contains multiplier value. #define SQUELCHCNT 0x3F // Bits for squelching. static signed char trans[16*16] = { -8,-7,-6,-5,-4,-3,-2,-1,1,2,3,4,5,6,7,8, // Multiplier of 1 -16,-14,-12,-10,-8,-6,-4,-2,2,4,6,8,10,12,14,16, // Multiplier of 2 -24,-21,-18,-15,-12,-9,-6,-3,3,6,9,12,15,18,21,24, // Multiplier of 3 -32,-28,-24,-20,-16,-12,-8,-4,4,8,12,16,20,24,28,32, // Multiplier of 4 -40,-35,-30,-25,-20,-15,-10,-5,5,10,15,20,25,30,35,40, // Multiplier of 5 -48,-42,-36,-30,-24,-18,-12,-6,6,12,18,24,30,36,42,48, // Multiplier of 6 -56,-49,-42,-35,-28,-21,-14,-7,7,14,21,28,35,42,49,56, // Multiplier of 7 -64,-56,-48,-40,-32,-24,-16,-8,8,16,24,32,40,48,56,64, // Multiplier of 8 -72,-63,-54,-45,-36,-27,-18,-9,9,18,27,36,45,54,63,72, // Multiplier of 9 -80,-70,-60,-50,-40,-30,-20,-10,10,20,30,40,50,60,70,80, // Multiplier of 10 -88,-77,-66,-55,-44,-33,-22,-11,11,22,33,44,55,66,77,88, // Multiplier of 11 -96,-84,-72,-60,-48,-36,-24,-12,12,24,36,48,60,72,84,96, // Multiplier of 12 -104,-91,-78,-65,-52,-39,-26,-13,13,26,39,52,65,78,91,104, // Multiplier of 13 -112,-98,-84,-70,-56,-42,-28,-14,14,28,42,56,70,84,98,112, // Multiplier of 14 -120,-105,-90,-75,-60,-45,-30,-15,15,30,45,60,75,90,105,120, // Multiplier of 15 -127,-112,-96,-80,-64,-48,-32,-16,16,32,48,64,80,96,112,127 }; // Multiplier of 16 short Get8086word(unsigned char far *temp); // UnCompressAudio will decompress data which was compressed using ACOMP // into the destination address provided. UnCompressAudio returns the // total size, in bytes, of the uncompressed audio data. unsigned int far UnCompressAudio(unsigned char far *source,unsigned char far *dest) { unsigned short slen,frame,bits,count,length,plier,bytes; unsigned char sample,mask; short prev,up,down,one,two,three,four; signed char *base; slen = length = Get8086word(source); source+=4; // Skip length, and then frequency word. frame = *source++; // Frame size. source+=3; // Skip sqelch value, and maximum error allowed. prev = *dest++ = *source++; // Get initial previous data point. slen--; // Decrement total sound length. while ( slen ) // While still audio data to decompress.... { sample = *source++; // Get sample. if ( sample & RESYNC ) // Is it a resync byte? { *dest++ = prev = (sample&0x7F)<<1; // Store resync byte. slen--; // Decrement output sample length. } else { if ( sample & SQLCH ) // Is it a squelch byte? { count = sample&SQUELCHCNT; // And off the number of squelch bytes slen-=count; // Decrement total samples remaining count. for (; count; count--) *dest++ = prev; // send the repeated data out.. } else // Must be a delta modulate byte!! { bits = sample&DELTAMOD; // Delta mod resolution. plier = sample&MULTIPLIER; base = trans+plier*16; // Compute base address to multiplier table. slen-=frame; // Pulling one frame out. switch ( bits ) { case ONEBIT: bytes = frame/8; // 8 samples per byte. down = base[7]; // Go down 1 bit. up = base[8]; // Go up 1 bit. for (;bytes;bytes--) { sample = *source++; for(mask=0x80; mask; mask=mask>>1) { if ( sample & mask ) prev+=up; else prev+=down; if ( prev < 0 ) prev = 0; if ( prev > 255 ) prev = 255; *dest++=prev; } } break; case TWOBIT: bytes = frame/4; // 4 samples per byte. base+=6; // Base address of two bit delta's. for (;bytes;bytes--) { sample = *source++; one = sample>>6; // Top two bits. two = (sample>>4)&0x3; // Next two bits. three = (sample>>2)&0x3; // New two. four = sample&0x3; // last two. prev+=base[one]; if ( prev < 0 ) prev = 0; if ( prev > 255 ) prev = 255; *dest++=prev; prev+=base[two]; if ( prev < 0 ) prev = 0; if ( prev > 255 ) prev = 255; *dest++=prev; prev+=base[three]; if ( prev < 0 ) prev = 0; if ( prev > 255 ) prev = 255; *dest++=prev; prev+=base[four]; if ( prev < 0 ) prev = 0; if ( prev > 255 ) prev = 255; *dest++=prev; } break; case FOURBIT: bytes = frame/2; for (;bytes;bytes--) { sample = *source++; up = sample>>4; // High nibble. down = sample&0x0F; // Low nibble. prev+=base[up]; if ( prev < 0 ) prev = 0; if ( prev > 255 ) prev = 255; *dest++=prev; prev+=base[down]; if ( prev < 0 ) prev = 0; if ( prev > 255 ) prev = 255; *dest++=prev; } break; } } } } return( length ); } // GetFreq will report the playback frequency of a particular ACOMP data // file. unsigned int far GetFreq(unsigned char far *sound) { return( Get8086word(sound+2) ); } // This is used to make certain this C code is compatible when compiled on // a 68000 based machine. (Which it has been done and tested on.) short Get8086word(unsigned char far *temp) { short low,high; low = *temp++; high = *temp; return( low + high*256 ); }