Создавайте аудиоклипы непрерывно в назначенное время запуска в JavaFx - PullRequest
0 голосов
/ 09 марта 2020

Мне нужно, чтобы короткий звуковой файл m4a воспроизводился непрерывно в течение 30 минут в назначенное время начала. Мне трудно заставить его играть непрерывно в назначенное время начала. Когда я исследовал эту проблему, мне показалось, что мне пришлось использовать отдельные потоки в JavaFX для выполнения этой работы, но я не смог написать ее с такими потоками, как Platform.runLater. Я также открыт для того, чтобы не использовать потоки для достижения этого эффекта. И я не могу сделать это l oop непрерывно в течение 30 минут с помощью методов MediaPlayer, предоставляемых в JavaFX (https://docs.oracle.com/javafx/2/api/javafx/scene/media/MediaPlayer.html).

Вот мой код, пока только возможность воспроизведения звукового файла один раз:

package MyAppPackage;


import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;

import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.scene.text.Text;
import javafx.application.Platform;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.scene.media.MediaView;
import javafx.util.Duration;

import java.io.File;

import java.time.ZoneId;
import java.time.ZonedDateTime;

public class MyApp extends Application {


    private void playAppTone(MediaPlayer mp, ZonedDateTime start, ZonedDateTime stop) {
        while(ZonedDateTime.now(ZoneId.of("America/New_York")).isAfter(start) &&
                 ZonedDateTime.now(ZoneId.of("America/New_York")).isBefore(stop) ) {

              mp.setOnEndOfMedia(new Runnable() {  
              @Override public void run() { 
                  mp.seek(Duration.ZERO); 
                  mp.play(); } 
              });
                 mp.setAutoPlay(true); 
        }
    }

    @Override
    public void start(Stage primaryStage) {

        Canvas canvas = new Canvas(400,400);

        HBox statusBar = new HBox(new Label("Start the App Tone"));
        BorderPane borderPane = new BorderPane(canvas, statusBar, null, null, null);
        StackPane root = new StackPane(borderPane);
        primaryStage.setScene(new Scene(root));
        primaryStage.setTitle("Timed Playback of Sound Clip");
        primaryStage.show();


        statusBar.setOnMouseClicked((event) -> {            
            ZoneId zoneId = ZoneId.of( "America/New_York" );
            ZonedDateTime appStartTime = ZonedDateTime.now( zoneId ); 
            System.out.println(appStartTime);
            ZonedDateTime start = appStartTime.plusMinutes(10);   
            ZonedDateTime stop = appStartTime.plusMinutes(40);

            String path = "media/PlaybackClip.m4a";

            Text playStatusText = new Text("A sound clip will be played continuously at the designated time...");
            Media media = new Media(new File(path).toURI().toString());
            MediaPlayer mp = new MediaPlayer(media);

            MediaView mv = new MediaView(mp);    
            StackPane stackPane = new StackPane();
            stackPane.getChildren().add(mv);
            stackPane.getChildren().add(playStatusText);
            primaryStage.setScene(new Scene(stackPane, 400, 400));

              mp.play(); 
              mp.setAutoPlay(true); 

            /*The Platform.runLater doesn't work
                Platform.runLater(() -> {
                    playAppTone(mp, start, stop); 
                }); */

        });
    }

    public static void main(String[] args) {
        launch(args);
    }
}

1 Ответ

2 голосов
/ 09 марта 2020

Я бы не использовал другой поток для реализации этого. Многократные потоки добавят ненужную сложность, особенно потому, что все вовлеченное должно быть (или, по крайней мере, может быть) выполнено в Потоке приложений JavaFX в любом случае. Вместо этого я бы использовал javafx.animation.Timeline с задержкой. Анимации сохраняют все в потоке FX и являются хорошим способом отложить или периодически выполнять (или оба) простые действия в указанном потоке.

Вот пример (некоторые пояснения в комментариях к коду):

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.stage.Stage;
import javafx.util.Duration;

public class App extends Application {

  @Override
  public void start(Stage primaryStage) {
    MediaPlayer player = new MediaPlayer(new Media(/* YOUR MEDIA URL */));
    /*
     * Set to INDEFINITE since the media may not be long enough to play
     * for the entire duration you want to. This will cause the media
     * to loop until stopped.
     */
    player.setCycleCount(MediaPlayer.INDEFINITE);

    Timeline timeline =
        new Timeline(
            /*
             * The first KeyFrame's time is set to Duration.ZERO in order
             * to start playing the MediaPlayer immediately after the
             * Timeline starts playing.
             */
            new KeyFrame(Duration.ZERO, e -> player.play()),
            /*
             * The next KeyFrame's time is set to however long you want the
             * media to play for. When the time elapses this KeyFrame will
             * stop the MediaPlayer. Depending on your use case you may want
             * to call MediaPlayer#dispose() to release its resources.
             */
            new KeyFrame(Duration.minutes(30.0), e -> player.stop())
        );
    /*
     * Set the delay of the Timeline to schedule playing the MediaPlayer
     * at some point in the future. The duration used here should be equal
     * to the duration between now and whatever wall clock time you want to
     * start playing the media.
     *
     * The delay should be calculated and set just before calling play. If you wait
     * then the delay will overshoot the wall clock time you expect the media to
     * start playing.
     */
    timeline.setDelay(durationUntil(LocalDateTime.now().plusSeconds(10L)));
    timeline.play();

    // provides some simple feedback
    Label statusLabel = new Label();
    statusLabel.textProperty().bind(player.statusProperty().asString("Current status: %s"));

    primaryStage.setScene(new Scene(new StackPane(statusLabel), 500.0, 300.0));
    primaryStage.show();
  }

  /**
   * Computes and returns the duration between {@linkplain LocalDateTime#now() now} and {@code
   * then}.
   *
   * @param then the time to compute the duration until
   * @return the duration between now and {@code then}
   * @throws IllegalArgumentException if {@code then} is in the past
   */
  private static Duration durationUntil(LocalDateTime then) {
    /*
     * Note: Will use the system Clock which may not be the same Clock
     *       used to create 'then'. May want to add a way to specify 
     *       a custom java.time.Clock if needed.
     */
    LocalDateTime now = LocalDateTime.now();
    if (then.isBefore(now)) {
      throw new IllegalArgumentException("then < now");
    }
    // Note: Using javafx.util.Duration, not java.time.Duration
    return Duration.millis(now.until(then, ChronoUnit.MILLIS));
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...