AudioObject.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. /**
  2. * @author alteredq / http://alteredqualia.com/
  3. *
  4. * AudioObject
  5. *
  6. * - 3d spatialized sound with Doppler-shift effect
  7. *
  8. * - uses Audio API (currently supported in WebKit-based browsers)
  9. * https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html
  10. *
  11. * - based on Doppler effect demo from Chromium
  12. * http://chromium.googlecode.com/svn/trunk/samples/audio/doppler.html
  13. *
  14. * - parameters
  15. *
  16. * - listener
  17. * dopplerFactor // A constant used to determine the amount of pitch shift to use when rendering a doppler effect.
  18. * speedOfSound // The speed of sound used for calculating doppler shift. The default value is 343.3 meters / second.
  19. *
  20. * - panner
  21. * refDistance // A reference distance for reducing volume as source move further from the listener.
  22. * maxDistance // The maximum distance between source and listener, after which the volume will not be reduced any further.
  23. * rolloffFactor // Describes how quickly the volume is reduced as source moves away from listener.
  24. * coneInnerAngle // An angle inside of which there will be no volume reduction.
  25. * coneOuterAngle // An angle outside of which the volume will be reduced to a constant value of coneOuterGain.
  26. * coneOuterGain // Amount of volume reduction outside of the coneOuterAngle.
  27. */
  28. THREE.AudioObject = function ( url, volume, playbackRate, loop ) {
  29. THREE.Object3D.call( this );
  30. if ( playbackRate === undefined ) playbackRate = 1;
  31. if ( volume === undefined ) volume = 1;
  32. if ( loop === undefined ) loop = true;
  33. if ( ! this.context ) {
  34. try {
  35. THREE.AudioObject.prototype.context = new webkitAudioContext();
  36. } catch ( error ) {
  37. console.warn( "THREE.AudioObject: webkitAudioContext not found" );
  38. return this;
  39. }
  40. }
  41. this.directionalSource = false;
  42. this.listener = this.context.listener;
  43. this.panner = this.context.createPanner();
  44. this.source = this.context.createBufferSource();
  45. this.masterGainNode = this.context.createGainNode();
  46. this.dryGainNode = this.context.createGainNode();
  47. // Setup initial gains
  48. this.masterGainNode.gain.value = volume;
  49. this.dryGainNode.gain.value = 3.0;
  50. // Connect dry mix
  51. this.source.connect( this.panner );
  52. this.panner.connect( this.dryGainNode );
  53. this.dryGainNode.connect( this.masterGainNode );
  54. // Connect master gain
  55. this.masterGainNode.connect( this.context.destination );
  56. // Set source parameters and load sound
  57. this.source.playbackRate.value = playbackRate;
  58. this.source.loop = loop;
  59. loadBufferAndPlay( url );
  60. // private properties
  61. var soundPosition = new THREE.Vector3(),
  62. cameraPosition = new THREE.Vector3(),
  63. oldSoundPosition = new THREE.Vector3(),
  64. oldCameraPosition = new THREE.Vector3(),
  65. soundDelta = new THREE.Vector3(),
  66. cameraDelta = new THREE.Vector3(),
  67. soundFront = new THREE.Vector3(),
  68. cameraFront = new THREE.Vector3(),
  69. soundUp = new THREE.Vector3(),
  70. cameraUp = new THREE.Vector3();
  71. var _this = this;
  72. // API
  73. this.setVolume = function ( volume ) {
  74. this.masterGainNode.gain.value = volume;
  75. };
  76. this.update = function ( camera ) {
  77. oldSoundPosition.copy( soundPosition );
  78. oldCameraPosition.copy( cameraPosition );
  79. soundPosition.setFromMatrixPosition( this.matrixWorld );
  80. cameraPosition.setFromMatrixPosition( camera.matrixWorld );
  81. soundDelta.subVectors( soundPosition, oldSoundPosition );
  82. cameraDelta.subVectors( cameraPosition, oldCameraPosition );
  83. cameraUp.copy( camera.up );
  84. cameraFront.set( 0, 0, - 1 );
  85. cameraFront.transformDirection( camera.matrixWorld );
  86. this.listener.setPosition( cameraPosition.x, cameraPosition.y, cameraPosition.z );
  87. this.listener.setVelocity( cameraDelta.x, cameraDelta.y, cameraDelta.z );
  88. this.listener.setOrientation( cameraFront.x, cameraFront.y, cameraFront.z, cameraUp.x, cameraUp.y, cameraUp.z );
  89. this.panner.setPosition( soundPosition.x, soundPosition.y, soundPosition.z );
  90. this.panner.setVelocity( soundDelta.x, soundDelta.y, soundDelta.z );
  91. if ( this.directionalSource ) {
  92. soundFront.set( 0, 0, - 1 );
  93. soundFront.transformDirection( this.matrixWorld );
  94. soundUp.copy( this.up );
  95. this.panner.setOrientation( soundFront.x, soundFront.y, soundFront.z, soundUp.x, soundUp.y, soundUp.z );
  96. }
  97. };
  98. function loadBufferAndPlay( url ) {
  99. // Load asynchronously
  100. var request = new XMLHttpRequest();
  101. request.open( "GET", url, true );
  102. request.responseType = "arraybuffer";
  103. request.onload = function() {
  104. _this.source.buffer = _this.context.createBuffer( request.response, true );
  105. _this.source.noteOn( 0 );
  106. };
  107. request.send();
  108. }
  109. };
  110. THREE.AudioObject.prototype = Object.create( THREE.Object3D.prototype );
  111. THREE.AudioObject.prototype.constructor = THREE.AudioObject;
  112. THREE.AudioObject.prototype.context = null;
  113. THREE.AudioObject.prototype.type = null;