Воспроизведение аудио с помощью JavaFX MediaPlayer в обычном приложении Java?

Мне нужно иметь возможность воспроизводить аудиофайлы (MP3/Wav) в обычном проекте Java. Я бы предпочел использовать новый JavaFX MediaPlayer, а не JMF. Я написал некоторый код, чтобы проверить это:

public void play()
{
    URL thing = getClass().getResource("mysound.wav");
    Media audioFile = new Media( thing.toString() );     
    try
    {                                       
        MediaPlayer player = new MediaPlayer(audioFile);
        player.play();
    }
    catch (Exception e)
    {
        System.out.println( e.getMessage() );
        System.exit(0);
    }        
}

Когда я запускаю это, я получаю исключение: Toolkit не инициализирован

Я понимаю, что это как-то связано с потоком JavaFX. Мой вопрос в том, как я могу это решить? Нужно ли мне создавать панель JavaFX только для воспроизведения некоторых аудиофайлов в фоновом режиме моего обычного приложения, или есть другой способ?

Изменить: трассировка стека:

java.lang.IllegalStateException: Toolkit not initialized
    at com.sun.javafx.application.PlatformImpl.runLater(PlatformImpl.java:121)
    at com.sun.javafx.application.PlatformImpl.runLater(PlatformImpl.java:116)
    at javafx.application.Platform.runLater(Platform.java:52)
    at javafx.scene.media.MediaPlayer.init(MediaPlayer.java:445)
    at javafx.scene.media.MediaPlayer.<init>(MediaPlayer.java:360)
    at javaapplication6.JavaApplication6.play(JavaApplication6.java:23)
    at javaapplication6.JavaApplication6.main(JavaApplication6.java:14)

person Ali    schedule 22.09.2012    source источник
comment
Является ли строка 14 Media audioFile = new Media( thing.toString() );?   -  person Andrew Thompson    schedule 23.09.2012
comment
@AndrewThompson Нет, это в основном методе, вызывающем метод play(). Строка 23 — вот эта; MediaPlayer player = new MediaPlayer(audioFile);   -  person Ali    schedule 23.09.2012


Ответы (1)


Для решения с интеграцией JavaFX MediaPlayer в Swing

Используйте JFXPanel и следите за тем, чтобы использовать объекты JavaFX в потоке JavaFX и после правильной инициализации системы JavaFX.

JavaFX — это обычная Java, что делает вопрос немного запутанным, но я думаю, вы имеете в виду Swing.

Вот пример аудиоплеера, который запускается из Swing. В примере предполагается, что в общедоступной папке образцов музыки по умолчанию для Windows 7 (C:\Users\Public\Music\Sample Music) есть несколько mp3-файлов, и каждый файл воспроизводится по очереди.

скриншот приложения

JavaFXMediaPlayerLaunchedFromSwing.java

Этот код отвечает за создание приложения Swing, которое, в свою очередь, инициализирует инструментарий JavaFX и создает сцену JavaFX в потоке приложения JavaFX.

import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Scene;

import javax.swing.*;

/**
 * Example of playing all mp3 audio files in a given directory
 * using a JavaFX MediaView launched from Swing
 */
public class JavaFXMediaPlayerLaunchedFromSwing {
    private static void initAndShowGUI() {
        // This method is invoked on Swing thread
        JFrame frame = new JFrame("FX");
        final JFXPanel fxPanel = new JFXPanel();
        frame.add(fxPanel);
        frame.setBounds(200, 100, 800, 250);
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.setVisible(true);

        Platform.runLater(() -> initFX(fxPanel));
    }

    private static void initFX(JFXPanel fxPanel) {
        // This method is invoked on JavaFX thread
        Scene scene = new MediaSceneGenerator().createScene();
        fxPanel.setScene(scene);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(
            JavaFXMediaPlayerLaunchedFromSwing::initAndShowGUI
        );
    }
}

MediaSceneGenerator.java

Создает медиаплеер JavaFX, который последовательно воспроизводит все .mp3 медиафайлов в заданной папке. Он предоставляет некоторые элементы управления для мультимедиа (воспроизведение, пауза, пропуск дорожки, индикатор хода воспроизведения текущей дорожки).

import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.layout.VBox;
import javafx.scene.media.*;
import javafx.util.Duration;

import java.io.File;
import java.util.*;

