AudioNode.java 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754
  1. /*
  2. * Copyright (c) 2009-2010 jMonkeyEngine
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are
  7. * met:
  8. *
  9. * * Redistributions of source code must retain the above copyright
  10. * notice, this list of conditions and the following disclaimer.
  11. *
  12. * * Redistributions in binary form must reproduce the above copyright
  13. * notice, this list of conditions and the following disclaimer in the
  14. * documentation and/or other materials provided with the distribution.
  15. *
  16. * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
  17. * may be used to endorse or promote products derived from this software
  18. * without specific prior written permission.
  19. *
  20. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
  22. * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  23. * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  24. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  25. * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  26. * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  27. * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  28. * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  29. * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  30. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31. */
  32. package com.jme3.audio;
  33. import com.jme3.asset.AssetManager;
  34. import com.jme3.asset.AssetNotFoundException;
  35. import com.jme3.export.InputCapsule;
  36. import com.jme3.export.JmeExporter;
  37. import com.jme3.export.JmeImporter;
  38. import com.jme3.export.OutputCapsule;
  39. import com.jme3.math.Vector3f;
  40. import com.jme3.scene.Node;
  41. import com.jme3.util.PlaceholderAssets;
  42. import java.io.IOException;
  43. import java.util.logging.Level;
  44. import java.util.logging.Logger;
  45. /**
  46. * An <code>AudioNode</code> is used in jME3 for playing audio files.
  47. * <br/>
  48. * First, an {@link AudioNode} is loaded from file, and then assigned
  49. * to an audio node for playback. Once the audio node is attached to the
  50. * scene, its location will influence the position it is playing from relative
  51. * to the {@link Listener}.
  52. * <br/>
  53. * An audio node can also play in "headspace", meaning its location
  54. * or velocity does not influence how it is played.
  55. * The "positional" property of an AudioNode can be set via
  56. * {@link AudioNode#setPositional(boolean) }.
  57. *
  58. * @author normenhansen
  59. * @author Kirill Vainer
  60. */
  61. public class AudioNode extends Node {
  62. protected boolean loop = false;
  63. protected float volume = 1;
  64. protected float pitch = 1;
  65. protected float timeOffset = 0;
  66. protected Filter dryFilter;
  67. protected AudioKey audioKey;
  68. protected transient AudioData data = null;
  69. protected transient volatile Status status = Status.Stopped;
  70. protected transient volatile int channel = -1;
  71. protected Vector3f velocity = new Vector3f();
  72. protected boolean reverbEnabled = true;
  73. protected float maxDistance = 200; // 200 meters
  74. protected float refDistance = 10; // 10 meters
  75. protected Filter reverbFilter;
  76. private boolean directional = false;
  77. protected Vector3f direction = new Vector3f(0, 0, 1);
  78. protected float innerAngle = 360;
  79. protected float outerAngle = 360;
  80. protected boolean positional = true;
  81. /**
  82. * <code>Status</code> indicates the current status of the audio node.
  83. */
  84. public enum Status {
  85. /**
  86. * The audio node is currently playing. This will be set if
  87. * {@link AudioNode#play() } is called.
  88. */
  89. Playing,
  90. /**
  91. * The audio node is currently paused.
  92. */
  93. Paused,
  94. /**
  95. * The audio node is currently stopped.
  96. * This will be set if {@link AudioNode#stop() } is called
  97. * or the audio has reached the end of the file.
  98. */
  99. Stopped,
  100. }
  101. /**
  102. * Creates a new <code>AudioNode</code> without any audio data set.
  103. */
  104. public AudioNode() {
  105. }
  106. /**
  107. * Creates a new <code>AudioNode</code> with the given data and key.
  108. *
  109. * @param audioData The audio data contains the audio track to play.
  110. * @param audioKey The audio key that was used to load the AudioData
  111. */
  112. public AudioNode(AudioData audioData, AudioKey audioKey) {
  113. setAudioData(audioData, audioKey);
  114. }
  115. /**
  116. * Creates a new <code>AudioNode</code> with the given audio file.
  117. *
  118. * @param assetManager The asset manager to use to load the audio file
  119. * @param name The filename of the audio file
  120. * @param stream If true, the audio will be streamed gradually from disk,
  121. * otherwise, it will be buffered.
  122. * @param streamCache If stream is also true, then this specifies if
  123. * the stream cache is used. When enabled, the audio stream will
  124. * be read entirely but not decoded, allowing features such as
  125. * seeking, looping and determining duration.
  126. */
  127. public AudioNode(AssetManager assetManager, String name, boolean stream, boolean streamCache) {
  128. this.audioKey = new AudioKey(name, stream, streamCache);
  129. this.data = (AudioData) assetManager.loadAsset(audioKey);
  130. }
  131. /**
  132. * Creates a new <code>AudioNode</code> with the given audio file.
  133. *
  134. * @param assetManager The asset manager to use to load the audio file
  135. * @param name The filename of the audio file
  136. * @param stream If true, the audio will be streamed gradually from disk,
  137. * otherwise, it will be buffered.
  138. */
  139. public AudioNode(AssetManager assetManager, String name, boolean stream) {
  140. this(assetManager, name, stream, false);
  141. }
  142. /**
  143. * Creates a new <code>AudioNode</code> with the given audio file.
  144. *
  145. * @param audioRenderer The audio renderer to use for playing. Cannot be null.
  146. * @param assetManager The asset manager to use to load the audio file
  147. * @param name The filename of the audio file
  148. *
  149. * @deprecated AudioRenderer parameter is ignored.
  150. */
  151. public AudioNode(AudioRenderer audioRenderer, AssetManager assetManager, String name) {
  152. this(assetManager, name, false);
  153. }
  154. /**
  155. * Creates a new <code>AudioNode</code> with the given audio file.
  156. *
  157. * @param assetManager The asset manager to use to load the audio file
  158. * @param name The filename of the audio file
  159. */
  160. public AudioNode(AssetManager assetManager, String name) {
  161. this(assetManager, name, false);
  162. }
  163. protected AudioRenderer getRenderer() {
  164. AudioRenderer result = AudioContext.getAudioRenderer();
  165. if( result == null )
  166. throw new IllegalStateException( "No audio renderer available, make sure call is being performed on render thread." );
  167. return result;
  168. }
  169. /**
  170. * Start playing the audio.
  171. */
  172. public void play(){
  173. getRenderer().playSource(this);
  174. }
  175. /**
  176. * Start playing an instance of this audio. This method can be used
  177. * to play the same <code>AudioNode</code> multiple times. Note
  178. * that changes to the parameters of this AudioNode will not effect the
  179. * instances already playing.
  180. */
  181. public void playInstance(){
  182. getRenderer().playSourceInstance(this);
  183. }
  184. /**
  185. * Stop playing the audio that was started with {@link AudioNode#play() }.
  186. */
  187. public void stop(){
  188. getRenderer().stopSource(this);
  189. }
  190. /**
  191. * Pause the audio that was started with {@link AudioNode#play() }.
  192. */
  193. public void pause(){
  194. getRenderer().pauseSource(this);
  195. }
  196. /**
  197. * Do not use.
  198. */
  199. public final void setChannel(int channel) {
  200. if (status != Status.Stopped) {
  201. throw new IllegalStateException("Can only set source id when stopped");
  202. }
  203. this.channel = channel;
  204. }
  205. /**
  206. * Do not use.
  207. */
  208. public int getChannel() {
  209. return channel;
  210. }
  211. /**
  212. * @return The {#link Filter dry filter} that is set.
  213. * @see AudioNode#setDryFilter(com.jme3.audio.Filter)
  214. */
  215. public Filter getDryFilter() {
  216. return dryFilter;
  217. }
  218. /**
  219. * Set the dry filter to use for this audio node.
  220. *
  221. * When {@link AudioNode#setReverbEnabled(boolean) reverb} is used,
  222. * the dry filter will only influence the "dry" portion of the audio,
  223. * e.g. not the reverberated parts of the AudioNode playing.
  224. *
  225. * See the relevent documentation for the {@link Filter} to determine
  226. * the effect.
  227. *
  228. * @param dryFilter The filter to set, or null to disable dry filter.
  229. */
  230. public void setDryFilter(Filter dryFilter) {
  231. this.dryFilter = dryFilter;
  232. if (channel >= 0)
  233. getRenderer().updateSourceParam(this, AudioParam.DryFilter);
  234. }
  235. /**
  236. * Set the audio data to use for the audio. Note that this method
  237. * can only be called once, if for example the audio node was initialized
  238. * without an {@link AudioData}.
  239. *
  240. * @param audioData The audio data contains the audio track to play.
  241. * @param audioKey The audio key that was used to load the AudioData
  242. */
  243. public void setAudioData(AudioData audioData, AudioKey audioKey) {
  244. if (data != null) {
  245. throw new IllegalStateException("Cannot change data once its set");
  246. }
  247. data = audioData;
  248. this.audioKey = audioKey;
  249. }
  250. /**
  251. * @return The {@link AudioData} set previously with
  252. * {@link AudioNode#setAudioData(com.jme3.audio.AudioData, com.jme3.audio.AudioKey) }
  253. * or any of the constructors that initialize the audio data.
  254. */
  255. public AudioData getAudioData() {
  256. return data;
  257. }
  258. /**
  259. * @return The {@link Status} of the audio node.
  260. * The status will be changed when either the {@link AudioNode#play() }
  261. * or {@link AudioNode#stop() } methods are called.
  262. */
  263. public Status getStatus() {
  264. return status;
  265. }
  266. /**
  267. * Do not use.
  268. */
  269. public final void setStatus(Status status) {
  270. this.status = status;
  271. }
  272. /**
  273. * @return True if the audio will keep looping after it is done playing,
  274. * otherwise, false.
  275. * @see AudioNode#setLooping(boolean)
  276. */
  277. public boolean isLooping() {
  278. return loop;
  279. }
  280. /**
  281. * Set the looping mode for the audio node. The default is false.
  282. *
  283. * @param loop True if the audio should keep looping after it is done playing.
  284. */
  285. public void setLooping(boolean loop) {
  286. this.loop = loop;
  287. if (channel >= 0)
  288. getRenderer().updateSourceParam(this, AudioParam.Looping);
  289. }
  290. /**
  291. * @return The pitch of the audio, also the speed of playback.
  292. *
  293. * @see AudioNode#setPitch(float)
  294. */
  295. public float getPitch() {
  296. return pitch;
  297. }
  298. /**
  299. * Set the pitch of the audio, also the speed of playback.
  300. * The value must be between 0.5 and 2.0.
  301. *
  302. * @param pitch The pitch to set.
  303. * @throws IllegalArgumentException If pitch is not between 0.5 and 2.0.
  304. */
  305. public void setPitch(float pitch) {
  306. if (pitch < 0.5f || pitch > 2.0f) {
  307. throw new IllegalArgumentException("Pitch must be between 0.5 and 2.0");
  308. }
  309. this.pitch = pitch;
  310. if (channel >= 0)
  311. getRenderer().updateSourceParam(this, AudioParam.Pitch);
  312. }
  313. /**
  314. * @return The volume of this audio node.
  315. *
  316. * @see AudioNode#setVolume(float)
  317. */
  318. public float getVolume() {
  319. return volume;
  320. }
  321. /**
  322. * Set the volume of this audio node.
  323. *
  324. * The volume is specified as gain. 1.0 is the default.
  325. *
  326. * @param volume The volume to set.
  327. * @throws IllegalArgumentException If volume is negative
  328. */
  329. public void setVolume(float volume) {
  330. if (volume < 0f) {
  331. throw new IllegalArgumentException("Volume cannot be negative");
  332. }
  333. this.volume = volume;
  334. if (channel >= 0)
  335. getRenderer().updateSourceParam(this, AudioParam.Volume);
  336. }
  337. /**
  338. * @return the time offset in the sound sample when to start playing.
  339. */
  340. public float getTimeOffset() {
  341. return timeOffset;
  342. }
  343. /**
  344. * Set the time offset in the sound sample when to start playing.
  345. *
  346. * @param timeOffset The time offset
  347. * @throws IllegalArgumentException If timeOffset is negative
  348. */
  349. public void setTimeOffset(float timeOffset) {
  350. if (timeOffset < 0f) {
  351. throw new IllegalArgumentException("Time offset cannot be negative");
  352. }
  353. this.timeOffset = timeOffset;
  354. if (data instanceof AudioStream) {
  355. System.out.println("request setTime");
  356. ((AudioStream) data).setTime(timeOffset);
  357. }else if(status == Status.Playing){
  358. stop();
  359. play();
  360. }
  361. }
  362. /**
  363. * @return The velocity of the audio node.
  364. *
  365. * @see AudioNode#setVelocity(com.jme3.math.Vector3f)
  366. */
  367. public Vector3f getVelocity() {
  368. return velocity;
  369. }
  370. /**
  371. * Set the velocity of the audio node. The velocity is expected
  372. * to be in meters. Does nothing if the audio node is not positional.
  373. *
  374. * @param velocity The velocity to set.
  375. * @see AudioNode#setPositional(boolean)
  376. */
  377. public void setVelocity(Vector3f velocity) {
  378. this.velocity.set(velocity);
  379. if (channel >= 0)
  380. getRenderer().updateSourceParam(this, AudioParam.Velocity);
  381. }
  382. /**
  383. * @return True if reverb is enabled, otherwise false.
  384. *
  385. * @see AudioNode#setReverbEnabled(boolean)
  386. */
  387. public boolean isReverbEnabled() {
  388. return reverbEnabled;
  389. }
  390. /**
  391. * Set to true to enable reverberation effects for this audio node.
  392. * Does nothing if the audio node is not positional.
  393. * <br/>
  394. * When enabled, the audio environment set with
  395. * {@link AudioRenderer#setEnvironment(com.jme3.audio.Environment) }
  396. * will apply a reverb effect to the audio playing from this audio node.
  397. *
  398. * @param reverbEnabled True to enable reverb.
  399. */
  400. public void setReverbEnabled(boolean reverbEnabled) {
  401. this.reverbEnabled = reverbEnabled;
  402. if (channel >= 0) {
  403. getRenderer().updateSourceParam(this, AudioParam.ReverbEnabled);
  404. }
  405. }
  406. /**
  407. * @return Filter for the reverberations of this audio node.
  408. *
  409. * @see AudioNode#setReverbFilter(com.jme3.audio.Filter)
  410. */
  411. public Filter getReverbFilter() {
  412. return reverbFilter;
  413. }
  414. /**
  415. * Set the reverb filter for this audio node.
  416. * <br/>
  417. * The reverb filter will influence the reverberations
  418. * of the audio node playing. This only has an effect if
  419. * reverb is enabled.
  420. *
  421. * @param reverbFilter The reverb filter to set.
  422. * @see AudioNode#setDryFilter(com.jme3.audio.Filter)
  423. */
  424. public void setReverbFilter(Filter reverbFilter) {
  425. this.reverbFilter = reverbFilter;
  426. if (channel >= 0)
  427. getRenderer().updateSourceParam(this, AudioParam.ReverbFilter);
  428. }
  429. /**
  430. * @return Max distance for this audio node.
  431. *
  432. * @see AudioNode#setMaxDistance(float)
  433. */
  434. public float getMaxDistance() {
  435. return maxDistance;
  436. }
  437. /**
  438. * Set the maximum distance for the attenuation of the audio node.
  439. * Does nothing if the audio node is not positional.
  440. * <br/>
  441. * The maximum distance is the distance beyond which the audio
  442. * node will no longer be attenuated. Normal attenuation is logarithmic
  443. * from refDistance (it reduces by half when the distance doubles).
  444. * Max distance sets where this fall-off stops and the sound will never
  445. * get any quieter than at that distance. If you want a sound to fall-off
  446. * very quickly then set ref distance very short and leave this distance
  447. * very long.
  448. *
  449. * @param maxDistance The maximum playing distance.
  450. * @throws IllegalArgumentException If maxDistance is negative
  451. */
  452. public void setMaxDistance(float maxDistance) {
  453. if (maxDistance < 0) {
  454. throw new IllegalArgumentException("Max distance cannot be negative");
  455. }
  456. this.maxDistance = maxDistance;
  457. if (channel >= 0)
  458. getRenderer().updateSourceParam(this, AudioParam.MaxDistance);
  459. }
  460. /**
  461. * @return The reference playing distance for the audio node.
  462. *
  463. * @see AudioNode#setRefDistance(float)
  464. */
  465. public float getRefDistance() {
  466. return refDistance;
  467. }
  468. /**
  469. * Set the reference playing distance for the audio node.
  470. * Does nothing if the audio node is not positional.
  471. * <br/>
  472. * The reference playing distance is the distance at which the
  473. * audio node will be exactly half of its volume.
  474. *
  475. * @param refDistance The reference playing distance.
  476. * @throws IllegalArgumentException If refDistance is negative
  477. */
  478. public void setRefDistance(float refDistance) {
  479. if (refDistance < 0) {
  480. throw new IllegalArgumentException("Reference distance cannot be negative");
  481. }
  482. this.refDistance = refDistance;
  483. if (channel >= 0)
  484. getRenderer().updateSourceParam(this, AudioParam.RefDistance);
  485. }
  486. /**
  487. * @return True if the audio node is directional
  488. *
  489. * @see AudioNode#setDirectional(boolean)
  490. */
  491. public boolean isDirectional() {
  492. return directional;
  493. }
  494. /**
  495. * Set the audio node to be directional.
  496. * Does nothing if the audio node is not positional.
  497. * <br/>
  498. * After setting directional, you should call
  499. * {@link AudioNode#setDirection(com.jme3.math.Vector3f) }
  500. * to set the audio node's direction.
  501. *
  502. * @param directional If the audio node is directional
  503. */
  504. public void setDirectional(boolean directional) {
  505. this.directional = directional;
  506. if (channel >= 0)
  507. getRenderer().updateSourceParam(this, AudioParam.IsDirectional);
  508. }
  509. /**
  510. * @return The direction of this audio node.
  511. *
  512. * @see AudioNode#setDirection(com.jme3.math.Vector3f)
  513. */
  514. public Vector3f getDirection() {
  515. return direction;
  516. }
  517. /**
  518. * Set the direction of this audio node.
  519. * Does nothing if the audio node is not directional.
  520. *
  521. * @param direction
  522. * @see AudioNode#setDirectional(boolean)
  523. */
  524. public void setDirection(Vector3f direction) {
  525. this.direction = direction;
  526. if (channel >= 0)
  527. getRenderer().updateSourceParam(this, AudioParam.Direction);
  528. }
  529. /**
  530. * @return The directional audio node, cone inner angle.
  531. *
  532. * @see AudioNode#setInnerAngle(float)
  533. */
  534. public float getInnerAngle() {
  535. return innerAngle;
  536. }
  537. /**
  538. * Set the directional audio node cone inner angle.
  539. * Does nothing if the audio node is not directional.
  540. *
  541. * @param innerAngle The cone inner angle.
  542. */
  543. public void setInnerAngle(float innerAngle) {
  544. this.innerAngle = innerAngle;
  545. if (channel >= 0)
  546. getRenderer().updateSourceParam(this, AudioParam.InnerAngle);
  547. }
  548. /**
  549. * @return The directional audio node, cone outer angle.
  550. *
  551. * @see AudioNode#setOuterAngle(float)
  552. */
  553. public float getOuterAngle() {
  554. return outerAngle;
  555. }
  556. /**
  557. * Set the directional audio node cone outer angle.
  558. * Does nothing if the audio node is not directional.
  559. *
  560. * @param outerAngle The cone outer angle.
  561. */
  562. public void setOuterAngle(float outerAngle) {
  563. this.outerAngle = outerAngle;
  564. if (channel >= 0)
  565. getRenderer().updateSourceParam(this, AudioParam.OuterAngle);
  566. }
  567. /**
  568. * @return True if the audio node is positional.
  569. *
  570. * @see AudioNode#setPositional(boolean)
  571. */
  572. public boolean isPositional() {
  573. return positional;
  574. }
  575. /**
  576. * Set the audio node as positional.
  577. * The position, velocity, and distance parameters effect positional
  578. * audio nodes. Set to false if the audio node should play in "headspace".
  579. *
  580. * @param positional True if the audio node should be positional, otherwise
  581. * false if it should be headspace.
  582. */
  583. public void setPositional(boolean positional) {
  584. this.positional = positional;
  585. if (channel >= 0) {
  586. getRenderer().updateSourceParam(this, AudioParam.IsPositional);
  587. }
  588. }
  589. @Override
  590. public void updateGeometricState(){
  591. boolean updatePos = false;
  592. if ((refreshFlags & RF_TRANSFORM) != 0){
  593. updatePos = true;
  594. }
  595. super.updateGeometricState();
  596. if (updatePos && channel >= 0)
  597. getRenderer().updateSourceParam(this, AudioParam.Position);
  598. }
  599. @Override
  600. public AudioNode clone(){
  601. AudioNode clone = (AudioNode) super.clone();
  602. clone.direction = direction.clone();
  603. clone.velocity = velocity.clone();
  604. return clone;
  605. }
  606. @Override
  607. public void write(JmeExporter ex) throws IOException {
  608. super.write(ex);
  609. OutputCapsule oc = ex.getCapsule(this);
  610. oc.write(audioKey, "audio_key", null);
  611. oc.write(loop, "looping", false);
  612. oc.write(volume, "volume", 1);
  613. oc.write(pitch, "pitch", 1);
  614. oc.write(timeOffset, "time_offset", 0);
  615. oc.write(dryFilter, "dry_filter", null);
  616. oc.write(velocity, "velocity", null);
  617. oc.write(reverbEnabled, "reverb_enabled", false);
  618. oc.write(reverbFilter, "reverb_filter", null);
  619. oc.write(maxDistance, "max_distance", 20);
  620. oc.write(refDistance, "ref_distance", 10);
  621. oc.write(directional, "directional", false);
  622. oc.write(direction, "direction", null);
  623. oc.write(innerAngle, "inner_angle", 360);
  624. oc.write(outerAngle, "outer_angle", 360);
  625. oc.write(positional, "positional", false);
  626. }
  627. @Override
  628. public void read(JmeImporter im) throws IOException {
  629. super.read(im);
  630. InputCapsule ic = im.getCapsule(this);
  631. // NOTE: In previous versions of jME3, audioKey was actually
  632. // written with the name "key". This has been changed
  633. // to "audio_key" in case Spatial's key will be written as "key".
  634. if (ic.getSavableVersion(AudioNode.class) == 0){
  635. audioKey = (AudioKey) ic.readSavable("key", null);
  636. }else{
  637. audioKey = (AudioKey) ic.readSavable("audio_key", null);
  638. }
  639. loop = ic.readBoolean("looping", false);
  640. volume = ic.readFloat("volume", 1);
  641. pitch = ic.readFloat("pitch", 1);
  642. timeOffset = ic.readFloat("time_offset", 0);
  643. dryFilter = (Filter) ic.readSavable("dry_filter", null);
  644. velocity = (Vector3f) ic.readSavable("velocity", null);
  645. reverbEnabled = ic.readBoolean("reverb_enabled", false);
  646. reverbFilter = (Filter) ic.readSavable("reverb_filter", null);
  647. maxDistance = ic.readFloat("max_distance", 20);
  648. refDistance = ic.readFloat("ref_distance", 10);
  649. directional = ic.readBoolean("directional", false);
  650. direction = (Vector3f) ic.readSavable("direction", null);
  651. innerAngle = ic.readFloat("inner_angle", 360);
  652. outerAngle = ic.readFloat("outer_angle", 360);
  653. positional = ic.readBoolean("positional", false);
  654. if (audioKey != null) {
  655. try {
  656. data = im.getAssetManager().loadAsset(audioKey);
  657. } catch (AssetNotFoundException ex){
  658. Logger.getLogger(AudioNode.class.getName()).log(Level.FINE, "Cannot locate {0} for audio node {1}", new Object[]{audioKey, key});
  659. data = PlaceholderAssets.getPlaceholderAudio();
  660. }
  661. }
  662. }
  663. @Override
  664. public String toString() {
  665. String ret = getClass().getSimpleName()
  666. + "[status=" + status;
  667. if (volume != 1f) {
  668. ret += ", vol=" + volume;
  669. }
  670. if (pitch != 1f) {
  671. ret += ", pitch=" + pitch;
  672. }
  673. return ret + "]";
  674. }
  675. }