ABX<->WAV converter; ABX code modularized, ABX encoder added, direct WAV file io

git-svn-id: svn://svn.code.sf.net/p/sc2/code/trunk@3596 8092fc87-c524-0410-9efc-e669fe64eaf9
This commit is contained in:
avolkov
2011-05-04 20:19:27 +00:00
parent ede7d2a545
commit c18134d041
13 changed files with 1925 additions and 315 deletions
+874
View File
@@ -0,0 +1,874 @@
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/*
* ABX encoder/decoder
* By Serge van den Boom (svdb@stack.nl) and Alex Volkov (codepro@usa.net)
* Based on ABX decoding code from Toys for Bob.
*
* TODO:
* - so far, it ignores sample rates, so it will work ok as long as all
* the frames have the same frequency. This is probably enough for
* our purposes.
*
* - add abx_setMaxError(), abx_setMinSquelch() and abx_setBlockSize() for
* the encoder parameters, if anyone cares that is. The 3DO abx files all
* used the same params, as far as I know.
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <memory.h>
#include <errno.h>
#include "abx.h"
// This number can be increased to almost anything, as long
// as you have enough memory to store the data. It's kept
// on the low end to improve the sanity checks.
#define MAX_REASONABLE_FRAMES 100000
#define abx_FrameInfo_size 8
#define abx_FrameHeader_size 8
static uint32_t abx_decodeFrame(abx_File *abx, const abx_FrameHeader *hdr,
int inlen, uint8_t *out);
static uint32_t abx_encodeFrame(abx_File *abx, abx_FrameHeader *hdr,
uint8_t *in, int inlen);
// The deltas table came from TFB
static const int deltas[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
-128,-112,-96,-80,-64,-48,-32,-16,16,32,48,64,80,96,112,127,// Multiplier of 16
};
static bool read_8 (FILE *fp, uint8_t *v)
{
return fread(v, sizeof(*v), 1, fp) == 1;
}
static bool read_le_16 (FILE *fp, uint16_t *v)
{
uint8_t buf[2];
if (fread(buf, sizeof(buf), 1, fp) != 1)
return false;
*v = (buf[1] << 8) | buf[0];
return true;
}
static bool read_le_32 (FILE *fp, uint32_t *v)
{
uint8_t buf[4];
if (fread(buf, sizeof(buf), 1, fp) != 1)
return false;
*v = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
return true;
}
static bool write_8 (FILE *fp, uint8_t v)
{
return fwrite(&v, sizeof(v), 1, fp) == 1;
}
static bool write_le_16 (FILE *fp, uint16_t v)
{
uint8_t buf[2];
buf[0] = v;
buf[1] = v >> 8;
return fwrite(buf, sizeof(buf), 1, fp) == 1;
}
static bool write_le_32 (FILE *fp, uint32_t v)
{
uint8_t buf[4];
buf[0] = v;
buf[1] = v >> 8;
buf[2] = v >> 16;
buf[3] = v >> 24;
return fwrite(buf, sizeof(buf), 1, fp) == 1;
}
static bool abx_readFileHeader(abx_File *abx, abx_FileHeader *hdr)
{
if (!read_le_16(abx->fp, &hdr->numFrames) ||
!read_le_32(abx->fp, &hdr->totalSize) ||
!read_le_16(abx->fp, &hdr->maxBufSize) ||
!read_le_16(abx->fp, &hdr->freq))
{
abx->last_error = errno;
return false;
}
return true;
}
static bool abx_writeFileHeader(abx_File *abx, const abx_FileHeader *hdr)
{
if (!write_le_16(abx->fp, hdr->numFrames) ||
!write_le_32(abx->fp, hdr->totalSize) ||
!write_le_16(abx->fp, hdr->maxBufSize) ||
!write_le_16(abx->fp, hdr->freq))
{
abx->last_error = errno;
return false;
}
return true;
}
static bool abx_readFrameInfo(abx_File *abx, abx_FrameInfo *info)
{
if (!read_le_32(abx->fp, &info->ofs) ||
!read_le_16(abx->fp, &info->fsize) ||
!read_le_16(abx->fp, &info->usize))
{
abx->last_error = errno;
return false;
}
return true;
}
static bool abx_writeFrameInfo(abx_File *abx, const abx_FrameInfo *info)
{
if (!write_le_32(abx->fp, info->ofs) ||
!write_le_16(abx->fp, info->fsize) ||
!write_le_16(abx->fp, info->usize))
{
abx->last_error = errno;
return false;
}
return true;
}
static bool abx_readFrameHeader(abx_File *abx, abx_FrameHeader *hdr)
{
if (!read_le_16(abx->fp, &hdr->usize) ||
!read_le_16(abx->fp, &hdr->freq) ||
!read_8(abx->fp, &hdr->blockSize) ||
!read_8(abx->fp, &hdr->minSquelch) ||
!read_le_16(abx->fp, &hdr->maxError))
{
abx->last_error = errno;
return false;
}
return true;
}
static bool abx_writeFrameHeader(abx_File *abx, const abx_FrameHeader *hdr)
{
if (!write_le_16(abx->fp, hdr->usize) ||
!write_le_16(abx->fp, hdr->freq) ||
!write_8(abx->fp, hdr->blockSize) ||
!write_8(abx->fp, hdr->minSquelch) ||
!write_le_16(abx->fp, hdr->maxError))
{
abx->last_error = errno;
return false;
}
return true;
}
bool abx_open(abx_File *abx, const char *filename)
{
abx_FileHeader fileHdr;
unsigned i;
unsigned maxCalcBuf;
memset(abx, 0, sizeof(*abx));
abx->fp = fopen(filename, "rb");
if (!abx->fp)
{
abx->last_error = errno;
return false;
}
// read abx header
if (!abx_readFileHeader(abx, &fileHdr))
{
abx->last_error = errno;
abx_close(abx);
return false;
}
abx->numFrames = fileHdr.numFrames;
abx->maxBufSize = fileHdr.maxBufSize;
abx->freq = fileHdr.freq;
if (abx->freq == 0)
abx->freq = ABX_DEFAULT_FREQ;
// Some sanity checks. ABX format does not have a magic number
// or anything like that, but we can do some math.
if (abx->numFrames > MAX_REASONABLE_FRAMES)
{
abx->last_error = -1;
fprintf(stderr, "abx_open(): number of frames (%u) is not reasonable\n",
abx->numFrames);
abx_close(abx);
return false;
}
if (abx->freq != 11025 && abx->freq != 22050 && abx->freq != 44100
&& abx->freq != 48000)
{
fprintf(stderr, "abx_open() Warning: sampling frequency (%u) is suspect\n",
(unsigned)abx->freq);
}
abx->frames = calloc(sizeof(abx->frames[0]), abx->numFrames);
if (!abx->frames)
{
abx->last_error = errno;
fprintf(stderr, "abx_open(): could not allocate frames array\n");
abx_close(abx);
return false;
}
maxCalcBuf = 0;
for (i = 0; i < abx->numFrames; ++i)
{
abx_FrameInfo *info = abx->frames + i;
if (!abx_readFrameInfo(abx, info))
{
abx_close(abx);
return false;
}
abx->totalSize += info->usize;
if (info->usize > maxCalcBuf)
maxCalcBuf = info->usize;
if (info->fsize > abx->maxEncSize)
abx->maxEncSize = info->fsize;
}
if (abx->totalSize != fileHdr.totalSize)
{
fprintf(stderr, "abx_open() Warning: "
"total size in header (%u) does not match sum of frames (%u)\n",
(unsigned)fileHdr.totalSize, (unsigned)abx->totalSize);
}
if (abx->maxBufSize < maxCalcBuf)
{
fprintf(stderr, "abx_open() Warning: "
"max buffer size in header (%u) is less than calculated max (%u)\n",
abx->maxBufSize, maxCalcBuf);
abx->maxBufSize = maxCalcBuf;
}
abx->data_ofs = ftell(abx->fp);
abx->maxFrames = abx->numFrames;
// Our buffer stores encoded data during decoding. The maximum buffer
// size needed was computed just above.
abx->buf = malloc(abx->maxEncSize);
if (!abx->buf)
{
abx->last_error = errno;
abx_close(abx);
return false;
}
return true;
}
static bool abx_writeHeaders(abx_File *abx)
{
abx_FileHeader fileHdr;
unsigned i;
fileHdr.numFrames = abx->numFrames;
fileHdr.maxBufSize = abx->maxBufSize;
fileHdr.freq = abx->freq;
fileHdr.totalSize = abx->totalSize;
if (!abx_writeFileHeader(abx, &fileHdr))
return false;
for (i = 0; i < abx->numFrames; ++i)
{
abx_FrameInfo *info = abx->frames + i;
if (!abx_writeFrameInfo(abx, info))
return false;
}
return true;
}
bool abx_create(abx_File *abx, const char *filename)
{
memset(abx, 0, sizeof(*abx));
abx->fp = fopen(filename, "wb");
if (!abx->fp)
{
abx->last_error = errno;
return false;
}
abx->freq = ABX_DEFAULT_FREQ;
abx->maxError = ABX_DEFAULT_ERROR;
if (!abx_writeHeaders(abx))
{
abx_close(abx);
return false;
}
abx->frames_ofs = ftell(abx->fp);
abx->maxFrames = 10;
abx->frames = calloc(sizeof(abx->frames[0]), abx->maxFrames);
if (!abx->frames)
{
abx->last_error = errno;
fprintf(stderr, "abx_create(): could not allocate frames array\n");
abx_close(abx);
return false;
}
fseek(abx->fp, abx->maxFrames * abx_FrameInfo_size, SEEK_CUR);
abx->data_ofs = ftell(abx->fp);
abx->writing = true;
return true;
}
static bool abx_flushHeaders(abx_File *abx)
{
fseek(abx->fp, 0, SEEK_SET);
if (!abx_writeHeaders(abx))
{
return false;
}
return true;
}
void abx_close(abx_File *abx)
{
if (abx->fp)
{
if (abx->writing)
abx_flushHeaders(abx);
fclose(abx->fp);
}
if (abx->frames)
free(abx->frames);
if (abx->buf)
free(abx->buf);
memset(abx, 0, sizeof(*abx));
}
bool abx_setSamplingRate(abx_File *abx, uint32_t freq)
{
if (!abx->writing)
return false;
abx->freq = freq;
return true;
}
uint32_t abx_getMaxBuffer(abx_File *abx)
{
return abx->maxBufSize;
}
bool abx_setMaxFrames(abx_File *abx, unsigned maxFrames)
{
abx_FrameInfo *newf;
if (!abx->writing)
return false;
if (maxFrames < abx->numFrames)
return false;
if (abx->numFrames > 0 && maxFrames <= abx->maxFrames)
{ // We've already written some audio data to the file.
// Decreasing the allocated frame info space at this point involves
// way too much work, so we'll silently ignore this.
return true;
}
else if (abx->numFrames > 0 && maxFrames > abx->maxFrames)
{ // We've already written some audio data to the file.
// Increasing the allocated frame info space at this point involves
// way too much work, so it is an error to attempt it.
abx->last_error = ENOSPC;
return false;
}
if (abx->frames && maxFrames > abx->maxFrames)
{ // grow the array
newf = realloc(abx->frames, maxFrames * sizeof(abx->frames[0]));
if (!newf)
{
abx->last_error = errno;
return false;
}
abx->frames = newf;
}
abx->maxFrames = maxFrames;
if (abx->numFrames == 0)
{ // We have not written any audio data yet.
// Adjust the data offset
fseek(abx->fp, abx->frames_ofs + abx->maxFrames * abx_FrameInfo_size, SEEK_SET);
abx->data_ofs = ftell(abx->fp);
}
return true;
}
uint32_t abx_readFrame(abx_File *abx, void *buf, uint32_t bufsize)
{
abx_FrameInfo *info;
abx_FrameHeader hdr;
uint32_t decSize;
uint32_t inlen;
if (abx->writing)
{
abx->last_error = EPERM;
return 0;
}
if (abx->nextFrame == abx->numFrames)
{ // EOF
abx->last_error = 0;
return 0;
}
info = abx->frames + abx->nextFrame;
// Go get the next frame
if (fseek(abx->fp, info->ofs, SEEK_SET) != 0)
{
abx->last_error = errno;
return 0;
}
if (!abx_readFrameHeader(abx, &hdr))
return 0;
if (hdr.usize != info->usize)
{
fprintf(stderr, "abx_readFrame() Warning: "
"decoded size in header (%u) does not match reported in info (%u) for frame %u\n",
(unsigned)hdr.usize, (unsigned)info->usize, abx->nextFrame);
}
if (hdr.freq != 0 && hdr.freq != abx->freq)
{
fprintf(stderr, "abx_readFrame() Warning: "
"frame frequency (%u) is different from file freq (%u) for frame %u\n",
(unsigned)hdr.freq, (unsigned)abx->freq, abx->nextFrame);
fprintf(stderr, "This is not supported. Output will be corrupted.\n");
}
if (bufsize < hdr.usize)
{ // Buffer is too small to accept the entire frame
// The caller should call abx_getMaxBuffer() to find out the size
abx->last_error = 0;
return 0;
}
inlen = info->fsize - abx_FrameHeader_size;
if (fread(abx->buf, inlen, 1, abx->fp) != 1)
{
abx->last_error = errno;
return 0;
}
decSize = abx_decodeFrame(abx, &hdr, inlen, buf);
if (decSize != hdr.usize)
{
fprintf(stderr, "abx_readFrame() Warning: "
"actual decoded data size (%u) does not match reported (%u) for frame %u\n",
(unsigned)decSize, (unsigned)hdr.usize, abx->nextFrame);
}
++abx->nextFrame;
return decSize;
}
uint32_t abx_writeFrame(abx_File *abx, void *buf, uint32_t bufsize)
{
abx_FrameInfo *info;
abx_FrameHeader hdr;
uint32_t encSize;
if (!abx->writing)
{
abx->last_error = EPERM;
return 0;
}
if (abx->nextFrame >= abx->maxFrames)
{ // No more room
abx->last_error = EFBIG;
return 0;
}
info = abx->frames + abx->nextFrame;
// Our buffer stores encoded data during encoding, but the encoded data
// can never be larger than the decoded one by algorithm definition.
if (bufsize > abx->maxBufSize)
{ // grow the buffer
if (abx->buf)
free(abx->buf);
abx->buf = malloc(bufsize);
if (!abx->buf)
{
abx->last_error = errno;
return 0;
}
abx->maxBufSize = bufsize;
}
if (fseek(abx->fp, abx->data_ofs, SEEK_SET) != 0)
{
abx->last_error = errno;
return 0;
}
hdr.blockSize = ABX_DEFAULT_BLOCKSIZE;
hdr.minSquelch = ABX_DEFAULT_SQUELCH;
hdr.maxError = abx->maxError;
encSize = abx_encodeFrame(abx, &hdr, buf, bufsize);
if (!abx_writeFrameHeader(abx, &hdr) ||
fwrite(abx->buf, encSize, 1, abx->fp) != 1)
{
abx->last_error = errno;
return 0;
}
encSize += abx_FrameHeader_size;
info->usize = bufsize;
info->ofs = abx->data_ofs;
info->fsize = encSize;
abx->data_ofs = ftell(abx->fp);
if (encSize > abx->maxEncSize)
abx->maxEncSize = encSize;
abx->totalSize += bufsize;
++abx->nextFrame;
++abx->numFrames;
return encSize;
}
static inline void clip_u8(int *val)
{
if (*val < 0)
*val= 0;
else if (*val > 255)
*val = 255;
}
static uint32_t abx_decodeFrame(abx_File *abx, const abx_FrameHeader *hdr,
int inlen, uint8_t *out)
{
uint8_t *in = abx->buf;
int outlen = hdr->usize;
int prev;
// Get initial data point
prev = *in;
++in;
--inlen; // one byte consumed
*out = prev;
++out;
--outlen; // one sample stored
while (outlen > 0 && inlen > 0)
{
unsigned bytes;
unsigned sample;
// Get next encoded byte
sample = *in;
++in;
--inlen;
if (sample & RESYNC) // Is it a resync byte?
{
prev = (sample & 0x7F) << 1; // Store resync byte.
*out = prev;
++out;
--outlen; // one sample stored
}
else if (sample & SQLCH) // Is it a squelch byte?
{
bytes = sample & SQUELCHCNT; // And off the number of squelch bytes
memset(out, prev, bytes);
out += bytes;
outlen -= bytes; // bytes samples stored
}
else if (sample & DELTAMOD) // Is it delta modulate byte?
{
// base address to multiplier table
const int *base = deltas + (sample & MULTIPLIER) * 16;
unsigned sampleBits; // bits per sample
unsigned mask;
int samplesPerByte;
// This is not optimized for efficiency, but rather deoptimized
// for readability
sampleBits = (sample & DELTAMOD) >> DELTASHIFT;
if (sampleBits == 3) // no 3-bit delta coding
sampleBits = 4;
// Base address of deltas: middle of the table minus half the
// range of the delta
base += 8 - (1 << (sampleBits - 1));
samplesPerByte = 8 / sampleBits;
mask = (1 << sampleBits) - 1;
for (bytes = hdr->blockSize / samplesPerByte; bytes > 0; --bytes)
{
unsigned val;
int i;
val = *in;
++in;
--inlen;
for (i = samplesPerByte; i > 0; --i)
{
val <<= sampleBits;
prev += base[(val >> 8) & mask];
clip_u8(&prev);
*out = prev;
++out;
}
}
outlen -= hdr->blockSize; // one block of samples stored
}
else
{ // None of the known bit combinations. Weird.
fprintf(stderr, "abx_decodeFrame() Warning: "
"unknown sample 0x%02x in frame %u\n",
(unsigned)sample, abx->nextFrame);
// We'll just suppress the sample
}
}
if (outlen != 0 || inlen != 0)
{
fprintf(stderr, "abx_decodeFrame() Warning: "
"byte counts do not match at end of frame (%i, %i)\n",
inlen, outlen);
}
return hdr->usize - outlen;
}
static int lookupDelta(const int *base, int cnt, int prev, int sample)
{
int i;
int imin = 0;
int mindiff = 65536;
for (i = 0; i < cnt; ++i)
{
int diff;
// We want the delta that gives us a resulting sample that is
// the closest to the original *after* any clipping occurs.
// This is important in cases where both the previous sample
// and the current sample are at min or max points, since there
// is no 0 deltas in the tables.
int cur = prev + base[i];
clip_u8(&cur);
diff = abs(cur - sample);
if (diff < mindiff)
{
mindiff = diff;
imin = i;
}
}
return imin;
}
static uint32_t abx_encodeBlock(const abx_FrameHeader *hdr, uint8_t *in,
uint8_t *out, int *last, unsigned sampleBits, unsigned mult,
int *blockError)
{
const int *base = deltas + mult * 16;
const int samplesPerByte = 8 / sampleBits;
const int deltaCnt = 1 << sampleBits;
unsigned bytes;
int prev = *last;
int error = 0;
// Base address of deltas: middle of the table minus half the
// range of the delta
base += 8 - deltaCnt / 2;
for (bytes = hdr->blockSize / samplesPerByte; bytes > 0; --bytes)
{
unsigned val = 0;
int i;
for (i = samplesPerByte; i > 0; --i)
{
int sample = *in;
unsigned index;
++in;
// Computing the closest delta index directly involves a ridiculous
// amount of logic because the delta tables have no 0 deltas. It is
// simpler to just iterate over all of them.
index = lookupDelta(base, deltaCnt, prev, sample);
prev += base[index];
clip_u8(&prev);
error += (prev - sample) * (prev - sample);
if (error > hdr->maxError)
return 0; // exceeded the maximum error, bail out
val <<= sampleBits;
val |= index;
}
if (out)
{
*out = val;
++out;
}
}
*last = prev;
if (blockError)
*blockError = error;
return hdr->blockSize / samplesPerByte;
}
static uint32_t abx_encodeFrame(abx_File *abx, abx_FrameHeader *hdr,
uint8_t *in, int inlen)
{
uint8_t *out = abx->buf;
int prev;
hdr->usize = inlen;
hdr->freq = abx->freq;
// Store initial data point
prev = *in;
++in;
--inlen; // one sample consumed
*out = prev;
++out;
// Speed and efficiency is not an issue for us. The strategy here is
// simply to achieve maximum compression by brute force. We try all of
// the 48 delta coding variants and pick the one with the smallest
// total error within the allowed limit.
while (inlen > 0)
{
int cnt;
// Try squelching first
for (cnt = 0; cnt < inlen && cnt < SQUELCHCNT; ++cnt)
{
if (in[cnt] != prev)
break;
}
if (cnt >= hdr->minSquelch)
{ // Squelch sample repeats
*out = SQLCH | cnt;
++out;
in += cnt;
inlen -= cnt;
continue;
}
// Now try resync + squelch
for (cnt = 0; cnt < inlen - 1 && cnt < SQUELCHCNT; ++cnt)
{
if (in[cnt + 1] != in[0])
break;
}
if (cnt >= hdr->minSquelch + 1)
{ // Resync and squelch sample repeats
prev = *in;
out[0] = RESYNC | (prev >> 1);
out[1] = SQLCH | cnt;
out += 2;
in += 1 + cnt;
inlen -= 1 + cnt;
continue;
}
// Try a delta-coding block
if (inlen >= hdr->blockSize)
{
int bits, bestBits = 0;
int mult, bestMult = 0;
int error, bestError = hdr->maxError * 4;
error = bestError; // for shortcutting
for (bits = 1; bits < 4 && error != 0; ++bits)
{
if (bits == 3) // no 3-bit coding
bits = 4;
for (mult = 0; mult < 16; ++mult)
{
uint32_t blk;
int last = prev;
blk = abx_encodeBlock(hdr, in, NULL, &last,
bits, mult, &error);
if (blk > 0 && error < bestError)
{ // remember the best one so far
bestError = error;
bestBits = bits;
bestMult = mult;
if (error == 0)
break; // shortcut
}
}
}
if (bestBits > 0)
{ // success!
// out+1 because we need space for the DELTAMOD byte
uint32_t blk = abx_encodeBlock(hdr, in, out + 1, &prev,
bestBits, bestMult, NULL);
if (bestBits == 4)
bestBits = 3;
*out = (bestBits << DELTASHIFT) | bestMult;
out += 1 + blk;
in += hdr->blockSize;
inlen -= hdr->blockSize;
continue;
}
}
// And when everything else fails, emit a RESYNC
prev = *in;
++in;
--inlen;
*out = RESYNC | (prev >> 1);
++out;
}
return out - abx->buf;
}