播放时如何获取音频元数据?

How obtain audio metadata when it's playing?

我正在创建一个简单的音频播放器。除了 MapChangeListener 之外,侦听器事件都很好。 运行 应用程序时,MapChangeListener(元数据)的侦听器同时被触发并获取最后一个元数据轨道。

例如:

我想做的:

我读到 Media 的 getMetadata() 方法在应用程序的同一线程中执行,因此我无法将监听器添加到 setOnPlaying( 运行nable() ) 方法。

还尝试在 Playing 事件中使用 Platform.runLater(),但简单不起作用。

感谢您的帮助。谢谢

我的听众

final public class MetadataListener implements MapChangeListener<String, Object> {

    private PlayerControl gui;
    private static int count;

    public MetadataListener(PlayerControl gui) {
        this.gui = gui;
    }
    @Override
    public void onChanged(Change<? extends String, ? extends Object> change) {
        count++;
        if(change.wasAdded()) {
            handleMetadata(change.getKey().toString(), change.getValueAdded());
        }
    }



    private void handleMetadata(String key, Object value) {
            if(key.equals("title"))
                gui.getLblTitle().setText(value.toString());
            if(key.equals("artist"))
                gui.getLblArtist().setText(value.toString());
            if(key.equals("album"))
                gui.getLblAlbum().setText(value.toString());
            if(key.equals("year"))
                gui.getLblYear().setText(value.toString());
            if(key.equals("image"))
                gui.getImgViewAlbum().setImage((Image) value);
        }

    }

我的播放器

public void createMediaList() {
        for(String file : filterFiles()) {
            playList.add(createPlayer(file));
        }
        if(playList.isEmpty()) {
            System.out.println("No se han encontrado archivos multimedia");
            Platform.exit();
            return;
        }
    }

private MediaPlayer createPlayer(String filePath) {
        Media media = new Media(new File(dir.toString()+"/"+filePath).toURI().toString());
        MediaPlayer player = new MediaPlayer(media);
        this.attachListeners(player);
        return player;
    }



private void attachListeners(MediaPlayer player) {
        player.setOnPlaying(() -> {
            duration = player.getMedia().getDuration();
            gui.getLblTotalDuration().setText(formatCurrentDuration(duration));
            gui.getBtnPlayPause().setText(" | | ");
            updateValues();
        });
        player.currentTimeProperty().addListener((observable, oldValue, newValue) -> {
            updateValues();
        });
        player.getMedia().getMetadata().addListener(metadataListener);
        player.setOnEndOfMedia(() -> {
            player.stop();
            this.nextMedia();
        });
        //player.setOnStopped( () -> player.getMedia().getMetadata().removeListener(metadataListener));
        player.setOnPaused( () -> gui.getBtnPlayPause().setText("►"));
        player.setOnError(() -> System.out.println("Ha ocurrido un error"));
    }

protected void updateValues() {
        Label lblCurrentTime = gui.getLblCurrentTime();
        Label lblTotalDuration = gui.getLblTotalDuration();
        Slider sldPlayer = gui.getSldPlayer();
        //Slider sldVolume = gui.getSldVolume();

        if(lblCurrentTime != null && lblTotalDuration != null && sldPlayer != null && /*sldVolume != null &&*/ duration != null) {
            Platform.runLater(() -> {
                Duration currentTime = this.playList.get(currentMediaIndex).getCurrentTime();
                lblCurrentTime.setText(formatCurrentDuration(currentTime));
                sldPlayer.setDisable(duration.isUnknown());
                if(!sldPlayer.isDisabled() && duration.greaterThan(Duration.ZERO) && !sldPlayer.isValueChanging()) {
                    sldPlayer.setValue(currentTime.divide(duration).toMillis() * 100.0);
                }

            });
        }
    }

原因

MapChangeListener is fired for all tracks

The metadata showed in my App is the last track.

在您的代码中:

for(String file : filterFiles()) {
    playList.add(createPlayer(file));
}

private MediaPlayer createPlayer(String filePath) {
    Media media = new Media(new File(dir.toString()+"/"+filePath).toURI().toString());
    MediaPlayer player = new MediaPlayer(media);
    this.attachListeners(player);
    return player;
}

当您启动您的应用程序时,您正在为播放列表中的所有曲目创建媒体播放器并为它们附加一个侦听器。一旦为每个轨道检索元数据,就会触发所有侦听器,所有标签都会更新,这就是为什么您只能看到最后一个轨道元数据的原因。

所以首先,检索有效媒体播放器列表,并调用第一首曲目:

private final List<MediaPlayer> playList = new ArrayList<>();

public void createMediaList() {
    for(String file : filterFiles()) {
        MediaPlayer player=createPlayer(file);
        if(player!=null){
            playList.add(player);
        }
    }
    if(playList.isEmpty()) {
        System.out.println("No se han encontrado archivos multimedia");
        Platform.exit();
        return;
    }
    // start first track
    nextMedia();
}

哪里

private MediaPlayer createPlayer(String filePath) {
    try{
        Media media = new Media(new File(dir+"/"+filePath).toURI().toString());
        MediaPlayer player=new MediaPlayer(media);
        return player;
    } catch(MediaException me){
        System.out.println("MediaException "+me);
    }
    return null;
}

因为不是每个文件都有效,所以在 try catch 中围绕媒体播放器创建很方便。

现在,nextMedia()只获取单个曲目的媒体播放器,并附加听众。

编辑: 通常,创建媒体播放器时,使用 setOnReady() 开始播放很方便,但在这种情况下,媒体播放器适用于所有曲目一开始就创建好了,此时玩家已经准备好了。

private void nextMedia(){
    MediaPlayer player=playList.get(currentMediaIndex++);
    attachListeners(player);
    if(player!=null){
        player.play();
    } else {
        nextMedia();
    }
}

曲目准备就绪后,将检索所有元数据并更新标签:

private void attachListeners(MediaPlayer player) {
    ...
    player.getMedia().getMetadata().addListener(metadataListener);
    ...
    player.setOnEndOfMedia(() -> {
        player.getMedia().getMetadata().removeListener(metadataListener);
        player.stop();
        nextMedia();
    });
}

当曲目结束时,您的听众将再次呼叫 nextMedia()