PositionalAudio.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. import { Vector3 } from '../math/Vector3.js';
  2. import { Quaternion } from '../math/Quaternion.js';
  3. import { Audio } from './Audio.js';
  4. const _position = /*@__PURE__*/ new Vector3();
  5. const _quaternion = /*@__PURE__*/ new Quaternion();
  6. const _scale = /*@__PURE__*/ new Vector3();
  7. const _orientation = /*@__PURE__*/ new Vector3();
  8. /**
  9. * Represents a positional audio object.
  10. *
  11. * ```js
  12. * // create an AudioListener and add it to the camera
  13. * const listener = new THREE.AudioListener();
  14. * camera.add( listener );
  15. *
  16. * // create the PositionalAudio object (passing in the listener)
  17. * const sound = new THREE.PositionalAudio( listener );
  18. *
  19. * // load a sound and set it as the PositionalAudio object's buffer
  20. * const audioLoader = new THREE.AudioLoader();
  21. * audioLoader.load( 'sounds/song.ogg', function( buffer ) {
  22. * sound.setBuffer( buffer );
  23. * sound.setRefDistance( 20 );
  24. * sound.play();
  25. * });
  26. *
  27. * // create an object for the sound to play from
  28. * const sphere = new THREE.SphereGeometry( 20, 32, 16 );
  29. * const material = new THREE.MeshPhongMaterial( { color: 0xff2200 } );
  30. * const mesh = new THREE.Mesh( sphere, material );
  31. * scene.add( mesh );
  32. *
  33. * // finally add the sound to the mesh
  34. * mesh.add( sound );
  35. *
  36. * @augments Audio
  37. */
  38. class PositionalAudio extends Audio {
  39. /**
  40. * Constructs a positional audio.
  41. *
  42. * @param {AudioListener} listener - The global audio listener.
  43. */
  44. constructor( listener ) {
  45. super( listener );
  46. /**
  47. * The panner node represents the location, direction, and behavior of an audio
  48. * source in 3D space.
  49. *
  50. * @type {PannerNode}
  51. * @readonly
  52. */
  53. this.panner = this.context.createPanner();
  54. this.panner.panningModel = 'HRTF';
  55. this.panner.connect( this.gain );
  56. }
  57. connect() {
  58. super.connect();
  59. this.panner.connect( this.gain );
  60. return this;
  61. }
  62. disconnect() {
  63. super.disconnect();
  64. this.panner.disconnect( this.gain );
  65. return this;
  66. }
  67. getOutput() {
  68. return this.panner;
  69. }
  70. /**
  71. * Returns the current reference distance.
  72. *
  73. * @return {number} The reference distance.
  74. */
  75. getRefDistance() {
  76. return this.panner.refDistance;
  77. }
  78. /**
  79. * Defines the reference distance for reducing volume as the audio source moves
  80. * further from the listener – i.e. the distance at which the volume reduction
  81. * starts taking effect.
  82. *
  83. * @param {number} value - The reference distance to set.
  84. * @return {PositionalAudio} A reference to this instance.
  85. */
  86. setRefDistance( value ) {
  87. this.panner.refDistance = value;
  88. return this;
  89. }
  90. /**
  91. * Returns the current rolloff factor.
  92. *
  93. * @return {number} The rolloff factor.
  94. */
  95. getRolloffFactor() {
  96. return this.panner.rolloffFactor;
  97. }
  98. /**
  99. * Defines how quickly the volume is reduced as the source moves away from the listener.
  100. *
  101. * @param {number} value - The rolloff factor.
  102. * @return {PositionalAudio} A reference to this instance.
  103. */
  104. setRolloffFactor( value ) {
  105. this.panner.rolloffFactor = value;
  106. return this;
  107. }
  108. /**
  109. * Returns the current distance model.
  110. *
  111. * @return {('linear'|'inverse'|'exponential')} The distance model.
  112. */
  113. getDistanceModel() {
  114. return this.panner.distanceModel;
  115. }
  116. /**
  117. * Defines which algorithm to use to reduce the volume of the audio source
  118. * as it moves away from the listener.
  119. *
  120. * Read [the spec]{@link https://www.w3.org/TR/webaudio-1.1/#enumdef-distancemodeltype}
  121. * for more details.
  122. *
  123. * @param {('linear'|'inverse'|'exponential')} value - The distance model to set.
  124. * @return {PositionalAudio} A reference to this instance.
  125. */
  126. setDistanceModel( value ) {
  127. this.panner.distanceModel = value;
  128. return this;
  129. }
  130. /**
  131. * Returns the current max distance.
  132. *
  133. * @return {number} The max distance.
  134. */
  135. getMaxDistance() {
  136. return this.panner.maxDistance;
  137. }
  138. /**
  139. * Defines the maximum distance between the audio source and the listener,
  140. * after which the volume is not reduced any further.
  141. *
  142. * This value is used only by the `linear` distance model.
  143. *
  144. * @param {number} value - The max distance.
  145. * @return {PositionalAudio} A reference to this instance.
  146. */
  147. setMaxDistance( value ) {
  148. this.panner.maxDistance = value;
  149. return this;
  150. }
  151. /**
  152. * Sets the directional cone in which the audio can be listened.
  153. *
  154. * @param {number} coneInnerAngle - An angle, in degrees, of a cone inside of which there will be no volume reduction.
  155. * @param {number} coneOuterAngle - An angle, in degrees, of a cone outside of which the volume will be reduced by a constant value, defined by the `coneOuterGain` parameter.
  156. * @param {number} coneOuterGain - The amount of volume reduction outside the cone defined by the `coneOuterAngle`. When set to `0`, no sound can be heard.
  157. * @return {PositionalAudio} A reference to this instance.
  158. */
  159. setDirectionalCone( coneInnerAngle, coneOuterAngle, coneOuterGain ) {
  160. this.panner.coneInnerAngle = coneInnerAngle;
  161. this.panner.coneOuterAngle = coneOuterAngle;
  162. this.panner.coneOuterGain = coneOuterGain;
  163. return this;
  164. }
  165. updateMatrixWorld( force ) {
  166. super.updateMatrixWorld( force );
  167. if ( this.hasPlaybackControl === true && this.isPlaying === false ) return;
  168. this.matrixWorld.decompose( _position, _quaternion, _scale );
  169. _orientation.set( 0, 0, 1 ).applyQuaternion( _quaternion );
  170. const panner = this.panner;
  171. if ( panner.positionX ) {
  172. // code path for Chrome and Firefox (see #14393)
  173. const endTime = this.context.currentTime + this.listener.timeDelta;
  174. panner.positionX.linearRampToValueAtTime( _position.x, endTime );
  175. panner.positionY.linearRampToValueAtTime( _position.y, endTime );
  176. panner.positionZ.linearRampToValueAtTime( _position.z, endTime );
  177. panner.orientationX.linearRampToValueAtTime( _orientation.x, endTime );
  178. panner.orientationY.linearRampToValueAtTime( _orientation.y, endTime );
  179. panner.orientationZ.linearRampToValueAtTime( _orientation.z, endTime );
  180. } else {
  181. panner.setPosition( _position.x, _position.y, _position.z );
  182. panner.setOrientation( _orientation.x, _orientation.y, _orientation.z );
  183. }
  184. }
  185. }
  186. export { PositionalAudio };
粤ICP备19079148号