BasicPlayerを使って音楽プレイヤーを作ってみる


# はじめに

この記事は、[SLP KBIT Advent Calendar 2019](https://adventar.org/calendars/5402) の13日目の記事です。
(~~滑り込みセーフ~~アウトでしたごめんなさい…)
やっと折返し地点まできましたね！

今回は、"BasicPlayer API"を使って、かねてから作ってみたかった音楽プレイヤーを作ってみようと思います。
Javaのアプリケーション開発といえばEclipseですが、今回はVSCodeの拡張機能"Java Extension Pack"を使用して開発を行います。

ソースコードは、一応GitHubで公開してますが、まだ単純な機能しかなく、途中段階なので今後も更新していきたいと思ってます。
https://github.com/kakukaku-15/media_player

# 開発環境

- macOS Big Sur
- Visual Studio Code 1.51.1
    - 拡張機能：Java Extension Pack
- Java 15.0.1

## 使用したAPI
- BasicPlayer API 3.0

# 準備

## Java環境構築

まずは、https://www.oracle.com/java/technologies/javase-jdk15-downloads.html より**JDK**をダウンロードします。
※**JDK**…Javaでソフトウェア開発を行うのに必要なソフトウェアを一つにまとめたパッケージ
![スクリーンショット 2020-12-13 22.43.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/548179/ded0215a-04a1-9855-df8e-cbfcec3e59bc.png)


ダウンロードが完了したら、.dmgファイルを開いてインストーラを立ち上げ、インストーラの手順に沿ってインストールを完了させます。

次に、VSCodeでJavaアプリケーションを開発するために、VSCodeの拡張機能"**Java Extension Pack**"をインストールします。
![Java Extension Pack](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/548179/51a967d0-7bd1-0a59-856e-0b7db357c46f.png)

## BasicPlayer APIのダウンロード

BasicPlayerとは、JavaSoundAPIに基づいてスレッド化された、単純な機能を持つAPIです。MP3などのオーディアファイルを再生することができます。

http://www.javazoom.net/jlgui/api.html より、**BasicPlayer API**をダウンロードします。
以下はダウンロードしたディレクトリの中身です。
![BasicPlayer API](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/548179/ac25fb8c-84c6-2ecc-6cac-8054edd68261.png)


## プロジェクト作成

VSCodeを開いて、コマンドパレットより`Java: Create Java Project...` -> `No build tools`の順に選択し、プロジェクト名を指定してプロジェクトを作成します。

すると、`src`, `lib`というディレクトリが自動的に生成されます。
上でダウンロードした`BasicPlayer3.0`直下にある`basicplayer3.0.jar`と`lib`ディレクトリにあるすべての.jarファイルを、作成したプロジェクトの`lib`に置きます。

これで準備OKです。
`src`ディレクトリの直下にソースコードを記述していきます。

## ファイル構造

```
media_player
├── README.md
├── lib
│   ├── basicplayer3.0.jar
│   ├── commons-logging-api.jar
│   ├── jl1.0.jar
│   ├── jogg-0.0.7.jar
│   ├── jorbis-0.0.15.jar
│   ├── jspeex0.9.7.jar
│   ├── mp3spi1.9.4.jar
│   ├── tritonus_share.jar
│   └── vorbisspi1.0.2.jar
└── src
    ├── Player.java
    └── SimplePlayer.java
```

# 開発

実際にできたものがこちらです。とてもシンプルですが、音楽プレイヤーと呼べる最低限の機能は持っているのではないかと思います。

![実行結果](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/548179/0b7032ae-bf36-1ceb-81b6-3e954d90d93e.png)

機能としては、読み込みボタンを押すとダイアログが表示され、ファイルパスを指定することでオーディオファイル(.mp3)を読み込むことができます。読み込むと、写真のようにファイル名がウィンドウの真ん中に表示されます。再生ボタンを押すと、音楽が再生されます。一時停止、停止位置からの再開も可能です。

完成したソースコードは以下のとおりです。

```java:SimplePlayer.java
package src;

import java.io.*;
import java.util.Map;
import javazoom.jlgui.basicplayer.*;
import java.awt.*;
import java.awt.event.*;

import javax.swing.*;

public class SimplePlayer extends JFrame implements ActionListener, BasicPlayerListener {
    // オブジェクト生成
    JFrame frame = new JFrame();
    JFileChooser chooser = new JFileChooser();
    BasicPlayer player = new BasicPlayer();
    BasicController control = (BasicController) player;
    File playFile;
    JButton btnOpen, btnPlay;
    JLabel label;

    // コンストラクタ
    SimplePlayer() {
        frame.setTitle("MP3 Player"); //  タイトル
        frame.setLayout(new BorderLayout());
        // display("" + player.getStatus());

        // ボタンを生成
        btnOpen = new JButton("読み込み"); // 開くボタン
        btnOpen.setActionCommand("open");
        btnOpen.addActionListener(this);

        btnPlay = new JButton("再生"); // 再生ボタン
        btnPlay.setActionCommand("play/pause");
        btnPlay.addActionListener(this);

        label = new JLabel("Set Music");
        label.setHorizontalAlignment(JLabel.CENTER); // 中央に表示

        // モジュール設置
        frame.add(btnOpen, BorderLayout.NORTH); // 開くボタンをセットする
        frame.add(btnPlay, BorderLayout.SOUTH); // 開くボタンをセットする
        frame.add(label, BorderLayout.CENTER); // 文字列表示

        // frame.pack(); // レイアウトの自動最適化
        frame.setSize(200, 200); // ウインドウサイズの設定
        frame.setVisible(true); // ウインドウの可視性を設定

        // プレイヤーの設定を反映させる
        player.addBasicPlayerListener(this);

        // ウインドウを閉じるときの処理
        frame.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                System.exit(0); // 終了
            }
        });

    }

    // 再生
    public void play() {
        try {
            // ファイルを再生、音量、パンなどを設定
            control.play();
            // display(control.toString());
            control.setGain(0.50);
            control.setPan(0.0);
        } catch (BasicPlayerException e) {
            e.printStackTrace();
        }
    }

    // 一時停止
    public void pause() {
        try {
            control.pause();
        } catch (BasicPlayerException e) {
            e.printStackTrace();
        }
    }

    // 再開
    public void resume() {
        try {
            control.resume(); // 一時停止したところから再生
        } catch (BasicPlayerException e) {
            e.printStackTrace();
        }
    }

    // BasicPlayerをcontrollerを通じて制御する
    public void setController(BasicController controller) {
        display("Controll: " + controller.toString());
    }

    // 音量、パン、プレイヤーの再生などの現在の状況を取得
    public void stateUpdated(BasicPlayerEvent event) {
        display("stateUpdated: " + event.toString());
    }

    // サウンドの進捗状況を取得
    public void progress(int read, long time, byte[] data, Map map) {
        // display("progress :" + map.toString());
        // display("" + player.getStatus());
    }

    // 開かれたサウンドファイルの詳細を取得
    public void opened(Object object, Map map) {
        display("opened :" + map.toString());
    }

    // コンソールに文字列を出力
    public void display(String str) {
        if (str != null)
            System.out.println(str);
    }

    // ボタンアクション
    public void actionPerformed(ActionEvent event) {
        if (event.getActionCommand() == "open") { // open
            int choice = chooser.showOpenDialog(this);
            try {
                if (choice == JFileChooser.APPROVE_OPTION) {
                    playFile = chooser.getSelectedFile(); // 選択されたファイルのパスを取得
                    FileReader fr = new FileReader(playFile); // ファイルパスから読み込み
                    // frame.setTitle(playFile.getName()); // タイトルを選ばれたファイルの名前に変更
                    label.setText(playFile.getName()); // 選ばれたファイルの名前を表示
                    fr.close(); // ファイルの解放

                    // 指定されたサウンドファイルを開く
                    control.open(playFile);
                }
            } catch (Exception e) {
                display("ファイルダイアログの展開に失敗しました。");
            }
        } else if (event.getActionCommand() == "play/pause") {
            switch (player.getStatus()) {
                case BasicPlayer.OPENED: // 再生
                    play();
                    display("再生");
                    btnPlay.setText("一時停止");
                    break;
                case BasicPlayer.PLAYING: // 一時停止
                    pause();
                    display("一時停止");
                    btnPlay.setText("再生");
                    break;
                case BasicPlayer.PAUSED: // 再開
                    resume();
                    display("再開");
                    btnPlay.setText("一時停止");
                    break;
                default:
                    break;
            }
        }
    }
}

```

mainで、作成したSimplePlayerインスタンスを生成し、画面を表示させます。

```java:Player.java
package src;

//プレイヤーのメインクラス
public class Player {
    public static void main(String[] args) {
        new SimplePlayer();
    }
}
```

# まとめ

BasicPlayerを使った、シンプルな音楽プレイヤーの作成を行いました。
思ったよりお手軽に作れてよかったかなと思います。

とはいえ課題は山積みです。
MP3の再生しかできませんでしたが、他のオーディオファイル、最終的に動画も再生できるようにしたいと思ってます。他の機能としては、プレイリストの作成や、再生位置の調節なども実装したいです。UIももっと考える必要があると思います。
もっと計画性を持って、コツコツとやっていけるような人間になりたいです。

# 参考

- Java環境構築(Mac版)　JDKインストール 
https://techfun.cc/java/mac-jdk-install.html

- VSCodeでJavaの開発環境を構築する
https://www.suzu6.net/posts/130-vscode-for-java/

- SwingとjavazoomのBasicPlayer APIを使用した超簡易プレイヤー
http://yasshiemd.web.fc2.com/sub/appli/simpleplayer/simpleplayer.html