Java 音乐播放器
实验七:设计一个音乐播放器
实验内容
一、实验要求:
综合利用Swing界面和自己写音乐播放类,设计音乐播放器如下:
(1)纯Swing给定的组件可以设计出一个普通的音乐播放器界面。如果有PS功底或者想追求界面的绚丽,素材可以自己设计也可以通过自己渠道寻求美工帮助。主要是按钮,进度条,标签等显示效果。
参考网址:https://blog.csdn.net/turkeyzhou/article/details/4490913
(2)音乐播放方面,不能调用其他第三方jar包,只能用JDK底层自带的API类,例如JavaSound。使用JavaSound API,可以实现各种基于声音的应用,例如声音录制、音乐播放、网络电话、音乐编辑等。JavaSound API又以各种解码和合成器SPI(服务提供者接口, Service Provider Interface)为基础,实现各种音乐格式的解码与转码。
参考网址:
基于Swing的MP3 音乐播放器_java swing 播放在线mp3-CSDN博客
使用Java实现MP3音乐播放器_java 实现mp3播放器-CSDN博客
Java播放音频,支持暂停播放停止继续_java 动态播放、暂停播放声音-CSDN博客
【Java】实现wav格式音乐的 播放、停止、循环播放、音量调节_java播放wav音频文件-CSDN博客
二、设计思路及程序代码
1.JavaMain.java文件
import java.awt.CardLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Timer;
import javax.sound.sampled.FloatControl;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JSlider;
import javax.swing.JTextArea;
import javax.swing.border.Border;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import 音乐播放器.AudioPlayer;
/*
* 一首歌的播放步骤:
* AP[i].stop();//停止播放当前歌曲
* AP[i].flage = false;//当前歌曲的音乐播放主线程运行标志设为假
* AP[i].destroy();
* if (AP[i].flage) {
AP[i].closeMainThread();杀掉当前歌曲的音乐播放主线程
}
AP[i = (++i%max)].start(true);启动下一首歌曲播放主线程,开始播放
* setvol(value;设置默认音量
* sto_con_f = false;暂停继续开关下次点击为暂停
* ((bofangThread) T2).setN(i);设置当前歌序
* ((bofangThread) T2).initcurtime();初始化当前播放时间为0
* setMusicName(ta2);//显示当前歌名
*/
public class JavaMain {
public static void main(String[] args) {
new AudioPlayerWin();
}
}
//播放器
class AudioPlayerWin{
JFrame win;//主窗体
JPanel panel1,panel2,panel3,panel_2;//轮播图,播放进度,音量控制
JButton button1,button2,button3;//上一首,暂停/播放,下一首
JProgressBar pb;//进度条
boolean sto_con_f = true;//控制继续或暂停播放,true:下次点击继续,false:下次点击暂停
JTextArea ta,ta1;//音量显示文本区,当前播放歌曲显示区
Thread T1,T2;//轮播图线程、播放进度线程、音量控制线程
float value = -15.5f;//音量大小(-37——6),舍弃(-80——-37)部分
//歌单
AudioPlayer AP[] = {new AudioPlayer("./resource/晴天.wav"),
new AudioPlayer("./resource/我落泪情绪零碎.wav"),
new AudioPlayer("./resource/说走就走.wav"),
new AudioPlayer("./resource/她的睫毛.wav"),
new AudioPlayer("./resource/魔杰座 - 说好的幸福呢.wav"),
new AudioPlayer("./resource/八度空间 - 暗号.wav"),
new AudioPlayer("./resource/爱的飞行日记.wav"),
new AudioPlayer("./resource/我不配.wav"),
new AudioPlayer("./resource/大笨钟-十二新作.wav"),
new AudioPlayer("./resource/断了的弦.wav"),
new AudioPlayer("./resource/回到过去-八度空间.wav"),
new AudioPlayer("./resource/蜗牛.wav"),
new AudioPlayer("./resource/爷爷泡的茶.wav"),
new AudioPlayer("./resource/珊瑚海.wav"),
new AudioPlayer("./resource/最长的电影.wav"),
new AudioPlayer("./resource/发如雪-十一月的萧邦.wav"),
new AudioPlayer("./resource/开不了口.wav"),
new AudioPlayer("./resource/轨迹.wav"),
new AudioPlayer("./resource/红尘客栈-十二新作.wav"),
new AudioPlayer("./resource/退后.wav"),
new AudioPlayer("./resource/发如雪-十一月的萧邦.wav"),
new AudioPlayer("./resource/爱在西元前-范特西.wav"),
new AudioPlayer("./resource/可爱女人.wav"),};
int i = 0, max = AP.length;//当前歌曲序号,最大歌曲数目
public AudioPlayerWin() {
//主窗体
setWin();
//轮播图
setLunbo();
//播放进度
setProgress();
//音量控制
setYinliang();
//面板嵌套
setpanel_2();
//打开程序第一次不用按钮进行start
AP[i].start(true);
setvol(value);
AP[i].stop();
}//构造函数
//设置主窗体
public void setWin() {
win = new JFrame("音乐播放器");
win.setSize(800, 900);
win.setLayout(new GridLayout(2, 1));//主窗体设为网格布局,三行一列
}
//设置轮播图
public void setLunbo() {
CardLayout card = new CardLayout();
panel1 = new JPanel(card);
T1 = new LunboThread(panel1);//轮播线程
win.add(panel1);
T1.start();
}
//设置播放进度
public void setProgress() {
panel2 = new JPanel();
panel2.setLayout(null);//默认空布局
Border border = BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "播放进度");
panel2.setBorder(border);//设置边框
pb = new JProgressBar(JProgressBar.HORIZONTAL,0,100);//进度条设置
pb.setStringPainted(true);//显示百分比
pb.setBounds(10, 30, 780, 60);
panel2.add(pb);
ta1 = new JTextArea("当前播放歌曲:");//歌曲名提示区
ta1.setBounds(10, 22, 100, 20);
ta1.setEditable(false);//不可编辑
ta1.setBackground(null);//背景颜色透明
JTextArea ta2;//音量值
ta2 = new JTextArea();//歌名
ta2.setBounds(110, 22, 250, 20);
ta2.setEditable(false);
ta2.setBackground(null);
panel2.add(ta1);
panel2.add(ta2);
setMusicName(ta2);//第一次启动程序不使用按钮显示当前歌名
button1 = new JButton("<<");//按钮设置
button2 = new JButton("|| / ▷");
button3 = new JButton(">>");
button1.setBounds(280, 80, 80, 40);
button2.setBounds(360, 80, 80, 40);
button3.setBounds(440, 80, 80, 40);
ActionListener nextSong = new ActionListener() {//下首歌的征听事件
@Override
public void actionPerformed(ActionEvent e) {
AP[i].stop();
AP[i].flage = false;
AP[i].destroy();
if (AP[i].flage) {
AP[i].closeMainThread();
}
AP[i = (++i%max)].start(true);
setvol(value);
sto_con_f = false;
((bofangThread) T2).setN(i);
((bofangThread) T2).initcurtime();
setMusicName(ta2);//显示当前歌名
}
};
ActionListener preSong = new ActionListener() {//上首歌的征听事件
@Override
public void actionPerformed(ActionEvent e) {
AP[i].stop();
AP[i].flage = false;
AP[i].destroy();
if (AP[i].flage) {
AP[i].closeMainThread();
}
AP[i = (--i+max)%max].start(true);
setvol(value);
sto_con_f = false;
((bofangThread) T2).setN(i);
((bofangThread) T2).initcurtime();
setMusicName(ta2);//显示当前歌名
}
};
button2.addActionListener(new ActionListener() {//|| / ▷
@Override
public void actionPerformed(ActionEvent e) {
//播放控制
stop_continue();
}
});
button1.addActionListener(preSong);//<<上一首
button3.addActionListener(nextSong);//>>下一首
panel2.add(button1);
panel2.add(button2);
panel2.add(button3);
}
public void setYinliang() {
//面板布局
panel3 = new JPanel();
panel3.setLayout(null);//默认空布局
Border border = BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "音量控制");
panel3.setBorder(border);//设置边框
//音量显示文本区,默认音量50(0——100)
ta = new JTextArea("当前音量:");
ta.setBounds(10, 65, 60, 20);
ta.setEditable(false);//不可编辑
ta.setBackground(null);//背景颜色透明
panel3.add(ta);
//音量调节
JSlider slider = new JSlider(JSlider.HORIZONTAL);
slider.setBounds(10, 110, 780, 40);
slider.setPaintTicks(true);
slider.setMajorTickSpacing(20);
slider.setMinorTickSpacing(5);
slider.setPaintLabels(true);// setPaintLabels()方法为设置是否数字标记,若设为true,则JSlider刻度上就会有数值出现。
panel3.add(slider);
JTextArea yl;//音量值
yl = new JTextArea();
yl.setBounds(70, 65, 200, 20);
yl.setEditable(false);
yl.setBackground(null);
yl.setText(String.valueOf(slider.getValue()));//默认值
slider.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
yl.setText(String.valueOf(slider.getValue()));
if (slider.getValue() == 0) {//静音
value = -80f;
}
else {
value = (float) (slider.getValue() * 0.43 - 37);
}
setvol(value);
}
});
panel3.add(yl);
T2 = new bofangThread(AP,i,pb);//音量控制线程
T2.start();
}
//面板嵌套,将播放进度面板和音量控制面板嵌套在一个面板中
public void setpanel_2() {
panel_2 = new JPanel(new GridLayout(2,1));
panel_2.add(panel2);
panel_2.add(panel3);
win.add(panel_2);
win.setVisible(true);
win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
//显示当前歌名
public void setMusicName(JTextArea area) {
String name = AP[i].getMusicName();
area.setText(name);
}
public void stop_continue() {//暂停播放切换
if(sto_con_f) {
AP[i].continues();
sto_con_f = false;
}
else {
AP[i].stop();
sto_con_f = true;
}
}
//设置音量
public void setvol(float value) {
if (AP[i].flage) {
FloatControl control = (FloatControl) AP[i].sourceDataLine.getControl(FloatControl.Type.MASTER_GAIN);
//System.out.println(control.getValue());//音量调节测试
control.setValue(value);
}
}
public void boFangShunXu() {//播放顺序(单曲循环、顺序播放、乱序播放)()
}
}
//播放进度控制线程(进度条自动显示进度)
class bofangThread extends Thread{
AudioPlayer aPlayer[];//歌单
int n;//当前歌序
int per;//当前歌曲进度
JProgressBar progressBar;
int sumtime;//当前已播放时间
public bofangThread(AudioPlayer ap[],int i,JProgressBar pb) {
this.aPlayer = ap;
this.n = i;
progressBar = pb;
sumtime = 0;
}
public void setN(int i) {//设置歌序
n = i;
}
public void initcurtime() {//当前播放时间清零,进度清零
sumtime = 0;
progressBar.setValue(0);
}
public void run() {
while(true) {
if (aPlayer[n].isPlaying()) {
per = (int) (sumtime/aPlayer[n].getalltime()*100);
progressBar.setValue(per);
if (per == 100) {
sumtime = 0;//播放完,清零
}
else {
try {
sleep(1000);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}//每休息一秒,curtime+1
sumtime += 1;
}
}
}
}
}
//轮播线程
class LunboThread extends Thread{
JPanel panel;//轮播图
JPanel p1 = new JPanel();//图片载体
JPanel p2 = new JPanel();
JPanel p3 = new JPanel();
JPanel p4 = new JPanel();
JPanel p5 = new JPanel();
int i = 0;
public LunboThread(JPanel panel) {
this.panel = panel;
p1.add(new JLabel(new ImageIcon("./resource/b1.jpg")));
p2.add(new JLabel(new ImageIcon("./resource/b2.jpg")));
p3.add(new JLabel(new ImageIcon("./resource/b3.jpg")));
p4.add(new JLabel(new ImageIcon("./resource/b4.jpeg")));
p5.add(new JLabel(new ImageIcon("./resource/b5.jpeg")));
this.panel.add(p1,"p1");
this.panel.add(p2,"p2");
this.panel.add(p3,"p3");
this.panel.add(p4,"p4");
this.panel.add(p5,"p5");
}
public void run() {
CardLayout card = (CardLayout) panel.getLayout();
while(true) {
switch (i++%5) {
case 1: {
card.show(panel, "p1");
break;
}
case 2: {
card.show(panel, "p2");
break;
}
case 3: {
card.show(panel, "p3");
break;
}
case 4: {
card.show(panel, "p4");
break;
}
case 0: {
card.show(panel, "p5");
break;
}
}
try {
sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
2. AudioPlayer.java文件
package 音乐播放器;
import javax.sound.sampled.*;
import java.io.*;
public class AudioPlayer {
private String musicPath; // 音频文件
private volatile boolean run = false; // 记录音频是否播放
private Thread mainThread; // 播放音频的任务线程
public boolean flage;//标记wav文件是否打开,用于音量控制
private double alltime;//歌曲总时间,与当前播放时间
private AudioInputStream audioStream;
private AudioFormat audioFormat;
public SourceDataLine sourceDataLine;
public AudioPlayer(String musicPath) {
this.musicPath = musicPath;
prefetch();
}
// 数据准备
private void prefetch() {
try {
// 获取音频输入流
audioStream = AudioSystem.getAudioInputStream(new File(musicPath));
// 获取音频的编码对象
audioFormat = audioStream.getFormat();
// 包装音频信息
DataLine.Info dataLineInfo = new DataLine.Info(SourceDataLine.class, audioFormat,
AudioSystem.NOT_SPECIFIED);
// 使用包装音频信息后的Info类创建源数据行,充当混频器的源
sourceDataLine = (SourceDataLine) AudioSystem.getLine(dataLineInfo);
// 获得音频的总时长
alltime = audioStream.getFrameLength() / audioFormat.getSampleRate();
sourceDataLine.open(audioFormat);
sourceDataLine.start();
} catch (UnsupportedAudioFileException ex) {
ex.printStackTrace();
} catch (LineUnavailableException ex) {
ex.printStackTrace();
} catch (IOException ex) {
ex.printStackTrace();
}
}
// 析构函数:关闭音频读取流和数据行
@SuppressWarnings("deprecation")
protected void finalize() throws Throwable {
super.finalize();
sourceDataLine.drain();
sourceDataLine.close();
audioStream.close();
}
// 播放音频:通过loop参数设置是否循环播放
private void playMusic(boolean loop) throws InterruptedException {
try {
if (loop) {
while (true) {
playMusic();
}
} else {
playMusic();
// 清空数据行并关闭
sourceDataLine.drain();
sourceDataLine.close();
audioStream.close();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
private void playMusic() {
try {
synchronized (this) {
run = true;
}
// 通过数据行读取音频数据流,发送到混音器;
// 数据流传输过程:AudioInputStream -> SourceDataLine;
audioStream = AudioSystem.getAudioInputStream(new File(musicPath));
int count;
byte tempBuff[] = new byte[1024];
while ((count = audioStream.read(tempBuff, 0, tempBuff.length)) != -1) {
synchronized (this) {
while (!run)
wait();
}
sourceDataLine.write(tempBuff, 0, count);//open,对应音量控制,只有此语句执行后,
//setVol方法中的FloatControl.Type.MASTER_GAIN读取不会异常
}
} catch (UnsupportedAudioFileException ex) {
ex.printStackTrace();
} catch (IOException ex) {
ex.printStackTrace();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
// 暂停播放音频
private void stopMusic() {
synchronized (this) {
run = false;
notifyAll();
}
}
// 继续播放音乐
private void continueMusic() {
synchronized (this) {
run = true;
notifyAll();
}
}
// 外部调用控制方法:生成音频主线程;
public void start(boolean loop) {
prefetch();
mainThread = new Thread(new Runnable() {
public void run() {
try {
playMusic(loop);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
mainThread.start();
flage = true;
}
// 外部调用控制方法:暂停音频线程
public void stop() {
new Thread(new Runnable() {
public void run() {
stopMusic();
}
}).start();
}
// 外部调用控制方法:继续音频线程
public void continues() {
new Thread(new Runnable() {
public void run() {
continueMusic();
}
}).start();
}
// 播放器的状态
public boolean isPlaying() {
return run;
}
//获取音频总时长
public double getalltime() {
return alltime;
}
//关闭当前音乐播放主线程
@SuppressWarnings("deprecation")
public void closeMainThread() {
mainThread.stop();
}
//获取歌名
public String getMusicName() {
String name = musicPath.substring(11, musicPath.length()-4);
return name;
}
// 销毁
public void destroy() {
try {
finalize();
} catch (Throwable e) {
e.printStackTrace();
}
}
}
作者:Judy716