#include #include #include #include #include #include "decode.h" #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif /* Derek start */ pthread_mutex_t mRingLock; pthread_cond_t mRunCond; pthread_t mThread; /* Derek end */ 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;ivars.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;ivars.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; }