MP3プレイヤーサンプル(JLayer,BasicPlayer,Java ID3 Tag Library)一通りの機能実装版
サンプルダウンロード
新しいサンプル
説明
Java/swing/BasicPlayer/Java ID3 Tag Library/JLayer
BasicPlayerを使ったmp3プレイヤーサンプル第二弾
再生/停止/一時停止/プレイリスト/ループ再生/シャッフル再生/ボリューム調整/シーク/次の曲/前の曲/ID3タグv1v2読み込み
ギリギリ最低限の機能をだいたい載せた気分のmp3プレイヤーサンプルです。BasicPlayerのお陰で結構簡単に作れました。
Java ID3 Tag Libraryは文字化け対応を埋め込んだ改造版です。
プレイリストは起動中に作るのみで保存も、読み込みも出来ません。そのうち実装したい…
サンプルソース(長すぎるけど
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.datatransfer.DataFlavor;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetAdapter;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JSlider;
import javax.swing.JTable;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.plaf.metal.MetalSliderUI;
import javax.swing.table.DefaultTableModel;
import javazoom.jlgui.basicplayer.BasicController;
import javazoom.jlgui.basicplayer.BasicPlayer;
import javazoom.jlgui.basicplayer.BasicPlayerEvent;
import javazoom.jlgui.basicplayer.BasicPlayerException;
import javazoom.jlgui.basicplayer.BasicPlayerListener;
import org.farng.mp3.MP3File;
import org.farng.mp3.TagException;
import org.farng.mp3.id3.AbstractID3v2;
import org.farng.mp3.id3.ID3v1;
// Java/swing/BasicPlayer/Java ID3 Tag Library/JLayer
// BasicPlayerを使ったmp3プレイヤーサンプル第二弾
// 再生/停止/一時停止/プレイリスト/ループ再生/シャッフル再生/ボリューム調整/シーク/次の曲/前の曲/ID3タグv1v2読み込み
// ギリギリ最低限の機能をだいたい載せた気分のmp3プレイヤーサンプルです。BasicPlayerのお陰で結構簡単に作れました。
// Java ID3 Tag Libraryは文字化け対応を埋め込んだ改造版です。
// プレイリストは起動中に作るのみで保存も、読み込みも出来ません。そのうち実装したい…
public class BasicPlayerSample3 extends JFrame {
// ベーシックプレイヤー
BasicPlayer player;
// Swing用変数、定数
// ボタン用テキスト 停止
final static String STOP = "Stop";
// ボタン用テキスト 再生
final static String PLAY = "Play";
// ボタン用テキスト 一時停止
final static String PAUSE = "Pause";
// タイトル
final static String TITLE = "BasicPlayerでmp3再生";
final static String FILE_NAME = "ファイル名:";
// プレイリスト再生方法
String PL_ONE = "1曲のみ";
String PL_LIST = "プレイリスト順";
String PL_SHUFFLE = "シャッフル";
// 再生中のインデックス
int playIndex;
// プレイリストのモデル
DefaultTableModel m;
// 再生ボタン
JButton bPlay;
// 曲名ラベル
JLabel label;
// 演奏位置スライダー
JSlider posSlider;
// ボリュームスライダー
JSlider volSlider;
// 演奏位置スライダ用のリスナー
ChangeListener posSliderCl;
// ボリュームスライダ用のリスナー
ChangeListener volSliderCl;
// ループチェックボックス
JCheckBox roopCheck;
// プレイリストJTable
JTable playList;
// プレイリストの再生方法コンボボックス
JComboBox plCombo;
// シャッフル用のインデックスリスト
ArrayList<Integer> shIndexs;
// BasicPlayerからのプロパティを保存する変数
private Map audioInfo = null;
// メイン関数
public static void main(String[] args) {
new BasicPlayerSample3();
}
// コンストラクタ
public BasicPlayerSample3() {
setTitle(TITLE);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setBounds(200, 100, 300, 420);
setLayout(new FlowLayout());
// ドロップターゲット設定
new DropTarget(this, DnDConstants.ACTION_COPY, new MyDropTargetListener());
// ラベル設置
label = new JLabel(" mp3ファイルをドロップしてください。");
label.setPreferredSize(new Dimension(getWidth() - 10, 20));
add(label);
add(getHr(2000, 0));
// 再生、停止ボタン設置
bPlay = new JButton(PLAY);
bPlay.addActionListener(new bPlayAction());
JButton bStop = new JButton(STOP);
bStop.addActionListener(new bStopAction());
JButton bNext = new JButton(">>");
bNext.addActionListener(new bNextAction());
JButton bPrev = new JButton("<<");
bPrev.addActionListener(new bPrevAction());
add(bPrev);
add(bPlay);
add(bStop);
add(bNext);
// 再生位置スライダー設置
add(getHr(2000, 0));
add(new JLabel("再生位置"));
posSlider = new JSlider(0, 100, 0);
posSliderCl = getPosChangeListener();
posSlider.addChangeListener(posSliderCl);
add(posSlider);
// ボリュームスライダー設置
add(getHr(2000, 0));
add(new JLabel("ボリューム"));
volSlider = new JSlider(0, 100, 50);
volSliderCl = getVolChangeListener();
volSlider.addChangeListener(volSliderCl);
volSlider.setUI(new TriSliderUI());
Dimension d = volSlider.getPreferredSize();
d.width = 100;
volSlider.setPreferredSize(d);
add(volSlider);
add(getHr(2000, 0));
// プレイリスト再生方法
plCombo = new JComboBox(new String[] { PL_ONE, PL_LIST, PL_SHUFFLE });
plCombo.setSelectedIndex(1);
setPlComboListener();
add(plCombo);
// ループ再生チェック
roopCheck = new JCheckBox("ループ再生");
add(roopCheck);
// プレイリスト
add(getHr(2000, 0));
m = new DefaultTableModel(new String[] { "タイトル", "アーティスト" }, 0);
playList = new JTable(m);
playList.setDefaultEditor(Object.class, null); // セルの編集禁止
setPlayListListener();
JScrollPane sc = new JScrollPane(playList);
sc.setPreferredSize(new Dimension(280, 200));
add(sc);
// BasicPlayerのインスタンス作成
player = new BasicPlayer();
addBasicPlayerListener();
setVisible(true);
}
// プレイリストの再生方法コンボボックスのリスナーを設定
String beforeItem;
private void setPlComboListener() {
plCombo.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
System.out.println(e.getStateChange());
if (e.getStateChange() == ItemEvent.SELECTED) {
if (PL_SHUFFLE.equals(e.getItem())) {
// シャッフルの場合
shIndexs = new ArrayList<Integer>();
for (int i = 0; i < files.size(); i++) {
shIndexs.add(i);
}
Collections.shuffle(shIndexs);
play(0);
} else if (PL_SHUFFLE.equals(beforeItem)) {
// シャッフルから他のアイテムになった場合
playIndex = shIndexs.get(playIndex);
}
} else {
beforeItem = e.getItem().toString();
}
}
});
}
// プレイリストにリスナーを設定します
private void setPlayListListener() {
playList.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
// リストダブルクリックで該当曲再生
if (e.getClickCount() == 2 && e.getButton() == MouseEvent.BUTTON1) {
int idx = playList.rowAtPoint(e.getPoint());
if (idx >= 0) {
play(idx);
}
}
super.mouseClicked(e);
}
});
}
// スライダの変更通知を受け取るリスナーです
private ChangeListener getPosChangeListener() {
return new ChangeListener() {
// スライダを操作すると通知が来ます
@Override
public void stateChanged(ChangeEvent e) {
// マウスで操作中は何もません
if (!posSlider.getValueIsAdjusting()) {
// マウスで操作中でなければスライダの位置に合わせてシークします
try {
if (player.getStatus() == BasicPlayer.STOPPED) {
// 停止中の場合、再生開始してからシークします
play();
}
// シーク位置計算
long l = getAudioBytes() * posSlider.getValue() / 100;
player.seek(l);
} catch (BasicPlayerException e1) {
e1.printStackTrace();
}
}
}
};
}
// スライダの変更通知を受け取るリスナーです
private ChangeListener getVolChangeListener() {
return new ChangeListener() {
// スライダを操作すると通知が来ます
@Override
public void stateChanged(ChangeEvent e) {
// マウスで操作中は何もません
if (!volSlider.getValueIsAdjusting()) {
// マウスで操作中でなければスライダの位置に合わせてシークします
setGain();
}
}
};
}
// スライダに合わせてボリュームを設定します
private void setGain() {
try {
player.setGain((double) volSlider.getValue() / volSlider.getMaximum());
} catch (BasicPlayerException e1) {
e1.printStackTrace();
}
}
// BasicPlayerリスナーを追加します
BasicPlayerListener bpl;
private void addBasicPlayerListener() {
bpl = new BasicPlayerListener() {
// プレイヤーのステータス変更通知
@Override
public void stateUpdated(BasicPlayerEvent event) {
if (event.getCode() == BasicPlayerEvent.EOM) {
try {
stopAction();
} catch (BasicPlayerException e) {
e.printStackTrace();
}
}
// プレイヤーのステータスが停止になった場合
if (player.getStatus() == BasicPlayer.STOPPED) {
}
}
@Override
public void setController(BasicController controller) {
}
// 再生中の進捗通知
@Override
public void progress(int bytesread, long microseconds, byte[] pcmdata, Map properties) {
// 再生が進んだらスライダを操作します。
if (getAudioBytes() > 0) {
// 新しいスライダの位置を計算します
int newValue = (int) (bytesread * 100 / getAudioBytes());
// スライダをマウスで摘んでる間は操作しません
// スライダの値と、現在の値が同じ場合も操作しません
if (!posSlider.getValueIsAdjusting() && posSlider.getValue() != newValue) {
// setValueでの操作でも、ChangeListenerに通知が行くのでリスナー取り出し、操作、リスナー追加の順に処理します
posSlider.removeChangeListener(posSliderCl);
posSlider.setValue(newValue);
posSlider.addChangeListener(posSliderCl);
}
}
}
// ファイルオープン通知
@Override
public void opened(Object stream, Map properties) {
// プロパティをクラス変数に保存します
audioInfo = properties;
}
};
player.addBasicPlayerListener(bpl);
}
// 曲の最後まできた時のアクション
private void stopAction() throws BasicPlayerException {
if (plCombo.getSelectedIndex() == 0) {
// 1曲のみ
if (roopCheck.isSelected()) {
// ループならまた再生
// stop();
play();
} else {
// ループでなければ停止
stop();
}
} else if (plCombo.getSelectedIndex() == 1) {
// プレイリスト順
if (roopCheck.isSelected()) {
// ループなら次のファイルへ
next();
} else {
if (playIndex + 1 >= files.size()) {
// ループじゃない&最後まで来てたら停止
stop();
} else {
// 最後じゃなければ次のファイルへ
next();
}
}
} else {
// SHUFFLE
if (roopCheck.isSelected()) {
// ループなら次のファイルへ
next();
} else {
if (playIndex + 1 >= files.size()) {
// ループじゃない&最後まで来てたら停止
stop();
} else {
// 最後じゃなければ次のファイルへ
next();
}
}
}
}
// プロパティから音楽ファイルの長さを返します
private long getAudioBytes() {
try {
return Long.parseLong(audioInfo.get("audio.length.bytes").toString());
} catch (Exception e) {
return 0;
}
}
// 水平線
public JSeparator getHr(int width, int hight) {
JSeparator sp = new JSeparator(JSeparator.HORIZONTAL);
sp.setPreferredSize(new Dimension(width, hight));
return sp;
}
// 再生ボタンのアクションクラス
class bPlayAction implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
try {
int status = player.getStatus();
if (status == BasicPlayer.PAUSED) {
// 一時停止の場合、一時停止を解除します
resume();
} else if (status == BasicPlayer.STOPPED) {
// 停止中の場合、再生開始します
play();
} else if (status == BasicPlayer.PLAYING) {
// 再生中の場合、一時停止します
pause();
}
} catch (Exception ex) {
// 握りつぶす
}
}
}
// 停止ボタンのアクションクラス
class bStopAction implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
try {
stop();
} catch (Exception ex) {
// 握りつぶす
}
}
}
// 次ボタンのアクションクラス
class bNextAction implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
try {
next();
} catch (Exception ex) {
// 握りつぶす
}
}
}
private void next() {
System.out.println("next");
play(playIndex + 1);
}
private void prev() {
System.out.println("prev");
play(playIndex - 1);
}
// 前ボタンのアクションクラス
class bPrevAction implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
try {
prev();
} catch (Exception ex) {
// 握りつぶす
}
}
}
// 一時停止
private void pause() throws BasicPlayerException {
// playMode = PLAY_MODE_PAUSE;
player.pause();
bPlay.setText(PLAY);
}
// 一時停止解除
private void resume() throws BasicPlayerException {
player.resume();
bPlay.setText(PAUSE);
}
// 再生
private void play() throws BasicPlayerException {
if (player.getStatus() != BasicPlayer.STOPPED) {
player.stop();
}
player.play();
bPlay.setText(PAUSE);
}
// インデックス指定で再生
private void play(int idx) {
try {
if (files.size() == 0) {
return;
}
if (files.size() <= idx) {
playIndex = 0;
} else if (0 > idx) {
playIndex = files.size() - 1;
} else {
playIndex = idx;
}
if (plCombo.getSelectedIndex() == 2) {
// シャッフル
open(files.get(shIndexs.get(playIndex)));
} else {
// 通常
open(files.get(playIndex));
}
} catch (BasicPlayerException e) {
e.printStackTrace();
}
if (plCombo.getSelectedIndex() == 2) {
// シャッフル
playList.setRowSelectionInterval(shIndexs.get(playIndex), shIndexs.get(playIndex));
} else {
// 通常
playList.setRowSelectionInterval(playIndex, playIndex);
}
}
// 停止
private void stop() throws BasicPlayerException {
if (player.getStatus() != BasicPlayer.STOPPED) {
player.stop();
}
bPlay.setText(PLAY);
}
// 開く
private void open(File file) throws BasicPlayerException {
if (player.getStatus() == BasicPlayer.PLAYING) {
// 一旦停止してから次のファイルを再生&リスナーがいると次のファイルを再生しようとするので一旦取り出し。
player.removeBasicPlayerListener(bpl);
player.stop();
for (int i = 0; i < 100; i++) {
if (player.getStatus() == BasicPlayer.STOPPED) {
break;
}
i++;
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
player.addBasicPlayerListener(bpl);
}
// ラベル設定
label.setText(FILE_NAME + file.getName());
// 取得したファイルを開く
player.open(file);
play(); // 再生
}
private void setPlayList() {
int rowCount = m.getRowCount();
for (int i = rowCount; i < files.size(); i++) {
File f = files.get(i);
m.addRow(readTag(f));
}
}
// mp3ファイルからID3タグを読んで表示します
private String[] readTag(File file) {
MP3File mp3file;
String[] ret = new String[2];
try {
mp3file = new MP3File(file);
String title = file.getName();
String artist = "--";
// v2タグを優先的に表示
if (mp3file.hasID3v2Tag()) {
AbstractID3v2 v2 = mp3file.getID3v2Tag();
title = v2.getSongTitle();
artist = v2.getLeadArtist();
} else if (mp3file.hasID3v2Tag()) {
// v2タグがない場合v1タグを表示
ID3v1 v1 = mp3file.getID3v1Tag();
byte[] ary = v1.getTitle().getBytes("ISO-8859-1");
title = new String(ary);
ary = v1.getArtist().getBytes("ISO-8859-1");
artist = new String(ary);
}
// 読み込んだタグを画面に設定
if (title == null || title.trim().length() == 0) {
title = file.getName();
}
ret[0] = title;
ret[1] = artist;
} catch (IOException e) {
e.printStackTrace();
} catch (TagException e) {
e.printStackTrace();
}
return ret;
}
List<File> files = new ArrayList<File>();
// ドロップターゲットリスナー
// ドロップされたファイルを受け取り、最初のファイルだけを再生します
class MyDropTargetListener extends DropTargetAdapter {
@Override
public void drop(DropTargetDropEvent dtde) {
dtde.acceptDrop(DnDConstants.ACTION_COPY);
boolean b = false;
try {
if (dtde.getTransferable().isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
b = true;
List<File> list = (List<File>) dtde.getTransferable().getTransferData(
DataFlavor.javaFileListFlavor);
int idx = files.size();
// 最初のファイルだけ取得
for (File file : list) {
if (file.getName().toLowerCase().endsWith("mp3")) {
files.add(file);
}
}
setPlayList();
play(idx);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
dtde.dropComplete(b);
}
}
}
}
// スライダー描画クラス
class TriSliderUI extends MetalSliderUI {
Color trackBackGroundColor = Color.LIGHT_GRAY;
Color trackColor = Color.WHITE;
Color trackBorderColor = Color.BLACK;
Color thumbColor = Color.LIGHT_GRAY;
public TriSliderUI() {
super();
}
public TriSliderUI(Color trackBackGroundColor, Color trackColor, Color trackBorderColor,
Color thumbColor) {
super();
this.trackBackGroundColor = trackBackGroundColor;
this.trackColor = trackColor;
this.trackBorderColor = trackBorderColor;
this.thumbColor = thumbColor;
}
public Color getTrackBackGroundColor() {
return trackBackGroundColor;
}
public void setTrackBackGroundColor(Color trackBackGroundColor) {
this.trackBackGroundColor = trackBackGroundColor;
}
public Color getTrackColor() {
return trackColor;
}
public void setTrackColor(Color trackColor) {
this.trackColor = trackColor;
}
public Color getTrackBorderColor() {
return trackBorderColor;
}
public void setTrackBorderColor(Color trackBorderColor) {
this.trackBorderColor = trackBorderColor;
}
public Color getThumbColor() {
return thumbColor;
}
public void setThumbColor(Color thumbColor) {
this.thumbColor = thumbColor;
}
// マーカー描画スレッド
@Override
public void paintThumb(Graphics g) {
if (slider.getOrientation() == JSlider.HORIZONTAL) {
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setPaint(thumbColor);
int x = thumbRect.x + thumbRect.width / 2 - 2;
int y = thumbRect.y;
int w = 5;
int h = thumbRect.height;
g2.fill3DRect(x, y, w, h, true);
} else {
super.paintThumb(g);
}
}
// トラック描画スレッド
@Override
public void paintTrack(Graphics g) {
int cx, cy, cw, ch;
int pad;
int pady = 3;
Rectangle trackBounds = trackRect;
if (slider.getOrientation() == JSlider.HORIZONTAL) {
Graphics2D g2 = (Graphics2D) g;
// いろいろ座標設定
pad = trackBuffer;
cx = pad;
cy = trackBounds.height / 2 - pady;
cw = trackBounds.width;
// アンチエイリアス設定
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.translate(trackBounds.x, trackBounds.y + cy + pady);
// トラックの背景描画(ライトグレー
g2.setPaint(trackBackGroundColor);
Polygon polygon1 = new Polygon();
polygon1.addPoint(0, cy);
polygon1.addPoint(cw, cy);
polygon1.addPoint(cw, -cy);
g2.fillPolygon(polygon1);
// トラックの支点から現在地までの描画(白
g2.setPaint(trackColor);
Polygon polygon2 = new Polygon();
polygon2.addPoint(0, cy);
polygon2.addPoint(thumbRect.x, cy);
polygon2.addPoint(thumbRect.x, cy - (int) (cy * 2 / (double) cw * thumbRect.x));
g2.fillPolygon(polygon2);
polygon2.reset();
// 黒い枠線描画
g2.setPaint(trackBorderColor);
g2.drawPolygon(polygon1);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
g2.translate(-trackBounds.x, -(trackBounds.y + cy + pady));
} else {
super.paintTrack(g);
}
}
}
コメント
最終更新:2013年02月22日 01:54