/** * @author alteredq / http://alteredqualia.com/ * * AudioObject * * - 3d spatialized sound with Doppler-shift effect * * - uses Audio API (currently supported in WebKit-based browsers) * https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html * * - based on Doppler effect demo from Chromium * http://chromium.googlecode.com/svn/trunk/samples/audio/doppler.html * * - parameters * * - listener * dopplerFactor // A constant used to determine the amount of pitch shift to use when rendering a doppler effect. * speedOfSound // The speed of sound used for calculating doppler shift. The default value is 343.3 meters / second. * * - panner * refDistance // A reference distance for reducing volume as source move further from the listener. * maxDistance // The maximum distance between source and listener, after which the volume will not be reduced any further. * rolloffFactor // Describes how quickly the volume is reduced as source moves away from listener. * coneInnerAngle // An angle inside of which there will be no volume reduction. * coneOuterAngle // An angle outside of which the volume will be reduced to a constant value of coneOuterGain. * coneOuterGain // Amount of volume reduction outside of the coneOuterAngle. */ THREE.AudioObject = function ( url, volume, playbackRate, loop ) { THREE.Object3D.call( this ); if ( playbackRate === undefined ) playbackRate = 1; if ( volume === undefined ) volume = 1; if ( loop === undefined ) loop = true; if ( ! this.context ) { try { THREE.AudioObject.prototype.context = new webkitAudioContext(); } catch ( error ) { console.warn( "THREE.AudioObject: webkitAudioContext not found" ); return this; } } this.directionalSource = false; this.listener = this.context.listener; this.panner = this.context.createPanner(); this.source = this.context.createBufferSource(); this.masterGainNode = this.context.createGainNode(); this.dryGainNode = this.context.createGainNode(); // Setup initial gains this.masterGainNode.gain.value = volume; this.dryGainNode.gain.value = 3.0; // Connect dry mix this.source.connect( this.panner ); this.panner.connect( this.dryGainNode ); this.dryGainNode.connect( this.masterGainNode ); // Connect master gain this.masterGainNode.connect( this.context.destination ); // Set source parameters and load sound this.source.playbackRate.value = playbackRate; this.source.loop = loop; loadBufferAndPlay( url ); // private properties var soundPosition = new THREE.Vector3(), cameraPosition = new THREE.Vector3(), oldSoundPosition = new THREE.Vector3(), oldCameraPosition = new THREE.Vector3(), soundDelta = new THREE.Vector3(), cameraDelta = new THREE.Vector3(), soundFront = new THREE.Vector3(), cameraFront = new THREE.Vector3(), soundUp = new THREE.Vector3(), cameraUp = new THREE.Vector3(); var _this = this; // API this.setVolume = function ( volume ) { this.masterGainNode.gain.value = volume; }; this.update = function ( camera ) { oldSoundPosition.copy( soundPosition ); oldCameraPosition.copy( cameraPosition ); soundPosition.setFromMatrixPosition( this.matrixWorld ); cameraPosition.setFromMatrixPosition( camera.matrixWorld ); soundDelta.subVectors( soundPosition, oldSoundPosition ); cameraDelta.subVectors( cameraPosition, oldCameraPosition ); cameraUp.copy( camera.up ); cameraFront.set( 0, 0, -1 ); cameraFront.transformDirection( camera.matrixWorld ); this.listener.setPosition( cameraPosition.x, cameraPosition.y, cameraPosition.z ); this.listener.setVelocity( cameraDelta.x, cameraDelta.y, cameraDelta.z ); this.listener.setOrientation( cameraFront.x, cameraFront.y, cameraFront.z, cameraUp.x, cameraUp.y, cameraUp.z ); this.panner.setPosition( soundPosition.x, soundPosition.y, soundPosition.z ); this.panner.setVelocity( soundDelta.x, soundDelta.y, soundDelta.z ); if ( this.directionalSource ) { soundFront.set( 0, 0, -1 ); soundFront.transformDirection( this.matrixWorld ); soundUp.copy( this.up ); this.panner.setOrientation( soundFront.x, soundFront.y, soundFront.z, soundUp.x, soundUp.y, soundUp.z ); } }; function loadBufferAndPlay( url ) { // Load asynchronously var request = new XMLHttpRequest(); request.open( "GET", url, true ); request.responseType = "arraybuffer"; request.onload = function() { _this.source.buffer = _this.context.createBuffer( request.response, true ); _this.source.noteOn( 0 ); } request.send(); } }; THREE.AudioObject.prototype = Object.create( THREE.Object3D.prototype ); THREE.AudioObject.prototype.constructor = THREE.AudioObject; THREE.AudioObject.prototype.context = null; THREE.AudioObject.prototype.type = null;