Ticket #2089: 2089_alpha1.patch
| File 2089_alpha1.patch, 29.8 KB (added by , 9 years ago) |
|---|
-
src/org/openstreetmap/josm/actions/audio/AudioBackAction.java
13 13 import org.openstreetmap.josm.actions.JosmAction; 14 14 import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer; 15 15 import org.openstreetmap.josm.io.audio.AudioPlayer; 16 import org.openstreetmap.josm.io.audio.AudioUtil; 16 17 import org.openstreetmap.josm.tools.Shortcut; 17 18 18 19 /** … … 39 40 else 40 41 MarkerLayer.playAudio(); 41 42 } catch (IOException | InterruptedException ex) { 42 Audio Player.audioMalfunction(ex);43 AudioUtil.audioMalfunction(ex); 43 44 } 44 45 } 45 46 } -
src/org/openstreetmap/josm/actions/audio/AudioFastSlowAction.java
7 7 import org.openstreetmap.josm.Main; 8 8 import org.openstreetmap.josm.actions.JosmAction; 9 9 import org.openstreetmap.josm.io.audio.AudioPlayer; 10 import org.openstreetmap.josm.io.audio.AudioUtil; 10 11 import org.openstreetmap.josm.tools.Shortcut; 11 12 12 13 /** … … 42 43 if (AudioPlayer.playing() || AudioPlayer.paused()) 43 44 AudioPlayer.play(AudioPlayer.url(), AudioPlayer.position(), speed * multiplier); 44 45 } catch (IOException | InterruptedException ex) { 45 Audio Player.audioMalfunction(ex);46 AudioUtil.audioMalfunction(ex); 46 47 } 47 48 } 48 49 } -
src/org/openstreetmap/josm/actions/audio/AudioFwdAction.java
12 12 import org.openstreetmap.josm.actions.JosmAction; 13 13 import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer; 14 14 import org.openstreetmap.josm.io.audio.AudioPlayer; 15 import org.openstreetmap.josm.io.audio.AudioUtil; 15 16 import org.openstreetmap.josm.tools.Shortcut; 16 17 17 18 /** … … 37 38 else 38 39 MarkerLayer.playAudio(); 39 40 } catch (IOException | InterruptedException ex) { 40 Audio Player.audioMalfunction(ex);41 AudioUtil.audioMalfunction(ex); 41 42 } 42 43 } 43 44 } -
src/org/openstreetmap/josm/actions/audio/AudioPlayPauseAction.java
13 13 import org.openstreetmap.josm.gui.layer.markerlayer.AudioMarker; 14 14 import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer; 15 15 import org.openstreetmap.josm.io.audio.AudioPlayer; 16 import org.openstreetmap.josm.io.audio.AudioUtil; 16 17 import org.openstreetmap.josm.tools.Shortcut; 17 18 import org.openstreetmap.josm.tools.Utils; 18 19 … … 54 55 } 55 56 } 56 57 } catch (IOException | InterruptedException ex) { 57 Audio Player.audioMalfunction(ex);58 AudioUtil.audioMalfunction(ex); 58 59 } 59 60 } 60 61 } -
src/org/openstreetmap/josm/gui/layer/markerlayer/AudioMarker.java
12 12 import org.openstreetmap.josm.data.gpx.GpxLink; 13 13 import org.openstreetmap.josm.data.gpx.WayPoint; 14 14 import org.openstreetmap.josm.io.audio.AudioPlayer; 15 import org.openstreetmap.josm.io.audio.AudioUtil; 15 16 import org.openstreetmap.josm.tools.template_engine.TemplateEngineDataProvider; 16 17 17 18 /** … … 59 60 AudioPlayer.play(audioUrl, offset + syncOffset + after); 60 61 recentlyPlayedMarker = this; 61 62 } catch (IOException | InterruptedException e) { 62 Audio Player.audioMalfunction(e);63 AudioUtil.audioMalfunction(e); 63 64 } 64 65 } 65 66 -
src/org/openstreetmap/josm/gui/layer/markerlayer/PlayHeadMarker.java
24 24 import org.openstreetmap.josm.gui.MapView; 25 25 import org.openstreetmap.josm.gui.layer.GpxLayer; 26 26 import org.openstreetmap.josm.io.audio.AudioPlayer; 27 import org.openstreetmap.josm.io.audio.AudioUtil; 27 28 28 29 /** 29 30 * Singleton marker class to track position of audio. … … 99 100 try { 100 101 AudioPlayer.pause(); 101 102 } catch (IOException | InterruptedException ex) { 102 Audio Player.audioMalfunction(ex);103 AudioUtil.audioMalfunction(ex); 103 104 } 104 105 } 105 106 } … … 113 114 try { 114 115 AudioPlayer.pause(); 115 116 } catch (IOException | InterruptedException ex) { 116 Audio Player.audioMalfunction(ex);117 AudioUtil.audioMalfunction(ex); 117 118 } 118 119 } 119 120 if (reset) { -
src/org/openstreetmap/josm/io/audio/AudioException.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.io.audio; 3 4 /** 5 * Generic audio exception. Mainly used to wrap backend exceptions varying between implementations. 6 * @since xxx 7 */ 8 public class AudioException extends Exception { 9 10 /** 11 * Constructs a new {@code AudioException}. 12 * @param message the detail message (which is saved for later retrieval by the {@link #getMessage()} method). 13 * @param cause the cause (which is saved for later retrieval by the {@link #getCause()} method). 14 */ 15 public AudioException(String message, Throwable cause) { 16 super(message, cause); 17 } 18 19 /** 20 * Constructs a new {@code AudioException}. 21 * @param message the detail message (which is saved for later retrieval by the {@link #getMessage()} method). 22 */ 23 public AudioException(String message) { 24 super(message); 25 } 26 27 /** 28 * Constructs a new {@code AudioException}. 29 * @param cause the cause (which is saved for later retrieval by the {@link #getCause()} method). 30 */ 31 public AudioException(Throwable cause) { 32 super(cause); 33 } 34 } -
src/org/openstreetmap/josm/io/audio/AudioListener.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.io.audio; 3 4 import java.net.URL; 5 6 /** 7 * Listener receiving audio playing events. 8 * @since xxx 9 */ 10 interface AudioListener { 11 12 /** 13 * Called when a new URL is being played. 14 * @param playingURL new URL being played 15 * @param position position, in seconds 16 * @param speed speed factor 17 */ 18 void playing(URL playingURL, double position, double speed); 19 } -
src/org/openstreetmap/josm/io/audio/AudioPlayer.java
1 1 // License: GPL. For details, see LICENSE file. 2 2 package org.openstreetmap.josm.io.audio; 3 3 4 import static org.openstreetmap.josm.tools.I18n.tr;5 6 import java.awt.GraphicsEnvironment;7 4 import java.io.IOException; 8 5 import java.net.URL; 9 6 10 import javax.sound.sampled.AudioFormat;11 import javax.sound.sampled.AudioInputStream;12 import javax.sound.sampled.AudioSystem;13 import javax.sound.sampled.DataLine;14 import javax.sound.sampled.LineUnavailableException;15 import javax.sound.sampled.SourceDataLine;16 import javax.sound.sampled.UnsupportedAudioFileException;17 import javax.swing.JOptionPane;18 19 7 import org.openstreetmap.josm.Main; 20 8 import org.openstreetmap.josm.tools.JosmRuntimeException; 21 import org.openstreetmap.josm.tools.Utils;22 9 23 10 /** 24 11 * Creates and controls a separate audio player thread. … … 27 14 * @since 12326 (move to new package) 28 15 * @since 547 29 16 */ 30 public final class AudioPlayer extends Thread {17 public final class AudioPlayer extends Thread implements AudioListener { 31 18 32 19 private static volatile AudioPlayer audioPlayer; 33 20 34 privateenum State { INITIALIZING, NOTPLAYING, PLAYING, PAUSED, INTERRUPTED }21 enum State { INITIALIZING, NOTPLAYING, PLAYING, PAUSED, INTERRUPTED } 35 22 36 privateenum Command { PLAY, PAUSE }23 enum Command { PLAY, PAUSE } 37 24 38 privateenum Result { WAITING, OK, FAILED }25 enum Result { WAITING, OK, FAILED } 39 26 40 27 private State state; 28 private SoundPlayer soundPlayer; 41 29 private URL playingUrl; 42 private final double leadIn; // seconds43 private final double calibration; // ratio of purported duration of samples to true duration44 30 private double position; // seconds 45 private double bytesPerSecond;46 private static long chunk = 4000; /* bytes */47 31 private double speed = 1.0; 48 32 49 33 /** 50 34 * Passes information from the control thread to the playing thread 51 35 */ 52 privateclass Execute {36 class Execute { 53 37 private Command command; 54 38 private Result result; 55 39 private Exception exception; … … 84 68 throw new IOException(exception); 85 69 } 86 70 87 pr ivatevoid possiblyInterrupt() throws InterruptedException {71 protected void possiblyInterrupt() throws InterruptedException { 88 72 if (interrupted() || result == Result.WAITING) 89 73 throw new InterruptedException(); 90 74 } … … 250 234 state = State.INITIALIZING; 251 235 command = new Execute(); 252 236 playingUrl = null; 253 leadIn = Main.pref.getDouble("audio.leadin", 1.0 /* default, seconds */); 254 calibration = Main.pref.getDouble("audio.calibration", 1.0 /* default, ratio */); 237 double leadIn = Main.pref.getDouble("audio.leadin", 1.0 /* default, seconds */); 238 double calibration = Main.pref.getDouble("audio.calibration", 1.0 /* default, ratio */); 239 try { 240 soundPlayer = new JavaFxMediaPlayer(); 241 } catch (NoClassDefFoundError e) { 242 Main.debug(e); 243 Main.warn("Java FX is unavailable. Falling back to Java Sound API"); 244 soundPlayer = new JavaSoundPlayer(leadIn, calibration); 245 } 246 soundPlayer.addAudioListener(this); 255 247 start(); 256 248 while (state == State.INITIALIZING) { 257 249 yield(); … … 262 254 * Starts the thread to actually play the audio, per Thread interface 263 255 * Not to be used as public, though Thread interface doesn't allow it to be made private 264 256 */ 265 @Override public void run() { 257 @Override 258 public void run() { 266 259 /* code running in separate thread */ 267 260 268 261 playingUrl = null; 269 AudioInputStream audioInputStream = null;270 SourceDataLine audioOutputLine = null;271 AudioFormat audioFormat;272 byte[] abData = new byte[(int) chunk];273 262 274 263 for (;;) { 275 264 try { … … 284 273 break; 285 274 case PLAYING: 286 275 command.possiblyInterrupt(); 287 for (;;) { 288 int nBytesRead = 0; 289 if (audioInputStream != null) { 290 nBytesRead = audioInputStream.read(abData, 0, abData.length); 291 position += nBytesRead / bytesPerSecond; 292 } 293 command.possiblyInterrupt(); 294 if (nBytesRead < 0 || audioInputStream == null || audioOutputLine == null) { 295 break; 296 } 297 audioOutputLine.write(abData, 0, nBytesRead); // => int nBytesWritten 298 command.possiblyInterrupt(); 299 } 300 // end of audio, clean up 301 if (audioOutputLine != null) { 302 audioOutputLine.drain(); 303 audioOutputLine.close(); 304 } 305 audioOutputLine = null; 306 Utils.close(audioInputStream); 307 audioInputStream = null; 276 soundPlayer.play(command); 308 277 playingUrl = null; 309 278 state = State.NOTPLAYING; 310 279 command.possiblyInterrupt(); … … 318 287 try { 319 288 switch (command.command()) { 320 289 case PLAY: 321 double offset = command.offset(); 322 speed = command.speed(); 323 if (playingUrl != command.url() || 324 stateChange != State.PAUSED || 325 offset != 0) { 326 if (audioInputStream != null) { 327 Utils.close(audioInputStream); 328 } 329 playingUrl = command.url(); 330 audioInputStream = AudioSystem.getAudioInputStream(playingUrl); 331 audioFormat = audioInputStream.getFormat(); 332 long nBytesRead; 333 position = 0.0; 334 offset -= leadIn; 335 double calibratedOffset = offset * calibration; 336 bytesPerSecond = audioFormat.getFrameRate() /* frames per second */ 337 * audioFormat.getFrameSize() /* bytes per frame */; 338 if (speed * bytesPerSecond > 256_000.0) { 339 speed = 256_000 / bytesPerSecond; 340 } 341 if (calibratedOffset > 0.0) { 342 long bytesToSkip = (long) (calibratedOffset /* seconds (double) */ * bytesPerSecond); 343 // skip doesn't seem to want to skip big chunks, so reduce it to smaller ones 344 while (bytesToSkip > chunk) { 345 nBytesRead = audioInputStream.skip(chunk); 346 if (nBytesRead <= 0) 347 throw new IOException(tr("This is after the end of the recording")); 348 bytesToSkip -= nBytesRead; 349 } 350 while (bytesToSkip > 0) { 351 long skippedBytes = audioInputStream.skip(bytesToSkip); 352 bytesToSkip -= skippedBytes; 353 if (skippedBytes == 0) { 354 // Avoid inifinite loop 355 Main.warn("Unable to skip bytes from audio input stream"); 356 bytesToSkip = 0; 357 } 358 } 359 position = offset; 360 } 361 if (audioOutputLine != null) { 362 audioOutputLine.close(); 363 } 364 audioFormat = new AudioFormat(audioFormat.getEncoding(), 365 audioFormat.getSampleRate() * (float) (speed * calibration), 366 audioFormat.getSampleSizeInBits(), 367 audioFormat.getChannels(), 368 audioFormat.getFrameSize(), 369 audioFormat.getFrameRate() * (float) (speed * calibration), 370 audioFormat.isBigEndian()); 371 DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat); 372 audioOutputLine = (SourceDataLine) AudioSystem.getLine(info); 373 audioOutputLine.open(audioFormat); 374 audioOutputLine.start(); 375 } 290 soundPlayer.playInterrupted(command, stateChange, playingUrl); 376 291 stateChange = State.PLAYING; 377 292 break; 378 293 case PAUSE: … … 381 296 default: // Do nothing 382 297 } 383 298 command.ok(stateChange); 384 } catch (LineUnavailableException | IOException | UnsupportedAudioFileException | 385 SecurityException | IllegalArgumentException startPlayingException) { 299 } catch (AudioException | IOException | SecurityException | IllegalArgumentException startPlayingException) { 386 300 Main.error(startPlayingException); 387 301 command.failed(startPlayingException); // sets state 388 302 } 389 } catch ( IOException e) {303 } catch (AudioException | IOException e) { 390 304 state = State.NOTPLAYING; 391 305 Main.error(e); 392 306 } … … 393 307 } 394 308 } 395 309 396 /** 397 * Shows a popup audio error message for the given exception. 398 * @param ex The exception used as error reason. Cannot be {@code null}. 399 */ 400 public static void audioMalfunction(Exception ex) { 401 String msg = ex.getMessage(); 402 if (msg == null) 403 msg = tr("unspecified reason"); 404 else 405 msg = tr(msg); 406 Main.error(msg); 407 if (!GraphicsEnvironment.isHeadless()) { 408 JOptionPane.showMessageDialog(Main.parent, 409 "<html><p>" + msg + "</p></html>", 410 tr("Error playing sound"), JOptionPane.ERROR_MESSAGE); 411 } 310 @Override 311 public void playing(URL playingURL, double position, double speed) { 312 this.playingUrl = playingURL; 313 this.position = position; 314 this.speed = speed; 412 315 } 413 316 } -
src/org/openstreetmap/josm/io/audio/AudioUtil.java
1 1 // License: GPL. For details, see LICENSE file. 2 2 package org.openstreetmap.josm.io.audio; 3 3 4 import static org.openstreetmap.josm.tools.I18n.tr; 5 6 import java.awt.GraphicsEnvironment; 4 7 import java.io.File; 5 8 import java.io.IOException; 6 9 import java.net.URL; … … 9 12 import javax.sound.sampled.AudioInputStream; 10 13 import javax.sound.sampled.AudioSystem; 11 14 import javax.sound.sampled.UnsupportedAudioFileException; 15 import javax.swing.JOptionPane; 12 16 13 17 import org.openstreetmap.josm.Main; 14 18 … … 45 49 return 0.0; 46 50 } 47 51 } 52 53 /** 54 * Shows a popup audio error message for the given exception. 55 * @param ex The exception used as error reason. Cannot be {@code null}. 56 */ 57 public static void audioMalfunction(Exception ex) { 58 String msg = ex.getMessage(); 59 if (msg == null) 60 msg = tr("unspecified reason"); 61 else 62 msg = tr(msg); 63 Main.error(msg); 64 if (!GraphicsEnvironment.isHeadless()) { 65 JOptionPane.showMessageDialog(Main.parent, 66 "<html><p>" + msg + "</p></html>", 67 tr("Error playing sound"), JOptionPane.ERROR_MESSAGE); 68 } 69 } 48 70 } -
src/org/openstreetmap/josm/io/audio/JavaFxMediaPlayer.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.io.audio; 3 4 import java.io.IOException; 5 import java.net.URL; 6 7 import org.openstreetmap.josm.io.audio.AudioPlayer.Execute; 8 import org.openstreetmap.josm.io.audio.AudioPlayer.State; 9 import org.openstreetmap.josm.tools.ListenerList; 10 11 /** 12 * Default sound player based on the Java FX Media API. 13 * Used on platforms where Java FX is available. It supports the following audio codecs:<ul> 14 * <li>MP3</li> 15 * <li>AIFF containing uncompressed PCM</li> 16 * <li>WAV containing uncompressed PCM</li> 17 * <li>MPEG-4 multimedia container with Advanced Audio Coding (AAC) audio</li> 18 * </ul> 19 * @since xxx 20 */ 21 class JavaFxMediaPlayer implements SoundPlayer { 22 23 private final ListenerList<AudioListener> listeners = ListenerList.create(); 24 25 JavaFxMediaPlayer() { 26 throw new NoClassDefFoundError("test legacy first"); 27 } 28 29 @Override 30 public void play(Execute command) throws AudioException, IOException { 31 // TODO Auto-generated method stub 32 } 33 34 @Override 35 public void playInterrupted(Execute command, State stateChange, URL playingUrl) throws AudioException, IOException { 36 // TODO Auto-generated method stub 37 } 38 39 @Override 40 public void addAudioListener(AudioListener listener) { 41 listeners.addWeakListener(listener); 42 } 43 } -
src/org/openstreetmap/josm/io/audio/JavaSoundPlayer.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.io.audio; 3 4 import static org.openstreetmap.josm.tools.I18n.tr; 5 6 import java.io.IOException; 7 import java.net.URL; 8 9 import javax.sound.sampled.AudioFormat; 10 import javax.sound.sampled.AudioInputStream; 11 import javax.sound.sampled.AudioSystem; 12 import javax.sound.sampled.DataLine; 13 import javax.sound.sampled.LineUnavailableException; 14 import javax.sound.sampled.SourceDataLine; 15 import javax.sound.sampled.UnsupportedAudioFileException; 16 17 import org.openstreetmap.josm.Main; 18 import org.openstreetmap.josm.io.audio.AudioPlayer.Execute; 19 import org.openstreetmap.josm.io.audio.AudioPlayer.State; 20 import org.openstreetmap.josm.tools.ListenerList; 21 import org.openstreetmap.josm.tools.Utils; 22 23 /** 24 * Legacy sound player based on the Java Sound API. 25 * Used on platforms where Java FX is not yet available. It supports only WAV files. 26 * @since xxx 27 */ 28 class JavaSoundPlayer implements SoundPlayer { 29 30 private static int chunk = 4000; /* bytes */ 31 32 private AudioInputStream audioInputStream; 33 private SourceDataLine audioOutputLine; 34 private AudioFormat audioFormat; 35 36 private final double leadIn; // seconds 37 private final double calibration; // ratio of purported duration of samples to true duration 38 39 private double bytesPerSecond; 40 private byte[] abData = new byte[chunk]; 41 42 private double position; // seconds 43 private double speed = 1.0; 44 45 private final ListenerList<AudioListener> listeners = ListenerList.create(); 46 47 JavaSoundPlayer(double leadIn, double calibration) { 48 this.leadIn = leadIn; 49 this.calibration = calibration; 50 } 51 52 @Override 53 public void play(Execute command) throws AudioException, IOException, InterruptedException { 54 for (;;) { 55 int nBytesRead = 0; 56 if (audioInputStream != null) { 57 nBytesRead = audioInputStream.read(abData, 0, abData.length); 58 position += nBytesRead / bytesPerSecond; 59 listeners.fireEvent(l -> l.playing(command.url(), position, speed)); 60 } 61 command.possiblyInterrupt(); 62 if (nBytesRead < 0 || audioInputStream == null || audioOutputLine == null) { 63 break; 64 } 65 audioOutputLine.write(abData, 0, nBytesRead); // => int nBytesWritten 66 command.possiblyInterrupt(); 67 } 68 // end of audio, clean up 69 if (audioOutputLine != null) { 70 audioOutputLine.drain(); 71 audioOutputLine.close(); 72 } 73 audioOutputLine = null; 74 Utils.close(audioInputStream); 75 audioInputStream = null; 76 } 77 78 @Override 79 public void playInterrupted(Execute command, State stateChange, URL playingUrl) throws AudioException, IOException { 80 final URL url = command.url(); 81 double offset = command.offset(); 82 speed = command.speed(); 83 if (playingUrl != url || 84 stateChange != State.PAUSED || 85 offset != 0) { 86 if (audioInputStream != null) { 87 Utils.close(audioInputStream); 88 } 89 listeners.fireEvent(l -> l.playing(url, position, speed)); 90 try { 91 audioInputStream = AudioSystem.getAudioInputStream(url); 92 } catch (UnsupportedAudioFileException e) { 93 throw new AudioException(e); 94 } 95 audioFormat = audioInputStream.getFormat(); 96 long nBytesRead; 97 position = 0.0; 98 listeners.fireEvent(l -> l.playing(url, position, speed)); 99 offset -= leadIn; 100 double calibratedOffset = offset * calibration; 101 bytesPerSecond = audioFormat.getFrameRate() /* frames per second */ 102 * audioFormat.getFrameSize() /* bytes per frame */; 103 if (speed * bytesPerSecond > 256_000.0) { 104 speed = 256_000 / bytesPerSecond; 105 listeners.fireEvent(l -> l.playing(url, position, speed)); 106 } 107 if (calibratedOffset > 0.0) { 108 long bytesToSkip = (long) (calibratedOffset /* seconds (double) */ * bytesPerSecond); 109 // skip doesn't seem to want to skip big chunks, so reduce it to smaller ones 110 while (bytesToSkip > chunk) { 111 nBytesRead = audioInputStream.skip(chunk); 112 if (nBytesRead <= 0) 113 throw new IOException(tr("This is after the end of the recording")); 114 bytesToSkip -= nBytesRead; 115 } 116 while (bytesToSkip > 0) { 117 long skippedBytes = audioInputStream.skip(bytesToSkip); 118 bytesToSkip -= skippedBytes; 119 if (skippedBytes == 0) { 120 // Avoid inifinite loop 121 Main.warn("Unable to skip bytes from audio input stream"); 122 bytesToSkip = 0; 123 } 124 } 125 position = offset; 126 listeners.fireEvent(l -> l.playing(url, position, speed)); 127 } 128 if (audioOutputLine != null) { 129 audioOutputLine.close(); 130 } 131 audioFormat = new AudioFormat(audioFormat.getEncoding(), 132 audioFormat.getSampleRate() * (float) (speed * calibration), 133 audioFormat.getSampleSizeInBits(), 134 audioFormat.getChannels(), 135 audioFormat.getFrameSize(), 136 audioFormat.getFrameRate() * (float) (speed * calibration), 137 audioFormat.isBigEndian()); 138 try { 139 DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat); 140 audioOutputLine = (SourceDataLine) AudioSystem.getLine(info); 141 audioOutputLine.open(audioFormat); 142 audioOutputLine.start(); 143 } catch (LineUnavailableException e) { 144 throw new AudioException(e); 145 } 146 } 147 } 148 149 @Override 150 public void addAudioListener(AudioListener listener) { 151 listeners.addWeakListener(listener); 152 } 153 } -
src/org/openstreetmap/josm/io/audio/SoundPlayer.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.io.audio; 3 4 import java.io.IOException; 5 import java.net.URL; 6 7 import org.openstreetmap.josm.io.audio.AudioPlayer.Execute; 8 import org.openstreetmap.josm.io.audio.AudioPlayer.State; 9 10 /** 11 * Sound player interface. Implementations can be backed up by Java Sound API or Java FX Media API. 12 * @since xxx 13 */ 14 interface SoundPlayer { 15 16 void play(Execute command) throws AudioException, IOException, InterruptedException; 17 18 void playInterrupted(Execute command, State stateChange, URL playingUrl) throws AudioException, IOException; 19 20 void addAudioListener(AudioListener listener); 21 }
