2006-04-17 09:06:32 -04:00
# include <stdio.h>
# include <stdlib.h>
# include <inttypes.h>
# include <errno.h>
# include <string.h>
# include "decode.h"
# ifndef TRUE
# define TRUE 1
# endif
# ifndef FALSE
# define FALSE 0
# endif
2006-06-03 16:25:44 -04:00
/* Derek start */
pthread_mutex_t mRingLock ;
pthread_cond_t mRunCond ;
pthread_t mThread ;
/* Derek end */
2006-04-17 09:06:32 -04:00
int shn_seek ( shn_file * this_shn , unsigned int time ) ;
int shn_seekable ( shn_file * this_shn ) ;
shn_file * shn_load ( char * filename , shn_config config ) ;
int shn_init_decoder ( shn_file * this_shn ) ;
int shn_cleanup_decoder ( shn_file * this_shn ) ;
unsigned int shn_get_song_length ( shn_file * this_shn ) ;
int shn_get_buffer_block_size ( shn_file * this_shn , int blocks ) ;
int shn_read ( shn_file * this_shn , uchar * read_buffer , int bytes_to_read ) ;
void shn_unload ( shn_file * this_shn ) ;
unsigned int shn_get_samplerate ( shn_file * this_shn ) ;
unsigned int shn_get_channels ( shn_file * this_shn ) ;
unsigned int shn_get_bitspersample ( shn_file * this_shn ) ;
static void swap_bytes ( shn_file * this_shn , int bytes ) ;
static int get_wave_header ( shn_file * this_shn ) ;
static int shn_init_decode_state ( shn_file * this_shn ) ;
static int write_to_buffer ( shn_file * this_shn , uchar * read_buffer , int bytes_to_read ) ;
static int buffer_is_full = 0 ;
static int buffer_ret = 0 ;
static slong * * buffer = NULL , * * offset = NULL ;
static slong lpcqoffset = 0 ;
static int version = FORMAT_VERSION , bitshift = 0 ;
static int ftype = TYPE_EOF ;
static char * magic = MAGIC ;
static int blocksize = DEFAULT_BLOCK_SIZE , nchan = DEFAULT_NCHAN ;
static int i , chan , nwrap , nskip = DEFAULT_NSKIP ;
static int * qlpc = NULL , maxnlpc = DEFAULT_MAXNLPC , nmean = UNDEFINED_UINT ;
static int cmd ;
static int internal_ftype ;
static int cklen ;
static uchar tmp ;
static ulong seekto_offset ;
int shn_seekable ( shn_file * this_shn ) {
if ( ! this_shn )
return 0 ;
if ( this_shn - > vars . seek_table_entries = = NO_SEEK_TABLE ) {
shn_debug ( this_shn - > config , " File not seekable " ) ;
return 0 ;
}
/* File is seekable */
return 1 ;
}
int shn_seek ( shn_file * this_shn , unsigned int time )
{
if ( NULL = = this_shn )
return 0 ;
if ( this_shn - > vars . seek_table_entries = = NO_SEEK_TABLE )
{
shn_error ( this_shn - > config , " Cannot seek to %d:%02d because there is no seek information for this file. " , time / 60 , time % 60 ) ;
return 0 ;
}
if ( time > ( unsigned int ) ( shn_get_song_length ( this_shn ) / 1000 ) ) {
shn_error ( this_shn - > config , " You cannot seek to this position! It is out of range! " ) ;
return 0 ;
}
this_shn - > vars . seek_to = time ;
this_shn - > vars . eof = FALSE ;
return 1 ;
}
static void swap_bytes ( shn_file * this_shn , int bytes )
{
int i ;
uchar tmp ;
for ( i = 0 ; i < bytes ; i = i + 2 ) {
tmp = this_shn - > vars . buffer [ i + 1 ] ;
this_shn - > vars . buffer [ i + 1 ] = this_shn - > vars . buffer [ i ] ;
this_shn - > vars . buffer [ i ] = tmp ;
}
}
static int get_wave_header ( shn_file * this_shn )
{
if ( ! this_shn )
return 0 ;
slong * * buffer = NULL , * * offset = NULL ;
slong lpcqoffset = 0 ;
int version = FORMAT_VERSION , bitshift = 0 ;
int ftype = TYPE_EOF ;
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 cklen ;
int retval = 0 ;
if ( ! shn_init_decode_state ( this_shn ) )
return 0 ;
/***********************/
/* EXTRACT starts here */
/***********************/
/* read magic number */
# ifdef STRICT_FORMAT_COMPATABILITY
if ( FORMAT_VERSION < 2 )
{
for ( i = 0 ; i < strlen ( magic ) ; i + + ) {
if ( getc_exit ( this_shn - > vars . fd ) ! = magic [ i ] )
return 0 ;
this_shn - > vars . bytes_read + + ;
}
/* get version number */
version = getc_exit ( this_shn - > vars . fd ) ;
this_shn - > vars . bytes_read + + ;
}
else
# endif /* STRICT_FORMAT_COMPATABILITY */
{
int nscan = 0 ;
version = MAX_VERSION + 1 ;
while ( version > MAX_VERSION )
{
int byte = getc ( this_shn - > vars . fd ) ;
this_shn - > vars . bytes_read + + ;
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 ;
/* 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 ( this_shn ) ;
if ( this_shn - > vars . fatal_error )
return 0 ;
/* initialise the fixed length file write for the uncompressed stream */
fwrite_type_init ( this_shn ) ;
/* get the internal file type */
internal_ftype = UINT_GET ( TYPESIZE , this_shn ) ;
/* 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 )
{
retval = 0 ;
goto got_enough_data ;
}
}
}
nchan = UINT_GET ( CHANSIZE , this_shn ) ;
/* get blocksize if version > 0 */
if ( version > 0 )
{
int byte ;
blocksize = UINT_GET ( ( int ) ( log ( ( double ) DEFAULT_BLOCK_SIZE ) / M_LN2 ) , this_shn ) ;
maxnlpc = UINT_GET ( LPCQSIZE , this_shn ) ;
nmean = UINT_GET ( 0 , this_shn ) ;
nskip = UINT_GET ( NSKIPSIZE , this_shn ) ;
for ( i = 0 ; i < nskip ; i + + )
{
byte = uvar_get ( XBYTESIZE , this_shn ) ;
}
}
else
blocksize = DEFAULT_BLOCK_SIZE ;
nwrap = MAX ( NWRAP , maxnlpc ) ;
/* grab some space for the input buffer */
buffer = long2d ( ( ulong ) nchan , ( ulong ) ( blocksize + nwrap ) , this_shn ) ;
if ( this_shn - > vars . fatal_error )
return 0 ;
offset = long2d ( ( ulong ) nchan , ( ulong ) MAX ( 1 , nmean ) , this_shn ) ;
if ( this_shn - > vars . fatal_error ) {
if ( buffer ) {
free ( buffer ) ;
buffer = NULL ;
}
return 0 ;
}
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 ) ) , this_shn ) ;
if ( this_shn - > vars . fatal_error ) {
if ( buffer ) {
free ( buffer ) ;
buffer = NULL ;
}
if ( offset ) {
free ( offset ) ;
buffer = NULL ;
}
return 0 ;
}
}
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 )
{
this_shn - > vars . reading_function_code = 1 ;
cmd = uvar_get ( FNSIZE , this_shn ) ;
this_shn - > vars . reading_function_code = 0 ;
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 = uvar_get ( ENERGYSIZE , this_shn ) ;
if ( this_shn - > vars . fatal_error ) {
retval = 0 ;
goto got_enough_data ;
}
/* 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 ] = var_get ( resn , this_shn ) + coffset ;
if ( this_shn - > vars . fatal_error ) {
retval = 0 ;
goto got_enough_data ;
}
}
break ;
case FN_DIFF1 :
for ( i = 0 ; i < blocksize ; i + + ) {
cbuffer [ i ] = var_get ( resn , this_shn ) + cbuffer [ i - 1 ] ;
if ( this_shn - > vars . fatal_error ) {
retval = 0 ;
goto got_enough_data ;
}
}
break ;
case FN_DIFF2 :
for ( i = 0 ; i < blocksize ; i + + ) {
cbuffer [ i ] = var_get ( resn , this_shn ) + ( 2 * cbuffer [ i - 1 ] - cbuffer [ i - 2 ] ) ;
if ( this_shn - > vars . fatal_error ) {
retval = 0 ;
goto got_enough_data ;
}
}
break ;
case FN_DIFF3 :
for ( i = 0 ; i < blocksize ; i + + ) {
cbuffer [ i ] = var_get ( resn , this_shn ) + 3 * ( cbuffer [ i - 1 ] - cbuffer [ i - 2 ] ) + cbuffer [ i - 3 ] ;
if ( this_shn - > vars . fatal_error ) {
retval = 0 ;
goto got_enough_data ;
}
}
break ;
case FN_QLPC :
nlpc = uvar_get ( LPCQSIZE , this_shn ) ;
if ( this_shn - > vars . fatal_error ) {
retval = 0 ;
goto got_enough_data ;
}
for ( i = 0 ; i < nlpc ; i + + ) {
qlpc [ i ] = var_get ( LPCQUANT , this_shn ) ;
if ( this_shn - > vars . fatal_error ) {
retval = 0 ;
goto got_enough_data ;
}
}
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 ] = var_get ( resn , this_shn ) + ( sum > > LPCQUANT ) ;
if ( this_shn - > vars . fatal_error ) {
retval = 0 ;
goto got_enough_data ;
}
}
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 ;
}
if ( 0 = = chan ) {
this_shn - > vars . initial_file_position = this_shn - > vars . last_file_position_no_really ;
goto got_enough_data ;
}
/* 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 )
{
fwrite_type ( buffer , ftype , nchan , blocksize , this_shn ) ;
this_shn - > vars . bytes_in_buf = 0 ;
}
chan = ( chan + 1 ) % nchan ;
break ;
}
break ;
case FN_BLOCKSIZE :
UINT_GET ( ( int ) ( log ( ( double ) blocksize ) / M_LN2 ) , this_shn ) ;
break ;
case FN_VERBATIM :
cklen = uvar_get ( VERBATIM_CKSIZE_SIZE , this_shn ) ;
while ( cklen - - ) {
if ( this_shn - > vars . bytes_in_header > = OUT_BUFFER_SIZE ) {
shn_debug ( this_shn - > config , " Unexpectedly large header - " PACKAGE " can only handle a maximum of %d bytes " , OUT_BUFFER_SIZE ) ;
goto got_enough_data ;
}
this_shn - > vars . bytes_in_buf = 0 ;
this_shn - > vars . header [ this_shn - > vars . bytes_in_header + + ] = ( char ) uvar_get ( VERBATIM_BYTE_SIZE , this_shn ) ;
}
retval = 1 ;
break ;
case FN_BITSHIFT :
bitshift = uvar_get ( BITSHIFTSIZE , this_shn ) ;
this_shn - > vars . bitshift = bitshift ;
break ;
default :
goto got_enough_data ;
}
}
got_enough_data :
/* wind up */
var_get_quit ( this_shn ) ;
fwrite_type_quit ( this_shn ) ;
if ( buffer ) free ( ( void * ) buffer ) ;
if ( offset ) free ( ( void * ) offset ) ;
if ( maxnlpc > 0 & & qlpc )
free ( ( void * ) qlpc ) ;
this_shn - > vars . bytes_in_buf = 0 ;
return retval ;
}
void shn_unload ( shn_file * this_shn )
{
if ( this_shn )
{
if ( this_shn - > vars . fd )
{
fclose ( this_shn - > vars . fd ) ;
this_shn - > vars . fd = NULL ;
}
if ( this_shn - > decode_state )
{
if ( this_shn - > decode_state - > getbuf )
{
free ( this_shn - > decode_state - > getbuf ) ;
this_shn - > decode_state - > getbuf = NULL ;
}
if ( this_shn - > decode_state - > writebuf )
{
free ( this_shn - > decode_state - > writebuf ) ;
this_shn - > decode_state - > writebuf = NULL ;
}
if ( this_shn - > decode_state - > writefub )
{
free ( this_shn - > decode_state - > writefub ) ;
this_shn - > decode_state - > writefub = NULL ;
}
free ( this_shn - > decode_state ) ;
this_shn - > decode_state = NULL ;
}
if ( this_shn - > seek_table )
{
free ( this_shn - > seek_table ) ;
this_shn - > seek_table = NULL ;
}
free ( this_shn ) ;
this_shn = NULL ;
}
}
shn_file * shn_load ( char * filename , shn_config config )
{
shn_file * tmp_file ;
shn_seek_entry * first_seek_table ;
if ( ! ( tmp_file = malloc ( sizeof ( shn_file ) ) ) )
{
fprintf ( stderr , " Could not allocate memory for SHN data structure " ) ;
return NULL ;
}
memset ( tmp_file , 0 , sizeof ( shn_file ) ) ;
/*Copying config */
tmp_file - > config = config ;
tmp_file - > vars . fd = NULL ;
tmp_file - > vars . seek_to = - 1 ;
tmp_file - > vars . eof = 0 ;
tmp_file - > vars . going = 0 ;
tmp_file - > vars . seek_table_entries = NO_SEEK_TABLE ;
tmp_file - > vars . bytes_in_buf = 0 ;
tmp_file - > vars . bytes_in_header = 0 ;
tmp_file - > vars . reading_function_code = 0 ;
tmp_file - > vars . initial_file_position = 0 ;
tmp_file - > vars . last_file_position = 0 ;
tmp_file - > vars . last_file_position_no_really = 0 ;
tmp_file - > vars . bytes_read = 0 ;
tmp_file - > vars . bitshift = 0 ;
tmp_file - > vars . seek_offset = 0 ;
tmp_file - > decode_state = NULL ;
tmp_file - > wave_header . filename = filename ;
tmp_file - > wave_header . wave_format = 0 ;
tmp_file - > wave_header . channels = 0 ;
tmp_file - > wave_header . block_align = 0 ;
tmp_file - > wave_header . bits_per_sample = 0 ;
tmp_file - > wave_header . samples_per_sec = 0 ;
tmp_file - > wave_header . avg_bytes_per_sec = 0 ;
tmp_file - > wave_header . rate = 0 ;
tmp_file - > wave_header . header_size = 0 ;
tmp_file - > wave_header . data_size = 0 ;
tmp_file - > wave_header . file_has_id3v2_tag = 0 ;
tmp_file - > wave_header . id3v2_tag_size = 0 ;
tmp_file - > seek_header . version = NO_SEEK_TABLE ;
tmp_file - > seek_header . shnFileSize = 0 ;
tmp_file - > seek_trailer . seekTableSize = 0 ;
tmp_file - > seek_table = NULL ;
if ( ! ( tmp_file - > vars . fd = shn_open_and_discard_id3v2_tag ( filename , & tmp_file - > wave_header . file_has_id3v2_tag , & tmp_file - > wave_header . id3v2_tag_size ) ) )
{
shn_debug ( tmp_file - > config , " Could not open file: '%s' " , filename ) ;
shn_unload ( tmp_file ) ;
return NULL ;
}
if ( 0 = = get_wave_header ( tmp_file ) )
{
shn_debug ( tmp_file - > config , " Unable to read WAVE header from file '%s' " , filename ) ;
shn_unload ( tmp_file ) ;
return NULL ;
}
if ( tmp_file - > wave_header . file_has_id3v2_tag )
{
fseek ( tmp_file - > vars . fd , tmp_file - > wave_header . id3v2_tag_size , SEEK_SET ) ;
tmp_file - > vars . bytes_read + = tmp_file - > wave_header . id3v2_tag_size ;
tmp_file - > vars . seek_offset = tmp_file - > wave_header . id3v2_tag_size ;
}
else
{
fseek ( tmp_file - > vars . fd , 0 , SEEK_SET ) ;
}
if ( 0 = = shn_verify_header ( tmp_file ) )
{
shn_debug ( tmp_file - > config , " Invalid WAVE header in file: '%s' " , filename ) ;
shn_unload ( tmp_file ) ;
return NULL ;
}
if ( tmp_file - > decode_state )
{
free ( tmp_file - > decode_state ) ;
tmp_file - > decode_state = NULL ;
}
shn_load_seek_table ( tmp_file , filename ) ;
if ( NO_SEEK_TABLE ! = tmp_file - > vars . seek_table_entries )
{
first_seek_table = ( shn_seek_entry * ) tmp_file - > seek_table ;
/* check for broken seek tables - if found, disable seeking */
if ( 0 = = tmp_file - > seek_header . version )
{
/* test, if the bitshift value in the file is identical to the bitshift value of the first seektable entry */
if ( tmp_file - > vars . bitshift ! = shn_uchar_to_ushort_le ( first_seek_table - > data + 22 ) )
{
shn_debug ( tmp_file - > config , " Broken seek table detected - seeking disabled for file '%s'. " , tmp_file - > wave_header . filename ) ;
tmp_file - > vars . seek_table_entries = NO_SEEK_TABLE ;
}
}
tmp_file - > vars . seek_offset + = tmp_file - > vars . initial_file_position - shn_uchar_to_ulong_le ( first_seek_table - > data + 8 ) ;
if ( 0 ! = tmp_file - > vars . seek_offset )
{
shn_debug ( tmp_file - > config , " Adjusting seek table offsets by %ld bytes due to mismatch between seek table values and input file - seeking might not work correctly. " ,
tmp_file - > vars . seek_offset ) ;
}
}
fseek ( tmp_file - > vars . fd , 0 , SEEK_SET ) ;
tmp_file - > vars . going = 1 ;
tmp_file - > vars . seek_to = - 1 ;
shn_debug ( tmp_file - > config , " Successfully loaded file: '%s' " , filename ) ;
return tmp_file ;
}
static int shn_init_decode_state ( shn_file * this_shn )
{
if ( this_shn - > decode_state )
{
if ( this_shn - > decode_state - > getbuf )
{
free ( this_shn - > decode_state - > getbuf ) ;
this_shn - > decode_state - > getbuf = NULL ;
}
if ( this_shn - > decode_state - > writebuf )
{
free ( this_shn - > decode_state - > writebuf ) ;
this_shn - > decode_state - > writebuf = NULL ;
}
if ( this_shn - > decode_state - > writefub )
{
free ( this_shn - > decode_state - > writefub ) ;
this_shn - > decode_state - > writefub = NULL ;
}
free ( this_shn - > decode_state ) ;
this_shn - > decode_state = NULL ;
}
if ( ! ( this_shn - > decode_state = malloc ( sizeof ( shn_decode_state ) ) ) )
{
shn_debug ( this_shn - > config , " Could not allocate memory for decode state data structure " ) ;
return 0 ;
}
this_shn - > decode_state - > getbuf = NULL ;
this_shn - > decode_state - > getbufp = NULL ;
this_shn - > decode_state - > nbitget = 0 ;
this_shn - > decode_state - > nbyteget = 0 ;
this_shn - > decode_state - > gbuffer = 0 ;
this_shn - > decode_state - > writebuf = NULL ;
this_shn - > decode_state - > writefub = NULL ;
this_shn - > decode_state - > nwritebuf = 0 ;
this_shn - > vars . bytes_in_buf = 0 ;
return 1 ;
}
int shn_init_decoder ( shn_file * this_shn ) {
if ( ! this_shn )
return 0 ;
if ( ! shn_init_decode_state ( this_shn ) ) {
shn_error ( this_shn - > config , " shn_init_decode state failed! \n " ) ;
return 0 ;
}
/* read magic number */
# ifdef STRICT_FORMAT_COMPATABILITY
if ( FORMAT_VERSION < 2 ) {
for ( i = 0 ; i < strlen ( magic ) ; i + + )
if ( getc_exit ( this_shn - > vars . fd ) ! = magic [ i ] ) {
shn_error_fatal ( this_shn , " Bad magic number " ) ;
return 0 ;
}
/* get version number */
version = getc_exit ( this_shn - > vars . fd ) ;
}
else
# endif /* STRICT_FORMAT_COMPATABILITY */
{
int nscan = 0 ;
version = MAX_VERSION + 1 ;
while ( version > MAX_VERSION ) {
int byte = getc ( this_shn - > vars . fd ) ;
if ( byte = = EOF ) {
shn_error_fatal ( this_shn , " No magic number " ) ;
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 ) {
shn_error_fatal ( this_shn , " Can't decode version %d " , version ) ;
return 0 ;
}
/* 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 ( this_shn ) ;
if ( this_shn - > vars . fatal_error )
return 0 ;
/* initialise the fixed length file write for the uncompressed stream */
fwrite_type_init ( this_shn ) ;
/* get the internal file type */
internal_ftype = UINT_GET ( TYPESIZE , this_shn ) ;
/* 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 ) {
shn_error_fatal ( this_shn , " Not able to perform requested output format conversion " ) ;
return 0 ;
}
}
nchan = UINT_GET ( CHANSIZE , this_shn ) ;
/* get blocksize if version > 0 */
if ( version > 0 ) {
int byte ;
blocksize = UINT_GET ( ( int ) ( log ( ( double ) DEFAULT_BLOCK_SIZE ) / M_LN2 ) , this_shn ) ;
maxnlpc = UINT_GET ( LPCQSIZE , this_shn ) ;
nmean = UINT_GET ( 0 , this_shn ) ;
nskip = UINT_GET ( NSKIPSIZE , this_shn ) ;
for ( i = 0 ; i < nskip ; i + + ) {
byte = uvar_get ( XBYTESIZE , this_shn ) ;
}
}
else
blocksize = DEFAULT_BLOCK_SIZE ;
nwrap = MAX ( NWRAP , maxnlpc ) ;
/* grab some space for the input buffer */
buffer = long2d ( ( ulong ) nchan , ( ulong ) ( blocksize + nwrap ) , this_shn ) ;
if ( this_shn - > vars . fatal_error )
return 0 ;
offset = long2d ( ( ulong ) nchan , ( ulong ) MAX ( 1 , nmean ) , this_shn ) ;
if ( this_shn - > vars . fatal_error ) {
if ( buffer ) {
free ( buffer ) ;
buffer = NULL ;
}
return 0 ;
}
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 ) ) , this_shn ) ;
if ( this_shn - > vars . fatal_error ) {
if ( buffer ) {
free ( buffer ) ;
buffer = NULL ;
}
if ( offset ) {
free ( offset ) ;
buffer = NULL ;
}
return 0 ;
}
}
if ( version > 1 )
lpcqoffset = V2LPCQOFFSET ;
init_offset ( offset , nchan , MAX ( 1 , nmean ) , internal_ftype ) ;
this_shn - > vars . eof = FALSE ;
chan = 0 ;
/* Success */
return 1 ;
}
int shn_cleanup_decoder ( shn_file * this_shn ) {
if ( ! this_shn )
return 0 ;
this_shn - > vars . seek_to = - 1 ;
this_shn - > vars . eof = TRUE ;
/* wind up */
var_get_quit ( this_shn ) ;
fwrite_type_quit ( this_shn ) ;
if ( buffer ) free ( ( void * ) buffer ) ;
if ( offset ) free ( ( void * ) offset ) ;
if ( maxnlpc > 0 & & qlpc )
free ( ( void * ) qlpc ) ;
return 1 ;
}
static int write_to_buffer ( shn_file * this_shn , uchar * read_buffer , int block_size )
{
int bytes_to_write , bytes_in_block , i ;
if ( this_shn - > vars . bytes_in_buf < block_size )
return 0 ;
bytes_in_block = min ( this_shn - > vars . bytes_in_buf , block_size ) ;
if ( bytes_in_block < = 0 )
return 0 ;
bytes_to_write = bytes_in_block ;
while ( ( bytes_to_write + bytes_in_block ) < = this_shn - > vars . bytes_in_buf )
bytes_to_write + = bytes_in_block ;
if ( this_shn - > vars . going & & this_shn - > vars . seek_to = = - 1 ) {
if ( this_shn - > config . swap_bytes )
swap_bytes ( this_shn , bytes_to_write ) ;
memcpy ( ( uchar * ) read_buffer , ( uchar * ) ( this_shn - > vars . buffer ) , bytes_to_write ) ;
} else
return 0 ;
/* shift data from end of buffer to the front */
this_shn - > vars . bytes_in_buf - = bytes_to_write ;
for ( i = 0 ; i < this_shn - > vars . bytes_in_buf ; i + + )
this_shn - > vars . buffer [ i ] = this_shn - > vars . buffer [ i + bytes_to_write ] ;
return bytes_to_write ;
}
int shn_get_buffer_block_size ( shn_file * this_shn , int blocks ) {
int blk_size = blocks * ( this_shn - > wave_header . bits_per_sample / 8 ) * this_shn - > wave_header . channels ;
if ( blk_size > OUT_BUFFER_SIZE ) {
shn_debug ( this_shn - > config , " Resetting to default blk_size! \n " ) ;
blk_size = NUM_DEFAULT_BUFFER_BLOCKS * ( this_shn - > wave_header . bits_per_sample / 8 ) * this_shn - > wave_header . channels ;
}
return blk_size ;
}
unsigned int shn_get_song_length ( shn_file * this_shn ) {
if ( this_shn ) {
if ( this_shn - > wave_header . length > 0 )
return ( unsigned int ) ( 1000 * this_shn - > wave_header . length ) ;
}
/* Something failed or just isn't correct */
return ( unsigned int ) 0 ;
}
int shn_read ( shn_file * this_shn , uchar * read_buffer , int bytes_to_read ) {
if ( ! this_shn )
return 0 ;
if ( ! read_buffer )
return 0 ;
/***********************/
/* EXTRACT starts here */
/***********************/
buffer_is_full = 0 ;
while ( ! buffer_is_full ) {
cmd = uvar_get ( FNSIZE , this_shn ) ;
if ( this_shn - > vars . fatal_error )
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 = uvar_get ( ENERGYSIZE , this_shn ) ;
if ( this_shn - > vars . fatal_error )
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 ] = var_get ( resn , this_shn ) + coffset ;
if ( this_shn - > vars . fatal_error )
goto cleanup ;
}
break ;
case FN_DIFF1 :
for ( i = 0 ; i < blocksize ; i + + ) {
cbuffer [ i ] = var_get ( resn , this_shn ) + cbuffer [ i - 1 ] ;
if ( this_shn - > vars . fatal_error )
goto cleanup ;
}
break ;
case FN_DIFF2 :
for ( i = 0 ; i < blocksize ; i + + ) {
cbuffer [ i ] = var_get ( resn , this_shn ) + ( 2 * cbuffer [ i - 1 ] - cbuffer [ i - 2 ] ) ;
if ( this_shn - > vars . fatal_error )
goto cleanup ;
}
break ;
case FN_DIFF3 :
for ( i = 0 ; i < blocksize ; i + + ) {
cbuffer [ i ] = var_get ( resn , this_shn ) + 3 * ( cbuffer [ i - 1 ] - cbuffer [ i - 2 ] ) + cbuffer [ i - 3 ] ;
if ( this_shn - > vars . fatal_error )
goto cleanup ;
}
break ;
case FN_QLPC :
nlpc = uvar_get ( LPCQSIZE , this_shn ) ;
if ( this_shn - > vars . fatal_error )
goto cleanup ;
for ( i = 0 ; i < nlpc ; i + + ) {
qlpc [ i ] = var_get ( LPCQUANT , this_shn ) ;
if ( this_shn - > vars . fatal_error )
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 ] = var_get ( resn , this_shn ) + ( sum > > LPCQUANT ) ;
if ( this_shn - > vars . fatal_error )
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 ( ! this_shn - > vars . going | | this_shn - > vars . fatal_error )
goto cleanup ;
fwrite_type ( buffer , ftype , nchan , blocksize , this_shn ) ;
buffer_ret = write_to_buffer ( this_shn , read_buffer , bytes_to_read ) ;
if ( buffer_ret = = bytes_to_read )
buffer_is_full = 1 ;
if ( this_shn - > vars . seek_to ! = - 1 ) {
shn_seek_entry * seek_info ;
shn_debug ( this_shn - > config , " Seeking to %d:%02d " , this_shn - > vars . seek_to / 60 , this_shn - > vars . seek_to % 60 ) ;
seek_info = shn_seek_entry_search ( this_shn - > config , this_shn - > seek_table , this_shn - > vars . seek_to * ( ulong ) this_shn - > wave_header . samples_per_sec , 0 , ( ulong ) ( this_shn - > vars . seek_table_entries - 1 ) , this_shn - > vars . seek_resolution ) ;
buffer [ 0 ] [ - 1 ] = shn_uchar_to_slong_le ( seek_info - > data + 24 ) ;
buffer [ 0 ] [ - 2 ] = shn_uchar_to_slong_le ( seek_info - > data + 28 ) ;
buffer [ 0 ] [ - 3 ] = shn_uchar_to_slong_le ( seek_info - > data + 32 ) ;
offset [ 0 ] [ 0 ] = shn_uchar_to_slong_le ( seek_info - > data + 48 ) ;
offset [ 0 ] [ 1 ] = shn_uchar_to_slong_le ( seek_info - > data + 52 ) ;
offset [ 0 ] [ 2 ] = shn_uchar_to_slong_le ( seek_info - > data + 56 ) ;
offset [ 0 ] [ 3 ] = shn_uchar_to_slong_le ( seek_info - > data + 60 ) ;
if ( nchan > 1 ) {
buffer [ 1 ] [ - 1 ] = shn_uchar_to_slong_le ( seek_info - > data + 36 ) ;
buffer [ 1 ] [ - 2 ] = shn_uchar_to_slong_le ( seek_info - > data + 40 ) ;
buffer [ 1 ] [ - 3 ] = shn_uchar_to_slong_le ( seek_info - > data + 44 ) ;
offset [ 1 ] [ 0 ] = shn_uchar_to_slong_le ( seek_info - > data + 64 ) ;
offset [ 1 ] [ 1 ] = shn_uchar_to_slong_le ( seek_info - > data + 68 ) ;
offset [ 1 ] [ 2 ] = shn_uchar_to_slong_le ( seek_info - > data + 72 ) ;
offset [ 1 ] [ 3 ] = shn_uchar_to_slong_le ( seek_info - > data + 76 ) ;
}
bitshift = shn_uchar_to_ushort_le ( seek_info - > data + 22 ) ;
seekto_offset = shn_uchar_to_ulong_le ( seek_info - > data + 8 ) + this_shn - > vars . seek_offset ;
fseek ( this_shn - > vars . fd , ( slong ) seekto_offset , SEEK_SET ) ;
fread ( ( uchar * ) this_shn - > decode_state - > getbuf , 1 , BUFSIZ , this_shn - > vars . fd ) ;
this_shn - > decode_state - > getbufp = this_shn - > decode_state - > getbuf + shn_uchar_to_ushort_le ( seek_info - > data + 14 ) ;
this_shn - > decode_state - > nbitget = shn_uchar_to_ushort_le ( seek_info - > data + 16 ) ;
this_shn - > decode_state - > nbyteget = shn_uchar_to_ushort_le ( seek_info - > data + 12 ) ;
this_shn - > decode_state - > gbuffer = shn_uchar_to_ulong_le ( seek_info - > data + 18 ) ;
this_shn - > vars . bytes_in_buf = 0 ;
this_shn - > vars . seek_to = - 1 ;
}
}
chan = ( chan + 1 ) % nchan ;
break ;
}
break ;
case FN_QUIT :
/* empty out last of buffer */
buffer_ret = write_to_buffer ( this_shn , read_buffer , this_shn - > vars . bytes_in_buf ) ;
if ( buffer_ret = = bytes_to_read )
buffer_is_full = 1 ;
this_shn - > vars . eof = TRUE ;
goto cleanup ;
break ;
case FN_BLOCKSIZE :
blocksize = UINT_GET ( ( int ) ( log ( ( double ) blocksize ) / M_LN2 ) , this_shn ) ;
if ( this_shn - > vars . fatal_error )
goto cleanup ;
break ;
case FN_BITSHIFT :
bitshift = uvar_get ( BITSHIFTSIZE , this_shn ) ;
if ( this_shn - > vars . fatal_error )
goto cleanup ;
break ;
case FN_VERBATIM :
cklen = uvar_get ( VERBATIM_CKSIZE_SIZE , this_shn ) ;
if ( this_shn - > vars . fatal_error )
goto cleanup ;
while ( cklen - - ) {
tmp = ( uchar ) uvar_get ( VERBATIM_BYTE_SIZE , this_shn ) ;
if ( this_shn - > vars . fatal_error )
goto cleanup ;
}
break ;
default :
shn_error_fatal ( this_shn , " Sanity check fails trying to decode function: %d " , cmd ) ;
goto cleanup ;
}
}
goto exit_func ;
cleanup :
buffer_ret = write_to_buffer ( this_shn , read_buffer , this_shn - > vars . bytes_in_buf ) ;
exit_func :
return buffer_ret ;
}
unsigned int shn_get_samplerate ( shn_file * this_shn ) {
if ( this_shn ) {
if ( this_shn - > wave_header . samples_per_sec > 0 )
return ( unsigned int ) this_shn - > wave_header . samples_per_sec ;
}
/* Something failed or just isn't correct */
return ( unsigned int ) 0 ;
}
unsigned int shn_get_channels ( shn_file * this_shn ) {
if ( this_shn ) {
if ( this_shn - > wave_header . channels > 0 )
return ( unsigned int ) this_shn - > wave_header . channels ;
}
/* Something failed or just isn't correct */
return ( unsigned int ) 0 ;
}
unsigned int shn_get_bitspersample ( shn_file * this_shn ) {
if ( this_shn ) {
if ( this_shn - > wave_header . bits_per_sample > 0 )
return ( unsigned int ) this_shn - > wave_header . bits_per_sample ;
}
/* Something failed or just isn't correct */
return ( unsigned int ) 0 ;
}