MP3音乐播放器.docx
- 文档编号:10461322
- 上传时间:2023-02-13
- 格式:DOCX
- 页数:24
- 大小:86.36KB
MP3音乐播放器.docx
《MP3音乐播放器.docx》由会员分享,可在线阅读,更多相关《MP3音乐播放器.docx(24页珍藏版)》请在冰豆网上搜索。
MP3音乐播放器
成绩
徐州工程学院
综合训练报告
课程名称面向对象程序设计综合设计
专业计算机科学与技术(单)
班级09计单
学生姓名夏军
学号20090502137
设计题目MP3音乐播放器
指导教师杨兴运
设计起止时间:
2011年12月21日至2012年1月6日
概述
本软件的设计是基于Java语言编写,其中涉及Java语言的内容包括:
图形用户界面GUI、Socket网络编程、多线程、文件的输入/输出、字符串处理、容器、字符串与正则表达式、异常处理、XML文件解析以及面向对象的基本模型。
在实现音乐播放功能上,因为本软件的最基础也是最重要的功能是音乐播放。
然而本次设计的侧重点是对Java语言的运用,所以并不会独立编写一个音频解码器。
只是借助已经编写好的API类库。
Java对于音乐的支持比较麻烦,主要原因是Java跨平台导致。
Java的播放音乐的API主要包括两部分,一是JavaSE里面包含的JavaSoundAPI,二是作为独立于JavaSE之外的JavaMusicFramework。
两者的主要差别在于所支持的声音文件格式不同。
Java平台提供的soundAPI支持的音乐文件有MIDI,RMF(RichMusicFormat)。
声音格式:
8位和16位单声道以及立体声音频数据,采样率在8khz和48khz之间。
并没有对MP3进行支持。
而JavaMusicFramework(JMF)支持播放MP3格式的音乐的播放。
JMF意为Java媒体框架。
该核心框架支持不同媒体(如:
音频输出和视频输出)间的时钟同步。
它是一个标准的扩展框架,允许用户制作纯音频流和视频流。
但JMF需要进行下载安装,我们使用的操作系统上并没有自带,并且在某些联想(Lenovo)的机器上还会安装失败,而且JMF已经年久失修,不推荐使用。
所以本软件使用了另外一个第三方类库Jlayer,它里面自带了两个后台播放器实现Player和AdvancedPlayer。
但在查看该类库的API文档发现,该库在对音乐播放的控制上只支持音乐的停止播放和开始播放,并没有暂停功能。
所以只能自己编写代码实现上述缺陷功能。
在实现音乐暂停时,设计思想是当用户发出暂停命令时,保存当前播放的帧位置,然后关闭声卡;当由暂停状态转为播放状态时,打开音乐文件,然后根据暂停处的帧位置,跳过若干数据流,接着创建解码器对象,将解码器加入声卡对象中,播放当前的音乐文件。
在读取MP3文件的ID3标签信息上使用的是互联网上的一段代码。
在实现对音乐播放控制上,由于音乐播放的过程中需要接受用户的操作命令,这就需要使用多线程技术的支持,使软件可以同时播放音乐和接收用户操作命令。
软件运行时的线程主要包括主线程、音乐文件播放线程、音乐文件播放进度监听线程、歌词搜索线程4个线程。
并这4个线程在运行过程中存在互相控制的情况。
在实现歌词在线下载功能上,本软件借鉴了网络上一段C#代码,对该C#代码使用Java语言进行了改写和改进。
歌词下载的歌词来自一款流行的音乐播放器千千静听的歌词服务器。
歌词下载的基本过程是获取需下载歌词的音乐文件的歌曲名和歌曲作者,然后进行相应处理,得到歌曲的下载链接,使用Java类库中的URL类对象对该网络路径下的歌词进行读取。
并对查找到的歌词保存本地。
一、需求分析
软件界面布局需求
软件的基本布局如图1-1所示。
并且要求软件启动时,其界面必须处于屏幕正中央。
图1-1软件界面布局
音乐播放控制需求
播放控制主要是对播放状态进行控制,为了支持用户更便捷的控制音乐的播放,此功能需要几个控制按钮。
控制按钮代表的功能有:
播放/暂停、停止、上一首、下一首、打开、退出。
其中播放/暂停是复合功能按钮,可将播放状态变为播放或暂停。
其基本布局如图1-2。
当用户启动软件,软件处于就绪状态,不播放任何音乐。
当用户通过打开按钮选择文件后,点击播放按钮,则此时软件播放播放列表中的第一个播放条目。
当处于播放状态时,用户点击播放按钮,则播放状态由播放状态转为暂停状态。
再次点击则再次进入播放状态。
当播放状态为播放或暂停时,点击停止可将其音乐关闭。
点击上一首/下一首按钮可以根据播放列表中的条目进行选择。
点击打开按钮可以进行音乐文件的选择。
点击退出则弹出确认是否退出对话框。
图1-2播放控制布局
播放进度显示需求
当音乐处于播放状态,能够通过进度条显示当前音乐的播放进度。
当某一音乐播放完毕,则进度条归0。
播放列表需求
播放列表中显示加入的歌曲的编号、名称和时长。
并能对加入列表中的播放条目进行编辑、播放、下载歌词、评级等功能。
可将通过文件选择对话框选择的文件加入播放列表中。
歌词面板需求
能将歌词文本显示在歌词面板中,并且为居中对齐。
下载歌词需求
当用户选中播放列表中的播放条目,可以执行下载对应歌词的功能。
并显示在歌词面板上。
二、具体实现
软件界面布局实现
创建一个框架,将框架分成1行2列的布局。
在第1列中加入播放控制面板和播放列表面板。
在第二列中加入歌词面板。
其中播放控制面板宽为1/2框架宽,面板高为1/3框架高;播放列表面板宽为1/2框架宽,面板高位2/3框架高;歌词面板宽为1/2框架宽,面板高为框架高。
对于启动时处于正中央,实现手段可以是:
首先获取当前计算机屏幕的宽高。
然后得到框架的宽和高为屏幕的1/2。
具体代码:
classMusicFrameextendsJFrame{
privateListPanellistPanel;//播放列表面板
privatePlayPanelplayPanel;//播放控制面板
privateLRCPanellrcPanel;//歌词面板
Toolkitkit=Toolkit.getDefaultToolkit();
DimensionscreenSize=kit.getScreenSize();
intscreenHeight=screenSize.height;//获取屏幕大小
intscreenWidth=screenSize.width;
setResizable(false);
setBounds(screenWidth/4,screenHeight/4-100,screenWidth/2,screenHeight/2+100);//设置框架启动位置和大小
setLayout(newGridLayout(1,2));
JPanelleftSpace=newJPanel();
JPanelrightSpace=newJPanel();
listPanel=newListPanel(this);
playPanel=newPlayPanel(this);
lrcPanel=newLRCPanel(this);
leftSpace.add(playPanel);
leftSpace.add(listPanel);
rightSpace.add(lrcPanel);
}
音乐播放控制实现
音乐播放控制是整个设计环节中最复杂最具挑战性的一环,因为音乐播放控制不仅要控制音乐播放线程的播放,还需要同时处理来自按钮和播放列表发来的操作命令。
首先实现布局问题:
首先6个按钮控件,并将其加入面板内。
为了让6个按钮能够响应动作,则需要编写对应的监听器。
为了方便起见,只编写了一个监听方法,并在方法内比对捕获的事件对象,从而确定执行相应的代码段。
但《Java核心技术》一书中说这种方法并不好,不利于国际化。
这里就姑且偷懒一次吧!
下面实现各个按钮的功能。
播放/暂停按钮:
播放暂停按钮实现音乐的播放和暂停控制。
当用户点击播放时,判断当前播放列表中是否有播放条目。
如果有则播放第一条音乐记录。
音乐播放的具体过程:
首先获取到音乐文件路径,接着创建一个播放线程PlayThread对象。
将音乐路径加入其中。
执行播放的线程PlayThread的代码如下:
publicclassPlayThreadimplementsRunnable{
privateStringfilePath;//音乐文件的路径
privateMyPlayerplayer;//播放音乐的播放器
privateLongtotalFrame;//音乐文件的总帧数
privateThreadplayerThread;//执行播放音乐的线程
privatebooleancomplete;//判断当前音乐是否播放完毕
/*----------------初始化参数-------------*/
publicPlayThread(StringfilePath){
this.filePath=filePath;
this.playerInitialize();
this.totalFrame=player.getTotalFrame();
}
privatevoidplayerInitialize(){
/*------------将播放路径加入到播放器MyPlayer类对象中------*/
try{
StringurlAsString="file:
///"
+filePath;
this.player=newMyPlayer(new.URL(urlAsString));
}catch(Exceptionex){
ex.printStackTrace();
}
}
/*--------------执行播放的方法---------------*/
privatevoidplay(){
//当歌曲播放时,整个播放执行处于一个阻塞状态,所以为了是软件能够
//继续运行,播放动作需要在另外一个独立的线程中运行。
//这里创建了一个播放线程,并将类对象自己加入其中,然后启动线程
this.playerThread=newThread(this,"AudioPlayerThread");
this.playerThread.start();
}
/*----------------执行暂停功能的方法-------------*/
privatevoidpause(){
this.player.pause();//让播放器player暂停
this.playerThread.stop();//杀死当前执行的线程
}
//整个播放是处于一个非播即停的状态,所以设置了一个播放开关
publicvoidplayToggle(){
if(this.player.isPaused==true){
this.play();
}else{
this.pause();
}
}
//给播放器进度监听线程使用,目的是获取当前已播放的帧数
publicintgetFrameCurrent(){
returnplayer.getFrameCurrent();
}
//该方法负责获取计算音乐文件的时长所需的总帧数参数。
因为每帧约定为26ms
//播放时长T=帧数*26ms,此方法也给播放进度监听线程提供数据来计算
//进度率
publicLonggetTotalFrame(){
returntotalFrame;
}
//
publicbooleanisComplete(){
returncomplete;
}
//线程执行的代码段
publicvoidrun(){
try{
//当歌曲为第一次播放,则从开头播放
//否则执行该方法,则播放上一次被暂停的位置的数据
complete=this.player.resume();
}catch(javazoom.jl.decoder.JavaLayerExceptionex){
ex.printStackTrace();
}
}
}
为了更加详细的论述播放器player对象的工作原理。
下面介绍player对象的实现。
player对象的类是MyPlayer类。
MyPlayer是Player类的子类。
Player是第三方类库Jlayer中的类。
因为Jlayer并没有实现暂停播放的控制功能,所以需要继承已经存在的Player类,编写具有暂停功能的子类MyPlayer。
MyPlayer类主要代码如下:
publicclassMyPlayer{
private.URLurlToStreamFrom;//音乐文件路径
privateBitstreambitstream;//音乐文件位流对象
privateDecoderdecoder;//Mp3解码器
privateAudioDeviceaudioDevice;//声卡
privatebooleanisClosed=false;//是否关闭状态
privatebooleanisComplete=false;//是否完成播放状态
privateintframeIndexCurrent;//当前播放的帧索引
publicbooleanisPaused;//是否暂停播放状态
/*初始化待播放的音乐文件路径和设置暂停状态*/
publicMyPlayer(URLurlToStreamFrom)throwsJavaLayerException{
this.urlToStreamFrom=urlToStreamFrom;
isPaused=true;
}
//暂停状态
publicvoidpause(){
this.isPaused=true;//置暂停状态为真
this.close();//执行关闭方法
}
//播放开始从0开始播放
publicbooleanplay()throwsJavaLayerException{
returnthis.play(0);
}
//从指定帧位置播放
publicbooleanplay(intframeIndexStart)throwsJavaLayerException{
returnthis.play(frameIndexStart,-1,52);
}
/*---------------真正执行播放任务的代码---------*/
publicbooleanplay(intframeIndexStart,intframeIndexFinal,
intcorrectionFactorInFrames)throwsJavaLayerException{
try{
//打开文件流
this.bitstream=newBitstream(this.urlToStreamFrom.openStream());
}catch(java.io.IOExceptionex){
}
//创建一个声卡对象
this.audioDevice=FactoryRegistry.systemRegistry().createAudioDevice();
//创建一个解码器对象
this.decoder=newDecoder();
this.audioDevice.open(decoder);//将解码器置于声卡中
booleanshouldContinueReadingFrames=true;
this.isPaused=false;
this.frameIndexCurrent=0;
//跳帧:
跳到上一次被暂停的帧位置
while(shouldContinueReadingFrames==true
&&this.frameIndexCurrent -correctionFactorInFrames){ shouldContinueReadingFrames=this.skipFrame(); this.frameIndexCurrent++; } while(shouldContinueReadingFrames==true &&this.frameIndexCurrent if(this.isPaused==true){ shouldContinueReadingFrames=false; try{ //如果暂停状态被改变,则让线程睡眠 Thread.sleep (1); }catch(Exceptionex){ } }else{ shouldContinueReadingFrames=this.decodeFrame(); this.frameIndexCurrent++; } } //最后,为了保证所有的帧都进入声卡中 if(this.audioDevice! =null){ this.audioDevice.flush(); synchronized(this){ this.isComplete=(this.isClosed==false); this.close(); } } returnthis.isComplete; } //执行恢复方法 publicbooleanresume()throwsJavaLayerException{ //返回是否完成状态,副作用是启动播放 returnthis.play(this.frameIndexCurrent); } //关闭播放,因为暂停并不是暂停而是停止播放, //只不过暂停时记住了断点,导致下次播放时可以从断点处继续 publicsynchronizedvoidclose(){ //关闭时首先将声卡关闭,然后将流关闭 if(this.audioDevice! =null){ this.isClosed=true; this.audioDevice.close(); this.audioDevice=null; try{ this.bitstream.close(); }catch(Exceptionex){ } } } //获得总的帧数 //基本思想是打开流并调用skipFrame()方法,如果该方法返回真 //说明文件没有到结尾,因为skipFrame()方法每次只读一帧,所以 //可以对其计数 publicLonggetTotalFrame(){ Longtotal=0L; try{ this.bitstream=newBitstream(this.urlToStreamFrom.openStream()); }catch(java.io.IOExceptionex){ } try{ while(skipFrame()) total++; }catch(JavaLayerExceptione){ e.printStackTrace(); } returntotal; } publicbooleanskipFrame()throwsJavaLayerException{ booleanreturnValue=false; Headerheader=bitstream.readFrame(); if(header! =null){ bitstream.closeFrame(); returnValue=true; } returnreturnValue; } //获得当前的帧索引 publicintgetFrameCurrent(){ returnframeIndexCurrent; } 停止按钮的实现代码: privatevoidclickStop(){ if(playState){ player.playToggle(); playState=false; plprLpleteListen(); }elseif(player! =null){ slider.setValue(0); player=null; } } 停止按钮的主要功能是让播放状态从播放或者暂停状态转到停止状态。 停止播放的动作执行的过程是: 如果当前播放状态为真,则调用播放开关函数playToggle()将播放暂停,然后置播放状态为false,表示处于非播放状态。 。 然后调用完成监听函数。 完成监听的功能在接下来会详细阐述。 这里只需知道它用来控制播放进度的。 上一首/下一首按钮功能的实现代码片段: 1)上一首/下一首按钮监听器代码片段 elseif(source==previousButton){ tempPlayState=playState; if(tempPlayState) clickStop(); if(playList.size()>=1){ musicURL=getMusicURL(false); } if(tempPlayState) clickPlay();//如果一开始在播放,则播放 }elseif(source==nextButton){ tempPlayState=playState; if(tempPlayState) clickStop(); if(playList.size()>=1){ musicURL=getMusicURL(true); } if(tempPlayState) clickPlay();//如果一开始在播放,则播放 2)获取上一首/下一首歌曲文件路径的方法 privateStringgetMusicURL(booleanisNext){ intsize=playList.
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- MP3 音乐 播放