precomp-cpp/contrib/packmp3/packmp3.cpp
Deus Libri 677e8c5fb0 Add fix from PackMP3 master.
Without this, some MP3 streams might crash on reconstruction.
2018-06-24 22:33:38 +02:00

8144 lines
247 KiB
C++

#include <stdlib.h>
#include <string.h>
#include <ctime>
#include "pmp3tbl.h"
#include "pmp3bitlen.h"
#include "../packjpg/bitops.h"
#include "../packjpg/aricoder.h"
#include "huffmp3.h"
#include "huffmp3tbl.h"
#if defined BUILD_DLL // define BUILD_LIB from the compiler options if you want to compile a DLL!
#define BUILD_LIB
#endif
#if defined BUILD_LIB // define BUILD_LIB from the compiler options if you want to compile a library!
#include "packmp3lib.h"
#endif
#define INTERN static
#define INIT_MODEL_S(a,b,c) new model_s( a, b, c, 511 )
#define INIT_MODEL_B(a,b) new model_b( a, b, 511 )
#define ABS(v1) ( (v1 < 0) ? -v1 : v1 )
#define ABSDIFF(v1,v2) ( (v1 > v2) ? (v1 - v2) : (v2 - v1) )
#define ROUND_F(v1) ( (v1 < 0) ? (int) (v1 - 0.5) : (int) (v1 + 0.5) )
#define CLAMPED(l,h,v) ( ( v < l ) ? l : ( v > h ) ? h : v )
#define MEM_ERRMSG "out of memory error"
#define FRD_ERRMSG "could not read file / file not found"
#define FWR_ERRMSG "could not write file / file write-protected"
#define MSG_SIZE 128
#define BARLEN 36
// special realloc with guaranteed free() of previous memory
static inline void* frealloc( void* ptr, size_t size ) {
void* n_ptr = realloc( ptr, (size) ? size : 1 );
if ( n_ptr == NULL ) free( ptr );
return n_ptr;
}
/* -----------------------------------------------
function declarations: main interface
----------------------------------------------- */
#if !defined( BUILD_LIB )
INTERN void initialize_options( int argc, char** argv );
INTERN void process_ui( void );
INTERN inline const char* get_status( bool (*function)() );
INTERN void show_help( void );
#endif
INTERN void process_file( void );
INTERN void execute( bool (*function)() );
/* -----------------------------------------------
function declarations: main functions
----------------------------------------------- */
#if !defined( BUILD_LIB )
INTERN bool check_file( void );
INTERN bool swap_streams( void );
INTERN bool compare_output( void );
#endif
INTERN bool reset_buffers( void );
INTERN bool read_mp3( void );
INTERN bool write_mp3( void );
INTERN bool analyze_frames( void );
INTERN bool compress_mp3( void );
INTERN bool uncompress_pmp( void );
/* -----------------------------------------------
function declarations: MP3-specific
----------------------------------------------- */
INTERN inline mp3Frame* mp3_read_frame( unsigned char* data, int max_size );
INTERN inline mp3Frame* mp3_build_frame( void );
INTERN inline bool mp3_append_frame( mp3Frame* frame );
INTERN inline bool mp3_discard_frame( mp3Frame* frame );
INTERN inline bool mp3_mute_frame( mp3Frame* frame );
INTERN inline bool mp3_unmute_frame( mp3Frame* frame );
INTERN inline unsigned char* mp3_build_fixed( mp3Frame* frame );
INTERN inline int mp3_seek_firstframe( unsigned char* data, int size );
INTERN inline int mp3_get_id3_size( unsigned char* id3tag, int max_size );
INTERN inline unsigned short mp3_calc_layer3_crc( unsigned char* header, unsigned char* sideinfo, int sidesize );
INTERN inline granuleData*** mp3_decode_frame( huffman_reader* dec, mp3Frame* frame );
/* -----------------------------------------------
function declarations: PMP-specific
----------------------------------------------- */
INTERN inline bool pmp_write_header( iostream* str );
INTERN inline bool pmp_read_header( iostream* str );
#if !defined( STORE_ID3 )
INTERN inline bool pmp_encode_id3( aricoder* enc );
INTERN inline bool pmp_decode_id3( aricoder* dec );
#else
INTERN inline int pmp_store_data( iostream* str, unsigned char* data, int size );
INTERN inline int pmp_unstore_data( iostream* str, unsigned char** data );
#endif
INTERN inline bool pmp_encode_padding( aricoder* enc );
INTERN inline bool pmp_decode_padding( aricoder* dec );
INTERN inline bool pmp_encode_block_types( aricoder* enc );
INTERN inline bool pmp_decode_block_types( aricoder* dec );
INTERN inline bool pmp_decode_stereo_ms( aricoder* dec );
INTERN inline bool pmp_encode_global_gain( aricoder* enc );
INTERN inline bool pmp_decode_global_gain( aricoder* dec );
INTERN inline bool pmp_encode_slength( aricoder* enc );
INTERN inline bool pmp_decode_slength( aricoder* dec );
INTERN inline bool pmp_encode_stereo_ms( aricoder* enc );
INTERN inline bool pmp_decode_stereo_ms( aricoder* dec );
INTERN inline bool pmp_encode_region_data( aricoder* enc );
INTERN inline bool pmp_decode_region_data( aricoder* dec );
INTERN inline bool pmp_encode_sharing( aricoder* enc );
INTERN inline bool pmp_decode_sharing( aricoder* dec );
INTERN inline bool pmp_encode_preemphasis( aricoder* enc );
INTERN inline bool pmp_decode_preemphasis( aricoder* dec );
INTERN inline bool pmp_encode_coarse_sf( aricoder* enc );
INTERN inline bool pmp_decode_coarse_sf( aricoder* dec );
INTERN inline bool pmp_encode_subblock_gain( aricoder* enc );
INTERN inline bool pmp_decode_subblock_gain( aricoder* dec );
INTERN inline bool pmp_encode_main_data( aricoder* enc );
INTERN inline bool pmp_decode_main_data( aricoder* dec );
INTERN inline bool pmp_store_unmute_data( iostream* str );
INTERN inline bool pmp_unstore_unmute_data( iostream* str );
INTERN inline bool pmp_build_context( void );
INTERN inline unsigned char* pmp_predict_lame_anc( int nbits, unsigned char* ref );
/* -----------------------------------------------
function declarations: miscelaneous helpers
----------------------------------------------- */
#if !defined( BUILD_LIB )
INTERN inline void progress_bar( int current, int last );
INTERN inline char* create_filename( const char* base, const char* extension );
INTERN inline char* unique_filename( const char* base, const char* extension );
INTERN inline void set_extension( const char* filename, const char* extension );
INTERN inline void add_underscore( char* filename );
#endif
INTERN inline bool file_exists( const char* filename );
/* -----------------------------------------------
function declarations: developers functions
----------------------------------------------- */
// these are developers functions, they are not needed
// in any way to compress MP3 or decompress PMP
#if !defined(BUILD_LIB) && defined(DEV_BUILD)
INTERN bool write_file( const char* base, const char* ext, void* data, int bpv, int size );
INTERN bool write_errfile( void );
INTERN bool write_file_analysis( void );
INTERN bool write_block_analysis( void );
INTERN bool write_stat_analysis( void );
INTERN bool visualize_headers( void );
INTERN bool visualize_decoded_data( void );
INTERN bool dump_main_sizes( void );
INTERN bool dump_aux_sizes( void );
INTERN bool dump_bitrates( void );
INTERN bool dump_stereo_ms( void );
INTERN bool dump_padding( void );
INTERN bool dump_main_data_bits( void );
INTERN bool dump_big_value_ns( void );
INTERN bool dump_global_gain( void );
INTERN bool dump_slength( void );
INTERN bool dump_block_types( void );
INTERN bool dump_sharing( void );
INTERN bool dump_preemphasis( void );
INTERN bool dump_coarse( void );
INTERN bool dump_htable_sel( void );
INTERN bool dump_region_sizes( void );
INTERN bool dump_subblock_gains( void );
INTERN bool dump_data_files( void );
INTERN bool dump_gg_ctx( void );
INTERN bool dump_decoded_data( void );
#endif
/* -----------------------------------------------
global variables: library only variables
----------------------------------------------- */
#if defined(BUILD_LIB)
INTERN int lib_in_type = -1;
INTERN int lib_out_type = -1;
#endif
/* -----------------------------------------------
global variables: data storage
----------------------------------------------- */
INTERN mp3Frame* firstframe = NULL; // first physical frame
INTERN mp3Frame* lastframe = NULL; // last physical frame
INTERN unsigned char* main_data = NULL; // (mainly) huffman coded data
INTERN unsigned char* data_before = NULL; // data before (should be ID3v2 tag)
INTERN unsigned char* data_after = NULL; // data after (should be ID3v1 or ID3v2 tag)
INTERN unsigned char* unmute_data = NULL; // fix data (to reverse muted frames)
INTERN int main_data_size = 0 ; // size of main data
INTERN int data_before_size = 0 ; // size of data before
INTERN int data_after_size = 0 ; // size of data after
INTERN int unmute_data_size = 0 ; // size of fix data
INTERN int n_bad_first = 0 ; // # of bad first frames (should be zero!)
INTERN unsigned char* gg_context[2] = {NULL}; // universal context based on global gain
/* -----------------------------------------------
global variables: info about audio file
----------------------------------------------- */
INTERN int g_nframes = 0; // number of frames
INTERN int g_nchannels = 0; // number of channels
INTERN int g_samplerate = 0; // sample rate
INTERN int g_bitrate = 0; // bit rate - global or zero for vbr
/* -----------------------------------------------
global variables: frame analysis info
----------------------------------------------- */
INTERN char i_mpeg = -1; // mpeg - non changing
INTERN char i_layer = -1; // layer - non changing
INTERN char i_samplerate = -1; // sample rate - non changing
INTERN char i_bitrate = -1; // bit rate - value or -1 (variable)
INTERN char i_protection = -1; // checksum - for all (1), none (0) or some (-1) frames
INTERN char i_padding = -1; // padding - for all (1), none (0) or some (-1) frames
INTERN char i_privbit = -1; // private bit - value or -1 (variable)
INTERN char i_channels = -1; // channel mode - non changing
INTERN char i_stereo_ms = -1; // ms stereo - for all (1), none (0) or some (-1) frames
INTERN char i_stereo_int = -1; // int stereo - for all (1), none (0) or some (-1) frames
INTERN char i_copyright = -1; // copyright bit - value or -1 (variable)
INTERN char i_original = -1; // original bit - value or -1 (variable)
INTERN char i_emphasis = -1; // emphasis - value or -1 (variable)
INTERN char i_padbits = -1; // side info padding bits - value or -1 (variable)
INTERN char i_bit_res = -1; // bit reservoir - is used (1) or not used (0)
INTERN char i_share = -1; // scalefactor sharing - is used (1) or not used (0)
INTERN char i_sblocks = -1; // special blocks - are used (1) or not used (0)
INTERN char i_mixed = -1; // mixed blocks - are used (1) or not used (0)
INTERN char i_preemphasis = -1; // preemphasis - value or -1 (variable)
INTERN char i_coarse = -1; // coarse scalefactors - value or -1 (variable)
INTERN char i_sbgain = -1; // subblock gain - used properly (1), not used (0) or used for non-short (-1)
INTERN char i_aux_h = -1; // auxiliary data handling - none (0), at begin and end (1), between frames (-1)
INTERN char i_sb_diff = -1; // special blocks diffs between ch0 and ch1 - none (0) or some (-1)
/* -----------------------------------------------
global variables: info about files
----------------------------------------------- */
INTERN char* mp3filename = NULL; // name of MP3 file
INTERN char* pmpfilename = NULL; // name of PMP file
INTERN int mp3filesize; // size of MP3 file
INTERN int pmpfilesize; // size of PMP file
INTERN int filetype; // type of current file
INTERN iostream* str_in = NULL; // input stream
INTERN iostream* str_out = NULL; // output stream
#if !defined(BUILD_LIB)
INTERN iostream* str_str = NULL; // storage stream
INTERN char** filelist = NULL; // list of files to process
INTERN int file_cnt = 0; // count of files in list
INTERN int file_no = 0; // number of current file
INTERN char** err_list = NULL; // list of error messages
INTERN int* err_tp = NULL; // list of error types
#endif
/* -----------------------------------------------
global variables: messages
----------------------------------------------- */
INTERN char errormessage [ 128 ];
INTERN bool (*errorfunction)();
INTERN int errorlevel;
// meaning of errorlevel:
// -1 -> wrong input
// 0 -> no error
// 1 -> warning
// 2 -> fatal error
/* -----------------------------------------------
global variables: settings
----------------------------------------------- */
#if !defined( BUILD_LIB )
INTERN int verbosity = -1; // level of verbosity
INTERN bool overwrite = false; // overwrite files yes / no
INTERN bool wait_exit = true; // pause after finished yes / no
INTERN int verify_lv = 0; // verification level ( none (0), simple (1), detailed output (2) )
INTERN int err_tol = 1; // error threshold ( proceed on warnings yes (2) / no (1) )
INTERN bool developer = false; // allow developers functions yes/no
INTERN int action = A_COMPRESS;// what to do with MP3/PMP files
INTERN FILE* msgout = stdout; // stream for output of messages
INTERN bool pipe_on = false; // use stdin/stdout instead of filelist
#else
INTERN int err_tol = 1; // error threshold ( proceed on warnings yes (2) / no (1) )
INTERN int action = A_COMPRESS;// what to do with MP3/PMP files
#endif
/* -----------------------------------------------
global variables: info about program
----------------------------------------------- */
INTERN const unsigned char appversion = 10;
INTERN const char* subversion = "g";
INTERN const char* apptitle = "packMP3";
INTERN const char* appname = "packMP3";
INTERN const char* versiondate = "01/22/2016";
INTERN const char* author = "Matthias Stirner";
#if !defined( BUILD_LIB )
INTERN const char* website = "http://packjpg.encode.ru/";
INTERN const char* email = "packjpg (at) matthiasstirner.com";
INTERN const char* copyright = "2010-2016 Ratisbon University & Matthias Stirner";
INTERN const char* pmp_ext = "pmp";
INTERN const char* mp3_ext = "mp3";
#endif
INTERN const char pmp_magic[] = { 'M', 'S' };
/* -----------------------------------------------
main-function
----------------------------------------------- */
#if !defined(BUILD_LIB)
int main( int argc, char** argv )
{
sprintf( errormessage, "no errormessage specified" );
clock_t begin, end;
int error_cnt = 0;
int warn_cnt = 0;
double acc_mp3size = 0;
double acc_pmpsize = 0;
int kbps;
double cr;
double total;
errorlevel = 0;
// read options from command line
initialize_options( argc, argv );
// write program info to screen
fprintf( msgout, "\n--> %s v%i.%i%s (%s) by %s <--\n",
apptitle, appversion / 10, appversion % 10, subversion, versiondate, author );
fprintf( msgout, "Copyright %s\nAll rights reserved\n\n", copyright );
// check if user input is wrong, show help screen if it is
if ( ( file_cnt == 0 ) ||
( ( !developer ) && ( (action != A_COMPRESS) || (verify_lv > 1) ) ) ) {
show_help();
return -1;
}
// (re)set program has to be done first
reset_buffers();
// process file(s) - this is the main function routine
begin = clock();
for ( file_no = 0; file_no < file_cnt; file_no++ ) {
// process current file
process_ui();
// store error message and type if any
if ( errorlevel > 0 ) {
err_list[ file_no ] = (char*) calloc( MSG_SIZE, sizeof( char ) );
err_tp[ file_no ] = errorlevel;
if ( err_list[ file_no ] != NULL )
strcpy( err_list[ file_no ], errormessage );
}
// count errors / warnings / file sizes
if ( errorlevel >= err_tol ) error_cnt++;
else {
if ( errorlevel == 1 ) warn_cnt++;
acc_mp3size += mp3filesize;
acc_pmpsize += pmpfilesize;
}
}
end = clock();
// errors summary: only needed for -v2 or progress bar
if ( ( verbosity == -1 ) || ( verbosity == 2 ) ) {
// print summary of errors to screen
if ( error_cnt > 0 ) {
fprintf( stderr, "\n\nfiles with errors:\n" );
fprintf( stderr, "------------------\n" );
for ( file_no = 0; file_no < file_cnt; file_no++ ) {
if ( err_tp[ file_no ] >= err_tol ) {
fprintf( stderr, "%s (%s)\n", filelist[ file_no ], err_list[ file_no ] );
}
}
}
// print summary of warnings to screen
if ( warn_cnt > 0 ) {
fprintf( stderr, "\n\nfiles with warnings:\n" );
fprintf( stderr, "------------------\n" );
for ( file_no = 0; file_no < file_cnt; file_no++ ) {
if ( err_tp[ file_no ] == 1 ) {
fprintf( stderr, "%s (%s)\n", filelist[ file_no ], err_list[ file_no ] );
}
}
}
}
// show statistics
fprintf( msgout, "\n\n-> %i file(s) processed, %i error(s), %i warning(s)\n",
file_cnt, error_cnt, warn_cnt );
if ( ( file_cnt > error_cnt ) && ( verbosity != 0 ) && ( action == A_COMPRESS ) ) {
acc_mp3size /= 1024.0; acc_pmpsize /= 1024.0;
total = (double) ( end - begin ) / CLOCKS_PER_SEC;
kbps = ( total > 0 ) ? ( acc_mp3size / total ) : acc_mp3size;
cr = ( acc_mp3size > 0 ) ? ( 100.0 * acc_pmpsize / acc_mp3size ) : 0;
fprintf( msgout, " -------------------------------- \n" );
if ( total >= 0 ) {
fprintf( msgout, " total time : %8.2f sec\n", total );
fprintf( msgout, " avrg. kbyte per s : %8i kbps\n", kbps );
}
else {
fprintf( msgout, " total time : %8s sec\n", "N/A" );
fprintf( msgout, " avrg. kbyte per s : %8s kbps\n", "N/A" );
}
fprintf( msgout, " avrg. comp. ratio : %8.2f %%\n", cr );
fprintf( msgout, " -------------------------------- \n" );
}
// pause before exit
if ( wait_exit && ( msgout != stderr ) ) {
fprintf( msgout, "\n\n< press ENTER >\n" );
fgetc( stdin );
}
return 0;
}
#endif
/* ----------------------- Begin of library only functions -------------------------- */
/* -----------------------------------------------
DLL export converter function
----------------------------------------------- */
#if defined(BUILD_LIB)
EXPORT bool pmplib_convert_stream2stream( char* msg )
{
// process in main function
return pmplib_convert_stream2mem( NULL, NULL, msg );
}
#endif
/* -----------------------------------------------
DLL export converter function
----------------------------------------------- */
#if defined(BUILD_LIB)
EXPORT bool pmplib_convert_file2file( char* in, char* out, char* msg )
{
// init streams
pmplib_init_streams( (void*) in, 0, 0, (void*) out, 0 );
// process in main function
return pmplib_convert_stream2mem( NULL, NULL, msg );
}
#endif
/* -----------------------------------------------
DLL export converter function
----------------------------------------------- */
#if defined(BUILD_LIB)
EXPORT bool pmplib_convert_stream2mem( unsigned char** out_file, unsigned int* out_size, char* msg )
{
clock_t begin, end;
int total;
float cr;
// (re)set buffers
reset_buffers();
action = A_COMPRESS;
// main compression / decompression routines
begin = clock();
// process one file
process_file();
// fetch pointer and size of output (only for memory output)
if ( ( errorlevel < err_tol ) && ( lib_out_type == 1 ) &&
( out_file != NULL ) && ( out_size != NULL ) ) {
*out_size = str_out->getsize();
*out_file = str_out->getptr();
}
// close iostreams
if ( str_in != NULL ) delete( str_in ); str_in = NULL;
if ( str_out != NULL ) delete( str_out ); str_out = NULL;
end = clock();
// copy errormessage / remove files if error (and output is file)
if ( errorlevel >= err_tol ) {
if ( lib_out_type == 0 ) {
if ( filetype == F_MP3 ) {
if ( file_exists( pmpfilename ) ) remove( pmpfilename );
} else if ( filetype == F_PMP ) {
if ( file_exists( mp3filename ) ) remove( mp3filename );
}
}
if ( msg != NULL ) strcpy( msg, errormessage );
return false;
}
// get compression info
total = (int) ( (double) (( end - begin ) * 1000) / CLOCKS_PER_SEC );
cr = ( mp3filesize > 0 ) ? ( 100.0 * pmpfilesize / mp3filesize ) : 0;
// write success message else
if ( msg != NULL ) {
switch( filetype )
{
case F_MP3:
sprintf( msg, "Compressed to %s (%.2f%%) in %ims",
pmpfilename, cr, ( total >= 0 ) ? total : -1 );
break;
case F_PMP:
sprintf( msg, "Decompressed to %s (%.2f%%) in %ims",
mp3filename, cr, ( total >= 0 ) ? total : -1 );
break;
case F_UNK:
sprintf( msg, "Unknown filetype" );
break;
}
}
return true;
}
#endif
/* -----------------------------------------------
DLL export init input (file/mem)
----------------------------------------------- */
#if defined(BUILD_LIB)
EXPORT void pmplib_init_streams( void* in_src, int in_type, int in_size, void* out_dest, int out_type )
{
/* a short reminder about input/output stream types:
if input is file
----------------
in_scr -> name of input file
in_type -> 0
in_size -> ignore
if input is memory
------------------
in_scr -> array containg data
in_type -> 1
in_size -> size of data array
if input is *FILE (f.e. stdin)
------------------------------
in_src -> stream pointer
in_type -> 2
in_size -> ignore
vice versa for output streams! */
unsigned char buffer[ 2 ];
// (re)set errorlevel
errorfunction = NULL;
errorlevel = 0;
mp3filesize = 0;
pmpfilesize = 0;
// open input stream, check for errors
str_in = new iostream( in_src, in_type, in_size, 0 );
if ( str_in->chkerr() ) {
sprintf( errormessage, "error opening input stream" );
errorlevel = 2;
return;
}
// open output stream, check for errors
str_out = new iostream( out_dest, out_type, 0, 1 );
if ( str_out->chkerr() ) {
sprintf( errormessage, "error opening output stream" );
errorlevel = 2;
return;
}
// free memory from filenames if needed
if ( mp3filename != NULL ) free( mp3filename ); mp3filename = NULL;
if ( pmpfilename != NULL ) free( pmpfilename ); pmpfilename = NULL;
// check input stream
str_in->read( buffer, 1, 2 );
str_in->rewind();
if ( (buffer[0] == pmp_magic[0]) && (buffer[1] == pmp_magic[1]) ) {
// file is PMP
filetype = F_PMP;
// copy filenames
pmpfilename = (char*) calloc( ( in_type == 0 ) ? strlen( (char*) in_src ) + 1 : 32, sizeof( char ) );
mp3filename = (char*) calloc( ( out_type == 0 ) ? strlen( (char*) out_dest ) + 1 : 32, sizeof( char ) );
strcpy( pmpfilename, ( in_type == 0 ) ? (char*) in_src : "PMP in memory" );
strcpy( mp3filename, ( out_type == 0 ) ? (char*) out_dest : "MP3 in memory" );
} else {
// file is MP3
filetype = F_MP3;
// copy filenames
mp3filename = (char*) calloc( ( in_type == 0 ) ? strlen( (char*) in_src ) + 1 : 32, sizeof( char ) );
pmpfilename = (char*) calloc( ( out_type == 0 ) ? strlen( (char*) out_dest ) + 1 : 32, sizeof( char ) );
strcpy( mp3filename, ( in_type == 0 ) ? (char*) in_src : "MP3 in memory" );
strcpy( pmpfilename, ( out_type == 0 ) ? (char*) out_dest : "PMP in memory" );
}
// store types of in-/output
lib_in_type = in_type;
lib_out_type = out_type;
}
#endif
/* -----------------------------------------------
DLL export version information
----------------------------------------------- */
#if defined(BUILD_LIB)
EXPORT const char* pmplib_version_info( void )
{
static char v_info[ 256 ];
// copy version info to string
sprintf( v_info, "--> %s library v%i.%i%s (%s) by %s <--",
apptitle, appversion / 10, appversion % 10, subversion, versiondate, author );
return (const char*) v_info;
}
#endif
/* -----------------------------------------------
DLL export version information
----------------------------------------------- */
#if defined(BUILD_LIB)
EXPORT const char* pmplib_short_name( void )
{
static char v_name[ 256 ];
// copy version info to string
sprintf( v_name, "%s v%i.%i%s",
apptitle, appversion / 10, appversion % 10, subversion );
return (const char*) v_name;
}
#endif
/* ----------------------- End of libary only functions -------------------------- */
/* ----------------------- Begin of main interface functions -------------------------- */
/* -----------------------------------------------
reads in commandline arguments
----------------------------------------------- */
#if !defined(BUILD_LIB)
INTERN void initialize_options( int argc, char** argv )
{
int tmp_val;
char** tmp_flp;
int i;
// get memory for filelist & preset with NULL
filelist = (char**) calloc( argc, sizeof( char* ) );
for ( i = 0; i < argc; i++ )
filelist[ i ] = NULL;
// preset temporary filelist pointer
tmp_flp = filelist;
// read in arguments
while ( --argc > 0 ) {
argv++;
// switches begin with '-'
if ( strcmp((*argv), "-p" ) == 0 ) {
err_tol = 2;
}
else if ( strcmp((*argv), "-ver" ) == 0 ) {
verify_lv = ( verify_lv < 1 ) ? 1 : verify_lv;
}
else if ( sscanf( (*argv), "-v%i", &tmp_val ) == 1 ){
verbosity = tmp_val;
verbosity = ( verbosity < 0 ) ? 0 : verbosity;
verbosity = ( verbosity > 2 ) ? 2 : verbosity;
}
else if ( strcmp((*argv), "-vp" ) == 0 ) {
verbosity = -1;
}
else if ( strcmp((*argv), "-np" ) == 0 ) {
wait_exit = false;
}
else if ( strcmp((*argv), "-o" ) == 0 ) {
overwrite = true;
}
#if defined(DEV_BUILD)
else if ( strcmp((*argv), "-dev") == 0 ) {
developer = true;
}
else if ( strcmp((*argv), "-test") == 0 ) {
verify_lv = 2;
}
else if ( strcmp((*argv), "-san") == 0 ) {
if ( file_exists( STAT_ANALYSIS_CSV ) )
remove( STAT_ANALYSIS_CSV );
action = A_STATS_ANALYSIS;
}
else if ( strcmp((*argv), "-fan") == 0 ) {
if ( file_exists( FILE_ANALYSIS_CSV ) )
remove( FILE_ANALYSIS_CSV );
action = A_FILE_ANALYSIS;
}
else if ( strcmp((*argv), "-ban") == 0 ) {
action = A_BLOCK_ANALYSIS;
}
else if ( strcmp((*argv), "-dmp") == 0 ) {
action = A_DUMP_SEPERATE;
}
else if ( strcmp((*argv), "-pgm") == 0 ) {
action = A_PGM_INFO;
}
else if ( ( strcmp((*argv), "-comp") == 0) ) {
action = A_COMPRESS;
}
#endif
else if ( strcmp((*argv), "-") == 0 ) {
// switch standard message out stream
msgout = stderr;
// use "-" as placeholder for stdin
*(tmp_flp++) = (char*) "-";
}
else {
// if argument is not switch, it's a filename
*(tmp_flp++) = *argv;
}
}
// count number of files (or filenames) in filelist
for ( file_cnt = 0; filelist[ file_cnt ] != NULL; file_cnt++ );
// alloc arrays for error messages and types storage
err_list = (char**) calloc( file_cnt, sizeof( char* ) );
err_tp = (int*) calloc( file_cnt, sizeof( int ) );
}
#endif
/* -----------------------------------------------
UI for processing one file
----------------------------------------------- */
#if !defined(BUILD_LIB)
INTERN void process_ui( void )
{
clock_t begin, end;
const char* actionmsg = NULL;
const char* errtypemsg = NULL;
int total, bpms;
float cr;
errorfunction = NULL;
errorlevel = 0;
mp3filesize = 0;
pmpfilesize = 0;
#if !defined(DEV_BUILD)
action = A_COMPRESS;
#endif
// compare file name, set pipe if needed
if ( ( strcmp( filelist[ file_no ], "-" ) == 0 ) && ( action == A_COMPRESS ) ) {
pipe_on = true;
filelist[ file_no ] = (char*) "STDIN";
}
else {
pipe_on = false;
}
if ( verbosity >= 0 ) { // standard UI
fprintf( msgout, "\nProcessing file %i of %i \"%s\" -> ",
file_no + 1, file_cnt, filelist[ file_no ] );
if ( verbosity > 1 )
fprintf( msgout, "\n----------------------------------------" );
// check input file and determine filetype
execute( check_file );
// get specific action message
if ( filetype == F_UNK ) actionmsg = "unknown filetype";
else switch ( action ) {
case A_COMPRESS: ( filetype == F_MP3 ) ? actionmsg = "Compressing" : actionmsg = "Decompressing";
break;
case A_DUMP_SEPERATE: actionmsg = "Dumping binary data"; break;
case A_PGM_INFO: actionmsg = "Writing PGM"; break;
case A_FILE_ANALYSIS: actionmsg = "Analysing files"; break;
case A_BLOCK_ANALYSIS: actionmsg = "Analysing frames"; break;
case A_STATS_ANALYSIS: actionmsg = "Analysing statistics"; break;
}
if ( verbosity < 2 ) fprintf( msgout, "%s -> ", actionmsg );
}
else { // progress bar UI
// update progress message
fprintf( msgout, "Processing file %2i of %2i ", file_no + 1, file_cnt );
progress_bar( file_no, file_cnt );
fprintf( msgout, "\r" );
execute( check_file );
}
fflush( msgout );
// main function routine
begin = clock();
// streams are initiated, start processing file
process_file();
// close iostreams
if ( str_in != NULL ) delete( str_in ); str_in = NULL;
if ( str_out != NULL ) delete( str_out ); str_out = NULL;
if ( str_str != NULL ) delete( str_str ); str_str = NULL;
// delete if broken or if output not needed
if ( ( !pipe_on ) && ( ( errorlevel >= err_tol ) || ( action != A_COMPRESS ) ) ) {
if ( filetype == F_MP3 ) {
if ( file_exists( pmpfilename ) ) remove( pmpfilename );
} else if ( filetype == F_PMP ) {
if ( file_exists( mp3filename ) ) remove( mp3filename );
}
}
end = clock();
// speed and compression ratio calculation
total = (int) ( (double) (( end - begin ) * 1000) / CLOCKS_PER_SEC );
bpms = ( total > 0 ) ? ( mp3filesize / total ) : mp3filesize;
cr = ( mp3filesize > 0 ) ? ( 100.0 * pmpfilesize / mp3filesize ) : 0;
if ( verbosity >= 0 ) { // standard UI
if ( verbosity > 1 )
fprintf( msgout, "\n----------------------------------------" );
// display success/failure message
switch ( verbosity ) {
case 0:
if ( errorlevel < err_tol ) {
if ( action == A_COMPRESS ) fprintf( msgout, "%.2f%%", cr );
else fprintf( msgout, "DONE" );
}
else fprintf( msgout, "ERROR" );
if ( errorlevel > 0 ) fprintf( msgout, "\n" );
break;
case 1:
fprintf( msgout, "%s\n", ( errorlevel < err_tol ) ? "DONE" : "ERROR" );
break;
case 2:
if ( errorlevel < err_tol ) fprintf( msgout, "\n-> %s OK\n", actionmsg );
else fprintf( msgout, "\n-> %s ERROR\n", actionmsg );
break;
}
// set type of error message
switch ( errorlevel ) {
case 0: errtypemsg = "none"; break;
case 1: ( err_tol > 1 ) ? errtypemsg = "warning (ignored)" : errtypemsg = "warning (skipped file)"; break;
case 2: errtypemsg = "fatal error"; break;
}
// error/ warning message
if ( errorlevel > 0 ) {
fprintf( msgout, " %s -> %s:\n", get_status( errorfunction ), errtypemsg );
fprintf( msgout, " %s\n", errormessage );
}
if ( (verbosity > 0) && (errorlevel < err_tol) && (action == A_COMPRESS) ) {
if ( total >= 0 ) {
fprintf( msgout, " time taken : %7i msec\n", total );
fprintf( msgout, " byte per ms : %7i byte\n", bpms );
}
else {
fprintf( msgout, " time taken : %7s msec\n", "N/A" );
fprintf( msgout, " byte per ms : %7s byte\n", "N/A" );
}
fprintf( msgout, " comp. ratio : %7.2f %%\n", cr );
}
if ( ( verbosity > 1 ) && ( action == A_COMPRESS ) )
fprintf( msgout, "\n" );
}
else { // progress bar UI
// if this is the last file, update progress bar one last time
if ( file_no + 1 == file_cnt ) {
// update progress message
fprintf( msgout, "Processed %2i of %2i files ", file_no + 1, file_cnt );
progress_bar( 1, 1 );
fprintf( msgout, "\r" );
}
}
}
#endif
/* -----------------------------------------------
gets statusmessage for function
----------------------------------------------- */
#if !defined(BUILD_LIB)
INTERN inline const char* get_status( bool (*function)() )
{
if ( function == NULL ) {
return "unknown action";
} else if ( function == *check_file ) {
return "Determining filetype";
} else if ( function == *read_mp3 ) {
return "Reading MP3";
} else if ( function == *write_mp3 ) {
return "Writing MP3";
} else if ( function == *analyze_frames ) {
return "Analysing frames";
} else if ( function == *compress_mp3 ) {
return "Compressing to PMP";
} else if ( function == *uncompress_pmp ) {
return "Uncompressing PMP";
} else if ( function == *swap_streams ) {
return "Swapping input/output streams";
} else if ( function == *compare_output ) {
return "Verifying output stream";
} else if ( function == *reset_buffers ) {
return "Resetting program";
}
#if defined(DEV_BUILD)
else if ( function == *write_file_analysis ) {
return "Writing file analysis to csv";
} else if ( function == *write_block_analysis ) {
return "Writing block analysis to csv";
} else if ( function == *write_stat_analysis ) {
return "Writing statistic analysis to csv";
} else if ( function == *visualize_headers ) {
return "Writing binary info PGM";
} else if ( function == *visualize_decoded_data ) {
return "Writing decoded data PGMs";
} else if ( function == *dump_main_sizes ) {
return "Dumping main data sizes";
} else if ( function == *dump_aux_sizes ) {
return "Dumping aux data sizes";
} else if ( function == *dump_bitrates ) {
return "Dumping bitrates";
} else if ( function == *dump_stereo_ms ) {
return "Dumping MS stereo bits";
} else if ( function == *dump_padding ) {
return "Dumping padding bits";
} else if ( function == *dump_main_data_bits ) {
return "Dumping main data bits";
} else if ( function == *dump_big_value_ns ) {
return "Dumping big value pair #s";
} else if ( function == *dump_global_gain ) {
return "Dumping global gain";
} else if ( function == *dump_slength ) {
return "Dumping slength values";
} else if ( function == *dump_block_types ) {
return "Dumping block types";
} else if ( function == *dump_sharing ) {
return "Dumping sharing bits";
} else if ( function == *dump_preemphasis ) {
return "Dumping preemphasis setting";
} else if ( function == *dump_coarse ) {
return "Dumping coarse scf settings";
} else if ( function == *dump_htable_sel ) {
return "Dumping hufftable selections";
} else if ( function == *dump_region_sizes ) {
return "Dumping region sizes";
} else if ( function == *dump_subblock_gains ) {
return "Dumping subblock gain";
} else if ( function == *dump_data_files ) {
return "Dumping data files";
} else if ( function == *dump_gg_ctx ) {
return "Dumping global gain context";
} else if ( function == *dump_decoded_data ) {
return "Dumping decoded data";
}
#endif
else {
return "Function description missing!";
}
}
#endif
/* -----------------------------------------------
shows help in case of wrong input
----------------------------------------------- */
#if !defined(BUILD_LIB)
INTERN void show_help( void )
{
fprintf( msgout, "\n" );
fprintf( msgout, "Website: %s\n", website );
fprintf( msgout, "Email : %s\n", email );
fprintf( msgout, "\n" );
fprintf( msgout, "Usage: %s [switches] [filename(s)]", appname );
fprintf( msgout, "\n" );
fprintf( msgout, "\n" );
fprintf( msgout, " [-ver] verify files after processing\n" );
fprintf( msgout, " [-v?] set level of verbosity (max: 2) (def: 0)\n" );
fprintf( msgout, " [-np] no pause after processing files\n" );
fprintf( msgout, " [-o] overwrite existing files\n" );
fprintf( msgout, " [-p] proceed on warnings\n" );
#if defined(DEV_BUILD)
if ( developer ) {
fprintf( msgout, "\n" );
fprintf( msgout, " [-fan] write files analysis to CSV file\n" );
fprintf( msgout, " [-ban] write block analysis to CSV file\n" );
fprintf( msgout, " [-san] write stats analysis to CSV file\n" );
fprintf( msgout, " [-pgm] visualize data as PGM image\n" );
fprintf( msgout, " [-dmp] dump data to several binary files\n" );
}
#endif
fprintf( msgout, "\n" );
fprintf( msgout, "Examples: \"%s -v1 -o luka.%s\"\n", appname, pmp_ext );
fprintf( msgout, " \"%s -p *.%s\"\n", appname, mp3_ext );
}
#endif
/* -----------------------------------------------
processes one file
----------------------------------------------- */
INTERN void process_file( void )
{
if ( filetype == F_MP3 ) {
switch ( action ) {
case A_COMPRESS:
execute( read_mp3 );
execute( analyze_frames );
execute( compress_mp3 );
#if !defined(BUILD_LIB)
if ( verify_lv > 0 ) { // verifcation
execute( reset_buffers );
execute( swap_streams );
execute( uncompress_pmp );
execute( write_mp3 );
execute( compare_output );
}
#endif
break;
#if !defined(BUILD_LIB) && defined(DEV_BUILD)
case A_DUMP_SEPERATE:
execute( read_mp3 );
execute( analyze_frames );
execute( dump_main_sizes );
execute( dump_aux_sizes );
execute( dump_main_data_bits );
execute( dump_big_value_ns );
execute( dump_global_gain );
execute( dump_bitrates );
execute( dump_htable_sel );
execute( dump_region_sizes );
execute( dump_slength );
execute( dump_stereo_ms );
execute( dump_padding );
execute( dump_block_types );
execute( dump_sharing );
execute( dump_preemphasis );
execute( dump_coarse );
execute( dump_subblock_gains );
execute( dump_data_files );
execute( dump_gg_ctx );
execute( dump_decoded_data );
break;
case A_PGM_INFO:
execute( read_mp3 );
execute( analyze_frames );
execute( visualize_headers );
execute( visualize_decoded_data );
break;
case A_FILE_ANALYSIS:
execute( read_mp3 );
execute( analyze_frames );
execute( write_file_analysis );
break;
case A_BLOCK_ANALYSIS:
execute( read_mp3 );
execute( analyze_frames );
execute( write_block_analysis );
break;
case A_STATS_ANALYSIS:
execute( read_mp3 );
execute( analyze_frames );
execute( write_stat_analysis );
break;
#else
default:
break;
#endif
}
}
else if ( filetype == F_PMP ) {
switch ( action )
{
case A_COMPRESS:
execute( uncompress_pmp );
execute( write_mp3 );
#if !defined(BUILD_LIB)
if ( verify_lv > 0 ) { // verify
execute( reset_buffers );
execute( swap_streams );
execute( read_mp3 );
execute( analyze_frames );
execute( compress_mp3 );
execute( compare_output );
}
#endif
break;
#if !defined(BUILD_LIB) && defined(DEV_BUILD)
case A_DUMP_SEPERATE:
execute( uncompress_pmp );
execute( dump_main_sizes );
execute( dump_aux_sizes );
execute( dump_main_data_bits );
execute( dump_big_value_ns );
execute( dump_global_gain );
execute( dump_bitrates );
execute( dump_htable_sel );
execute( dump_region_sizes );
execute( dump_slength );
execute( dump_stereo_ms );
execute( dump_padding );
execute( dump_block_types );
execute( dump_sharing );
execute( dump_preemphasis );
execute( dump_coarse );
execute( dump_subblock_gains );
execute( dump_data_files );
execute( dump_gg_ctx );
execute( dump_decoded_data );
break;
case A_PGM_INFO:
execute( uncompress_pmp );
execute( visualize_headers );
execute( visualize_decoded_data );
break;
case A_FILE_ANALYSIS:
execute( uncompress_pmp );
execute( write_file_analysis );
break;
case A_BLOCK_ANALYSIS:
execute( uncompress_pmp );
execute( write_block_analysis );
break;
case A_STATS_ANALYSIS:
execute( uncompress_pmp );
execute( write_stat_analysis );
break;
#else
default:
break;
#endif
}
}
#if !defined(BUILD_LIB) && defined(DEV_BUILD)
// write error file if verify lv > 1
if ( ( verify_lv > 1 ) && ( errorlevel >= err_tol ) )
write_errfile();
#endif
// reset buffers
reset_buffers();
}
/* -----------------------------------------------
main-function execution routine
----------------------------------------------- */
INTERN void execute( bool (*function)() )
{
if ( errorlevel < err_tol ) {
#if !defined BUILD_LIB
clock_t begin, end;
bool success;
int total;
// write statusmessage
if ( verbosity == 2 ) {
fprintf( msgout, "\n%s ", get_status( function ) );
for ( int i = strlen( get_status( function ) ); i <= 30; i++ )
fprintf( msgout, " " );
}
// set starttime
begin = clock();
// call function
success = ( *function )();
// set endtime
end = clock();
if ( ( errorlevel > 0 ) && ( errorfunction == NULL ) )
errorfunction = function;
// write time or failure notice
if ( success ) {
total = (int) ( (double) (( end - begin ) * 1000) / CLOCKS_PER_SEC );
if ( verbosity == 2 ) fprintf( msgout, "%6ims", ( total >= 0 ) ? total : -1 );
}
else {
errorfunction = function;
if ( verbosity == 2 ) fprintf( msgout, "%8s", "ERROR" );
}
#else
// call function
( *function )();
// store errorfunction if needed
if ( ( errorlevel > 0 ) && ( errorfunction == NULL ) )
errorfunction = function;
#endif
}
}
/* ----------------------- End of main interface functions -------------------------- */
/* ----------------------- Begin of main functions -------------------------- */
/* -----------------------------------------------
check file and determine filetype
----------------------------------------------- */
#if !defined(BUILD_LIB)
INTERN bool check_file( void )
{
unsigned char fileid[ 2 ] = { 0, 0 };
const char* filename = filelist[ file_no ];
// open input stream, check for errors
str_in = new iostream( (void*) filename, ( !pipe_on ) ? 0 : 2, 0, 0 );
if ( str_in->chkerr() ) {
sprintf( errormessage, FRD_ERRMSG );
errorlevel = 2;
return false;
}
// free memory from filenames if needed
if ( mp3filename != NULL ) free( mp3filename ); mp3filename = NULL;
if ( pmpfilename != NULL ) free( pmpfilename ); pmpfilename = NULL;
// immediately return error if 2 bytes can't be read
if ( str_in->read( fileid, 1, 2 ) != 2 ) {
filetype = F_UNK;
sprintf( errormessage, "file doesn't contain enough data" );
errorlevel = 2;
return false;
}
// rewind (need to start from the beginning)
if ( str_in->rewind() != 0 ) {
sprintf( errormessage, FRD_ERRMSG );
errorlevel = 2;
return false;
}
// check file id, determine filetype
if ( ( fileid[0] == pmp_magic[0] ) && ( fileid[1] == pmp_magic[1] ) ) {
// PMP marker -> file is PMP
filetype = F_PMP;
// create filenames
if ( !pipe_on ) {
pmpfilename = (char*) calloc( strlen( filename ) + 1, sizeof( char ) );
strcpy( pmpfilename, filename );
mp3filename = ( overwrite ) ?
create_filename( filename, (char*) mp3_ext ) :
unique_filename( filename, (char*) mp3_ext );
}
else {
mp3filename = create_filename( "STDOUT", NULL );
pmpfilename = create_filename( "STDIN", NULL );
}
// open output stream, check for errors
str_out = new iostream( (void*) mp3filename, ( !pipe_on ) ? 0 : 2, 0, 1 );
if ( str_out->chkerr() ) {
sprintf( errormessage, FWR_ERRMSG );
errorlevel = 2;
return false;
}
}
else {
// for all other cases we assume that file might be MPEG X LAYER Y
filetype = F_MP3;
// create filenames
if ( !pipe_on ) {
mp3filename = (char*) calloc( strlen( filename ) + 1, sizeof( char ) );
strcpy( mp3filename, filename );
pmpfilename = ( overwrite ) ?
create_filename( filename, (char*) pmp_ext ) :
unique_filename( filename, (char*) pmp_ext );
}
else {
mp3filename = create_filename( "STDIN", NULL );
pmpfilename = create_filename( "STDOUT", NULL );
}
// open output stream, check for errors
str_out = new iostream( (void*) pmpfilename, ( !pipe_on ) ? 0 : 2, 0, 1 );
if ( str_out->chkerr() ) {
sprintf( errormessage, FWR_ERRMSG );
errorlevel = 2;
return false;
}
}
return true;
}
#endif
/* -----------------------------------------------
swap streams / init verification
----------------------------------------------- */
#if !defined(BUILD_LIB)
INTERN bool swap_streams( void )
{
// store input stream
str_str = str_in;
str_str->rewind();
// replace input stream by output stream / switch mode for reading
str_in = str_out;
str_in->switch_mode();
// open new stream for output / check for errors
str_out = new iostream( NULL, 1, 0, 1 );
if ( str_out->chkerr() ) {
sprintf( errormessage, "error opening comparison stream" );
errorlevel = 2;
return false;
}
return true;
}
#endif
/* -----------------------------------------------
comparison between input & output
----------------------------------------------- */
#if !defined(BUILD_LIB)
INTERN bool compare_output( void )
{
unsigned char* buff_ori;
unsigned char* buff_cmp;
int bsize = 1024;
int dsize;
int i, b;
// init buffer arrays
buff_ori = ( unsigned char* ) calloc( bsize, sizeof( char ) );
buff_cmp = ( unsigned char* ) calloc( bsize, sizeof( char ) );
if ( ( buff_ori == NULL ) || ( buff_cmp == NULL ) ) {
if ( buff_ori != NULL ) free( buff_ori );
if ( buff_cmp != NULL ) free( buff_cmp );
sprintf( errormessage, MEM_ERRMSG );
errorlevel = 2;
return false;
}
// switch output stream mode / check for stream errors
str_out->switch_mode();
while ( true ) {
if ( str_out->chkerr() )
sprintf( errormessage, "error in comparison stream" );
else if ( str_in->chkerr() )
sprintf( errormessage, "error in output stream" );
else if ( str_str->chkerr() )
sprintf( errormessage, "error in input stream" );
else break;
errorlevel = 2;
return false;
}
// compare sizes
dsize = str_str->getsize();
if ( str_out->getsize() != dsize ) {
sprintf( errormessage, "file sizes do not match" );
errorlevel = 2;
return false;
}
// compare files byte by byte
for ( i = 0; i < dsize; i++ ) {
b = i % bsize;
if ( b == 0 ) {
str_str->read( buff_ori, sizeof( char ), bsize );
str_out->read( buff_cmp, sizeof( char ), bsize );
}
if ( buff_ori[ b ] != buff_cmp[ b ] ) {
sprintf( errormessage, "difference found at 0x%X", i );
errorlevel = 2;
return false;
}
}
return true;
}
#endif
/* -----------------------------------------------
set each variable to its initial value
----------------------------------------------- */
INTERN bool reset_buffers( void )
{
mp3Frame* frame;
// --- free frame data ---
// start from first frame, throw away all frame data
if ( firstframe != NULL ) {
frame = firstframe;
while ( true ) {
if ( frame->next != NULL ) {
frame = frame->next;
mp3_discard_frame( frame->prev );
frame->prev = NULL;
} else {
mp3_discard_frame( frame );
break;
}
}
}
firstframe = NULL;
lastframe = NULL;
// throw away huffman coded data block
if ( main_data != NULL ) free ( main_data );
main_data = NULL;
main_data_size = 0;
// --- free other data ---
if ( data_before != NULL ) free ( data_before );
if ( data_after != NULL ) free( data_after );
if ( unmute_data != NULL ) free( unmute_data );
data_before = NULL;
data_after = NULL;
unmute_data = NULL;
data_before_size = 0;
data_after_size = 0;
unmute_data_size = 0;
if ( gg_context[0] != NULL ) free ( gg_context[0] );
if ( gg_context[1] != NULL ) free ( gg_context[1] );
gg_context[0] = NULL;
gg_context[1] = NULL;
// --- reset global variables ---
g_nframes = 0; // number of frames
g_nchannels = 0; // number of channels
g_samplerate = 0; // sample rate
g_bitrate = 0; // average bit rate
n_bad_first = 0; // # of bad first frames
// --- reset frame analysis variables ---
i_mpeg = -1; // mpeg - non changing
i_layer = -1; // layer - non changing
i_samplerate = -1; // sample rate - non changing
i_bitrate = -1; // bit rate - value or -1 (variable)
i_protection = -1; // checksum - for all (1), none (0) or some (-1) frames
i_padding = -1; // padding - for all (1), none (0) or some (-1) frames
i_privbit = -1; // private bit - value or -1 (variable)
i_channels = -1; // channel mode - non changing
i_stereo_ms = -1; // ms stereo - for all (1), none (0) or some (-1) frames
i_stereo_int = -1; // int stereo - for all (1), none (0) or some (-1) frames
i_copyright = -1; // copyright bit - value or -1 (variable)
i_original = -1; // original bit - value or -1 (variable)
i_emphasis = -1; // emphasis - value or -1 (variable)
i_padbits = -1; // side info padding bits - value or -1 (variable)
i_bit_res = -1; // bit reservoir - is used (1) or not used (0)
i_share = -1; // scalefactor sharing - is used (1) or not used (0)
i_sblocks = -1; // special blocks - are used (1) or not used (0)
i_mixed = -1; // mixed blocks - are used (1) or not used (0)
i_preemphasis = -1; // preemphasis - value or -1 (variable)
i_coarse = -1; // coarse scalefactors - value or -1 (variable)
i_sbgain = -1; // subblock gain - is used (1) or not used (0)
i_aux_h = -1; // auxiliary data handling - none (0), at begin and end (1), between frames (-1)
i_sb_diff = -1; // special blocks diffs between ch0 and ch1 - none (0) or some (-1)
return true;
}
/* -----------------------------------------------
parse MP3 frame structure
----------------------------------------------- */
INTERN bool read_mp3( void )
{
unsigned char* mp3data;
int main_data_begin;
int main_data_end;
char mpeg = -1;
char layer = -1;
char samples = -1;
char channels = -1;
abytewriter* data_writer;
mp3Frame* frame = NULL;
bool incomplete_last_frame = false;
int type;
int pos;
// read the first few bytes from the file into memory
// (these will be used to check for proper frames)
mp3filesize = str_in->getsize();
if ( mp3filesize > FIRST_FRAME_AREA ) mp3filesize = FIRST_FRAME_AREA;
mp3data = (unsigned char*) calloc( mp3filesize + 1, sizeof( char ) );
if ( ( mp3data == NULL ) || ( mp3filesize <= 0 ) ) {
if ( mp3data != NULL ) free( mp3data );
sprintf( errormessage, MEM_ERRMSG );
errorlevel = 2;
return false;
}
str_in->read( mp3data, 1, mp3filesize );
// find first proper mpeg audio frame
pos = mp3_seek_firstframe( mp3data, mp3filesize );
if ( pos == -1 ) {
sprintf( errormessage, "no mpeg audio data recognized" );
errorlevel = 2;
free( mp3data );
return false;
}
main_data_begin = pos;
// check first frame header for MPEG and LAYER version
if ( mp3filesize - pos >= 2 ) {
type = MBITS( mp3data[pos+1], 5, 1 );
if ( type != MPEG1_LAYER_III ) {
sprintf( errormessage, "file is %s, not supported", filetype_description[type] );
errorlevel = 2;
free( mp3data );
return false;
}
}
// if theres still more data, read the whole file into memory now
// (bad for mem consumption, but for now -> so what?)
if ( mp3filesize == FIRST_FRAME_AREA ) {
mp3filesize = str_in->getsize(); // check size of file again
mp3data = (unsigned char*) frealloc( mp3data, mp3filesize * sizeof( char ) );
if ( mp3data == NULL ) {
sprintf( errormessage, MEM_ERRMSG );
errorlevel = 2;
return false;
}
str_in->read( mp3data + FIRST_FRAME_AREA, 1, mp3filesize - FIRST_FRAME_AREA );
}
// now, read frames until the end of the stream or a bad frame is encountered
data_writer = new abytewriter(0);
while ( pos < mp3filesize ) {
// read frame, check result
frame = mp3_read_frame( mp3data + pos, mp3filesize - pos );
if ( frame == NULL ) {
if ( errorlevel == 2 ) {
// append some useful info to the existing error message
sprintf( errormessage + strlen( errormessage ), " (frame #%i at 0x%X)",
( lastframe != NULL ) ? lastframe->n + 1 : 0, pos );
free( mp3data );
delete( data_writer );
return false;
}
else break;
} else if ( frame->frame_size > mp3filesize - pos ) {
// check for incomplete last frame
incomplete_last_frame = true;
mp3_discard_frame( frame );
break;
} else if ( ( frame->mpeg != mpeg ) || ( frame->layer != layer ) ||
( frame->samples != samples ) || ( frame->channels != channels ) ) {
// check for inconsistencies
if ( mpeg == -1 ) {
mpeg = frame->mpeg;
layer = frame->layer;
samples = frame->samples;
channels = frame->channels;
} else {
mp3_discard_frame( frame );
break;
}
}
if ( lastframe != NULL ) { // fix previous frames aux size, check for problems
if ( frame->aux_size < 0 ) { // main data out of bounds
if ( lastframe->n > MAX_BAD_FIRST - 2 ) { mp3_discard_frame( frame ); break; }
else n_bad_first = lastframe->n + 2;
} else if ( lastframe->aux_size < frame->bit_reservoir ) { // overlapping main data
if ( lastframe->n > MAX_BAD_FIRST - 1 ) { mp3_discard_frame( frame ); break; }
else n_bad_first = lastframe->n + 1;
} else if ( frame->n == -1 ) { // obvious contradiction (see mp3_read_frame())
if ( lastframe->n > MAX_BAD_FIRST - 2 ) { mp3_discard_frame( frame ); break; }
else n_bad_first = lastframe->n + 2;
}
// fix previous frame aux size
lastframe->aux_size -= frame->bit_reservoir;
} else if ( ( frame->aux_size < 0 ) || ( frame->n == -1 ) ) n_bad_first = 1;
// update main index, store main data
frame->main_index = data_writer->getpos() - frame->bit_reservoir;
data_writer->write_n( mp3data + pos + frame->fixed_size, frame->frame_size - frame->fixed_size );
// insert frame into the frame chain
mp3_append_frame( frame );
// check for pointing to empty space
if ( frame->main_index < 0 ) n_bad_first = frame->n + 1;
// advance to next physical frame
pos += frame->frame_size;
}
// store main data
main_data_size = data_writer->getpos();
if ( main_data_size > 0 ) main_data = data_writer->getptr();
delete( data_writer );
// check number of proper frames (must be at least 5)
if ( lastframe->n + 1 - n_bad_first < 5 ) {
sprintf( errormessage, "corrupted file, compression not possible" );
errorlevel = 2;
return false;
}
// end of main mp3 data processing
main_data_end = pos;
// check for ID3 tags at end of file
if ( !incomplete_last_frame && ( main_data_end < mp3filesize ) ) {
if ( main_data_end + mp3_get_id3_size( mp3data+main_data_end, mp3filesize-main_data_end ) != mp3filesize ) {
// allow for a tolerance of 64K unidentified garbage data at EOF
if ( mp3filesize - main_data_end > GARBAGE_TOLERANCE ) {
sprintf( errormessage, "synching failure (frame #%i at 0x%X)", lastframe->n + 1, pos );
errorlevel = 2;
free( mp3data );
return false;
}/* else { // (!!!) strict mode?
sprintf( errormessage, "%i byte of unidentified garbage after EOF", mp3filesize - main_data_end );
errorlevel = 1;
}*/
}
}
// clean up and store id3 tags and garbage data
data_after_size = mp3filesize - main_data_end;
data_before_size = main_data_begin;
if ( data_after_size > 0 ) {
data_after = (unsigned char*) calloc( data_after_size, sizeof( char ) );
if ( ( mp3data == NULL ) || ( mp3filesize <= 0 ) ) {
sprintf( errormessage, MEM_ERRMSG );
errorlevel = 2;
return false;
}
memcpy( data_after, mp3data + main_data_end, data_after_size );
}
if ( data_before_size > 0 )
data_before = (unsigned char*) frealloc( mp3data, data_before_size * sizeof(char) );
else free( mp3data );
return true;
}
/* -----------------------------------------------
write MP3 from frame structure
----------------------------------------------- */
INTERN bool write_mp3( void )
{
unsigned char* data = main_data;
unsigned char* fixed;
mp3Frame* frame;
// write data before (usually ID3v2 tag)
str_out->write( data_before, 1, data_before_size );
// write physical frames
// main data has to be in order!
for ( frame = firstframe; frame != NULL; frame = frame->next ) {
fixed = mp3_build_fixed( frame );
str_out->write( fixed, 1, frame->fixed_size );
str_out->write( data, 1, frame->frame_size - frame->fixed_size );
data += frame->frame_size - frame->fixed_size;
}
// write data after
str_out->write( data_after, 1, data_after_size );
// errormessage if write error
if ( str_out->chkerr() ) {
sprintf( errormessage, "write error, possibly drive is full" );
errorlevel = 2;
return false;
}
// get filesize
mp3filesize = str_out->getsize();
return true;
}
/* -----------------------------------------------
analyse frames for redundancies and errors
----------------------------------------------- */
INTERN bool analyze_frames( void )
{
mp3Frame* frame;
granuleInfo* granule;
bool aux0 = false;
bool aux1 = false;
bool sbx_gain = false;
int nch;
int ch, gr;
// (re)set analysis data
i_mpeg = firstframe->mpeg;
i_layer = firstframe->layer;
i_samplerate = firstframe->samples;
i_bitrate = firstframe->bits;
i_protection = firstframe->protection;
i_padding = firstframe->padding;
i_privbit = firstframe->privbit;
i_channels = firstframe->channels;
i_stereo_ms = firstframe->stereo_ms;
i_stereo_int = firstframe->stereo_int;
i_copyright = firstframe->copyright;
i_original = firstframe->original;
i_emphasis = firstframe->emphasis;
i_padbits = firstframe->padbits;
i_bit_res = ( firstframe->bit_reservoir > 0 ) ? 1 : 0;
if ( firstframe->aux_size > 0 ) {
i_aux_h = 1;
aux0 = true;
} else i_aux_h = 0;
// for granules
granule = firstframe->granules[0][0];
i_share = ( granule->share != 0 ) ? 1 : 0;
i_sblocks = ( granule->window_switching != 0 ) ? 1 : 0;
i_mixed = ( granule->mixed_flag != 0 ) ? 1 : 0;
i_preemphasis = granule->preemphasis;
i_coarse = granule->coarse_scalefactors;
// easy way out for these - will be tested later
i_sb_diff = 0;
i_sbgain = 0;
// analyse frames, check for redundancies and errors
for ( frame = firstframe; frame != NULL; frame = frame->next ) {
// no need to check mpeg, layer, samples, channels
// already did this in read_mp3()!
// analyse (mainly) frame header data
// special tolerance for the following, as they often get mixed up in frame #0
if ( ( i_privbit != -1 ) && ( i_privbit != frame->privbit ) ) {
if ( frame->n == 1 ) { // tolerance for first frame
if ( n_bad_first == 0 ) n_bad_first = 1;
i_privbit = frame->privbit;
} else i_privbit = -1;
}
if ( ( i_copyright != -1 ) && ( i_copyright != frame->copyright ) ) {
if ( frame->n == 1 ) { // tolerance for first frame
if ( n_bad_first == 0 ) n_bad_first = 1;
i_copyright = frame->copyright;
} else i_copyright = -1;
}
if ( ( i_original != -1 ) && ( i_original != frame->original ) ) {
if ( frame->n == 1 ) { // tolerance for first frame
if ( n_bad_first == 0 ) n_bad_first = 1;
i_original = frame->original;
} else i_original = -1;
}
if ( ( i_protection != -1 ) && ( i_protection != frame->protection ) ) i_protection = -1;
if ( ( i_bitrate != -1 ) && ( i_bitrate != frame->bits ) ) i_bitrate = -1;
if ( ( i_padding != -1 ) && ( i_padding != frame->padding ) ) i_padding = -1;
if ( ( i_stereo_ms != -1 ) && ( i_stereo_ms != frame->stereo_ms ) ) i_stereo_ms = -1;
if ( ( i_stereo_int != -1 ) && ( i_stereo_int != frame->stereo_int ) ) i_stereo_int = -1;
if ( ( i_emphasis != -1 ) && ( i_emphasis != frame->emphasis ) ) i_emphasis = -1;
if ( ( i_padbits != -1 ) && ( i_padbits != frame->padbits ) ) i_padbits = -1;
if ( ( i_bit_res != 1 ) && ( frame->bit_reservoir > 0 ) ) i_bit_res = 1;
if ( i_aux_h != -1 ) {
// no auxiliary before aux0 or after aux1
if ( frame->aux_size > 0 ) {
if ( !aux0 ) aux1 = true;
} else {
if ( aux1 ) i_aux_h = -1;
if ( aux0 ) aux0 = false;
}
}
nch = frame->nchannels;
for ( ch = 0; ch < nch; ch++ ) {
for ( gr = 0; gr < 2; gr++ ) {
granule = frame->granules[ch][gr];
sbx_gain =
( granule->sb_gain[0] != 0 ) ||
( granule->sb_gain[1] != 0 ) ||
( granule->sb_gain[2] != 0 );
if ( ( i_share != 1 ) && ( granule->share != 0 ) ) i_share = 1;
if ( ( i_sblocks != 1 ) && ( granule->window_switching ) ) i_sblocks = 1;
if ( ( i_mixed != 1 ) && ( granule->mixed_flag ) ) i_mixed = 1;
if ( ( i_preemphasis != -1 ) && ( i_preemphasis != granule->preemphasis ) ) i_preemphasis = -1;
if ( ( i_coarse != -1 ) && ( i_coarse != granule->coarse_scalefactors ) ) i_coarse = -1;
if ( ( i_sbgain != -1 ) && ( sbx_gain ) ) i_sbgain = ( granule->block_type == SHORT_BLOCK ) ? 1 : -1;
if ( ( i_sb_diff != 1 ) && ( ch == 1 ) )
if ( granule->block_type != frame->granules[0][gr]->block_type ) i_sb_diff = 1;
}
}
}
// last check for aux data handling
if ( ( i_aux_h != -1 ) && ( aux1 ) ) i_aux_h = 1;
// fill in some global info
g_nframes = lastframe->n + 1;
g_nchannels = ( i_channels == MP3_MONO ) ? 1 : 2; // number of channels
g_samplerate = mp3_samplerate_table[(int)i_samplerate]; // sample rate
g_bitrate = ( i_bitrate == -1 ) ? 0 : mp3_bitrate_table[(int)i_bitrate]; // bit rate
return true;
}
INTERN bool compress_mp3( void )
{
aricoder* encoder;
// --- check for incompatibilities and problems ---
// unsupported stuff -> not recoverable
if ( i_protection == -1 ) { // inconsistent use of checksums
sprintf( errormessage, "inconsistent use of checksums, not supported" );
errorlevel = 2;
return false;
}
if ( i_stereo_int == -1 ) { // inconsistent use of intensity stereo
sprintf( errormessage, "inconsistent use of int stereo, not supported" );
errorlevel = 2;
return false;
}
if ( i_emphasis == -1 ) { // inconsistent use of emphasis
sprintf( errormessage, "inconsistent use of emphasis, not supported" );
errorlevel = 2;
return false;
}
if ( i_mixed != 0x0 ) { // mixed blocks
sprintf( errormessage, "mixed blocks used, not supported" );
errorlevel = 2;
return false;
}
// inconsistencies -> recoverable
if ( i_privbit == -1 ) { // inconsistent private bit -> steganograhy?
sprintf( errormessage, "inconsistent private bit" );
errorlevel = 1;
i_privbit = 0;
}
if ( i_copyright == -1 ) { // inconsistent copyright bit -> steganograhy?
sprintf( errormessage, "inconsistent copyright bit" );
errorlevel = 1;
i_copyright = 0;
}
if ( i_original == -1 ) { // inconsistent original bit -> steganograhy?
sprintf( errormessage, "inconsistent original bit" );
errorlevel = 1;
i_original = 1;
}
if ( i_padbits != 0 ) { // non-zero padbits -> steganograhy?
sprintf( errormessage, "non-zero padbits found" );
errorlevel = 1;
i_padbits = 0;
}
// --- write PMP header with some basic info ---
// PMP magic number & version byte
str_out->write( (void*) pmp_magic, 1, 2 );
str_out->write( (void*) &appversion, 1, 1 );
// PMP header data
if ( !pmp_write_header( str_out ) ) return false;
#if defined( STORE_ID3 )
// --- store ID3 data instead of compressing ---
if ( data_before_size > 0 )
if ( pmp_store_data( str_out, data_before, data_before_size ) != data_before_size ) return false;
if ( data_after_size > 0 )
if ( pmp_store_data( str_out, data_after, data_after_size ) != data_after_size ) return false;
#endif
// --- mute frames, store fix data (only if broken) ---
if ( n_bad_first > 0 ) {
// mute all frames up to last bad
for ( mp3Frame* frame = firstframe; n_bad_first > 0; frame = frame->next, n_bad_first-- )
mp3_mute_frame( frame );
// store fix data
if ( !pmp_store_unmute_data( str_out ) ) return false;
}
// --- actual compressed data writing starts here ---
// init arithmetic compression
encoder = new aricoder( str_out, 1 );
#if !defined( STORE_ID3 )
// id3 tags / other tags / garbage
if ( ( data_before_size > 0 ) || ( data_after_size > 0 ) )
if ( !pmp_encode_id3( encoder ) ) return false;
#endif
// frame header data
if ( i_padding != 0 ) // padding bits
if ( !pmp_encode_padding( encoder ) ) return false;
if ( i_sblocks != 0 ) // special block types
if ( !pmp_encode_block_types( encoder ) ) return false;
// global gain
if ( !pmp_encode_global_gain( encoder ) ) return false;
// build context from global gain
if ( !pmp_build_context() ) return false;
// slength
if ( !pmp_encode_slength( encoder ) ) return false;
/*
// region sizes
if ( !pmp_encode_region_bounds( encoder ) ) return false;
// huffman table selections
if ( !pmp_encode_htable_selection( encoder ) ) return false;
*/
if ( !pmp_encode_region_data( encoder ) ) return false;
if ( i_share != 0 ) // scalefactor sharing
if ( !pmp_encode_sharing( encoder ) ) return false;
if ( i_preemphasis != 0 ) // preemphasis
if ( !pmp_encode_preemphasis( encoder ) ) return false;
if ( i_coarse != 0 ) // coarse setting
if ( !pmp_encode_coarse_sf( encoder ) ) return false;
if ( i_sbgain != 0 ) // subblock gain
if ( !pmp_encode_subblock_gain( encoder ) ) return false;
if ( i_stereo_ms != 0 ) // ms stereo settings
if ( !pmp_encode_stereo_ms( encoder ) ) return false;
// main data
if ( !pmp_encode_main_data( encoder ) ) return false;
// finalize arithmetic compression
delete( encoder );
// --- final checks ---
// errormessage if write error
if ( str_out->chkerr() ) {
sprintf( errormessage, "write error, possibly drive is full" );
errorlevel = 2;
return false;
}
// get filesize
pmpfilesize = str_out->getsize();
return true;
}
INTERN bool uncompress_pmp( void )
{
aricoder* decoder;
unsigned char hcode;
// --- no error checks needed! ---
// (we already did this when compressing)
// --- read PMP header and analyse basic info ---
// PMP magic number
// skip 2 bytes (no need to check again)
str_in->read( &hcode, 1, 1 );
str_in->read( &hcode, 1, 1 );
// version number
str_in->read( &hcode, 1, 1 );
// compare version number
if ( hcode != appversion ) {
sprintf( errormessage, "incompatible file, use %s v%i.%i",
appname, hcode / 10, hcode % 10 );
errorlevel = 2;
return false;
}
// read and analyse header
if ( !pmp_read_header( str_in ) ) return false;
#if defined( STORE_ID3 )
// --- unstore ID3 data ---
if ( data_before_size > 0 ) {
data_before_size = pmp_unstore_data( str_in, &data_before );
if ( data_before_size <= 0 ) return false;
}
if ( data_after_size > 0 ) {
data_after_size = pmp_unstore_data( str_in, &data_after );
if ( data_after_size <= 0 ) return false;
}
#endif
// --- unstore unmute fix data for later use (only if needed) ---
// unstore fix data
if ( n_bad_first > 0 ) {
if ( !pmp_unstore_unmute_data( str_in ) ) return false;
n_bad_first = 0; // not a beautiful solution
}
// --- actual decompression starts here ---
// init arithmetic decompression
decoder = new aricoder( str_in, 0 );
#if !defined( STORE_ID3 )
// id3 tags / other tags / garbage
if ( ( data_before_size > 0 ) || ( data_after_size > 0 ) )
if ( !pmp_decode_id3( decoder ) ) return false;
#endif
// frame header data
if ( i_padding != 0 ) // padding bits
if ( !pmp_decode_padding( decoder ) ) return false;
if ( i_sblocks != 0 ) // special block types
if ( !pmp_decode_block_types( decoder ) ) return false;
// global gain
if ( !pmp_decode_global_gain( decoder ) ) return false;
// build context from global gain
if ( !pmp_build_context() ) return false;
// slength
if ( !pmp_decode_slength( decoder ) ) return false;
/*
// region sizes
if ( !pmp_decode_region_bounds( decoder ) ) return false;
// huffman table selections
if ( !pmp_decode_htable_selection( decoder ) ) return false;
*/
if ( !pmp_decode_region_data( decoder ) ) return false;
if ( i_share != 0 ) // scalefactor sharing
if ( !pmp_decode_sharing( decoder ) ) return false;
if ( i_preemphasis != 0 ) // preemphasis
if ( !pmp_decode_preemphasis( decoder ) ) return false;
if ( i_coarse != 0 ) // coarse setting
if ( !pmp_decode_coarse_sf( decoder ) ) return false;
if ( i_sbgain != 0 ) // subblock gain
if ( !pmp_decode_subblock_gain( decoder ) ) return false;
if ( i_stereo_ms != 0 ) // ms stereo settings
if ( !pmp_decode_stereo_ms( decoder ) ) return false;
// main data
if ( !pmp_decode_main_data( decoder ) ) return false;
// finalize arithmetic compression
delete( decoder );
// --- unmute frames and reverse to bad (but bitwise identical) state ---
if ( unmute_data_size > 0 ) // unmute all frames up to # found in unmute data first byte
for ( mp3Frame* frame = firstframe; n_bad_first < *unmute_data; frame = frame->next, n_bad_first++ )
mp3_unmute_frame( frame );
// --- final checks ---
// get filesize
pmpfilesize = str_in->getsize();
return true;
}
/* ----------------------- End of main functions -------------------------- */
/* ----------------------- Begin of MP3 specific functions -------------------------- */
/* -----------------------------------------------
read one physical frame
----------------------------------------------- */
INTERN inline mp3Frame* mp3_read_frame( unsigned char* data, int max_size )
{
mp3Frame* frame;
granuleInfo* granule;
abitreader* side_reader;
unsigned char* header;
unsigned char* sideinfo;
unsigned short crc;
int nsb = 0;
int nch = 0;
int ch, gr;
// immediately return if not enough data (min size for header and side info = 21)
if ( max_size < 21 ) return NULL;
// --- frame header ---
header = data + 0;
// check syncword
if ( ( header[0] != 0xFF ) || ( (header[1]&0xFA) != 0xFA ) ) {
// no syncword or not MPEG-1 LAYER III
// might be end of audio data -> process in other function
return NULL;
}
// alloc memory for frame
frame = (mp3Frame*) calloc( 1, sizeof( mp3Frame ) );
if ( frame == NULL ) {
sprintf( errormessage, MEM_ERRMSG );
errorlevel = 2;
return NULL;
}
// preset pointers
frame->granules = NULL;
frame->prev = NULL;
frame->next = NULL;
frame->n = 0;
// extract data
frame->mpeg = (header[1]>>3)&0x3;
frame->layer = (header[1]>>1)&0x3;
frame->protection = ((header[1]>>0)&0x1)^1;
frame->bits = (header[2]>>4)&0xF;
frame->samples = (header[2]>>2)&0x3;
frame->padding = (header[2]>>1)&0x1;
frame->privbit = (header[2]>>0)&0x1;
frame->channels = (header[3]>>6)&0x3;
frame->stereo_ms = (header[3]>>5)&0x1;
frame->stereo_int = (header[3]>>4)&0x1;
frame->copyright = (header[3]>>3)&0x1;
frame->original = (header[3]>>2)&0x1;
frame->emphasis = (header[3]>>0)&0x3;
// check data for problems
// this also contains the never used free form stream flag -> so what?
if ( ( frame->bits == 0x0 ) || ( frame->bits == 0xF ) || ( frame->samples == 0x3 ) ) {
free( frame );
return NULL;
}
// number of channels
frame->nchannels = ( frame->channels == MP3_MONO ) ? 1 : 2;
nch = frame->nchannels;
// alloc memory for granules
frame->granules = (granuleInfo***) calloc( nch, sizeof( granuleInfo** ) );
for ( ch = 0; ch < nch; ch++ ) {
frame->granules[ch] = (granuleInfo**) calloc( 2, sizeof( granuleInfo* ) );
for ( gr = 0; gr < 2; gr++ )
frame->granules[ch][gr] = (granuleInfo*) calloc( 1, sizeof( granuleInfo ) );
}
// calculate size of frame - use the lookup table
frame->frame_size = mp3_frame_size_table[(int)frame->samples][(int)frame->bits];
if ( frame->padding ) frame->frame_size++;
// this is the actual calculation routine, which is now no more used
/* if ( frame->layer != LAYER_I ) frame->frame_size = (int) ( frame->padding +
( ( 144000 * bitrate_table[frame->mpeg][frame->layer][frame->bits] ) /
samplerate_table[frame->mpeg][frame->samples] ) );
else frame->frame_size = (int) ( ( ( frame->padding ) ? 4 : 0 ) +
( ( 48000 * bitrate_table[frame->mpeg][frame->layer][frame->bits] ) /
samplerate_table[frame->mpeg][frame->samples] ) );*/
// --- side information... ---
nsb = ( nch == 1 ) ? 17 : 32;
frame->fixed_size = 4 + nsb + (frame->protection ? 2 : 0);
// check if enough data is available
if ( frame->fixed_size > max_size ) {
free( frame );
return NULL;
}
// --- ...and optional crc checksum ---
if ( frame->protection == 0x1 ) {
sideinfo = data + 6;
// if there is a crc: check and discard
crc = (data[4]<<8) + (data[5]<<0);
if ( crc != mp3_calc_layer3_crc( header, sideinfo, nsb ) ) {
sprintf( errormessage, "crc checksum mismatch" );
errorlevel = 1; // (!!!) careful - file might be broken
}
}
else sideinfo = data + 4;
side_reader = new abitreader( sideinfo, nsb );
// frame global side info
frame->bit_reservoir = (short) side_reader->read( 9 );
frame->padbits = (char) side_reader->read( (nch==1) ? 5 : 3 );
for ( ch = 0; ch < nch; ch++ ) {
frame->granules[ch][0]->share = (char) side_reader->read( 4 );
frame->granules[ch][1]->share = 0x0;
}
// granule specific side info
for ( gr = 0; gr < 2; gr++ ) {
for ( ch = 0; ch < nch; ch++ ) {
granule = frame->granules[ch][gr];
granule->main_data_bit = (short) side_reader->read( 12 );
granule->big_val_pairs = (short) side_reader->read( 9 );
granule->global_gain = (short) side_reader->read( 8 );
granule->slength = (char) side_reader->read( 4 );
granule->window_switching = (char) side_reader->read( 1 );
if ( granule->window_switching == 0 ) { // for normal blocks
granule->region_table[0] = (char) side_reader->read( 5 );
granule->region_table[1] = (char) side_reader->read( 5 );
granule->region_table[2] = (char) side_reader->read( 5 );
granule->region0_size = (char) side_reader->read( 4 );
granule->region1_size = (char) side_reader->read( 3 );
if ( granule->region0_size+granule->region1_size > 20 ) {
sprintf( errormessage, "region size out of bounds" );
errorlevel = 2;
return NULL;
}
granule->region_bound[0] =
mp3_bandwidth_bounds[(int)frame->samples][(int)granule->region0_size+1];
granule->region_bound[1] =
mp3_bandwidth_bounds[(int)frame->samples][(int)granule->region0_size+granule->region1_size+2];
granule->region_bound[2] = granule->big_val_pairs << 1;
if ( granule->region_bound[0] > granule->region_bound[2] ) {
granule->region_bound[0] = granule->region_bound[2];
granule->region_bound[1] = granule->region_bound[2];
} else if ( granule->region_bound[1] > granule->region_bound[2] )
granule->region_bound[1] = granule->region_bound[2];
granule->block_type = LONG_BLOCK;
granule->mixed_flag = 0;
granule->sb_gain[0] = 0;
granule->sb_gain[1] = 0;
granule->sb_gain[2] = 0;
} else { // for special blocks
granule->block_type = (char) side_reader->read( 2 );
granule->mixed_flag = (char) side_reader->read( 1 );
granule->region_table[0] = (char) side_reader->read( 5 );
granule->region_table[1] = (char) side_reader->read( 5 );
granule->sb_gain[0] = (char) side_reader->read( 3 );
granule->sb_gain[1] = (char) side_reader->read( 3 );
granule->sb_gain[2] = (char) side_reader->read( 3 );
if ( granule->block_type != SHORT_BLOCK ) {
// region sizes for different block types
granule->region0_size = 8;
granule->region1_size = 0;
granule->region_bound[0] =
mp3_bandwidth_bounds[(int)frame->samples][8];
} else { // special treatment for mixed blocks needed (!)
granule->region0_size = 9;
granule->region1_size = 0;
granule->region_bound[0] =
mp3_bandwidth_bounds_short[(int)frame->samples][9/3] * 3;
}
granule->region_bound[1] = granule->big_val_pairs << 1;
if ( granule->region_bound[0] > granule->region_bound[1] )
granule->region_bound[0] = granule->region_bound[1];
granule->region_bound[2] = granule->region_bound[1];
granule->region_table[2] = 0;
}
granule->preemphasis = (char) side_reader->read( 1 );
granule->coarse_scalefactors = (char) side_reader->read( 1 );
granule->select_htabB = (char) side_reader->read( 1 );
granule->sv_bound = 0;
// check for obvious problems/contradictions
if ( granule->main_data_bit == 0 ) {
if ( ( granule->big_val_pairs != 0 ) || ( granule->slength != 0 ) )
frame->n = -1; // mistreat frame number as trouble indicator :-)
}
}
}
delete( side_reader );
// calculate total size of main (not aux) data (in byte)
frame->main_bits = 0;
for ( gr = 0; gr < 2; gr++ )
for ( ch = 0; ch < nch; ch++ )
frame->main_bits += frame->granules[ch][gr]->main_data_bit;
frame->main_size = (frame->main_bits+7)>>3;
// calculate temporary size of aux data (in byte), subject to change
frame->aux_size =
frame->frame_size -
frame->fixed_size +
frame->bit_reservoir -
frame->main_size;
return frame;
}
/* -----------------------------------------------
build frame from i_variables
----------------------------------------------- */
INTERN inline mp3Frame* mp3_build_frame( void )
{
mp3Frame* frame;
granuleInfo* granule;
int ch, gr;
int nch;
// alloc memory for frame
frame = (mp3Frame*) calloc( 1, sizeof( mp3Frame ) );
if ( frame == NULL ) {
sprintf( errormessage, MEM_ERRMSG );
errorlevel = 2;
return NULL;
}
// preset pointers
frame->granules = NULL;
frame->prev = NULL;
frame->next = NULL;
// fill in some info
frame->mpeg = i_mpeg;
frame->layer = i_layer;
frame->protection = i_protection;
frame->bits = i_bitrate;
frame->samples = i_samplerate;
frame->padding = i_padding;
frame->privbit = i_privbit;
frame->channels = i_channels;
frame->stereo_ms = i_stereo_ms;
frame->stereo_int = i_stereo_int;
frame->copyright = i_copyright;
frame->original = i_original;
frame->emphasis = i_emphasis;
frame->bit_reservoir = 0;
frame->padbits = i_padbits;
// stuff that is known right now (# channels and fixed size)
if ( frame->channels == MP3_MONO ) {
frame->fixed_size = ( frame->protection ) ? 4 + 2 + 17 : 4 + 17;
frame->nchannels = 1; nch = frame->nchannels;
} else {
frame->fixed_size = ( frame->protection ) ? 4 + 2 + 32 : 4 + 32;
frame->nchannels = 2; nch = frame->nchannels;
}
// alloc memory for granules
frame->granules = (granuleInfo***) calloc( nch, sizeof( granuleInfo** ) );
for ( ch = 0; ch < nch; ch++ ) {
frame->granules[ch] = (granuleInfo**) calloc( 2, sizeof( granuleInfo* ) );
for ( gr = 0; gr < 2; gr++ )
frame->granules[ch][gr] = (granuleInfo*) calloc( 1, sizeof( granuleInfo ) );
}
// granule specific stuff
for ( ch = 0; ch < nch; ch++ ) {
for ( gr = 0; gr < 2; gr++ ) {
granule = frame->granules[ch][gr];
granule->share = ( gr == 0 ) ? i_share : 0;
granule->window_switching = i_sblocks;
granule->mixed_flag = i_mixed;
granule->block_type = LONG_BLOCK;
granule->sb_gain[0] = i_sbgain;
granule->sb_gain[1] = i_sbgain;
granule->sb_gain[2] = i_sbgain;
granule->preemphasis = i_preemphasis;
granule->coarse_scalefactors = i_coarse;
}
}
return frame;
}
/* -----------------------------------------------
append frame to the frame chain
----------------------------------------------- */
INTERN inline bool mp3_append_frame( mp3Frame* frame )
{
static granuleInfo* lastgranule[2] = { NULL, NULL };
static int n = 0;
int ch;
// insert frame into the frame chain, set up links between frames
// aux size correction has to take place elsewhere
if ( lastframe == NULL ) {
firstframe = frame;
frame->prev = NULL;
lastgranule[0] = NULL;
lastgranule[1] = NULL;
n = 0;
} else {
lastframe->next = frame;
frame->prev = lastframe;
}
lastframe = frame;
// set up links for granules between each other
for ( ch = 0; ch < frame->nchannels; ch++ ) {
frame->granules[ch][0]->n = n<<1;
frame->granules[ch][1]->n = frame->granules[ch][0]->n|1;
frame->granules[ch][0]->next = frame->granules[ch][1];
frame->granules[ch][1]->prev = frame->granules[ch][0];
frame->granules[ch][0]->prev = lastgranule[ch];
frame->granules[ch][1]->next = NULL;
if ( lastgranule[ch] != NULL )
lastgranule[ch]->next = frame->granules[ch][0];
lastgranule[ch] = frame->granules[ch][1];
}
// some generic stuff
frame->n = n++;
frame->next = NULL;
return true;
}
/* -----------------------------------------------
discard frame data
----------------------------------------------- */
INTERN inline bool mp3_discard_frame( mp3Frame* frame )
{
int nch;
int ch, gr;
// discard all data in one frame
nch = frame->nchannels;
if ( frame->granules != NULL ) {
for ( ch = 0; ch < nch; ch++ ) {
for ( gr = 0; gr < 2; gr++ )
free ( frame->granules[ch][gr] );
free ( frame->granules[ch] );
}
free ( frame->granules );
}
free ( frame );
return true;
}
/* -----------------------------------------------
mute a single frame
----------------------------------------------- */
INTERN inline bool mp3_mute_frame( mp3Frame* frame )
{
granuleInfo* granule;
unsigned char* ptr;
int nch, ums;
int ch, gr;
// mute a single frame -> dangerous, be careful!
nch = frame->nchannels;
ums = 2 + ( nch * 2 * 4 );
// store reconstruction data
if ( unmute_data_size == 0 ) { // first alloc - no mem check needed
unmute_data = (unsigned char*) calloc( ++unmute_data_size, sizeof( char ) );
unmute_data_size = 1;
*unmute_data = 0;
}
unmute_data = (unsigned char*) // realloc for new size
frealloc( unmute_data, ( unmute_data_size + ums ) * sizeof( char ) );
if ( unmute_data == NULL ) {
sprintf( errormessage, MEM_ERRMSG );
errorlevel = 2;
return false;
}
ptr = unmute_data + unmute_data_size;
unmute_data_size += ums;
// some bits are wasted here
// normally muting frames shouldn't be necessary at all anyways
(*ptr) = (frame->privbit&0x1) << 7;
(*ptr) |= (frame->original&0x1) << 6;
(*ptr) |= (frame->copyright&0x1) << 5;
(*ptr++) |= (frame->bit_reservoir >> 8) & 0x1;
(*ptr++) = (frame->bit_reservoir >> 0) & 0xFF;
for ( ch = 0; ch < nch; ch++ ) {
for ( gr = 0; gr < 2; gr++ ) {
granule = frame->granules[ch][gr];
(*ptr++) = (granule->main_data_bit >> 4) & 0xFF;
(*ptr++) = ( (granule->main_data_bit << 4) & 0xF0 ) | ( granule->slength & 0x0F );
(*ptr++) = (granule->big_val_pairs >> 1) & 0xFF;
(*ptr++) = (granule->big_val_pairs << 7) & 0x80;
}
}
// take count - careful this doesn't excede 255
(*unmute_data)++;
// mute frame - this has to be done in order
// we assume that all previous frames are muted, too!
// move aux data around the bit reservoirs only for files that already provide a
// bit reservoir. the decoder depends on this.
if ( frame->prev != NULL && i_bit_res != 0 ) {
// make some room, use prev frame aux too!
frame->bit_reservoir += frame->prev->aux_size;
if ( frame->bit_reservoir >= 512 ) {
frame->prev->aux_size = frame->bit_reservoir - 511;
frame->bit_reservoir = 511;
} else frame->prev->aux_size = 0;
} else frame->bit_reservoir = 0;
frame->aux_size = frame->frame_size - frame->fixed_size + frame->bit_reservoir;
if ( frame->next != NULL ) frame->aux_size -= frame->next->bit_reservoir;
frame->main_size = 0;
frame->main_bits = 0;
if ( frame->granules != NULL ) {
for ( ch = 0; ch < nch; ch++ ) {
for ( gr = 0; gr < 2; gr++ ) {
granule = frame->granules[ch][gr];
granule->main_data_bit = 0;
granule->big_val_pairs = 0;
granule->slength = 0;
// fix region bounds
granule->region_bound[0] = 0;
granule->region_bound[1] = 0;
granule->region_bound[2] = 0;
}
}
}
return true;
}
/* -----------------------------------------------
unmute a single frame
----------------------------------------------- */
INTERN inline bool mp3_unmute_frame( mp3Frame* frame )
{
granuleInfo* granule;
unsigned char* ptr;
int nch, ums;
int ch, gr;
// restore a single frame to it's original, broken state
// enough fix data has to be present, channel mode has to be consistent
nch = g_nchannels;
ums = 2 + ( nch * 2 * 4 );
ptr = unmute_data + 1 + ( n_bad_first * ums );
// unmute frame - has to be done in order, too!
frame->privbit = ((*ptr)>>7) & 0x1;
frame->original = ((*ptr)>>6) & 0x1;
frame->copyright = ((*ptr)>>5) & 0x1;
frame->bit_reservoir = (*ptr++) << 8;
frame->bit_reservoir |= (*ptr++) << 0;
for ( ch = 0; ch < nch; ch++ ) {
for ( gr = 0; gr < 2; gr++ ) {
granule = frame->granules[ch][gr];
granule->main_data_bit = (*ptr++) << 4;
granule->main_data_bit |= (*ptr) >> 4;
granule->slength = (*ptr++) & 0x0F;
granule->big_val_pairs = (*ptr++) << 1;
granule->big_val_pairs |= (*ptr++) >> 7;
}
} // no need to accomodate for the changed frame params - for now (!!!)
return true;
}
/* -----------------------------------------------
build fixed part of physical frame
----------------------------------------------- */
INTERN inline unsigned char* mp3_build_fixed( mp3Frame* frame )
{
static unsigned char* fixed = ( unsigned char* ) calloc( 64, 1 );
unsigned char* tmp_ptr;
granuleInfo* granule;
abitwriter* side_writer;
unsigned char* header;
unsigned char* sideinfo;
unsigned short crc;
int nsb = 0;
int nch = 0;
int ch, gr;
// preparations
memset( fixed, 0, 64 );
nch = frame->nchannels;
nsb = ( nch == 1 ) ? 17 : 32;
header = fixed + 0;
sideinfo = fixed + ( ( frame->protection ) ? 4 + 2 : 4 );
// --- frame header ---
// insert data
header[0] = 0xFF;
header[1] = 0xFA;
header[1] |= frame->mpeg << 3;
header[1] |= frame->layer << 1;
header[1] |= frame->protection ^ 1;
header[2] |= frame->bits << 4;
header[2] |= frame->samples << 2;
header[2] |= frame->padding << 1;
header[2] |= frame->privbit << 0;
header[3] |= frame->channels << 6;
header[3] |= frame->stereo_ms << 5;
header[3] |= frame->stereo_int << 4;
header[3] |= frame->copyright << 3;
header[3] |= frame->original << 2;
header[3] |= frame->emphasis << 0;
// --- side information... ---
// init abitwriter
side_writer = new abitwriter( 64 );
// frame global side info
side_writer->write( frame->bit_reservoir, 9 );
side_writer->write( frame->padbits, (nch==1) ? 5 : 3 );
for ( ch = 0; ch < nch; ch++ )
side_writer->write( frame->granules[ch][0]->share, 4 );
// granule specific side info
for ( gr = 0; gr < 2; gr++ ) {
for ( ch = 0; ch < nch; ch++ ) {
granule = frame->granules[ch][gr];
side_writer->write( granule->main_data_bit, 12 );
side_writer->write( granule->big_val_pairs, 9 );
side_writer->write( granule->global_gain, 8 );
side_writer->write( granule->slength, 4 );
side_writer->write( granule->window_switching, 1 );
if ( granule->window_switching == 0 ) { // for normal blocks
side_writer->write( granule->region_table[0], 5 );
side_writer->write( granule->region_table[1], 5 );
side_writer->write( granule->region_table[2], 5 );
side_writer->write( granule->region0_size, 4 );
side_writer->write( granule->region1_size, 3 );
} else { // for special blocks
side_writer->write( granule->block_type, 2 );
side_writer->write( granule->mixed_flag, 1 );
side_writer->write( granule->region_table[0], 5 );
side_writer->write( granule->region_table[1], 5 );
side_writer->write( granule->sb_gain[0], 3 );
side_writer->write( granule->sb_gain[1], 3 );
side_writer->write( granule->sb_gain[2], 3 );
}
side_writer->write( granule->preemphasis, 1 );
side_writer->write( granule->coarse_scalefactors, 1 );
side_writer->write( granule->select_htabB, 1 );
}
}
// get pointer, store and free up memory
tmp_ptr = side_writer->getptr();
memcpy( sideinfo, tmp_ptr, nsb );
delete( side_writer );
free( tmp_ptr );
// --- ...and optional crc checksum ---
if ( frame->protection == 0x1 ) {
crc = mp3_calc_layer3_crc( header, sideinfo, nsb );
header[ 4 ] = (crc>>8)&0xFF;
header[ 5 ] = (crc>>0)&0xFF;
}
return fixed;
}
/* -----------------------------------------------
seeks for the first proper MPEG audio frame
----------------------------------------------- */
INTERN inline int mp3_seek_firstframe( unsigned char* data, int size )
{
int mpeg = -1;
int layer = -1;
int samples = -1;
int channels = -1;
int protection = -1;
int bits;
int padding;
int frame_size;
int pos0 = 0;
int pos1 = 0;
int pos, n;
// check for ID3 or other tag at beginning of data
pos0 = mp3_get_id3_size( data, size );
// calculate last tolerable seek position
pos1 = pos0 + GARBAGE_TOLERANCE;
if ( pos1 > size - 4 ) pos1 = size - 4;
// mp3 frame seeker loop
// conditions for proper first frame: 5 consecutive frames,
// same channel, mpeg, layer, samples setting
while( true ) {
// seek for first frame candidate
for ( ; pos0 < pos1; pos0++ )
if ( data[pos0] == 0xFF ) if ( (data[pos0+1]&0xE0) == 0xE0 ) break;
// nothing found -> give up
if ( pos0 == pos1 ) return -1;
// check for consecutive frames
for ( pos = pos0, n = 0; ( n < 5 ) && ( pos < size - 4 ); n++, pos += frame_size ) {
// check syncword
if ( ( data[pos] != 0xFF ) || ( (data[pos+1]&0xE0) != 0xE0 ) ) break;
// extract and store or compare data from header
if ( n == 0 ) {
mpeg = (data[pos+1]>>3)&0x3;
layer = (data[pos+1]>>1)&0x3;
protection = (data[pos+1]>>0)&0x1;
samples = (data[pos+2]>>2)&0x3;
channels = (data[pos+3]>>6)&0x3;
} else if (
( mpeg != ((data[pos+1]>>3)&0x3) ) ||
( layer != ((data[pos+1]>>1)&0x3) ) ||
( protection != ((data[pos+1]>>0)&0x1) ) ||
( samples != ((data[pos+2]>>2)&0x3) ) ||
( channels != ((data[pos+3]>>6)&0x3) ) ) break;
bits = (data[pos+2]>>4)&0xF;
padding = (data[pos+2]>>1)&0x1;
// check for problems
if ( ( mpeg == 0x1 ) || ( layer == 0x0 ) ||
( bits == 0x0 ) || ( bits == 0xF ) || ( samples == 0x3 ) ) break;
// find out frame size
frame_size = frame_size_table[mpeg][layer][samples][bits];
if ( padding ) frame_size += (layer == LAYER_I) ? 4 : 1;
}
// consider first frame proper if 5 consecutive frames are found
if ( n >= 5 ) break;
pos0++;
}
return pos0;
}
/* -----------------------------------------------
extract ID3v2 tag from beginning of file
----------------------------------------------- */
INTERN inline int mp3_get_id3_size( unsigned char* id3tag, int max_size )
{
static const char* id3v1_begin = "TAG";
static const char* id3v2_begin = "ID3";
static const char* lyrics3_begin = "LYRICSBEGIN";
static const char* apetag_begin = "APETAGEX";
bool unsynchronized = false;
int size;
// check if at least 10 bytes are available
if ( max_size < 10 ) return 0;
if ( memcmp( id3v2_begin, id3tag, 3 ) == 0 ) {
// ID3v2 tag -> find out size, start with 10
size = 10;
// check for unsynchronization
unsynchronized = ( BITN( id3tag[5], 7 ) == 1 );
// check for footer
size += ( BITN( id3tag[5], 4 ) == 1 ) ? 10 : 0;
// calculate size
size += id3tag[9] << 0;
size += id3tag[8] << 7;
size += id3tag[7] << 14;
size += id3tag[6] << 21;
// check if size is ok
if ( size > max_size ) return 0;
// pay attention to unsynchronization where needed
if ( unsynchronized ) for ( int pos = 0; pos < size-1; pos++ ) {
if ( id3tag[pos] == 0xFF ) if ( id3tag[pos+1] == 0x00 ) {
pos++; size++;
if ( size > max_size ) return 0;
}
}
}
else if ( memcmp( id3v1_begin, id3tag, 3 ) == 0 ) {
// ID3v1 tag -> size is exactle 128 byte
size = 128;
// check if size is ok
if ( size > max_size ) return 0;
}
else if ( ( memcmp( lyrics3_begin, id3tag, 6 ) == 0 ) && ( max_size >= 148 ) ) {
// LYRICS3 tag, must be followed by ID3v1 -> doublecheck and find out size
if ( ( memcmp( lyrics3_begin, id3tag, 11 ) == 0 ) && ( memcmp( id3v1_begin, id3tag + max_size - 128, 3 ) == 0 ) && ( memcmp( lyrics3_begin, (char*) id3tag + max_size - 128 - 9, 6 ) == 0 ) )
size = max_size;
else return 0;
}
else if ( memcmp( apetag_begin, id3tag, 8 ) == 0 ) {
// APE tag -> keep all the data after (ok solution for now)
size = max_size;
}
else {
// no tag header found -> not a proper tag
return 0;
}
return size;
}
/* -----------------------------------------------
calculate frame crc
----------------------------------------------- */
INTERN inline unsigned short mp3_calc_layer3_crc( unsigned char* header, unsigned char* sideinfo, int sidesize )
{
// crc has a start value of 0xFFFF
unsigned short crc = 0xFFFF;
// process two last bytes from header...
crc = (crc << 8) ^ crc_table[(crc>>8) ^ header[2]];
crc = (crc << 8) ^ crc_table[(crc>>8) ^ header[3]];
// ... and all the bytes from the side information
for ( int i = 0; i < sidesize; i++ )
crc = (crc << 8) ^ crc_table[(crc>>8) ^ sideinfo[i]];
return crc;
}
/* -----------------------------------------------
decode one MP3 frame
----------------------------------------------- */
INTERN inline granuleData*** mp3_decode_frame( huffman_reader* dec, mp3Frame* frame )
{
// storage
static granuleData*** frame_data = NULL;
granuleInfo* granule;
signed short* coefs;
unsigned char* scfs;
// scalefactors settings
const int* slen;
int sl;
// coefficients settings
short* region_bounds;
char* region_tables;
huffman_dec_table* bv_table;
huffman_conv_set* conv_set;
signed short* cf_start;
int linbits;
// general settings
unsigned char vals[4];
int bitp = 0;
int lmaxp = 0;
char share;
// counters
int ch, gr;
int p, g, r;
int i;
// alloc mem for frame data if not done before
// no need to free this memory again!
if ( frame_data == NULL ) {
frame_data = ( granuleData*** ) calloc( 2, sizeof( granuleData** ) );
for ( ch = 0; ch < 2; ch++ ) {
frame_data[ ch ] = ( granuleData** ) calloc( 2, sizeof( granuleData* ) );
for ( gr = 0; gr < 2; gr++ ) {
frame_data[ ch ][ gr ] = ( granuleData* ) calloc( 1, sizeof( granuleData ) );
frame_data[ ch ][ gr ]->scalefactors = ( unsigned char* ) calloc( 36, sizeof( char ) );
frame_data[ ch ][ gr ]->coefficients = ( signed short* ) calloc( 578, sizeof( short ) );
}
}
}
// decode frame
for ( gr = 0; gr < 2; gr++ ) {
for ( ch = 0; ch < frame->nchannels; ch++ ) {
// --- preparations ---
granule = frame->granules[ch][gr];
coefs = frame_data[ch][gr]->coefficients;
scfs = frame_data[ch][gr]->scalefactors;
cf_start = coefs;
// clear memory (= set coefficents/scalefactors zero)
memset( coefs, 0, sizeof( short ) * 576 );
memset( scfs, 0, sizeof( char ) * 36 );
// set position in stream
dec->setpos( frame->main_index + ( bitp / 8 ), 8 - ( bitp % 8 ) );
dec->reset_counter();
// take count of inner frame main data bits
lmaxp = granule->main_data_bit;
bitp += lmaxp;
// set decoding parameters
slen = slength_table[ (int) granule->slength ];
region_bounds = granule->region_bound;
region_tables = granule->region_table;
// --- scale factors ---
// read long block scalefactors (with sharing)
if ( granule->block_type != SHORT_BLOCK ) {
// get sharing params if any
share = ( gr ) ? frame->granules[ch][0]->share : 0;
// read 21 (max) scalefactors
for ( g = 0, p = 0; g < 4; g++ ) {
if ( (share>>(3-g)) & 0x1 ) { // shared
memcpy( scfs, frame_data[ch][0]->scalefactors + p, sizeof( char) * scf_width[ g ] );
scfs += scf_width[ g ];
p = scf_bounds[ g ];
} else for ( sl = slen[ (g<2)?0:1 ]; p < scf_bounds[ g ]; p++ ) { // non shared
*(scfs++) = dec->read_bits( sl );
}
}
} else { // read short block scfs
// read 36 (3*12) scalefactors
for ( i = 0; i < 3; i++ ) // 3 subblocks
for ( g = 0, p = 0; g < 3; g++ ) // 3 groups
for ( sl = slen[ (g==0)?0:1 ]; p < scf_bounds_short[ g ]; p++ ) // no sharing!
*(scfs++) = dec->read_bits( sl );
}
// --- coefficients / big values ---
for ( p = 0, r = 0; r < 3; r++ ) {
if ( region_tables[ r ] == 0 ) { // tbl0 skipping
coefs = cf_start + region_bounds[ r ];
p = region_bounds[ r ];
continue;
}
// decoding with other tables
bv_table = bv_dec_table + region_tables[ r ];
if ( bv_table == NULL ) return NULL;
conv_set = bv_table->h;
linbits = bv_table->linbits;
if ( linbits == 0 ) { // without linbits
for ( ; p < region_bounds[ r ]; p += 2, coefs += 2 ) {
dec->decode_pair( conv_set, vals );
for ( i = 0; i < 2; i++ ) if ( vals[i] > 0 )
coefs[i] = ( dec->read_bit() ) ? -vals[i] : vals[i];
if ( dec->get_count() > lmaxp ) {
// high error tolerance!
memset( coefs, 0, sizeof( short ) * 2 );
break;
}
}
} else { // with linbits
for ( ; p < region_bounds[ r ]; p += 2, coefs += 2 ) {
dec->decode_pair( conv_set, vals );
for ( i = 0; i < 2; i++ ) if ( vals[i] > 0 ) {
coefs[i] = ( vals[i] == 15 ) ? 15 + dec->read_bits( linbits ) : vals[i];
if ( dec->read_bit() ) coefs[i] = - coefs[i];
}
if ( dec->get_count() > lmaxp ) {
// high error tolerance!
memset( coefs, 0, sizeof( short ) * 2 );
break;
}
}
}
}
// --- coefficients / small values ---
conv_set = ( granule->select_htabB ) ? &htabB_dec : &htabA_dec;
for ( ; ( dec->get_count() < lmaxp ) && ( p < 576 ); p += 4, coefs += 4 ) {
dec->decode_quadruple( conv_set, vals );
for ( i = 0; i < 4; i++ ) if ( vals[i] )
coefs[i] = ( dec->read_bit() ) ? -1 : 1;
if ( dec->get_count() > lmaxp ) {
memset( coefs, 0, sizeof( short ) * 4 );
break;
}
}
// set sv_bound
granule->sv_bound = p;
}
}
return frame_data;
}
/* ----------------------- End of MP3 specific functions -------------------------- */
/* ----------------------- Begin of PMP specific functions -------------------------- */
/* -----------------------------------------------
writes the PMP file header
----------------------------------------------- */
INTERN inline bool pmp_write_header( iostream* str )
{
unsigned char header[4] = { 0 };
unsigned char nframes[4] = { 0 };
// build the header[]
// store necessary information, ignore unsupported
// 1st byte: global samples and channels, and...
// ...bitrate (zero if vbr/not global)
header[0] |= i_samplerate << 6; // sample rate
header[0] |= i_channels << 4; // channel mode
header[0] |= ( (i_bitrate!=-1) ? i_bitrate : 0 ) << 0; // bitrate
// 2nd byte: usage of padding, ms/int stereo, special blocks, subblock gain, ...
// ... sharing, preemphasis and coarse scalefactors
header[1] |= ( (i_padding==0) ? 0 : 1 ) << 7; // padding
header[1] |= ( (i_stereo_ms==0 ) ? 0 : 1 ) << 6; // ms stereo
header[1] |= ( (i_stereo_int==0 ) ? 0 : 1 ) << 5; // int stereo
header[1] |= ( (i_sblocks==0 ) ? 0 : 1 ) << 4; // special blocks
header[1] |= ( (i_sbgain==0) ? 0 : 1 ) << 3; // subblock gain
header[1] |= ( (i_share==0) ? 0 : 1 ) << 2; // sharing
header[1] |= ( (i_preemphasis==0) ? 0 : 1 ) << 1; // preemphasis
header[1] |= ( (i_coarse==0) ? 0 : 1 ) << 0; // coarse scfs
// 3rd byte: setting of protection, original, copyright and private bits, ...
// ... emphasis setting and indicators for data before/after, special block diffs, bad first frames
header[2] |= i_protection << 7; // protection
header[2] |= i_original << 6; // original bit
header[2] |= i_copyright << 5; // copyright bit
header[2] |= i_privbit << 4; // private bit
header[2] |= i_emphasis << 2; // emphasis
header[2] |= ( (data_before_size>0) ? 1 : 0 ) << 1; // data before
header[2] |= ( (data_after_size>0) ? 1 : 0 ) << 0; // data after
// 4th byte: usage of bit reservoir, special block diffs, bad first frames
// ... still 5 free (use for max comp)
header[3] |= ( (i_bit_res==0) ? 0 : 1 ) << 7; // bit reservoir
header[3] |= ( (i_sb_diff==0) ? 0 : 1 ) << 6; // special block diffs
header[3] |= ( (n_bad_first>0) ? 1 : 0 ) << 5; // bad first frames
// store # of frames in nframes[] in little endian
// yup, that's a waste, but - so what?
nframes[0] = ( g_nframes >> 24 ) & 0xFF;
nframes[1] = ( g_nframes >> 16 ) & 0xFF;
nframes[2] = ( g_nframes >> 8 ) & 0xFF;
nframes[3] = ( g_nframes >> 0 ) & 0xFF;
// 8 bytes to write, together with magic # and version: 11 bytes
str->write( (void*) header, 1, 4 );
str->write( (void*) nframes, 1, 4 );
return true;
}
/* -----------------------------------------------
reads the PMP file header
----------------------------------------------- */
INTERN inline bool pmp_read_header( iostream* str )
{
unsigned char header[4] = { 0 };
unsigned char nframes[4] = { 0 };
mp3Frame* frame = NULL;
int n;
// read header and size from file
str->read( (void*) header, 1, 4 );
str->read( (void*) nframes, 1, 4 );
if ( str->chkeof() ) {
sprintf( errormessage, "unexpected end of data" );
errorlevel = 2;
return false;
}
// extract information from header[]
// 1st byte: global samples and channels, and...
// ...bitrate (zero if vbr/not global)
i_samplerate = (header[0]>>6)&0x3;
i_channels = (header[0]>>4)&0x3;
i_bitrate = (header[0]>>0)&0xF;
if ( i_bitrate == 0 ) i_bitrate =-1;
// 2nd byte: usage of padding, ms/int stereo, special blocks, subblock gain, ...
// ... sharing, preemphasis and coarse scalefactors
i_padding = -((header[1]>>7)&0x1);
i_stereo_ms = -((header[1]>>6)&0x1);
i_stereo_int = ((header[1]>>5)&0x1);
i_sblocks = ((header[1]>>4)&0x1);
i_sbgain = -((header[1]>>3)&0x1);
i_share = -((header[1]>>2)&0x1);
i_preemphasis = -((header[1]>>1)&0x1);
i_coarse = -((header[1]>>0)&0x1);
// 3rd byte: setting of protection, original, copyright and private bits, ...
// ... emphasis setting and indicators for data before/after, special block diffs, bad first frames
i_protection = (header[2]>>7)&0x1;
i_original = (header[2]>>6)&0x1;
i_copyright = (header[2]>>5)&0x1;
i_privbit = (header[2]>>4)&0x1;
i_emphasis = (header[2]>>2)&0x3;
data_before_size = (header[2]>>1)&0x1;
data_after_size = (header[2]>>0)&0x1;
// 4th byte: usage of bit reservoir, special block diffs, bad first frames
// ... still 5 free (use for max comp)
i_bit_res = -((header[3]>>7)&0x1);
i_sb_diff = ((header[3]>>6)&0x1);
n_bad_first = (header[3]>>5)&0x1;
// set nframes known i_variables
i_mpeg = MP3_V1_0;
i_layer = LAYER_III;
i_padbits = 0;
i_mixed = 0;
i_aux_h = -2;
// check for possible mistakes made by myself
if ( ( i_bitrate == 0xF ) || ( i_samplerate == 0x3 ) ) {
sprintf( errormessage, "not a proper PMP file" );
errorlevel = 2;
return false;
}
// extract # of frames from nframes[] in little endian
g_nframes = 0;
g_nframes |= nframes[0] << 24;
g_nframes |= nframes[1] << 16;
g_nframes |= nframes[2] << 8;
g_nframes |= nframes[3] << 0;
// build frames structure
for ( n = 0; n < g_nframes; n++ ) {
// read frame, check result
frame = mp3_build_frame();
// bad result -> use existing error message and exit
if ( frame == NULL ) return false;
// insert frame into the frame chain
mp3_append_frame( frame );
}
// make some safe assumptions
g_nchannels = ( i_channels == MP3_MONO ) ? 1 : 2; // number of channels
g_samplerate = mp3_samplerate_table[(int)i_samplerate]; // sample rate
g_bitrate = ( i_bitrate == -1 ) ? 0 : mp3_bitrate_table[(int)i_bitrate]; // bit rate
return true;
}
#if !defined( STORE_ID3 )
/* -----------------------------------------------
encodes a stream of generic data (8bit)
----------------------------------------------- */
INTERN bool pmp_encode_id3( aricoder* enc )
{
// this will be used for id3 tags, other tags and garbage
// little is known, so we'll just use a generic markov model
model_s* model;
int i;
// arithmetic encode data
model = INIT_MODEL_S( 256 + 1, 256, 0 );
// data before main data
if ( data_before_size > 0 ) {
for ( i = 0; i < data_before_size; i++ )
encode_ari( enc, model, data_before[ i ] );
// encode end-of-data symbol (256)
encode_ari( enc, model, 256 );
}
// data after main data
if ( data_after_size > 0 ) {
for ( i = 0; i < data_after_size; i++ )
encode_ari( enc, model, data_after[ i ] );
// encode end-of-data symbol (256)
encode_ari( enc, model, 256 );
}
// done, delete model
delete( model );
return true;
}
/* -----------------------------------------------
decodes a stream of generic data (8 bit)
----------------------------------------------- */
INTERN bool pmp_decode_id3( aricoder* dec )
{
abytewriter* bwrt;
model_s* model;
int c;
// decode max. 2 chunks of data, ending with 256 symbol
model = INIT_MODEL_S( 256 + 1, 256, 0 );
// data before main data
if ( data_before_size > 0 ) {
bwrt = new abytewriter( 1024 ); // start byte writer
while ( true ) {
c = decode_ari( dec, model );
if ( c == 256 ) break;
bwrt->write( (unsigned char) c );
}
// check for out of memory
if ( bwrt->error ) {
delete bwrt; delete model;
sprintf( errormessage, MEM_ERRMSG );
errorlevel = 2;
return false;
}
// get data/true length and close byte writer
data_before = bwrt->getptr();
data_before_size = bwrt->getpos();
delete bwrt;
}
// data after main data
if ( data_after_size > 0 ) {
bwrt = new abytewriter( 1024 ); // start byte writer
while ( true ) {
c = decode_ari( dec, model );
if ( c == 256 ) break;
bwrt->write( (unsigned char) c );
}
// check for out of memory
if ( bwrt->error ) {
delete bwrt; delete model;
sprintf( errormessage, MEM_ERRMSG );
errorlevel = 2;
return false;
}
// get data/true length and close byte writer
data_after = bwrt->getptr();
data_after_size = bwrt->getpos();
delete bwrt;
}
// done, delete model
delete( model );
return true;
}
#else
/* -----------------------------------------------
stores a stream of generic data (8bit)
----------------------------------------------- */
INTERN inline int pmp_store_data( iostream* str, unsigned char* data, int size )
{
unsigned char ezis[4]; // little endian size
// store size in little endian
ezis[0] = ( size >> 24 ) & 0xFF;
ezis[1] = ( size >> 16 ) & 0xFF;
ezis[2] = ( size >> 8 ) & 0xFF;
ezis[3] = ( size >> 0 ) & 0xFF;
// the rest is as simple as it gets...
str->write( ezis, sizeof( char ), 4 );
str->write( data, sizeof( char ), size );
return size;
}
/* -----------------------------------------------
unstores a stream of generic data (8bit)
----------------------------------------------- */
INTERN inline int pmp_unstore_data( iostream* str, unsigned char** data )
{
unsigned char ezis[4]; // little endian size
int size = 0; // integer size
// unstore little endian size
str->read( ezis, sizeof( char ), 4 );
size |= ezis[0] << 24;
size |= ezis[1] << 16;
size |= ezis[2] << 8;
size |= ezis[3] << 0;
// alloc memory for data
*data = (unsigned char*) calloc( size, sizeof( char ) );
if ( *data == NULL ) {
sprintf( errormessage, MEM_ERRMSG );
errorlevel = 2;
return -1;
}
// read data to memory
str->read( *data, sizeof( char ), size );
// check for eof trouble
if ( str->chkeof() ) {
sprintf( errormessage, "unexpected end of data" );
errorlevel = 2;
return -1;
}
return size;
}
#endif
/* -----------------------------------------------
encode the padding bit (1 bit)
----------------------------------------------- */
INTERN inline bool pmp_encode_padding( aricoder* enc )
{
// padding bit:
// - no correlation with others at all
// - always comes in perfectly predictable run/length pairs
// -> use run length encoding and arithmetic compression
// (can still be improved upon)
model_s* model;
int run = 0;
char bit = 0;
// encoding start
model = INIT_MODEL_S( PADDING_MAX_RUN+1, PADDING_MAX_RUN+1, 1 );
for ( mp3Frame* frame = firstframe; frame != NULL; frame = frame->next ) {
if ( frame->padding != bit ) {
bit ^= 0x1;
while ( run >= PADDING_MAX_RUN+1 ) {
encode_ari( enc, model, PADDING_MAX_RUN );
model->shift_context( PADDING_MAX_RUN );
encode_ari( enc, model, 0 );
model->shift_context( 0 );
run -= PADDING_MAX_RUN;
}
encode_ari( enc, model, run );
model->shift_context( run );
run = 0;
}
run++;
}
// encode last run
if ( run > 0 ) {
while ( run >= PADDING_MAX_RUN+1 ) {
encode_ari( enc, model, PADDING_MAX_RUN );
model->shift_context( PADDING_MAX_RUN );
encode_ari( enc, model, 0 );
model->shift_context( 0 );
run -= PADDING_MAX_RUN;
}
encode_ari( enc, model, run );
}
// done, delete model
delete( model );
return true;
}
/* -----------------------------------------------
decode the padding bit (1 bit)
----------------------------------------------- */
INTERN inline bool pmp_decode_padding( aricoder* dec )
{
model_s* model;
int run = 0;
char bit = 0;
// decoding start
model = INIT_MODEL_S( PADDING_MAX_RUN+1, PADDING_MAX_RUN+1, 1 );
// decode the first run
run = decode_ari( dec, model );
model->shift_context( run );
// ... and all others
for ( mp3Frame* frame = firstframe; frame != NULL; frame = frame->next ) {
while ( run == 0 ) {
bit ^= 0x1;
run = decode_ari( dec, model );
model->shift_context( run );
}
run--;
frame->padding = bit;
}
// done, delete model
delete( model );
return true;
}
/* -----------------------------------------------
encode block types (1-3 bit)
----------------------------------------------- */
INTERN inline bool pmp_encode_block_types( aricoder* enc )
{
// block types:
// - consist of two things: switching bit and block type spec
// - safe for the first few frames, block type spec is predictable
// - switching occurs periodically
// - ch1 block types are often identical to ch0 block types
// -> use run length encoding for switching and predict specs
granuleInfo* granule;
model_s* mod_sw;
model_s* mod_bt;
int run = 0;
char bit = 0;
int ctx;
int c;
// init models
mod_sw = INIT_MODEL_S( SWITCHING_MAX_RUN+1, SWITCHING_MAX_RUN+1, 1 );
mod_bt = INIT_MODEL_S( 4, 4, 1 );
// encoding start
for ( int ch = 0; ch < g_nchannels; ch++ ) {
if ( ( i_sb_diff == 0 ) && ( ch == 1 ) ) break;
mod_sw->shift_context( 0 ); run = 0;
// first step - encode the switching runs
for ( bit = 0, granule = firstframe->granules[ch][0]; granule != NULL; granule = granule->next ) {
// encode the switching run
if ( granule->window_switching != bit ) {
bit ^= 0x1;
while ( run >= SWITCHING_MAX_RUN+1 ) {
encode_ari( enc, mod_sw, SWITCHING_MAX_RUN );
mod_sw->shift_context( SWITCHING_MAX_RUN );
encode_ari( enc, mod_sw, 0 );
mod_sw->shift_context( 0 );
run -= SWITCHING_MAX_RUN;
}
encode_ari( enc, mod_sw, run );
mod_sw->shift_context( run );
run = 0;
}
run++;
}
if ( run > 0 ) { // encode last run
while ( run >= SWITCHING_MAX_RUN+1 ) {
encode_ari( enc, mod_sw, SWITCHING_MAX_RUN );
mod_sw->shift_context( SWITCHING_MAX_RUN );
encode_ari( enc, mod_sw, 0 );
mod_sw->shift_context( 0 );
run -= SWITCHING_MAX_RUN;
}
encode_ari( enc, mod_sw, run );
mod_sw->shift_context( run );
}
// second step - encode the block types
c = STOP_BLOCK;
for ( granule = firstframe->granules[ch][0]; granule != NULL; granule = granule->next ) {
if ( granule->window_switching ) {
if ( c == STOP_BLOCK ) ctx = START_BLOCK;
else if ( granule->next != NULL )
ctx = ( granule->next->window_switching ) ? SHORT_BLOCK : STOP_BLOCK;
else ctx = STOP_BLOCK;
mod_bt->shift_context( ctx );
c = granule->block_type;
encode_ari( enc, mod_bt, c );
}
}
}
// done, delete models
delete( mod_sw );
delete( mod_bt );
return true;
}
/* -----------------------------------------------
decode block types (1-3 bit)
----------------------------------------------- */
INTERN inline bool pmp_decode_block_types( aricoder* dec )
{
granuleInfo* granule0;
granuleInfo* granule1;
model_s* mod_sw;
model_s* mod_bt;
char bit = 0;
int run;
int ctx;
int c;
// init models
mod_sw = INIT_MODEL_S( SWITCHING_MAX_RUN+1, SWITCHING_MAX_RUN+1, 1 );
mod_bt = INIT_MODEL_S( 4, 4, 1 );
// decoding start
for ( int ch = 0; ch < g_nchannels; ch++ ) {
if ( ( i_sb_diff == 0 ) && ( ch == 1 ) ) break;
mod_sw->shift_context( 0 );
// first step - decode the switching runs
// decode the first run
run = decode_ari( dec, mod_sw );
mod_sw->shift_context( run );
// ... and all others
for ( bit = 0, granule0 = firstframe->granules[ch][0]; granule0 != NULL; granule0 = granule0->next ) {
while ( run == 0 ) {
bit ^= 0x1;
run = decode_ari( dec, mod_sw );
mod_sw->shift_context( run );
}
run--;
granule0->window_switching = bit;
}
// second step - decode the block types
c = STOP_BLOCK;
for ( granule0 = firstframe->granules[ch][0]; granule0 != NULL; granule0 = granule0->next ) {
if ( granule0->window_switching ) {
if ( c == STOP_BLOCK ) ctx = START_BLOCK;
else if ( granule0->next != NULL )
ctx = ( granule0->next->window_switching ) ? SHORT_BLOCK : STOP_BLOCK;
else ctx = STOP_BLOCK;
mod_bt->shift_context( ctx );
c = decode_ari( dec, mod_bt );
granule0->block_type = c;
} else granule0->block_type = LONG_BLOCK;
}
}
// done, delete model
delete( mod_sw );
delete( mod_bt );
// copy ch1 block types to ch0 if identical
if ( ( i_sb_diff == 0 ) && ( g_nchannels == 2 ) ) {
granule1 = firstframe->granules[1][0];
for ( granule0 = firstframe->granules[0][0]; granule0 != NULL; granule0 = granule0->next ) {
granule1->window_switching = granule0->window_switching;
granule1->block_type = granule0->block_type;
granule1 = granule1->next;
}
}
return true;
}
/* -----------------------------------------------
encode the global gain (8 bit)
----------------------------------------------- */
INTERN inline bool pmp_encode_global_gain( aricoder* enc )
{
// global gain:
// - high correlation with everything else
// - high noise
// - difficult to predict from each other
// -> encode using differential coding and 0th order model
// this will serve as context for everything else
granuleInfo* granule0;
granuleInfo* granule1;
model_s* model;
int last = 0;
int c = 0;
// set up model (will use one model for both channels)
model = INIT_MODEL_S( 256, 0, 0 );
// --- first channel ---
// model->shift_context( 0 );
for ( granule0 = firstframe->granules[0][0]; granule0 != NULL; granule0 = granule0->next ) {
c = ( granule0->global_gain - last ) & 0xFF;
last = granule0->global_gain;
encode_ari( enc, model, c );
}
// --- second channel ---
if ( g_nchannels == 2 ) {
// model->shift_context( 0 );
granule0 = firstframe->granules[0][0];
for ( granule1 = firstframe->granules[1][0]; granule1 != NULL; granule1 = granule1->next ) {
c = ( granule1->global_gain - granule0->global_gain ) & 0xFF;
granule0 = granule0->next;
encode_ari( enc, model, c );
}
}
// done, delete model
delete( model );
return true;
}
/* -----------------------------------------------
decode the global gain (8 bit)
----------------------------------------------- */
INTERN inline bool pmp_decode_global_gain( aricoder* dec )
{
granuleInfo* granule0;
granuleInfo* granule1;
model_s* model;
int last = 0;
int c = 0;
// set up model (will use one model for both channels)
model = INIT_MODEL_S( 256, 0, 0 );
// --- first channel ---
// model->shift_context( 0 );
for ( granule0 = firstframe->granules[0][0]; granule0 != NULL; granule0 = granule0->next ) {
c = decode_ari( dec, model );
// model->shift_context( c );
last = ( c + last ) & 0xFF;
granule0->global_gain = last;
}
// --- second channel ---
if ( g_nchannels == 2 ) {
// model->shift_context( 0 );
granule0 = firstframe->granules[0][0];
for ( granule1 = firstframe->granules[1][0]; granule1 != NULL; granule1 = granule1->next ) {
c = decode_ari( dec, model );
// model->shift_context( c );
granule1->global_gain = ( c + granule0->global_gain ) & 0xFF;
granule0 = granule0->next;
}
}
// done, delete model
delete( model );
return true;
}
/* -----------------------------------------------
encode the slength (4 bit)
----------------------------------------------- */
INTERN inline bool pmp_encode_slength( aricoder* enc )
{
// slength:
// - high correlation with global gain
// - different correlation for ch0 and ch1
// - predictable from each other to some degree
// -> encode using gg_context and markov model
unsigned char* gg_ctx;
granuleInfo* granule;
model_s* model;
int ch;
int c;
// set up model (one model for both channels, flush in between)
model = INIT_MODEL_S( 16, (GG_CONTEXT_SIZE > 16 ) ? GG_CONTEXT_SIZE : 16, 2 );
// encoding start
for ( ch = 0; ch < g_nchannels; ch++ ) {
// reset and flush model
model->flush_model( 1 );
gg_ctx = gg_context[ ch ]; c = 0;
// encode one channel
for ( granule = firstframe->granules[ch][0]; granule != NULL; granule = granule->next ) {
shift_model( model, *(gg_ctx++), c );
c = granule->slength;
encode_ari( enc, model, c );
}
}
// done, delete model
delete( model );
return true;
}
/* -----------------------------------------------
decode the slength (4 bit)
----------------------------------------------- */
INTERN inline bool pmp_decode_slength( aricoder* dec )
{
unsigned char* gg_ctx;
granuleInfo* granule;
model_s* model;
int ch;
int c;
// set up model (one model for both channels, flush in between)
model = INIT_MODEL_S( 16, (GG_CONTEXT_SIZE > 16 ) ? GG_CONTEXT_SIZE : 16, 2 );
// decoding start
for ( ch = 0; ch < g_nchannels; ch++ ) {
// reset and flush model
model->flush_model( 1 );
gg_ctx = gg_context[ ch ]; c = 0;
// decode one channel
for ( granule = firstframe->granules[ch][0]; granule != NULL; granule = granule->next ) {
shift_model( model, *(gg_ctx++), c );
c = decode_ari( dec, model );
granule->slength = c;
}
}
// done, delete model
delete( model );
return true;
}
/* -----------------------------------------------
encode ms stereo setting (1 bit)
----------------------------------------------- */
INTERN inline bool pmp_encode_stereo_ms( aricoder* enc )
{
// stereo ms bit:
// - only occurs in joint stereo mode
// - predictable from each other to some degree
// -> encode using markov model
model_b* model;
unsigned char ctx = 0;
// encoding start
model = INIT_MODEL_B( 16, 1 );
for ( mp3Frame* frame = firstframe; frame != NULL; frame = frame->next ) {
model->shift_context( ctx );
encode_ari( enc, model, frame->stereo_ms );
ctx = ( ( ctx << 1 ) | frame->stereo_ms ) & 0xF;
}
// done, delete model
delete( model );
return true;
}
/* -----------------------------------------------
decode ms stereo setting (1 bit)
----------------------------------------------- */
INTERN inline bool pmp_decode_stereo_ms( aricoder* dec )
{
model_b* model;
unsigned char ctx = 0;
// decoding start
model = INIT_MODEL_B( 16, 1 );
for ( mp3Frame* frame = firstframe; frame != NULL; frame = frame->next ) {
model->shift_context( ctx );
frame->stereo_ms = decode_ari( dec, model );
ctx = ( ( ctx << 1 ) | frame->stereo_ms ) & 0xF;
}
// done, delete model
delete( model );
return true;
}
/* -----------------------------------------------
encode region bounds and tables (32 bit)
----------------------------------------------- */
INTERN inline bool pmp_encode_region_data( aricoder* enc )
{
// region bounds:
// - little correlation with global gain
// - high correlation among each others
// - no good context for bv bounds at all
// - sv bounds have to be processed elsewhere
// -> different treatment for r0, r1 and bv:
// -> r0: markov model and bv context
// -> r1: r0 and bv context
// -> bv: block type context
//
// huffman table selection:
// - high correlation with global gain
// - high correlation between neighbors and tables
// -> encode using gg_context and markov model
// -> each region gets its own model
const int* bw_conv = mp3_bandwidth_conv[ (int) i_samplerate ];
unsigned char* gg_ctx;
granuleInfo* granule;
model_s* mod_t0;
model_s* mod_t1;
model_s* mod_t2;
model_b* mod_ts;
model_s* mod_s0;
model_s* mod_s1;
model_s* mod_bv;
int t_r0, t_r1, t_r2, t_sv;
int s_r0, s_r2;
// int s_r0, s_r1, s_r2;
int ctx_sv;
int ch;
// set up models (one for each region table and size)
mod_t0 = INIT_MODEL_S( 32, (GG_CONTEXT_SIZE > 32 ) ? GG_CONTEXT_SIZE : 32, 2 );
mod_t1 = INIT_MODEL_S( 32, 32, 2 );
mod_t2 = INIT_MODEL_S( 32, 32, 2 );
mod_ts = INIT_MODEL_B( 16, 1 );
mod_s0 = INIT_MODEL_S( 16, 22, 2 );
mod_s1 = INIT_MODEL_S( 8, 22, 2 );
mod_bv = INIT_MODEL_S( (576/2)+1, 1+1, 1 );
// encoding start
for ( ch = 0; ch < g_nchannels; ch++ ) {
// reset and flush models
mod_s0->flush_model( 1 );
mod_s1->flush_model( 1 );
mod_bv->flush_model( 1 );
// reset context
gg_ctx = gg_context[ ch ]; ctx_sv = 0;
t_r0 = 0; t_r1 = 0; t_r2 = 0; t_sv = 0;
// s_r0 = 0; s_r1 = 0; s_r2 = 0;
s_r0 = 0; s_r2 = 0;
// encode all region bounds and tables for one channel
for ( granule = firstframe->granules[ch][0]; granule != NULL; granule = granule->next ) {
// --- region bounds (big_val_pairs, region0_size, region2_size) ---
if ( granule->big_val_pairs > 576 / 2 ) {
sprintf( errormessage, "big value pairs out of bounds (%ch>%ch)", granule->big_val_pairs, 576 / 2 );
errorlevel = 2;
return false;
}
if ( granule->window_switching ) {
// region 2 size (# big value pairs)
mod_bv->shift_context( ( granule->block_type == SHORT_BLOCK ) ? 1 : 0 );
encode_ari( enc, mod_bv, granule->big_val_pairs );
s_r0 = 0; // s_r1 = 0;
} else {
s_r2 = bw_conv[ granule->big_val_pairs << 1 ];
// region 2 size (# big value pairs)
mod_bv->shift_context( 0 );
encode_ari( enc, mod_bv, granule->big_val_pairs );
// region 0 size
shift_model( mod_s0, s_r0, s_r2 );
s_r0 = granule->region0_size;
encode_ari( enc, mod_s0, s_r0 );
// region 1 size
shift_model( mod_s1, s_r0, s_r2 );
encode_ari( enc, mod_s1, granule->region1_size );
// context customizations
s_r0++; // s_r1 = s_r0 + granule->region1_size + 1;
}
// --- region tables (region0/1/2_table, select_htabB) ---
// region 0 table
shift_model( mod_t0, *gg_ctx, t_r0 );
// shift_model( mod_t0, s_r0, t_r0 );
t_r0 = granule->region_table[0];
encode_ari( enc, mod_t0, t_r0 );
// region 1 table
shift_model( mod_t1, t_r0, s_r0 );
t_r1 = granule->region_table[1];
encode_ari( enc, mod_t1, t_r1 );
// region 2 table
if ( !granule->window_switching ) {
shift_model( mod_t2, t_r0, t_r1 );
t_r2 = granule->region_table[2];
encode_ari( enc, mod_t2, t_r2 );
}
// small values table
mod_ts->shift_context( ctx_sv );
t_sv = granule->select_htabB;
encode_ari( enc, mod_ts, t_sv );
ctx_sv = ( ( ctx_sv << 1 ) | t_sv ) & 0xF;
// advance context
gg_ctx++;
}
}
// done, delete models
delete( mod_t0 );
delete( mod_t1 );
delete( mod_t2 );
delete( mod_ts );
delete( mod_s0 );
delete( mod_s1 );
delete( mod_bv );
return true;
}
/* -----------------------------------------------
decode region sizes and table selection (32 bit)
----------------------------------------------- */
INTERN inline bool pmp_decode_region_data( aricoder* dec )
{
const int* bw_conv = mp3_bandwidth_conv[ (int) i_samplerate ];
unsigned char* gg_ctx;
granuleInfo* granule;
model_s* mod_t0;
model_s* mod_t1;
model_s* mod_t2;
model_b* mod_ts;
model_s* mod_s0;
model_s* mod_s1;
model_s* mod_bv;
int t_r0, t_r1, t_r2, t_sv;
int s_r0, s_r2;
// int s_r0, s_r1, s_r2;
int ctx_sv;
int ch;
// set up models (one for each region table and size)
mod_t0 = INIT_MODEL_S( 32, (GG_CONTEXT_SIZE > 32 ) ? GG_CONTEXT_SIZE : 32, 2 );
mod_t1 = INIT_MODEL_S( 32, 32, 2 );
mod_t2 = INIT_MODEL_S( 32, 32, 2 );
mod_ts = INIT_MODEL_B( 16, 1 );
mod_s0 = INIT_MODEL_S( 16, 22, 2 );
mod_s1 = INIT_MODEL_S( 8, 22, 2 );
mod_bv = INIT_MODEL_S( (576/2)+1, 1+1, 1 );
// decoding start
for ( ch = 0; ch < g_nchannels; ch++ ) {
// reset and flush model
mod_s0->flush_model( 1 );
mod_s1->flush_model( 1 );
mod_bv->flush_model( 1 );
// reset context
gg_ctx = gg_context[ ch ]; ctx_sv = 0;
t_r0 = 0; t_r1 = 0; t_r2 = 0; t_sv = 0;
// s_r0 = 0; s_r1 = 0; s_r2 = 0;
s_r0 = 0; s_r2 = 0;
// decode all region tables for one channel
for ( granule = firstframe->granules[ch][0]; granule != NULL; granule = granule->next ) {
// --- region bounds (big_val_pairs, region0_size, region2_size) ---
if ( !granule->window_switching ) {
// region 2
mod_bv->shift_context( 0 );
granule->big_val_pairs = decode_ari( dec, mod_bv );
s_r2 = bw_conv[ granule->big_val_pairs << 1 ];
// region 0
shift_model( mod_s0, s_r0, s_r2 );
s_r0 = decode_ari( dec, mod_s0 );
granule->region0_size = s_r0;
// region 1
shift_model( mod_s1, s_r0, s_r2 );
granule->region1_size = decode_ari( dec, mod_s1 );
// set region 1/2 bounds
granule->region_bound[0] =
mp3_bandwidth_bounds[(int)i_samplerate][s_r0+1];
granule->region_bound[1] =
mp3_bandwidth_bounds[(int)i_samplerate][s_r0+granule->region1_size+2];
// set bv bound and check other bounds
granule->region_bound[2] = granule->big_val_pairs << 1;
if ( granule->region_bound[0] > granule->region_bound[2] ) {
granule->region_bound[0] = granule->region_bound[2];
granule->region_bound[1] = granule->region_bound[2];
} else if ( granule->region_bound[1] > granule->region_bound[2] )
granule->region_bound[1] = granule->region_bound[2];
// context customizations
s_r0++; // s_r1 = s_r0 + granule->region1_size + 1;
} else if ( granule->block_type != SHORT_BLOCK ) {
// region sizes for different block types
granule->region0_size = 8;
granule->region1_size = 0;
// set region bound
granule->region_bound[0] =
mp3_bandwidth_bounds[(int)i_samplerate][8];
// decode bv pairs, set bound and check r0 bound
mod_bv->shift_context( 0 );
granule->big_val_pairs = decode_ari( dec, mod_bv );
granule->region_bound[1] = granule->big_val_pairs << 1;
if ( granule->region_bound[0] > granule->region_bound[1] )
granule->region_bound[0] = granule->region_bound[1];
granule->region_bound[2] = granule->region_bound[1];
// context setting
s_r0 = 0; // s_r1 = 0;
} else { // special treatment for mixed blocks needed (!)
granule->region0_size = 9;
granule->region1_size = 0;
// set region bound
granule->region_bound[0] =
mp3_bandwidth_bounds_short[(int)i_samplerate][9/3] * 3;
// decode bv pairs, set bound and check r0 bound
mod_bv->shift_context( 1 );
granule->big_val_pairs = decode_ari( dec, mod_bv );
granule->region_bound[1] = granule->big_val_pairs << 1;
if ( granule->region_bound[0] > granule->region_bound[1] )
granule->region_bound[0] = granule->region_bound[1];
granule->region_bound[2] = granule->region_bound[1];
// context setting
s_r0 = 0; // s_r1 = 0;
}
// --- region tables (region0/1/2_table, select_htabB) ---
// region 0 table
shift_model( mod_t0, *gg_ctx, t_r0 );
t_r0 = decode_ari( dec, mod_t0 );
granule->region_table[0] = t_r0;
// region 1 table
shift_model( mod_t1, t_r0, s_r0 );
t_r1 = decode_ari( dec, mod_t1 );
granule->region_table[1] = t_r1;
// region 2 table
if ( !granule->window_switching ) {
shift_model( mod_t2, t_r0, t_r1 );
t_r2 = decode_ari( dec, mod_t2 );
granule->region_table[2] = t_r2;
} else granule->region_table[2] = 0;
// small values table
mod_ts->shift_context( ctx_sv );
t_sv = decode_ari( dec, mod_ts );
granule->select_htabB = t_sv;
ctx_sv = ( ( ctx_sv << 1 ) | t_sv ) & 0xF;
// advance context
gg_ctx++;
}
}
// done, delete models
delete( mod_t0 );
delete( mod_t1 );
delete( mod_t2 );
delete( mod_ts );
delete( mod_s0 );
delete( mod_s1 );
delete( mod_bv );
return true;
}
/* -----------------------------------------------
encode scalefactor sharing (2/4 bit)
----------------------------------------------- */
INTERN inline bool pmp_encode_sharing( aricoder* enc )
{
// scalefactor sharing:
// - comes in packs of four
// - only every second granule matters
// - high correlation with slength
// - high correlation among fourpacks
// -> encode using markov model + slength context
granuleInfo* granule;
model_s* model;
int ch;
int c;
// set up model (one model for both channels, flush in between)
model = INIT_MODEL_S( 16, 16, 3 );
// encoding start
for ( ch = 0; ch < g_nchannels; ch++ ) {
// reset and flush model
model->flush_model( 1 );
c = 0;
// encode one channel
for ( granule = firstframe->granules[ch][0]; granule != NULL; granule = granule->next->next ) {
shift_model( model, c, granule->slength, granule->next->slength );
c = granule->share;
encode_ari( enc, model, c );
}
}
// done, delete model
delete( model );
return true;
}
/* -----------------------------------------------
decode scalefactor sharing (2/4 bit)
----------------------------------------------- */
INTERN inline bool pmp_decode_sharing( aricoder* dec )
{
granuleInfo* granule;
model_s* model;
int ch;
int c;
// set up model (one model for both channels, flush in between)
model = INIT_MODEL_S( 16, 16, 3 );
// decoding start
for ( ch = 0; ch < g_nchannels; ch++ ) {
// reset and flush model
model->flush_model( 1 );
c = 0;
// decode one channel
for ( granule = firstframe->granules[ch][0]; granule != NULL; granule = granule->next->next ) {
shift_model( model, c, granule->slength, granule->next->slength );
c = decode_ari( dec, model );
granule->share = c;
}
}
// done, delete model
delete( model );
return true;
}
/* -----------------------------------------------
encode the preemphasis setting (1 bit)
----------------------------------------------- */
INTERN inline bool pmp_encode_preemphasis( aricoder* enc )
{
// preemphasis:
// - little correlation with global gain
// - near impossible to predict from each other
// - high differences between ch0 and ch1 (joint)
// -> encode using simple markov model
// -> improve later
granuleInfo* granule;
model_b* model;
int ctx;
int ch;
int c;
// set up model (one model for both channels, flush in between)
model = INIT_MODEL_B( 16, 1 );
// encoding start
for ( ch = 0; ch < g_nchannels; ch++ ) {
// reset and flush model
model->flush_model( 1 );
ctx = 0;
// encode one channel
for ( granule = firstframe->granules[ch][0]; granule != NULL; granule = granule->next ) {
model->shift_context( ctx );
c = granule->preemphasis;
encode_ari( enc, model, c );
ctx = ( ( ctx << 1 ) | c ) & 0xF;
}
}
// done, delete model
delete( model );
return true;
}
/* -----------------------------------------------
decode the preemphasis setting (1 bit)
----------------------------------------------- */
INTERN inline bool pmp_decode_preemphasis( aricoder* dec )
{
granuleInfo* granule;
model_b* model;
int ctx;
int ch;
int c;
// set up model (one model for both channels, flush in between)
model = INIT_MODEL_B( 16, 1 );
// decoding start
for ( ch = 0; ch < g_nchannels; ch++ ) {
// reset and flush model
model->flush_model( 1 );
ctx = 0;
// decode one channel
for ( granule = firstframe->granules[ch][0]; granule != NULL; granule = granule->next ) {
model->shift_context( ctx );
c = decode_ari( dec, model );
granule->preemphasis = c;
ctx = ( ( ctx << 1 ) | c ) & 0xF;
}
}
// done, delete model
delete( model );
return true;
}
/* -----------------------------------------------
encode the coarse sf setting (1 bit)
----------------------------------------------- */
INTERN inline bool pmp_encode_coarse_sf( aricoder* enc )
{
// coarse scalefactors
// - little correlation with global gain
// - near impossible to predict from each other
// - differences between ch0 and ch1 (joint)
// -> encode using simple markov model
// -> improve later
granuleInfo* granule;
model_b* model;
int ctx;
int ch;
int c;
// set up model (one model for both channels, flush in between)
model = INIT_MODEL_B( 16, 1 );
// encoding start
for ( ch = 0; ch < g_nchannels; ch++ ) {
// reset and flush model
model->flush_model( 1 );
ctx = 0;
// encode one channel
for ( granule = firstframe->granules[ch][0]; granule != NULL; granule = granule->next ) {
model->shift_context( ctx );
c = granule->coarse_scalefactors;
encode_ari( enc, model, c );
ctx = ( ( ctx << 1 ) | c ) & 0xF;
}
}
// done, delete model
delete( model );
return true;
}
/* -----------------------------------------------
decode the coarse sf setting (1 bit)
----------------------------------------------- */
INTERN inline bool pmp_decode_coarse_sf( aricoder* dec )
{
granuleInfo* granule;
model_b* model;
int ctx;
int ch;
int c;
// set up model (one model for both channels, flush in between)
model = INIT_MODEL_B( 16, 1 );
// decoding start
for ( ch = 0; ch < g_nchannels; ch++ ) {
// reset and flush model
model->flush_model( 1 );
ctx = 0;
// decode one channel
for ( granule = firstframe->granules[ch][0]; granule != NULL; granule = granule->next ) {
model->shift_context( ctx );
c = decode_ari( dec, model );
granule->coarse_scalefactors = c;
ctx = ( ( ctx << 1 ) | c ) & 0xF;
}
}
// done, delete model
delete( model );
return true;
}
/* -----------------------------------------------
encode the subblock gain (9 bit)
----------------------------------------------- */
INTERN inline bool pmp_encode_subblock_gain( aricoder* enc )
{
// subblock gain:
// - only occurs for short blocks
// - little correlation with each other
// - no visible correlation with anything else
// -> use simple markov model
granuleInfo* granule;
model_s* model;
int ch, sb;
// set up model (one model for both channels, flush in between)
model = INIT_MODEL_S( 8, 8, 1 );
// decoding start
model->shift_context( 0 );
for ( ch = 0; ch < g_nchannels; ch++ ) {
// encode one channel
for ( granule = firstframe->granules[ch][0]; granule != NULL; granule = granule->next ) {
if ( granule->window_switching ) {
for ( sb = 0; sb < 3; sb++ ) {
encode_ari( enc, model, granule->sb_gain[sb] );
model->shift_context( granule->sb_gain[sb] );
}
}
}
}
// done, delete model
delete( model );
return true;
}
/* -----------------------------------------------
decode the subblock gain (9 bit)
----------------------------------------------- */
INTERN inline bool pmp_decode_subblock_gain( aricoder* dec )
{
granuleInfo* granule;
model_s* model;
int ch, sb;
// set up model (one model for both channels, flush in between)
model = INIT_MODEL_S( 8, 8, 1 );
// decoding start
model->shift_context( 0 );
for ( ch = 0; ch < g_nchannels; ch++ ) {
// decode one channel
for ( granule = firstframe->granules[ch][0]; granule != NULL; granule = granule->next ) {
if ( granule->window_switching ) {
for ( sb = 0; sb < 3; sb++ ) {
granule->sb_gain[sb] = decode_ari( dec, model );
model->shift_context( granule->sb_gain[sb] );
}
} else memset( granule->sb_gain, 0, sizeof( char ) * 3 );
}
}
// done, delete model
delete( model );
return true;
}
/* -----------------------------------------------
encode the main data
----------------------------------------------- */
INTERN inline bool pmp_encode_main_data( aricoder* enc )
{
// main data:
// consists of scalefactors & coefficients
// several other data is also encoded here:
// - sv bound (for performance reasons)
// - aux data, aux data size & padding bits
// - reconstruction information (stuffing bits)
// - bitrate (for performance reasons)
//
// scalefactors:
// correlation with temporal and local neighbours
// -> encode with neighborhood context
//
// coefficients:
// good routines are already included in mp3 standard,
// but no temporal context is used there
// -> remodel mp3 coding with arithmetic coding
// -> use neighborhood context
//
// stuffing bits (between granules):
// this data either follows a pattern or is garbage
// -> use 4 bit prev bits context
//
// padding bits/aux data (at end of frame):
// usually follows a specific pattern
// -> try prediction
// -> use 8 bit prev bits context
//
// sv bound:
// should be predictable from neighborhood
// and bv bound
// -> encode diff with bv bound
// -> use prev and bv bound context
//
// bitrate:
// high correlation with main size
// -> encode using main size prediction as context
// context / storage
static unsigned char* pad_and_aux= ( unsigned char* ) calloc( 2048, 1 ); // !!! (length)
mp3Frame* frame;
granuleInfo* granule;
unsigned char* scf_c[2];
unsigned char* scf_l_long[2];
unsigned char* scf_l_short[2];
unsigned char* abs_c[2];
unsigned char* sgn_c[2];
unsigned char* len_c[2];
unsigned short* lbt_c[2];
unsigned char* absl_ctx_h[2];
unsigned char* abss_ctx_h[2];
unsigned char* sgnl_ctx_h[2];
unsigned char* sgns_ctx_h[2];
unsigned char* lenl_ctx_h[2];
unsigned char* lens_ctx_h[2];
unsigned char* scf_prev;
unsigned char* ctx_h_abs;
unsigned char* ctx_h_sgn;
unsigned char* ctx_h_len;
unsigned char* scf;
unsigned char* abs;
unsigned char* sgn;
unsigned char* len;
unsigned short* lbt;
unsigned char* swap;
unsigned char* pna_c;
unsigned char ctx_scf;
unsigned char ctx_abs;
unsigned char ctx_pat;
unsigned char ctx_svb[2] = { 0, 0 };
unsigned char ctx_nst = 0;
unsigned char ctx_bst = 0;
unsigned char ctx_aux = 0;
unsigned char ctx_pad = 0;
// statistical models
model_s* mod_scf[2][4][10]; // scalefactors
model_s* mod_abv[2][8][32]; // absolutes big values <= 15
model_b* mod_asv[2][8][2]; // absolulte small values
model_b* mod_sgn[2][8]; // signs
model_s* mod_len[2][14]; // residual bitlengths
model_b* mod_res; // residual bits
model_s* mod_svb; // small values bound
model_s* mod_bvf; // fix for sv bound (usually none)
model_s* mod_nst; // number of stuffing bits (usually zero)
model_b* mod_bst; // stuffing bits
model_b* mod_pad; // padding and ancillary bits
model_b* mod_pap; // predcition for p&a bits
model_s* mod_aux; // byte size of ancillary data
model_s* mod_btr; // frame bitrate
model_s* mod_abc; // current absolute big values (shortcut)
model_b* mod_asc; // current absolute small values (shortcut)
model_b* mod_sgc; // current signs (shortcut)
model_s* mod_lnc; // current bitlengths (shortcut)
model_s* mod_sfc; // current scalefactors (shortcut)
// lookup tables
const int* bitrate_pred = mp3_bitrate_pred[(int)i_samplerate];
// huffman decoder
huffman_reader* huffman;
// mp3 decoding settings
short* region_bounds;
char* region_tables;
huffman_dec_table* bv_table;
huffman_conv_set* conv_set;
int linbits;
const int* slen;
int rlb, sl;
// general settings
int bitp = 0;
int bitc = 0;
int lmaxp = 0;
int sbl = 0;
int flags = 0;
bool j_coding;
bool bad_coding = false;
char shared[2][4];
// counters
int ch, gr;
int p, g, r;
int i, c, n;
// --- PRE-PROCESSING PREPARATIONS: INIT STATISTICAL MODELS/HUFFMAN DECODER/CONTEXT ---
// decide if joint context will be used (will be used for joint & standard stereo)
j_coding = ( i_channels == MP3_STEREO ) || ( i_channels == MP3_JOINT_STEREO );
// init huffman decoder/reader
huffman = new huffman_reader( main_data, main_data_size );
// reset ancillary predictor
pmp_predict_lame_anc( -1, NULL );
// init statistical models
mod_svb = INIT_MODEL_S( ( 576 / 4 ) + 1 + 1, ( 576 / 4 ) + 1 + 1, 1 );
mod_bvf = INIT_MODEL_S( ( 576 / 2 ) + 1, 0, 0 );
mod_nst = INIT_MODEL_S( STUFFING_STEP + 1, STUFFING_STEP + 1, 1 );
mod_bst = INIT_MODEL_B( 16, 1 );
mod_pad = INIT_MODEL_B( 256, 1 );
mod_pap = INIT_MODEL_B( 0, 0 );
mod_aux = INIT_MODEL_S( AUX_DATA_STEP + 1, AUX_DATA_STEP + 1, 1 );
mod_btr = INIT_MODEL_S( 16, 16, 1 );
mod_res = INIT_MODEL_B( 13 + 1, 2 );
for ( ch = 0; ch < g_nchannels; ch++ ) {
for ( g = 0; g < 10; g++ ) {
mod_scf[ch][0][g] = INIT_MODEL_S( 2, 16, 2 );
mod_scf[ch][1][g] = INIT_MODEL_S( 4, 16, 2 );
mod_scf[ch][2][g] = INIT_MODEL_S( 8, 16, 2 );
mod_scf[ch][3][g] = INIT_MODEL_S( 16, 16, 2 );
}
for ( flags = 0x0; flags < ( (j_coding&&ch) ? 0x8 : 0x2 ); flags++ ) {
mod_abv[ch][flags][ 0] = NULL;
mod_abv[ch][flags][ 1] = INIT_MODEL_S( 1 + 1, 16, 2 );
mod_abv[ch][flags][ 2] = INIT_MODEL_S( 2 + 1, 16, 2 );
mod_abv[ch][flags][ 3] = INIT_MODEL_S( 2 + 1, 16, 2 );
mod_abv[ch][flags][ 4] = NULL;
mod_abv[ch][flags][ 5] = INIT_MODEL_S( 3 + 1, 16, 2 );
mod_abv[ch][flags][ 6] = INIT_MODEL_S( 3 + 1, 16, 2 );
mod_abv[ch][flags][ 7] = INIT_MODEL_S( 5 + 1, 16, 2 );
mod_abv[ch][flags][ 8] = INIT_MODEL_S( 5 + 1, 16, 2 );
mod_abv[ch][flags][ 9] = INIT_MODEL_S( 5 + 1, 16, 2 );
mod_abv[ch][flags][10] = INIT_MODEL_S( 7 + 1, 16, 2 );
mod_abv[ch][flags][11] = INIT_MODEL_S( 7 + 1, 16, 2 );
mod_abv[ch][flags][12] = INIT_MODEL_S( 7 + 1, 16, 2 );
mod_abv[ch][flags][13] = INIT_MODEL_S( 15 + 1, 16, 2 );
mod_abv[ch][flags][14] = NULL;
mod_abv[ch][flags][15] = INIT_MODEL_S( 15 + 1, 16, 2 );
mod_abv[ch][flags][16] = INIT_MODEL_S( 15 + 1, 16, 2 );
mod_abv[ch][flags][17] = mod_abv[ch][flags][16];
mod_abv[ch][flags][18] = mod_abv[ch][flags][16];
mod_abv[ch][flags][19] = mod_abv[ch][flags][16];
mod_abv[ch][flags][20] = mod_abv[ch][flags][16];
mod_abv[ch][flags][21] = mod_abv[ch][flags][16];
mod_abv[ch][flags][22] = mod_abv[ch][flags][16];
mod_abv[ch][flags][23] = mod_abv[ch][flags][16];
mod_abv[ch][flags][24] = INIT_MODEL_S( 15 + 1, 16, 2 );
mod_abv[ch][flags][25] = mod_abv[ch][flags][24];
mod_abv[ch][flags][26] = mod_abv[ch][flags][24];
mod_abv[ch][flags][27] = mod_abv[ch][flags][24];
mod_abv[ch][flags][28] = mod_abv[ch][flags][24];
mod_abv[ch][flags][29] = mod_abv[ch][flags][24];
mod_abv[ch][flags][30] = mod_abv[ch][flags][24];
mod_abv[ch][flags][31] = mod_abv[ch][flags][24];
mod_asv[ch][flags][0] = INIT_MODEL_B( 16, 2 );
mod_asv[ch][flags][1] = INIT_MODEL_B( 16, 2 );
mod_sgn[ch][flags] = INIT_MODEL_B( 16, 2 );
}
mod_len[ch][0] = NULL;
for ( i = 1; i <= 13; i++ )
mod_len[ch][i] = INIT_MODEL_S( i + 1, 13 + 1, 1 );
}
// --- PRE-PROCESSING PREPARATIONS: ALLOCATION OF MEMORY ---
for ( ch = 0; ch < g_nchannels; ch++ ) {
// alloc memory
scf_c[ch] = ( unsigned char* ) calloc( 21, sizeof( char ) );
scf_l_long[ch] = ( unsigned char* ) calloc( 21, sizeof( char ) );
scf_l_short[ch] = ( unsigned char* ) calloc( 21, sizeof( char ) );
abs_c[ch] = ( unsigned char* ) calloc( 578+1+1, sizeof( char ) );
sgn_c[ch] = ( unsigned char* ) calloc( 578+1+1, sizeof( char ) );
len_c[ch] = ( unsigned char* ) calloc( 578+1+1, sizeof( char ) );
lbt_c[ch] = ( unsigned short* ) calloc( 576, sizeof( short ) );
absl_ctx_h[ch] = ( unsigned char* ) calloc( 578+1+1, sizeof( char ) );
abss_ctx_h[ch] = ( unsigned char* ) calloc( 578+1+1, sizeof( char ) );
sgnl_ctx_h[ch] = ( unsigned char* ) calloc( 578+1+1, sizeof( char ) );
sgns_ctx_h[ch] = ( unsigned char* ) calloc( 578+1+1, sizeof( char ) );
lenl_ctx_h[ch] = ( unsigned char* ) calloc( 578+1+1, sizeof( char ) );
lens_ctx_h[ch] = ( unsigned char* ) calloc( 578+1+1, sizeof( char ) );
// check for problems
if ( ( scf_c[ch] == NULL ) || ( scf_l_long[ch] == NULL ) || ( scf_l_short[ch] == NULL ) ||
( abs_c[ch] == NULL ) || ( absl_ctx_h[ch] == NULL ) || ( abss_ctx_h[ch] == NULL ) ||
( sgn_c[ch] == NULL ) || ( sgnl_ctx_h[ch] == NULL ) || ( sgns_ctx_h[ch] == NULL ) ||
( len_c[ch] == NULL ) || ( lenl_ctx_h[ch] == NULL ) || ( lens_ctx_h[ch] == NULL ) ||
( lbt_c[ch] == NULL ) ) {
sprintf( errormessage, MEM_ERRMSG );
errorlevel = 2;
return false;
}
}
// --- MAIN PROCESSING LOOP: ENCODING AND DECODING ---
for ( frame = firstframe; frame != NULL; frame = frame->next, bitp = 0 ) {
for ( gr = 0; gr < 2; gr++ ) {
for ( ch = 0; ch < g_nchannels; ch++ ) {
// --- MAIN DATA DECODING: PREPARATIONS ---
// initialize shortcuts
granule = frame->granules[ch][gr];
sbl = ( granule->block_type == SHORT_BLOCK ) ? 1 : 0;
lmaxp = granule->main_data_bit;
slen = slength_table[ (int) granule->slength ];
region_bounds = granule->region_bound;
region_tables = granule->region_table;
abs = abs_c[ch] + 1;
sgn = sgn_c[ch] + 1;
len = len_c[ch] + 1;
lbt = lbt_c[ch];
// set compression flags
flags = ( !j_coding || ( ch == 0 ) ) ? sbl :
( sbl << 0 ) | // long (0) or short (1) block
( frame->stereo_ms << 1 ) | // stereo ms on/off
( ( flags ^ sbl ) << 2 ); // ch0/ch1 block type diffs y/n
// store sharing info
if ( gr == 0 ) {
shared[ch][0] = ( granule->share >> 3 ) & 0x1;
shared[ch][1] = ( granule->share >> 2 ) & 0x1;
shared[ch][2] = ( granule->share >> 1 ) & 0x1;
shared[ch][3] = ( granule->share >> 0 ) & 0x1;
}
// reset counter
huffman->reset_counter();
bitc = 0;
bad_coding = false;
// ---> SCALEFACTOR PROCESSING <---
if ( !sbl ) {
// --- SCALEFACTOR READING/ENCODING: LONG BLOCKS ---
scf = scf_c[ch];
scf_prev = scf_l_long[ch];
ctx_scf = 0;
// read/encode 21 scalefactors with/without sharing
for ( g = 0, p = 0; g < 4; g++ ) {
sl = slen[ ( g < 2 ) ? 0 : 1 ];
if ( ( gr ) & ( shared[ch][g] ) ) { // shared
// loop invariant conditions (-funswitch-loops)
memcpy( scf + p, scf_prev + p, scf_width[ g ] );
p = scf_bounds[ g ];
ctx_scf = scf[p-1];
} else if ( sl == 0 ) { // zero slength
memset( scf + p, 0, scf_width[ g ] );
p = scf_bounds[ g ];
ctx_scf = 0;
} else {
mod_sfc = mod_scf[ch][sl-1][(shared[ch][g])?g|4:g];
for ( ; p < scf_bounds[ g ]; p++ ) { // non shared
scf[p] = huffman->read_bits( sl );
shift_model( mod_sfc, ctx_scf, scf_prev[p] );
encode_ari( enc, mod_sfc, scf[p] );
ctx_scf = scf[p];
}
}
}
// --- SCALEFACTORS FINISHED: SWAP DATA ---
swap = scf_c[ch]; scf_c[ch] = scf_l_long[ch]; scf_l_long[ch] = swap;
} else {
// --- SCALEFACTOR READING/ENCODING: SHORT BLOCKS ---
// encode (only non shared)
for ( i = 0; i < 3; i++ ) { // 3 subblocks
scf = scf_c[ch];
scf_prev = scf_l_short[ch];
ctx_scf = 0;
for ( g = 0, p = 0; g < 2; g++ ) { // lo/hi groups
sl = slen[ g ];
if ( sl == 0 ) { // zero slength
memset( scf + p, 0, scf_lh_width_short[ g ] );
p = scf_lh_bounds_short[ g ];
} else {
mod_sfc = mod_scf[ch][sl-1][g|0x8];
for ( ; p < scf_lh_bounds_short[ g ]; p++ ) {
scf[p] = huffman->read_bits( sl );
shift_model( mod_sfc, ctx_scf, scf_prev[p] );
encode_ari( enc, mod_sfc, scf[p] );
ctx_scf = scf[p];
}
}
}
// --- SWAP DATA ---
swap = scf_c[ch]; scf_c[ch] = scf_l_short[ch]; scf_l_short[ch] = swap;
}
}
// sanity check
if ( huffman->get_count() > lmaxp ) { // main data not big enough - no rollback for scfs
sprintf( errormessage, "huffman decoding error (in frame #%i)", frame->n );
errorlevel = 2;
return false;
}
// ---> COEFFICIENTS PROCESSING <---
// --- COEFFICIENT DECODING: BIG VALUES ---
for ( p = 0, r = 0; ( r < 3 ) && ( !bad_coding ); r++ ) {
if ( region_tables[ r ] == 0 ) { // tbl0 skipping
p = region_bounds[ r ];
continue;
}
// set table and linbits
bv_table = bv_dec_table + region_tables[ r ];
if ( bv_table->h == NULL ) { // illegal table?
sprintf( errormessage, "bad huffman table (%i) used (in frame #%i)",
region_tables[ r ], frame->n );
errorlevel = 2;
return false;
}
conv_set = bv_table->h;
linbits = bv_table->linbits;
// decoding with/without linbits
for ( ; p < region_bounds[ r ]; bitc = huffman->get_count() ) {
huffman->decode_pair( conv_set, abs + p );
for ( i = 0; i < 2; i++, p++ ) if ( abs[p] > 0 ) {
if ( linbits > 0 ) if ( abs[p] == 15 ) {
// loop invariant condition (-unswitch-loops)
lbt[p] = huffman->read_bits( linbits );
len[p] = BITLEN8192P(lbt[p]);
}
sgn[p] = huffman->read_bit();
}
if ( huffman->get_count() > lmaxp ) { // bad coding rollback
bad_coding = true;
abs[--p] = 0; sgn[p] = 0; len[p] = 0;
abs[--p] = 0; sgn[p] = 0; len[p] = 0;
huffman->rewind_bits( huffman->get_count() - bitc );
break; // rollback complete
}
}
}
// --- COEFFICIENT DECODING: SMALL VALUES ---
if ( !bad_coding ) {
conv_set = ( granule->select_htabB ) ? &htabB_dec : &htabA_dec;
for ( bitc = huffman->get_count(); p < 576; bitc = huffman->get_count() ) {
if ( bitc == lmaxp ) break;
huffman->decode_quadruple( conv_set, abs + p );
for ( i = 0; i < 4; i++, p++ ) if ( abs[p] > 0 )
sgn[p] = huffman->read_bit();
if ( huffman->get_count() > lmaxp ) { // bad coding rollback
bad_coding = true;
abs[--p] = 0; sgn[p] = 0;
abs[--p] = 0; sgn[p] = 0;
abs[--p] = 0; sgn[p] = 0;
abs[--p] = 0; sgn[p] = 0;
huffman->rewind_bits( huffman->get_count() - bitc );
break; // rollback complete
}
}
}
// --- SETTING AND ENCODING OF SV BOUND ---
granule->sv_bound = p; // set sv_bound
if ( !sbl )
mod_svb->shift_context( ctx_svb[ch] );
else mod_svb->shift_context( 144 + 1 );
if ( p >= granule->region_bound[ 2 ] ) { // for proper files: encode actual sv bound
c = granule->sv_bound >> 2;
encode_ari( enc, mod_svb, c );
if ( !sbl ) ctx_svb[ch] = c;
} else { // for broken files: encode negative diff with r2 bound
encode_ari( enc, mod_svb, 144 + 1 );
c = ( granule->region_bound[ 2 ] - granule->sv_bound ) >> 1;
encode_ari( enc, mod_bvf, c );
// fix bounds
granule->region_bound[ 2 ] = p;
for ( i = 1; i >= 0; i-- ) if ( p < granule->region_bound[i] )
granule->region_bound[i] = p;
else break;
}
// --- COEFFICIENT ENCODING: PREPARATIONS ---
if ( !sbl ) {
ctx_h_abs = absl_ctx_h[ch]+1;
ctx_h_sgn = sgnl_ctx_h[ch]+1;
ctx_h_len = lenl_ctx_h[ch]+1;
} else {
ctx_h_abs = abss_ctx_h[ch]+1;
ctx_h_sgn = sgns_ctx_h[ch]+1;
ctx_h_len = lens_ctx_h[ch]+1;
}
ctx_abs = 0; ctx_pat = 0;
rlb = region_bounds[2];
mod_sgc = mod_sgn[ch][flags];
// --- COEFFICIENT ENCODING: SMALL VALUES ---
mod_asc = mod_asv[ch][flags][(int) granule->select_htabB];
for ( p = granule->sv_bound-1; p >= rlb; p-- ) {
shift_model( mod_asc, ctx_pat, ctx_h_abs[p] );
encode_ari( enc, mod_asc, abs[p] ); // absolutes
ctx_pat = ( (ctx_pat<<1) | abs[p] ) & 0xF;
ctx_abs = ( 2 * abs[p] + ctx_abs + 2 ) / 3;
if ( abs[p] == 1 ) {
shift_model( mod_sgc, ctx_h_abs[p], ctx_h_sgn[p] );
encode_ari( enc, mod_sgc, sgn[p] ); // signs
}
}
// --- COEFFICIENT ENCODING: BIG VALUES ---
for ( r = 2; r >= 0; r-- ) {
rlb = (r==0) ? 0 : region_bounds[ r-1 ];
if ( p < rlb ) continue;
if ( region_tables[ r ] == 0 ) { // tbl0 skipping
memset( abs + rlb, 0, p - rlb );
p = rlb; ctx_abs = 0;
continue;
}
// set table and linbits
bv_table = bv_dec_table + region_tables[ r ];
linbits = bv_table->linbits;
mod_abc = mod_abv[ch][flags][(int) region_tables[ r ]];
mod_lnc = mod_len[ch][linbits];
// encoding with/without linbits
for ( ; p >= rlb; p-- ) {
shift_model( mod_abc, ctx_abs, ctx_h_abs[p] );
encode_ari( enc, mod_abc, abs[p] ); // absolutes
ctx_abs = ( 2 * abs[p] + ctx_abs + 2 ) / 3;
if ( abs[p] > 0 ) {
shift_model( mod_sgc, ctx_h_abs[p], ctx_h_sgn[p] );
encode_ari( enc, mod_sgc, sgn[p] ); // signs
if ( linbits > 0 ) if ( abs[p] == 15 ) {
// loop invariant condition (-funswitch-loops)
mod_lnc->shift_context( ctx_h_len[p] );
encode_ari( enc, mod_lnc, len[p] ); // bitlengths
for ( i = len[p] - 2; i >= 0; i-- ) {
shift_model( mod_res, len[p], i ); // bit residuals
encode_ari( enc, mod_res, BITN( lbt[p], i ) );
}
}
}
}
}
// --- COEFFICIENTS FINISHED: UPDATE CONTEXT ---
p = granule->sv_bound;
if ( p < 578 ) memset( abs + p, 0, 578 - p );
// loop invariant condition
if ( !j_coding || ( ch == 0 ) ) { // !!!
// channel 0 context
for ( i = 578-1; i >= 0; i-- ) {
ctx_h_abs[i] = ( 3 * abs[i] + abs[i-1] + abs[i+1] + 2 * ctx_h_abs[i] + 4 ) / 7;
if ( abs[i] > 0 ) {
ctx_h_sgn[i] = ( ( ctx_h_sgn[i] << 1 ) | ( sgn[i] & 0x1 ) ) & 0xF;
if ( abs[i] == 15 ) ctx_h_len[i] = ( 2 * len[i] + ctx_h_len[i] + 2 ) / 3;
else ctx_h_len[i] >>= 1;
} else {
ctx_h_sgn[i] = ( ctx_h_sgn[i] << 1 ) & 0xF;
ctx_h_len[i] >>= 1;
}
}
// loop invariant condition
if ( j_coding ) {
// channel 1 context
if ( !sbl ) {
ctx_h_abs = absl_ctx_h[1]+1;
ctx_h_sgn = sgnl_ctx_h[1]+1;
ctx_h_len = lenl_ctx_h[1]+1;
} else {
ctx_h_abs = abss_ctx_h[1]+1;
ctx_h_sgn = sgns_ctx_h[1]+1;
ctx_h_len = lens_ctx_h[1]+1;
}
if ( p < 578 ) {
memset( ctx_h_abs + p, 0, 578 - p );
memset( ctx_h_sgn + p, 2, 578 - p ); // !!!
memset( ctx_h_len + p, 0, 578 - p );
}
for ( i = p-1; i >= 0; i-- ) {
ctx_h_abs[i] = abs[i];
if ( abs[i] > 0 ) {
ctx_h_sgn[i] = sgn[i];
if ( abs[i] == 15 ) ctx_h_len[i] = len[i];
else ctx_h_len[i] = 0;
} else {
ctx_h_sgn[i] = 2;
ctx_h_len[i] = 0;
}
}
}
}
// ---> RECONSTRUCTION INFORMATION: STUFFING BITS <---
n = lmaxp - bitc; // # of stuffing bits
if ( n == 0 ) { // the prefered case
mod_nst->shift_context( ctx_nst );
encode_ari( enc, mod_nst, 0 );
ctx_nst = 0;
} else { // encode # of stuff bits ( should be zero! )
for ( ; n >= STUFFING_STEP; n -= STUFFING_STEP ) {
mod_nst->shift_context( ctx_nst );
encode_ari( enc, mod_nst, STUFFING_STEP );
ctx_nst = STUFFING_STEP;
}
mod_nst->shift_context( ctx_nst );
encode_ari( enc, mod_nst, n );
ctx_nst = n;
// encode stuffing bits
for ( ; bitc < lmaxp; bitc++ ) {
mod_bst->shift_context( ctx_bst );
c = huffman->read_bit();
encode_ari( enc, mod_bst, c );
ctx_bst = ( (ctx_bst<<1) | c ) & 0xF;
}
}
// ---> GRANULE FINISHED! <---
bitp += lmaxp;
/*if ( ( granule->n >= 0 ) && ( granule->n <= 0 ) ) {
fprintf( stderr, "\ngranule %i channel %i block_type: %i flags: %i\n", granule->n, ch, granule->block_type, flags );
fprintf( stderr, "\ngranule %i channel %i nst: %i bst: %i aus: %i aux: %i pad: %i abs: %i\n", granule->n, ch, ctx_nst, ctx_bst, ctx_aux, ctx_aux, ctx_pad, ctx_abs );
fprintf( stderr, "\ngranule %i channel %i bounds: %i/%i/%i/%i/%i\n", granule->n, ch, granule->region_bound[0], granule->region_bound[1], granule->region_bound[2], granule->sv_bound, granule->main_data_bit );
fprintf( stderr, "\ngranule %i channel %i tables: %i/%i/%i/%i\n", granule->n, ch, granule->region_table[0], granule->region_table[1], granule->region_table[2], granule->select_htabB );
fprintf( stderr, "\ngranule %i channel %i pos: %i\n", granule->n, ch, huffman->getpos() );
fprintf( stderr, "\ngranule %i channel %i bad_coding: %s\n", granule->n, ch, bad_coding ? "yes" : "no" );
fprintf( stderr, "\ngranule %i channel %i scfs: (%i/%i)\n", granule->n, ch, granule->share, granule->slength );
for ( i = 0; i < 21; i++ ) fprintf( stderr, "%i, ", scf[i] );
fprintf( stderr, "\n" );
fprintf( stderr, "\ngranule %i channel %i coefs:\n", granule->n, ch );
for ( i = 0; i < 578; i++ ) fprintf( stderr, "%i, ", abs[i] );
fprintf( stderr, "\n" );
for ( i = 0; i < 576; i++ ) if ( abs[i] == 15 ) fprintf( stderr, "%i, ", lbt[i] );
fprintf( stderr, "\n" );
for ( i = 0; i < 578; i++ ) if ( abs[i] > 0 ) fprintf( stderr, "%s", (sgn[i]) ? "+" : "-" );
fprintf( stderr, "\n" );
// fprintf( stderr, "\ngranule %i channel %i abs_ctx:\n", granule->n, ch );
// for ( i = 0; i < 578; i++ ) fprintf( stderr, "%i, ", ctx_h_abs[i] );
// fprintf( stderr, "\n" );
// fprintf( stderr, "\ngranule %i channel %i sgn_ctx:\n", granule->n, ch );
// for ( i = 0; i < 578; i++ ) fprintf( stderr, "%i, ", ctx_h_sgn[i] );
// fprintf( stderr, "\n" );
// fprintf( stderr, "\ngranule %i channel %i len_ctx:\n", granule->n, ch );
// for ( i = 0; i < 578; i++ ) fprintf( stderr, "%i, ", ctx_h_len[i] );
// fprintf( stderr, "\n" );
}*/
}
}
// ---> ENCODE BITRATE <---
if ( i_bitrate == -1 ) {
mod_btr->shift_context( bitrate_pred[ frame->main_size ] ); // !!! (bit-reservoir?)
encode_ari( enc, mod_btr, frame->bits );
}
// ---> RECONSTRUCTION INFORMATION: AUX DATA AND PADDING <---
if ( ( frame != lastframe ) && ( i_bit_res != 0 ) ) {
if ( frame->aux_size + frame->next->bit_reservoir < 511 )
n = frame->aux_size; // byte size of aux data
else n = 511 - frame->next->bit_reservoir; // space taken from bit reservoir
for ( ; n >= AUX_DATA_STEP; n -= AUX_DATA_STEP ) {
mod_aux->shift_context( ctx_aux );
encode_ari( enc, mod_aux, AUX_DATA_STEP );
ctx_aux = AUX_DATA_STEP;
}
mod_aux->shift_context( ctx_aux );
encode_ari( enc, mod_aux, n );
ctx_aux = n;
}
// # of padding and aux data bits
n = ( ( frame->main_size + frame->aux_size ) * 8 ) - bitp;
// locally store padding and aux data
for ( pna_c = pad_and_aux, i = n; i >= 8; i -= 8 )
*(pna_c++) = huffman->read_bits( 8 );
*pna_c = huffman->read_bits( i );
// make and check prediction
if ( pmp_predict_lame_anc( n, pad_and_aux ) == NULL )
// prediction matches
encode_ari( enc, mod_pap, 1 );
else { // prediction doesn't match
encode_ari( enc, mod_pap, 0 );
huffman->rewind_bits( n );
for ( ctx_pad = 0xFF; n > 0; n-- ) {
mod_pad->shift_context( ctx_pad );
c = huffman->read_bit();
encode_ari( enc, mod_pad, c );
ctx_pad = ( (ctx_pad<<1) | c ) & 0xFF;
}
}
// encode padding bits(convert to full bytes)
/*for ( c = 0xFF; n >= 8; n -= 8 ) {
mod_pad->shift_context( c );
c = huffman->read_bits( 8 );
encode_ari( enc, mod_pad, c );
}
if ( n > 0 ) {
mod_pad->shift_context( 0xFF + n );
c = huffman->read_bits( n );
encode_ari( enc, mod_pad, c );
}*/
// ---> FRAME FINISHED! <----
}
// ---> AFTER ENCODING: CLEAN UP <---
// --- CLEAN UP: MODELS AND HUFFMAN CODER ---
delete( huffman );
delete( mod_svb );
delete( mod_bvf );
delete( mod_nst );
delete( mod_bst );
delete( mod_pad );
delete( mod_pap );
delete( mod_aux );
delete( mod_btr );
delete( mod_res );
for ( ch = 0; ch < g_nchannels; ch++ ) {
for ( sl = 0; sl < 4; sl++ )
for ( g = 0; g < 10; g++ )
delete( mod_scf[ch][sl][g] );
for ( flags = 0x0; flags < ( (j_coding&&ch) ? 0x8 : 0x2 ); flags++ ) {
delete( mod_abv[ch][flags][ 1] );
delete( mod_abv[ch][flags][ 2] );
delete( mod_abv[ch][flags][ 3] );
delete( mod_abv[ch][flags][ 5] );
delete( mod_abv[ch][flags][ 6] );
delete( mod_abv[ch][flags][ 7] );
delete( mod_abv[ch][flags][ 8] );
delete( mod_abv[ch][flags][ 9] );
delete( mod_abv[ch][flags][10] );
delete( mod_abv[ch][flags][11] );
delete( mod_abv[ch][flags][12] );
delete( mod_abv[ch][flags][13] );
delete( mod_abv[ch][flags][15] );
delete( mod_abv[ch][flags][16] );
delete( mod_abv[ch][flags][24] );
delete( mod_asv[ch][flags][0] );
delete( mod_asv[ch][flags][1] );
delete( mod_sgn[ch][flags] );
}
for ( i = 0; i <= 13; i++ )
delete( mod_len[ch][i] );
}
// --- CLEAN UP: MEMORY DEALLOCATION ---
for ( ch = 0; ch < g_nchannels; ch++ ) {
free( scf_c[ch] ); free( scf_l_long[ch] ); free( scf_l_short[ch] );
free( abs_c[ch] ); free( absl_ctx_h[ch] ); free( abss_ctx_h[ch] );
free( sgn_c[ch] ); free( sgnl_ctx_h[ch] ); free( sgns_ctx_h[ch] );
free( len_c[ch] ); free( lenl_ctx_h[ch] ); free( lens_ctx_h[ch] );
free( lbt_c[ch] );
}
return true;
}
/* -----------------------------------------------
decode the main data
----------------------------------------------- */
INTERN inline bool pmp_decode_main_data( aricoder* dec )
{
// context / storage
static unsigned char* pad_and_aux= ( unsigned char* ) calloc( 2048, 1 );
mp3Frame* frame;
granuleInfo* granule;
unsigned char* scf_c[2];
unsigned char* scf_l_long[2];
unsigned char* scf_l_short[2];
unsigned char* abs_c[2];
unsigned char* sgn_c[2];
unsigned char* len_c[2];
unsigned short* lbt_c[2];
unsigned char* absl_ctx_h[2];
unsigned char* abss_ctx_h[2];
unsigned char* sgnl_ctx_h[2];
unsigned char* sgns_ctx_h[2];
unsigned char* lenl_ctx_h[2];
unsigned char* lens_ctx_h[2];
unsigned char* scf_prev;
unsigned char* ctx_h_abs;
unsigned char* ctx_h_sgn;
unsigned char* ctx_h_len;
unsigned char* scf;
unsigned char* abs;
unsigned char* sgn;
unsigned char* len;
unsigned short* lbt;
unsigned char* swap;
unsigned char* pna_c;
unsigned char ctx_scf;
unsigned char ctx_abs;
unsigned char ctx_pat;
unsigned char ctx_svb[2] = { 0, 0 };
unsigned char ctx_nst = 0;
unsigned char ctx_bst = 0;
unsigned char ctx_aux = 0;
unsigned char ctx_pad = 0;
// statistical models
model_s* mod_scf[2][4][10];
model_s* mod_abv[2][8][32];
model_b* mod_asv[2][8][2];
model_b* mod_sgn[2][8];
model_s* mod_len[2][14];
model_b* mod_res;
model_s* mod_svb;
model_s* mod_bvf;
model_s* mod_nst;
model_b* mod_bst;
model_b* mod_pad;
model_b* mod_pap;
model_s* mod_aux;
model_s* mod_btr;
model_s* mod_abc;
model_b* mod_asc;
model_b* mod_sgc;
model_s* mod_lnc;
model_s* mod_sfc;
// lookup tables
const int* bitrate_pred = mp3_bitrate_pred[(int)i_samplerate];
const int* frame_size = mp3_frame_size_table[(int)i_samplerate];
// huffman encoder
huffman_writer* huffman;
// mp3 encoding settings
short* region_bounds;
char* region_tables;
huffman_enc_table* bv_table;
huffman_code** hcodes;
huffman_code* hcode;
int bitres = 0;
int linbits;
const int* slen;
int rlb, sl;
// general settings
int bitp = 0;
int sbl = 0;
int flags = 0;
bool j_coding;
char shared[2][4];
// counters
int ch, gr;
int p, g, r;
int i, c, n;
// --- PRE-PROCESSING PREPARATIONS: INIT STATISTICAL MODELS/HUFFMAN DECODER/CONTEXT ---
// decide if joint context will be used (will be used for joint & standard stereo)
j_coding = ( i_channels == MP3_STEREO ) || ( i_channels == MP3_JOINT_STEREO );
// init huffman encoder/writer
huffman = new huffman_writer( 0 );
// reset ancillary predictor
pmp_predict_lame_anc( -1, NULL );
// init statistical models
mod_svb = INIT_MODEL_S( ( 576 / 4 ) + 1 + 1, ( 576 / 4 ) + 1 + 1, 1 );
mod_bvf = INIT_MODEL_S( ( 576 / 2 ) + 1, 0, 0 );
mod_nst = INIT_MODEL_S( STUFFING_STEP + 1, STUFFING_STEP + 1, 1 );
mod_bst = INIT_MODEL_B( 16, 1 );
mod_pad = INIT_MODEL_B( 256, 1 );
mod_pap = INIT_MODEL_B( 0, 0 );
mod_aux = INIT_MODEL_S( AUX_DATA_STEP + 1, AUX_DATA_STEP + 1, 1 );
mod_btr = INIT_MODEL_S( 16, 16, 1 );
mod_res = INIT_MODEL_B( 13 + 1, 2 );
for ( ch = 0; ch < g_nchannels; ch++ ) {
for ( g = 0; g < 10; g++ ) {
mod_scf[ch][0][g] = INIT_MODEL_S( 2, 16, 2 );
mod_scf[ch][1][g] = INIT_MODEL_S( 4, 16, 2 );
mod_scf[ch][2][g] = INIT_MODEL_S( 8, 16, 2 );
mod_scf[ch][3][g] = INIT_MODEL_S( 16, 16, 2 );
}
for ( flags = 0x0; flags < ( (j_coding&&ch) ? 0x8 : 0x2 ); flags++ ) {
mod_abv[ch][flags][ 0] = NULL;
mod_abv[ch][flags][ 1] = INIT_MODEL_S( 1 + 1, 16, 2 );
mod_abv[ch][flags][ 2] = INIT_MODEL_S( 2 + 1, 16, 2 );
mod_abv[ch][flags][ 3] = INIT_MODEL_S( 2 + 1, 16, 2 );
mod_abv[ch][flags][ 4] = NULL;
mod_abv[ch][flags][ 5] = INIT_MODEL_S( 3 + 1, 16, 2 );
mod_abv[ch][flags][ 6] = INIT_MODEL_S( 3 + 1, 16, 2 );
mod_abv[ch][flags][ 7] = INIT_MODEL_S( 5 + 1, 16, 2 );
mod_abv[ch][flags][ 8] = INIT_MODEL_S( 5 + 1, 16, 2 );
mod_abv[ch][flags][ 9] = INIT_MODEL_S( 5 + 1, 16, 2 );
mod_abv[ch][flags][10] = INIT_MODEL_S( 7 + 1, 16, 2 );
mod_abv[ch][flags][11] = INIT_MODEL_S( 7 + 1, 16, 2 );
mod_abv[ch][flags][12] = INIT_MODEL_S( 7 + 1, 16, 2 );
mod_abv[ch][flags][13] = INIT_MODEL_S( 15 + 1, 16, 2 );
mod_abv[ch][flags][14] = NULL;
mod_abv[ch][flags][15] = INIT_MODEL_S( 15 + 1, 16, 2 );
mod_abv[ch][flags][16] = INIT_MODEL_S( 15 + 1, 16, 2 );
mod_abv[ch][flags][17] = mod_abv[ch][flags][16];
mod_abv[ch][flags][18] = mod_abv[ch][flags][16];
mod_abv[ch][flags][19] = mod_abv[ch][flags][16];
mod_abv[ch][flags][20] = mod_abv[ch][flags][16];
mod_abv[ch][flags][21] = mod_abv[ch][flags][16];
mod_abv[ch][flags][22] = mod_abv[ch][flags][16];
mod_abv[ch][flags][23] = mod_abv[ch][flags][16];
mod_abv[ch][flags][24] = INIT_MODEL_S( 15 + 1, 16, 2 );
mod_abv[ch][flags][25] = mod_abv[ch][flags][24];
mod_abv[ch][flags][26] = mod_abv[ch][flags][24];
mod_abv[ch][flags][27] = mod_abv[ch][flags][24];
mod_abv[ch][flags][28] = mod_abv[ch][flags][24];
mod_abv[ch][flags][29] = mod_abv[ch][flags][24];
mod_abv[ch][flags][30] = mod_abv[ch][flags][24];
mod_abv[ch][flags][31] = mod_abv[ch][flags][24];
mod_asv[ch][flags][0] = INIT_MODEL_B( 16, 2 );
mod_asv[ch][flags][1] = INIT_MODEL_B( 16, 2 );
mod_sgn[ch][flags] = INIT_MODEL_B( 16, 2 );
}
mod_len[ch][0] = NULL;
for ( i = 1; i <= 13; i++ )
mod_len[ch][i] = INIT_MODEL_S( i + 1, 13 + 1, 1 );
}
// --- PRE-PROCESSING PREPARATIONS: ALLOCATION OF MEMORY ---
for ( ch = 0; ch < g_nchannels; ch++ ) {
// alloc memory
scf_c[ch] = ( unsigned char* ) calloc( 21, sizeof( char ) );
scf_l_long[ch] = ( unsigned char* ) calloc( 21, sizeof( char ) );
scf_l_short[ch] = ( unsigned char* ) calloc( 21, sizeof( char ) );
abs_c[ch] = ( unsigned char* ) calloc( 578+1+1, sizeof( char ) );
sgn_c[ch] = ( unsigned char* ) calloc( 578+1+1, sizeof( char ) );
len_c[ch] = ( unsigned char* ) calloc( 578+1+1, sizeof( char ) );
lbt_c[ch] = ( unsigned short* ) calloc( 576, sizeof( short ) );
absl_ctx_h[ch] = ( unsigned char* ) calloc( 578+1+1, sizeof( char ) );
abss_ctx_h[ch] = ( unsigned char* ) calloc( 578+1+1, sizeof( char ) );
sgnl_ctx_h[ch] = ( unsigned char* ) calloc( 578+1+1, sizeof( char ) );
sgns_ctx_h[ch] = ( unsigned char* ) calloc( 578+1+1, sizeof( char ) );
lenl_ctx_h[ch] = ( unsigned char* ) calloc( 578+1+1, sizeof( char ) );
lens_ctx_h[ch] = ( unsigned char* ) calloc( 578+1+1, sizeof( char ) );
// check for problems
if ( ( scf_c[ch] == NULL ) || ( scf_l_long[ch] == NULL ) || ( scf_l_short[ch] == NULL ) ||
( abs_c[ch] == NULL ) || ( absl_ctx_h[ch] == NULL ) || ( abss_ctx_h[ch] == NULL ) ||
( sgn_c[ch] == NULL ) || ( sgnl_ctx_h[ch] == NULL ) || ( sgns_ctx_h[ch] == NULL ) ||
( len_c[ch] == NULL ) || ( lenl_ctx_h[ch] == NULL ) || ( lens_ctx_h[ch] == NULL ) ||
( lbt_c[ch] == NULL ) ) {
sprintf( errormessage, MEM_ERRMSG );
errorlevel = 2;
return false;
}
}
// --- MAIN PROCESSING LOOP: DECODING AND ENCODING ---
for ( frame = firstframe; frame != NULL; frame = frame->next, bitp = 0 ) {
// record main index
frame->main_index = huffman->getpos();
for ( gr = 0; gr < 2; gr++ ) {
for ( ch = 0; ch < g_nchannels; ch++ ) {
// --- MAIN DATA DECODING: PREPARATIONS ---
// initialize shortcuts
granule = frame->granules[ch][gr];
sbl = ( granule->block_type == SHORT_BLOCK ) ? 1 : 0;
slen = slength_table[ (int) granule->slength ];
region_bounds = granule->region_bound;
region_tables = granule->region_table;
abs = abs_c[ch] + 1;
sgn = sgn_c[ch] + 1;
len = len_c[ch] + 1;
lbt = lbt_c[ch];
// set compression flags
flags = ( !j_coding || ( ch == 0 ) ) ? sbl :
( sbl << 0 ) | // long (0) or short (1) block
( frame->stereo_ms << 1 ) | // stereo ms on/off
( ( flags ^ sbl ) << 2 ); // ch0/ch1 block type diffs y/n
// store sharing info
if ( gr == 0 ) {
shared[ch][0] = ( granule->share >> 3 ) & 0x1;
shared[ch][1] = ( granule->share >> 2 ) & 0x1;
shared[ch][2] = ( granule->share >> 1 ) & 0x1;
shared[ch][3] = ( granule->share >> 0 ) & 0x1;
}
// reset counter
huffman->reset_counter();
// store bit reservoir state
frame->bit_reservoir = bitres;
// ---> SCALEFACTOR PROCESSING <---
if ( !sbl ) {
// --- SCALEFACTOR DECODING/WRITING: LONG BLOCKS ---
scf = scf_c[ch];
scf_prev = scf_l_long[ch];
ctx_scf = 0;
// decode/write 21 scalefactors with/without sharing
for ( g = 0, p = 0; g < 4; g++ ) {
sl = slen[ ( g < 2 ) ? 0 : 1 ];
if ( ( gr ) & ( shared[ch][g] ) ) { // shared
// loop invariant condition (-funswitch-loops)
memcpy( scf + p, scf_prev + p, scf_width[ g ] );
p = scf_bounds[ g ];
ctx_scf = scf[p-1];
} else if ( sl == 0 ) { // zero slength
memset( scf + p, 0, scf_width[ g ] );
p = scf_bounds[ g ];
ctx_scf = 0;
} else {
mod_sfc = mod_scf[ch][sl-1][(shared[ch][g])?g|0x4:g];
for ( ; p < scf_bounds[ g ]; p++ ) { // non shared
shift_model( mod_sfc, ctx_scf, scf_prev[p] );
scf[p] = decode_ari( dec, mod_sfc );
huffman->write_bits( scf[p], sl );
ctx_scf = scf[p];
}
}
}
// --- SCALEFACTORS FINISHED: SWAP DATA ---
swap = scf_c[ch]; scf_c[ch] = scf_l_long[ch]; scf_l_long[ch] = swap;
} else {
// --- SCALEFACTOR DECODING/WRITING: SHORT BLOCKS ---
// encode (only non shared)
for ( i = 0; i < 3; i++ ) { // 3 subblocks
scf = scf_c[ch];
scf_prev = scf_l_short[ch];
ctx_scf = 0;
for ( g = 0, p = 0; g < 2; g++ ) { // lo&hi groups
sl = slen[ g ];
if ( sl == 0 ) { // zero slength
memset( scf + p, 0, scf_lh_width_short[ g ] );
p = scf_lh_bounds_short[ g ];
} else {
mod_sfc = mod_scf[ch][sl-1][g|0x8];
for ( ; p < scf_lh_bounds_short[ g ]; p++ ) { // scfs
shift_model( mod_sfc, ctx_scf, scf_prev[p] );
scf[p] = decode_ari( dec, mod_sfc );
huffman->write_bits( scf[p], sl );
ctx_scf = scf[p];
}
}
}
// --- SWAP DATA ---
swap = scf_c[ch]; scf_c[ch] = scf_l_short[ch]; scf_l_short[ch] = swap;
}
}
// ---> COEFFICIENTS PROCESSING <---
// --- DECODING AND FIXING OF SV BOUND ---
if ( !sbl ) {
mod_svb->shift_context( ctx_svb[ch] );
} else mod_svb->shift_context( 144 + 1 );
c = decode_ari( dec, mod_svb );
if ( c <= 144 ) { // for proper files
granule->sv_bound = c << 2;
if ( granule->region_bound[ 2 ] % 4 == 2 )
granule->sv_bound += 2;
if ( !sbl ) ctx_svb[ch] = c;
} else { // for broken files
c = decode_ari( dec, mod_bvf );
granule->sv_bound = granule->region_bound[ 2 ] - ( c << 1 );
// fix bounds
granule->region_bound[ 2 ] = granule->sv_bound;
for ( i = 1; i >= 0; i-- ) if ( granule->sv_bound < granule->region_bound[i] )
granule->region_bound[i] = granule->sv_bound;
else break;
}
// --- COEFFICIENT DECODING: PREPARATIONS ---
if ( granule->block_type != SHORT_BLOCK ) {
ctx_h_abs = absl_ctx_h[ch]+1;
ctx_h_sgn = sgnl_ctx_h[ch]+1;
ctx_h_len = lenl_ctx_h[ch]+1;
} else {
ctx_h_abs = abss_ctx_h[ch]+1;
ctx_h_sgn = sgns_ctx_h[ch]+1;
ctx_h_len = lens_ctx_h[ch]+1;
}
ctx_abs = 0; ctx_pat = 0;
rlb = region_bounds[2];
mod_sgc = mod_sgn[ch][flags];
// --- COEFFICIENT DECODING: SMALL VALUES ---
mod_asc = mod_asv[ch][flags][(int) granule->select_htabB];
for ( p = granule->sv_bound-1; p >= rlb; p-- ) {
shift_model( mod_asc, ctx_pat, ctx_h_abs[p] );
abs[p] = decode_ari( dec, mod_asc ); // absolutes
ctx_pat = ( (ctx_pat<<1) | abs[p] ) & 0xF;
ctx_abs = ( 2 * abs[p] + ctx_abs + 2 ) / 3;
if ( abs[p] == 1 ) {
shift_model( mod_sgc, ctx_h_abs[p], ctx_h_sgn[p] );
sgn[p] = decode_ari( dec, mod_sgc ); // signs
}
}
// --- COEFFICIENT DECODING: BIG VALUES ---
for ( r = 2; r >= 0; r-- ) {
rlb = (r==0) ? 0 : region_bounds[ r-1 ];
if ( p < rlb ) continue;
if ( region_tables[ r ] == 0 ) { // tbl0 skipping
memset( abs + rlb, 0, p - rlb );
p = rlb; ctx_abs = 0;
continue;
}
// set table and linbits
bv_table = bv_enc_table + region_tables[ r ];
linbits = bv_table->linbits;
mod_abc = mod_abv[ch][flags][(int) region_tables[ r ]];
mod_lnc = mod_len[ch][linbits];
// decoding with/without linbits
for ( ; p >= rlb; p-- ) {
shift_model( mod_abc, ctx_abs, ctx_h_abs[p] );
abs[p] = decode_ari( dec, mod_abc ); // absolutes
ctx_abs = ( 2 * abs[p] + ctx_abs + 2 ) / 3;
if ( abs[p] > 0 ) {
shift_model( mod_sgc, ctx_h_abs[p], ctx_h_sgn[p] );
sgn[p] = decode_ari( dec, mod_sgc ); // signs
if ( linbits > 0 ) if ( abs[p] == 15 ) {
// loop invariant condition (-funswitch-loops)
mod_lnc->shift_context( ctx_h_len[p] );
len[p] = decode_ari( dec, mod_lnc ); // bitlengths
if ( len[p] > 0 ) {
for ( lbt[p] = 1, i = len[p] - 2; i >= 0; i-- ) {
shift_model( mod_res, len[p], i ); // bit residuals
lbt[p] = ( lbt[p] << 1 ) | decode_ari( dec, mod_res );
}
} else lbt[p] = 0;
}
}
}
}
// --- COEFFICIENT ENCODING: BIG VALUES ---
for ( p = 0, r = 0; r < 3; r++ ) {
if ( region_tables[ r ] == 0 ) { // tbl0 skipping
p = region_bounds[ r ];
continue;
}
// set table and linbits
bv_table = bv_enc_table + region_tables[ r ];
hcodes = bv_table->h;
linbits = bv_table->linbits;
// encoding with/without linbits
while ( p < region_bounds[ r ] ) {
huffman->encode_pair( hcodes, abs + p );
for ( i = 0; i < 2; i++, p++ ) if ( abs[p] > 0 ) {
if ( linbits > 0 ) // loop invariant condition (-unswitch-loops)
if ( abs[p] == 15 ) huffman->write_bits( lbt[p], linbits );
huffman->write_bit( sgn[p] );
}
}
}
// --- COEFFICIENT ENCODING: SMALL VALUES ---
hcode = ( granule->select_htabB ) ? htabB_enc : htabA_enc;
while ( p < granule->sv_bound ) {
huffman->encode_quadruple( hcode, abs + p );
for ( i = 0; i < 4; i++, p++ ) if ( abs[p] )
huffman->write_bit( sgn[p] );
}
// --- COEFFICIENTS FINISHED: UPDATE CONTEXT ---
if ( p < 578 ) memset( abs + p, 0, 578 - p );
// loop invariant condition
if ( !j_coding || ( ch == 0 ) ) {
// channel 0 context
for ( i = 578-1; i >= 0; i-- ) {
ctx_h_abs[i] = ( 3 * abs[i] + abs[i-1] + abs[i+1] + 2 * ctx_h_abs[i] + 4 ) / 7;
if ( abs[i] > 0 ) {
ctx_h_sgn[i] = ( ( ctx_h_sgn[i] << 1 ) | ( sgn[i] & 0x1 ) ) & 0xF;
if ( abs[i] == 15 ) ctx_h_len[i] = ( 2 * len[i] + ctx_h_len[i] + 2 ) / 3;
else ctx_h_len[i] >>= 1;
} else {
ctx_h_sgn[i] = ( ctx_h_sgn[i] << 1 ) & 0xF;
ctx_h_len[i] >>= 1;
}
}
// loop invariant condition
if ( j_coding ) {
// channel 1 context
if ( !sbl ) {
ctx_h_abs = absl_ctx_h[1]+1;
ctx_h_sgn = sgnl_ctx_h[1]+1;
ctx_h_len = lenl_ctx_h[1]+1;
} else {
ctx_h_abs = abss_ctx_h[1]+1;
ctx_h_sgn = sgns_ctx_h[1]+1;
ctx_h_len = lens_ctx_h[1]+1;
}
if ( p < 578 ) {
memset( ctx_h_abs + p, 0, 578 - p );
memset( ctx_h_sgn + p, 2, 578 - p );
memset( ctx_h_len + p, 0, 578 - p );
}
for ( i = p-1; i >= 0; i-- ) {
ctx_h_abs[i] = abs[i];
if ( abs[i] > 0 ) {
ctx_h_sgn[i] = sgn[i];
if ( abs[i] == 15 ) ctx_h_len[i] = len[i];
else ctx_h_len[i] = 0;
} else {
ctx_h_sgn[i] = 2;
ctx_h_len[i] = 0;
}
}
}
}
// ---> RECONSTRUCTION INFORMATION: STUFFING BITS <---
for ( n = 0, c = STUFFING_STEP; c == STUFFING_STEP; n += c ) {
// find out # of stuffing bits
mod_nst->shift_context( ctx_nst );
c = decode_ari( dec, mod_nst );
ctx_nst = c;
}
for ( ; n > 0; n-- ) {
mod_bst->shift_context( ctx_bst );
c = decode_ari( dec, mod_bst );
huffman->write_bit( c );
ctx_bst = ( (ctx_bst<<1) | c ) & 0xF;
}
// ---> GRANULE FINISHED! <---
granule->main_data_bit = huffman->get_count();
bitp += granule->main_data_bit;
/*if ( ( granule->n >= 0 ) && ( granule->n <= 0 ) ) {
fprintf( stderr, "\ngranule %i channel %i block_type: %i flags: %i\n", granule->n, ch, granule->block_type, flags );
fprintf( stderr, "\ngranule %i channel %i nst: %i bst: %i aus: %i aux: %i pad: %i abs: %i\n", granule->n, ch, ctx_nst, ctx_bst, ctx_aux, ctx_aux, ctx_pad, ctx_abs );
fprintf( stderr, "\ngranule %i channel %i bounds: %i/%i/%i/%i/%i\n", granule->n, ch, granule->region_bound[0], granule->region_bound[1], granule->region_bound[2], granule->sv_bound, granule->main_data_bit );
fprintf( stderr, "\ngranule %i channel %i tables: %i/%i/%i/%i\n", granule->n, ch, granule->region_table[0], granule->region_table[1], granule->region_table[2], granule->select_htabB );
fprintf( stderr, "\ngranule %i channel %i pos: %i\n", granule->n, ch, huffman->getpos() );
fprintf( stderr, "\ngranule %i channel %i scfs: (%i/%i)\n", granule->n, ch, granule->share, granule->slength );
for ( i = 0; i < 21; i++ ) fprintf( stderr, "%i, ", scf[i] );
fprintf( stderr, "\n" );
fprintf( stderr, "\ngranule %i channel %i coefs:\n", granule->n, ch );
for ( i = 0; i < 578; i++ ) fprintf( stderr, "%i, ", abs[i] );
fprintf( stderr, "\n" );
for ( i = 0; i < 576; i++ ) if ( abs[i] == 15 ) fprintf( stderr, "%i, ", lbt[i] );
fprintf( stderr, "\n" );
for ( i = 0; i < 578; i++ ) if ( abs[i] > 0 ) fprintf( stderr, "%s", (sgn[i]) ? "+" : "-" );
fprintf( stderr, "\n" );
// fprintf( stderr, "\ngranule %i channel %i abs_ctx:\n", granule->n, ch );
// for ( i = 0; i < 578; i++ ) fprintf( stderr, "%i, ", ctx_h_abs[i] );
// fprintf( stderr, "\n" );
// fprintf( stderr, "\ngranule %i channel %i sgn_ctx:\n", granule->n, ch );
// for ( i = 0; i < 578; i++ ) fprintf( stderr, "%i, ", ctx_h_sgn[i] );
// fprintf( stderr, "\n" );
// fprintf( stderr, "\ngranule %i channel %i len_ctx:\n", granule->n, ch );
// for ( i = 0; i < 578; i++ ) fprintf( stderr, "%i, ", ctx_h_len[i] );
// fprintf( stderr, "\n" );
}*/
}
}
// --- STORE FRAME MAIN DATA SIZES ---
frame->main_bits = bitp;
frame->main_size = ( bitp + 7 ) / 8;
// ---> DECODE BITRATE <---
if ( i_bitrate == -1 ) {
mod_btr->shift_context( bitrate_pred[ frame->main_size ] );
frame->bits = decode_ari( dec, mod_btr );
}
// calculate size of frame - use the lookup table
frame->frame_size = frame_size[(int)frame->bits];
if ( frame->padding ) frame->frame_size++;
// ---> RECONSTRUCTION INFORMATION: AUX DATA AND PADDING <---
if ( i_bit_res != 0 ) {
// preliminary next frame bit reservoir size
bitres =
frame->frame_size -
frame->fixed_size +
frame->bit_reservoir -
frame->main_size;
if ( frame != lastframe ) {
n = ( bitres < 511 ) ? 0 : bitres - 511;
for ( c = AUX_DATA_STEP; c == AUX_DATA_STEP; n += c ) {
// byte size of aux data
mod_aux->shift_context( ctx_aux );
c = decode_ari( dec, mod_aux );
ctx_aux = c;
}
frame->aux_size = n;
} else frame->aux_size = bitres;
// finalize size of bit reservoir
bitres -= frame->aux_size;
} else {
bitres = 0;
frame->aux_size =
frame->frame_size -
frame->fixed_size +
frame->bit_reservoir -
frame->main_size;
}
// # of padding and aux data bits
n = ( ( frame->main_size + frame->aux_size ) * 8 ) - bitp;
// check if data can be predicted 100%
if ( decode_ari( dec, mod_pap ) == 1 ) {
// prediction matches !
pna_c = pmp_predict_lame_anc( n, NULL );
for ( ; n >= 8; n -= 8 ) huffman->write_bits( *(pna_c++), 8 );
if ( n > 0 ) huffman->write_bits( *(pna_c) >> (8-n), n );
} else {
// prediction doesn't match (d'oh!)
pna_c = pad_and_aux; *pna_c = 0;
for ( ctx_pad = 0xFF, i = 1; i <= n; i++ ) {
mod_pad->shift_context( ctx_pad );
c = decode_ari( dec, mod_pad );
huffman->write_bit( c );
ctx_pad = ( (ctx_pad<<1) | c ) & 0xFF;
*pna_c = (*pna_c<<1)|c;
if ( i % 8 == 0 ) *(++pna_c) = 0;
}
if ( pmp_predict_lame_anc( n, pad_and_aux ) == NULL ) {
sprintf( errormessage, "ancilary prediction error, please report" );
errorlevel = 2;
return false;
}
}
// decode padding bits
/*for ( c = 0xFF; n >= 8; n -= 8 ) {
mod_pad->shift_context( c );
c = decode_ari( dec, mod_pad );
huffman->write_bits( c, 8 );
}
if ( n > 0 ) {
mod_pad->shift_context( 0xFF + n );
c = decode_ari( dec, mod_pad );
huffman->write_bits( c, n );
}*/
// ---> FRAME FINISHED! <----
}
// ---> AFTER ENCODING: GRAB POINTER AND CLEAN UP <---
// --- GRAB POINTER AND SIZE ---
main_data = huffman->getptr();
main_data_size = huffman->getpos();
// --- CALCULATE MP3 FILE SIZE ---
mp3filesize =
data_before_size +
data_after_size +
main_data_size +
( g_nframes * lastframe->fixed_size );
// --- CLEAN UP: MODELS AND HUFFMAN CODER ---
delete( huffman );
delete( mod_svb );
delete( mod_bvf );
delete( mod_nst );
delete( mod_bst );
delete( mod_pad );
delete( mod_pap );
delete( mod_aux );
delete( mod_btr );
delete( mod_res );
for ( ch = 0; ch < g_nchannels; ch++ ) {
for ( sl = 0; sl < 4; sl++ )
for ( g = 0; g < 10; g++ )
delete( mod_scf[ch][sl][g] );
for ( flags = 0x0; flags < ( (j_coding&&ch) ? 0x8 : 0x2 ); flags++ ) {
delete( mod_abv[ch][flags][ 1] );
delete( mod_abv[ch][flags][ 2] );
delete( mod_abv[ch][flags][ 3] );
delete( mod_abv[ch][flags][ 5] );
delete( mod_abv[ch][flags][ 6] );
delete( mod_abv[ch][flags][ 7] );
delete( mod_abv[ch][flags][ 8] );
delete( mod_abv[ch][flags][ 9] );
delete( mod_abv[ch][flags][10] );
delete( mod_abv[ch][flags][11] );
delete( mod_abv[ch][flags][12] );
delete( mod_abv[ch][flags][13] );
delete( mod_abv[ch][flags][15] );
delete( mod_abv[ch][flags][16] );
delete( mod_abv[ch][flags][24] );
delete( mod_asv[ch][flags][0] );
delete( mod_asv[ch][flags][1] );
delete( mod_sgn[ch][flags] );
}
for ( i = 0; i <= 13; i++ )
delete( mod_len[ch][i] );
}
// --- CLEAN UP: MEMORY DEALLOCATION ---
for ( ch = 0; ch < g_nchannels; ch++ ) {
free( scf_c[ch] ); free( scf_l_long[ch] ); free( scf_l_short[ch] );
free( abs_c[ch] ); free( absl_ctx_h[ch] ); free( abss_ctx_h[ch] );
free( sgn_c[ch] ); free( sgnl_ctx_h[ch] ); free( sgns_ctx_h[ch] );
free( len_c[ch] ); free( lenl_ctx_h[ch] ); free( lens_ctx_h[ch] );
free( lbt_c[ch] );
}
return true;
}
/* -----------------------------------------------
store unmute data
----------------------------------------------- */
INTERN inline bool pmp_store_unmute_data( iostream* str )
{
// as simple as it gets...
str->write( unmute_data, sizeof( char ), unmute_data_size );
return true;
}
/* -----------------------------------------------
unstore unmute data
----------------------------------------------- */
INTERN inline bool pmp_unstore_unmute_data( iostream* str )
{
unsigned char n;
// read # of of muted frames, set data size
str->read( &n, sizeof( char ), 1 );
if ( str->chkeof() ) n = 0;
unmute_data_size = 1 + n * ( 2 + ( g_nchannels * 2 * 4 ) );
// alloc memory for unmute data
unmute_data = (unsigned char*) calloc( unmute_data_size, sizeof( char ) );
if ( unmute_data == NULL ) {
sprintf( errormessage, MEM_ERRMSG );
errorlevel = 2;
return false;
}
// read unmute data
*unmute_data = n;
str->read( unmute_data + 1, sizeof( char ), unmute_data_size - 1 );
// check for eof
if ( str->chkeof() ) {
sprintf( errormessage, "unexpected end of data" );
errorlevel = 2;
return false;
}
return true;
}
/* -----------------------------------------------
build universal context from global gain
----------------------------------------------- */
INTERN inline bool pmp_build_context( void )
{
granuleInfo* granule;
int count_gg[ 2 ][ 256 ];
unsigned char* gg0;
unsigned char* gg1;
int ngr;
int lbound;
int inc0, inc1;
int i, ch;
// check and free gg context arrays where needed
if ( gg_context[0] != NULL ) free ( gg_context[0] );
if ( gg_context[1] != NULL ) free ( gg_context[1] );
gg_context[0] = NULL;
gg_context[1] = NULL;
// number of granules (per channel)
ngr = g_nframes * 2;
// alloc memory for three global gain contexts
// yup, this might be a waste for mono files
gg_context[0] = (unsigned char*) calloc ( ngr, sizeof( char ) );
gg_context[1] = (unsigned char*) calloc ( ngr, sizeof( char ) );
if ( ( gg_context[0] == NULL ) || ( gg_context[1] == NULL ) ) {
sprintf( errormessage, MEM_ERRMSG );
errorlevel = 2;
return false;
}
// set everything zero
memset( count_gg[0], 0, 256 * 4 );
memset( count_gg[1], 0, 256 * 4 );
// count channel 0 symbol occurences
gg0 = gg_context[0];
for ( granule = firstframe->granules[0][0]; granule != NULL; granule = granule->next ) {
*gg0 = granule->global_gain;
count_gg[0][*(gg0++)]++;
}
for ( i = 1; i < 256; i++ ) // accumulate counts
count_gg[0][i] += count_gg[0][i-1];
// count channel 1 and diff symbol occurences
if ( g_nchannels == 2 ) {
gg0 = gg_context[0];
gg1 = gg_context[1];
for ( granule = firstframe->granules[1][0]; granule != NULL; granule = granule->next ) {
*gg1 = granule->global_gain;
count_gg[1][*(gg1++)]++;
gg0++;
}
for ( i = 1; i < 256; i++ ) // accumulate counts
count_gg[1][i] += count_gg[1][i-1];
}
// analyse and process gg_context
for ( ch = 0; ch < g_nchannels; ch++ ) {
// find the interval with the highest increase
for ( lbound = 0, inc0 = 0, inc1 = 0, i = 0; i < 256 - GG_CONTEXT_SIZE; i++ ) {
inc1 = count_gg[ch][i + GG_CONTEXT_SIZE] - count_gg[ch][i];
if ( inc1 >= inc0 ) {
inc0 = inc1;
lbound = i;
}
}
// process gg_context
for ( i = 0, gg0 = gg_context[ch]; i < ngr; i++, gg0++ ) {
if ( *gg0 >= lbound ) {
*gg0 -= lbound;
if ( *gg0 >= GG_CONTEXT_SIZE ) *gg0 = GG_CONTEXT_SIZE - 1;
} else *gg0 = 0;
}
}
return true;
}
/* -----------------------------------------------
predict LAME ancillary data
----------------------------------------------- */
INTERN inline unsigned char* pmp_predict_lame_anc( int nbits, unsigned char* ref )
{
static unsigned char* pred = (unsigned char*) calloc( 2048, sizeof( char ) );
static unsigned char* lame_str = (unsigned char*) calloc( 4 + 16, sizeof( char ) ); // !!!
const unsigned char b01 = 0x55;
const unsigned char b10 = 0xAA;
const unsigned char b00 = 0x00;
const unsigned char b11 = 0xFF;
static int lame_str_len = 4;
static int lame_bit = 0;
static bool alt_pred = 0;
int offset;
int nbytes;
int i;
// fprintf( stderr, "state:%i/%i/%02X/%02X ", (alt_pred)?0:1, nbits, pred[0], (ref!=NULL)?ref[0]:0xFF );
if ( nbits < 0 ) {
// (re-)init prediction
lame_bit = 0;
lame_str_len = 4;
memcpy( lame_str, "LAME", 4 );
alt_pred = false;
} else {
offset = nbits % 8;
nbytes = nbits / 8;
// build prediction
if ( !alt_pred ) { // option 0 - lame predictor
for ( i = 0; ( nbits >=8 ) && ( i < 4 ); nbits -= 8, i++ )
pred[i] = lame_str[i]; // 'LAME'
if ( nbits >= 32 ) {
for ( ; ( nbits >= 8 ) && ( i < lame_str_len ); nbits -= 8, i++ )
pred[i] = lame_str[i]; // version number
}
for ( ; nbits > 0; nbits -= 8, i++ )
pred[i] = ( lame_bit == 0 ) ? b01 : b10; // switching bits
lame_bit ^= ( offset % 2 );
} // else: keep the prediction as is
if ( ref != NULL ) { // compare prediction with reference (if any)
if ( ( memcmp( pred, ref, nbytes ) == 0 ) && // match
( ref[nbytes] == pred[nbytes] >> (8-offset) ) ) return NULL;
else { // no match - check alternatives / relearn version number
if ( (nbytes >= 8 ) && ( memcmp( ref, lame_str, 4 ) == 0 ) ) { // 'LAME' match
// careful !!! (ref has to be >=8 in size)
// relearn version number (if any)
for ( i = 4; ( i < 4 + 16 ) && ( i < nbytes ); i++ ) {
if ( ref[i] == b01 ) {
lame_bit = 0;
break;
} else if ( ref[i] == b10 ) {
lame_bit = 1;
break;
}
lame_str[i] = ref[i];
}
lame_str_len = i;
} else if ( ( nbytes == 0 ) && ( offset > 1 ) ) {
if ( (b00^ref[0]) >> (8-offset) == 0 ) {
memset( pred, b00, 2048 );
alt_pred = true;
} else if ( (b11^ref[0]) >> (8-offset) == 0 ) {
memset( pred, b11, 2048 );
alt_pred = true;
} else alt_pred = false;
} else if ( nbytes >= 1 ) {
if ( b00 == ref[0] ) {
memset( pred, b00, 2048 );
alt_pred = true;
} else if ( b11 == ref[0] ) {
memset( pred, b11, 2048 );
alt_pred = true;
} else alt_pred = false;
}
}
}
// else: no reference, so prediction is assumed correct
}
return pred;
}
/* ----------------------- End of PMP specific functions -------------------------- */
/* ----------------------- Begin of miscellaneous helper functions -------------------------- */
/* -----------------------------------------------
displays progress bar on screen
----------------------------------------------- */
#if !defined(BUILD_LIB)
INTERN inline void progress_bar( int current, int last )
{
int barpos = ( ( current * BARLEN ) + ( last / 2 ) ) / last;
int i;
// generate progress bar
fprintf( msgout, "[" );
#if defined(_WIN32)
for ( i = 0; i < barpos; i++ )
fprintf( msgout, "\xFE" );
#else
for ( i = 0; i < barpos; i++ )
fprintf( msgout, "X" );
#endif
for ( ; i < BARLEN; i++ )
fprintf( msgout, " " );
fprintf( msgout, "]" );
}
#endif
/* -----------------------------------------------
creates filename, callocs memory for it
----------------------------------------------- */
#if !defined(BUILD_LIB)
INTERN inline char* create_filename( const char* base, const char* extension )
{
int len = strlen( base ) + ( ( extension == NULL ) ? 0 : strlen( extension ) + 1 ) + 1;
char* filename = (char*) calloc( len, sizeof( char ) );
// create a filename from base & extension
strcpy( filename, base );
set_extension( filename, extension );
return filename;
}
#endif
/* -----------------------------------------------
creates filename, callocs memory for it
----------------------------------------------- */
#if !defined(BUILD_LIB)
INTERN inline char* unique_filename( const char* base, const char* extension )
{
int len = strlen( base ) + ( ( extension == NULL ) ? 0 : strlen( extension ) + 1 ) + 1;
char* filename = (char*) calloc( len, sizeof( char ) );
// create a unique filename using underscores
strcpy( filename, base );
set_extension( filename, extension );
while ( file_exists( filename ) ) {
len += sizeof( char );
filename = (char*) realloc( filename, len );
add_underscore( filename );
}
return filename;
}
#endif
/* -----------------------------------------------
changes extension of filename
----------------------------------------------- */
#if !defined(BUILD_LIB)
INTERN inline void set_extension( const char* filename, const char* extension )
{
char* extstr;
// find position of extension in filename
extstr = ( strrchr( filename, '.' ) == NULL ) ?
strrchr( filename, '\0' ) : strrchr( filename, '.' );
// set new extension
if ( extension != NULL ) {
(*extstr++) = '.';
strcpy( extstr, extension );
}
else
(*extstr) = '\0';
}
#endif
/* -----------------------------------------------
adds underscore after filename
----------------------------------------------- */
#if !defined(BUILD_LIB)
INTERN inline void add_underscore( char* filename )
{
char* tmpname = (char*) calloc( strlen( filename ) + 1, sizeof( char ) );
char* extstr;
// copy filename to tmpname
strcpy( tmpname, filename );
// search extension in filename
extstr = strrchr( filename, '.' );
// add underscore before extension
if ( extstr != NULL ) {
(*extstr++) = '_';
strcpy( extstr, strrchr( tmpname, '.' ) );
}
else
sprintf( filename, "%s_", tmpname );
// free memory
free( tmpname );
}
#endif
/* -----------------------------------------------
checks if a file exists
----------------------------------------------- */
INTERN inline bool file_exists( const char* filename )
{
// needed for both, executable and library
FILE* fp = fopen( filename, "rb" );
if ( fp == NULL ) return false;
else {
fclose( fp );
return true;
}
}
/* ----------------------- End of miscellaneous helper functions -------------------------- */
/* ----------------------- Begin of developers functions -------------------------- */
#if !defined(BUILD_LIB) && defined(DEV_BUILD)
/* -----------------------------------------------
Writes to file
----------------------------------------------- */
INTERN bool write_file( const char* base, const char* ext, void* data, int bpv, int size )
{
FILE* fp;
char* fn;
// create filename
fn = create_filename( base, ext );
// open file for output
fp = fopen( fn, "wb" );
if ( fp == NULL ) {
sprintf( errormessage, FWR_ERRMSG );
errorlevel = 2;
return false;
}
free( fn );
// write & close
fwrite( data, bpv, size, fp );
fclose( fp );
return true;
}
/* -----------------------------------------------
Writes error info file
----------------------------------------------- */
INTERN bool write_errfile( void )
{
FILE* fp;
char* fn;
// return immediately if theres no error
if ( errorlevel == 0 ) return true;
// create filename based on errorlevel
if ( errorlevel == 1 ) {
fn = create_filename( filelist[ file_no ], "wrn.nfo" );
}
else {
fn = create_filename( filelist[ file_no ], "err.nfo" );
}
// open file for output
fp = fopen( fn, "w" );
if ( fp == NULL ){
sprintf( errormessage, FWR_ERRMSG );
errorlevel = 2;
return false;
}
free( fn );
// write status and errormessage to file
fprintf( fp, "--> error (level %i) in file \"%s\" <--\n", errorlevel, filelist[ file_no ] );
fprintf( fp, "\n" );
// write error specification to file
fprintf( fp, " %s -> %s:\n", get_status( errorfunction ),
( errorlevel == 1 ) ? "warning" : "error" );
fprintf( fp, " %s\n", errormessage );
// done, close file
fclose( fp );
return true;
}
/* -----------------------------------------------
Writes info to a specific csv file
----------------------------------------------- */
INTERN bool write_file_analysis( void )
{
static const char* fn = FILE_ANALYSIS_CSV;
FILE* fp;
bool labels;
mp3Frame* frame;
granuleInfo* granule;
int total_fixed = 0;
int total_scf = 0;
int total_coef = 0;
int total_aux = 0;
int main_bits;
int cur_scf;
int share = 0;
const int* slen;
int ch, gr;
// check if file exists
labels = !file_exists( fn );
// open file for output
fp = fopen( fn, "a" );
if ( fp == NULL ){
sprintf( errormessage, FWR_ERRMSG );
errorlevel = 2;
return false;
}
// calculate sizes of scf/coef/aux/fixed
for ( frame = firstframe; frame != NULL; frame = frame->next ) {
main_bits = 0; cur_scf = 0;
for ( ch = 0; ch < g_nchannels; ch++ ) for ( gr = 0; gr < 2; gr++ ) {
granule = frame->granules[ch][gr];
slen = slength_table[ (int) granule->slength ];
main_bits += granule->main_data_bit;
if ( granule->block_type != SHORT_BLOCK ) {
if ( ( ( share >> 3 ) & 1 ) == 0 ) cur_scf += slen[ 0 ] * 6;
if ( ( ( share >> 2 ) & 1 ) == 0 ) cur_scf += slen[ 0 ] * 5;
if ( ( ( share >> 1 ) & 1 ) == 0 ) cur_scf += slen[ 1 ] * 5;
if ( ( ( share >> 0 ) & 1 ) == 0 ) cur_scf += slen[ 1 ] * 5;
} else {
cur_scf += slen[ 0 ] * 6 * 3;
cur_scf += slen[ 1 ] * 6 * 3;
}
share = granule->share;
}
total_fixed += frame->fixed_size * 8;
total_scf += cur_scf;
total_coef += main_bits - cur_scf;
total_aux += frame->aux_size * 8;
if ( main_bits % 8 > 0 )
total_aux += 8 - ( main_bits % 8 );
}
// write labels
if ( labels ) {
fprintf( fp, "name;" );
fprintf( fp, "total_bits;" );
fprintf( fp, "tag_bits;" );
fprintf( fp, "fixed_bits;" );
fprintf( fp, "scf_bits;" );
fprintf( fp, "coef_bits;" );
fprintf( fp, "aux_bits;" );
fprintf( fp, "frames;" );
fprintf( fp, "mpeg;" );
fprintf( fp, "layer;" );
fprintf( fp, "samples;" );
fprintf( fp, "bitrate;" );
fprintf( fp, "channels;" );
fprintf( fp, "protection;" );
fprintf( fp, "padding;" );
fprintf( fp, "ms_stereo;" );
fprintf( fp, "int_stereo;" );
fprintf( fp, "private;" );
fprintf( fp, "copyright;" );
fprintf( fp, "original;" );
fprintf( fp, "emphasis;" );
fprintf( fp, "padbits;" );
fprintf( fp, "bitres;" );
fprintf( fp, "sharing;" );
fprintf( fp, "switching;" );
fprintf( fp, "mixed;" );
fprintf( fp, "preemphasis;" );
fprintf( fp, "coarse;" );
fprintf( fp, "sbgain;" );
fprintf( fp, "auxiliary_h;" );
fprintf( fp, "sblock_diff;" );
fprintf( fp, "bad_first" );
fprintf( fp, "\n" );
}
// write data
fprintf( fp, "%s;", mp3filename );
fprintf( fp, "%i;", mp3filesize * 8 );
fprintf( fp, "%i;", ( data_before_size + data_after_size ) * 8 );
fprintf( fp, "%i;", total_fixed );
fprintf( fp, "%i;", total_scf );
fprintf( fp, "%i;", total_coef );
fprintf( fp, "%i;", total_aux );
fprintf( fp, "%i;", g_nframes );
fprintf( fp, "%s;", mpeg_description[(int)i_mpeg] );
fprintf( fp, "%s;", layer_description[(int)i_layer] );
fprintf( fp, "%i;", g_samplerate );
fprintf( fp, "%i;", g_bitrate * 1000 );
fprintf( fp, "%s;", channels_description[(int)i_channels ] );
fprintf( fp, "%i;", i_protection );
fprintf( fp, "%i;", i_padding );
fprintf( fp, "%i;", i_stereo_ms );
fprintf( fp, "%i;", i_stereo_int );
fprintf( fp, "%i;", i_privbit );
fprintf( fp, "%i;", i_copyright );
fprintf( fp, "%i;", i_original );
fprintf( fp, "%i;", i_emphasis );
fprintf( fp, "%i;", i_padbits );
fprintf( fp, "%i;", i_bit_res);
fprintf( fp, "%i;", i_share );
fprintf( fp, "%i;", i_sblocks );
fprintf( fp, "%i;", i_mixed );
fprintf( fp, "%i;", i_preemphasis );
fprintf( fp, "%i;", i_coarse );
fprintf( fp, "%i;", i_sbgain );
fprintf( fp, "%i;", i_aux_h );
fprintf( fp, "%i;", i_sb_diff );
fprintf( fp, "%i", n_bad_first );
fprintf( fp, "\n" );
// close file
fclose( fp );
return true;
}
/* -----------------------------------------------
Writes block analysis to csv
----------------------------------------------- */
INTERN bool write_block_analysis( void )
{
const char* frm_ext = "frm.csv";
const char* ch_ext[2] = { "ch0.csv", "ch1.csv" };
FILE* fp;
char* fn;
mp3Frame* frame;
int ch, gr;
/* --- frames analysis --- */
// create filename
fn = create_filename( filelist[ file_no ], frm_ext );
// open file for output
fp = fopen( fn, "w" );
if ( fp == NULL ){
sprintf( errormessage, FWR_ERRMSG );
errorlevel = 2;
return false;
}
free( fn );
// write labels (header)
fprintf( fp, "frame;" );
fprintf( fp, "main_index;" );
fprintf( fp, "frame_size;" );
fprintf( fp, "fixed_size;" );
fprintf( fp, "bit_reservoir;");
fprintf( fp, "main_bits;" );
fprintf( fp, "main_size;" );
fprintf( fp, "aux_size;" );
fprintf( fp, "mpeg;");
fprintf( fp, "layer;");
fprintf( fp, "samples;");
fprintf( fp, "bitrate;");
fprintf( fp, "channels;");
fprintf( fp, "protection;");
fprintf( fp, "padding;");
fprintf( fp, "ms_stereo;");
fprintf( fp, "int_stereo;");
fprintf( fp, "private;");
fprintf( fp, "copyright;");
fprintf( fp, "original;");
fprintf( fp, "emphasis;");
fprintf( fp, "padbits");
fprintf( fp, "\n" );
// write frame data
for ( frame = firstframe; frame != NULL; frame = frame->next ) {
// header/frame-global info
fprintf( fp, "%i;", frame->n );
fprintf( fp, "%i;", frame->main_index );
fprintf( fp, "%i;", frame->frame_size );
fprintf( fp, "%i;", frame->fixed_size );
fprintf( fp, "%i;", frame->bit_reservoir );
fprintf( fp, "%i;", frame->main_bits );
fprintf( fp, "%i;", frame->main_size );
fprintf( fp, "%i;", frame->aux_size );
fprintf( fp, "%s;", mpeg_description[(int)frame->mpeg ] );
fprintf( fp, "%s;", layer_description[(int)frame->layer ] );
fprintf( fp, "%i;", mp3_samplerate_table[(int)frame->samples] );
fprintf( fp, "%i;", mp3_bitrate_table[(int)frame->bits] * 1000 );
fprintf( fp, "%s;", channels_description[(int)frame->channels ] );
fprintf( fp, "%i;", frame->protection );
fprintf( fp, "%i;", frame->padding );
fprintf( fp, "%i;", frame->stereo_ms );
fprintf( fp, "%i;", frame->stereo_int );
fprintf( fp, "%i;", frame->privbit );
fprintf( fp, "%i;", frame->copyright );
fprintf( fp, "%i;", frame->original );
fprintf( fp, "%i;", frame->emphasis );
fprintf( fp, "%i;", frame->padbits );
fprintf( fp, "\n" );
}
// close file
fclose( fp );
/* --- granules analysis --- */
for ( ch = 0; ch < g_nchannels; ch++ ) {
// create filename
fn = create_filename( filelist[ file_no ], ch_ext[ch] );
// open file for output
fp = fopen( fn, "w" );
if ( fp == NULL ){
sprintf( errormessage, FWR_ERRMSG );
errorlevel = 2;
return false;
}
free( fn );
// labels (granules)
fprintf( fp, "block;" );
fprintf( fp, "ms_stereo;" );
fprintf( fp, "int_stereo;" );
fprintf( fp, "share;" );
fprintf( fp, "main_bit;" );
fprintf( fp, "big_vals;" );
fprintf( fp, "gl_gain;" );
fprintf( fp, "slength;" );
fprintf( fp, "switching;" );
fprintf( fp, "block_type;" );
fprintf( fp, "mixed;" );
fprintf( fp, "preemphasis;" );
fprintf( fp, "coarse;" );
fprintf( fp, "r0_tbl;" );
fprintf( fp, "r1_tbl;" );
fprintf( fp, "r2_tbl;" );
fprintf( fp, "sm_tbl;" );
fprintf( fp, "r0_size;" );
fprintf( fp, "r1_size;" );
fprintf( fp, "r0_bound;" );
fprintf( fp, "r1_bound;" );
fprintf( fp, "r2_bound;" );
fprintf( fp, "sv_bound;" );
fprintf( fp, "sb0_gain;" );
fprintf( fp, "sb1_gain;" );
fprintf( fp, "sb2_gain" );
fprintf( fp, "\n" );
// write granules data
for ( frame = firstframe; frame != NULL; frame = frame->next ) if ( ch < frame->nchannels ) {
for ( gr = 0; gr < 2; gr++ ) {
fprintf( fp, "%i;", frame->granules[ch][gr]->n );
fprintf( fp, "%i;", frame->stereo_ms );
fprintf( fp, "%i;", frame->stereo_int );
fprintf( fp, "0b%i%i%i%i;",
BITN( frame->granules[ch][gr]->share, 3 ),
BITN( frame->granules[ch][gr]->share, 2 ),
BITN( frame->granules[ch][gr]->share, 1 ),
BITN( frame->granules[ch][gr]->share, 0 ) );
fprintf( fp, "%i;", frame->granules[ch][gr]->main_data_bit );
fprintf( fp, "%i;", frame->granules[ch][gr]->big_val_pairs );
fprintf( fp, "%i;", frame->granules[ch][gr]->global_gain );
fprintf( fp, "%i/%i;", slength_table[ (int) frame->granules[ch][gr]->slength ][ 0 ], slength_table[ (int) frame->granules[ch][gr]->slength ][ 1 ] );
fprintf( fp, "%i;", frame->granules[ch][gr]->window_switching );
fprintf( fp, "%s;", blocktype_description[(int)frame->granules[ch][gr]->block_type] );
fprintf( fp, "%i;", frame->granules[ch][gr]->mixed_flag );
fprintf( fp, "%i;", frame->granules[ch][gr]->preemphasis );
fprintf( fp, "%i;", frame->granules[ch][gr]->coarse_scalefactors );
fprintf( fp, "%i;", frame->granules[ch][gr]->region_table[0] );
fprintf( fp, "%i;", frame->granules[ch][gr]->region_table[1] );
fprintf( fp, "%i;", frame->granules[ch][gr]->region_table[2] );
fprintf( fp, "%i;", frame->granules[ch][gr]->select_htabB );
fprintf( fp, "%i;", frame->granules[ch][gr]->region0_size );
fprintf( fp, "%i;", frame->granules[ch][gr]->region1_size );
fprintf( fp, "%i;", frame->granules[ch][gr]->region_bound[0] );
fprintf( fp, "%i;", frame->granules[ch][gr]->region_bound[1] );
fprintf( fp, "%i;", frame->granules[ch][gr]->region_bound[2] );
fprintf( fp, "%i;", frame->granules[ch][gr]->sv_bound );
fprintf( fp, "%i;", frame->granules[ch][gr]->sb_gain[0] );
fprintf( fp, "%i;", frame->granules[ch][gr]->sb_gain[1] );
fprintf( fp, "%i", frame->granules[ch][gr]->sb_gain[2] );
fprintf( fp, "\n" );
}
}
// close file
fclose( fp );
}
return true;
}
/* -----------------------------------------------
Writes statistic info to a specific csv file
----------------------------------------------- */
INTERN bool write_stat_analysis( void )
{
static const char* fn = STAT_ANALYSIS_CSV;
FILE* fp;
bool labels;
huffman_reader* decoder;
mp3Frame* frame;
granuleInfo* granule;
granuleData*** frame_data;
int ch, gr;
int i;
unsigned int coef_stats[ 16 ] = { 0 };
unsigned int coefb_stats[ 15 ] = { 0 };
unsigned int scf_stats[ 16 ] = { 0 };
unsigned int bv_tbl_stats[ 32 ] = { 0 };
unsigned int sv_tbl_stats[ 2 ] = { 0 };
unsigned int blt_stats[ 4 ] = { 0 };
unsigned int n_bad_bv_bound = 0;
unsigned int n_bad_sv_bound = 0;
int bv_bound_max_dist = 0;
int sv_bound_max_dist = 0;
int bv_bound_avrg = 0;
int sv_bound_avrg = 0;
int bad_code = 0;
int r2bvs_avrg = 0;
// check if file exists
labels = !file_exists( fn );
// open file for output
fp = fopen( fn, "a" );
if ( fp == NULL ){
sprintf( errormessage, FWR_ERRMSG );
errorlevel = 2;
return false;
}
// write labels
if ( labels ) {
fprintf( fp, "name;" );
fprintf( fp, "n_frames;" );
fprintf( fp, "muted;" );
fprintf( fp, "bad_code;" );
for ( i = 0; i < 15; i++ )
fprintf( fp, "cf=%i;", i );
fprintf( fp, "cf>=15;" );
for ( i = 0; i < 15; i++ )
fprintf( fp, "cb=%i;", i );
for ( i = 0; i < 16; i++ )
fprintf( fp, "sc=%i;", i );
for ( i = 0; i < 32; i++ )
fprintf( fp, "bv_tbl%i;", i );
fprintf( fp, "sv_tblA;" );
fprintf( fp, "sv_tblB;" );
fprintf( fp, "long;" );
fprintf( fp, "start;" );
fprintf( fp, "short;" );
fprintf( fp, "stop;" );
fprintf( fp, "n_bad_bv;" );
fprintf( fp, "bad_bv_lim;" );
fprintf( fp, "bad_bv_avr;" );
fprintf( fp, "n_bad_sv;" );
fprintf( fp, "bad_sv_lim;" );
fprintf( fp, "bad_sv_avr;" );
fprintf( fp, "reg2_bv_avr" );
fprintf( fp, "\n" );
}
// init decoder
decoder = new huffman_reader( main_data, main_data_size );
// run statistical analysis
for ( frame = firstframe; frame != NULL; frame = frame->next ) {
// skip bad first frames
if ( frame->n < n_bad_first ) continue;
frame_data = mp3_decode_frame( decoder, frame );
if ( frame_data == NULL ) bad_code++;
for ( ch = 0; ch < frame->nchannels; ch++ ) {
for ( gr = 0; gr < 2; gr++ ) {
granule = frame->granules[ch][gr];
// simple stuff first
bv_tbl_stats[ (int) granule->region_table[0] ]++;
bv_tbl_stats[ (int) granule->region_table[1] ]++;
sv_tbl_stats[ (int) granule->select_htabB ]++;
blt_stats[ (int) granule->block_type ]++;
if ( !granule->window_switching )
bv_tbl_stats[ (int) granule->region_table[2] ]++;
if ( frame_data == NULL ) continue;
// count coefficients and scalefactors
if ( granule->block_type != SHORT_BLOCK ) {
for ( i = 0; i < 21; i++ )
scf_stats[ (int) frame_data[ch][gr]->scalefactors[i] ]++;
} else {
for ( i = 0; i < 36; i++ )
scf_stats[ (int) frame_data[ch][gr]->scalefactors[i] ]++;
}
for ( i = 0; i < 576; i++ ) {
if ( ABS(frame_data[ch][gr]->coefficients[i]) < 16 )
coef_stats[ ABS(frame_data[ch][gr]->coefficients[i]) ]++;
else coef_stats[ 15 ]++;
coefb_stats[ BITLEN8224N(frame_data[ch][gr]->coefficients[i]) ]++;
}
// check for bad sv bounds
for ( i = 0; i < granule->sv_bound; i++ )
if ( frame_data[ch][gr]->coefficients[granule->sv_bound-i-1] != 0 ) break;
i /= 4;
if ( i > 0 ) {
n_bad_sv_bound++;
sv_bound_avrg += i;
if ( sv_bound_max_dist < i ) sv_bound_max_dist = i;
}
// done if zero table
// if ( granule->window_switching ) if ( granule->region_table[2] == 0 ) continue;
// else if ( granule->region_table[1] == 0 ) continue;
// check for bad bv bounds
for ( i = 0; i < granule->region_bound[2]; i++ )
if ( ABS(frame_data[ch][gr]->coefficients[granule->region_bound[2]-i-1]) > 1 ) break;
i /= 2;
if ( i > 1 ) {
n_bad_bv_bound++;
bv_bound_avrg += i;
if ( bv_bound_max_dist < i ) bv_bound_max_dist = i;
}
// count region 2 coefficients ABS > 2
for ( i = granule->region_bound[(granule->window_switching)?0:1]; i < granule->region_bound[2]; i++ )
if ( ABS(frame_data[ch][gr]->coefficients[i]) > 1 ) r2bvs_avrg++;
}
}
}
sv_bound_avrg = ( n_bad_sv_bound > 0 ) ? sv_bound_avrg / n_bad_sv_bound : 0;
bv_bound_avrg = ( n_bad_bv_bound > 0 ) ? bv_bound_avrg / n_bad_bv_bound : 0;
r2bvs_avrg = r2bvs_avrg / ( g_nframes * 2 * g_nchannels );
// done analyzing, delete decoder
delete( decoder );
// write data
fprintf( fp, "%s;", mp3filename );
fprintf( fp, "%i;", g_nframes );
fprintf( fp, "%i;", n_bad_first );
fprintf( fp, "%i;", bad_code );
for ( i = 0; i < 16; i++ )
fprintf( fp, "%i;", (int) coef_stats[i] );
for ( i = 0; i < 15; i++ )
fprintf( fp, "%i;", (int) coefb_stats[i] );
for ( i = 0; i < 16; i++ )
fprintf( fp, "%i;", (int) scf_stats[i] );
for ( i = 0; i < 32; i++ )
fprintf( fp, "%i;", (int) bv_tbl_stats[i] );
for ( i = 0; i < 2; i++ )
fprintf( fp, "%i;", (int) sv_tbl_stats[i] );
for ( i = 0; i < 4; i++ )
fprintf( fp, "%i;", (int) blt_stats[i] );
fprintf( fp, "%i;", (int) n_bad_bv_bound );
fprintf( fp, "%i;", bv_bound_max_dist );
fprintf( fp, "%i;", bv_bound_avrg );
fprintf( fp, "%i;", (int) n_bad_sv_bound );
fprintf( fp, "%i;", sv_bound_max_dist );
fprintf( fp, "%i;", sv_bound_avrg );
fprintf( fp, "%i", r2bvs_avrg );
fprintf( fp, "\n" );
// close file
fclose( fp );
return true;
}
/* -----------------------------------------------
Make a PGM of header/sideinfo data
----------------------------------------------- */
INTERN bool visualize_headers( void )
{
static const int img_width = 1280; // must be divisible by 2
static const bool inc_all = false;
static unsigned char* line = (unsigned char*) calloc ( img_width, sizeof( char ) );
mp3Frame* frame;
mp3Frame* frame0 = firstframe;
mp3Frame* frame1 = firstframe;
int i;
FILE* fp;
char* fn;
// create filename
fn = create_filename( filelist[ file_no ], "headers.pgm" );
// open file for output
fp = fopen( fn, "wb" );
if ( fp == NULL ){
sprintf( errormessage, FWR_ERRMSG );
errorlevel = 2;
return false;
}
free( fn );
// write PGM header
fprintf( fp, "P5\n" );
fprintf( fp, "# created by %s v%i.%i%s (%s) by %s\n",
appname, appversion / 10, appversion % 10, subversion, versiondate, author );
fprintf( fp, "%i %i\n", img_width, ( ((g_nframes*2) + img_width - 1) / img_width ) * ( inc_all ? 60 : 40 ) );
fprintf( fp, "255\n" );
do { // write data, line per line, 2x2 pixels for header stuff
memset ( line, 0x0, img_width );
frame0 = frame1;
for ( i = 0; i < (img_width/2); i++ ) {
frame1 = frame1->next;
if ( frame1 == NULL ) break;
}
i = frame0->n;
if ( inc_all ) {
for ( frame = frame0; frame != frame1; frame = frame->next ) {
line[ (frame->n-i) * 2 ] = frame->mpeg << 6;
line[ (frame->n-i) * 2 + 1 ] = line[ (frame->n-i) * 2 ];
} // 2 (MPEG)
fwrite( line, 1, img_width, fp );
fwrite( line, 1, img_width, fp );
} // -2
if ( inc_all ) {
for ( frame = frame0; frame != frame1; frame = frame->next ) {
line[ (frame->n-i) * 2 ] = frame->layer << 6;
line[ (frame->n-i) * 2 + 1 ] = line[ (frame->n-i) * 2 ];
} // 4 (LAYER)
fwrite( line, 1, img_width, fp );
fwrite( line, 1, img_width, fp );
} // -4
if ( inc_all ) {
for ( frame = frame0; frame != frame1; frame = frame->next ) {
line[ (frame->n-i) * 2 ] = frame->channels << 6;
line[ (frame->n-i) * 2 + 1 ] = line[ (frame->n-i) * 2 ];
} // 6 (CHANNELS)
fwrite( line, 1, img_width, fp );
fwrite( line, 1, img_width, fp );
} // -6
if ( inc_all ) {
for ( frame = frame0; frame != frame1; frame = frame->next ) {
line[ (frame->n-i) * 2 ] = frame->samples << 6;
line[ (frame->n-i) * 2 + 1 ] = line[ (frame->n-i) * 2 ];
} // 8 (SAMPLES)
fwrite( line, 1, img_width, fp );
fwrite( line, 1, img_width, fp );
} // -8
for ( frame = frame0; frame != frame1; frame = frame->next ) {
line[ (frame->n-i) * 2 ] = frame->bits << 4;
line[ (frame->n-i) * 2 + 1 ] = line[ (frame->n-i) * 2 ];
} // 10 (BITRATE)
fwrite( line, 1, img_width, fp );
fwrite( line, 1, img_width, fp );
if ( inc_all ) {
for ( frame = frame0; frame != frame1; frame = frame->next ) {
line[ (frame->n-i) * 2 ] = ( frame->protection == 0 ) ? 0 : 255;
line[ (frame->n-i) * 2 + 1 ] = line[ (frame->n-i) * 2 ];
} // 12 (PROTECTION)
fwrite( line, 1, img_width, fp );
fwrite( line, 1, img_width, fp );
} // -10
for ( frame = frame0; frame != frame1; frame = frame->next ) {
line[ (frame->n-i) * 2 ] = ( frame->padding == 0 ) ? 0 : 255;
line[ (frame->n-i) * 2 + 1 ] = line[ (frame->n-i) * 2 ];
} // 14 (PADDING)
fwrite( line, 1, img_width, fp );
fwrite( line, 1, img_width, fp );
if ( inc_all ) {
for ( frame = frame0; frame != frame1; frame = frame->next ) {
line[ (frame->n-i) * 2 ] = ( frame->privbit == 0 ) ? 0 : 255;
line[ (frame->n-i) * 2 + 1 ] = line[ (frame->n-i) * 2 ];
} // 16 (PRIVATE)
fwrite( line, 1, img_width, fp );
fwrite( line, 1, img_width, fp );
} // -12
if ( inc_all ) {
for ( frame = frame0; frame != frame1; frame = frame->next ) {
line[ (frame->n-i) * 2 ] = ( frame->copyright == 0 ) ? 0 : 255;
line[ (frame->n-i) * 2 + 1 ] = line[ (frame->n-i) * 2 ];
} // 18 (COPYRIGHT)
fwrite( line, 1, img_width, fp );
fwrite( line, 1, img_width, fp );
} // -14
if ( inc_all ) {
for ( frame = frame0; frame != frame1; frame = frame->next ) {
line[ (frame->n-i) * 2 ] = ( frame->original == 0 ) ? 0 : 255;
line[ (frame->n-i) * 2 + 1 ] = line[ (frame->n-i) * 2 ];
} // 20 (ORIGINAL)
fwrite( line, 1, img_width, fp );
fwrite( line, 1, img_width, fp );
} // -16
for ( frame = frame0; frame != frame1; frame = frame->next ) {
line[ (frame->n-i) * 2 ] = ( frame->stereo_ms == 0 ) ? 0 : 255;
line[ (frame->n-i) * 2 + 1 ] = line[ (frame->n-i) * 2 ];
} // 22 (MS STEREO)
fwrite( line, 1, img_width, fp );
fwrite( line, 1, img_width, fp );
if ( inc_all ) {
for ( frame = frame0; frame != frame1; frame = frame->next ) {
line[ (frame->n-i) * 2 ] = ( frame->stereo_int == 0 ) ? 0 : 255;
line[ (frame->n-i) * 2 + 1 ] = line[ (frame->n-i) * 2 ];
} // 24 (INT STEREO)
fwrite( line, 1, img_width, fp );
fwrite( line, 1, img_width, fp );
} // -18
if ( inc_all ) {
for ( frame = frame0; frame != frame1; frame = frame->next ) {
line[ (frame->n-i) * 2 ] = frame->emphasis << 6;
line[ (frame->n-i) * 2 + 1 ] = line[ (frame->n-i) * 2 ];
} // 26 (EMPHASIS)
fwrite( line, 1, img_width, fp );
fwrite( line, 1, img_width, fp );
} // -20
for ( frame = frame0; frame != frame1; frame = frame->next ) {
line[ (frame->n-i) * 2 + 0 ] = frame->granules[0][0]->main_data_bit >> 4;
line[ (frame->n-i) * 2 + 1 ] = frame->granules[0][1]->main_data_bit >> 4;
}
fwrite( line, 1, img_width, fp ); // 27 (MAIN DATA BIT)
if ( g_nchannels == 2 ) {
for ( frame = frame0; frame != frame1; frame = frame->next ) {
line[ (frame->n-i) * 2 + 0 ] = frame->granules[1][0]->main_data_bit >> 4;
line[ (frame->n-i) * 2 + 1 ] = frame->granules[1][1]->main_data_bit >> 4;
}
}
fwrite( line, 1, img_width, fp ); // 28 (MAIN DATA BIT)
for ( frame = frame0; frame != frame1; frame = frame->next ) {
line[ (frame->n-i) * 2 + 0 ] = frame->granules[0][0]->big_val_pairs >> 1;
line[ (frame->n-i) * 2 + 1 ] = frame->granules[0][1]->big_val_pairs >> 1;
}
fwrite( line, 1, img_width, fp ); // 29 (BIG VALUES)
if ( g_nchannels == 2 ) {
for ( frame = frame0; frame != frame1; frame = frame->next ) {
line[ (frame->n-i) * 2 + 0 ] = frame->granules[1][0]->big_val_pairs >> 1;
line[ (frame->n-i) * 2 + 1 ] = frame->granules[1][1]->big_val_pairs >> 1;
}
}
fwrite( line, 1, img_width, fp ); // 30 (BIG VALUES)
for ( frame = frame0; frame != frame1; frame = frame->next ) {
line[ (frame->n-i) * 2 + 0 ] = frame->granules[0][0]->share << 4;
line[ (frame->n-i) * 2 + 1 ] = frame->granules[0][1]->share << 4;
}
fwrite( line, 1, img_width, fp ); // 31 (SHARING)
if ( g_nchannels == 2 ) {
for ( frame = frame0; frame != frame1; frame = frame->next ) {
line[ (frame->n-i) * 2 + 0 ] = frame->granules[1][0]->share << 4;
line[ (frame->n-i) * 2 + 1 ] = frame->granules[1][1]->share << 4;
}
}
fwrite( line, 1, img_width, fp ); // 32 (SHARING)
for ( frame = frame0; frame != frame1; frame = frame->next ) {
line[ (frame->n-i) * 2 + 0 ] = frame->granules[0][0]->global_gain;
line[ (frame->n-i) * 2 + 1 ] = frame->granules[0][1]->global_gain;
}
fwrite( line, 1, img_width, fp ); // 33 (GLOBAL GAIN)
if ( g_nchannels == 2 ) {
for ( frame = frame0; frame != frame1; frame = frame->next ) {
line[ (frame->n-i) * 2 + 0 ] = frame->granules[1][0]->global_gain;
line[ (frame->n-i) * 2 + 1 ] = frame->granules[1][1]->global_gain;
}
}
fwrite( line, 1, img_width, fp ); // 34 (GLOBAL GAIN)
for ( frame = frame0; frame != frame1; frame = frame->next ) {
line[ (frame->n-i) * 2 + 0 ] = frame->granules[0][0]->slength << 4;
line[ (frame->n-i) * 2 + 1 ] = frame->granules[0][1]->slength << 4;
}
fwrite( line, 1, img_width, fp ); // 35 (SLENGTH)
if ( g_nchannels == 2 ) {
for ( frame = frame0; frame != frame1; frame = frame->next ) {
line[ (frame->n-i) * 2 + 0 ] = frame->granules[1][0]->slength << 4;
line[ (frame->n-i) * 2 + 1 ] = frame->granules[1][1]->slength << 4;
}
}
fwrite( line, 1, img_width, fp ); // 36 (SLENGTH)
for ( frame = frame0; frame != frame1; frame = frame->next ) {
line[ (frame->n-i) * 2 + 0 ] = frame->granules[0][0]->block_type << 6;
line[ (frame->n-i) * 2 + 1 ] = frame->granules[0][1]->block_type << 6;
}
fwrite( line, 1, img_width, fp ); // 37 (BLOCK TYPE)
if ( g_nchannels == 2 ) {
for ( frame = frame0; frame != frame1; frame = frame->next ) {
line[ (frame->n-i) * 2 + 0 ] = frame->granules[1][0]->block_type << 6;
line[ (frame->n-i) * 2 + 1 ] = frame->granules[1][1]->block_type << 6;
}
}
fwrite( line, 1, img_width, fp ); // 38 (BLOCK TYPE)
for ( frame = frame0; frame != frame1; frame = frame->next ) {
line[ (frame->n-i) * 2 + 0 ] = frame->granules[0][0]->preemphasis * 255;
line[ (frame->n-i) * 2 + 1 ] = frame->granules[0][1]->preemphasis * 255;
}
fwrite( line, 1, img_width, fp ); // 39 (PREEMPHASIS)
if ( g_nchannels == 2 ) {
for ( frame = frame0; frame != frame1; frame = frame->next ) {
line[ (frame->n-i) * 2 + 0 ] = frame->granules[1][0]->preemphasis * 255;
line[ (frame->n-i) * 2 + 1 ] = frame->granules[1][1]->preemphasis * 255;
}
}
fwrite( line, 1, img_width, fp ); // 40 (PREEMPHASIS)
for ( frame = frame0; frame != frame1; frame = frame->next ) {
line[ (frame->n-i) * 2 + 0 ] = frame->granules[0][0]->coarse_scalefactors * 255;
line[ (frame->n-i) * 2 + 1 ] = frame->granules[0][1]->coarse_scalefactors * 255;
}
fwrite( line, 1, img_width, fp ); // 41 (COARSE)
if ( g_nchannels == 2 ) {
for ( frame = frame0; frame != frame1; frame = frame->next ) {
line[ (frame->n-i) * 2 + 0 ] = frame->granules[1][0]->coarse_scalefactors * 255;
line[ (frame->n-i) * 2 + 1 ] = frame->granules[1][1]->coarse_scalefactors * 255;
}
}
fwrite( line, 1, img_width, fp ); // 42 (COARSE)
for ( frame = frame0; frame != frame1; frame = frame->next ) {
line[ (frame->n-i) * 2 + 0 ] = frame->granules[0][0]->region_table[0] << 3;
line[ (frame->n-i) * 2 + 1 ] = frame->granules[0][1]->region_table[0] << 3;
}
fwrite( line, 1, img_width, fp ); // 43 (R0 TABLE)
if ( g_nchannels == 2 ) {
for ( frame = frame0; frame != frame1; frame = frame->next ) {
line[ (frame->n-i) * 2 + 0 ] = frame->granules[1][0]->region_table[0] << 3;
line[ (frame->n-i) * 2 + 1 ] = frame->granules[1][1]->region_table[0] << 3;
}
}
fwrite( line, 1, img_width, fp ); // 44 (R0 TABLE)
for ( frame = frame0; frame != frame1; frame = frame->next ) {
line[ (frame->n-i) * 2 + 0 ] = frame->granules[0][0]->region_table[1] << 3;
line[ (frame->n-i) * 2 + 1 ] = frame->granules[0][1]->region_table[1] << 3;
}
fwrite( line, 1, img_width, fp ); // 45 (R1 TABLE)
if ( g_nchannels == 2 ) {
for ( frame = frame0; frame != frame1; frame = frame->next ) {
line[ (frame->n-i) * 2 + 0 ] = frame->granules[1][0]->region_table[1] << 3;
line[ (frame->n-i) * 2 + 1 ] = frame->granules[1][1]->region_table[1] << 3;
}
}
fwrite( line, 1, img_width, fp ); // 46 (R1 TABLE)
for ( frame = frame0; frame != frame1; frame = frame->next ) {
line[ (frame->n-i) * 2 + 0 ] = frame->granules[0][0]->region_table[2] << 3;
line[ (frame->n-i) * 2 + 1 ] = frame->granules[0][1]->region_table[2] << 3;
}
fwrite( line, 1, img_width, fp ); // 47 (R2 TABLE)
if ( g_nchannels == 2 ) {
for ( frame = frame0; frame != frame1; frame = frame->next ) {
line[ (frame->n-i) * 2 + 0 ] = frame->granules[1][0]->region_table[2] << 3;
line[ (frame->n-i) * 2 + 1 ] = frame->granules[1][1]->region_table[2] << 3;
}
}
fwrite( line, 1, img_width, fp ); // 48 (R2 TABLE)
for ( frame = frame0; frame != frame1; frame = frame->next ) {
line[ (frame->n-i) * 2 + 0 ] = frame->granules[0][0]->select_htabB * 255;
line[ (frame->n-i) * 2 + 1 ] = frame->granules[0][1]->select_htabB * 255;
}
fwrite( line, 1, img_width, fp ); // 49 (R4 TABLE)
if ( g_nchannels == 2 ) {
for ( frame = frame0; frame != frame1; frame = frame->next ) {
line[ (frame->n-i) * 2 + 0 ] = frame->granules[1][0]->select_htabB * 255;
line[ (frame->n-i) * 2 + 1 ] = frame->granules[1][1]->select_htabB * 255;
}
}
fwrite( line, 1, img_width, fp ); // 50 (R4 TABLE)
for ( frame = frame0; frame != frame1; frame = frame->next ) {
line[ (frame->n-i) * 2 + 0 ] = frame->granules[0][0]->region0_size << 4;
line[ (frame->n-i) * 2 + 1 ] = frame->granules[0][1]->region0_size << 4;
}
fwrite( line, 1, img_width, fp ); // 51 (R0 SIZE)
if ( g_nchannels == 2 ) {
for ( frame = frame0; frame != frame1; frame = frame->next ) {
line[ (frame->n-i) * 2 + 0 ] = frame->granules[1][0]->region0_size << 4;
line[ (frame->n-i) * 2 + 1 ] = frame->granules[1][1]->region0_size << 4;
}
}
fwrite( line, 1, img_width, fp ); // 52 (R0 SIZE)
for ( frame = frame0; frame != frame1; frame = frame->next ) {
line[ (frame->n-i) * 2 + 0 ] = frame->granules[0][0]->region1_size << 4;
line[ (frame->n-i) * 2 + 1 ] = frame->granules[0][1]->region1_size << 4;
}
fwrite( line, 1, img_width, fp ); // 53 (R1 SIZE)
if ( g_nchannels == 2 ) {
for ( frame = frame0; frame != frame1; frame = frame->next ) {
line[ (frame->n-i) * 2 + 0 ] = frame->granules[1][0]->region1_size << 4;
line[ (frame->n-i) * 2 + 1 ] = frame->granules[1][1]->region1_size << 4;
}
}
fwrite( line, 1, img_width, fp ); // 54 (R1 SIZE)
for ( frame = frame0; frame != frame1; frame = frame->next ) {
line[ (frame->n-i) * 2 + 0 ] = frame->granules[0][0]->sb_gain[0] << 5;
line[ (frame->n-i) * 2 + 1 ] = frame->granules[0][1]->sb_gain[0] << 5;
}
fwrite( line, 1, img_width, fp ); // 55 (SB0 GAIN)
if ( g_nchannels == 2 ) {
for ( frame = frame0; frame != frame1; frame = frame->next ) {
line[ (frame->n-i) * 2 + 0 ] = frame->granules[1][0]->sb_gain[0] << 5;
line[ (frame->n-i) * 2 + 1 ] = frame->granules[1][1]->sb_gain[0] << 5;
}
}
fwrite( line, 1, img_width, fp ); // 56 (SB0 GAIN)
for ( frame = frame0; frame != frame1; frame = frame->next ) {
line[ (frame->n-i) * 2 + 0 ] = frame->granules[0][0]->sb_gain[1] << 5;
line[ (frame->n-i) * 2 + 1 ] = frame->granules[0][1]->sb_gain[1] << 5;
}
fwrite( line, 1, img_width, fp ); // 57 (SB1 GAIN)
if ( g_nchannels == 2 ) {
for ( frame = frame0; frame != frame1; frame = frame->next ) {
line[ (frame->n-i) * 2 + 0 ] = frame->granules[1][0]->sb_gain[1] << 5;
line[ (frame->n-i) * 2 + 1 ] = frame->granules[1][1]->sb_gain[1] << 5;
}
}
fwrite( line, 1, img_width, fp ); // 58 (SB1 GAIN)
for ( frame = frame0; frame != frame1; frame = frame->next ) {
line[ (frame->n-i) * 2 + 0 ] = frame->granules[0][0]->sb_gain[2] << 5;
line[ (frame->n-i) * 2 + 1 ] = frame->granules[0][1]->sb_gain[2] << 5;
}
fwrite( line, 1, img_width, fp ); // 59 (SB2 GAIN)
if ( g_nchannels == 2 ) {
for ( frame = frame0; frame != frame1; frame = frame->next ) {
line[ (frame->n-i) * 2 + 0 ] = frame->granules[1][0]->sb_gain[2] << 5;
line[ (frame->n-i) * 2 + 1 ] = frame->granules[1][1]->sb_gain[2] << 5;
}
}
fwrite( line, 1, img_width, fp ); // 60 (SB2 GAIN)
} while ( frame1 != NULL );
// close file
fclose( fp );
return true;
}
/* -----------------------------------------------
Make a PGM of coefs/scalefactors
----------------------------------------------- */
INTERN bool visualize_decoded_data( void )
{
static const int nfs = 2;
static const int nfc = 9;
static const unsigned char v_types[4][2] = {
{ 0xFF, 0xFF }, { 0xFF, 0x00 },
{ 0x00, 0x00 }, { 0x00, 0xFF }
};
static const unsigned char v_unk = 0x7F;
static const unsigned char v_zero = 0x00;
FILE* fp[nfs+nfc];
const char* ext[nfs+nfc] = {
"scale.unt.pgm",
"scale.nrm.pgm",
"coefs.f15.pgm",
"coefs.sgn.pgm",
"coefs.l15.pgm",
"coefs.len.pgm",
"coefs.nzz.pgm",
"coefs.reg.pgm",
"coefs.tbl.pgm",
"coefs.bsb.pgm",
"coefs.nnz.pgm"
};
char* fn;
int total_cf, total_sc;
int width_cf, width_sc;
int height_cf, height_sc;
mp3Frame* frame;
granuleInfo* granule;
granuleData*** frame_data;
huffman_reader* decoder;
unsigned char* data_cf[nfc];
unsigned char* data_sc[nfs];
unsigned char* temp_cf;
unsigned char* temp_sc;
int temp_reg[15];
int tmp;
int gr, ch;
int blt;
int n_sc, n_cf;
int i, r;
// open files
for ( i = 0; i < (nfs+nfc); i++ ) {
fn = create_filename( filelist[ file_no ], ext[i] );
// open file for output
fp[i] = fopen( fn, "wb" );
free( fn );
if ( fp[i] == NULL ) {
for ( i--; i >= 0; i-- ) fclose( fp[i] );
sprintf( errormessage, FWR_ERRMSG );
errorlevel = 2;
return false;
}
}
// decide widths for both (try reaching 4:3 ratio)
total_cf = g_nframes * g_nchannels * 2 * ( 576 + 2 + 2 + 2 );
total_sc = g_nframes * g_nchannels * 2 * ( 32 + 2 + 2 + 2 );
height_cf = ( 576 + 2 + 2 + 2 ) * 2;
for ( i = 2; ( 582 * 582 * i * i ) <= total_cf; i += 2 )
height_cf = ( 576 + 2 + 2 + 2 ) * i;
height_sc = ( 32 + 2 + 2 + 2 ) * 2;
for ( i = 2; ( 38 * 38 * i * i ) <= total_sc; i += 2 )
height_sc = ( 32 + 2 + 2 + 2 ) * i;
width_cf = ( total_cf + height_cf - 1 ) / height_cf;
width_sc = ( total_sc + height_sc - 1 ) / height_sc;
if ( width_cf % 2 == 1 ) width_cf++;
if ( width_sc % 2 == 1 ) width_sc++;
// write pgm headers
for ( i = 0; i < (nfs+nfc); i++ ) {
fprintf( fp[i], "P5\n" );
fprintf( fp[i], "# created by %s v%i.%i%s (%s) by %s\n",
appname, appversion / 10, appversion % 10, subversion, versiondate, author );
fprintf( fp[i], "%i %i\n", (i>=nfs)?width_cf:width_sc, (i>=nfs)?height_cf:height_sc );
fprintf( fp[i], "255\n" );
}
// alloc memory
temp_sc = ( unsigned char* ) calloc( 32, sizeof( char ) );
temp_cf = ( unsigned char* ) calloc( 576, sizeof( char ) );
for ( i = 0; i < nfs; i++ ) {
data_sc[i] = ( unsigned char* ) calloc( width_sc * g_nchannels * 38, sizeof( char ) );
if ( data_sc[i] == NULL ) {
free( temp_sc ); free( temp_cf );
for ( i = (nfs+nfc) - 1; i >= 0; i-- ) fclose( fp[i] );
sprintf( errormessage, MEM_ERRMSG );
errorlevel = 2;
return false;
}
}
for ( i = 0; i < nfc; i++ ) {
data_cf[i] = ( unsigned char* ) calloc( width_cf * g_nchannels * 582, sizeof( char ) );
if ( data_cf[i] == NULL ) {
free( temp_sc ); free( temp_cf );
for ( i = (nfs+nfc) - 1; i >= 0; i-- ) fclose( fp[i] );
sprintf( errormessage, MEM_ERRMSG );
errorlevel = 2;
return false;
}
}
// init decoder
decoder = new huffman_reader( main_data, main_data_size );
// main processing loop
for ( n_sc = 0, n_cf = 0, frame = firstframe; frame != NULL; frame = frame->next ) {
frame_data = ( frame->n >= n_bad_first ) ? mp3_decode_frame( decoder, frame ) : NULL;
for ( gr = 0; gr < 2; gr++, n_sc++, n_cf++ ) {
if ( frame_data != NULL ) for ( ch = 0; ch < g_nchannels; ch++ ) {
granule = frame->granules[ch][gr];
// block type
blt = frame->granules[ch][gr]->block_type;
// scalefactors visualization
for ( i = 0; i < nfs; i++ ) {
switch ( i ) {
case 0: // untouched scalefactors
for ( r = ( blt == SHORT_BLOCK ) ? 31 : 20; r >= 0; r-- )
temp_sc[r] = frame_data[ch][gr]->scalefactors[ r ] * 16;
break;
case 1: // normalized scalefactors
for ( r = ( blt == SHORT_BLOCK ) ? 31 : 20; r >= 0; r-- ) {
temp_sc[r] = frame_data[ch][gr]->scalefactors[ r ];
if ( granule->preemphasis && ( granule->block_type != SHORT_BLOCK ) )
temp_sc[r] += preemphasis_table[ i ];
if ( granule->coarse_scalefactors )
temp_sc[r] <<= 1;
temp_sc[r] *= 6;
}
break;
}
data_sc[i][ ( ( 0 + (ch*40) ) * width_sc ) + n_sc ] = v_types[blt][0];
data_sc[i][ ( ( 1 + (ch*40) ) * width_sc ) + n_sc ] = v_types[blt][1];
data_sc[i][ ( ( 34 + (ch*40) ) * width_sc ) + n_sc ] = v_types[blt][0];
data_sc[i][ ( ( 35 + (ch*40) ) * width_sc ) + n_sc ] = v_types[blt][1];
if ( ch == 0 ) for ( r = ( blt == SHORT_BLOCK ) ? 31 : 20; r >= 0; r-- )
data_sc[i][ ( ( 2 + 31 - r ) * width_sc ) + n_sc ] = temp_sc[r];
else for ( r = ( blt == SHORT_BLOCK ) ? 31 : 20; r >= 0; r-- )
data_sc[i][ ( ( 40 + 2 + r ) * width_sc ) + n_sc ] = temp_sc[r];
if ( g_nchannels == 1 ) {
data_sc[i][ ( 36 * width_sc ) + n_sc ] = granule->global_gain;
data_sc[i][ ( 37 * width_sc ) + n_sc ] = granule->global_gain;
} else if ( ch == 0 ) {
data_sc[i][ ( 36 * width_sc ) + n_sc ] = granule->global_gain;
data_sc[i][ ( 37 * width_sc ) + n_sc ] = frame->stereo_ms * 255;
} else {
data_sc[i][ ( 38 * width_sc ) + n_sc ] = frame->stereo_ms * 255;
data_sc[i][ ( 39 * width_sc ) + n_sc ] = granule->global_gain;
}
}
// coefficients visualization
for ( i = 0; i < nfc; i++ ) {
switch ( i ) {
case 0: // fixed 4 bit part
for ( r = 0; r < 576; r++ ) temp_cf[r] =
( frame_data[ch][gr]->coefficients[ r ] > 15 ) ? 15 * 16:
( frame_data[ch][gr]->coefficients[ r ] < -15 ) ? 15 * 16:
( frame_data[ch][gr]->coefficients[ r ] >= 0 ) ?
frame_data[ch][gr]->coefficients[ r ] * 16:
-frame_data[ch][gr]->coefficients[ r ] * 16;
break;
case 1: // sign
for ( r = 0; r < 576; r++ ) temp_cf[r] =
( frame_data[ch][gr]->coefficients[ r ] == 0 ) ? 128 :
( frame_data[ch][gr]->coefficients[ r ] > 0 ) ? 255 : 0;
break;
case 2: // 4 bit truncated bitlength
for ( r = 0; r < 576; r++ ) temp_cf[r] =
( frame_data[ch][gr]->coefficients[ r ] > 15 ) ?
BITLEN8192P( ( frame_data[ch][gr]->coefficients[ r ] - 15 ) ) * 18 :
( frame_data[ch][gr]->coefficients[ r ] < -15 ) ?
BITLEN8192P( ( -frame_data[ch][gr]->coefficients[ r ] - 15 ) ) * 18 : 0;
break;
case 3: // full bitlength
for ( r = 0; r < 576; r++ ) temp_cf[r] =
BITLEN8224N( frame_data[ch][gr]->coefficients[ r ] ) * 19;
break;
case 4: // zeroes - non-zeroes
for ( r = 0; r < 576; r++ ) temp_cf[r] =
( frame_data[ch][gr]->coefficients[ r ] == 0 ) ? 0 : 128;
break;
case 5: // regions
for ( r = 0; r < 576; r++ ) temp_cf[r] =
( r < granule->region_bound[0] ) ? 255 :
( r < granule->region_bound[1] ) ? 191 :
( r < granule->region_bound[2] ) ? 131 :
( r < granule->sv_bound ) ? 63 : 0;
break;
case 6: // tables
for ( r = 0; r < 576; r++ ) temp_cf[r] =
( r < granule->region_bound[0] ) ? granule->region_table[0] * 7 + 21 :
( r < granule->region_bound[1] ) ? granule->region_table[1] * 7 + 21 :
( r < granule->region_bound[2] ) ? granule->region_table[2] * 7 + 21 :
( r < granule->sv_bound ) ? granule->select_htabB * 7 + 7 : 0;
break;
case 7: // only bv/sv region diffs
for ( r = 0; r < 576; r++ ) temp_cf[r] =
( r < granule->sv_bound - granule->region_bound[2] ) ? 255 : 0;
break;
case 8: // numbers of non-zeroes
memset( temp_reg, 0x00, 16 * sizeof(int) );
for ( tmp = 0; tmp < 16; tmp++ ) for ( r = 0; r < 576; r++ )
if ( ABS( frame_data[ch][gr]->coefficients[ r ] ) >= tmp ) temp_reg[tmp]++;
for ( tmp = 0, r = 0; tmp < 16; tmp++ ) for ( ; r < temp_reg[15-tmp]; r++ )
temp_cf[r] = (15-tmp) * 16;
break;
}
data_cf[i][ ( ( 0 + (ch*584) ) * width_cf ) + n_cf ] = v_types[blt][0];
data_cf[i][ ( ( 1 + (ch*584) ) * width_cf ) + n_cf ] = v_types[blt][1];
data_cf[i][ ( ( 578 + (ch*584) ) * width_cf ) + n_cf ] = v_types[blt][0];
data_cf[i][ ( ( 579 + (ch*584) ) * width_cf ) + n_cf ] = v_types[blt][1];
if ( ch == 0 ) for ( r = 576 - 1; r >= 0; r-- )
data_cf[i][ ( ( 2 + 575 - r ) * width_cf ) + n_cf ] = temp_cf[ r ];
else for ( r = 0; r < 576; r++ )
data_cf[i][ ( ( 584 + 2 + r ) * width_cf ) + n_cf ] = temp_cf[ r ];
if ( g_nchannels == 1 ) {
data_cf[i][ ( 580 * width_cf ) + n_cf ] = granule->global_gain;
data_cf[i][ ( 581 * width_cf ) + n_cf ] = granule->global_gain;
} else if ( ch == 0 ) {
data_cf[i][ ( 580 * width_cf ) + n_cf ] = granule->global_gain;
data_cf[i][ ( 581 * width_cf ) + n_cf ] = frame->stereo_ms * 255;
} else {
data_cf[i][ ( 582 * width_cf ) + n_cf ] = frame->stereo_ms * 255;
data_cf[i][ ( 583 * width_cf ) + n_cf ] = granule->global_gain;
}
}
} else {
// no scalefactors
for ( i = 0; i < nfs; i++ ) {
data_sc[i][ ( 0 * width_sc ) + n_sc ] = v_unk;
data_sc[i][ ( 1 * width_sc ) + n_sc ] = v_unk;
data_sc[i][ ( 34 * width_sc ) + n_sc ] = v_unk;
data_sc[i][ ( 35 * width_sc ) + n_sc ] = v_unk;
if ( g_nchannels == 2 ) {
data_sc[i][ ( 36 * width_sc ) + n_sc ] = v_unk;
data_sc[i][ ( 37 * width_sc ) + n_sc ] = v_unk;
data_sc[i][ ( 70 * width_sc ) + n_sc ] = v_unk;
data_sc[i][ ( 71 * width_sc ) + n_sc ] = v_unk;
}
}
// no coefficients
for ( i = 0; i < nfc; i++ ) {
data_cf[i][ ( 0 * width_cf ) + n_cf ] = v_unk;
data_cf[i][ ( 1 * width_cf ) + n_cf ] = v_unk;
data_cf[i][ ( 578 * width_cf ) + n_cf ] = v_unk;
data_cf[i][ ( 579 * width_cf ) + n_cf ] = v_unk;
if ( g_nchannels == 2 ) {
data_cf[i][ ( 580 * width_cf ) + n_cf ] = v_unk;
data_cf[i][ ( 581 * width_cf ) + n_cf ] = v_unk;
data_cf[i][ ( 1158 * width_cf ) + n_cf ] = v_unk;
data_cf[i][ ( 1159 * width_cf ) + n_cf ] = v_unk;
}
}
}
}
if ( n_sc >= width_sc ) { // flush scfs
for ( i = 0; i < nfs; i++ ) {
fwrite( data_sc[i], 1, width_sc * g_nchannels * 38, fp[i] );
memset( data_sc[i], v_zero, width_sc * g_nchannels * 38 );
}
n_sc = 0;
}
if ( n_cf >= width_cf ) { // flush coefs
for ( i = 0; i < nfc; i++ ) {
fwrite( data_cf[i], 1, width_cf * g_nchannels * 582, fp[nfs+i] );
memset( data_cf[i], v_zero, width_cf * g_nchannels * 582 );
}
n_cf = 0;
}
}
if ( n_sc > 0 ) { // flush scfs
for ( i = 0; i < nfs; i++ )
fwrite( data_sc[i], 1, width_sc * g_nchannels * 38, fp[i] );
n_sc = 0;
}
if ( n_cf > 0 ) { // flush coefs
for ( i = 0; i < nfc; i++ )
fwrite( data_cf[i], 1, width_cf * g_nchannels * 582, fp[nfs+i] );
n_cf = 0;
}
// close file pointers / dealloc memory
free( temp_cf );
free( temp_sc );
for ( i = 0; i < (nfs+nfc); i++ ) fclose( fp[i] );
for ( i = 0; i < nfs; i++ ) free( data_sc[i] );
for ( i = 0; i < nfc; i++ ) free( data_cf[i] );
// delete decoder
delete( decoder );
return true;
}
/* -----------------------------------------------
dump main data size
----------------------------------------------- */
INTERN bool dump_main_sizes( void )
{
FILE* fp;
char* fn;
// create filename
fn = create_filename( filelist[ file_no ], "main_size.u2b" );
// open file for output
fp = fopen( fn, "wb" );
if ( fp == NULL ){
sprintf( errormessage, FWR_ERRMSG );
errorlevel = 2;
return false;
}
free( fn );
// dump data & close file
for ( mp3Frame* frame = firstframe; frame != NULL; frame = frame->next )
fwrite( &(frame->main_size), 2, 1, fp );
fclose( fp );
return true;
}
/* -----------------------------------------------
dump aux data size
----------------------------------------------- */
INTERN bool dump_aux_sizes( void )
{
FILE* fp;
char* fn;
// create filename
fn = create_filename( filelist[ file_no ], "aux_size.u2b" );
// open file for output
fp = fopen( fn, "wb" );
if ( fp == NULL ){
sprintf( errormessage, FWR_ERRMSG );
errorlevel = 2;
return false;
}
free( fn );
// dump data & close file
for ( mp3Frame* frame = firstframe; frame != NULL; frame = frame->next )
fwrite( &(frame->aux_size), 2, 1, fp );
fclose( fp );
return true;
}
/* -----------------------------------------------
dump bitrates
----------------------------------------------- */
INTERN bool dump_bitrates( void )
{
FILE* fp;
char* fn;
// only output for vbr files
if ( i_bitrate != -1 ) return true;
// create filename
fn = create_filename( filelist[ file_no ], "bitrate.u1b" );
// open file for output
fp = fopen( fn, "wb" );
if ( fp == NULL ){
sprintf( errormessage, FWR_ERRMSG );
errorlevel = 2;
return false;
}
free( fn );
// dump data & close file
for ( mp3Frame* frame = firstframe; frame != NULL; frame = frame->next )
fwrite( &(frame->bits), 1, 1, fp );
fclose( fp );
return true;
}
/* -----------------------------------------------
dump stereo ms bit
----------------------------------------------- */
INTERN bool dump_stereo_ms( void )
{
FILE* fp;
char* fn;
// check if there is any ms stereo
if ( i_stereo_ms == 0 ) return true;
// create filename
fn = create_filename( filelist[ file_no ], "stereo_ms.u1b" );
// open file for output
fp = fopen( fn, "wb" );
if ( fp == NULL ){
sprintf( errormessage, FWR_ERRMSG );
errorlevel = 2;
return false;
}
free( fn );
// dump data & close file
for ( mp3Frame* frame = firstframe; frame != NULL; frame = frame->next )
fwrite( &(frame->stereo_ms), 1, 1, fp );
fclose( fp );
return true;
}
/* -----------------------------------------------
dump padding bit
----------------------------------------------- */
INTERN bool dump_padding( void )
{
FILE* fp;
char* fn;
// check if there is any padding
if ( i_padding == 0 ) return true;
// create filename
fn = create_filename( filelist[ file_no ], "padding.u1b" );
// open file for output
fp = fopen( fn, "wb" );
if ( fp == NULL ){
sprintf( errormessage, FWR_ERRMSG );
errorlevel = 2;
return false;
}
free( fn );
// dump data & close file
for ( mp3Frame* frame = firstframe; frame != NULL; frame = frame->next )
fwrite( &(frame->padding), 1, 1, fp );
fclose( fp );
return true;
}
/* -----------------------------------------------
dump main data bit counts
----------------------------------------------- */
INTERN bool dump_main_data_bits( void )
{
FILE* fp;
char* fn;
int ch, gr;
// create filename
fn = create_filename( filelist[ file_no ], "main_bits.u2b" );
// open file for output
fp = fopen( fn, "wb" );
if ( fp == NULL ){
sprintf( errormessage, FWR_ERRMSG );
errorlevel = 2;
return false;
}
free( fn );
// dump data
for ( ch = 0; ch < g_nchannels; ch++ ) {
for ( mp3Frame* frame = firstframe; frame != NULL; frame = frame->next )
for ( gr = 0; gr < 2; gr++ )
fwrite( &(frame->granules[ch][gr]->main_data_bit), 2, 1, fp );
}
// close file
fclose( fp );
return true;
}
/* -----------------------------------------------
dump big values
----------------------------------------------- */
INTERN bool dump_big_value_ns( void )
{
FILE* fp;
char* fn;
int ch, gr;
// create filename
fn = create_filename( filelist[ file_no ], "big_value_pairs.u2b" );
// open file for output
fp = fopen( fn, "wb" );
if ( fp == NULL ){
sprintf( errormessage, FWR_ERRMSG );
errorlevel = 2;
return false;
}
free( fn );
// dump data
for ( ch = 0; ch < g_nchannels; ch++ ) {
for ( mp3Frame* frame = firstframe; frame != NULL; frame = frame->next )
for ( gr = 0; gr < 2; gr++ )
fwrite( &(frame->granules[ch][gr]->big_val_pairs), 2, 1, fp );
}
// close file
fclose( fp );
return true;
}
/* -----------------------------------------------
dump global gains
----------------------------------------------- */
INTERN bool dump_global_gain( void )
{
FILE* fp;
char* fn;
int ch, gr;
// create filename
fn = create_filename( filelist[ file_no ], "global_gain.u1b" );
// open file for output
fp = fopen( fn, "wb" );
if ( fp == NULL ){
sprintf( errormessage, FWR_ERRMSG );
errorlevel = 2;
return false;
}
free( fn );
// dump data
for ( ch = 0; ch < g_nchannels; ch++ ) {
for ( mp3Frame* frame = firstframe; frame != NULL; frame = frame->next )
for ( gr = 0; gr < 2; gr++ )
fwrite( &(frame->granules[ch][gr]->global_gain), 1, 1, fp );
}
// close file
fclose( fp );
return true;
}
/* -----------------------------------------------
dump slength
----------------------------------------------- */
INTERN bool dump_slength( void )
{
FILE* fp;
char* fn;
int ch, gr;
// create filename
fn = create_filename( filelist[ file_no ], "slength.u1b" );
// open file for output
fp = fopen( fn, "wb" );
if ( fp == NULL ){
sprintf( errormessage, FWR_ERRMSG );
errorlevel = 2;
return false;
}
free( fn );
// dump data
for ( ch = 0; ch < g_nchannels; ch++ ) {
for ( mp3Frame* frame = firstframe; frame != NULL; frame = frame->next )
for ( gr = 0; gr < 2; gr++ )
fwrite( &(frame->granules[ch][gr]->slength), 1, 1, fp );
}
// close file
fclose( fp );
return true;
}
/* -----------------------------------------------
dump block types
----------------------------------------------- */
INTERN bool dump_block_types( void )
{
FILE* fp;
char* fn;
int ch, gr;
// create filename
fn = create_filename( filelist[ file_no ], "block_types.u1b" );
// open file for output
fp = fopen( fn, "wb" );
if ( fp == NULL ){
sprintf( errormessage, FWR_ERRMSG );
errorlevel = 2;
return false;
}
free( fn );
// dump data
for ( ch = 0; ch < g_nchannels; ch++ ) {
for ( mp3Frame* frame = firstframe; frame != NULL; frame = frame->next )
for ( gr = 0; gr < 2; gr++ )
fwrite( &(frame->granules[ch][gr]->block_type), 1, 1, fp );
}
// close file
fclose( fp );
return true;
}
/* -----------------------------------------------
dump sharing bits
----------------------------------------------- */
INTERN bool dump_sharing( void )
{
FILE* fp;
char* fn;
int ch;
int c;
// create filename
fn = create_filename( filelist[ file_no ], "sharing.u1b" );
// open file for output
fp = fopen( fn, "wb" );
if ( fp == NULL ){
sprintf( errormessage, FWR_ERRMSG );
errorlevel = 2;
return false;
}
free( fn );
// dump data
for ( ch = 0; ch < g_nchannels; ch++ ) {
for ( mp3Frame* frame = firstframe; frame != NULL; frame = frame->next ) {
c = BITN( frame->granules[ch][0]->share, 3 ); fwrite( &c, 1, 1, fp );
c = BITN( frame->granules[ch][0]->share, 2 ); fwrite( &c, 1, 1, fp );
c = BITN( frame->granules[ch][0]->share, 1 ); fwrite( &c, 1, 1, fp );
c = BITN( frame->granules[ch][0]->share, 0 ); fwrite( &c, 1, 1, fp );
}
}
// close file
fclose( fp );
return true;
}
/* -----------------------------------------------
dump preemphasis setting
----------------------------------------------- */
INTERN bool dump_preemphasis( void )
{
FILE* fp;
char* fn;
int ch, gr;
// create filename
fn = create_filename( filelist[ file_no ], "preemphasis.u1b" );
// open file for output
fp = fopen( fn, "wb" );
if ( fp == NULL ){
sprintf( errormessage, FWR_ERRMSG );
errorlevel = 2;
return false;
}
free( fn );
// dump data
for ( ch = 0; ch < g_nchannels; ch++ ) {
for ( mp3Frame* frame = firstframe; frame != NULL; frame = frame->next )
for ( gr = 0; gr < 2; gr++ )
fwrite( &(frame->granules[ch][gr]->preemphasis), 1, 1, fp );
}
// close file
fclose( fp );
return true;
}
/* -----------------------------------------------
dump coarse setting
----------------------------------------------- */
INTERN bool dump_coarse( void )
{
FILE* fp;
char* fn;
int ch, gr;
// create filename
fn = create_filename( filelist[ file_no ], "coarse_scalefactors.u1b" );
// open file for output
fp = fopen( fn, "wb" );
if ( fp == NULL ){
sprintf( errormessage, FWR_ERRMSG );
errorlevel = 2;
return false;
}
free( fn );
// dump data
for ( ch = 0; ch < g_nchannels; ch++ ) {
for ( mp3Frame* frame = firstframe; frame != NULL; frame = frame->next )
for ( gr = 0; gr < 2; gr++ )
fwrite( &(frame->granules[ch][gr]->coarse_scalefactors), 1, 1, fp );
}
// close file
fclose( fp );
return true;
}
/* -----------------------------------------------
dump huffman table selections
----------------------------------------------- */
INTERN bool dump_htable_sel( void )
{
FILE* fp;
char* fn;
int ch, gr;
// create filename
fn = create_filename( filelist[ file_no ], "htable_selection.u1b" );
// open file for output
fp = fopen( fn, "wb" );
if ( fp == NULL ){
sprintf( errormessage, FWR_ERRMSG );
errorlevel = 2;
return false;
}
free( fn );
// dump data
for ( ch = 0; ch < g_nchannels; ch++ ) {
for ( int i = 0; i < 3; i++ ) {
for ( mp3Frame* frame = firstframe; frame != NULL; frame = frame->next )
for ( gr = 0; gr < 2; gr++ )
fwrite( &(frame->granules[ch][gr]->region_table[i]), 1, 1, fp );
}
for ( mp3Frame* frame = firstframe; frame != NULL; frame = frame->next )
for ( gr = 0; gr < 2; gr++ )
fwrite( &(frame->granules[ch][gr]->select_htabB), 1, 1, fp );
}
// close file
fclose( fp );
return true;
}
/* -----------------------------------------------
dump region sizes
----------------------------------------------- */
INTERN bool dump_region_sizes( void )
{
FILE* fp;
char* fn;
int ch, gr;
// create filename
fn = create_filename( filelist[ file_no ], "region_sizes.u1b" );
// open file for output
fp = fopen( fn, "wb" );
if ( fp == NULL ){
sprintf( errormessage, FWR_ERRMSG );
errorlevel = 2;
return false;
}
free( fn );
// dump data
for ( ch = 0; ch < g_nchannels; ch++ ) {
for ( mp3Frame* frame = firstframe; frame != NULL; frame = frame->next )
for ( gr = 0; gr < 2; gr++ )
fwrite( &(frame->granules[ch][gr]->region0_size), 1, 1, fp );
for ( mp3Frame* frame = firstframe; frame != NULL; frame = frame->next )
for ( gr = 0; gr < 2; gr++ )
fwrite( &(frame->granules[ch][gr]->region1_size), 1, 1, fp );
}
// close file
fclose( fp );
return true;
}
/* -----------------------------------------------
dump subblock gains
----------------------------------------------- */
INTERN bool dump_subblock_gains( void )
{
FILE* fp;
char* fn;
int ch, gr;
// check if there is any subblock gain
if ( i_sbgain == 0 ) return true;
// create filename
fn = create_filename( filelist[ file_no ], "subblock_gain.u1b" );
// open file for output
fp = fopen( fn, "wb" );
if ( fp == NULL ){
sprintf( errormessage, FWR_ERRMSG );
errorlevel = 2;
return false;
}
free( fn );
// dump data
for ( ch = 0; ch < g_nchannels; ch++ ) {
for ( mp3Frame* frame = firstframe; frame != NULL; frame = frame->next ) {
for ( gr = 0; gr < 2; gr++ ) if ( frame->granules[ch][gr]->block_type == SHORT_BLOCK ) {
fwrite( &(frame->granules[ch][gr]->sb_gain[0]), 1, 1, fp );
fwrite( &(frame->granules[ch][gr]->sb_gain[1]), 1, 1, fp );
fwrite( &(frame->granules[ch][gr]->sb_gain[2]), 1, 1, fp );
}
}
}
// close file
fclose( fp );
return true;
}
/* -----------------------------------------------
dump data files
----------------------------------------------- */
INTERN bool dump_data_files( void )
{
FILE* fp_aux;
FILE* fp_huf;
char* fn;
// --- simple stuff ---
if ( main_data_size > 0 )
if ( !write_file( filelist[ file_no ], "main_data.s1b", main_data, 1, main_data_size ) )
return false;
if ( data_before_size > 0 )
if ( !write_file( filelist[ file_no ], "data_before.s1b", data_before, 1, data_before_size ) )
return false;
if ( data_after_size > 0 )
if ( !write_file( filelist[ file_no ], "data_after.s1b", data_after, 1, data_after_size ) )
return false;
// --- huffman and aux data files ---
// open huffman file
fn = create_filename( filelist[ file_no ], "huff_data.s1b" );
fp_huf = fopen( fn, "wb" );
free( fn );
// open aux file
fn = create_filename( filelist[ file_no ], "aux_data.s1b" );
fp_aux = fopen( fn, "wb" );
free( fn );
// check for problems
if ( ( fp_huf == NULL ) || ( fp_aux == NULL ) ) {
if ( fp_huf != NULL ) fclose( fp_huf );
if ( fp_aux != NULL ) fclose( fp_aux );
sprintf( errormessage, FWR_ERRMSG );
errorlevel = 2;
return false;
}
// dump data
for ( mp3Frame* frame = firstframe; frame != NULL; frame = frame->next ) {
if ( frame->n < n_bad_first ) continue;
fwrite( main_data + frame->main_index, 1, frame->main_size, fp_huf );
fwrite( main_data + frame->main_index + frame->main_size, 1, frame->aux_size, fp_aux );
}
// close files
fclose( fp_huf );
fclose( fp_aux );
return true;
}
/* -----------------------------------------------
dump global gain context
----------------------------------------------- */
INTERN bool dump_gg_ctx( void )
{
FILE* fp;
char* fn;
// build the context (even if it is already built)
pmp_build_context();
// create filename
fn = create_filename( filelist[ file_no ], "gg_ctx.u1b" );
// open file for output
fp = fopen( fn, "wb" );
if ( fp == NULL ){
sprintf( errormessage, FWR_ERRMSG );
errorlevel = 2;
return false;
}
free( fn );
// dump data
fwrite( gg_context[0], 1, 2 * g_nframes, fp );
if ( g_nchannels == 2 )
fwrite( gg_context[1], 1, 2 * g_nframes, fp );
// close file
fclose( fp );
return true;
}
/* -----------------------------------------------
dump coefficients
----------------------------------------------- */
INTERN bool dump_decoded_data( void )
{
static const unsigned char zeroes[32] = { 0 };
FILE* fp_cf[2];
FILE* fp_sc[2];
const char* ext_cf[2] = { "coefs.ch0.s2b", "coefs.ch1.s2b" };
const char* ext_sc[2] = { "scale.ch0.u1b", "scale.ch1.u1b" };
char* fn;
int ch, gr;
granuleData*** frame_data;
huffman_reader* decoder;
// open 4 files (max)
for ( ch = 0; ch < g_nchannels; ch++ ) {
// coeficients file
// create filename
fn = create_filename( filelist[ file_no ], ext_cf[ch] );
// open file for output
fp_cf[ch] = fopen( fn, "wb" );
free( fn );
// scalefactors file
// create filename
fn = create_filename( filelist[ file_no ], ext_sc[ch] );
// open file for output
fp_sc[ch] = fopen( fn, "wb" );
free( fn );
// error checking and file pointer rollback
if ( ( fp_cf[ch] == NULL ) || ( fp_sc[ch] == NULL ) ) {
for ( ; ch >= 0; ch-- ) {
if ( fp_cf[ch] != NULL ) fclose( fp_cf[ch] );
if ( fp_sc[ch] != NULL ) fclose( fp_sc[ch] );
}
sprintf( errormessage, FWR_ERRMSG );
errorlevel = 2;
return false;
}
}
// init decoder
decoder = new huffman_reader( main_data, main_data_size );
// dump data
for ( mp3Frame* frame = firstframe; frame != NULL; frame = frame->next ) {
// skip bad first frames
if ( frame->n < n_bad_first ) continue;
frame_data = mp3_decode_frame( decoder, frame );
if ( frame_data == NULL ) continue;
for ( gr = 0; gr < 2; gr++ ) {
for ( ch = 0; ch < g_nchannels; ch++ ) {
fwrite( frame_data[ch][gr]->coefficients, sizeof( short ), 576, fp_cf[ch] );
if ( frame->granules[ch][gr]->block_type != SHORT_BLOCK ) {
fwrite( frame_data[ch][gr]->scalefactors, sizeof( char ), 21, fp_sc[ch] );
fwrite( zeroes, sizeof( char ), 11, fp_sc[ch] );
} else fwrite( frame_data[ch][gr]->scalefactors, sizeof( char ), 32, fp_sc[ch] );
}
}
}
// close files
for ( ch = 0; ch < g_nchannels; ch++ ) {
fclose( fp_cf[ch] );
fclose( fp_sc[ch] );
}
// delete decoder
delete( decoder );
return true;
}
#endif
/* ----------------------- End of developers functions -------------------------- */
/* ----------------------- End of file -------------------------- */