(二)用JAVA编写MP3解码器——帧头信息解码

来源:互联网 发布:好看的末世小说知乎 编辑:程序博客网 时间:2024/05/01 18:51

1.解析帧头   帧头共4字节,从高位到低位这32比特的含义如下:

比特数名称内容11sync0x7FF2version1=mpeg1.0, 0=mpeg2.02lay4-lay = layerI, II or III1error protection0=yes, 1=no4bitrate_index见下文2sampling_freq见下文1padding填充位1extension见下文2mode见下文2mode_ext联合立体声(joint stereo)模式1copyright0=no 1=yes1original0=no 1=yes2emphasis预加重

  Header.parseHeader(int)方法中的这几行依次解码上面的各个变量:

intVersionID = (h >> 19) & 3;intLayer = 4 - (h >> 17) & 3;intProtectionBit = (h >> 16) & 0x1;intBitrateIndex = (h >> 12) & 0xF;intSamplingFrequency = (h >> 10) & 3;intPaddingBit = (h >> 9) & 0x1;intMode = (h >> 6) & 3;intModeExtension = (h >> 4) & 3;


 

各变量的含义如下:

version  MPEG的版本,本程序支持MPEG 1.0/2.0/2.5,从MPEG 2.0开始支持32Kbps以下的低位率。

 

lay  MPEG Audio的压缩分为I、II、III共3层,Layer III的解码过程最为复杂。

 

error protection  设置为0表示有32位的循环冗余校检(CRC)。

 

bitrate_index   主数据的位率(单位KBits/s),例如对192Kbps的MP3,解码时每秒读取192*1024/8=24576字节的码流,如果你是从网络在线播放要确保每秒下载192/8=24KBytes以上才能流畅播放。

mpeg 1.0Layer\值1234567891011121314layer1326496128160192224256288320352384416448layer2324856648096112128160192224256320384layer332404856648096112128160192224256320

 

mpeg 2.0Layer\值1234567891011121314layer1324856648096112128144160176192224256layer28162432404856648096112128144160layer38162432404856648096112128144160


sampling_freq PCM样本的采样率,用它来初始化音频硬件以播放MP3。
mpeg1.0时其值0,1,2分别对应的采样是44100Hz,48000Hz,32000Hz
mpeg2.0时其值0,1,2分别对应的采样是22050Hz,24000Hz,16000Hz
mpeg2.5时其值0,1,2分别对应的采样是11025Hz,12000Hz,8000Hz


padding  设置为1表示有1字节的填充位,相应帧的长度增加1字节。

 

mode 声道模式,其值表示的含义:
0  立体声(stereo)
1  联合立体声(joint stereo)
2  双声道(dual channel)
3  单声道(single channel)


联合立体声(joint stereo) 采用联合立体声编码方式的两个声道具有关联性。例如MS_stereo将两个声道相加、相差后处理,相减后去掉了左右声道相同的成份,后续的压缩可得到更高的压缩率。


extension 其值表示采用哪种联合立体声方式

extensionintensity_stereoms_stereo00offoff01onof10ofon11onon


  帧头信息解码除解码上述信息外,还要进行帧同步、计算帧长、计算帧边信息长度等供后续解码。

  2.帧同步  (1)帧头的4字节中高11位全部设置为1(11111111 111xxxxx xxxxxxxx xxxxxxxx),用它作为查找帧的重要依据。(2)考虑到MP3文件可能有的数据帧有损坏,帧同步时还要用version、lay、bitrate_index、sampling_freq的值是否合法去检验;(3)每一帧的version、lay、sampling_freq保持不变,把已经解码的帧的这些变量保存起来,用以与下一帧这些变量的值比较;(4)根据当前帧的帧长,移到下一帧去解析下一帧的帧头来确定当前的4字节是否是有效的帧头。如源代码Header.syncFrame()方法中的这些行进行帧同步:

