实验七:设计一个音乐播放器

实验内容

一、实验要求:

综合利用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

物联沃分享整理
物联沃-IOTWORD物联网 » Java 音乐播放器

发表回复