001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.io.audio.fx; 003 004import java.io.File; 005import java.io.FileNotFoundException; 006import java.io.IOException; 007import java.net.URISyntaxException; 008import java.net.URL; 009import java.util.concurrent.CountDownLatch; 010 011import org.openstreetmap.josm.io.audio.AudioException; 012import org.openstreetmap.josm.io.audio.AudioListener; 013import org.openstreetmap.josm.io.audio.AudioPlayer.Execute; 014import org.openstreetmap.josm.io.audio.AudioPlayer.State; 015import org.openstreetmap.josm.io.audio.SoundPlayer; 016import org.openstreetmap.josm.tools.JosmRuntimeException; 017import org.openstreetmap.josm.tools.ListenerList; 018 019import com.sun.javafx.application.PlatformImpl; 020 021import javafx.scene.media.Media; 022import javafx.scene.media.MediaException; 023import javafx.scene.media.MediaPlayer; 024import javafx.scene.media.MediaPlayer.Status; 025import javafx.util.Duration; 026 027/** 028 * Default sound player based on the Java FX Media API. 029 * Used on platforms where Java FX is available. It supports the following audio codecs:<ul> 030 * <li>MP3</li> 031 * <li>AIFF containing uncompressed PCM</li> 032 * <li>WAV containing uncompressed PCM</li> 033 * <li>MPEG-4 multimedia container with Advanced Audio Coding (AAC) audio</li> 034 * </ul> 035 * @since 12328 036 * @deprecated MP3 support moved to openjfx plugin as JavaFX is gone with Java 11. 037 */ 038@Deprecated 039public class JavaFxMediaPlayer implements SoundPlayer { 040 041 private final ListenerList<AudioListener> listeners = ListenerList.create(); 042 043 private MediaPlayer mediaPlayer; 044 045 JavaFxMediaPlayer() { 046 try { 047 initFxPlatform(); 048 } catch (InterruptedException e) { 049 throw new JosmRuntimeException(e); 050 } 051 } 052 053 /** 054 * Initializes the JavaFX platform runtime. 055 * @throws InterruptedException if the current thread is interrupted while waiting 056 */ 057 public static void initFxPlatform() throws InterruptedException { 058 final CountDownLatch startupLatch = new CountDownLatch(1); 059 060 // Note, this method is called on the FX Application Thread 061 PlatformImpl.startup(startupLatch::countDown); 062 063 // Wait for FX platform to start 064 startupLatch.await(); 065 } 066 067 @Override 068 public synchronized void play(Execute command, State stateChange, URL playingUrl) throws AudioException, IOException { 069 try { 070 final URL url = command.url(); 071 if (playingUrl != url) { 072 if (mediaPlayer != null) { 073 mediaPlayer.stop(); 074 } 075 // Fail fast in case of invalid local URI (JavaFX Media locator retries 5 times with a 1 second delay) 076 if ("file".equals(url.getProtocol()) && !new File(url.toURI()).exists()) { 077 throw new FileNotFoundException(url.toString()); 078 } 079 mediaPlayer = new MediaPlayer(new Media(url.toString())); 080 mediaPlayer.setOnPlaying(() -> 081 listeners.fireEvent(l -> l.playing(url)) 082 ); 083 } 084 mediaPlayer.setRate(command.speed()); 085 if (Status.PLAYING == mediaPlayer.getStatus()) { 086 Duration seekTime = Duration.seconds(command.offset()); 087 if (!seekTime.equals(mediaPlayer.getCurrentTime())) { 088 mediaPlayer.seek(seekTime); 089 } 090 } 091 mediaPlayer.play(); 092 } catch (MediaException | URISyntaxException e) { 093 throw new AudioException(e); 094 } 095 } 096 097 @Override 098 public synchronized void pause(Execute command, State stateChange, URL playingUrl) throws AudioException, IOException { 099 if (mediaPlayer != null) { 100 try { 101 mediaPlayer.pause(); 102 } catch (MediaException e) { 103 throw new AudioException(e); 104 } 105 } 106 } 107 108 @Override 109 public boolean playing(Execute command) throws AudioException, IOException, InterruptedException { 110 // Not used: JavaFX handles the low-level audio playback 111 return false; 112 } 113 114 @Override 115 public synchronized double position() { 116 return mediaPlayer != null ? mediaPlayer.getCurrentTime().toSeconds() : -1; 117 } 118 119 @Override 120 public synchronized double speed() { 121 return mediaPlayer != null ? mediaPlayer.getCurrentRate() : -1; 122 } 123 124 @Override 125 public void addAudioListener(AudioListener listener) { 126 listeners.addWeakListener(listener); 127 } 128}