source: josm/trunk/src/org/openstreetmap/josm/io/audio/JavaSoundPlayer.java@ 12413

Last change on this file since 12413 was 12413, checked in by Don-vip, 7 years ago

fix some SonarQube issues

File size: 6.3 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.io.audio;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.io.IOException;
7import java.net.URL;
8
9import javax.sound.sampled.AudioFormat;
10import javax.sound.sampled.AudioInputStream;
11import javax.sound.sampled.AudioSystem;
12import javax.sound.sampled.DataLine;
13import javax.sound.sampled.LineUnavailableException;
14import javax.sound.sampled.SourceDataLine;
15import javax.sound.sampled.UnsupportedAudioFileException;
16
17import org.openstreetmap.josm.Main;
18import org.openstreetmap.josm.io.audio.AudioPlayer.Execute;
19import org.openstreetmap.josm.io.audio.AudioPlayer.State;
20import org.openstreetmap.josm.tools.ListenerList;
21import 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 12328
27 */
28class JavaSoundPlayer implements SoundPlayer {
29
30 private static int chunk = 4000; /* bytes */
31
32 private AudioInputStream audioInputStream;
33 private SourceDataLine audioOutputLine;
34
35 private final double leadIn; // seconds
36 private final double calibration; // ratio of purported duration of samples to true duration
37
38 private double bytesPerSecond;
39 private byte[] abData = new byte[chunk];
40
41 private double position; // seconds
42 private double speed = 1.0;
43
44 private final ListenerList<AudioListener> listeners = ListenerList.create();
45
46 JavaSoundPlayer(double leadIn, double calibration) {
47 this.leadIn = leadIn;
48 this.calibration = calibration;
49 }
50
51 @Override
52 public void play(Execute command, State stateChange, URL playingUrl) throws AudioException, IOException {
53 final URL url = command.url();
54 double offset = command.offset();
55 speed = command.speed();
56 if (playingUrl != url ||
57 stateChange != State.PAUSED ||
58 offset != 0) {
59 if (audioInputStream != null) {
60 Utils.close(audioInputStream);
61 }
62 listeners.fireEvent(l -> l.playing(url));
63 try {
64 audioInputStream = AudioSystem.getAudioInputStream(url);
65 } catch (UnsupportedAudioFileException e) {
66 throw new AudioException(e);
67 }
68 AudioFormat audioFormat = audioInputStream.getFormat();
69 long nBytesRead;
70 position = 0.0;
71 offset -= leadIn;
72 double calibratedOffset = offset * calibration;
73 bytesPerSecond = audioFormat.getFrameRate() /* frames per second */
74 * audioFormat.getFrameSize() /* bytes per frame */;
75 if (speed * bytesPerSecond > 256_000.0) {
76 speed = 256_000 / bytesPerSecond;
77 }
78 if (calibratedOffset > 0.0) {
79 long bytesToSkip = (long) (calibratedOffset /* seconds (double) */ * bytesPerSecond);
80 // skip doesn't seem to want to skip big chunks, so reduce it to smaller ones
81 while (bytesToSkip > chunk) {
82 nBytesRead = audioInputStream.skip(chunk);
83 if (nBytesRead <= 0)
84 throw new IOException(tr("This is after the end of the recording"));
85 bytesToSkip -= nBytesRead;
86 }
87 while (bytesToSkip > 0) {
88 long skippedBytes = audioInputStream.skip(bytesToSkip);
89 bytesToSkip -= skippedBytes;
90 if (skippedBytes == 0) {
91 // Avoid inifinite loop
92 Main.warn("Unable to skip bytes from audio input stream");
93 bytesToSkip = 0;
94 }
95 }
96 position = offset;
97 }
98 if (audioOutputLine != null) {
99 audioOutputLine.close();
100 }
101 audioFormat = new AudioFormat(audioFormat.getEncoding(),
102 audioFormat.getSampleRate() * (float) (speed * calibration),
103 audioFormat.getSampleSizeInBits(),
104 audioFormat.getChannels(),
105 audioFormat.getFrameSize(),
106 audioFormat.getFrameRate() * (float) (speed * calibration),
107 audioFormat.isBigEndian());
108 try {
109 DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
110 audioOutputLine = (SourceDataLine) AudioSystem.getLine(info);
111 audioOutputLine.open(audioFormat);
112 audioOutputLine.start();
113 } catch (LineUnavailableException e) {
114 throw new AudioException(e);
115 }
116 }
117 }
118
119 @Override
120 public void pause(Execute command, State stateChange, URL playingUrl) throws AudioException, IOException {
121 // Do nothing. As we are very low level, the playback is paused if we stop writing to audio output line
122 }
123
124 @Override
125 public boolean playing(Execute command) throws AudioException, IOException, InterruptedException {
126 for (;;) {
127 int nBytesRead = 0;
128 if (audioInputStream != null) {
129 nBytesRead = audioInputStream.read(abData, 0, abData.length);
130 position += nBytesRead / bytesPerSecond;
131 }
132 command.possiblyInterrupt();
133 if (nBytesRead < 0 || audioInputStream == null || audioOutputLine == null) {
134 break;
135 }
136 audioOutputLine.write(abData, 0, nBytesRead); // => int nBytesWritten
137 command.possiblyInterrupt();
138 }
139 // end of audio, clean up
140 if (audioOutputLine != null) {
141 audioOutputLine.drain();
142 audioOutputLine.close();
143 }
144 audioOutputLine = null;
145 Utils.close(audioInputStream);
146 audioInputStream = null;
147 speed = 0;
148 return true;
149 }
150
151 @Override
152 public double position() {
153 return position;
154 }
155
156 @Override
157 public double speed() {
158 return speed;
159 }
160
161 @Override
162 public void addAudioListener(AudioListener listener) {
163 listeners.addWeakListener(listener);
164 }
165}
Note: See TracBrowser for help on using the repository browser.