- 浏览: 100586 次
- 性别:
- 来自: 北京
最新评论
-
wangshengyuan:
非常感谢博主,真是站在了巨人的肩膀上了
(五)用JAVA编写MP3解码器——解析文件信息 -
落枫飘飘:
楼主,你的播放器下载地址http://jmp123.sf.n ...
(附)用JAVA编写MP3解码器——GUI -
lfp001:
解码纯短块的增益因子时,解码得到12个频带的值。在逆量化纯短块 ...
(十)用JAVA编写MP3解码器——逆量化和重排序 -
lfp001:
逆量化混合块时:前8个频带是长块,用长块公式逆量化;后9个频带 ...
(八)用JAVA编写MP3解码器——解码增益因子 -
clearstarrysky:
短块:576个频谱值分为13个增益因子频带,但是在解码增益因子 ...
(十)用JAVA编写MP3解码器——逆量化和重排序
1.解析帧头 帧头共4字节,从高位到低位这32比特的含义如下:
比特数 | 名称 | 内容 |
11 | sync | 0x7FF |
2 | version | 1=mpeg1.0, 0=mpeg2.0 |
2 | lay | 4-lay = layerI, II or III |
1 | error protection | 0=yes, 1=no |
4 | bitrate_index | 见下文 |
2 | sampling_freq | 见下文 |
1 | padding | 填充位 |
1 | extension | 见下文 |
2 | mode | 见下文 |
2 | mode_ext | 联合立体声(joint stereo)模式 |
1 | copyright | 0=no 1=yes |
1 | original | 0=no 1=yes |
2 | emphasis | 预加重 |
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以上才能流畅播放。
Layer\值 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
layer1 | 32 | 64 | 96 | 128 | 160 | 192 | 224 | 256 | 288 | 320 | 352 | 384 | 416 | 448 |
layer2 | 32 | 48 | 56 | 64 | 80 | 96 | 112 | 128 | 160 | 192 | 224 | 256 | 320 | 384 |
layer3 | 32 | 40 | 48 | 56 | 64 | 80 | 96 | 112 | 128 | 160 | 192 | 224 | 256 | 320 |
Layer\值 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
layer1 | 32 | 48 | 56 | 64 | 80 | 96 | 112 | 128 | 144 | 160 | 176 | 192 | 224 | 256 |
layer2 | 8 | 16 | 24 | 32 | 40 | 48 | 56 | 64 | 80 | 96 | 112 | 128 | 144 | 160 |
layer3 | 8 | 16 | 24 | 32 | 40 | 48 | 56 | 64 | 80 | 96 | 112 | 128 | 144 | 160 |
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 其值表示采用哪种联合立体声方式
extension | intensity_stereo | ms_stereo |
00 | off | off |
01 | on | of |
10 | of | on |
11 | on | on |
帧头信息解码除解码上述信息外,还要进行帧同步、计算帧长、计算帧边信息长度等供后续解码。
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; //syncword cur_mask |= h & 0x180000; //intVersionID cur_mask |= h & 0x60000; //intLayer cur_mask |= h & 0x60000; //intSamplingFrequency if(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_stereo boolMS_Stereo * '00' off off * '01' on off * '10' off on * '11' on on */ private static int intModeExtension; private static int intFrameSize; private static int intMainDataBytes; //main_data length private static int intSideInfoSize; //side_information length private 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; else intSideInfoSize = (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; //syncword cur_mask |= h & 0x180000; //intVersionID cur_mask |= h & 0x60000; //intLayer cur_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_word if (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 frames longFrames = makeInt32(b, iOff); iOff += 4; System.out.println("track frames: " + longFrames + " [" + new String(b,0,4) + "]"); } if ((xing_flags & 0x2) != 0) { // track bytes longAllFrameSize = 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 quality int 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 bytes int 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 bytes if(iTagSize - iOff < 36 || b[iOff] == 0) { strBitRate = "VBR"; b = null; return true; } //Encoder Version: 9 bytes String 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:reserved int lame_vbr = b[iOff++] & 0xf; //0:unknown //Lowpass filter value(低通滤波上限值): 1 byte int lowpass = b[iOff++] & 0xff; System.out.println(" lowpass: " + (lowpass * 100) + "Hz" +" [revi "+revi+"]"); //Replay Gain(回放增益):8 bytes float peak = Float.intBitsToFloat(makeInt32(b, iOff)); //Peak signal amplitude iOff += 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 album boolean nogap_cont = ((enc_flag & 0x8) == 0) ? false : true;*/ iOff++; // ABR/CBR位率或VBR的最小位率(0xFF表示位率为255Kbps以上): 1 byte int lame_bitrate = b[iOff++] & 0xff; switch (lame_vbr) { case 1: case 8: // CBR strBitRate = String.format("CBR %1$dK", getBitrate()); break; case 2: case 9: // ABR if(lame_bitrate < 0xff) strBitRate = String.format("ABR %1$dK", lame_bitrate); else strBitRate = String.format("ABR %1$dK以上", lame_bitrate); break; default: // 0: unknown is VBR ? if(lame_bitrate == 0) //unknown strBitRate = "VBR"; else strBitRate = String.format("VBR %1$dK以上", lame_bitrate); } //Encoder delays: 3 bytes iOff += 3; //Misc: 1 byte iOff++; //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.83833951 int mp3_gain = b[iOff++]; //其缺省值为0 if(mp3_gain != 0) System.out.println(" MP3 Gain: " + mp3_gain); //Preset and surround info: 2 bytes int preset_surround = ((b[iOff] & 0xff) << 8) | (b[iOff+1] & 0xff); int surround_info = (preset_surround >> 11) & 0x7; switch(surround_info) { case 0: //no surround info break; case 1: //DPL encoding System.out.println(" surround: DPL"); break; case 2: //DPL2 encoding System.out.println(" surround: DPL2"); break; case 3: //Ambisonic encoding System.out.println(" surround: Ambisonic"); break; case 7: // reserved System.out.println(" surround: invalid data"); break; } preset_surround &= 0x7ff; //11 bits: 2047 presets if(preset_surround != 0) //0: unknown / no preset used System.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 bytes iOff += 2; //CRC-16 of Info Tag: 2 bytes b = 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; else percent = (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/
评论
官方的syncword 是 1111 1111 1111 而不是 11个 1,versionID只有一位
你看得很仔细。帧头同步字和版本共13位,iso 11172-3规定同步字12位(全为1),版本(MPEG-1或MPEG-2)占一位。为了扩展MPEG-1或MPEG-2音频流在低位率压缩时的性能,有一民间版本MPEG-2.5,为了兼容MPEG-1和MPEG-2那么帧头同步字更改为11位(全为1),用2位表示MPEG的3种版本。
这已经够充分了,具体还得自己看文档和代码
2 version 1=mpeg1.0, 0=mpeg2.0
这个描述是不正确的
官方的syncword 是 1111 1111 1111 而不是 11个 1,versionID只有一位
后来就把这事给忘记了。
VBR和ABR每一帧的位率不一定相同,不能一次计算出所有帧的位率。
return (int)((medFrameSize * (double)getFrequency()) / (1000.0 * ((getLayerIndex()==3) ? 12.0 : 144.0)));
不存在的话就查表
这种方法计算当前帧的位率是正确的,但VBR和ABR每一帧的位率不一定相同,如果是用LAME压缩的MP3的话,通过读LAME tag可以知道其最小的位率是多大,最高位率无法知道,除非去扫描每一帧得到最大位率。
return (int)((medFrameSize * (double)getFrequency()) / (1000.0 * ((getLayerIndex()==3) ? 12.0 : 144.0)));
不存在的话就查表
<p>这是酷狗的<br> <br><img src="http://dl.iteye.com/upload/attachment/311756/ccb52994-4594-3d93-8aed-dd9b84a95e8d.png" alt=""></p>
<p> </p>
<p>这是你的程序的<br> <br><img src="http://dl.iteye.com/upload/attachment/311758/5e8a3f3e-ee12-32e6-b522-d662cb277fa8.png" alt=""></p>
<p> </p>
<p> </p>
<p>这是那首歌曲的下载地址:<a href="http://upwap.ru/1035149">http://upwap.ru/1035149</a><br> </p>
通过查表得到位率。
你用其它的播放器播放试试看,是否真的是320Kbps。
解码类的代码, 基本不看编码/解码规范 注释啊, 原理啊基本是没有大的作用的。 看明白规范, 基本代码也就出来了。
表示帧头信息的几个变量的含义、占几比特都作了注解。
移位操作就是从帧头的4字节中取相关的那几比特出来。第一个表格解释了帧头4字节中哪几位表示什么含义。
原理后文陆续作简介。
发表评论
-
(附)用JAVA编写MP3解码器——GUI
2010-12-24 19:32 4315以下代码是开源(GPL)程序jmp123的一部分。 ... -
获取网络MP3真实地址
2010-09-20 00:12 1684MP3网站的歌曲链接都采用了不同的加密方法,直接从页面的源 ... -
jmp123修改日志
2010-09-10 22:36 538【2012-09-23】 jmp123 0.4 ... -
(十八)用JAVA编写MP3解码器——迷你播放器
2010-08-30 18:01 57311.定义解码一帧的接口 ILayer123 ... -
(十七)用JAVA编写MP3解码器——解码Layer1
2010-08-29 14:32 3859Layer1的编码更简单,解码端的代码也就比La ... -
(十六)用JAVA编写MP3解码器——解码Layer2
2010-08-29 14:17 4116MPEG 1.0/2.0/2.5 对声音的压缩分 ... -
(十五)用JAVA编写MP3解码器——音频输出
2010-08-29 13:40 5347解码一帧Layer3第10步:音频输出 -- class ... -
(十四)用JAVA编写MP3解码器——多相合成滤波
2010-08-29 10:03 3555解码一帧Layer3第8步:多相频率倒置 (Inverse ... -
(十三)用JAVA编写MP3解码器——IMDCT快速算法
2010-08-28 18:14 2881解码一帧Layer3第7步:IMDCT和子带混合 -- cl ... -
(十二)用JAVA编写MP3解码器——消混叠处理
2010-08-26 19:37 2460解码一帧Layer3第6步:消混叠处理 -- class L ... -
(十一)用JAVA编写MP3解码器——立体声处理
2010-08-25 14:32 2875解码一帧Layer3第5步: ... -
(十)用JAVA编写MP3解码器——逆量化和重排序
2010-08-22 15:20 3140解码一帧Layer3第4步:逆量化和重排序 -- requa ... -
(九)用JAVA编写MP3解码器——哈夫曼解码
2010-08-21 16:21 3448解码一帧Layer3第3步:哈夫曼解码 -- huffman ... -
(八)用JAVA编写MP3解码器——解码增益因子
2010-08-20 15:39 2365解码一帧Layer3第2步:解码增益因子 -- getSc ... -
(七)用JAVA编写MP3解码器——解码帧边信息
2010-08-20 11:39 2703解码一帧Layer3第1步: ... -
(六)用JAVA编写MP3解码器——帧数据结构
2010-08-19 20:39 3401MP3文件按帧(frame)依次存储,解码时也是 ... -
(五)用JAVA编写MP3解码器——解析文件信息
2010-08-19 11:34 4913前文提到解析MP3标签,程序源码中也已经出现了调 ... -
(四)用JAVA编写MP3解码器——读取文件
2010-08-18 13:28 36301.随机文件访问接口 对MP3解码时需要随机读 ... -
(三)用JAVA编写MP3解码器——读取位流
2010-08-17 23:17 3460文件以字节为单位读取,MP3解码器输入的数据是位流,即每 ... -
(一)用JAVA编写MP3解码器——前言
2010-08-17 21:04 18948【内容提要】 《用JAVA编写MP3解码器》系列文章将 ...
相关推荐
综上所述,"jmpg123.tar.gz_jmpg1_jmpg123_mp1_mp2_mp3 解码"涉及到的是用Java编写的MP1、MP2和MP3音频解码器的源代码,其在多媒体处理领域有广泛应用前景,同时也展示了Java在实现跨平台音频处理上的能力。...
这个压缩包文件的标题揭示了它的核心内容——一个使用jmp123解码器的MP3播放器项目。jmp123是一个开源的、用C语言编写的MP3解码库,它能够解析并解码MP3音频文件,将压缩的数字音频数据转化为可以播放的格式。这个...
用JAVA编写了一个小工具,用于检测当前显示器也就是显卡的显示模式,比如分辨率,色彩以及刷新频率等。 Java波浪文字制作方法及源代码 1个目标文件 摘要:Java源码,初学实例,波浪文字 Java波浪文字,一个利用...
【Java课程设计——电子音乐盒】是一个以Java技术为基础,利用Java Media Framework(JMF)开发的音乐播放软件。这个项目旨在让学生掌握Java编程语言在多媒体应用中的实践,特别是音频处理和用户界面设计方面的能力...
本篇将深入探讨如何使用Java进行逐行读取TXT文件,并提供相关示例代码。 首先,我们需要了解Java中的几个关键类,它们在文件读取过程中扮演着重要角色: 1. `File` 类:代表文件或目录的路径名。可以创建、重命名...
该项目用Java编写,但通过Java绑定,也可以在其他编程语言,如MATLAB中使用。 MATLAB是一个强大的数学计算和数据分析环境,但其原生功能并不包括二维码的处理。在MATLAB中调用ZXing解码二维码,需要进行一些额外的...
【音乐播放器——湘潭大学JAVA课程设计题目】是湘潭大学一门JAVA课程的期末项目,旨在让学生运用所学的JAVA编程知识开发一个功能丰富的音乐播放器。这个播放器不仅包含了基本的音乐播放功能,还增加了多项高级特性,...
今天我们将聚焦于一个名为"jmp123_400_utf8_mini"的JAVA开源项目,它包含了MP3解码器库和播放器,对于那些想要深入理解音频处理和JAVA编程的朋友们来说,这是一个宝贵的资源。 首先,我们来解析一下这个项目的名称...
用JAVA编写了一个小工具,用于检测当前显示器也就是显卡的显示模式,比如分辨率,色彩以及刷新频率等。 Java波浪文字制作方法及源代码 1个目标文件 摘要:Java源码,初学实例,波浪文字 Java波浪文字,一个利用...
用JAVA编写了一个小工具,用于检测当前显示器也就是显卡的显示模式,比如分辨率,色彩以及刷新频率等。 Java波浪文字制作方法及源代码 1个目标文件 摘要:Java源码,初学实例,波浪文字 Java波浪文字,一个利用...
本话题聚焦于一个特定的应用——"用Java语言写的JPEG图象生成器程序",这是一个适用于课程设计和学习管理系统开发的宝贵资源。 首先,我们要理解JPEG(Joint Photographic Experts Group)是一种广泛使用的有损图像...
- 考虑到性能和内存消耗,合理选择FFmpeg的编译选项,如是否包含所有解码器和编码器。 - 处理好异步操作,避免在主线程中执行耗时的原生代码,以免引起UI卡顿。 这个"Android 使用 FFmpeg (一)——编译.so文件...
Java编写的显示器显示模式检测程序 2个目标文件 内容索引:JAVA源码,系统相关,系统信息检测 用JAVA编写了一个小工具,用于检测当前显示器也就是显卡的显示模式,比如分辨率,色彩以及刷新频率等。 Java波浪文字制作...
FFmpeg的JNI接口会被用来在Java层调用C/C++编写的解码器,这样可以充分利用CPU的性能并降低内存消耗。 4. **Android多媒体框架**: Android系统本身提供了一个多媒体框架,包括MediaPlayer类,它可以播放本地和网络...
3. **FFmpeg**:FFmpeg是一个开源的跨平台多媒体处理工具,它包含了各种音频和视频编解码器,可以进行音视频的编码、解码、转换、封装等操作。在直播系统中,FFmpeg用于处理视频源,将其转化为适合网络传输的格式,...
Java编写的显示器显示模式检测程序 2个目标文件 内容索引:JAVA源码,系统相关,系统信息检测 用JAVA编写了一个小工具,用于检测当前显示器也就是显卡的显示模式,比如分辨率,色彩以及刷新频率等。 Java波浪文字制作...
Java二维码(JAVA二维码)是一种广泛应用于移动设备和网络服务中的数据编码技术,它将大量信息如网址、文本、名片等编码成一个二维图形——二维码,然后通过扫描来快速读取和解析这些信息。在Java中处理二维码,通常...
用JAVA编写了一个小工具,用于检测当前显示器也就是显卡的显示模式,比如分辨率,色彩以及刷新频率等。 Java波浪文字制作方法及源代码 1个目标文件 摘要:Java源码,初学实例,波浪文字 Java波浪文字,一个利用...
Java编写的显示器显示模式检测程序 2个目标文件 内容索引:JAVA源码,系统相关,系统信息检测 用JAVA编写了一个小工具,用于检测当前显示器也就是显卡的显示模式,比如分辨率,色彩以及刷新频率等。 Java波浪文字制作...