在開發應用程式或是遊戲的時候,常常會需要加入一些音樂或是音效來提示或是取悅使用者。那麼如果是使用Java程式語言的話該如何播放出聲音呢?難不成要像是Csound一樣需要使用振盪器來振盪出不同波形、振幅、頻率的訊號嗎?不用!實際上,Java有提供音樂播放的相關套件,可以直接讀取網路上或是電腦內的音訊檔案(Audio File)。



在Javax函式庫中,提供了AudioInputStream可以用來讀取音訊檔案,但它只能支援WAV、AIFF、AU等PCM(Pulse-code modulation)格式,如果要能播放如MP2、MP3等其他有編碼壓縮過音訊格式的話,可以使用JavaFX提供的MediaPlayer或AudioClip,或是第三方的Java函式庫。

使用MediaPlayer或AudioClip播放音訊

AudioClip能播放的音訊格式有非常多種,如果想要知道有哪幾種的話,可以使用MediaManager的getSupportedContentTypes方法來取得。

MediaManager.getSupportedContentTypes();

AudioClip使用方式十分簡單,只要將其實體化後再使用play方法即可。

AudioClip plonkSound = new AudioClip(url);
plonkSound.play();

MediaPlayer是AudioClip的進階版本,提供多種取得媒體資訊及播放控制的方法。

MediaPlayer plonkSound = new MediaPlayer(new Media(uri));
plonkSound.play();

雖然使用MediaPlayer或AudioClip來播放聲音十分方便,但是MediaPlayer或AudioClip的跨平台性實在不怎麼好,因為它的音樂解碼器仰賴於執行的作業系統上的音訊解碼器(函式庫),所以可能會在某些平台下無法播放某些特定的格式。因此,最保險的作法,還是將Java要播放的音樂都轉成WAV、AIFF或是AU格式,然後使用以下提到的AudioInputStream來播放。

使用AudioInputStream播放音訊

AudioInputStream會使用JDK Service提供的AudioFileReader來讀取音訊檔案,建立AudioInputStream實體可以透過AudioSystem的getAudioInputStream方法,程式如下:

AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(url);

由於AudioInputStream只是一個輸入串流,並有沒辦法控制聲音的播放與輸出,因此我們必須要有一個時間軸。要產生出一個時間軸前,我們必須要先知道音訊的SampleRate、Bitrate、Size等等重要的資訊,可以透過AudioInputStream的getFormat方法來取得。

AudioFormat audioFormat = audioInputStream.getFormat();

再來就是利用這些資訊,建立出時間軸,時間軸可以使用繼承DataLine的Clip物件。要建立出Clip物件,可以先建立出DataLine的Info物件後,再使用AudioSystem的getLine方法取得Clip物件,然後再用這個擁有audioFormat資訊的Clip物件去開啟audioInputStream。javax把這個部份搞得有點複雜,程式如下:

int bufferSize = (int) Math.min(audioInputStream.getFrameLength() * audioFormat.getFrameSize(), Integer.MAX_VALUE); //緩衝大小,如果音訊檔案不大,可以全部存入緩衝空間。這個數值應該要按照用途來決定
DataLine.Info dataLineInfo = new DataLine.Info(Clip.class, audioFormat, bufferSize);
Clip clip = (Clip) AudioSystem.getLine(dataLineInfo);
clip.open(audioInputStream);

再來就可以使用Clip物件的start、stop等等的方法來控制聲音的播放了!

使用MagicAudioPlayer播放音訊

MagicAudioPlayer是一個基於AudioInputStream和MediaPlayer所實作出來的聲音播放器,因此除了能支援WAV、AIFF或是AU等未壓縮音訊格式之外,還可以藉由系統函式庫的幫助來支援MP3、AAC等音訊格式。

取得MagicAudioPlayer

GitHub:

使用MagicAudioPlayer

MagicAudioPlayer的使用方法十分容易,只要將其實體化後再使用play方法即可。讀取音訊檔案的方式如下:

File audioFile = new File("/home/magiclen/test.wav");
AudioPlayer player = AudioPlayer.createPlayer(audioFile);
player.play();

也可以用URL當作音訊來源:

AudioPlayer player = AudioPlayer.createPlayer(url);
player.play();

如果要暫停、停止播放的話,可以使用pause與stop方法。

player.pause();
player.stop();

如果要設定播放次數以及播放完成後自動關閉並釋放資源的話,可以使用setPlayCount和setAutoClose方法,程式如下:

player.setPlayCount(2);
player.setAutoClose(true);

另外還可以使用setVolume和setBalance方法設定播放的音量以及左右聲道的平衡,程式如下:

player.setVolume(100);
player.setBalance(100);

如果要取得AudioPlayer目前的狀態,可以使用getStatus方法,如下:

AudioPlayer.Status status = player.getStatus();

如果要監聽AudioPlayer的狀態改變事件,可以使用setStatusChangedListener方法,如下:

player.setStatusChangedListener((previousStatus, currentStatus) -> {
    switch (currentStatus) {
        case OPEN:
            break;
        case START:
            break;
        case STOP:
            break;
        case CLOSE:
            break;
    }
});

當然也可以取得與設定目前播放的位置,如下:

long position = player.getAudioPosition();
//往前一秒
player.setAudioPosition(position + 1000000);