summaryrefslogtreecommitdiff
path: root/src/tetris/audio/SoundPlayer.java
blob: 6e2cf8fe3932a498e75673b06e5c44960f4e6fd2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
/*
 * Audio Clip Player TCSS 305 - Autumn 2008 (Zimmerman)
 */

package tetris.audio;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.Line;
import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineListener;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;

/**
 * A class to play audio clips. Caches previously-played clips, allowing fast
 * re-playback of previously played sounds.
 * 
 * @author Marty Stepp
 * @author Daniel M. Zimmerman (code cleanup)
 * @version 1.0
 */

public class SoundPlayer {
  /**
   * A cache of previously-played audio clips.
   */

  private final Map<String, Clip> my_clips = new HashMap<String, Clip>();

  /**
   * Plays the audio file with the given file name. This method returns
   * instantly, without waiting for the clip to finish playing.
   * 
   * @param the_filename The name of the file (relative to the current working
   *          directory) to play.
   * @return a Clip object representing the sound played. (for convenience; this
   *         result need not be used/stored).
   * @exception IllegalArgumentException if there is a problem reading from the
   *              sound file.
   */

  public Clip play(final String the_filename) throws IllegalArgumentException {
    final Clip clip = getClip(the_filename);

    if (clip != null) {
      clip.start();
    }

    return clip;
  }

  /**
   * Plays the audio file with the given file name, waiting until the clip is
   * done playing before returning.
   * 
   * @param the_filename The name of the file (relative to the current working
   *          directory) to play.
   * @return a Clip object representing the sound played. (for convenience; this
   *         result need not be used/stored).
   * @exception IllegalArgumentException if there is a problem reading from the
   *              sound file.
   */

  public Clip playAndWait(final String the_filename) throws IllegalArgumentException {
    final Clip clip = getClip(the_filename);

    if (clip != null) {
      clip.start();
      try {
        synchronized (clip) {
          while (clip.isRunning()) {
            clip.wait(); // wait for clip to finish
          }
        }
      } catch (final InterruptedException ie) {
        // ignored
        return clip;
      }
    }

    return clip;
  }

  /**
   * Plays the clip with the given file name in a continuous loop. The clip
   * keeps looping until it is later stopped by calling the stop() method. This
   * function returns instantly
   * 
   * @param the_filename The name of the file (relative to the current working
   *          directory) to play.
   * @return a Clip object representing the sound played. (for convenience; this
   *         result need not be used/stored).
   * @exception IllegalArgumentException if there is a problem reading from the
   *              sound file.
   */

  public Clip loop(final String the_filename) throws IllegalArgumentException {
    return loop(the_filename, Clip.LOOP_CONTINUOUSLY);
  }

  /**
   * Plays the clip with the given file name in a loop. The clip loops until it
   * has played the specified number of times, or until it is later stopped by
   * calling the stop() method. This function returns instantly, without waiting
   * for the clip to finish looping.
   * 
   * @param the_filename The name of the file (relative to the current working
   *          directory) to play.
   * @param the_number_of_times The number of times to loop the clip.
   * @return a Clip object representing the sound played. (for convenience; this
   *         result need not be used/stored).
   * @exception IllegalArgumentException if there is a problem reading from the
   *              sound file.
   */

  public Clip loop(final String the_filename, final int the_number_of_times)
    throws IllegalArgumentException {
    final Clip clip = getClip(the_filename);

    if (clip != null) {
      clip.loop(the_number_of_times);
    }

    return clip;
  }

  /**
   * Plays the clip with the given file name in a loop. The clip loops
   * continuously until it is stopped by calling the stop() method. This method
   * waits until the clip is done looping before returning. Note that since the
   * clip loops continuously, this method will not return unless some other
   * thread stops the clip.
   * 
   * @param the_filename The name of the file (relative to the current working
   *          directory) to play.
   * @return a Clip object representing the sound played. (for convenience; this
   *         result need not be used/stored).
   * @exception IllegalArgumentException if there is a problem reading from the
   *              sound file.
   */

  public Clip loopAndWait(final String the_filename) throws IllegalArgumentException {
    return loopAndWait(the_filename, Clip.LOOP_CONTINUOUSLY);
  }

  /**
   * Plays the clip with the given file name in a loop. The clip loops until it
   * has played the specified number of times, or until it is stopped by calling
   * the stop() method. This method waits until the clip is done looping before
   * returning.
   * 
   * @param the_filename The name of the file (relative to the current working
   *          directory) to play.
   * @param the_number_of_times The number of times to loop the clip.
   * @return a Clip object representing the sound played. (for convenience; this
   *         result need not be used/stored).
   * @exception IllegalArgumentException if there is a problem reading from the
   *              sound file.
   */

