JavaFX多媒體(2)使用Media播放音訊與影像 by 黃嘉輝 | CodeData
top

JavaFX多媒體(2)使用Media播放音訊與影像

分享:

JavaFX多媒體(1)使用Audio Clip播放音訊 << 前情

在前一篇當中,我們介紹如何以JavaFX的AudioClip類別播放簡短音訊,AudioClip類別的主要功能僅在於播放與停止播放音訊,並無法處理暫停播放、音訊時間長度、後設資料、等化器、事件等功能。

除了AudioClip類別之外,欲播放音訊與影像媒體,可使用以下類別處理:

  • javafx.scene.media.Media
  • javafx.scene.media.MediaPlayer
  • javafx.scene.media.MediaView

其中Media類別用以設定音訊與影像來源,可為URL或本機檔案。此外,Media類別並處理音訊與影像的時間長度、後設資料、標記等資訊。

Media類別設定音訊與影像來源之後,則交由MediaPlayer類別處理,如同其名稱Media Player (媒體播放器) 一般,用以播放、暫停、重覆或停止播放媒體,並處理調整音量平衡、重覆播放次數、播放速率、音量大小、時間長度、頻譜等資訊。

MediaView類別如同TableViewTreeViewWebView一般,用以「顯示」影像媒體。在實作上,若處理音訊,僅需使用MediaMediaPlayer類別;若處理影像,則需以MediaMediaPlayerMediaView類別三者搭配一起使用。

請參考以下範例,分別以MediaMediaPlayer類別播放音訊,首先以Media類別設定音訊來源,可為URL或本機檔案,接著以MediaPlayer類別的setAutoPlay()方法自動播放音訊:

Media media;
MediaPlayer mediaplayer;
...

try {
  String directory = System.getProperty("user.dir");
  File file = new File(directory, "../../media/Audio.mp3");

  media = new Media(file.toURI().toString());
  mediaplayer = new MediaPlayer(media);

  // 自動播放音訊
  mediaplayer.setAutoPlay(true);
}
catch (Exception ex) {
  ex.toString();
}

請參考以下範例,分別以MediaMediaPlayerMediaView類別播放影像,首先以Media類別設定影像來源,可為URL或本機檔案,接著以MediaPlayer類別建立MediaPlayer物件,並由MediaView類別「顯示」影像媒體,最後再以MediaPlayer類別的play()方法播放影像:

Media media;
MediaPlayer mediaplayer;
MediaView mediaview;
...

try {
  String directory = System.getProperty("user.dir");
  File file = new File(directory, "../../media/Video.mp4");

  media = new Media(file.toURI().toString());
  mediaplayer = new MediaPlayer(media);
  mediaview = new MediaView(mediaplayer);
}
catch (Exception ex) {
  ex.toString();
}
...

BorderPane borderpane = new BorderPane(); 
borderpane.setCenter(mediaview);
...

// 播放影像
mediaplayer.play();

為免影像因尺寸太大而超出視窗範圍,因此以MediaView類別的setFitHeight()setFitWidth()方法分別設定影像的適合 (Fit) 高度與寬度,以便讓影像符合視窗範圍。

此外,並以heightProperty().addListener()widthProperty().addListener()方法分別處理視窗高度與寬度的改變事件,當視窗高度或寬度改變時,同時調整影像的高度或寬度:

mediaview.setFitHeight(primaryStage.getHeight()); 
mediaview.setFitWidth(primaryStage.getWidth());

primaryStage.heightProperty().addListener(new ChangeListener() {
  @Override public void changed(ObservableValue observable,
  Number oldValue, Number newValue) {
    mediaview.setFitHeight((Double)newValue);
  }
});

primaryStage.widthProperty().addListener(new ChangeListener() {
  @Override public void changed(ObservableValue observable,
  Number oldValue, Number newValue) {
    mediaview.setFitWidth((Double)newValue);
  }
});

請參考以下範例,範例加入標籤與滑動軸處理音訊,前者處理播放、暫停、向前與倒轉,後者調整時間長度、音量大小與音量平衡。

以播放與暫停為例,當點選播放標籤時,首先以MediaPlayer類別的getStatus()方法取得媒體目前的狀態,其回傳值分別為:

  • MediaPlayer.Status.DISPOSED:當執行dispose()方法釋放資源時。
  • MediaPlayer.Status.HALTED:錯誤發生時。
  • MediaPlayer.Status.PAUSED:暫停播放時。
  • MediaPlayer.Status.PLAYING:正在播放時。
  • MediaPlayer.Status.READY:媒體準備播放時。
  • MediaPlayer.Status.STALLED:當無法處理媒體致使播放速度變慢或停止時。
  • MediaPlayer.Status.STOPPED:停止播放時。
  • MediaPlayer.Status.UNKNOWNMediaPlayer物件剛建立時。

若狀態為正在播放時 (MediaPlayer.Status.PLAYING),則以MediaPlayer類別的pause()方法暫停播放;反之則以play()方法播放音訊:

Label lblPlay = new Label("");
...
lblPlay.setOnMouseClicked(new EventHandler() {
  @Override public void handle(MouseEvent e) {
    // 取得媒體目前的狀態
    if (mediaplayer.getStatus() == MediaPlayer.Status.PLAYING) {
      // 暫停播放
      mediaplayer.pause();
    }
    else {
      // 播放音訊
      mediaplayer.play();
    }
  }
});

