cog/Frameworks/Shorten/Files/shorten/src/shn_reader.cpp

980 lines
23 KiB
C++

/*
* shn_reader.cpp
* shorten_decoder
*
* Created by Alex Lagutin on Sun Jul 07 2002.
* Copyright (c) 2002 Eckysoft All rights reserved.
*
*/
/* shn.c - main functions for xmms-shn
* Copyright (C) 2000-2001 Jason Jordan (shnutils@freeshell.org)
*
* 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.
*/
/*
* $Id: shn.c,v 1.9 2001/12/30 05:12:04 jason Exp $
*/
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <sys/stat.h>
#include "shn_reader.h"
#define WAVE_RIFF (0x46464952) /* 'RIFF' in little-endian */
#define WAVE_WAVE (0x45564157) /* 'WAVE' in little-endian */
#define WAVE_FMT (0x20746d66) /* ' fmt' in little-endian */
#define WAVE_DATA (0x61746164) /* 'data' in little-endian */
#define WAVE_FORMAT_PCM (0x0001)
#define CANONICAL_HEADER_SIZE (44)
#define CACHE_SIZE (256*1024) //256K
static void init_offset(slong **offset,int nchan,int nblock,int ftype)
{
slong mean = 0;
int chan, i;
/* initialise offset */
switch(ftype)
{
case TYPE_AU1:
case TYPE_S8:
case TYPE_S16HL:
case TYPE_S16LH:
case TYPE_ULAW:
case TYPE_AU2:
case TYPE_AU3:
case TYPE_ALAW:
mean = 0;
break;
case TYPE_U8:
mean = 0x80;
break;
case TYPE_U16HL:
case TYPE_U16LH:
mean = 0x8000;
break;
default:
//fprintf(stderr, "Unknown file type: %d\n", ftype);
break;
}
for(chan = 0; chan < nchan; chan++)
for(i = 0; i < nblock; i++)
offset[chan][i] = mean;
}
shn_reader::shn_reader()
{
init();
pthread_mutex_init(&mRingLock, NULL);
pthread_cond_init(&mRunCond, NULL);
}
shn_reader::~shn_reader()
{
exit();
pthread_mutex_destroy(&mRingLock);
pthread_cond_destroy(&mRunCond);
}
void shn_reader::init()
{
mSeekTable = NULL;
mSeekTo = -1;
mFP = NULL;
mFatalError = false;
mGoing = false;
mEOF = false;
memset(&mDecodeState, 0, sizeof(mDecodeState));
memset(&mWAVEHeader, 0, sizeof(mWAVEHeader));
mRing.Init(CACHE_SIZE);
}
void shn_reader::exit()
{
if (mGoing)
{
mGoing = false;
pthread_cond_signal(&mRunCond);
pthread_join(mThread, NULL);
}
if (mDecodeState.getbuf)
{
free(mDecodeState.getbuf);
mDecodeState.getbuf = NULL;
}
if (mDecodeState.writebuf)
{
free(mDecodeState.writebuf);
mDecodeState.writebuf = NULL;
}
if (mDecodeState.writefub)
{
free(mDecodeState.writefub);
mDecodeState.writefub = NULL;
}
if (mSeekTable)
{
free(mSeekTable);
mSeekTable = NULL;
}
if (mFP)
{
fclose(mFP);
mFP = NULL;
}
}
int shn_reader::open(const char *fn, bool should_load_seek_table)
{
struct stat sz;
exit();
init();
if (stat(fn, &sz))
return -1;
if (!S_ISREG(sz.st_mode))
return -1;
mWAVEHeader.actual_size = (ulong)sz.st_size;
mFP = fopen(fn, "r");
if (!mFP)
return -1;
if (!get_wave_header() || !verify_header())
{
fclose(mFP);
mFP = NULL;
return -1;
}
if (should_load_seek_table)
load_seek_table(fn);
return 0;
}
int shn_reader::go()
{
if (!mFP)
return -1;
fseek(mFP, 0, SEEK_SET);
mGoing = true;
if (pthread_create(&mThread, NULL, (void *(*)(void *))thread_runner, this))
{
mGoing = false;
return -1;
}
return 0;
}
long shn_reader::read(void *buf, long size)
{
long actread;
if (!mGoing)
return -2;
pthread_mutex_lock(&mRingLock);
actread = mRing.ReadData((char *) buf, size);
if (!actread)
actread = mEOF ? -2 : -1;
pthread_mutex_unlock(&mRingLock);
if (actread > 0)
pthread_cond_signal(&mRunCond);
return actread;
}
float shn_reader::seek(float seconds)
{
if (!mSeekTable)
return -1.0f;
mSeekTo = (int) seconds;
shn_seek_entry *seek_info = seek_entry_search(mSeekTo * (ulong)mWAVEHeader.samples_per_sec,0,
(ulong)(mSeekTableEntries - 1));
ulong sample = uchar_to_ulong_le(seek_info->data);
return (float)sample/((float)mWAVEHeader.samples_per_sec);
}
int shn_reader::shn_get_buffer_block_size(int blocks)//derek
{
int blk_size = blocks * (mWAVEHeader.bits_per_sample / 8) * mWAVEHeader.channels;
if(blk_size > OUT_BUFFER_SIZE)
{
blk_size = NUM_DEFAULT_BUFFER_BLOCKS * (mWAVEHeader.bits_per_sample / 8) * mWAVEHeader.channels;
}
return blk_size;
}
unsigned int shn_reader::shn_get_song_length()//derek
{
if(mWAVEHeader.length > 0)
return (unsigned int)(1000 * mWAVEHeader.length);
/* Something failed or just isn't correct */
return (unsigned int)0;
}
bool shn_reader::file_info(long *size, int *nch, float *rate, float *time,
int *samplebits, bool *seekable)
{
float frate;
int frame_size;
if (!mFP)
return false;
if (seekable)
*seekable = mSeekTable ? true : false;
if (size)
*size = mWAVEHeader.actual_size;
if (nch)
*nch = mWAVEHeader.channels;
frame_size = (uint)mWAVEHeader.channels * (uint)mWAVEHeader.bits_per_sample / 8;
frate = (float)mWAVEHeader.samples_per_sec;
if (rate)
*rate = frate;
if (time)
*time = (float)mWAVEHeader.data_size/((float)frame_size * frate);
if (samplebits)
*samplebits = mWAVEHeader.bits_per_sample;
return true;
}
void shn_reader::init_decode_state()
{
if (mDecodeState.getbuf)
free(mDecodeState.getbuf);
if (mDecodeState.writebuf)
free(mDecodeState.writebuf);
if (mDecodeState.writefub)
free(mDecodeState.writefub);
memset(&mDecodeState, 0, sizeof(mDecodeState));
}
int shn_reader::get_wave_header()
{
int version = FORMAT_VERSION;
int ftype = TYPE_EOF;
const char *magic = MAGIC;
int internal_ftype;
int retval = 1;
init_decode_state();
mBytesInHeader = 0;
/***********************/
/* EXTRACT starts here */
/***********************/
/* read magic number */
#ifdef STRICT_FORMAT_COMPATABILITY
if(FORMAT_VERSION < 2)
{
for(int i = 0; i < strlen(magic); i++)
if(getc(mFP) != magic[i])
return 0;
/* get version number */
version = getc(mFP);
if (version == EOF)
return 0;
}
else
#endif /* STRICT_FORMAT_COMPATABILITY */
{
int nscan = 0;
version = MAX_VERSION + 1;
while(version > MAX_VERSION)
{
int byte = getc(mFP);
if(byte == EOF)
return 0;
if(magic[nscan] != '\0' && byte == magic[nscan])
nscan++;
else
if(magic[nscan] == '\0' && byte <= MAX_VERSION)
version = byte;
else
{
if(byte == magic[0])
nscan = 1;
else
{
nscan = 0;
}
version = MAX_VERSION + 1;
}
}
}
/* check version number */
if(version > MAX_SUPPORTED_VERSION)
return 0;
/* initialise the variable length file read for the compressed stream */
var_get_init();
if (mFatalError)
return 0;
/* get the internal file type */
internal_ftype = (int)UINT_GET(TYPESIZE);
/* has the user requested a change in file type? */
if(internal_ftype != ftype) {
if(ftype == TYPE_EOF)
ftype = internal_ftype; /* no problems here */
else /* check that the requested conversion is valid */
if(internal_ftype == TYPE_AU1 || internal_ftype == TYPE_AU2 ||
internal_ftype == TYPE_AU3 || ftype == TYPE_AU1 ||ftype == TYPE_AU2 || ftype == TYPE_AU3)
goto got_enough_data;
}
UINT_GET(CHANSIZE);
/* get blocksize if version > 0 */
if(version > 0)
{
int byte, nskip;
UINT_GET((int) (log((double) DEFAULT_BLOCK_SIZE) / M_LN2));
UINT_GET(LPCQSIZE);
UINT_GET(0);
nskip = (int)UINT_GET(NSKIPSIZE);
for(int i = 0; i < nskip; i++)
{
byte = (int)uvar_get(XBYTESIZE);
}
}
/* find verbatim command */
while(1)
{
int cmd = (int)uvar_get(FNSIZE);
switch(cmd)
{
case FN_VERBATIM:
{
int cklen = (int)uvar_get(VERBATIM_CKSIZE_SIZE);
while (cklen--)
{
if (mBytesInHeader >= OUT_BUFFER_SIZE)
{
retval = 0;
goto got_enough_data;
}
mBytesInBuf = 0;
mHeader[mBytesInHeader++] = (unsigned char)uvar_get(VERBATIM_BYTE_SIZE);
}
}
break;
default:
goto got_enough_data;
}
}
got_enough_data:
/* wind up */
var_get_quit();
mBytesInBuf = 0;
return retval;
}
int shn_reader::verify_header()
{
ulong l;
int cur = 0;
if (mBytesInHeader < CANONICAL_HEADER_SIZE)
return 0;
if (WAVE_RIFF != uchar_to_ulong_le(&mHeader[cur]))
return 0;
cur += 4;
mWAVEHeader.chunk_size = uchar_to_ulong_le(&mHeader[cur]);
cur += 4;
if (WAVE_WAVE != uchar_to_ulong_le(&mHeader[cur]))
return 0;
cur += 4;
for (;;)
{
cur += 4;
l = uchar_to_ulong_le(&mHeader[cur]);
cur += 4;
if (WAVE_FMT == uchar_to_ulong_le(&mHeader[cur-8]))
break;
cur += l;
}
if (l < 16)
return 0;
mWAVEHeader.wave_format = uchar_to_ushort_le(&mHeader[cur]);
cur += 2;
switch (mWAVEHeader.wave_format)
{
case WAVE_FORMAT_PCM:
break;
default:
return 0;
}
mWAVEHeader.channels = uchar_to_ushort_le(&mHeader[cur]);
cur += 2;
mWAVEHeader.samples_per_sec = uchar_to_ulong_le(&mHeader[cur]);
cur += 4;
mWAVEHeader.avg_bytes_per_sec = uchar_to_ulong_le(&mHeader[cur]);
cur += 4;
mWAVEHeader.block_align = uchar_to_ushort_le(&mHeader[cur]);
cur += 2;
mWAVEHeader.bits_per_sample = uchar_to_ushort_le(&mHeader[cur]);
cur += 2;
if (mWAVEHeader.bits_per_sample != 8 && mWAVEHeader.bits_per_sample != 16)
return 0;
l -= 16;
if (l > 0)
cur += l;
for (;;)
{
cur += 4;
l = uchar_to_ulong_le(&mHeader[cur]);
cur += 4;
if (WAVE_DATA == uchar_to_ulong_le(&mHeader[cur-8]))
break;
cur += l;
}
mWAVEHeader.rate = ((uint)mWAVEHeader.samples_per_sec *
(uint)mWAVEHeader.channels *
(uint)mWAVEHeader.bits_per_sample) / 8;
mWAVEHeader.header_size = cur;
mWAVEHeader.data_size = l;
mWAVEHeader.total_size = mWAVEHeader.chunk_size + 8;
mWAVEHeader.length = mWAVEHeader.data_size / mWAVEHeader.rate;
/* header looks ok */
return 1;
}
void shn_reader::write_and_wait(int block_size)
{
int bytes_to_write;
if (mBytesInBuf < block_size)
return;
bytes_to_write = MIN(mBytesInBuf, block_size);
if (bytes_to_write <= 0)
return;
while (mGoing && mSeekTo == -1)
{
long written;
pthread_mutex_lock(&mRingLock);
written = mRing.WriteData((char *) mBuffer, bytes_to_write);
bytes_to_write -= written;
mBytesInBuf -= written;
if (!bytes_to_write)
{
pthread_mutex_unlock(&mRingLock);
break;
}
pthread_cond_wait(&mRunCond, &mRingLock);
pthread_mutex_unlock(&mRingLock);
}
}
void shn_reader::Run()
{
slong **buffer = NULL, **offset = NULL;
slong lpcqoffset = 0;
int version = FORMAT_VERSION, bitshift = 0;
int ftype = TYPE_EOF;
const char *magic = MAGIC;
int blocksize = DEFAULT_BLOCK_SIZE, nchan = DEFAULT_NCHAN;
int i, chan, nwrap, nskip = DEFAULT_NSKIP;
int *qlpc = NULL, maxnlpc = DEFAULT_MAXNLPC, nmean = UNDEFINED_UINT;
int cmd;
int internal_ftype;
int blk_size;
int cklen;
uchar tmp;
restart:
mBytesInBuf = 0;
init_decode_state();
blk_size = 512 * (mWAVEHeader.bits_per_sample / 8) * mWAVEHeader.channels;
/***********************/
/* EXTRACT starts here */
/***********************/
/* read magic number */
#ifdef STRICT_FORMAT_COMPATABILITY
if(FORMAT_VERSION < 2)
{
for(i = 0; i < strlen(magic); i++)
if(getc(this_shn->vars.fd) != magic[i])
{
mFatalError = true;
goto exit_thread;
}
/* get version number */
version = getc(this_shn->vars.fd);
if (version == EOF)
{
mFatalError = true;
goto exit_thread;
}
}
else
#endif /* STRICT_FORMAT_COMPATABILITY */
{
int nscan = 0;
version = MAX_VERSION + 1;
while(version > MAX_VERSION)
{
int byte = getc(mFP);
if(byte == EOF) {
mFatalError = true;
goto exit_thread;
}
if(magic[nscan] != '\0' && byte == magic[nscan])
nscan++;
else
if(magic[nscan] == '\0' && byte <= MAX_VERSION)
version = byte;
else
{
if(byte == magic[0])
nscan = 1;
else
{
nscan = 0;
}
version = MAX_VERSION + 1;
}
}
}
/* check version number */
if(version > MAX_SUPPORTED_VERSION) {
mFatalError = true;
goto exit_thread;
}
/* set up the default nmean, ignoring the command line state */
nmean = (version < 2) ? DEFAULT_V0NMEAN : DEFAULT_V2NMEAN;
/* initialise the variable length file read for the compressed stream */
var_get_init();
if (mFatalError)
goto exit_thread;
/* initialise the fixed length file write for the uncompressed stream */
fwrite_type_init();
/* get the internal file type */
internal_ftype = (int)UINT_GET(TYPESIZE);
/* has the user requested a change in file type? */
if(internal_ftype != ftype) {
if(ftype == TYPE_EOF)
ftype = internal_ftype; /* no problems here */
else /* check that the requested conversion is valid */
if(internal_ftype == TYPE_AU1 || internal_ftype == TYPE_AU2 ||
internal_ftype == TYPE_AU3 || ftype == TYPE_AU1 ||ftype == TYPE_AU2 || ftype == TYPE_AU3) {
mFatalError = true;
goto cleanup;
}
}
nchan = (int)UINT_GET(CHANSIZE);
/* get blocksize if version > 0 */
if(version > 0)
{
int byte;
blocksize = (int)UINT_GET((int) (log((double) DEFAULT_BLOCK_SIZE) / M_LN2));
maxnlpc = (int)UINT_GET(LPCQSIZE);
nmean = (int)UINT_GET(0);
nskip = (int)UINT_GET(NSKIPSIZE);
for(i = 0; i < nskip; i++)
{
byte = (int)uvar_get(XBYTESIZE);
}
}
else
blocksize = DEFAULT_BLOCK_SIZE;
nwrap = MAX(NWRAP, maxnlpc);
/* grab some space for the input buffer */
buffer = long2d((ulong) nchan, (ulong) (blocksize + nwrap));
if (mFatalError)
goto exit_thread;
offset = long2d((ulong) nchan, (ulong) MAX(1, nmean));
if (mFatalError) {
if (buffer) {
free(buffer);
buffer = NULL;
}
goto exit_thread;
}
for(chan = 0; chan < nchan; chan++)
{
for(i = 0; i < nwrap; i++)
buffer[chan][i] = 0;
buffer[chan] += nwrap;
}
if(maxnlpc > 0) {
qlpc = (int*) pmalloc((ulong) (maxnlpc * sizeof(*qlpc)));
if (mFatalError) {
if (buffer) {
free(buffer);
buffer = NULL;
}
if (offset) {
free(offset);
buffer = NULL;
}
goto exit_thread;
}
}
if(version > 1)
lpcqoffset = V2LPCQOFFSET;
init_offset(offset, nchan, MAX(1, nmean), internal_ftype);
/* get commands from file and execute them */
chan = 0;
while(1)
{
cmd = (int)uvar_get(FNSIZE);
if (mFatalError)
goto cleanup;
switch(cmd)
{
case FN_ZERO:
case FN_DIFF0:
case FN_DIFF1:
case FN_DIFF2:
case FN_DIFF3:
case FN_QLPC:
{
slong coffset, *cbuffer = buffer[chan];
int resn = 0, nlpc, j;
if(cmd != FN_ZERO)
{
resn = (int)uvar_get(ENERGYSIZE);
if (mFatalError)
goto cleanup;
/* this is a hack as version 0 differed in definition of var_get */
if(version == 0)
resn--;
}
/* find mean offset : N.B. this code duplicated */
if(nmean == 0)
coffset = offset[chan][0];
else
{
slong sum = (version < 2) ? 0 : nmean / 2;
for(i = 0; i < nmean; i++)
sum += offset[chan][i];
if(version < 2)
coffset = sum / nmean;
else
coffset = ROUNDEDSHIFTDOWN(sum / nmean, bitshift);
}
switch(cmd)
{
case FN_ZERO:
for(i = 0; i < blocksize; i++)
cbuffer[i] = 0;
break;
case FN_DIFF0:
for(i = 0; i < blocksize; i++) {
cbuffer[i] = (int)var_get(resn) + coffset;
if (mFatalError)
goto cleanup;
}
break;
case FN_DIFF1:
for(i = 0; i < blocksize; i++) {
cbuffer[i] = (int)(var_get(resn) + cbuffer[i - 1]);
if (mFatalError)
goto cleanup;
}
break;
case FN_DIFF2:
for(i = 0; i < blocksize; i++) {
cbuffer[i] = (int)(var_get(resn) + (2 * cbuffer[i - 1] - cbuffer[i - 2]));
if (mFatalError)
goto cleanup;
}
break;
case FN_DIFF3:
for(i = 0; i < blocksize; i++) {
cbuffer[i] = (int)(var_get(resn) + 3 * (cbuffer[i - 1] - cbuffer[i - 2]) + cbuffer[i - 3]);
if (mFatalError)
goto cleanup;
}
break;
case FN_QLPC:
nlpc = (int)uvar_get(LPCQSIZE);
if (mFatalError)
goto cleanup;
for(i = 0; i < nlpc; i++) {
qlpc[i] = (int)var_get(LPCQUANT);
if (mFatalError)
goto cleanup;
}
for(i = 0; i < nlpc; i++)
cbuffer[i - nlpc] -= coffset;
for(i = 0; i < blocksize; i++)
{
slong sum = lpcqoffset;
for(j = 0; j < nlpc; j++)
sum += qlpc[j] * cbuffer[i - j - 1];
cbuffer[i] = (int)(var_get(resn) + (sum >> LPCQUANT));
if (mFatalError)
goto cleanup;
}
if(coffset != 0)
for(i = 0; i < blocksize; i++)
cbuffer[i] += coffset;
break;
}
/* store mean value if appropriate : N.B. Duplicated code */
if(nmean > 0)
{
slong sum = (version < 2) ? 0 : blocksize / 2;
for(i = 0; i < blocksize; i++)
sum += cbuffer[i];
for(i = 1; i < nmean; i++)
offset[chan][i - 1] = offset[chan][i];
if(version < 2)
offset[chan][nmean - 1] = sum / blocksize;
else
offset[chan][nmean - 1] = (sum / blocksize) << bitshift;
}
/* do the wrap */
for(i = -nwrap; i < 0; i++)
cbuffer[i] = cbuffer[i + blocksize];
fix_bitshift(cbuffer, blocksize, bitshift, internal_ftype);
if(chan == nchan - 1)
{
if (!mGoing || mFatalError)
goto cleanup;
fwrite_type(buffer, ftype, nchan, blocksize);
write_and_wait(blk_size);
if (mSeekTo != -1)
{
shn_seek_entry *seek_info = seek_entry_search(mSeekTo * (ulong)mWAVEHeader.samples_per_sec,0,
(ulong)(mSeekTableEntries - 1));
pthread_mutex_lock(&mRingLock);
mRing.Empty();
pthread_mutex_unlock(&mRingLock);
buffer[0][-1] = uchar_to_slong_le(seek_info->data+24);
buffer[0][-2] = uchar_to_slong_le(seek_info->data+28);
buffer[0][-3] = uchar_to_slong_le(seek_info->data+32);
offset[0][0] = uchar_to_slong_le(seek_info->data+48);
offset[0][1] = uchar_to_slong_le(seek_info->data+52);
offset[0][2] = uchar_to_slong_le(seek_info->data+56);
offset[0][3] = uchar_to_slong_le(seek_info->data+60);
if (nchan > 1)
{
buffer[1][-1] = uchar_to_slong_le(seek_info->data+36);
buffer[1][-2] = uchar_to_slong_le(seek_info->data+40);
buffer[1][-3] = uchar_to_slong_le(seek_info->data+44);
offset[1][0] = uchar_to_slong_le(seek_info->data+64);
offset[1][1] = uchar_to_slong_le(seek_info->data+68);
offset[1][2] = uchar_to_slong_le(seek_info->data+72);
offset[1][3] = uchar_to_slong_le(seek_info->data+76);
}
bitshift = uchar_to_ushort_le(seek_info->data+22);
fseek(mFP,(slong)uchar_to_ulong_le(seek_info->data+8),SEEK_SET);
fread((uchar*) mDecodeState.getbuf, 1, BUFSIZ, mFP);
mDecodeState.getbufp = mDecodeState.getbuf + uchar_to_ushort_le(seek_info->data+14);
mDecodeState.nbitget = uchar_to_ushort_le(seek_info->data+16);
mDecodeState.nbyteget = uchar_to_ushort_le(seek_info->data+12);
mDecodeState.gbuffer = uchar_to_ulong_le(seek_info->data+18);
mBytesInBuf = 0;
mSeekTo = -1;
}
}
chan = (chan + 1) % nchan;
break;
}
break;
case FN_QUIT:
/* empty out last of buffer */
write_and_wait(mBytesInBuf);
mEOF = true;
while (1)
{
if (!mGoing)
goto finish;
if (mSeekTo != -1)
{
var_get_quit();
fwrite_type_quit();
if (buffer) free((void *) buffer);
if (offset) free((void *) offset);
if(maxnlpc > 0 && qlpc)
free((void *) qlpc);
fseek(mFP,0,SEEK_SET);
goto restart;
}
}
goto cleanup;
break;
case FN_BLOCKSIZE:
blocksize = (int)UINT_GET((int) (log((double) blocksize) / M_LN2));
if (mFatalError)
goto cleanup;
break;
case FN_BITSHIFT:
bitshift = (int)uvar_get(BITSHIFTSIZE);
if (mFatalError)
goto cleanup;
break;
case FN_VERBATIM:
cklen = (int)uvar_get(VERBATIM_CKSIZE_SIZE);
if (mFatalError)
goto cleanup;
while (cklen--) {
tmp = (uchar)uvar_get(VERBATIM_BYTE_SIZE);
if (mFatalError)
goto cleanup;
}
break;
default:
mFatalError = true;
//fprintf(stderr,"Sanity check fails trying to decode function: %d\n",cmd);
goto cleanup;
}
}
cleanup:
write_and_wait(mBytesInBuf);
finish:
mSeekTo = -1;
mEOF = true;
/* wind up */
var_get_quit();
fwrite_type_quit();
if (buffer) free((void *) buffer);
if (offset) free((void *) offset);
if(maxnlpc > 0 && qlpc)
free((void *) qlpc);
exit_thread:
pthread_exit(NULL);
}