1、MP3音乐播放器成绩 徐州工程学院综 合 训 练 报 告 课 程 名 称 面向对象程序设计综合设计专 业 计算机科学与技术(单) 班 级 09计单 学 生 姓 名 夏军 学 号 20090502137 设 计 题 目 MP3音乐播放器 指 导 教 师 杨兴运 设计起止时间:2011 年12月21日至 2012年 1月6日 概述本软件的设计是基于Java语言编写,其中涉及Java语言的内容包括:图形用户界面GUI、Socket网络编程、多线程、文件的输入/输出、字符串处理、容器、字符串与正则表达式、异常处理、XML文件解析以及面向对象的基本模型。 在实现音乐播放功能上,因为本软件的最基础也是最重
2、要的功能是音乐播放。然而本次设计的侧重点是对Java语言的运用,所以并不会独立编写一个音频解码器。只是借助已经编写好的API类库。Java对于音乐的支持比较麻烦,主要原因是Java跨平台导致。Java的播放音乐的API主要包括两部分,一是Java SE里面包含的Java Sound API,二是作为独立于Java SE之外的Java Music Framework。两者的主要差别在于所支持的声音文件格式不同。 Java平台提供的sound API支持的音乐文件有MIDI,RMF(Rich Music Format)。声音格式:8位和16位单声道以及立体声音频数据,采样率在8khz和48khz之
3、间。并没有对MP3进行支持。而Java Music Framework(JMF)支持播放MP3格式的音乐的播放。JMF意为Java媒体框架。该核心框架支持不同媒体(如:音频输出和视频输出)间的时钟同步。它是一个标准的扩展框架,允许用户制作纯音频流和视频流。但JMF需要进行下载安装,我们使用的操作系统上并没有自带,并且在某些联想(Lenovo)的机器上还会安装失败,而且JMF已经年久失修,不推荐使用。所以本软件使用了另外一个第三方类库Jlayer,它里面自带了两个后台播放器实现Player和AdvancedPlayer。但在查看该类库的API文档发现,该库在对音乐播放的控制上只支持音乐的停止播放
4、和开始播放,并没有暂停功能。所以只能自己编写代码实现上述缺陷功能。在实现音乐暂停时,设计思想是当用户发出暂停命令时,保存当前播放的帧位置,然后关闭声卡;当由暂停状态转为播放状态时,打开音乐文件,然后根据暂停处的帧位置,跳过若干数据流,接着创建解码器对象,将解码器加入声卡对象中,播放当前的音乐文件。在读取MP3文件的ID3标签信息上使用的是互联网上的一段代码。 在实现对音乐播放控制上,由于音乐播放的过程中需要接受用户的操作命令,这就需要使用多线程技术的支持,使软件可以同时播放音乐和接收用户操作命令。软件运行时的线程主要包括主线程、音乐文件播放线程、音乐文件播放进度监听线程、歌词搜索线程4个线程。
5、并这4个线程在运行过程中存在互相控制的情况。 在实现歌词在线下载功能上,本软件借鉴了网络上一段C#代码,对该C#代码使用Java语言进行了改写和改进。歌词下载的歌词来自一款流行的音乐播放器千千静听的歌词服务器。歌词下载的基本过程是获取需下载歌词的音乐文件的歌曲名和歌曲作者,然后进行相应处理,得到歌曲的下载链接,使用Java类库中的URL类对象对该网络路径下的歌词进行读取。并对查找到的歌词保存本地。一、需求分析软件界面布局需求软件的基本布局如图1-1所示。并且要求软件启动时,其界面必须处于屏幕正中央。图1-1 软件界面布局音乐播放控制需求播放控制主要是对播放状态进行控制,为了支持用户更便捷的控制
6、音乐的播放,此功能需要几个控制按钮。控制按钮代表的功能有:播放/暂停、停止、上一首、下一首、打开、退出。其中播放/暂停是复合功能按钮,可将播放状态变为播放或暂停。其基本布局如图1-2。当用户启动软件,软件处于就绪状态,不播放任何音乐。当用户通过打开按钮选择文件后,点击播放按钮,则此时软件播放播放列表中的第一个播放条目。当处于播放状态时,用户点击播放按钮,则播放状态由播放状态转为暂停状态。再次点击则再次进入播放状态。当播放状态为播放或暂停时,点击停止可将其音乐关闭。点击上一首/下一首按钮可以根据播放列表中的条目进行选择。点击打开按钮可以进行音乐文件的选择。点击退出则弹出确认是否退出对话框。图1-
7、2 播放控制布局播放进度显示需求当音乐处于播放状态,能够通过进度条显示当前音乐的播放进度。当某一音乐播放完毕,则进度条归0。播放列表需求播放列表中显示加入的歌曲的编号、名称和时长。并能对加入列表中的播放条目进行编辑、播放、下载歌词、评级等功能。可将通过文件选择对话框选择的文件加入播放列表中。歌词面板需求能将歌词文本显示在歌词面板中,并且为居中对齐。下载歌词需求当用户选中播放列表中的播放条目,可以执行下载对应歌词的功能。并显示在歌词面板上。二、具体实现软件界面布局实现创建一个框架,将框架分成1行2列的布局。在第1列中加入播放控制面板和播放列表面板。在第二列中加入歌词面板。其中播放控制面板宽为1/
8、2框架宽,面板高为1/3框架高;播放列表面板宽为1/2框架宽,面板高位2/3框架高;歌词面板宽为1/2框架宽,面板高为框架高。对于启动时处于正中央,实现手段可以是:首先获取当前计算机屏幕的宽高。然后得到框架的宽和高为屏幕的1/2。具体代码:class MusicFrame extends JFrame private ListPanel listPanel; / 播放列表面板 private PlayPanel playPanel; / 播放控制面板 private LRCPanel lrcPanel; / 歌词面板 Toolkit kit = Toolkit.getDefaultToolki
9、t(); Dimension screenSize = kit.getScreenSize(); int screenHeight = screenSize.height; / 获取屏幕大小 int screenWidth = screenSize.width; setResizable(false);setBounds(screenWidth / 4, screenHeight / 4 - 100, screenWidth / 2, screenHeight / 2 + 100); / 设置框架启动位置和大小 setLayout(new GridLayout(1, 2); JPanel le
10、ftSpace = new JPanel(); JPanel rightSpace = new JPanel(); listPanel = new ListPanel(this); playPanel = new PlayPanel(this); lrcPanel = new LRCPanel(this); leftSpace.add(playPanel); leftSpace.add(listPanel); rightSpace.add(lrcPanel);音乐播放控制实现音乐播放控制是整个设计环节中最复杂最具挑战性的一环,因为音乐播放控制不仅要控制音乐播放线程的播放,还需要同时处理来自按钮
11、和播放列表发来的操作命令。首先实现布局问题:首先6个按钮控件,并将其加入面板内。为了让6个按钮能够响应动作,则需要编写对应的监听器。为了方便起见,只编写了一个监听方法,并在方法内比对捕获的事件对象,从而确定执行相应的代码段。但Java核心技术一书中说这种方法并不好,不利于国际化。这里就姑且偷懒一次吧!下面实现各个按钮的功能。播放/暂停按钮:播放暂停按钮实现音乐的播放和暂停控制。当用户点击播放时,判断当前播放列表中是否有播放条目。如果有则播放第一条音乐记录。音乐播放的具体过程:首先获取到音乐文件路径,接着创建一个播放线程PlayThread对象。将音乐路径加入其中。执行播放的线程PlayThre
12、ad的代码如下:public class PlayThread implements Runnable private String filePath; / 音乐文件的路径 private MyPlayer player; / 播放音乐的播放器 private Long totalFrame; / 音乐文件的总帧数 private Thread playerThread; / 执行播放音乐的线程 private boolean complete; / 判断当前音乐是否播放完毕/*-初始化参数-*/ public PlayThread(String filePath) this.filePath
13、 = filePath; this.playerInitialize(); this.totalFrame = player.getTotalFrame(); private void playerInitialize() /*-将播放路径加入到播放器MyPlayer类对象中-*/ try String urlAsString = file:/ + filePath; this.player = new MyPlayer(new .URL(urlAsString); catch (Exception ex) ex.printStackTrace(); /*-执行播放的方法-*/ private
14、 void play() / 当歌曲播放时,整个播放执行处于一个阻塞状态,所以为了是软件能够 /继续运行,播放动作需要在另外一个独立的线程中运行。 / 这里创建了一个播放线程,并将类对象自己加入其中,然后启动线程 this.playerThread = new Thread(this, AudioPlayerThread); this.playerThread.start(); /*- 执行暂停功能的方法-*/ private void pause() this.player.pause(); / 让播放器player暂停 this.playerThread.stop(); / 杀死当前执行的
15、线程 /整个播放是处于一个非播即停的状态,所以设置了一个播放开关 public void playToggle() if (this.player.isPaused = true) this.play(); else this.pause(); / 给播放器进度监听线程使用,目的是获取当前已播放的帧数 public int getFrameCurrent() return player.getFrameCurrent(); / 该方法负责获取计算音乐文件的时长所需的总帧数参数。因为每帧约定为26ms/ 播放时长T = 帧数 * 26ms,此方法也给播放进度监听线程提供数据来计算/ 进度率 pu
16、blic Long getTotalFrame() return totalFrame; / public boolean isComplete() return complete; / 线程执行的代码段 public void run() try / 当歌曲为第一次播放,则从开头播放/ 否则执行该方法,则播放上一次被暂停的位置的数据 complete = this.player.resume(); catch (javazoom.jl.decoder.JavaLayerException ex) ex.printStackTrace(); 为了更加详细的论述播放器player对象的工作原理。
17、下面介绍player对象的实现。player对象的类是MyPlayer类。MyPlayer是Player类的子类。Player是第三方类库Jlayer中的类。因为Jlayer并没有实现暂停播放的控制功能,所以需要继承已经存在的Player类,编写具有暂停功能的子类MyPlayer。MyPlayer类主要代码如下:public class MyPlayer private .URL urlToStreamFrom; / 音乐文件路径 private Bitstream bitstream; / 音乐文件位流对象 private Decoder decoder; / Mp3解码器 private
18、AudioDevice audioDevice; / 声卡 private boolean isClosed = false; / 是否关闭状态 private boolean isComplete = false; / 是否完成播放状态 private int frameIndexCurrent; / 当前播放的帧索引 public boolean isPaused; / 是否暂停播放状态/*初始化待播放的音乐文件路径和设置暂停状态*/ public MyPlayer(URL urlToStreamFrom) throws JavaLayerException this.urlToStrea
19、mFrom = urlToStreamFrom; isPaused = true; / 暂停状态 public void pause() this.isPaused = true; / 置暂停状态为真 this.close(); / 执行关闭方法 / 播放开始从0开始播放 public boolean play() throws JavaLayerException return this.play(0); / 从指定帧位置播放 public boolean play(int frameIndexStart) throws JavaLayerException return this.play
20、(frameIndexStart, -1, 52); /* -真正执行播放任务的代码-*/public boolean play(int frameIndexStart, int frameIndexFinal, int correctionFactorInFrames) throws JavaLayerException try / 打开文件流 this.bitstream=new Bitstream(this.urlToStreamFrom.openStream(); catch (java.io.IOException ex) / 创建一个声卡对象 this.audioDevice= F
21、actoryRegistry.systemRegistry().createAudioDevice(); / 创建一个解码器对象 this.decoder = new Decoder(); this.audioDevice.open(decoder); / 将解码器置于声卡中 boolean shouldContinueReadingFrames = true; this.isPaused = false; this.frameIndexCurrent = 0; / 跳帧:跳到上一次被暂停的帧位置 while (shouldContinueReadingFrames = true & this
22、.frameIndexCurrent frameIndexStart - correctionFactorInFrames) shouldContinueReadingFrames = this.skipFrame(); this.frameIndexCurrent+; while (shouldContinueReadingFrames = true & this.frameIndexCurrent = 1) musicURL = getMusicURL(false); if (tempPlayState) clickPlay(); / 如果一开始在播放,则播放 else if (source = nextButton) tempPlayState = playState; if (tempPlayState) clickStop(); if (playList.size() = 1) musicURL = getMusicURL(true); if (tempPlayState) clickPlay(); / 如果一开始在播放,则播放2) 获取上一首/下一首歌曲文件路径的方法private String getMusicURL(boolean isNext) int size = playList.
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1