iraInput.read(b4, 0, 4);h = makeInt32(b4, 0);while(!bfind) {// 1.查找帧同步字while((h & intStandardMask) != intStandardMask|| ((h >> 19) & 3) == 1// version ID:  01 - reserved|| ((h >> 17) & 3) == 0// Layer index: 00 - reserved|| ((h >> 12) & 0xf) == 0xf// Bitrate Index: 1111 - reserved|| ((h >> 12) & 0xf) == 0// Bitrate Index: 0000 - free|| ((h >> 10) & 3) == 3)// Sampling Rate Index: 11 - reserved{//...}//...// 2.与下一帧的同步头比较cur_mask = 0xffe00000;//syncwordcur_mask |= h & 0x180000;//intVersionIDcur_mask |= h & 0x60000;//intLayercur_mask |= h & 0x60000;//intSamplingFrequencyif(iraInput.dump(intFrameSize-4, b4, 0, 4) < 4)return false;i = makeInt32(b4, 0);bfind = (i & cur_mask) == cur_mask && ((i >> 19) & 3) != 1&& ((i >> 17) & 3) != 0 && ((i >> 12) & 15) != 15&& ((i >> 12) & 0xf) != 0 && ((i >> 10) & 3) != 3;//...


3.计算帧长  一帧的长度应该用槽(slot)来描述,MPEG 1.0/2.0/2.5 对声音的3种压缩方式Layer1、Layer2和Layer3,每种压缩方式一帧的槽数是固定的,Layer1一槽就是4个字节,Layer2和Layer3一槽就是一个字节,据此可以计算出帧的字节数;

 

  4.计算帧边信息长度  根据MP3帧头解码出的表示立体声编码模式(mode)、MPEG的版本(version)、压缩层(lay)套公式计算。

 

  5.解析VBR信息  见Header.parseVBR()方法,其中各个变量在其官方文档中有详细说明。如果你想了解细节,请查阅其官方文档。

 

Header.javar完整的源码如下:

/** Header.java -- MPEG 1.0/2.0/2.5 Audio Layer I/II/III 帧同步和帧头信息解码* Copyright (C) 2010** 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 3 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, see <http://www.gnu.org/licenses/>.** If you would like to negotiate alternate licensing terms, you may do* so by contacting the author: <http://jmp123.sourceforge.net/>.*/package jmp123.decoder;import jmp123.instream.IRandomAccess;public final class Header {public static final int MPEG1 = 3;public static final int MPEG2 = 2;public static final int MPEG25 = 0;public static final int MAX_FRAMESIZE = 1732;//MPEG 1.0/2.0/2.5, Lay 1/2/3/* * intBitrateTable[intLSF][intLayer-1][intBitrateIndex] */private static final int[][][] intBitrateTable = {{//MPEG 1//Layer I{0,32,64,96,128,160,192,224,256,288,320,352,384,416,448},//Layer II{0,32,48,56, 64, 80, 96,112,128,160,192,224,256,320,384},//Layer III{0,32,40,48, 56, 64, 80, 96,112,128,160,192,224,256,320}},{//MPEG 2.0/2.5//Layer I{0,32,48,56,64,80,96,112,128,144,160,176,192,224,256},//Layer II{0,8,16,24,32,40,48,56,64,80,96,112,128,144,160},//Layer III{0,8,16,24,32,40,48,56,64,80,96,112,128,144,160}}};/* * intSamplingRateTable[intVersionID][intSamplingFrequency] */private static final int[][] intSamplingRateTable = {{11025 , 12000 , 8000,0},//MPEG Version 2.5{0,0,0,0,},//reserved{22050, 24000, 16000 ,0},//MPEG Version 2 (ISO/IEC 13818-3){44100, 48000, 32000,0}//MPEG Version 1 (ISO/IEC 11172-3)};/* * intVersionID: 2 bits * "00"  MPEG Version 2.5 (unofficial extension of MPEG 2); * "01"  reserved; * "10"  MPEG Version 2 (ISO/IEC 13818-3); * "11"  MPEG Version 1 (ISO/IEC 11172-3). */private static int intVersionID;/* * intLayer: 2 bits * "11" Layer I * "10" Layer II * "01" Layer III * "00" reserved * 已换算intLayer=4-intLayer: 1-Layer I; 2-Layer II; 3-Layer III; 4-reserved */private static int intLayer;/* * intProtectionBit: 1 bit * "1"  no CRC; * "0"  protected by 16 bit CRC following header. */private static int intProtectionBit;/*  * intBitrateIndex: 4 bits */private static int intBitrateIndex;/* * intSamplingFrequency: 2 bits * '00' 44.1kHz * '01' 48kHz * '10' 32kHz * '11'  reserved */private static int intSamplingFrequency;private static int intPaddingBit;/* * intMode: 2 bits * '00'  Stereo; * '01'  Joint Stereo (Stereo); * '10'  Dual channel (Two mono channels); * '11'  Single channel (Mono). */private static int intMode;/* * intModeExtension: 2 bits *  intensity_stereoboolMS_Stereo * '00' offoff * '01' onoff * '10' offon * '11' onon */private static int intModeExtension;private static int intFrameSize;private static int intMainDataBytes;//main_data lengthprivate static int intSideInfoSize;//side_information lengthprivate static int intLSF;private static int intStandardMask = 0xffe00000;private static boolean boolMS_Stereo, boolIntensityStereo;private static IRandomAccess iraInput;public Header(IRandomAccess in_rai) {iraInput = in_rai;}private void parseHeader(int h) {intVersionID = (h >> 19) & 3;intLayer = 4 - (h >> 17) & 3;intProtectionBit = (h >> 16) & 0x1;intBitrateIndex = (h >> 12) & 0xF;intSamplingFrequency = (h >> 10) & 3;intPaddingBit = (h >> 9) & 0x1;intMode = (h >> 6) & 3;intModeExtension = (h >> 4) & 3;boolMS_Stereo = intMode == 1 && (intModeExtension & 2) != 0;boolIntensityStereo = intMode == 1 && (intModeExtension & 0x1) != 0;intLSF = (intVersionID == MPEG1) ? 0 : 1;switch (intLayer) {case 1:intFrameSize  = intBitrateTable[intLSF][0][intBitrateIndex] * 12000;intFrameSize /= intSamplingRateTable[intVersionID][intSamplingFrequency];intFrameSize  = ((intFrameSize+intPaddingBit)<<2);break;case 2:intFrameSize  = intBitrateTable[intLSF][1][intBitrateIndex] * 144000;intFrameSize /= intSamplingRateTable[intVersionID][intSamplingFrequency];intFrameSize += intPaddingBit;break;case 3:intFrameSize  = intBitrateTable[intLSF][2][intBitrateIndex] * 144000;intFrameSize /= intSamplingRateTable[intVersionID][intSamplingFrequency]<<(intLSF);intFrameSize += intPaddingBit;//计算帧边信息长度if(intVersionID == MPEG1)intSideInfoSize = (intMode == 3) ? 17 : 32;elseintSideInfoSize = (intMode == 3) ? 9 : 17;break;}//计算主数据长度intMainDataBytes = intFrameSize - 4 - intSideInfoSize;if(intProtectionBit == 0)intMainDataBytes -= 2;}private static void headerCRC() throws Exception {if(iraInput.read() == -1 || iraInput.read() == -1)throw new Exception("crc() 文件读完");}private static int makeInt32(byte[] b, int off) {int h = b[off] & 0xff;h <<= 8;h |= b[off + 1] & 0xff;h <<= 8;h |= b[off + 2] & 0xff;h <<= 8;h |= b[off + 3] & 0xff;return h;}private static int intFrameCounter;//当前帧序号private static boolean boolSync;//true:帧头的特征未改变private static final byte[] b4 = new byte[4];/* * 帧同步: 查找到帧同步字后与下一帧的intVersionID等比较,确定是否找到有效的同步字. */public boolean syncFrame()  throws Exception{int h, idx = 0, i, cur_mask = 0;iraInput.read(b4, 0, 4);h = (b4[0]<<24) | ((b4[1] & 0xff)<<16) | ((b4[2] & 0xff)<<8) | (b4[3] & 0xff);while(true) {// 1.查找帧同步字while((h & intStandardMask) != intStandardMask|| ((h >> 19) & 3) == 1// version ID:  01 - reserved|| ((h >> 17) & 3) == 0// Layer index: 00 - reserved|| ((h >> 12) & 0xf) == 0xf// Bitrate Index: 1111 - reserved|| ((h >> 12) & 0xf) == 0// Bitrate Index: 0000 - free|| ((h >> 10) & 3) == 3)// Sampling Rate Index: 11 - reserved{if((i = iraInput.read()) == -1)return false;idx++;h = (h << 8) | i;}if (idx > 0)boolSync = false;// 2. 解析帧头parseHeader(h);//若intVersionID等帧的特征未改变(boolSync=true),不用与下一帧的同步头比较.if(boolSync)break;if(idx >= 0xffff) {System.out.println("\n搜索 64K 未发现MP3帧后放弃。");return false;}// 3.与下一帧的同步头比较cur_mask = 0xffe00000;//syncwordcur_mask |= h & 0x180000;//intVersionIDcur_mask |= h & 0x60000;//intLayercur_mask |= h & 0x60000;//intSamplingFrequency//cur_mask |= h & 0xC0;//intMode//intMode,intModeExtension 不是始终不变.if(iraInput.dump(intFrameSize-4, b4, 0, 4) < 4)return false;i = makeInt32(b4, 0);if( (i & cur_mask) == cur_mask && ((i >> 19) & 3) != 1&& ((i >> 17) & 3) != 0 && ((i >> 12) & 15) != 15&& ((i >> 12) & 0xf) != 0 && ((i >> 10) & 3) != 3 )break;idx++;h = (h << 8) | iraInput.read();}//if(idx > 0)//System.out.println("frs="+intFrameCounter+",skip bytes:" + idx);if(boolSync == false) {boolSync = true;if(intStandardMask == 0xffe00000) {//是第一帧...longFrameOffset = iraInput.getFilePointer();longAllFrameSize = iraInput.length() - longFrameOffset;longFrames = longAllFrameSize / intFrameSize;parseVBR();//若有VBR tag以上3个变量将被改写intStandardMask = cur_mask;floatFrameDuration = 1152f / (getFrequency() << intLSF);}}if (intProtectionBit == 0)headerCRC();intFrameCounter++;return true;}public boolean isMSStereo() {return boolMS_Stereo;}public boolean isIStereo() {return boolIntensityStereo;}public int getBitrate() {return intBitrateTable[intLSF][intLayer-1][intBitrateIndex];}public int getBitrateIndex() {return intBitrateIndex;}public int getChannels() {return intMode == 3 ? 1 : 2;}public int getMode() {return intMode;}public int getModeExtension() {return intModeExtension;}public int getVersion() {return intVersionID;}public int getLayer() {return intLayer;}public int getSampleFrequency() {return intSamplingFrequency;}public int getFrequency() {return intSamplingRateTable[intVersionID][intSamplingFrequency];}public int getMainDataBytes() {return intMainDataBytes;}public int getSideInfoSize() {return intSideInfoSize;}public int getFrameSize() {return intFrameSize;}public int getFrameCounter() {return intFrameCounter;}// MP3 文件帧数等信息private static long longAllFrameSize;//帧长度总和(文件长度减去ID3 tag, APE tag 等长度)private static long longFrameOffset;//第一帧的偏移量private static long longFrames;//帧数private static float floatFrameDuration;//一帧时长(秒)private static String strDuration;public long getTrackFrames() {return longFrames;}/* * 返回MP3文件时长(秒) */public float getDuration() {return floatFrameDuration * longFrames;}/** 解码存储在第一帧的VBR信息.若第一帧存储的是VBR信息,帧边信息被填充为零,不解* 码VBR tag而把这一帧作为音频帧不影响正常解码.*/private boolean boolVBRtag;private byte[] byteVBRToc;private int intTocNumber, intTocPer, intTocFactor;private String strBitRate;private boolean parseVBR() throws Exception {int iTagSize = intFrameSize - intSideInfoSize;if (iTagSize < 124)return false;byte[] b = new byte[iTagSize];iraInput.dump(0, b, 0, intSideInfoSize);for (int i = 2; i < intSideInfoSize; i++)//前2字节可能是CRC_wordif (b[i] != 0) {b = null;return false;}iraInput.dump(intSideInfoSize, b, 0, iTagSize);//-------------------------------VBR tag------------------------------int iOff = 0;if ((b[0] == 'X' && b[1] == 'i'&& b[2] == 'n' && b[3] == 'g') ||(b[0] == 'I' && b[1] == 'n' && b[2] == 'f' && b[3] == 'o')) {//--------Xing/Info header--------boolVBRtag = true;longAllFrameSize -= intFrameSize;longFrameOffset += intFrameSize;int xing_flags = makeInt32(b, 4);iOff = 8;if ((xing_flags & 1) == 1) { // track frameslongFrames = makeInt32(b, iOff);iOff += 4;System.out.println("track frames: " + longFrames +"  [" + new String(b,0,4) + "]");}if ((xing_flags & 0x2) != 0) { // track byteslongAllFrameSize = makeInt32(b, iOff);iOff += 4;System.out.println(" track bytes: " + longAllFrameSize);}if ((xing_flags & 0x4) != 0) { // TOC: 100 bytes.byteVBRToc = new byte[100];System.arraycopy(b, iOff, byteVBRToc, 0, 100);iOff += 100;//System.out.println("         TOC: true");}if ((xing_flags & 0x8) != 0) { // VBR qualityint xing_quality = makeInt32(b, iOff);iOff += 4;System.out.println("     quality: " + xing_quality);}intTocNumber = 100;//TOC共100个表项intTocPer = 1;//每个表项1字节intTocFactor = 1;} else if(b[0] == 'V' && b[1] == 'B' && b[2] == 'R' && b[3] == 'I') {//--------VBRI header--------//version ID: 2 bytes//Delay: 2 bytesint vbri_quality = (b[8] & 0xff) | (b[9] & 0xff);System.out.println("     quality: " + vbri_quality +"  [" + new String(b,0,4) + "]");longAllFrameSize = makeInt32(b, 10);System.out.println(" track bytes: " + longAllFrameSize);longFrames = makeInt32(b, 14);System.out.println("track frames: " + longFrames);intTocNumber = (b[18] & 0xff) | (b[19] & 0xff);intTocFactor = (b[20] & 0xff) | (b[21] & 0xff);intTocPer = (b[22] & 0xff) | (b[23] & 0xff);//int toc_frames = (b[24] & 0xff) | (b[25] & 0xff);//每个TOC表项的帧数int toc_size = intTocNumber * intTocPer;iOff = 26 + toc_size;System.out.println("         TOC: " + intTocNumber + " * " +intTocPer + " = " + toc_size + "factor=" + intTocFactor);if (intFrameSize - intSideInfoSize < iOff)return false;byteVBRToc = new byte[toc_size];System.arraycopy(b, 26, byteVBRToc, 0, toc_size);} else {b = null;return false;}//-------------------------------LAME tag------------------------------//9+1+1+8+1+1+3+1+1+2+4+2+2=36 bytesif(iTagSize - iOff < 36 || b[iOff] == 0) {strBitRate = "VBR";b = null;return true;}//Encoder Version: 9 bytesString strEncoder = new String(b, iOff, 9);iOff += 9;System.out.println("     encoder: " + strEncoder);//'Info Tag' revision + VBR method: 1 byte//boolean isCBR=false, isABR=false, isVBR=false;int revi = (b[iOff] & 0xff) >> 4;//0:rev0; 1:rev1; 15:reservedint lame_vbr = b[iOff++] & 0xf;//0:unknown//Lowpass filter value(低通滤波上限值): 1 byteint lowpass = b[iOff++] & 0xff;System.out.println("     lowpass: " + (lowpass * 100) + "Hz" +"  [revi "+revi+"]");//Replay Gain(回放增益):8 bytesfloat peak = Float.intBitsToFloat(makeInt32(b, iOff));//Peak signal amplitudeiOff += 4;int radio = ((b[iOff] & 0xff) << 8) | (b[iOff+1] & 0xff);//Radio Replay Gain/** radio:* bits 0h-2h: NAME of Gain adjustment:*000 = not set*001 = radio*010 = audiophile* bits 3h-5h: ORIGINATOR of Gain adjustment:*000 = not set*001 = set by artist*010 = set by user*011 = set by my model*100 = set by simple RMS average* bit 6h: Sign bit* bits 7h-Fh: ABSOLUTE GAIN ADJUSTMENT.*  storing 10x the adjustment (to give the extra decimal place).*/iOff += 2;int phile = ((b[iOff] & 0xff) << 8) | (b[iOff+1] & 0xff);//Audiophile Replay Gain/** phile各位含义同上(radio)*/iOff += 2;//Encoding flags + ATH Type: 1 byte/*int enc_flag = (b[iOff] & 0xff) >> 4;int ath_type = b[iOff] & 0xf;//000?0000: LAME uses "--nspsytune" ?boolean nsp = ((enc_flag & 0x1) == 0) ? false : true;//00?00000: LAME uses "--nssafejoint" ?boolean nsj = ((enc_flag & 0x2) == 0) ? false : true;//0?000000: This track is --nogap continued in a next track ?//is true for all but the last track in a --nogap album boolean nogap_next = ((enc_flag & 0x4) == 0) ? false : true;//?0000000: This track is the --nogap continuation of an earlier one ?//is true for all but the first track in a --nogap albumboolean nogap_cont = ((enc_flag & 0x8) == 0) ? false : true;*/iOff++;// ABR/CBR位率或VBR的最小位率(0xFF表示位率为255Kbps以上): 1 byteint lame_bitrate = b[iOff++] & 0xff;switch (lame_vbr) {case 1:case 8: // CBRstrBitRate = String.format("CBR %1$dK", getBitrate());break;case 2:case 9: // ABRif(lame_bitrate < 0xff)strBitRate = String.format("ABR %1$dK", lame_bitrate);elsestrBitRate = String.format("ABR %1$dK以上", lame_bitrate);break;default: // 0: unknown is VBR ?if(lame_bitrate == 0)//unknownstrBitRate = "VBR";elsestrBitRate = String.format("VBR %1$dK以上", lame_bitrate);}//Encoder delays: 3 bytesiOff += 3;//Misc: 1 byteiOff++;//MP3 Gain: 1 byte. //任何MP3能无损放大2^(mp3_gain/4).以1.5dB为步进值改变'Replay Gain'的3个域://'Peak signal amplitude', 'Radio Replay Gain', 'Audiophile Replay Gain'//mp3_gain = -127..+127, 对应的://分贝值-190.5dB..+190.5dB; mp3_gain增加1, 增加1.5dB//放大倍数0.000000000276883..3611622602.83833951int mp3_gain = b[iOff++];//其缺省值为0if(mp3_gain != 0)System.out.println("    MP3 Gain: " + mp3_gain);//Preset and surround info: 2 bytesint preset_surround = ((b[iOff] & 0xff) << 8) | (b[iOff+1] & 0xff);int surround_info = (preset_surround >> 11) & 0x7;switch(surround_info) {case 0://no surround infobreak;case 1://DPL encodingSystem.out.println("    surround: DPL");break;case 2://DPL2 encodingSystem.out.println("    surround: DPL2");break;case 3://Ambisonic encodingSystem.out.println("    surround: Ambisonic");break;case 7:// reservedSystem.out.println("    surround: invalid data");break;}preset_surround &= 0x7ff;//11 bits: 2047 presetsif(preset_surround != 0)//0: unknown / no preset usedSystem.out.println("    surround: preset " + preset_surround);iOff += 2;//MusicLength: 4 bytes//MP3文件原始的(即除去ID3 tag,APE tag等)'LAME Tag frame'和'音乐数据'的总字节数int music_len = makeInt32(b, iOff);iOff += 4;if(music_len != 0)longAllFrameSize = music_len;//MusicCRC: 2 bytesiOff += 2;//CRC-16 of Info Tag: 2 bytesb = null;return true;}// -------------------------------------------------------------------// 以下是辅助功能,删除掉源码及相关调用不影响正常播放// -------------------------------------------------------------------// 打印信息public void printHeaderInfo() {String[] sver = {"MPEG 2.5", "reserved", "MPEG 2.0", "MPEG 1.0"};String[] mode_str = {", Stereo",", Joint Stereo",", Dual channel",", Single channel(Mono)"};String[] exmode_str = {"","(I/S)","(M/S)","(I/S & M/S)"};if(strDuration == null) {float duration = getDuration();int m = (int)(duration / 60);strDuration = String.format("%1$02d:%2$02d", m, (int)(duration - m * 60 + 0.5));progress = new StringBuffer(">----------------------------------------");}if(!boolVBRtag)strBitRate = String.format("%1$dK", intBitrateTable[intLSF][intLayer-1][intBitrateIndex]);System.out.println("\r" + sver[intVersionID] + ", Layer " + intLayer + ", " + getFrequency()+"Hz, " + strBitRate +mode_str[intMode] +exmode_str[intModeExtension] + ", " +strDuration);}private static StringBuffer progress;private static int progress_index = 1;public void printState() {float t = intFrameCounter * floatFrameDuration;int m = (int)(t / 60);float s = t - 60 * m;float percent;if(boolVBRtag)percent = (float)intFrameCounter / longFrames * 100;elsepercent = (float)iraInput.getFilePointer() / iraInput.length() * 100;int i = ((int)(percent + 0.5) << 2) / 10;if(i == progress_index) {progress.replace(i-1, i+1, "=>");progress_index++;}System.out.printf("\r%1$02d:%2$04.1f [%3$-41s] %4$.1f%%", m, s, progress, percent);}}


 

【下载地址】http://jmp123.sourceforge.net/

 

1 0
原创粉丝点击