Spec-Zone .ru
спецификации, руководства, описания, API
|
001/* 002 * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. Oracle designates this 008 * particular file as subject to the "Classpath" exception as provided 009 * by Oracle in the LICENSE file that accompanied this code. 010 * 011 * This code is distributed in the hope that it will be useful, but WITHOUT 012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 014 * version 2 for more details (a copy is included in the LICENSE file that 015 * accompanied this code). 016 * 017 * You should have received a copy of the GNU General Public License version 018 * 2 along with this work; if not, write to the Free Software Foundation, 019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 020 * 021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 022 * or visit www.oracle.com if you need additional information or have any 023 * questions. 024 */ 025 026package javafx.scene.media; 027 028import java.net.URI; 029import java.io.IOException; 030import java.io.FileNotFoundException; 031import java.net.URISyntaxException; 032 033import javafx.beans.property.DoubleProperty; 034import javafx.beans.property.DoublePropertyBase; 035import javafx.beans.property.IntegerProperty; 036import javafx.beans.property.IntegerPropertyBase; 037 038/** 039 * An <code>AudioClip</code> represents a segment of audio that can be played 040 * with minimal latency. Clips are loaded similarly to <code>Media</code> 041 * objects but have different behavior, for example, a <code>Media</code> cannot 042 * play itself. <code>AudioClip</code>s are also usable immediately. Playback 043 * behavior is fire and forget: once one of the play methods is called the only 044 * operable control is {@link #stop()}. An <code>AudioClip</code> may also be 045 * played multiple times simultaneously. To accomplish the same task using 046 * <code>Media</code> one would have to create a new <code>MediaPlayer</code> 047 * object for each sound played in parallel. <code>Media</code> objects are 048 * however better suited for long-playing sounds. This is primarily because 049 * <code>AudioClip</code> stores in memory the raw, uncompressed audio data for 050 * the entire sound, which can be quite large for long audio clips. A 051 * <code>MediaPlayer</code> will only have enough decompressed audio data 052 * pre-rolled in memory to play for a short amount of time so it is much more 053 * memory efficient for long clips, especially if they are compressed. 054 * <br> 055 * <p>Example usage: 056 * <pre><code> 057 * AudioClip plonkSound = new AudioClip("http://somehost/path/plonk.aiff"); 058 * plonkSound.play(); 059 * </code></pre> 060 * </p> 061 */ 062 063public final class AudioClip { 064 private String sourceURL; 065 private com.sun.media.jfxmedia.AudioClip audioClip; 066 067 /** 068 * Create an <code>AudioClip</code> loaded from the supplied source URL. 069 * 070 * @param source URL string from which to load the audio clip. This can be an 071 * HTTP, file or jar source. 072 * @throws NullPointerException if the parameter is <code>null</code>. 073 * @throws IllegalArgumentException if the parameter violates 074 * <a href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a>. 075 * @throws MediaException if there is some other problem loading the media. 076 */ 077 public AudioClip(String source) { 078 URI srcURI = URI.create(source); 079 sourceURL = source; 080 try { 081 audioClip = com.sun.media.jfxmedia.AudioClip.load(srcURI); 082 } catch(URISyntaxException use) { 083 throw new IllegalArgumentException(use); 084 } catch(FileNotFoundException fnfe) { 085 throw new MediaException(MediaException.Type.MEDIA_UNAVAILABLE, fnfe.getMessage()); 086 } catch(IOException ioe) { 087 throw new MediaException(MediaException.Type.MEDIA_INACCESSIBLE, ioe.getMessage()); 088 } catch(com.sun.media.jfxmedia.MediaException me) { 089 throw new MediaException(MediaException.Type.MEDIA_UNSUPPORTED, me.getMessage()); 090 } 091 } 092 093 /** 094 * Get the source URL used to create this <code>AudioClip</code>. 095 * @return source URL as provided to the constructor 096 */ 097 public String getSource() { 098 return sourceURL; 099 } 100 101 /** 102 * The relative volume level at which the clip is played. Valid range is 0.0 103 * (muted) to 1.0 (full volume). Values are clamped to this range internally 104 * so values outside this range will have no additional effect. Volume is 105 * controlled by attenuation, so values below 1.0 will reduce the sound 106 * level accordingly. 107 */ 108 private DoubleProperty volume; 109 110 /** 111 * Set the default volume level. The new setting will only take effect on 112 * subsequent plays. 113 * @see #volume 114 * @param value new default volume level for this clip 115 */ 116 public final void setVolume(double value) { 117 volumeProperty().set(value); 118 } 119 120 /** 121 * Get the default volume level. 122 * @see #volume 123 * @return the default volume level for this clip 124 */ 125 public final double getVolume() { 126 return (null == volume) ? 1.0 : volume.get(); 127 } 128 public DoubleProperty volumeProperty() { 129 if (volume == null) { 130 volume = new DoublePropertyBase(1.0) { 131 @Override 132 protected void invalidated() { 133 if (null != audioClip) { 134 audioClip.setVolume(volume.get()); 135 } 136 } 137 138 @Override 139 public Object getBean() { 140 return AudioClip.this; 141 } 142 143 @Override 144 public String getName() { 145 return "volume"; 146 } 147 }; 148 } 149 return volume; 150 } 151 152 /** 153 * The relative left and right volume levels of the clip. 154 * Valid range is -1.0 to 1.0 where -1.0 gives full volume to the left 155 * channel while muting the right channel, 0.0 gives full volume to both 156 * channels and 1.0 gives full volume to right channel and mutes the left 157 * channel. Values outside this range are clamped internally. 158 */ 159 private DoubleProperty balance; 160 161 /** 162 * Set the default balance level. The new value will only affect subsequent 163 * plays. 164 * @see #balance 165 * @param balance new default balance 166 */ 167 public void setBalance(double balance) { 168 balanceProperty().set(balance); 169 } 170 171 /** 172 * Get the default balance level for this clip. 173 * @see #balance 174 * @return the default balance for this clip 175 */ 176 public double getBalance() { 177 return (null != balance) ? balance.get() : 0.0; 178 } 179 public DoubleProperty balanceProperty() { 180 if (null == balance) { 181 balance = new DoublePropertyBase(0.0) { 182 @Override 183 protected void invalidated() { 184 if (null != audioClip) { 185 audioClip.setBalance(balance.get()); 186 } 187 } 188 189 @Override 190 public Object getBean() { 191 return AudioClip.this; 192 } 193 194 @Override 195 public String getName() { 196 return "balance"; 197 } 198 }; 199 } 200 return balance; 201 } 202 203 /** 204 * The relative rate at which the clip is played. Valid range is 0.125 205 * (1/8 speed) to 8.0 (8x speed); values outside this range are clamped 206 * internally. Normal playback for a clip is 1.0; any other rate will affect 207 * pitch and duration accordingly. 208 */ 209 private DoubleProperty rate; 210 211 /** 212 * Set the default playback rate. The new value will only affect subsequent 213 * plays. 214 * @see #rate 215 * @param rate the new default playback rate 216 */ 217 public void setRate(double rate) { 218 rateProperty().set(rate); 219 } 220 221 /** 222 * Get the default playback rate. 223 * @see #rate 224 * @return default playback rate for this clip 225 */ 226 public double getRate() { 227 return (null != rate) ? rate.get() : 1.0; 228 } 229 public DoubleProperty rateProperty() { 230 if (null == rate) { 231 rate = new DoublePropertyBase(1.0) { 232 @Override 233 protected void invalidated() { 234 if (null != audioClip) { 235 audioClip.setPlaybackRate(rate.get()); 236 } 237 } 238 239 @Override 240 public Object getBean() { 241 return AudioClip.this; 242 } 243 244 @Override 245 public String getName() { 246 return "rate"; 247 } 248 }; 249 } 250 return rate; 251 } 252 253 /** 254 * The relative "center" of the clip. A pan value of 0.0 plays 255 * the clip normally where a -1.0 pan shifts the clip entirely to the left 256 * channel and 1.0 shifts entirely to the right channel. Unlike balance this 257 * setting mixes both channels so neither channel loses data. Setting 258 * pan on a mono clip has the same effect as setting balance, but with a 259 * much higher cost in CPU overhead so this is not recommended for mono 260 * clips. 261 */ 262 private DoubleProperty pan; 263 264 /** 265 * Set the default pan value. The new value will only affect subsequent 266 * plays. 267 * @see #pan 268 * @param pan the new default pan value 269 */ 270 public void setPan(double pan) { 271 panProperty().set(pan); 272 } 273 274 /** 275 * Get the default pan value. 276 * @see #pan 277 * @return the default pan value for this clip 278 */ 279 public double getPan() { 280 return (null != pan) ? pan.get() : 0.0; 281 } 282 public DoubleProperty panProperty() { 283 if (null == pan) { 284 pan = new DoublePropertyBase(0.0) { 285 @Override 286 protected void invalidated() { 287 if (null != audioClip) { 288 audioClip.setPan(pan.get()); 289 } 290 } 291 292 @Override 293 public Object getBean() { 294 return AudioClip.this; 295 } 296 297 @Override 298 public String getName() { 299 return "pan"; 300 } 301 }; 302 } 303 return pan; 304 } 305 306 /** 307 * The relative priority of the clip with respect to other clips. This value 308 * is used to determine which clips to remove when the maximum allowed number 309 * of clips is exceeded. The lower the priority, the more likely the 310 * clip is to be stopped and removed from the mixer channel it is occupying. 311 * Valid range is any integer; there are no constraints. The default priority 312 * is zero for all clips until changed. The number of simultaneous sounds 313 * that can be played is implementation- and possibly system-dependent. 314 */ 315 private IntegerProperty priority; 316 317 /** 318 * Set the default playback priority. The new value will only affect 319 * subsequent plays. 320 * @see #priority 321 * @param priority the new default playback priority 322 */ 323 public void setPriority(int priority) { 324 priorityProperty().set(priority); 325 } 326 327 /** 328 * Get the default playback priority. 329 * @see #priority 330 * @return the default playback priority of this clip 331 */ 332 public int getPriority() { 333 return (null != priority) ? priority.get() : 0; 334 } 335 public IntegerProperty priorityProperty() { 336 if (null == priority) { 337 priority = new IntegerPropertyBase(0) { 338 @Override 339 protected void invalidated() { 340 if (null != audioClip) { 341 audioClip.setPriority(priority.get()); 342 } 343 } 344 345 @Override 346 public Object getBean() { 347 return AudioClip.this; 348 } 349 350 @Override 351 public String getName() { 352 return "priority"; 353 } 354 }; 355 } 356 return priority; 357 } 358 359 /** 360 * When {@link #cycleCountProperty cycleCount} is set to this value, the 361 * <code>AudioClip</code> will loop continuously until stopped. This value is 362 * synonymous with {@link MediaPlayer#INDEFINITE} and 363 * {@link javafx.animation.Animation#INDEFINITE}, these values may be used 364 * interchangeably. 365 */ 366 public static final int INDEFINITE = -1; 367 368 /** 369 * The number of times the clip will be played when {@link #play()} 370 * is called. A cycleCount of 1 plays exactly once, a cycleCount of 2 371 * plays twice and so on. Valid range is 1 or more, but setting this to 372 * {@link #INDEFINITE INDEFINITE} will cause the clip to continue looping 373 * until {@link #stop} is called. 374 */ 375 private IntegerProperty cycleCount; 376 377 /** 378 * Set the default cycle count. The new value will only affect subsequent 379 * plays. 380 * @see #cycleCount 381 * @param count the new default cycle count for this clip 382 */ 383 public void setCycleCount(int count) { 384 cycleCountProperty().set(count); 385 } 386 387 /** 388 * Get the default cycle count. 389 * @see #cycleCount 390 * @return the default cycleCount for this audio clip 391 */ 392 public int getCycleCount() { 393 return (null != cycleCount) ? cycleCount.get() : 1; 394 } 395 public IntegerProperty cycleCountProperty() { 396 if (null == cycleCount) { 397 cycleCount = new IntegerPropertyBase(1) { 398 @Override 399 protected void invalidated() { 400 if (null != audioClip) { 401 int value = cycleCount.get(); 402 if (INDEFINITE != value) { 403 value = Math.max(1, value); 404 audioClip.setLoopCount(value - 1); 405 } else { 406 audioClip.setLoopCount(value); // INDEFINITE is the same there 407 } 408 } 409 } 410 411 @Override 412 public Object getBean() { 413 return AudioClip.this; 414 } 415 416 @Override 417 public String getName() { 418 return "cycleCount"; 419 } 420 }; 421 } 422 return cycleCount; 423 } 424 /** 425 * Play the <code>AudioClip</code> using all the default parameters. 426 */ 427 public void play() { 428 if (null != audioClip) { 429 audioClip.play(); 430 } 431 } 432 433 /** 434 * Play the <code>AudioClip</code> using all the default parameters except volume. 435 * This method does not modify the clip's default parameters. 436 * @param volume the volume level at which to play the clip 437 */ 438 public void play(double volume) { 439 if (null != audioClip) { 440 audioClip.play(volume); 441 } 442 } 443 444 /** 445 * Play the <code>AudioClip</code> using the given parameters. Values outside 446 * the ranges as specified by their associated properties are clamped. 447 * This method does not modify the clip's default parameters. 448 * 449 * @param volume Volume level at which to play this clip. Valid volume range is 450 * 0.0 to 1.0, where 0.0 is effectively muted and 1.0 is full volume. 451 * @param balance Left/right balance or relative channel volumes for stereo 452 * effects. 453 * @param rate Playback rate multiplier. 1.0 will play at the normal 454 * rate while 2.0 will double the rate. 455 * @param pan Left/right shift to be applied to the clip. A pan value of 456 * -1.0 means full left channel, 1.0 means full right channel, 0.0 has no 457 * effect. 458 * @param priority Audio effect priority. Lower priority effects will be 459 * dropped first if too many effects are trying to play simultaneously. 460 */ 461 public void play(double volume, double balance, double rate, double pan, int priority) { 462 if (null != audioClip) { 463 audioClip.play(volume, balance, rate, pan, audioClip.loopCount(), priority); 464 } 465 } 466 467 /** 468 * Indicate whether this <code>AudioClip</code> is playing. If this returns true 469 * then <code>play()</code> has been called at least once and it is still playing. 470 * @return true if any mixer channel has this clip queued, false otherwise 471 */ 472 public boolean isPlaying() { 473 return null != audioClip && audioClip.isPlaying(); 474 } 475 476 /** 477 * Immediately stop all playback of this <code>AudioClip</code>. 478 */ 479 public void stop() { 480 if (null != audioClip) { 481 audioClip.stop(); 482 } 483 } 484}