public class MediaSceneGenerator {
    private static final String MUSIC_FOLDER = "C:\\Users\\Public\\Music\\Sample Music";
    private static final String MUSIC_FILE_EXTENSION = ".mp3";

    private final Label currentlyPlaying = new Label();
    private final ProgressBar progress = new ProgressBar();
    private ChangeListener<Duration> progressChangeListener;

    public Scene createScene() {
        final StackPane layout = new StackPane();

        // determine the source directory for the playlist
        final File dir = new File(MUSIC_FOLDER);
        if (!dir.exists() || !dir.isDirectory()) {
            System.out.println("Cannot find media source directory: " + dir);
            Platform.exit();
            return null;
        }

        // create some media players.
        final List<MediaPlayer> players = new ArrayList<>();
        for (String file : Objects.requireNonNull(dir.list((dir1, name) -> name.endsWith(MUSIC_FILE_EXTENSION))))
            players.add(
                    createPlayer(
                            normalizeFileURL(dir, file)
                    )
            );
        if (players.isEmpty()) {
            System.out.println("No audio found in " + dir);
            Platform.exit();
            return null;
        }

        // create a view to show the mediaplayers.
        final MediaView mediaView = new MediaView(players.get(0));
        final Button skip = new Button("Skip");
        final Button play = new Button("Pause");

        // play each audio file in turn.
        for (int i = 0; i < players.size(); i++) {
            MediaPlayer player = players.get(i);
            MediaPlayer nextPlayer = players.get((i + 1) % players.size());
            player.setOnEndOfMedia(() -> {
                final MediaPlayer curPlayer = mediaView.getMediaPlayer();
                nextPlayer.seek(Duration.ZERO);
                if (nextPlayer != curPlayer) {
                    curPlayer.currentTimeProperty().removeListener(progressChangeListener);
                }
                mediaView.setMediaPlayer(nextPlayer);
                nextPlayer.play();
            });
        }

        // allow the user to skip a track.
        skip.setOnAction(actionEvent -> {
            final MediaPlayer curPlayer = mediaView.getMediaPlayer();
            MediaPlayer nextPlayer = players.get((players.indexOf(curPlayer) + 1) % players.size());
            nextPlayer.seek(Duration.ZERO);
            mediaView.setMediaPlayer(nextPlayer);
            if (nextPlayer != curPlayer) {
                curPlayer.currentTimeProperty().removeListener(progressChangeListener);
            }
            nextPlayer.play();
        });

        // allow the user to play or pause a track.
        play.setOnAction(actionEvent -> {
            if ("Pause".equals(play.getText())) {
                mediaView.getMediaPlayer().pause();
                play.setText("Play");
            } else {
                mediaView.getMediaPlayer().play();
                play.setText("Pause");
            }
        });

        // display the name of the currently playing track.
        mediaView.mediaPlayerProperty().addListener(
                (observableValue, oldPlayer, newPlayer) -> setCurrentlyPlaying(newPlayer)
        );

        // start playing the first track.
        mediaView.setMediaPlayer(players.get(0));
        mediaView.getMediaPlayer().play();
        setCurrentlyPlaying(mediaView.getMediaPlayer());

        // silly invisible button used as a template to get the actual preferred size of the Pause button.
        Button invisiblePause = new Button("Pause");
        invisiblePause.setVisible(false);
        play.prefHeightProperty().bind(invisiblePause.heightProperty());
        play.prefWidthProperty().bind(invisiblePause.widthProperty());

        // layout the scene.
        HBox controls = new HBox(10, skip, play, progress);
        controls.setAlignment(Pos.CENTER);
        VBox mediaPanel = new VBox(10, currentlyPlaying, mediaView, controls);

        layout.setStyle("-fx-background-color: cornsilk; -fx-font-size: 20; -fx-padding: 20; -fx-alignment: center;");
        layout.getChildren().addAll(
                invisiblePause,
                mediaPanel
        );
        progress.setMaxWidth(Double.MAX_VALUE);
        HBox.setHgrow(progress, Priority.ALWAYS);

        return new Scene(layout);
    }

    /**
     * sets the currently playing label to the label of the new media player and updates the progress monitor.
     */
    private void setCurrentlyPlaying(final MediaPlayer newPlayer) {
        progress.setProgress(0);
        progressChangeListener = (observableValue, oldValue, newValue) ->
                progress.setProgress(
                        1.0 * newPlayer.getCurrentTime().toMillis() / newPlayer.getTotalDuration().toMillis()
                );
        newPlayer.currentTimeProperty().addListener(progressChangeListener);

        String source = getUserFriendlyMediaName(newPlayer);
        currentlyPlaying.setText("Now Playing: " + source);
    }

