/** ** Supermodel ** A Sega Model 3 Arcade Emulator. ** Copyright 2011 Bart Trzynadlowski, Nik Henson ** ** This file is part of Supermodel. ** ** Supermodel is free software: you can redistribute it and/or modify it under ** the terms of the GNU General Public License as published by the Free ** Software Foundation, either version 3 of the License, or (at your option) ** any later version. ** ** Supermodel is distributed in the hope that it will be useful, but WITHOUT ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ** more details. ** ** You should have received a copy of the GNU General Public License along ** with Supermodel. If not, see . **/ /* * getbits.cpp * * Amp library internal module. */ /* this file is a part of amp software, (C) tomislav uzelac 1996,1997 */ /* getbits.c bit level routines, input buffer * * Created by: tomislav uzelac Apr 1996 * better synchronization, tomislav uzelac, Apr 23 1997 */ #include #include #include #include "amp.h" #include "audio.h" #include "formats.h" #include "rtbuf.h" #define GETBITS #include "getbits.h" static const char *fstart, *lstart; static int offset, end, eof, lend; void MPEG_GetPlayPosition(int *playOffset, int *endOffset) { if (in_file) *playOffset = ftell(in_file); else *playOffset = offset; *endOffset = end; } void MPEG_SetPlayPosition(int playOffset, int endOffset) { offset = playOffset; end = endOffset; } void m1setfile(const char *mstart, int mend) { fstart = mstart; offset = 0; eof = 0; end = mend; lstart = NULL; } void MPEG_SetLoop(const char *loop, int loopend) { lstart = loop; lend = loopend; } int m1fread(unsigned char *buf, int size1, int size2, void *f) { int total = size1 * size2; if (in_file) return fread(buf, size1, size2, (FILE *) f); //printf("fstart=%X, total=%X, offset=%X, end=%X\n", fstart, total, offset, end); // if past EOF if ((total + offset) >= end) { if (lstart == NULL) { total = end - offset; eof = 1; } else { // if past the end, do the xfer in 2 pieces if ((total + offset) > end) { memcpy(buf, fstart + offset, end-offset); buf += (end-offset); total -= (end-offset); } fstart = lstart; offset = 0; end = lend; } } memcpy(buf, fstart + offset, total); offset += total; return total; } int m1feof(void *f) { if (in_file) return feof((FILE *)f); return eof; } int m1fseek(void *f, int offs, int whence) { if (in_file) return fseek((FILE *) f, offs, whence); switch (whence) { case SEEK_CUR: if ((offset + offs) < 0) { offset = 0; eof = 0; return -1; } if ((offset + offs) > end) { offset = end; eof = 1; return end; } offset += offs; eof = 0; break; } return 0; } /* * buffer and bit manipulation functions *************************************** */ static inline int _fillbfr(unsigned int size) { _bptr=0; return get_input(_buffer, size); } static inline int readsync() { _bptr=0; _buffer[0]=_buffer[1]; _buffer[1]=_buffer[2]; _buffer[2]=_buffer[3]; return get_input(&_buffer[3],1); } static inline unsigned int _getbits(int n) { unsigned int pos,ret_value; pos = _bptr >> 3; ret_value = _buffer[pos] << 24 | _buffer[pos+1] << 16 | _buffer[pos+2] << 8 | _buffer[pos+3]; ret_value <<= _bptr & 7; ret_value >>= 32 - n; _bptr += n; return ret_value; } int fillbfr(unsigned int advance) { int overflow,retval; retval=get_input(&buffer[append], advance); if ( append + advance >= BUFFER_SIZE ) { overflow = append + advance - BUFFER_SIZE; memcpy (buffer,&buffer[BUFFER_SIZE], overflow); if (overflow < 4) memcpy(&buffer[BUFFER_SIZE],buffer,4); append = overflow; } else { if (append==0) memcpy(&buffer[BUFFER_SIZE],buffer,4); append+=advance; } return retval; } unsigned int getbits(int n) { if (n) { unsigned int pos,ret_value; pos = data >> 3; ret_value = buffer[pos] << 24 | buffer[pos+1] << 16 | buffer[pos+2] << 8 | buffer[pos+3]; ret_value <<= data & 7; ret_value >>= 32 - n; data += n; data &= (8*BUFFER_SIZE)-1; return ret_value; } else return 0; } /* * header and side info parsing stuff ****************************************** */ static inline void parse_header(struct AUDIO_HEADER *header) { header->ID=_getbits(1); header->layer=_getbits(2); header->protection_bit=_getbits(1); header->bitrate_index=_getbits(4); header->sampling_frequency=_getbits(2); header->padding_bit=_getbits(1); header->private_bit=_getbits(1); header->mode=_getbits(2); header->mode_extension=_getbits(2); if (!header->mode) header->mode_extension=0; header->copyright=_getbits(1); header->original=_getbits(1); header->emphasis=_getbits(2); } static inline int header_sanity_check(struct AUDIO_HEADER *header) { if ( header->layer==0 || header->bitrate_index==15 || header->sampling_frequency==3) return -1; /* an additional check to make shure that stuffing never gets mistaken * for a syncword. This rules out some legal layer1 streams, but who * cares about layer1 anyway :-). I must get this right sometime. */ if ( header->ID==1 && header->layer==3 && header->protection_bit==1) return -1; return 0; } int gethdr(struct AUDIO_HEADER *header) { int s,retval; struct AUDIO_HEADER tmp; /* TODO: add a simple byte counter to check only first, say, 1024 * bytes for a new header and then return GETHDR_SYN */ if ((retval=_fillbfr(4))!=0) return retval; for(;;) { while ((s=_getbits(12)) != 0xfff) { if (s==0xffe) { parse_header(&tmp); if (header_sanity_check(&tmp)==0) return GETHDR_NS; } if ((retval=readsync())!=0) return retval; } parse_header(&tmp); if (header_sanity_check(&tmp)!=0) { if ((retval=readsync())!=0) return retval; } else break; } if (tmp.layer==3) return GETHDR_FL1; /* if (tmp.layer==2) return GETHDR_FL2; */ if (tmp.bitrate_index==0) return GETHDR_FF; //printf("layer: %d\n", tmp.layer); //printf("sampling frequency: %d\n", tmp.sampling_frequency); memcpy(header,&tmp,sizeof(tmp)); return 0; } /* dummy function, to get crc out of the way */ void getcrc() { _fillbfr(2); _getbits(16); } /* sizes of side_info: * MPEG1 1ch 17 2ch 32 * MPEG2 1ch 9 2ch 17 */ void getinfo(struct AUDIO_HEADER *header,struct SIDE_INFO *info) { int gr,ch,scfsi_band,region,window; int nch; if (header->mode==3) { nch=1; if (header->ID) { _fillbfr(17); info->main_data_begin=_getbits(9); _getbits(5); } else { _fillbfr(9); info->main_data_begin=_getbits(8); _getbits(1); } } else { nch=2; if (header->ID) { _fillbfr(32); info->main_data_begin=_getbits(9); _getbits(3); } else { _fillbfr(17); info->main_data_begin=_getbits(8); _getbits(2); } } if (header->ID) for (ch=0;chscfsi[ch][scfsi_band]=_getbits(1); for (gr=0;gr<(header->ID ? 2:1);gr++) for (ch=0;chpart2_3_length[gr][ch]=_getbits(12); info->big_values[gr][ch]=_getbits(9); info->global_gain[gr][ch]=_getbits(8); if (header->ID) info->scalefac_compress[gr][ch]=_getbits(4); else info->scalefac_compress[gr][ch]=_getbits(9); info->window_switching_flag[gr][ch]=_getbits(1); if (info->window_switching_flag[gr][ch]) { info->block_type[gr][ch]=_getbits(2); info->mixed_block_flag[gr][ch]=_getbits(1); for (region=0;region<2;region++) info->table_select[gr][ch][region]=_getbits(5); info->table_select[gr][ch][2]=0; for (window=0;window<3;window++) info->subblock_gain[gr][ch][window]=_getbits(3); } else { for (region=0;region<3;region++) info->table_select[gr][ch][region]=_getbits(5); info->region0_count[gr][ch]=_getbits(4); info->region1_count[gr][ch]=_getbits(3); info->block_type[gr][ch]=0; } if (header->ID) info->preflag[gr][ch]=_getbits(1); info->scalefac_scale[gr][ch]=_getbits(1); info->count1table_select[gr][ch]=_getbits(1); } return; } int dummy_getinfo(int n) { n-=4; if ( m1fseek(in_file,n,SEEK_CUR) != 0) { if (m1feof(in_file)) return GETHDR_EOF; else return GETHDR_ERR; } return 0; } int rewind_stream(int nbytes) { nbytes+=5; if (m1fseek(in_file, -nbytes, SEEK_CUR) != 0) { /* what if we need to be at the very beginning? */ nbytes--; if (m1fseek(in_file, -nbytes, SEEK_CUR) != 0) return GETHDR_ERR; } return 0; } static inline int get_input(unsigned char* bp, unsigned int size) { #ifdef LINUX_REALTIME return prefetch_get_input(bp,size); #else /* LINUX_REALTIME */ if ( m1fread( bp , 1, size, in_file) != size) { if (m1feof(in_file)) return GETHDR_EOF; else return GETHDR_ERR; } return 0; #endif /* LINUX_REALTIME */ }