  public Clip loopAndWait(final String the_filename, final int the_number_of_times)
    throws IllegalArgumentException {
    final Clip clip = getClip(the_filename);

    if (clip != null) {
      clip.loop(the_number_of_times);
      try {
        while (clip.isRunning()) {
          clip.wait(); // wait for clip to finish
        }
      } catch (final InterruptedException ie) {
        return clip;
      }
    }

    return clip;
  }

  /**
   * Pauses the clip with the given file name. If the clip is later played, it
   * will resume from where it was paused. Calling this method does not resume a
   * thread that is suspended on a playAndWait() or a loopAndWait().
   * 
   * If stop() is called on a paused clip, it will reset to the beginning of the
   * clip for the next play.
   * 
   * @param the_filename The name of the file (relative to the current working
   *          directory) to pause.
   * @exception IllegalArgumentException if there is a problem reading from or
   *              playing the sound file.
   */

  public void pause(final String the_filename) throws IllegalArgumentException {
    final Clip clip = getClip(the_filename);

    if (clip != null) {
      final int pos = clip.getFramePosition();
      clip.stop();
      clip.setFramePosition(pos);
    }
  }

  /**
   * Stops the clip with the specified filename (and wakes up any threads
   * waiting for it to finish playing).
   * 
   * @param the_filename The name of the file (relative to the current working
   *          directory) to stop.
   * @return a Clip object representing the sound stopped.
   * @exception IllegalArgumentException if there is a problem reading from the
   *              sound file.
   */

  public Clip stop(final String the_filename) throws IllegalArgumentException {
    final Clip clip = getClip(the_filename);
    stopClip(clip);

    return clip;
  }

  /**
   * Stops all currently playing sound clips (and wakes up the threads waiting
   * for them to finish playing).
   */

  public void stopAll() {
    for (Clip clip : my_clips.values()) {
      stopClip(clip);
    }
  }

  /**
   * Preloads the clip at the given file name. This means the clip will be
   * available faster, when requested for playing the first time.
   * 
   * @param the_filename The name of the file (relative to the current working
   *          directory) to preload.
   * @return a Clip object representing the preloaded sound.
   * @exception IllegalArgumentException if there is a problem reading from the
   *              sound file.
   */

  public Clip preLoad(final String the_filename) throws IllegalArgumentException {
    return getClip(the_filename);
  }

  /**
   * Returns a Clip object for a filename, either by creating a new one or
   * loading it from the cache.
   * 
   * @param the_filename The name of the file (relative to the current working
   *          directory) to load.
   * @return a Clip object, or null if one is not found.
   * @exception IllegalArgumentException if there is a problem reading from the
   *              sound file.
   */

  private Clip getClip(final String the_filename) throws IllegalArgumentException {
    Clip clip = null;
    AudioInputStream ais = null;

    if (my_clips.containsKey(the_filename)) {
      clip = my_clips.get(the_filename);
    } else {
      // read audio file from disk
      try {
        ais = AudioSystem.getAudioInputStream(ClassLoader.getSystemResource(the_filename));
        clip = (Clip) AudioSystem.getLine(new Line.Info(Clip.class));
        clip.open(ais);
        clip.addLineListener(new LineListener() {
          /**
           * Responds to audio events generated by clips.
           * 
           * @param the_event The event generated.
           */

          public void update(final LineEvent the_event) {
            if (the_event.getType() == LineEvent.Type.STOP) {
              // clip is done playing
              stopClip((Clip) the_event.getSource());
            }
          }
        });
        my_clips.put(the_filename, clip);
      } catch (final UnsupportedAudioFileException uafe) {
        throw new IllegalArgumentException("Not a valid supported audio file: \"" +
                                           the_filename + "\"");
      } catch (final LineUnavailableException lue) {
        throw new IllegalArgumentException("Line is not available to play sound \"" +
                                           the_filename + "\"");
      } catch (final IOException ioe) {
        throw new IllegalArgumentException("I/O error while reading file: \"" + the_filename +
                                           "\"");
      }
    }

    return clip;
  }

  /**
   * Stops the playing of the specified clip.
   * 
   * @param the_clip The clip.
   */

  private void stopClip(final Clip the_clip) {
    if (the_clip != null) {
      synchronized (the_clip) {
        the_clip.stop();
        the_clip.setFramePosition(0);
        the_clip.notifyAll(); // awaken threads waiting for this Clip
      }
    }
  }
}

// end of class SoundPlayer