    /**
     * @return a MediaPlayer for the given source which will report any errors it encounters
     */
    private MediaPlayer createPlayer(String aMediaSrc) {
        System.out.println("Creating player for: " + aMediaSrc);
        final MediaPlayer player = new MediaPlayer(new Media(aMediaSrc));
        player.setOnError(() -> System.out.println("Media error occurred: " + player.getError()));
        return player;
    }

    private String normalizeFileURL(File dir, String file) {
        return "file:///" + (dir + "\\" + file).replace("\\", "/").replaceAll(" ", "%20");
    }

    private String getUserFriendlyMediaName(MediaPlayer newPlayer) {
        String source = newPlayer.getMedia().getSource();

        source = source.substring(0, source.length() - MUSIC_FILE_EXTENSION.length());
        source = source.substring(source.lastIndexOf("/") + 1).replaceAll("%20", " ");

        return source;
    }
}

Если вам просто нужно родное приложение JavaFX с MediaPlayer и без Swing

Приведенное выше решение, в котором используется Swing, отвечает на заданный вопрос. Однако я заметил, что иногда люди брали этот ответ и использовали его для создания медиаплееров на основе Java, даже если у них нет существующего приложения Swing, в которое они встраивают свое приложение.

Если у вас нет существующего приложения Swing, полностью исключите код Swing из своего приложения и вместо этого напишите собственное приложение JavaFX. Для этого используйте класс JavaFXMediaPlayer ниже вместо класса JavaFXMediaPlayerLaunchedFromSwing из примера выше.

JavaFXMediaPlayer

import javafx.application.Application;
import javafx.stage.Stage;

public class JavaFXMediaPlayer extends Application {
    @Override
    public void start(Stage stage) throws Exception {
        stage.setScene(new MediaSceneGenerator().createScene());
        stage.show();
    }
}

Ответы на дополнительные вопросы

Будет ли в мой файл .JAR, созданный с помощью Swing, автоматически добавляться JavaFX после того, как я добавлю его в библиотеки в netbeans?

Примечание: информация об упаковке в этом последующем ответе, вероятно, устарела, и в настоящее время существуют другие предпочтительные варианты упаковки (например, https://github.com/openjfx/javafx-maven-plugin).

Технически Swing не создает файлы Jar, а команды упаковки jar javafx.

Если ваше приложение содержит JavaFX, лучше использовать инструменты упаковки JavaFX. Без них у вас могут возникнуть некоторые проблемы с развертыванием, поскольку jar среды выполнения Java (jfxrt.jar) автоматически не находится в пути к классам загрузки java для jdk7u7. Пользователи могут вручную добавить его в свой путь к классам во время выполнения, но это может быть немного неудобно. В будущих версиях jdk (возможно, jdk7u10 или jdk8) файл jfxrt.jar будет находиться в пути к классам. Даже в этом случае рекомендуется использовать инструменты упаковки JavaFX, так как это лучший способ убедиться, что ваш пакет развертывания будет работать наиболее совместимым образом.

Проект NetBeans SwingInterop представляет собой образец проекта NetBeans, в котором используется JavaFX. инструменты развертывания для проекта Swing, включающего компоненты JavaFX. Исходный код для SwingInterop является частью демонстраций и примеров JDK 7 и JavaFX скачать.

person jewelsea    schedule 23.09.2012
comment
Спасибо за это, и да, я имел в виду Swing. Будет ли в мой файл .JAR, созданный с помощью netbeans, автоматически добавляться JavaFX, как только я добавлю его в библиотеки в netbeans? - person Ali; 23.09.2012
comment
Я добавил в ответ информацию о развертывании приложений Swing, в которые встроены компоненты JavaFX. - person jewelsea; 23.09.2012
comment
VBoxBuilder устарел - person Guillaume F.; 07.09.2019
comment
Обновлен пример, чтобы не использовать конструкции use, которые устарели. - person jewelsea; 09.09.2019
comment
Спасибо большое. Это действительно рабочее решение, которое я искал. Мерси бьен! - person Alexandre Marechal Ferrant; 17.05.2020