(十八)用JAVA编写MP3解码器

1.定义解码一帧的接口 ILayer123

  Layer1、Layer2和Layer3这三个类都实现了ILayer123的decodeFrame方法。

// ILayer123.javapackage jmp123.decoder;public interface ILayer123 {public void decodeFrame(int intFirstChannel, int intLastChannel) throws Exception;}

  2.封装解码器 对帧头解码之后可以知道当前待解码的文件是采用MPEG Audio的哪一层压缩方式,根据压缩层的不同,解码器自动初始化Layer1、Layer2和Layer3这三个类中的某一个实例。你也就明白了为什么我们把这三个类的大部分初始化放在其构造方法内的道理了。class Decoder的decodeFrame方法完成解码和播放一帧的任务。

//Decoder.javapackage jmp123.decoder;import jmp123.output.Audio;public final class Decoder {public final static int CH_LEFT = 0;public final static int CH_RIGHT = 1;public final static int CH_BOTH = 2;private static int intFirstChannel, intLastChannel;private static int intChannels;private static int intOutputChannel;private static int intForwardMultiple;private ILayer123 layer123;public Decoder(BitStream objBS, Header objHeader) {intChannels = objHeader.getChannels();switch(objHeader.getLayer()) {case 1:layer123 = new Layer1(objBS, objHeader);break;case 2:layer123 = new Layer2(objBS, objHeader);break;case 3:layer123 = new Layer3(objBS, objHeader);break;}// 设置参数缺省值intForwardMultiple = 1;setOutputChannel(CH_BOTH);}public void decodeFrame() throws Exception {layer123.decodeFrame(intFirstChannel, intLastChannel);if(intChannels == 1 && intOutputChannel == CH_BOTH) {int i;byte[] buf = Synthesis.bytePCMBuf;for(i = 0; i < 4608; i += 4) {buf[i+2] = buf[i];buf[i+3] = buf[i+1];}}if(intForwardMultiple == 1)Audio.write(Synthesis.bytePCMBuf, 4608);else {//变速变调?int i, k = 0, i0, i1, N = intForwardMultiple;byte[] buf = Synthesis.bytePCMBuf;for(i = 0; i < 4608; i += N << 2) {i0 = (buf[1+i] << 8) | (buf[2+i] & 0xff);i1 = (buf[3+i] << 8) | (buf[4+i] & 0xff);buf[k++] = (byte)i0; buf[k++] = (byte)(i0 >>> 8);buf[k++] = (byte)i1; buf[k++] = (byte)(i1 >>> 8);}Audio.write(buf, k);}}/** intWhichChannel – 输出的声道: CH_LEFT/CH_RIGHT/CH_BOTH*/public static void setOutputChannel(int intWhichChannel) {intOutputChannel = intWhichChannel;switch (intWhichChannel) {case CH_LEFT:intFirstChannel = intLastChannel = 0;break;case CH_RIGHT:intFirstChannel = intLastChannel = 1;break;case CH_BOTH:default:if(intChannels == 1)intFirstChannel = intLastChannel = 0;else {intFirstChannel = 0;intLastChannel = 1;}break;}}public static void setForwardMultiple(int intMultiple) {if(intMultiple < 1 || intMultiple > 8)intMultiple = 1;intForwardMultiple = intMultiple;}}

解码、播放一个文件的类class PlayingThread:

// PlayingThread.javapackage jmp123.player;import jmp123.decoder.BitStream;import jmp123.decoder.Decoder;import jmp123.decoder.Header;import jmp123.instream.IRandomAccess;import jmp123.instream.BuffRandAcceFile;import jmp123.instream.BuffRandAcceURL;import jmp123.output.Audio;public final class PlayingThread implements Runnable {private Decoder objDec123;private Header objHeader;private IRandomAccess objIRA;public PlayingThread(String strFileName) throws Exception {strFileName.toLowerCase();if (strFileName.startsWith(""))objIRA = new BuffRandAcceURL(strFileName);elseobjIRA = new BuffRandAcceFile(strFileName);objHeader = new Header(objIRA);}public void run() {int frame_count = 0;Thread.currentThread().setPriority(Thread.NORM_PRIORITY + 2);try {if (objHeader.syncFrame() == false)return;objHeader.printHeaderInfo();objDec123 = new Decoder(new BitStream(objIRA), objHeader);Audio.open(objHeader.getFrequency());// Playingwhile (true) {objDec123.decodeFrame();if (objHeader.syncFrame() == false)break;if ((++frame_count & 0x7) == 0x7)//每8帧(44.1kHz,时长约0.2s)更新一次objHeader.printState();}objHeader.printState();Audio.close();} catch (Exception e) {//e.printStackTrace();}objIRA.close();}}

mpg123的README文件 写道

mpg123 is fast. Any faster software player is probably based on some hacked mpg123;-)

  PlayingThread类完成了对播放一个文件的封装,实现了Runnable接口是为了便于实现以“后台”方式解码、播放。

  class PlayingThread中的这一行:

Thread.currentThread().setPriority(Thread.NORM_PRIORITY + 2);

为解码播放线程设置了更高的优先级,这对流畅解码播放是很有必要的,防止你在运行其它稍复杂的任务时播放时断时续。

  命令行播放器class Player :

爱人,却不一定能够听懂。他们听见的,多是抱怨不休,心烦意乱。

(十八)用JAVA编写MP3解码器

相关文章:

你感兴趣的文章:

标签云: