/* MPEG Maaate: An Australian MPEG audio analysis toolkit Copyright (C) 2000 Commonwealth Scientific and Industrial Research Organisation (CSIRO), Australia. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #if HAVE_CONFIG_H #include #endif #include "MPEGfile.H" #include "layer1.H" #include "layer2.H" #include "layer3.H" #include "byteOrder.h" /*---------- constructor and destructor ---------------*/ // constructor CSAPI_MPEG MPEGfile::MPEGfile(string filenm) { //MPEG specifics attributs analysed = false; decoded = dec_fields; audio = 0; lastlayer = RESERVED; frameNo = 0; gr_current = -1; // call the common constructor in AllFormat class // which initialises the variables // filename, windowNo, fileDuration, windowDuration if(!common_constr(filenm)) return; // open file and save file descriptor in AllFormat's fd variable if ((fd = fopen (filename.c_str(), "rb")) == NULL) { cerr << "MaaateMPEG: Cannot open file "; cerr << filename.c_str() << "." << endl; return; } if (!skip_frame()) //not a MPEG file return; // calculate the duration of the file and store it while (skip_frame()); fileDuration = frameNo * nb_granules(); windowNo = fileDuration; //calculate windowDuration windowDuration = timeticks(LOW) * sample_duration(LOW); // reset to start of file seek_window(0); if (layer()==III) { ((Layer3*) audio)->clearinterbuffer(); } return; } // destructor CSAPI_MPEG MPEGfile::~MPEGfile() { common_destr(); if (fd) fclose (fd); if (audio) delete audio; audio=0; } /*---------------- About this file -----------------*/ CSAPI_MPEG bool MPEGfile::file_ok() { if (fd == NULL) { return false; } if (fileDuration <=0) { return false; } return true; } CSAPI_MPEG bool MPEGfile::is_stereo() { return ( mode() <= jstereo); //either stereo or jstereo } CSAPI_MPEG int MPEGfile::channel() { return channels(); } CSAPI_MPEG double MPEGfile::sampling_rate() { return samplingrate(); } /*---------------- time functions --------------------*/ CSAPI_MPEG float MPEGfile::sample_duration( Resolution res ) { //duration of one MPEG frame double frameDur = (1.0*samples_per_frame() / (1000.0 * samplingrate())); if ( res == PCM ) { return 1.0 / (1000.0 * samplingrate() ); //return the duration of one pcm sample } if (layer()==I) { return frameDur / 12.0; //there is one granule of 12 samples } else if (layer()==II || (layer()==III && res == LOW)) { return frameDur / 36.0; // three granules of 12 samples each } else { // layer==III HIGH res return frameDur/((Layer3*)audio)->granules(); // one or two granule(s) of 1 sample each. } } /*---------------- window function --------------------*/ CSAPI_MPEG bool MPEGfile::seek_window( long w_nb ) { int nb_gr = nb_granules(); // frame no to stop long stopframe = w_nb / (long) nb_gr; // update current granule number after seeking gr_current = (w_nb == 0) ? -1 : (w_nb - 1) % nb_gr; if ( w_nb < windowNo ) { // position file pointer at beginning rewind(fd); frameNo = 0; bitsread = 0; analysed = false; } // skip frames until specified frame is reached while ( frameNo < stopframe ) { if (!skip_frame()) { windowNo = frameNo * nb_gr; return false; } analysed = false; } windowNo = w_nb; return true; } CSAPI_MPEG unsigned int MPEGfile::timeticks( Resolution res ) { if ( layer() == III ) { if ( res == LOW || res == PCM) { return 18; } else { return 1; } } else { return 12; } } /*------------------- skip and analyse function -----------*/ CSAPI_MPEG bool MPEGfile::next_window( Resolution res ) { int nb_gr = nb_granules(); DecodeLevel touse = dec_subbands; //future current window long act = windowNo; // matching resolution and decodelevel switch (res) { case NO: touse = dec_fields; break; case LOW: touse = dec_subbands; break; case HIGH: if ( layer() == III) { touse = dec_subsubbands; } else { touse = dec_subbands; }; break; case PCM: touse = dec_pcm; break; default: break; } // also works for gr_current == -1, at the begining if ( gr_current < (nb_gr - 1)) { //that is not the last granule of the current frame if ( analysed && touse < decoded ) { //the current frame has been analysed before, but with a wrong resolution //so we come back to the current window, but without analysis windowNo++; seek_window (act); } if ( !analysed || touse < decoded ) { //the current window is not in the current frame! //because no analyse has been done //so just analyse the next frame with the right decodeLevel if (!parse_frame(touse)) return false; //now the current window is in the current frame decoded = touse; analysed = true; } //just go to the next granule to go the next window gr_current++; } else { //last granule of a frame if ( !analysed ) { //the current window is not in the current frame! //because no analyse has been done //so must skip the frame containing the current window //before parsing the next one if (!skip_frame()) return false; } //now parse the next frame, the next window will be granule 0 of that frame if (!parse_frame(touse)) return false; decoded = touse; analysed = true; gr_current = 0; } windowNo++; return true; } CSAPI_MPEG bool MPEGfile::skip_window(){ int nb_gr = nb_granules(); // also works for gr_current == -1, at the begining if ( gr_current < (nb_gr - 1) ) { //that is not the last granule of the current frame gr_current++; } else { //last granule of a frame if (!data_available()) return false; if ( (windowNo / nb_gr) > frameNo) { //that means that the current window is not in the current frame because //of skipping windows if (!skip_frame()) return false; if (!data_available()) return false; //means this is the end of the file } //new window won`t be in the current frame, //but in the next frame at granule 0 gr_current = 0; analysed = false; } windowNo++; return true; } /*------------- access function ----------------*/ CSAPI_MPEG double MPEGfile::freq_value( unsigned int ch, unsigned int sb, unsigned int nb = 0, Resolution res) { // I don't believe this was correct... // ...but it works if ( res == HIGH && layer() == III) { return mdct_sample (ch,sb); //576 subbands } else { return restored_sample (ch,sb,nb); // 32 subbands } /* This might be correct, but it doesn't work well if (layer() != III) { return restored_sample (ch, sb, nb); // 32 subbands } // layer III needs extra treatment if (res == HIGH) { int subb = sb/SSLIMIT; int ss = sb%SSLIMIT; return restored_sample(ch,subb,ss); //576 subbands } else { return mdct_sample(ch,sb,nb); //32 subbands in 18 windows } */ } CSAPI_MPEG unsigned int MPEGfile::nb_subbands( Resolution res ) { if ( layer() == III && res == HIGH ) { return 576; } else { return 32; } } /*--------------- pcm decoder --------------------*/ // returns number of samples in buffer CSAPI_MPEG long MPEGfile::decode (short *buffer, long window, Channels ch) { // check space in buffer if (buffer == NULL) { return -1; } // check channels requested if ( ch != LEFT && channels()==1) { cerr << "MaaateMPEG: wrong channel to be decoded, will decode LEFT channel." << endl; ch = LEFT; } //number of samples in buffer long samps = 0; int bd,sub,gr,tot; int nb_g; long act_win, act_w; switch (layer()) { case I: // from one window to another for (act_win = 0; act_win < window; act_win++ ) { if (!next_window(PCM)) break; // from one sample to another for (int bandsample = 0; bandsample < 12; bandsample++) { // from one subband to another for (int subband = 0; subband < SBLIMIT; subband++) { if (ch != RIGHT) { buffer[samps++] = pcm_sample(0, subband, bandsample, gr_current); } if (ch != LEFT) { buffer[samps++] = pcm_sample(1, subband, bandsample, gr_current); } } } } break; case II: // from one window to another for (act_win = 0; act_win < window; act_win++ ) { if (!next_window(PCM)) break; // from one sample to another for (int bandsample = 0; bandsample < 12; bandsample++) { // from one subband to another for (int subband = 0; subband < SBLIMIT; subband++) { if (ch != RIGHT) { //special mapping because layer II has a special way to //store the pcm samples tot = subband + bandsample * SBLIMIT + gr_current * 384; bd = tot / 96; sub = tot % 96; gr = sub / SBLIMIT; sub = sub % SBLIMIT; buffer[samps++] = pcm_sample(0, sub, bd, gr); } if (ch != LEFT) { //special mapping because layer II has a special way to //store the pcm samples tot = subband + bandsample * SBLIMIT + gr_current * 384; bd = tot / 96; sub = tot % 96; gr = sub / SBLIMIT; sub = sub % SBLIMIT; buffer[samps++] = pcm_sample(1, sub, bd, gr); } } } } break; case III: nb_g = nb_granules() - 1; act_w = 0; while (act_w < window) { if (!next_window(PCM)) break; // from one sample to another for (int bandsample = 0; bandsample < 18; bandsample++) { // from one subband to another for (int subband = 0; subband < SBLIMIT; subband++) { if (ch != RIGHT) { buffer[samps++] = pcm_sample(0, gr_current, subband, bandsample); } if (ch != LEFT) { buffer[samps++] = pcm_sample(1, gr_current, subband, bandsample); } } } act_w++; } break; default: break; } return samps; } /*------------ MPEG specific functions ---------------------------*/ //return gr_current CSAPI_MPEG int MPEGfile::selected_granule() { return gr_current; } //return frameNo CSAPI_MPEG long MPEGfile::frame() { return frameNo; } //allow to parse next frame if available, keep windowNo up to date CSAPI_MPEG bool MPEGfile::goTo_nextFrame( Resolution res) { if (!data_available()) return false; long oldfr = frameNo; while (frameNo == oldfr) { if (!next_window(res)) return false; } return true; } // returns whether end of stream was/will be reached CSAPI_MPEG bool MPEGfile::data_available() { if (!(fd)) return false; static struct stat *buf = (struct stat *) malloc (sizeof(struct stat)); stat(filename.c_str(), buf); if (((long)buf->st_size - ftell(fd)) < 5) return false; //fileDuration = windowNo; return true; } // reads and checks the header of a frame CSAPI_MPEG bool MPEGfile::parse_header() { #define MAXSEARCH 2048 // read 4 bytes into header bitfield register unsigned short buf=0; // read over initial junk to find header; give up after 2048 bytes int i=-2; while (((buf & 0xfff0) != 0xfff0) && (i> 8); #endif } } if (i>0) { if (i==MAXSEARCH) { cerr << "MaaateP: Gave up searching valid MPEG header after " << MAXSEARCH << " bytes" << endl; return false; } cerr << "MaaateP: Skipped " << i << " byte(s) to find valid MPEG header at file position " << ftell(fd) << endl; } // write first two bytes to header #ifndef WORDS_BIGENDIAN header.sync = (buf >> 4) & 0xFFF; header.version = (buf >> 3) & 0x1; header.layer = (buf >> 1) & 0x3; header.errorprotection = buf & 0x1; #else ((unsigned short*) (&header))[0] = buf; #endif if (fread(&buf, sizeof (unsigned short), 1, fd) != 1) { return false; } #ifndef WORDS_BIGENDIAN // exchange the two bytes because of wrong byteorder buf = (buf << 8) | (buf >> 8); // write last two bytes to header header.bitrate = (buf >> 12) & 0xF; header.samplingrate = (buf >> 10) & 0x3; header.padding = (buf >> 9) & 0x1; header.extension = (buf >> 8) & 0x1; header.mode = (buf >> 6) & 0x3; header.mode_ext = (buf >> 4) & 0x3; header.copyright = (buf >> 3) & 0x1; header.original = (buf >> 2) & 0x1; header.emphasis = buf & 0x3; #else // write last two bytes to header ((unsigned short*) (&header))[1] = buf; #endif // update frame number frameNo++; // check header if (!checkheader()) { cerr << "MaaateP: Error parsing header of frame " << frameNo << " of file " << filename.c_str() << "." << endl; return false; } return true; } // reads the header of a frame, decides about the layer, // creates a layer-object accordingly, reads the checksum (if required), // and reads the data (via the layer-object) CSAPI_MPEG bool MPEGfile::parse_data(DecodeLevel decode) { // create layerspecific object if (!create_layer()) return false; // read checksum if (!read_checksum()) return false; // buffer data if (!buffer_data()) return false; // parse buffered audio data via layer object if (!audio->parse_data(decode)) { cerr << "MaaateP: Error parsing audio data."<parse_data(dec_fields)) { cerr << "MaaateP: Error parsing audio data."<> 8); #endif } return true; } // read audio data into buffer CSAPI_MPEG bool MPEGfile::buffer_data() { bitsread=0; unsigned int nobytes = framesize(); if (fread((char*)buffer, sizeof(unsigned char), nobytes, fd) != nobytes) { if (feof(fd)) { // end of file // cerr << "MaaateP: WARNING: End of file reached." << endl; } else { cerr << "MaaateP: Error buffering stream." << endl; return false; } } #ifndef WORDS_BIGENDIAN // swap the bytes for (unsigned int i=0; i < ((nobytes/4)+1); i++) { swap_int((unsigned char *) &(buffer[i])); } #endif return true; } /*------------- layer-specific access to audio data -------------------*/ CSAPI_MPEG int MPEGfile::bitallocation (unsigned int ch, unsigned int sb) { if (layer()==III) { cerr << "MaaateP: Layer 3 has no bitallocation scheme\n"; return 0; } else { return audio->bitallocation(ch, sb); } } CSAPI_MPEG int MPEGfile::scfsi (unsigned int ch, unsigned int sb) { if (layer()==I) { cerr << "MaaateP: Layer 1 has no scale factor selection information\n"; return 0; } else { return audio->scfsi(ch, sb); } } CSAPI_MPEG float MPEGfile::scalefactor (unsigned int ch, unsigned int sb) { return audio->scalefactor(ch, sb, gr_current); } CSAPI_MPEG int MPEGfile::sample (unsigned int ch, unsigned int sb, unsigned int no) { return audio->sample(ch, sb, no, gr_current); } CSAPI_MPEG double MPEGfile::restored_sample (unsigned int ch, unsigned int sb, unsigned int no) { return audio->restored_sample(ch, sb, no, gr_current); } CSAPI_MPEG short MPEGfile::pcm_sample (unsigned int ch, unsigned int sb, unsigned int no, unsigned int ss) { return audio->pcm_sample(ch, sb, no, ss); } /* get a pcm sample of the current frame this does not continue reading to a requested position*/ CSAPI_MPEG short MPEGfile::pcm (unsigned int ch, unsigned int number) { // make sure it's in the current frame if (number > samples_per_frame()) { number = samples_per_frame(); } // XXX: THIS IS UNTESTED! // calculate sb, no and ss from number unsigned int sb = 0 , gr = 0, ss = 0; if (layer() == I) { gr = 0; sb = number % 32; ss = number / 32; } if (layer() == II) { sb = number % 32; gr = (number / 32 ) % 2; ss = (number / 32 ) / 2; } if (layer() == III) { //TESTED ss = number % 32; gr = (number / 32) % 18; sb = (number / 32) / 18; } return audio->pcm_sample(ch, sb, ss, gr); } CSAPI_MPEG double MPEGfile::mdct_sample (unsigned int ch, unsigned int ssb) { if (layer() == III) { return ((Layer3*) audio)->mdct_sample(ch, gr_current, ssb); } else { cerr << "MaaateP: Only Layer 3 has mdct_sample information\n"; return 0.0; } } CSAPI_MPEG void MPEGfile::printSideinfo () { if (layer() != III) { cerr << "MaaateP: Side information not available for Layer" << layer()+1 << "." << endl; cerr << "Only Layer III has a side information header." << endl; } else { ((Layer3*) audio)->printSideinfo(); } } CSAPI_MPEG unsigned int MPEGfile::part2_3_length (unsigned int ch) { if (layer()!=III) { cerr << "MaaateP: Only Layer 3 has part2_3_length information\n"; return 0; } else { return ((Layer3*) audio)->part2_3_length(ch,gr_current); } } CSAPI_MPEG unsigned int MPEGfile::big_values (unsigned int ch) { if (layer()!=III) { cerr << "MaaateP: Only Layer 3 has big_values information\n"; return 0; } else { return ((Layer3*) audio)->big_values(ch,gr_current); } } CSAPI_MPEG unsigned int MPEGfile::count1_values (unsigned int ch) { if (layer()!=III) { cerr << "MaaateP: Only Layer 3 has count1_values information\n"; return 0; } else { return ((Layer3*) audio)->count1_values(ch,gr_current); } } CSAPI_MPEG unsigned int MPEGfile::global_gain (unsigned int ch) { if (layer()!=III) { cerr << "MaaateP: Only Layer 3 has global_gain information\n"; return 0; } else { return ((Layer3*) audio)->global_gain(ch,gr_current); } } CSAPI_MPEG unsigned int MPEGfile::scalefac_compress (unsigned int ch) { if (layer()!=III) { cerr << "MaaateP: Only Layer 3 has scalefac_compress information\n"; return 0; } else { return ((Layer3*) audio)->scalefac_compress(ch,gr_current); } } CSAPI_MPEG bool MPEGfile::window_switching (unsigned int ch) { if (layer()!=III) { cerr << "MaaateP: Only Layer 3 has window_switching information\n"; return 0; } else { return ((Layer3*) audio)->window_switching(ch,gr_current); } } CSAPI_MPEG unsigned int MPEGfile::blocktype (unsigned int ch) { if (layer()!=III) { cerr << "MaaateP: Only Layer 3 has blocktype information\n"; return 0; } else { return ((Layer3*) audio)->blocktype(ch,gr_current); } } CSAPI_MPEG bool MPEGfile::mixedblock (unsigned int ch) { if (layer()!=III) { cerr << "MaaateP: Only Layer 3 has mixedblock information\n"; return 0; } else { return ((Layer3*) audio)->mixedblock(ch,gr_current); } } CSAPI_MPEG unsigned int MPEGfile::table_select (unsigned int ch, unsigned int area) { if (layer()!=III) { cerr << "MaaateP: Only Layer 3 has table_select information\n"; return 0; } else { return ((Layer3*) audio)->table_select(ch,gr_current, area); } } CSAPI_MPEG unsigned int MPEGfile::subblock_gain (unsigned int ch, unsigned int area) { if (layer()!=III) { cerr << "MaaateP: Only Layer 3 has subblock_gain information\n"; return 0; } else { return ((Layer3*) audio)->subblock_gain(ch,gr_current, area); } } CSAPI_MPEG unsigned int MPEGfile::region0_samps (unsigned int ch) { if (layer()!=III) { cerr << "MaaateP: Only Layer 3 has region0_samps information\n"; return 0; } else { return ((Layer3*) audio)->region0_samps(ch,gr_current); } } CSAPI_MPEG unsigned int MPEGfile::region1_samps (unsigned int ch) { if (layer()!=III) { cerr << "MaaateP: Only Layer 3 has region1_samps information\n"; return 0; } else { return ((Layer3*) audio)->region1_samps(ch,gr_current); } } CSAPI_MPEG unsigned int MPEGfile::region2_samps (unsigned int ch) { if (layer()!=III) { cerr << "MaaateP: Only Layer 3 has region2_samps information\n"; return 0; } else { return ((Layer3*) audio)->region2_samps(ch,gr_current); } } CSAPI_MPEG unsigned int MPEGfile::preflag (unsigned int ch) { if (layer()!=III) { cerr << "MaaateP: Only Layer 3 has preflag information\n"; return 0; } else { return ((Layer3*) audio)->preflag(ch,gr_current); } } CSAPI_MPEG unsigned int MPEGfile::scalefac_scale (unsigned int ch) { if (layer()!=III) { cerr << "MaaateP: Only Layer 3 has scalefac_scale information\n"; return 0; } else { return ((Layer3*) audio)->scalefac_scale(ch,gr_current); } } CSAPI_MPEG unsigned int MPEGfile::count1table_select(unsigned int ch) { if (layer()!=III) { cerr << "MaaateP: Only Layer 3 has count1table_select information\n"; return 0; } else { return ((Layer3*) audio)->count1table_select(ch,gr_current); } } CSAPI_MPEG unsigned int MPEGfile::scf_band_bound_l(unsigned int subbandindex) { if (layer()!=III) { cerr << "MaaateP: Only Layer 3 has scf_band_bound_l information\n"; return 0; } else { return ((Layer3*) audio)->scf_band_bound_l(subbandindex); } } CSAPI_MPEG unsigned int MPEGfile::scf_band_bound_s(unsigned int subbandindex) { if (layer()!=III) { cerr << "MaaateP: Only Layer 3 has scf_band_bound_s information\n"; return 0; } else { return ((Layer3*) audio)->scf_band_bound_s(subbandindex); } } CSAPI_MPEG unsigned int MPEGfile::slen1(unsigned int ch) { if (layer()!=III) { cerr << "MaaateP: Only Layer 3 has slen1 information\n"; return 0; } else { return ((Layer3*) audio)->slen1(ch,gr_current); } } CSAPI_MPEG unsigned int MPEGfile::slen2(unsigned int ch) { if (layer()!=III) { cerr << "MaaateP: Only Layer 3 has slen2 information\n"; return 0; } else { return ((Layer3*) audio)->slen2(ch,gr_current); } } // returns to the layer-specific parser the number of requested bits // from the audio file CSAPI_MPEG unsigned int MPEGfile::readbitsfrombuffer(unsigned int nobits) { static unsigned int bitmask[33] = { 0, // dummy 0x00000001, 0x00000003, 0x00000007, 0x0000000F, 0x0000001F, 0x0000003F, 0x0000007F, 0x000000FF, 0x000001FF, 0x000003FF, 0x000007FF, 0x00000FFF, 0x00001FFF, 0x00003FFF, 0x00007FFF, 0x0000FFFF, 0x0001FFFF, 0x0003FFFF, 0x0007FFFF, 0x000FFFFF, 0x001FFFFF, 0x003FFFFF, 0x007FFFFF, 0x00FFFFFF, 0x01FFFFFF, 0x03FFFFFF, 0x07FFFFFF, 0x0FFFFFFF, 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF }; unsigned int val = 0; // resulting value unsigned int bufferindex = bitsread / 32; // index of current word unsigned int whichbit = bitsread % 32; // current bitnumber within word unsigned int intlength = whichbit+nobits; // last bit to be read bitsread += nobits; // remember until where read if (nobits > 32 || bufferindex>=MAX_BUFSIZE) { cerr << "MaaateP: Error reading from bitstream (" << nobits << "," << bufferindex << ")" << endl; return 0; } if (intlength <= 32) { // all bits contained in buffer[bufferindex] val = (buffer[bufferindex] >> (32 - intlength)) & bitmask[nobits]; } else { // bits are in buffer[bufferindex] and buffer[bufferindex+1] unsigned int fromfirst = 32-whichbit; unsigned int fromsec = intlength-32; val = (buffer[bufferindex] & bitmask[fromfirst]) << fromsec; val |= (buffer[bufferindex+1] >> (32-fromsec)) & bitmask[fromsec]; } return val; } CSAPI_MPEG int MPEGfile::nb_granules() { if (layer()==I) { return 1; } else if (layer()==II) { return 3; // three granules } else { // layer==III HIGH res return ((Layer3*)audio)->granules(); } }