以向前播放 (Forward) 為例,當點選向前標籤時,首先以MediaPlayer類別的getTotalDuration()方法取得音訊的總時間,接著以seek()方法移至音訊的最後,並藉此調整滑動軸的位置:

Label lblForward = new Label("");
...
lblForward.setOnMouseClicked(new EventHandler() {
  @Override public void handle(MouseEvent e) {
    // 取得音訊的總時間
    final Duration totalDuration = mediaplayer.getTotalDuration();
    // 取得音訊目前的狀態
    if (mediaplayer.getStatus() == MediaPlayer.Status.STOPPED) {
      mediaplayer.pause();
    }
    // 移至音訊的最後
    mediaplayer.seek(totalDuration);
    // 取得音訊目前的狀態
    if (mediaplayer.getStatus() != MediaPlayer.Status.PLAYING) {
      if (sliderDuration.isValueChanging())
        return;

      final Duration total = mediaplayer.getTotalDuration();

      if (total == null || totalDuration == null) {
        // 調整滑動軸的位置
        sliderDuration.setValue(0);
      }
      else {
        // 調整滑動軸的位置
        sliderDuration.setValue(
        totalDuration.toMillis() / total.toMillis());
      }
    }
  }
});

倒轉播放 (Rewind) 類似於向前播放,差別僅在於當點選倒轉標籤時,首先將音訊的目前進度時間設為Duration.ZERO (歸零),接著以seek()方法移至音訊的最前端,並藉此調整滑動軸的位置:

Label lblRewind = new Label("");
...
lblRewind.setOnMouseClicked(new EventHandler() {
  @Override public void handle(MouseEvent e) {
    // 設定目前的進度時間為零
    final Duration currentduration = Duration.ZERO;
    // 取得音訊目前的狀態
    if (mediaplayer.getStatus() == MediaPlayer.Status.STOPPED) {
      mediaplayer.pause();
    }
    // 移至音訊的最前端
    mediaplayer.seek(currentduration);
    // 取得音訊目前的狀態
    if (mediaplayer.getStatus() != MediaPlayer.Status.PLAYING) {
      if (sliderDuration.isValueChanging())
        return;

      final Duration total = mediaplayer.getTotalDuration();

      if (total == null) {
        // 調整滑動軸的位置
        sliderDuration.setValue(0);
      }
      else {
        // 調整滑動軸的位置
        sliderDuration.setValue(
        currentduration.toMillis() / total.toMillis());
      }
    }
  }
});

以滑動軸調整音量大小 (Volume) 為例,首先以Slider類別的valueProperty()方法取得滑動軸的值屬性,接著以bindBidirectional()方法與MediaPlayer類別的volumeProperty()建立雙向繫結 (Bidirectional Binding),當移動滑動軸時,則以滑動軸的值設定音訊的音量大小:

Slider sliderVolume = new Slider();
sliderVolume.setMin(0.0);
sliderVolume.setMax(1.0);
sliderVolume.setValue(1.0);
sliderVolume.setPrefWidth(60);
sliderVolume.setMaxWidth(Region.USE_PREF_SIZE);
sliderVolume.setMinWidth(30);

// 建立滑動軸與音訊音量的雙向繫結
sliderVolume.valueProperty().bindBidirectional(mediaplayer.volumeProperty());

以滑動軸調整音量平衡 (Balance) 為例,首先以Slider類別的valueProperty()方法取得滑動軸的值屬性,接著以addListener()方法處理滑動軸事件,當移動滑動軸時,則以滑動軸的值設定音訊的音量平衡:

final Slider sliderBalance = new Slider();
sliderBalance.setMin(-1.0);
sliderBalance.setMax(1.0);
sliderBalance.setValue(0.0);
sliderBalance.setPrefWidth(60); 
sliderBalance.setMaxWidth(Region.USE_PREF_SIZE);
sliderBalance.setMinWidth(30);
sliderBalance.valueProperty().addListener(new InvalidationListener() {
  @Override public void invalidated(Observable observable) {
    if (sliderBalance.isValueChanging()) {
      // 設定音訊的音量平衡
      mediaplayer.setBalance(sliderBalance.getValue());
    }
  }
});

【執行結果】

18-5

請參考以下範例,加入標籤與滑動軸處理影像,前者處理播放、暫停、向前與倒轉,後者調整時間長度、音量大小與音量平衡,程式與前述範例幾乎一樣,差別僅在於,以MediaPlayer類別所建立MediaPlayer物件,需由MediaView類別「顯示」影像媒體。

【執行結果】

18-6

【參考資料】

[1] Java Official Web Site
[2] JavaFX
[3] NetBeans
[4] JavaFX 2.2 API Specification.
[5] Java Platform, Standard Edition 7 API Specification.

後續 >> JavaFX多媒體(3)Metadata與Marker

分享:
按讚!加入 CodeData Facebook 粉絲群

相關文章

留言

留言請先。還沒帳號註冊也可以使用FacebookGoogle+登錄留言

關於作者

黃嘉輝副教授,目前任職於國立臺北商業大學企業管理學系,喜歡寫程式,特別愛Java,範例可參考教學網站

熱門論壇文章

熱門技術文章