Update framework

This commit is contained in:
2022-11-26 02:22:02 +01:00
parent 9a6ce1f832
commit ae057940af
508 changed files with 26011 additions and 14248 deletions

View File

@@ -0,0 +1,40 @@
export default Attenuation;
/**
* @class Attenuation
* @description Distance-based attenuation filter.
* @param {AudioContext} context
* Associated {@link
https://developer.mozilla.org/en-US/docs/Web/API/AudioContext AudioContext}.
* @param {Object} options
* @param {Number} options.minDistance
* Min. distance (in meters). Defaults to
* {@linkcode Utils.DEFAULT_MIN_DISTANCE DEFAULT_MIN_DISTANCE}.
* @param {Number} options.maxDistance
* Max. distance (in meters). Defaults to
* {@linkcode Utils.DEFAULT_MAX_DISTANCE DEFAULT_MAX_DISTANCE}.
* @param {string} options.rolloff
* Rolloff model to use, chosen from options in
* {@linkcode Utils.ATTENUATION_ROLLOFFS ATTENUATION_ROLLOFFS}. Defaults to
* {@linkcode Utils.DEFAULT_ATTENUATION_ROLLOFF DEFAULT_ATTENUATION_ROLLOFF}.
*/
declare class Attenuation {
constructor(context: any, options: any);
minDistance: any;
maxDistance: any;
_gainNode: any;
input: any;
output: any;
/**
* Set distance from the listener.
* @param {Number} distance Distance (in meters).
*/
setDistance(distance: number): void;
/**
* Set rolloff.
* @param {string} rolloff
* Rolloff model to use, chosen from options in
* {@linkcode Utils.ATTENUATION_ROLLOFFS ATTENUATION_ROLLOFFS}.
*/
setRolloff(rolloff: string): void;
_rolloff: string;
}

View File

@@ -0,0 +1,151 @@
/**
* @license
* Copyright 2017 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file Distance-based attenuation filter.
* @author Andrew Allen <bitllama@google.com>
*/
'use strict';
// Internal dependencies.
import Utils from './utils.js';
/**
* @class Attenuation
* @description Distance-based attenuation filter.
* @param {AudioContext} context
* Associated {@link
https://developer.mozilla.org/en-US/docs/Web/API/AudioContext AudioContext}.
* @param {Object} options
* @param {Number} options.minDistance
* Min. distance (in meters). Defaults to
* {@linkcode Utils.DEFAULT_MIN_DISTANCE DEFAULT_MIN_DISTANCE}.
* @param {Number} options.maxDistance
* Max. distance (in meters). Defaults to
* {@linkcode Utils.DEFAULT_MAX_DISTANCE DEFAULT_MAX_DISTANCE}.
* @param {string} options.rolloff
* Rolloff model to use, chosen from options in
* {@linkcode Utils.ATTENUATION_ROLLOFFS ATTENUATION_ROLLOFFS}. Defaults to
* {@linkcode Utils.DEFAULT_ATTENUATION_ROLLOFF DEFAULT_ATTENUATION_ROLLOFF}.
*/
class Attenuation {
constructor(context, options) {
// Public variables.
/**
* Min. distance (in meters).
* @member {Number} minDistance
* @memberof Attenuation
* @instance
*/
/**
* Max. distance (in meters).
* @member {Number} maxDistance
* @memberof Attenuation
* @instance
*/
/**
* Mono (1-channel) input {@link
* https://developer.mozilla.org/en-US/docs/Web/API/AudioNode AudioNode}.
* @member {AudioNode} input
* @memberof Attenuation
* @instance
*/
/**
* Mono (1-channel) output {@link
* https://developer.mozilla.org/en-US/docs/Web/API/AudioNode AudioNode}.
* @member {AudioNode} output
* @memberof Attenuation
* @instance
*/
// Use defaults for undefined arguments.
if (options == undefined) {
options = {};
}
if (options.minDistance == undefined) {
options.minDistance = Utils.DEFAULT_MIN_DISTANCE;
}
if (options.maxDistance == undefined) {
options.maxDistance = Utils.DEFAULT_MAX_DISTANCE;
}
if (options.rolloff == undefined) {
options.rolloff = Utils.DEFAULT_ATTENUATION_ROLLOFF;
}
// Assign values.
this.minDistance = options.minDistance;
this.maxDistance = options.maxDistance;
this.setRolloff(options.rolloff);
// Create node.
this._gainNode = context.createGain();
// Initialize distance to max distance.
this.setDistance(options.maxDistance);
// Input/Output proxy.
this.input = this._gainNode;
this.output = this._gainNode;
}
/**
* Set distance from the listener.
* @param {Number} distance Distance (in meters).
*/
setDistance(distance) {
let gain = 1;
if (this._rolloff == 'logarithmic') {
if (distance > this.maxDistance) {
gain = 0;
}
else if (distance > this.minDistance) {
let range = this.maxDistance - this.minDistance;
if (range > Utils.EPSILON_FLOAT) {
// Compute the distance attenuation value by the logarithmic curve
// "1 / (d + 1)" with an offset of |minDistance|.
let relativeDistance = distance - this.minDistance;
let attenuation = 1 / (relativeDistance + 1);
let attenuationMax = 1 / (range + 1);
gain = (attenuation - attenuationMax) / (1 - attenuationMax);
}
}
}
else if (this._rolloff == 'linear') {
if (distance > this.maxDistance) {
gain = 0;
}
else if (distance > this.minDistance) {
let range = this.maxDistance - this.minDistance;
if (range > Utils.EPSILON_FLOAT) {
gain = (this.maxDistance - distance) / range;
}
}
}
this._gainNode.gain.value = gain;
}
/**
* Set rolloff.
* @param {string} rolloff
* Rolloff model to use, chosen from options in
* {@linkcode Utils.ATTENUATION_ROLLOFFS ATTENUATION_ROLLOFFS}.
*/
setRolloff(rolloff) {
let isValidModel = ~Utils.ATTENUATION_ROLLOFFS.indexOf(rolloff);
if (rolloff == undefined || !isValidModel) {
if (!isValidModel) {
Utils.log('Invalid rolloff model (\"' + rolloff +
'\"). Using default: \"' + Utils.DEFAULT_ATTENUATION_ROLLOFF + '\".');
}
rolloff = Utils.DEFAULT_ATTENUATION_ROLLOFF;
}
else {
rolloff = rolloff.toString().toLowerCase();
}
this._rolloff = rolloff;
}
}
export default Attenuation;

View File

@@ -0,0 +1,217 @@
declare module 'resonance-audio' {
namespace ResonanceAudio {
/** Options for constructing a new ResonanceAudio scene */
interface Options {
/** Desired ambisonic Order */
ambisonicOrder?: number;
/** The listener's initial position (in meters), where origin is the center of
* the room */
listenerPosition?: Float32Array;
/** The listener's initial forward vector */
listenerForward?: Float32Array;
/** The listener's initial up vector */
listenerUp?: Float32Array;
/** Room dimensions (in meters) */
dimensions?: Utils.RoomDimensions;
/** Named acoustic materials per wall */
materials?: Utils.RoomMaterials;
/** (in meters/second) */
speedOfSound?: number;
}
}
/** Main class for managing sources, room and listener models */
class ResonanceAudio {
/** Binaurally-rendered stereo (2-channel) output */
output: AudioNode;
/** Ambisonic (multichannel) input */
ambisonicInput: AudioNode;
/** Ambisonic (multichannel) output */
ambisonicOutput: AudioNode;
constructor(context: AudioContext, options?: ResonanceAudio.Options);
/**
* Create a new source for the scene.
* @param options
* Options for constructing a new Source.
*/
createSource(options?: Source.Options): Source;
/**
* Set the scene's desired ambisonic order.
* @param ambisonicOrder Desired ambisonic order.
*/
setAmbisonicOrder(ambisonicOrder: any): void;
/**
* Set the room's dimensions and wall materials.
* @param dimensions Room dimensions (in meters).
* @param materials Named acoustic materials per wall.
*/
setRoomProperties(dimensions: Utils.RoomDimensions, materials: Utils.RoomMaterials): void;
/**
* Set the listener's position (in meters), where origin is the center of
* the room.
*/
setListenerPosition(x: number, y: number, z: number): any;
/** Set the source's orientation using forward and up vectors. */
setOrientation(forwardX: number, forwardY: number, forwardZ: number, upX: number, upY: number, upZ: number): void;
/**
* Set the listener's position and orientation using a Three.js Matrix4 object.
* @param matrix
* The Three.js Matrix4 object representing the listener's world transform.
*/
setListenerFromMatrix(matrix4: Float32Array): void;
/**
* Set the speed of sound.
*/
setSpeedOfSound(speedOfSound: number): void;
}
namespace Source {
/** Options for constructing a new Source. */
interface Options {
/** The source's initial position (in meters), where origin is the center of
* the room */
position?: Float32Array;
/** The source's initial forward vector */
forward?: Float32Array;
/** The source's initial up vector */
up?: Float32Array;
/** Min. distance (in meters) */
minDistance?: number;
/** Max. distance (in meters) */
maxDistance?: number;
/** Rolloff model to use */
rolloff?: string;
/** Input gain (linear) */
gain?: number;
/** Directivity alpha */
alpha?: number;
/** Directivity sharpness */
sharpness?: number;
/** Source width (in degrees). Where 0 degrees is a point source and 360 degrees
* is an omnidirectional source */
sourceWidth?: number;
}
}
/**
* Source model to spatialize an audio buffer.
*/
class Source {
constructor(scene: ResonanceAudio, options?: Source.Options);
/** Mono (1-channel) input */
input: AudioNode;
/**
* Set source's position (in meters), where origin is the center of
* the room.
*/
setPosition(x: number, y: number, z: number): void;
/** Set source's rolloff. */
setRolloff(rolloff: string): void;
/** Set source's minimum distance (in meters). */
setMinDistance(minDistance: number): void;
/** Set source's maximum distance (in meters). */
setMaxDistance(maxDistance: number): void;
/** Set source's gain (linear). */
setGain(gain: number): void;
/** Set the source's orientation using forward and up vectors. */
setOrientation(forwardX: number, forwardY: number, forwardZ: number, upX: number, upY: number, upZ: number): void;
/** Set source's position and orientation using a
* Three.js modelViewMatrix object */
setFromMatrix(matrix4: Float32Array): void;
/** Set the source width (in degrees). Where 0 degrees is a point source and 360
* degrees is an omnidirectional source */
setSourceWidth(sourceWidth: number): void;
/**
* Set source's directivity pattern (defined by alpha), where 0 is an
* omnidirectional pattern, 1 is a bidirectional pattern, 0.5 is a cardiod
* pattern. The sharpness of the pattern is increased exponentially
* @param alpha
* Determines directivity pattern (0 to 1).
* @param sharpness
* Determines the sharpness of the directivity pattern (1 to Inf).
*/
setDirectivityPattern(alpha: number, sharpness: number): void;
}
namespace Room {
interface Options {
/** The listener's initial position (in meters), where origin is the center of
* the room */
listenerPosition?: Float32Array;
/** Room dimensions (in meters) */
dimensions?: Utils.RoomDimensions;
/** Named acoustic materials per wall */
materials?: Utils.RoomMaterials;
/** (in meters/second) */
speedOfSound?: number;
}
}
/**
* Model that manages early and late reflections using acoustic
* properties and listener position relative to a rectangular room.
*/
class Room {
constructor(context: AudioContext, options?: Room.Options);
/**
* Set the room's dimensions and wall materials.
* @param dimensions Room dimensions (in meters)
* @param materials Named acoustic materials per wall
*/
setProperties(dimensions: Utils.RoomDimensions, materials: Utils.RoomMaterials): void;
/**
* Set the listener's position (in meters), where origin is the center of
* the room.
*/
setListenerPosition(x: number, y: number, z: number): void;
/**
* Compute distance outside room of provided position (in meters).
* @return
* Distance outside room (in meters). Returns 0 if inside room.
*/
getDistanceOutsideRoom(x: number, y: number, z: number): number;
}
namespace Listener {
interface Options {
/** Desired ambisonic order */
ambisonicOrder: number;
/** Initial position (in meters), where origin is the center of
* the room */
position?: Float32Array;
/** The listener's initial forward vector */
forward?: Float32Array;
/** The listener's initial up vector */
up?: Float32Array;
}
}
/** Listener model to spatialize sources in an environment */
class Listener {
/** Position (in meters) */
position: Float32Array;
/** Ambisonic (multichannel) input */
input: AudioNode;
/** Binaurally-rendered stereo (2-channel) output */
output: AudioNode;
/** Ambisonic (multichannel) output */
ambisonicOutput: AudioNode;
/**
* Set the listener's orientation using forward and up vectors.
*/
setOrientation(forwardX: number, forwardY: number, forwardZ: number, upX: number, upY: number, upZ: number): void;
/** Set listener's position and orientation using a
* Three.js modelViewMatrix object */
setFromMatrix(matrix4: Float32Array): void;
}
namespace Utils {
/** Properties describing the geometry of a room. */
interface RoomDimensions {
width: number;
height: number;
depth: number;
}
/** Properties describing the wall materials */
interface RoomMaterials {
left: string;
right: string;
front: string;
back: string;
down: string;
up: string;
}
}
}

View File

View File

@@ -0,0 +1,47 @@
export default Directivity;
/**
* @class Directivity
* @description Directivity/occlusion filter.
* @param {AudioContext} context
* Associated {@link
https://developer.mozilla.org/en-US/docs/Web/API/AudioContext AudioContext}.
* @param {Object} options
* @param {Number} options.alpha
* Determines directivity pattern (0 to 1). See
* {@link Directivity#setPattern setPattern} for more details. Defaults to
* {@linkcode Utils.DEFAULT_DIRECTIVITY_ALPHA DEFAULT_DIRECTIVITY_ALPHA}.
* @param {Number} options.sharpness
* Determines the sharpness of the directivity pattern (1 to Inf). See
* {@link Directivity#setPattern setPattern} for more details. Defaults to
* {@linkcode Utils.DEFAULT_DIRECTIVITY_SHARPNESS
* DEFAULT_DIRECTIVITY_SHARPNESS}.
*/
declare class Directivity {
constructor(context: any, options: any);
_context: any;
_lowpass: any;
_cosTheta: number;
input: any;
output: any;
/**
* Compute the filter using the source's forward orientation and the listener's
* position.
* @param {Float32Array} forward The source's forward vector.
* @param {Float32Array} direction The direction from the source to the
* listener.
*/
computeAngle(forward: Float32Array, direction: Float32Array): void;
/**
* Set source's directivity pattern (defined by alpha), where 0 is an
* omnidirectional pattern, 1 is a bidirectional pattern, 0.5 is a cardiod
* pattern. The sharpness of the pattern is increased exponenentially.
* @param {Number} alpha
* Determines directivity pattern (0 to 1).
* @param {Number} sharpness
* Determines the sharpness of the directivity pattern (1 to Inf).
* DEFAULT_DIRECTIVITY_SHARPNESS}.
*/
setPattern(alpha: number, sharpness: number): void;
_alpha: number;
_sharpness: number;
}

View File

@@ -0,0 +1,117 @@
/**
* @license
* Copyright 2017 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file Directivity/occlusion filter.
* @author Andrew Allen <bitllama@google.com>
*/
'use strict';
// Internal dependencies.
import Utils from './utils.js';
/**
* @class Directivity
* @description Directivity/occlusion filter.
* @param {AudioContext} context
* Associated {@link
https://developer.mozilla.org/en-US/docs/Web/API/AudioContext AudioContext}.
* @param {Object} options
* @param {Number} options.alpha
* Determines directivity pattern (0 to 1). See
* {@link Directivity#setPattern setPattern} for more details. Defaults to
* {@linkcode Utils.DEFAULT_DIRECTIVITY_ALPHA DEFAULT_DIRECTIVITY_ALPHA}.
* @param {Number} options.sharpness
* Determines the sharpness of the directivity pattern (1 to Inf). See
* {@link Directivity#setPattern setPattern} for more details. Defaults to
* {@linkcode Utils.DEFAULT_DIRECTIVITY_SHARPNESS
* DEFAULT_DIRECTIVITY_SHARPNESS}.
*/
class Directivity {
constructor(context, options) {
// Public variables.
/**
* Mono (1-channel) input {@link
* https://developer.mozilla.org/en-US/docs/Web/API/AudioNode AudioNode}.
* @member {AudioNode} input
* @memberof Directivity
* @instance
*/
/**
* Mono (1-channel) output {@link
* https://developer.mozilla.org/en-US/docs/Web/API/AudioNode AudioNode}.
* @member {AudioNode} output
* @memberof Directivity
* @instance
*/
// Use defaults for undefined arguments.
if (options == undefined) {
options = {};
}
if (options.alpha == undefined) {
options.alpha = Utils.DEFAULT_DIRECTIVITY_ALPHA;
}
if (options.sharpness == undefined) {
options.sharpness = Utils.DEFAULT_DIRECTIVITY_SHARPNESS;
}
// Create audio node.
this._context = context;
this._lowpass = context.createBiquadFilter();
// Initialize filter coefficients.
this._lowpass.type = 'lowpass';
this._lowpass.Q.value = 0;
this._lowpass.frequency.value = context.sampleRate * 0.5;
this._cosTheta = 0;
this.setPattern(options.alpha, options.sharpness);
// Input/Output proxy.
this.input = this._lowpass;
this.output = this._lowpass;
}
/**
* Compute the filter using the source's forward orientation and the listener's
* position.
* @param {Float32Array} forward The source's forward vector.
* @param {Float32Array} direction The direction from the source to the
* listener.
*/
computeAngle(forward, direction) {
let forwardNorm = Utils.normalizeVector(forward);
let directionNorm = Utils.normalizeVector(direction);
let coeff = 1;
if (this._alpha > Utils.EPSILON_FLOAT) {
let cosTheta = forwardNorm[0] * directionNorm[0] +
forwardNorm[1] * directionNorm[1] + forwardNorm[2] * directionNorm[2];
coeff = (1 - this._alpha) + this._alpha * cosTheta;
coeff = Math.pow(Math.abs(coeff), this._sharpness);
}
this._lowpass.frequency.value = this._context.sampleRate * 0.5 * coeff;
}
/**
* Set source's directivity pattern (defined by alpha), where 0 is an
* omnidirectional pattern, 1 is a bidirectional pattern, 0.5 is a cardiod
* pattern. The sharpness of the pattern is increased exponenentially.
* @param {Number} alpha
* Determines directivity pattern (0 to 1).
* @param {Number} sharpness
* Determines the sharpness of the directivity pattern (1 to Inf).
* DEFAULT_DIRECTIVITY_SHARPNESS}.
*/
setPattern(alpha, sharpness) {
// Clamp and set values.
this._alpha = Math.min(1, Math.max(0, alpha));
this._sharpness = Math.max(1, sharpness);
// Update angle calculation using new values.
this.computeAngle([this._cosTheta * this._cosTheta, 0, 0], [1, 0, 0]);
}
}
export default Directivity;

View File

@@ -0,0 +1,56 @@
export default EarlyReflections;
/**
* @class EarlyReflections
* @description Ray-tracing-based early reflections model.
* @param {AudioContext} context
* Associated {@link
https://developer.mozilla.org/en-US/docs/Web/API/AudioContext AudioContext}.
* @param {Object} options
* @param {Utils~RoomDimensions} options.dimensions
* Room dimensions (in meters). Defaults to
* {@linkcode Utils.DEFAULT_ROOM_DIMENSIONS DEFAULT_ROOM_DIMENSIONS}.
* @param {Object} options.coefficients
* Frequency-independent reflection coeffs per wall. Defaults to
* {@linkcode Utils.DEFAULT_REFLECTION_COEFFICIENTS
* DEFAULT_REFLECTION_COEFFICIENTS}.
* @param {Number} options.speedOfSound
* (in meters / second). Defaults to {@linkcode Utils.DEFAULT_SPEED_OF_SOUND
* DEFAULT_SPEED_OF_SOUND}.
* @param {Float32Array} options.listenerPosition
* (in meters). Defaults to
* {@linkcode Utils.DEFAULT_POSITION DEFAULT_POSITION}.
*/
declare class EarlyReflections {
constructor(context: any, options: any);
speedOfSound: any;
input: any;
output: any;
_lowpass: any;
_delays: {};
_gains: {};
_inverters: {};
_merger: any;
_listenerPosition: any;
/**
* Set the listener's position (in meters),
* where [0,0,0] is the center of the room.
* @param {Number} x
* @param {Number} y
* @param {Number} z
*/
setListenerPosition(x: number, y: number, z: number): void;
/**
* Set the room's properties which determines the characteristics of
* reflections.
* @param {Utils~RoomDimensions} dimensions
* Room dimensions (in meters). Defaults to
* {@linkcode Utils.DEFAULT_ROOM_DIMENSIONS DEFAULT_ROOM_DIMENSIONS}.
* @param {Object} coefficients
* Frequency-independent reflection coeffs per wall. Defaults to
* {@linkcode Utils.DEFAULT_REFLECTION_COEFFICIENTS
* DEFAULT_REFLECTION_COEFFICIENTS}.
*/
setRoomProperties(dimensions: any, coefficients: any): void;
_coefficients: any;
_halfDimensions: {};
}

View File

@@ -0,0 +1,212 @@
/**
* @license
* Copyright 2017 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file Ray-tracing-based early reflections model.
* @author Andrew Allen <bitllama@google.com>
*/
'use strict';
// Internal dependencies.
import Utils from './utils.js';
/**
* @class EarlyReflections
* @description Ray-tracing-based early reflections model.
* @param {AudioContext} context
* Associated {@link
https://developer.mozilla.org/en-US/docs/Web/API/AudioContext AudioContext}.
* @param {Object} options
* @param {Utils~RoomDimensions} options.dimensions
* Room dimensions (in meters). Defaults to
* {@linkcode Utils.DEFAULT_ROOM_DIMENSIONS DEFAULT_ROOM_DIMENSIONS}.
* @param {Object} options.coefficients
* Frequency-independent reflection coeffs per wall. Defaults to
* {@linkcode Utils.DEFAULT_REFLECTION_COEFFICIENTS
* DEFAULT_REFLECTION_COEFFICIENTS}.
* @param {Number} options.speedOfSound
* (in meters / second). Defaults to {@linkcode Utils.DEFAULT_SPEED_OF_SOUND
* DEFAULT_SPEED_OF_SOUND}.
* @param {Float32Array} options.listenerPosition
* (in meters). Defaults to
* {@linkcode Utils.DEFAULT_POSITION DEFAULT_POSITION}.
*/
class EarlyReflections {
constructor(context, options) {
// Public variables.
/**
* The room's speed of sound (in meters/second).
* @member {Number} speedOfSound
* @memberof EarlyReflections
* @instance
*/
/**
* Mono (1-channel) input {@link
* https://developer.mozilla.org/en-US/docs/Web/API/AudioNode AudioNode}.
* @member {AudioNode} input
* @memberof EarlyReflections
* @instance
*/
/**
* First-order ambisonic (4-channel) output {@link
* https://developer.mozilla.org/en-US/docs/Web/API/AudioNode AudioNode}.
* @member {AudioNode} output
* @memberof EarlyReflections
* @instance
*/
// Use defaults for undefined arguments.
if (options == undefined) {
options = {};
}
if (options.speedOfSound == undefined) {
options.speedOfSound = Utils.DEFAULT_SPEED_OF_SOUND;
}
if (options.listenerPosition == undefined) {
options.listenerPosition = Utils.DEFAULT_POSITION.slice();
}
if (options.coefficients == undefined) {
options.coefficients = {};
Object.assign(options.coefficients, Utils.DEFAULT_REFLECTION_COEFFICIENTS);
}
// Assign room's speed of sound.
this.speedOfSound = options.speedOfSound;
// Create nodes.
this.input = context.createGain();
this.output = context.createGain();
this._lowpass = context.createBiquadFilter();
this._delays = {};
this._gains = {}; // gainPerWall = (ReflectionCoeff / Attenuation)
this._inverters = {}; // 3 of these are needed for right/back/down walls.
this._merger = context.createChannelMerger(4); // First-order encoding only.
// Connect audio graph for each wall reflection.
for (let property in Utils.DEFAULT_REFLECTION_COEFFICIENTS) {
if (Utils.DEFAULT_REFLECTION_COEFFICIENTS
.hasOwnProperty(property)) {
this._delays[property] =
context.createDelay(Utils.MAX_DURATION);
this._gains[property] = context.createGain();
}
}
this._inverters.right = context.createGain();
this._inverters.down = context.createGain();
this._inverters.back = context.createGain();
// Initialize lowpass filter.
this._lowpass.type = 'lowpass';
this._lowpass.frequency.value = Utils.DEFAULT_REFLECTION_CUTOFF_FREQUENCY;
this._lowpass.Q.value = 0;
// Initialize encoder directions, set delay times and gains to 0.
for (let property in Utils.DEFAULT_REFLECTION_COEFFICIENTS) {
if (Utils.DEFAULT_REFLECTION_COEFFICIENTS
.hasOwnProperty(property)) {
this._delays[property].delayTime.value = 0;
this._gains[property].gain.value = 0;
}
}
// Initialize inverters for opposite walls ('right', 'down', 'back' only).
this._inverters.right.gain.value = -1;
this._inverters.down.gain.value = -1;
this._inverters.back.gain.value = -1;
// Connect nodes.
this.input.connect(this._lowpass);
for (let property in Utils.DEFAULT_REFLECTION_COEFFICIENTS) {
if (Utils.DEFAULT_REFLECTION_COEFFICIENTS
.hasOwnProperty(property)) {
this._lowpass.connect(this._delays[property]);
this._delays[property].connect(this._gains[property]);
this._gains[property].connect(this._merger, 0, 0);
}
}
// Connect gains to ambisonic channel output.
// Left: [1 1 0 0]
// Right: [1 -1 0 0]
// Up: [1 0 1 0]
// Down: [1 0 -1 0]
// Front: [1 0 0 1]
// Back: [1 0 0 -1]
this._gains.left.connect(this._merger, 0, 1);
this._gains.right.connect(this._inverters.right);
this._inverters.right.connect(this._merger, 0, 1);
this._gains.up.connect(this._merger, 0, 2);
this._gains.down.connect(this._inverters.down);
this._inverters.down.connect(this._merger, 0, 2);
this._gains.front.connect(this._merger, 0, 3);
this._gains.back.connect(this._inverters.back);
this._inverters.back.connect(this._merger, 0, 3);
this._merger.connect(this.output);
// Initialize.
this._listenerPosition = options.listenerPosition;
this.setRoomProperties(options.dimensions, options.coefficients);
}
/**
* Set the listener's position (in meters),
* where [0,0,0] is the center of the room.
* @param {Number} x
* @param {Number} y
* @param {Number} z
*/
setListenerPosition(x, y, z) {
// Assign listener position.
this._listenerPosition = [x, y, z];
// Determine distances to each wall.
let distances = {
left: Utils.DEFAULT_REFLECTION_MULTIPLIER * Math.max(0, this._halfDimensions.width + x) + Utils.DEFAULT_REFLECTION_MIN_DISTANCE,
right: Utils.DEFAULT_REFLECTION_MULTIPLIER * Math.max(0, this._halfDimensions.width - x) + Utils.DEFAULT_REFLECTION_MIN_DISTANCE,
front: Utils.DEFAULT_REFLECTION_MULTIPLIER * Math.max(0, this._halfDimensions.depth + z) + Utils.DEFAULT_REFLECTION_MIN_DISTANCE,
back: Utils.DEFAULT_REFLECTION_MULTIPLIER * Math.max(0, this._halfDimensions.depth - z) + Utils.DEFAULT_REFLECTION_MIN_DISTANCE,
down: Utils.DEFAULT_REFLECTION_MULTIPLIER * Math.max(0, this._halfDimensions.height + y) + Utils.DEFAULT_REFLECTION_MIN_DISTANCE,
up: Utils.DEFAULT_REFLECTION_MULTIPLIER * Math.max(0, this._halfDimensions.height - y) + Utils.DEFAULT_REFLECTION_MIN_DISTANCE,
};
// Assign delay & attenuation values using distances.
for (let property in Utils.DEFAULT_REFLECTION_COEFFICIENTS) {
if (Utils.DEFAULT_REFLECTION_COEFFICIENTS
.hasOwnProperty(property)) {
// Compute and assign delay (in seconds).
let delayInSecs = distances[property] / this.speedOfSound;
this._delays[property].delayTime.value = delayInSecs;
// Compute and assign gain, uses logarithmic rolloff: "g = R / (d + 1)"
let attenuation = this._coefficients[property] / distances[property];
this._gains[property].gain.value = attenuation;
}
}
}
/**
* Set the room's properties which determines the characteristics of
* reflections.
* @param {Utils~RoomDimensions} dimensions
* Room dimensions (in meters). Defaults to
* {@linkcode Utils.DEFAULT_ROOM_DIMENSIONS DEFAULT_ROOM_DIMENSIONS}.
* @param {Object} coefficients
* Frequency-independent reflection coeffs per wall. Defaults to
* {@linkcode Utils.DEFAULT_REFLECTION_COEFFICIENTS
* DEFAULT_REFLECTION_COEFFICIENTS}.
*/
setRoomProperties(dimensions, coefficients) {
if (dimensions == undefined) {
dimensions = {};
Object.assign(dimensions, Utils.DEFAULT_ROOM_DIMENSIONS);
}
if (coefficients == undefined) {
coefficients = {};
Object.assign(coefficients, Utils.DEFAULT_REFLECTION_COEFFICIENTS);
}
this._coefficients = coefficients;
// Sanitize dimensions and store half-dimensions.
this._halfDimensions = {};
this._halfDimensions.width = dimensions.width * 0.5;
this._halfDimensions.height = dimensions.height * 0.5;
this._halfDimensions.depth = dimensions.depth * 0.5;
// Update listener position with new room properties.
this.setListenerPosition(this._listenerPosition[0], this._listenerPosition[1], this._listenerPosition[2]);
}
}
export default EarlyReflections;

View File

@@ -0,0 +1,64 @@
export default Encoder;
/**
* @class Encoder
* @description Spatially encodes input using weighted spherical harmonics.
* @param {AudioContext} context
* Associated {@link
https://developer.mozilla.org/en-US/docs/Web/API/AudioContext AudioContext}.
* @param {Object} options
* @param {Number} options.ambisonicOrder
* Desired ambisonic order. Defaults to
* {@linkcode Utils.DEFAULT_AMBISONIC_ORDER DEFAULT_AMBISONIC_ORDER}.
* @param {Number} options.azimuth
* Azimuth (in degrees). Defaults to
* {@linkcode Utils.DEFAULT_AZIMUTH DEFAULT_AZIMUTH}.
* @param {Number} options.elevation
* Elevation (in degrees). Defaults to
* {@linkcode Utils.DEFAULT_ELEVATION DEFAULT_ELEVATION}.
* @param {Number} options.sourceWidth
* Source width (in degrees). Where 0 degrees is a point source and 360 degrees
* is an omnidirectional source. Defaults to
* {@linkcode Utils.DEFAULT_SOURCE_WIDTH DEFAULT_SOURCE_WIDTH}.
*/
declare class Encoder {
constructor(context: any, options: any);
_context: any;
input: any;
_channelGain: any[];
_merger: any;
output: any;
_azimuth: any;
_elevation: any;
/**
* Set the desired ambisonic order.
* @param {Number} ambisonicOrder Desired ambisonic order.
*/
setAmbisonicOrder(ambisonicOrder: number): void;
_ambisonicOrder: number;
/**
* Set the direction of the encoded source signal.
* @param {Number} azimuth
* Azimuth (in degrees). Defaults to
* {@linkcode Utils.DEFAULT_AZIMUTH DEFAULT_AZIMUTH}.
* @param {Number} elevation
* Elevation (in degrees). Defaults to
* {@linkcode Utils.DEFAULT_ELEVATION DEFAULT_ELEVATION}.
*/
setDirection(azimuth: number, elevation: number): void;
/**
* Set the source width (in degrees). Where 0 degrees is a point source and 360
* degrees is an omnidirectional source.
* @param {Number} sourceWidth (in degrees).
*/
setSourceWidth(sourceWidth: number): void;
_spreadIndex: number;
}
declare namespace Encoder {
/**
* Validate the provided ambisonic order.
* @param {Number} ambisonicOrder Desired ambisonic order.
* @return {Number} Validated/adjusted ambisonic order.
* @private
*/
function validateAmbisonicOrder(ambisonicOrder: number): number;
}

View File

@@ -0,0 +1,194 @@
/**
* @license
* Copyright 2017 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file Spatially encodes input using weighted spherical harmonics.
* @author Andrew Allen <bitllama@google.com>
*/
'use strict';
// Internal dependencies.
import Tables from './tables.js';
import Utils from './utils.js';
/**
* @class Encoder
* @description Spatially encodes input using weighted spherical harmonics.
* @param {AudioContext} context
* Associated {@link
https://developer.mozilla.org/en-US/docs/Web/API/AudioContext AudioContext}.
* @param {Object} options
* @param {Number} options.ambisonicOrder
* Desired ambisonic order. Defaults to
* {@linkcode Utils.DEFAULT_AMBISONIC_ORDER DEFAULT_AMBISONIC_ORDER}.
* @param {Number} options.azimuth
* Azimuth (in degrees). Defaults to
* {@linkcode Utils.DEFAULT_AZIMUTH DEFAULT_AZIMUTH}.
* @param {Number} options.elevation
* Elevation (in degrees). Defaults to
* {@linkcode Utils.DEFAULT_ELEVATION DEFAULT_ELEVATION}.
* @param {Number} options.sourceWidth
* Source width (in degrees). Where 0 degrees is a point source and 360 degrees
* is an omnidirectional source. Defaults to
* {@linkcode Utils.DEFAULT_SOURCE_WIDTH DEFAULT_SOURCE_WIDTH}.
*/
class Encoder {
constructor(context, options) {
// Public variables.
/**
* Mono (1-channel) input {@link
* https://developer.mozilla.org/en-US/docs/Web/API/AudioNode AudioNode}.
* @member {AudioNode} input
* @memberof Encoder
* @instance
*/
/**
* Ambisonic (multichannel) output {@link
* https://developer.mozilla.org/en-US/docs/Web/API/AudioNode AudioNode}.
* @member {AudioNode} output
* @memberof Encoder
* @instance
*/
// Use defaults for undefined arguments.
if (options == undefined) {
options = {};
}
if (options.ambisonicOrder == undefined) {
options.ambisonicOrder = Utils.DEFAULT_AMBISONIC_ORDER;
}
if (options.azimuth == undefined) {
options.azimuth = Utils.DEFAULT_AZIMUTH;
}
if (options.elevation == undefined) {
options.elevation = Utils.DEFAULT_ELEVATION;
}
if (options.sourceWidth == undefined) {
options.sourceWidth = Utils.DEFAULT_SOURCE_WIDTH;
}
this._context = context;
// Create I/O nodes.
this.input = context.createGain();
this._channelGain = [];
this._merger = undefined;
this.output = context.createGain();
// Set initial order, angle and source width.
this.setAmbisonicOrder(options.ambisonicOrder);
this._azimuth = options.azimuth;
this._elevation = options.elevation;
this.setSourceWidth(options.sourceWidth);
}
/**
* Set the desired ambisonic order.
* @param {Number} ambisonicOrder Desired ambisonic order.
*/
setAmbisonicOrder(ambisonicOrder) {
this._ambisonicOrder = Encoder.validateAmbisonicOrder(ambisonicOrder);
this.input.disconnect();
for (let i = 0; i < this._channelGain.length; i++) {
this._channelGain[i].disconnect();
}
if (this._merger != undefined) {
this._merger.disconnect();
}
delete this._channelGain;
delete this._merger;
// Create audio graph.
let numChannels = (this._ambisonicOrder + 1) * (this._ambisonicOrder + 1);
this._merger = this._context.createChannelMerger(numChannels);
this._channelGain = new Array(numChannels);
for (let i = 0; i < numChannels; i++) {
this._channelGain[i] = this._context.createGain();
this.input.connect(this._channelGain[i]);
this._channelGain[i].connect(this._merger, 0, i);
}
this._merger.connect(this.output);
}
/**
* Set the direction of the encoded source signal.
* @param {Number} azimuth
* Azimuth (in degrees). Defaults to
* {@linkcode Utils.DEFAULT_AZIMUTH DEFAULT_AZIMUTH}.
* @param {Number} elevation
* Elevation (in degrees). Defaults to
* {@linkcode Utils.DEFAULT_ELEVATION DEFAULT_ELEVATION}.
*/
setDirection(azimuth, elevation) {
// Format input direction to nearest indices.
if (azimuth == undefined || isNaN(azimuth)) {
azimuth = Utils.DEFAULT_AZIMUTH;
}
if (elevation == undefined || isNaN(elevation)) {
elevation = Utils.DEFAULT_ELEVATION;
}
// Store the formatted input (for updating source width).
this._azimuth = azimuth;
this._elevation = elevation;
// Format direction for index lookups.
azimuth = Math.round(azimuth % 360);
if (azimuth < 0) {
azimuth += 360;
}
elevation = Math.round(Math.min(90, Math.max(-90, elevation))) + 90;
// Assign gains to each output.
this._channelGain[0].gain.value = Tables.MAX_RE_WEIGHTS[this._spreadIndex][0];
for (let i = 1; i <= this._ambisonicOrder; i++) {
let degreeWeight = Tables.MAX_RE_WEIGHTS[this._spreadIndex][i];
for (let j = -i; j <= i; j++) {
let acnChannel = (i * i) + i + j;
let elevationIndex = i * (i + 1) / 2 + Math.abs(j) - 1;
let val = Tables.SPHERICAL_HARMONICS[1][elevation][elevationIndex];
if (j != 0) {
let azimuthIndex = Tables.SPHERICAL_HARMONICS_MAX_ORDER + j - 1;
if (j < 0) {
azimuthIndex = Tables.SPHERICAL_HARMONICS_MAX_ORDER + j;
}
val *= Tables.SPHERICAL_HARMONICS[0][azimuth][azimuthIndex];
}
this._channelGain[acnChannel].gain.value = val * degreeWeight;
}
}
}
/**
* Set the source width (in degrees). Where 0 degrees is a point source and 360
* degrees is an omnidirectional source.
* @param {Number} sourceWidth (in degrees).
*/
setSourceWidth(sourceWidth) {
// The MAX_RE_WEIGHTS is a 360 x (Tables.SPHERICAL_HARMONICS_MAX_ORDER+1)
// size table.
this._spreadIndex = Math.min(359, Math.max(0, Math.round(sourceWidth)));
this.setDirection(this._azimuth, this._elevation);
}
}
/**
* Validate the provided ambisonic order.
* @param {Number} ambisonicOrder Desired ambisonic order.
* @return {Number} Validated/adjusted ambisonic order.
* @private
*/
Encoder.validateAmbisonicOrder = ambisonicOrder => {
if (isNaN(ambisonicOrder) || ambisonicOrder == undefined) {
Utils.log('Error: Invalid ambisonic order', options.ambisonicOrder, '\nUsing ambisonicOrder=1 instead.');
ambisonicOrder = 1;
}
else if (ambisonicOrder < 1) {
Utils.log('Error: Unable to render ambisonic order', options.ambisonicOrder, '(Min order is 1)', '\nUsing min order instead.');
ambisonicOrder = 1;
}
else if (ambisonicOrder > Tables.SPHERICAL_HARMONICS_MAX_ORDER) {
Utils.log('Error: Unable to render ambisonic order', options.ambisonicOrder, '(Max order is', Tables.SPHERICAL_HARMONICS_MAX_ORDER, ')\nUsing max order instead.');
options.ambisonicOrder = Tables.SPHERICAL_HARMONICS_MAX_ORDER;
}
return ambisonicOrder;
};
export default Encoder;

View File

@@ -0,0 +1,42 @@
export default LateReflections;
/**
* @class LateReflections
* @description Late-reflections reverberation filter for Ambisonic content.
* @param {AudioContext} context
* Associated {@link
https://developer.mozilla.org/en-US/docs/Web/API/AudioContext AudioContext}.
* @param {Object} options
* @param {Array} options.durations
* Multiband RT60 durations (in seconds) for each frequency band, listed as
* {@linkcode Utils.DEFAULT_REVERB_FREQUENCY_BANDS
* FREQUDEFAULT_REVERB_FREQUENCY_BANDSENCY_BANDS}. Defaults to
* {@linkcode Utils.DEFAULT_REVERB_DURATIONS DEFAULT_REVERB_DURATIONS}.
* @param {Number} options.predelay Pre-delay (in milliseconds). Defaults to
* {@linkcode Utils.DEFAULT_REVERB_PREDELAY DEFAULT_REVERB_PREDELAY}.
* @param {Number} options.gain Output gain (linear). Defaults to
* {@linkcode Utils.DEFAULT_REVERB_GAIN DEFAULT_REVERB_GAIN}.
* @param {Number} options.bandwidth Bandwidth (in octaves) for each frequency
* band. Defaults to
* {@linkcode Utils.DEFAULT_REVERB_BANDWIDTH DEFAULT_REVERB_BANDWIDTH}.
* @param {Number} options.tailonset Length (in milliseconds) of impulse
* response to apply a half-Hann window. Defaults to
* {@linkcode Utils.DEFAULT_REVERB_TAIL_ONSET DEFAULT_REVERB_TAIL_ONSET}.
*/
declare class LateReflections {
constructor(context: any, options: any);
_bandwidthCoeff: number;
_tailonsetSamples: number;
_context: any;
input: any;
_predelay: any;
_convolver: any;
output: any;
/**
* Re-compute a new impulse response by providing Multiband RT60 durations.
* @param {Array} durations
* Multiband RT60 durations (in seconds) for each frequency band, listed as
* {@linkcode Utils.DEFAULT_REVERB_FREQUENCY_BANDS
* DEFAULT_REVERB_FREQUENCY_BANDS}.
*/
setDurations(durations: any[]): void;
}

View File

@@ -0,0 +1,187 @@
/**
* @license
* Copyright 2017 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file Late reverberation filter for Ambisonic content.
* @author Andrew Allen <bitllama@google.com>
*/
'use strict';
// Internal dependencies.
import Utils from './utils.js';
/**
* @class LateReflections
* @description Late-reflections reverberation filter for Ambisonic content.
* @param {AudioContext} context
* Associated {@link
https://developer.mozilla.org/en-US/docs/Web/API/AudioContext AudioContext}.
* @param {Object} options
* @param {Array} options.durations
* Multiband RT60 durations (in seconds) for each frequency band, listed as
* {@linkcode Utils.DEFAULT_REVERB_FREQUENCY_BANDS
* FREQUDEFAULT_REVERB_FREQUENCY_BANDSENCY_BANDS}. Defaults to
* {@linkcode Utils.DEFAULT_REVERB_DURATIONS DEFAULT_REVERB_DURATIONS}.
* @param {Number} options.predelay Pre-delay (in milliseconds). Defaults to
* {@linkcode Utils.DEFAULT_REVERB_PREDELAY DEFAULT_REVERB_PREDELAY}.
* @param {Number} options.gain Output gain (linear). Defaults to
* {@linkcode Utils.DEFAULT_REVERB_GAIN DEFAULT_REVERB_GAIN}.
* @param {Number} options.bandwidth Bandwidth (in octaves) for each frequency
* band. Defaults to
* {@linkcode Utils.DEFAULT_REVERB_BANDWIDTH DEFAULT_REVERB_BANDWIDTH}.
* @param {Number} options.tailonset Length (in milliseconds) of impulse
* response to apply a half-Hann window. Defaults to
* {@linkcode Utils.DEFAULT_REVERB_TAIL_ONSET DEFAULT_REVERB_TAIL_ONSET}.
*/
class LateReflections {
constructor(context, options) {
// Public variables.
/**
* Mono (1-channel) input {@link
* https://developer.mozilla.org/en-US/docs/Web/API/AudioNode AudioNode}.
* @member {AudioNode} input
* @memberof LateReflections
* @instance
*/
/**
* Mono (1-channel) output {@link
* https://developer.mozilla.org/en-US/docs/Web/API/AudioNode AudioNode}.
* @member {AudioNode} output
* @memberof LateReflections
* @instance
*/
// Use defaults for undefined arguments.
if (options == undefined) {
options = {};
}
if (options.durations == undefined) {
options.durations = Utils.DEFAULT_REVERB_DURATIONS.slice();
}
if (options.predelay == undefined) {
options.predelay = Utils.DEFAULT_REVERB_PREDELAY;
}
if (options.gain == undefined) {
options.gain = Utils.DEFAULT_REVERB_GAIN;
}
if (options.bandwidth == undefined) {
options.bandwidth = Utils.DEFAULT_REVERB_BANDWIDTH;
}
if (options.tailonset == undefined) {
options.tailonset = Utils.DEFAULT_REVERB_TAIL_ONSET;
}
// Assign pre-computed variables.
let delaySecs = options.predelay / 1000;
this._bandwidthCoeff = options.bandwidth * Utils.LOG2_DIV2;
this._tailonsetSamples = options.tailonset / 1000;
// Create nodes.
this._context = context;
this.input = context.createGain();
this._predelay = context.createDelay(delaySecs);
this._convolver = context.createConvolver();
this.output = context.createGain();
// Set reverb attenuation.
this.output.gain.value = options.gain;
// Disable normalization.
this._convolver.normalize = false;
// Connect nodes.
this.input.connect(this._predelay);
this._predelay.connect(this._convolver);
this._convolver.connect(this.output);
// Compute IR using RT60 values.
this.setDurations(options.durations);
}
/**
* Re-compute a new impulse response by providing Multiband RT60 durations.
* @param {Array} durations
* Multiband RT60 durations (in seconds) for each frequency band, listed as
* {@linkcode Utils.DEFAULT_REVERB_FREQUENCY_BANDS
* DEFAULT_REVERB_FREQUENCY_BANDS}.
*/
setDurations(durations) {
if (durations.length !== Utils.NUMBER_REVERB_FREQUENCY_BANDS) {
Utils.log('Warning: invalid number of RT60 values provided to reverb.');
return;
}
// Compute impulse response.
let durationsSamples = new Float32Array(Utils.NUMBER_REVERB_FREQUENCY_BANDS);
let sampleRate = this._context.sampleRate;
for (let i = 0; i < durations.length; i++) {
// Clamp within suitable range.
durations[i] =
Math.max(0, Math.min(Utils.DEFAULT_REVERB_MAX_DURATION, durations[i]));
// Convert seconds to samples.
durationsSamples[i] = Math.round(durations[i] * sampleRate *
Utils.DEFAULT_REVERB_DURATION_MULTIPLIER);
}
;
// Determine max RT60 length in samples.
let durationsSamplesMax = 0;
for (let i = 0; i < durationsSamples.length; i++) {
if (durationsSamples[i] > durationsSamplesMax) {
durationsSamplesMax = durationsSamples[i];
}
}
// Skip this step if there is no reverberation to compute.
if (durationsSamplesMax < 1) {
durationsSamplesMax = 1;
}
// Create impulse response buffer.
let buffer = this._context.createBuffer(1, durationsSamplesMax, sampleRate);
let bufferData = buffer.getChannelData(0);
// Create noise signal (computed once, referenced in each band's routine).
let noiseSignal = new Float32Array(durationsSamplesMax);
for (let i = 0; i < durationsSamplesMax; i++) {
noiseSignal[i] = Math.random() * 2 - 1;
}
// Compute the decay rate per-band and filter the decaying noise signal.
for (let i = 0; i < Utils.NUMBER_REVERB_FREQUENCY_BANDS; i++) {
// Compute decay rate.
let decayRate = -Utils.LOG1000 / durationsSamples[i];
// Construct a standard one-zero, two-pole bandpass filter:
// H(z) = (b0 * z^0 + b1 * z^-1 + b2 * z^-2) / (1 + a1 * z^-1 + a2 * z^-2)
let omega = Utils.TWO_PI *
Utils.DEFAULT_REVERB_FREQUENCY_BANDS[i] / sampleRate;
let sinOmega = Math.sin(omega);
let alpha = sinOmega * Math.sinh(this._bandwidthCoeff * omega / sinOmega);
let a0CoeffReciprocal = 1 / (1 + alpha);
let b0Coeff = alpha * a0CoeffReciprocal;
let a1Coeff = -2 * Math.cos(omega) * a0CoeffReciprocal;
let a2Coeff = (1 - alpha) * a0CoeffReciprocal;
// We optimize since b2 = -b0, b1 = 0.
// Update equation for two-pole bandpass filter:
// u[n] = x[n] - a1 * x[n-1] - a2 * x[n-2]
// y[n] = b0 * (u[n] - u[n-2])
let um1 = 0;
let um2 = 0;
for (let j = 0; j < durationsSamples[i]; j++) {
// Exponentially-decaying white noise.
let x = noiseSignal[j] * Math.exp(decayRate * j);
// Filter signal with bandpass filter and add to output.
let u = x - a1Coeff * um1 - a2Coeff * um2;
bufferData[j] += b0Coeff * (u - um2);
// Update coefficients.
um2 = um1;
um1 = u;
}
}
// Create and apply half of a Hann window to the beginning of the
// impulse response.
let halfHannLength = Math.round(this._tailonsetSamples);
for (let i = 0; i < Math.min(bufferData.length, halfHannLength); i++) {
let halfHann = 0.5 * (1 - Math.cos(Utils.TWO_PI * i / (2 * halfHannLength - 1)));
bufferData[i] *= halfHann;
}
this._convolver.buffer = buffer;
}
}
export default LateReflections;

View File

@@ -0,0 +1,49 @@
export default Listener;
/**
* @class Listener
* @description Listener model to spatialize sources in an environment.
* @param {AudioContext} context
* Associated {@link
https://developer.mozilla.org/en-US/docs/Web/API/AudioContext AudioContext}.
* @param {Object} options
* @param {Number} options.ambisonicOrder
* Desired ambisonic order. Defaults to
* {@linkcode Utils.DEFAULT_AMBISONIC_ORDER DEFAULT_AMBISONIC_ORDER}.
* @param {Float32Array} options.position
* Initial position (in meters), where origin is the center of
* the room. Defaults to
* {@linkcode Utils.DEFAULT_POSITION DEFAULT_POSITION}.
* @param {Float32Array} options.forward
* The listener's initial forward vector. Defaults to
* {@linkcode Utils.DEFAULT_FORWARD DEFAULT_FORWARD}.
* @param {Float32Array} options.up
* The listener's initial up vector. Defaults to
* {@linkcode Utils.DEFAULT_UP DEFAULT_UP}.
*/
declare class Listener {
constructor(context: any, options: any);
position: Float32Array;
_tempMatrix3: Float32Array;
_ambisonicOrder: number;
_context: any;
_renderer: any;
input: any;
output: any;
ambisonicOutput: any;
/**
* Set the source's orientation using forward and up vectors.
* @param {Number} forwardX
* @param {Number} forwardY
* @param {Number} forwardZ
* @param {Number} upX
* @param {Number} upY
* @param {Number} upZ
*/
setOrientation(forwardX: number, forwardY: number, forwardZ: number, upX: number, upY: number, upZ: number): void;
/**
* Set the listener's position and orientation using a Three.js Matrix4 object.
* @param {Object} matrix4
* The Three.js Matrix4 object representing the listener's world transform.
*/
setFromMatrix(matrix4: any): void;
}

View File

@@ -0,0 +1,168 @@
/**
* @license
* Copyright 2017 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file Listener model to spatialize sources in an environment.
* @author Andrew Allen <bitllama@google.com>
*/
'use strict';
// Internal dependencies.
import Omnitone from 'omnitone/build/omnitone.esm';
import Encoder from './encoder.js';
import Utils from './utils.js';
/**
* @class Listener
* @description Listener model to spatialize sources in an environment.
* @param {AudioContext} context
* Associated {@link
https://developer.mozilla.org/en-US/docs/Web/API/AudioContext AudioContext}.
* @param {Object} options
* @param {Number} options.ambisonicOrder
* Desired ambisonic order. Defaults to
* {@linkcode Utils.DEFAULT_AMBISONIC_ORDER DEFAULT_AMBISONIC_ORDER}.
* @param {Float32Array} options.position
* Initial position (in meters), where origin is the center of
* the room. Defaults to
* {@linkcode Utils.DEFAULT_POSITION DEFAULT_POSITION}.
* @param {Float32Array} options.forward
* The listener's initial forward vector. Defaults to
* {@linkcode Utils.DEFAULT_FORWARD DEFAULT_FORWARD}.
* @param {Float32Array} options.up
* The listener's initial up vector. Defaults to
* {@linkcode Utils.DEFAULT_UP DEFAULT_UP}.
*/
class Listener {
constructor(context, options) {
// Public variables.
/**
* Position (in meters).
* @member {Float32Array} position
* @memberof Listener
* @instance
*/
/**
* Ambisonic (multichannel) input {@link
* https://developer.mozilla.org/en-US/docs/Web/API/AudioNode AudioNode}.
* @member {AudioNode} input
* @memberof Listener
* @instance
*/
/**
* Binaurally-rendered stereo (2-channel) output {@link
* https://developer.mozilla.org/en-US/docs/Web/API/AudioNode AudioNode}.
* @member {AudioNode} output
* @memberof Listener
* @instance
*/
/**
* Ambisonic (multichannel) output {@link
* https://developer.mozilla.org/en-US/docs/Web/API/AudioNode AudioNode}.
* @member {AudioNode} ambisonicOutput
* @memberof Listener
* @instance
*/
// Use defaults for undefined arguments.
if (options == undefined) {
options = {};
}
if (options.ambisonicOrder == undefined) {
options.ambisonicOrder = Utils.DEFAULT_AMBISONIC_ORDER;
}
if (options.position == undefined) {
options.position = Utils.DEFAULT_POSITION.slice();
}
if (options.forward == undefined) {
options.forward = Utils.DEFAULT_FORWARD.slice();
}
if (options.up == undefined) {
options.up = Utils.DEFAULT_UP.slice();
}
// Member variables.
this.position = new Float32Array(3);
this._tempMatrix3 = new Float32Array(9);
// Select the appropriate HRIR filters using 2-channel chunks since
// multichannel audio is not yet supported by a majority of browsers.
this._ambisonicOrder =
Encoder.validateAmbisonicOrder(options.ambisonicOrder);
// Create audio nodes.
this._context = context;
if (this._ambisonicOrder == 1) {
this._renderer = Omnitone.createFOARenderer(context, {});
}
else if (this._ambisonicOrder > 1) {
this._renderer = Omnitone.createHOARenderer(context, {
ambisonicOrder: this._ambisonicOrder,
});
}
// These nodes are created in order to safely asynchronously load Omnitone
// while the rest of the scene is being created.
this.input = context.createGain();
this.output = context.createGain();
this.ambisonicOutput = context.createGain();
// Initialize Omnitone (async) and connect to audio graph when complete.
let that = this;
this._renderer.initialize().then(() => {
// Connect pre-rotated soundfield to renderer.
that.input.connect(that._renderer.input);
// Connect rotated soundfield to ambisonic output.
if (that._ambisonicOrder > 1) {
that._renderer._hoaRotator.output.connect(that.ambisonicOutput);
}
else {
that._renderer._foaRotator.output.connect(that.ambisonicOutput);
}
// Connect binaurally-rendered soundfield to binaural output.
that._renderer.output.connect(that.output);
});
// Set orientation and update rotation matrix accordingly.
this.setOrientation(options.forward[0], options.forward[1], options.forward[2], options.up[0], options.up[1], options.up[2]);
}
/**
* Set the source's orientation using forward and up vectors.
* @param {Number} forwardX
* @param {Number} forwardY
* @param {Number} forwardZ
* @param {Number} upX
* @param {Number} upY
* @param {Number} upZ
*/
setOrientation(forwardX, forwardY, forwardZ, upX, upY, upZ) {
let right = Utils.crossProduct([forwardX, forwardY, forwardZ], [upX, upY, upZ]);
this._tempMatrix3[0] = right[0];
this._tempMatrix3[1] = right[1];
this._tempMatrix3[2] = right[2];
this._tempMatrix3[3] = upX;
this._tempMatrix3[4] = upY;
this._tempMatrix3[5] = upZ;
this._tempMatrix3[6] = forwardX;
this._tempMatrix3[7] = forwardY;
this._tempMatrix3[8] = forwardZ;
this._renderer.setRotationMatrix3(this._tempMatrix3);
}
/**
* Set the listener's position and orientation using a Three.js Matrix4 object.
* @param {Object} matrix4
* The Three.js Matrix4 object representing the listener's world transform.
*/
setFromMatrix(matrix4) {
// Update ambisonic rotation matrix internally.
this._renderer.setRotationMatrix4(matrix4.elements);
// Extract position from matrix.
this.position[0] = matrix4.elements[12];
this.position[1] = matrix4.elements[13];
this.position[2] = matrix4.elements[14];
}
}
export default Listener;

View File

@@ -0,0 +1,2 @@
export default ResonanceAudio;
import ResonanceAudio from "./resonance-audio";

View File

@@ -0,0 +1,23 @@
/**
* @license
* Copyright 2017 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file Primary namespace for ResonanceAudio library.
* @author Andrew Allen <bitllama@google.com>
*/
'use strict';
import ResonanceAudio from './resonance-audio';
// Main module.
export default ResonanceAudio;

View File

@@ -0,0 +1,130 @@
export default ResonanceAudio;
/**
* ~ResonanceAudioOptions
*/
export type ResonanceAudio = {
/**
* Desired ambisonic Order. Defaults to
* {@link Utils.DEFAULT_AMBISONIC_ORDER DEFAULT_AMBISONIC_ORDER}.
*/
ambisonicOrder: number;
/**
* The listener's initial position (in meters), where origin is the center of
* the room. Defaults to {@link Utils.DEFAULT_POSITION DEFAULT_POSITION}.
*/
listenerPosition: Float32Array;
/**
* The listener's initial forward vector.
* Defaults to {@link Utils.DEFAULT_FORWARD DEFAULT_FORWARD}.
*/
listenerForward: Float32Array;
/**
* The listener's initial up vector.
* Defaults to {@link Utils.DEFAULT_UP DEFAULT_UP}.
*/
listenerUp: Float32Array;
/**
* ~RoomDimensions} dimensions Room dimensions (in meters). Defaults to
* {@link Utils.DEFAULT_ROOM_DIMENSIONS DEFAULT_ROOM_DIMENSIONS}.
*/
"": Utils;
/**
* (in meters/second). Defaults to
* {@link Utils.DEFAULT_SPEED_OF_SOUND DEFAULT_SPEED_OF_SOUND}.
*/
speedOfSound: number;
};
/**
* Options for constructing a new ResonanceAudio scene.
* @typedef {Object} ResonanceAudio~ResonanceAudioOptions
* @property {Number} ambisonicOrder
* Desired ambisonic Order. Defaults to
* {@linkcode Utils.DEFAULT_AMBISONIC_ORDER DEFAULT_AMBISONIC_ORDER}.
* @property {Float32Array} listenerPosition
* The listener's initial position (in meters), where origin is the center of
* the room. Defaults to {@linkcode Utils.DEFAULT_POSITION DEFAULT_POSITION}.
* @property {Float32Array} listenerForward
* The listener's initial forward vector.
* Defaults to {@linkcode Utils.DEFAULT_FORWARD DEFAULT_FORWARD}.
* @property {Float32Array} listenerUp
* The listener's initial up vector.
* Defaults to {@linkcode Utils.DEFAULT_UP DEFAULT_UP}.
* @property {Utils~RoomDimensions} dimensions Room dimensions (in meters). Defaults to
* {@linkcode Utils.DEFAULT_ROOM_DIMENSIONS DEFAULT_ROOM_DIMENSIONS}.
* @property {Utils~RoomMaterials} materials Named acoustic materials per wall.
* Defaults to {@linkcode Utils.DEFAULT_ROOM_MATERIALS DEFAULT_ROOM_MATERIALS}.
* @property {Number} speedOfSound
* (in meters/second). Defaults to
* {@linkcode Utils.DEFAULT_SPEED_OF_SOUND DEFAULT_SPEED_OF_SOUND}.
*/
/**
* @class ResonanceAudio
* @description Main class for managing sources, room and listener models.
* @param {AudioContext} context
* Associated {@link
https://developer.mozilla.org/en-US/docs/Web/API/AudioContext AudioContext}.
* @param {ResonanceAudio~ResonanceAudioOptions} options
* Options for constructing a new ResonanceAudio scene.
*/
declare class ResonanceAudio {
constructor(context: any, options: any);
_ambisonicOrder: number;
_sources: any[];
_room: Room;
_listener: Listener;
_context: any;
output: any;
ambisonicOutput: any;
ambisonicInput: any;
/**
* Create a new source for the scene.
* @param {Source~SourceOptions} options
* Options for constructing a new Source.
* @return {Source}
*/
createSource(options: any): Source;
/**
* Set the scene's desired ambisonic order.
* @param {Number} ambisonicOrder Desired ambisonic order.
*/
setAmbisonicOrder(ambisonicOrder: number): void;
/**
* Set the room's dimensions and wall materials.
* @param {Object} dimensions Room dimensions (in meters).
* @param {Object} materials Named acoustic materials per wall.
*/
setRoomProperties(dimensions: any, materials: any): void;
/**
* Set the listener's position (in meters), where origin is the center of
* the room.
* @param {Number} x
* @param {Number} y
* @param {Number} z
*/
setListenerPosition(x: number, y: number, z: number): void;
/**
* Set the source's orientation using forward and up vectors.
* @param {Number} forwardX
* @param {Number} forwardY
* @param {Number} forwardZ
* @param {Number} upX
* @param {Number} upY
* @param {Number} upZ
*/
setListenerOrientation(forwardX: number, forwardY: number, forwardZ: number, upX: number, upY: number, upZ: number): void;
/**
* Set the listener's position and orientation using a Three.js Matrix4 object.
* @param {Object} matrix
* The Three.js Matrix4 object representing the listener's world transform.
*/
setListenerFromMatrix(matrix: any): void;
/**
* Set the speed of sound.
* @param {Number} speedOfSound
*/
setSpeedOfSound(speedOfSound: number): void;
}
import Utils from "./utils.js";
import Room from "./room.js";
import Listener from "./listener.js";
import Source from "./source.js";

View File

@@ -0,0 +1,213 @@
/**
* @license
* Copyright 2017 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file ResonanceAudio library name space and common utilities.
* @author Andrew Allen <bitllama@google.com>
*/
'use strict';
// Internal dependencies.
import Listener from './listener.js';
import Source from './source.js';
import Room from './room.js';
import Encoder from './encoder.js';
import Utils from './utils.js';
/**
* Options for constructing a new ResonanceAudio scene.
* @typedef {Object} ResonanceAudio~ResonanceAudioOptions
* @property {Number} ambisonicOrder
* Desired ambisonic Order. Defaults to
* {@linkcode Utils.DEFAULT_AMBISONIC_ORDER DEFAULT_AMBISONIC_ORDER}.
* @property {Float32Array} listenerPosition
* The listener's initial position (in meters), where origin is the center of
* the room. Defaults to {@linkcode Utils.DEFAULT_POSITION DEFAULT_POSITION}.
* @property {Float32Array} listenerForward
* The listener's initial forward vector.
* Defaults to {@linkcode Utils.DEFAULT_FORWARD DEFAULT_FORWARD}.
* @property {Float32Array} listenerUp
* The listener's initial up vector.
* Defaults to {@linkcode Utils.DEFAULT_UP DEFAULT_UP}.
* @property {Utils~RoomDimensions} dimensions Room dimensions (in meters). Defaults to
* {@linkcode Utils.DEFAULT_ROOM_DIMENSIONS DEFAULT_ROOM_DIMENSIONS}.
* @property {Utils~RoomMaterials} materials Named acoustic materials per wall.
* Defaults to {@linkcode Utils.DEFAULT_ROOM_MATERIALS DEFAULT_ROOM_MATERIALS}.
* @property {Number} speedOfSound
* (in meters/second). Defaults to
* {@linkcode Utils.DEFAULT_SPEED_OF_SOUND DEFAULT_SPEED_OF_SOUND}.
*/
/**
* @class ResonanceAudio
* @description Main class for managing sources, room and listener models.
* @param {AudioContext} context
* Associated {@link
https://developer.mozilla.org/en-US/docs/Web/API/AudioContext AudioContext}.
* @param {ResonanceAudio~ResonanceAudioOptions} options
* Options for constructing a new ResonanceAudio scene.
*/
class ResonanceAudio {
constructor(context, options) {
// Public variables.
/**
* Binaurally-rendered stereo (2-channel) output {@link
* https://developer.mozilla.org/en-US/docs/Web/API/AudioNode AudioNode}.
* @member {AudioNode} output
* @memberof ResonanceAudio
* @instance
*/
/**
* Ambisonic (multichannel) input {@link
* https://developer.mozilla.org/en-US/docs/Web/API/AudioNode AudioNode}
* (For rendering input soundfields).
* @member {AudioNode} ambisonicInput
* @memberof ResonanceAudio
* @instance
*/
/**
* Ambisonic (multichannel) output {@link
* https://developer.mozilla.org/en-US/docs/Web/API/AudioNode AudioNode}
* (For allowing external rendering / post-processing).
* @member {AudioNode} ambisonicOutput
* @memberof ResonanceAudio
* @instance
*/
// Use defaults for undefined arguments.
if (options == undefined) {
options = {};
}
if (options.ambisonicOrder == undefined) {
options.ambisonicOrder = Utils.DEFAULT_AMBISONIC_ORDER;
}
if (options.listenerPosition == undefined) {
options.listenerPosition = Utils.DEFAULT_POSITION.slice();
}
if (options.listenerForward == undefined) {
options.listenerForward = Utils.DEFAULT_FORWARD.slice();
}
if (options.listenerUp == undefined) {
options.listenerUp = Utils.DEFAULT_UP.slice();
}
if (options.dimensions == undefined) {
options.dimensions = {};
Object.assign(options.dimensions, Utils.DEFAULT_ROOM_DIMENSIONS);
}
if (options.materials == undefined) {
options.materials = {};
Object.assign(options.materials, Utils.DEFAULT_ROOM_MATERIALS);
}
if (options.speedOfSound == undefined) {
options.speedOfSound = Utils.DEFAULT_SPEED_OF_SOUND;
}
// Create member submodules.
this._ambisonicOrder = Encoder.validateAmbisonicOrder(options.ambisonicOrder);
this._sources = [];
this._room = new Room(context, {
listenerPosition: options.listenerPosition,
dimensions: options.dimensions,
materials: options.materials,
speedOfSound: options.speedOfSound,
});
this._listener = new Listener(context, {
ambisonicOrder: options.ambisonicOrder,
position: options.listenerPosition,
forward: options.listenerForward,
up: options.listenerUp,
});
// Create auxillary audio nodes.
this._context = context;
this.output = context.createGain();
this.ambisonicOutput = context.createGain();
this.ambisonicInput = this._listener.input;
// Connect audio graph.
this._room.output.connect(this._listener.input);
this._listener.output.connect(this.output);
this._listener.ambisonicOutput.connect(this.ambisonicOutput);
}
/**
* Create a new source for the scene.
* @param {Source~SourceOptions} options
* Options for constructing a new Source.
* @return {Source}
*/
createSource(options) {
// Create a source and push it to the internal sources array, returning
// the object's reference to the user.
let source = new Source(this, options);
this._sources[this._sources.length] = source;
return source;
}
/**
* Set the scene's desired ambisonic order.
* @param {Number} ambisonicOrder Desired ambisonic order.
*/
setAmbisonicOrder(ambisonicOrder) {
this._ambisonicOrder = Encoder.validateAmbisonicOrder(ambisonicOrder);
}
/**
* Set the room's dimensions and wall materials.
* @param {Object} dimensions Room dimensions (in meters).
* @param {Object} materials Named acoustic materials per wall.
*/
setRoomProperties(dimensions, materials) {
this._room.setProperties(dimensions, materials);
}
/**
* Set the listener's position (in meters), where origin is the center of
* the room.
* @param {Number} x
* @param {Number} y
* @param {Number} z
*/
setListenerPosition(x, y, z) {
// Update listener position.
this._listener.position[0] = x;
this._listener.position[1] = y;
this._listener.position[2] = z;
this._room.setListenerPosition(x, y, z);
// Update sources with new listener position.
this._sources.forEach(element => {
element._update();
});
}
/**
* Set the source's orientation using forward and up vectors.
* @param {Number} forwardX
* @param {Number} forwardY
* @param {Number} forwardZ
* @param {Number} upX
* @param {Number} upY
* @param {Number} upZ
*/
setListenerOrientation(forwardX, forwardY, forwardZ, upX, upY, upZ) {
this._listener.setOrientation(forwardX, forwardY, forwardZ, upX, upY, upZ);
}
/**
* Set the listener's position and orientation using a Three.js Matrix4 object.
* @param {Object} matrix
* The Three.js Matrix4 object representing the listener's world transform.
*/
setListenerFromMatrix(matrix) {
this._listener.setFromMatrix(matrix);
// Update the rest of the scene using new listener position.
this.setListenerPosition(this._listener.position[0], this._listener.position[1], this._listener.position[2]);
}
/**
* Set the speed of sound.
* @param {Number} speedOfSound
*/
setSpeedOfSound(speedOfSound) {
this._room.speedOfSound = speedOfSound;
}
}
export default ResonanceAudio;

View File

@@ -0,0 +1,55 @@
export default Room;
/**
* @class Room
* @description Model that manages early and late reflections using acoustic
* properties and listener position relative to a rectangular room.
* @param {AudioContext} context
* Associated {@link
https://developer.mozilla.org/en-US/docs/Web/API/AudioContext AudioContext}.
* @param {Object} options
* @param {Float32Array} options.listenerPosition
* The listener's initial position (in meters), where origin is the center of
* the room. Defaults to {@linkcode Utils.DEFAULT_POSITION DEFAULT_POSITION}.
* @param {Utils~RoomDimensions} options.dimensions Room dimensions (in meters). Defaults to
* {@linkcode Utils.DEFAULT_ROOM_DIMENSIONS DEFAULT_ROOM_DIMENSIONS}.
* @param {Utils~RoomMaterials} options.materials Named acoustic materials per wall.
* Defaults to {@linkcode Utils.DEFAULT_ROOM_MATERIALS DEFAULT_ROOM_MATERIALS}.
* @param {Number} options.speedOfSound
* (in meters/second). Defaults to
* {@linkcode Utils.DEFAULT_SPEED_OF_SOUND DEFAULT_SPEED_OF_SOUND}.
*/
declare class Room {
constructor(context: any, options: any);
early: EarlyReflections;
late: LateReflections;
speedOfSound: any;
output: any;
_merger: any;
/**
* Set the room's dimensions and wall materials.
* @param {Utils~RoomDimensions} dimensions Room dimensions (in meters). Defaults to
* {@linkcode Utils.DEFAULT_ROOM_DIMENSIONS DEFAULT_ROOM_DIMENSIONS}.
* @param {Utils~RoomMaterials} materials Named acoustic materials per wall. Defaults to
* {@linkcode Utils.DEFAULT_ROOM_MATERIALS DEFAULT_ROOM_MATERIALS}.
*/
setProperties(dimensions: any, materials: any): void;
/**
* Set the listener's position (in meters), where origin is the center of
* the room.
* @param {Number} x
* @param {Number} y
* @param {Number} z
*/
setListenerPosition(x: number, y: number, z: number): void;
/**
* Compute distance outside room of provided position (in meters).
* @param {Number} x
* @param {Number} y
* @param {Number} z
* @return {Number}
* Distance outside room (in meters). Returns 0 if inside room.
*/
getDistanceOutsideRoom(x: number, y: number, z: number): number;
}
import EarlyReflections from "./early-reflections.js";
import LateReflections from "./late-reflections.js";

View File

@@ -0,0 +1,300 @@
/**
* @license
* Copyright 2017 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file Complete room model with early and late reflections.
* @author Andrew Allen <bitllama@google.com>
*/
'use strict';
// Internal dependencies.
import LateReflections from './late-reflections.js';
import EarlyReflections from './early-reflections.js';
import Utils from './utils.js';
/**
* Generate absorption coefficients from material names.
* @param {Object} materials
* @return {Object}
*/
function _getCoefficientsFromMaterials(materials) {
// Initialize coefficients to use defaults.
let coefficients = {};
for (let property in Utils.DEFAULT_ROOM_MATERIALS) {
if (Utils.DEFAULT_ROOM_MATERIALS.hasOwnProperty(property)) {
coefficients[property] = Utils.ROOM_MATERIAL_COEFFICIENTS[Utils.DEFAULT_ROOM_MATERIALS[property]];
}
}
// Sanitize materials.
if (materials == undefined) {
materials = {};
Object.assign(materials, Utils.DEFAULT_ROOM_MATERIALS);
}
// Assign coefficients using provided materials.
for (let property in Utils.DEFAULT_ROOM_MATERIALS) {
if (Utils.DEFAULT_ROOM_MATERIALS.hasOwnProperty(property) &&
materials.hasOwnProperty(property)) {
if (materials[property] in Utils.ROOM_MATERIAL_COEFFICIENTS) {
coefficients[property] =
Utils.ROOM_MATERIAL_COEFFICIENTS[materials[property]];
}
else {
Utils.log('Material \"' + materials[property] + '\" on wall \"' +
property + '\" not found. Using \"' +
Utils.DEFAULT_ROOM_MATERIALS[property] + '\".');
}
}
else {
Utils.log('Wall \"' + property + '\" is not defined. Default used.');
}
}
return coefficients;
}
/**
* Sanitize coefficients.
* @param {Object} coefficients
* @return {Object}
*/
function _sanitizeCoefficients(coefficients) {
if (coefficients == undefined) {
coefficients = {};
}
for (let property in Utils.DEFAULT_ROOM_MATERIALS) {
if (!(coefficients.hasOwnProperty(property))) {
// If element is not present, use default coefficients.
coefficients[property] = Utils.ROOM_MATERIAL_COEFFICIENTS[Utils.DEFAULT_ROOM_MATERIALS[property]];
}
}
return coefficients;
}
/**
* Sanitize dimensions.
* @param {Utils~RoomDimensions} dimensions
* @return {Utils~RoomDimensions}
*/
function _sanitizeDimensions(dimensions) {
if (dimensions == undefined) {
dimensions = {};
}
for (let property in Utils.DEFAULT_ROOM_DIMENSIONS) {
if (!(dimensions.hasOwnProperty(property))) {
dimensions[property] = Utils.DEFAULT_ROOM_DIMENSIONS[property];
}
}
return dimensions;
}
/**
* Compute frequency-dependent reverb durations.
* @param {Utils~RoomDimensions} dimensions
* @param {Object} coefficients
* @param {Number} speedOfSound
* @return {Array}
*/
function _getDurationsFromProperties(dimensions, coefficients, speedOfSound) {
let durations = new Float32Array(Utils.NUMBER_REVERB_FREQUENCY_BANDS);
// Sanitize inputs.
dimensions = _sanitizeDimensions(dimensions);
coefficients = _sanitizeCoefficients(coefficients);
if (speedOfSound == undefined) {
speedOfSound = Utils.DEFAULT_SPEED_OF_SOUND;
}
// Acoustic constant.
let k = Utils.TWENTY_FOUR_LOG10 / speedOfSound;
// Compute volume, skip if room is not present.
let volume = dimensions.width * dimensions.height * dimensions.depth;
if (volume < Utils.ROOM_MIN_VOLUME) {
return durations;
}
// Room surface area.
let leftRightArea = dimensions.width * dimensions.height;
let floorCeilingArea = dimensions.width * dimensions.depth;
let frontBackArea = dimensions.depth * dimensions.height;
let totalArea = 2 * (leftRightArea + floorCeilingArea + frontBackArea);
for (let i = 0; i < Utils.NUMBER_REVERB_FREQUENCY_BANDS; i++) {
// Effective absorptive area.
let absorbtionArea = (coefficients.left[i] + coefficients.right[i]) * leftRightArea +
(coefficients.down[i] + coefficients.up[i]) * floorCeilingArea +
(coefficients.front[i] + coefficients.back[i]) * frontBackArea;
let meanAbsorbtionArea = absorbtionArea / totalArea;
// Compute reverberation using Eyring equation [1].
// [1] Beranek, Leo L. "Analysis of Sabine and Eyring equations and their
// application to concert hall audience and chair absorption." The
// Journal of the Acoustical Society of America, Vol. 120, No. 3.
// (2006), pp. 1399-1399.
durations[i] = Utils.ROOM_EYRING_CORRECTION_COEFFICIENT * k * volume /
(-totalArea * Math.log(1 - meanAbsorbtionArea) + 4 *
Utils.ROOM_AIR_ABSORPTION_COEFFICIENTS[i] * volume);
}
return durations;
}
/**
* Compute reflection coefficients from absorption coefficients.
* @param {Object} absorptionCoefficients
* @return {Object}
*/
function _computeReflectionCoefficients(absorptionCoefficients) {
let reflectionCoefficients = [];
for (let property in Utils.DEFAULT_REFLECTION_COEFFICIENTS) {
if (Utils.DEFAULT_REFLECTION_COEFFICIENTS
.hasOwnProperty(property)) {
// Compute average absorption coefficient (per wall).
reflectionCoefficients[property] = 0;
for (let j = 0; j < Utils.NUMBER_REFLECTION_AVERAGING_BANDS; j++) {
let bandIndex = j + Utils.ROOM_STARTING_AVERAGING_BAND;
reflectionCoefficients[property] +=
absorptionCoefficients[property][bandIndex];
}
reflectionCoefficients[property] /=
Utils.NUMBER_REFLECTION_AVERAGING_BANDS;
// Convert absorption coefficient to reflection coefficient.
reflectionCoefficients[property] =
Math.sqrt(1 - reflectionCoefficients[property]);
}
}
return reflectionCoefficients;
}
/**
* @class Room
* @description Model that manages early and late reflections using acoustic
* properties and listener position relative to a rectangular room.
* @param {AudioContext} context
* Associated {@link
https://developer.mozilla.org/en-US/docs/Web/API/AudioContext AudioContext}.
* @param {Object} options
* @param {Float32Array} options.listenerPosition
* The listener's initial position (in meters), where origin is the center of
* the room. Defaults to {@linkcode Utils.DEFAULT_POSITION DEFAULT_POSITION}.
* @param {Utils~RoomDimensions} options.dimensions Room dimensions (in meters). Defaults to
* {@linkcode Utils.DEFAULT_ROOM_DIMENSIONS DEFAULT_ROOM_DIMENSIONS}.
* @param {Utils~RoomMaterials} options.materials Named acoustic materials per wall.
* Defaults to {@linkcode Utils.DEFAULT_ROOM_MATERIALS DEFAULT_ROOM_MATERIALS}.
* @param {Number} options.speedOfSound
* (in meters/second). Defaults to
* {@linkcode Utils.DEFAULT_SPEED_OF_SOUND DEFAULT_SPEED_OF_SOUND}.
*/
class Room {
constructor(context, options) {
// Public variables.
/**
* EarlyReflections {@link EarlyReflections EarlyReflections} submodule.
* @member {AudioNode} early
* @memberof Room
* @instance
*/
/**
* LateReflections {@link LateReflections LateReflections} submodule.
* @member {AudioNode} late
* @memberof Room
* @instance
*/
/**
* Ambisonic (multichannel) output {@link
* https://developer.mozilla.org/en-US/docs/Web/API/AudioNode AudioNode}.
* @member {AudioNode} output
* @memberof Room
* @instance
*/
// Use defaults for undefined arguments.
if (options == undefined) {
options = {};
}
if (options.listenerPosition == undefined) {
options.listenerPosition = Utils.DEFAULT_POSITION.slice();
}
if (options.dimensions == undefined) {
options.dimensions = {};
Object.assign(options.dimensions, Utils.DEFAULT_ROOM_DIMENSIONS);
}
if (options.materials == undefined) {
options.materials = {};
Object.assign(options.materials, Utils.DEFAULT_ROOM_MATERIALS);
}
if (options.speedOfSound == undefined) {
options.speedOfSound = Utils.DEFAULT_SPEED_OF_SOUND;
}
// Sanitize room-properties-related arguments.
options.dimensions = _sanitizeDimensions(options.dimensions);
let absorptionCoefficients = _getCoefficientsFromMaterials(options.materials);
let reflectionCoefficients = _computeReflectionCoefficients(absorptionCoefficients);
let durations = _getDurationsFromProperties(options.dimensions, absorptionCoefficients, options.speedOfSound);
// Construct submodules for early and late reflections.
this.early = new EarlyReflections(context, {
dimensions: options.dimensions,
coefficients: reflectionCoefficients,
speedOfSound: options.speedOfSound,
listenerPosition: options.listenerPosition,
});
this.late = new LateReflections(context, {
durations: durations,
});
this.speedOfSound = options.speedOfSound;
// Construct auxillary audio nodes.
this.output = context.createGain();
this.early.output.connect(this.output);
this._merger = context.createChannelMerger(4);
this.late.output.connect(this._merger, 0, 0);
this._merger.connect(this.output);
}
/**
* Set the room's dimensions and wall materials.
* @param {Utils~RoomDimensions} dimensions Room dimensions (in meters). Defaults to
* {@linkcode Utils.DEFAULT_ROOM_DIMENSIONS DEFAULT_ROOM_DIMENSIONS}.
* @param {Utils~RoomMaterials} materials Named acoustic materials per wall. Defaults to
* {@linkcode Utils.DEFAULT_ROOM_MATERIALS DEFAULT_ROOM_MATERIALS}.
*/
setProperties(dimensions, materials) {
// Compute late response.
let absorptionCoefficients = _getCoefficientsFromMaterials(materials);
let durations = _getDurationsFromProperties(dimensions, absorptionCoefficients, this.speedOfSound);
this.late.setDurations(durations);
// Compute early response.
this.early.speedOfSound = this.speedOfSound;
let reflectionCoefficients = _computeReflectionCoefficients(absorptionCoefficients);
this.early.setRoomProperties(dimensions, reflectionCoefficients);
}
/**
* Set the listener's position (in meters), where origin is the center of
* the room.
* @param {Number} x
* @param {Number} y
* @param {Number} z
*/
setListenerPosition(x, y, z) {
this.early.speedOfSound = this.speedOfSound;
this.early.setListenerPosition(x, y, z);
// Disable room effects if the listener is outside the room boundaries.
let distance = this.getDistanceOutsideRoom(x, y, z);
let gain = 1;
if (distance > Utils.EPSILON_FLOAT) {
gain = 1 - distance / Utils.LISTENER_MAX_OUTSIDE_ROOM_DISTANCE;
// Clamp gain between 0 and 1.
gain = Math.max(0, Math.min(1, gain));
}
this.output.gain.value = gain;
}
/**
* Compute distance outside room of provided position (in meters).
* @param {Number} x
* @param {Number} y
* @param {Number} z
* @return {Number}
* Distance outside room (in meters). Returns 0 if inside room.
*/
getDistanceOutsideRoom(x, y, z) {
let dx = Math.max(0, -this.early._halfDimensions.width - x, x - this.early._halfDimensions.width);
let dy = Math.max(0, -this.early._halfDimensions.height - y, y - this.early._halfDimensions.height);
let dz = Math.max(0, -this.early._halfDimensions.depth - z, z - this.early._halfDimensions.depth);
return Math.sqrt(dx * dx + dy * dy + dz * dz);
}
}
export default Room;

View File

@@ -0,0 +1,182 @@
export default Source;
/**
* ~SourceOptions
*/
export type Source = {
/**
* The source's initial position (in meters), where origin is the center of
* the room. Defaults to {@link Utils.DEFAULT_POSITION DEFAULT_POSITION}.
*/
position: Float32Array;
/**
* The source's initial forward vector. Defaults to
* {@link Utils.DEFAULT_FORWARD DEFAULT_FORWARD}.
*/
forward: Float32Array;
/**
* The source's initial up vector. Defaults to
* {@link Utils.DEFAULT_UP DEFAULT_UP}.
*/
up: Float32Array;
/**
* Min. distance (in meters). Defaults to
* {@link Utils.DEFAULT_MIN_DISTANCE DEFAULT_MIN_DISTANCE}.
*/
minDistance: number;
/**
* Max. distance (in meters). Defaults to
* {@link Utils.DEFAULT_MAX_DISTANCE DEFAULT_MAX_DISTANCE}.
*/
maxDistance: number;
/**
* Rolloff model to use, chosen from options in
* {@link Utils.ATTENUATION_ROLLOFFS ATTENUATION_ROLLOFFS}. Defaults to
* {@link Utils.DEFAULT_ATTENUATION_ROLLOFF DEFAULT_ATTENUATION_ROLLOFF}.
*/
rolloff: string;
/**
* Input gain (linear). Defaults to
* {@link Utils.DEFAULT_SOURCE_GAIN DEFAULT_SOURCE_GAIN}.
*/
gain: number;
/**
* Directivity alpha. Defaults to
* {@link Utils.DEFAULT_DIRECTIVITY_ALPHA DEFAULT_DIRECTIVITY_ALPHA}.
*/
alpha: number;
/**
* Directivity sharpness. Defaults to
* {@link Utils.DEFAULT_DIRECTIVITY_SHARPNESS * DEFAULT_DIRECTIVITY_SHARPNESS}.
*/
sharpness: number;
/**
* Source width (in degrees). Where 0 degrees is a point source and 360 degrees
* is an omnidirectional source. Defaults to
* {@link Utils.DEFAULT_SOURCE_WIDTH DEFAULT_SOURCE_WIDTH}.
*/
sourceWidth: number;
};
/**
* Options for constructing a new Source.
* @typedef {Object} Source~SourceOptions
* @property {Float32Array} position
* The source's initial position (in meters), where origin is the center of
* the room. Defaults to {@linkcode Utils.DEFAULT_POSITION DEFAULT_POSITION}.
* @property {Float32Array} forward
* The source's initial forward vector. Defaults to
* {@linkcode Utils.DEFAULT_FORWARD DEFAULT_FORWARD}.
* @property {Float32Array} up
* The source's initial up vector. Defaults to
* {@linkcode Utils.DEFAULT_UP DEFAULT_UP}.
* @property {Number} minDistance
* Min. distance (in meters). Defaults to
* {@linkcode Utils.DEFAULT_MIN_DISTANCE DEFAULT_MIN_DISTANCE}.
* @property {Number} maxDistance
* Max. distance (in meters). Defaults to
* {@linkcode Utils.DEFAULT_MAX_DISTANCE DEFAULT_MAX_DISTANCE}.
* @property {string} rolloff
* Rolloff model to use, chosen from options in
* {@linkcode Utils.ATTENUATION_ROLLOFFS ATTENUATION_ROLLOFFS}. Defaults to
* {@linkcode Utils.DEFAULT_ATTENUATION_ROLLOFF DEFAULT_ATTENUATION_ROLLOFF}.
* @property {Number} gain Input gain (linear). Defaults to
* {@linkcode Utils.DEFAULT_SOURCE_GAIN DEFAULT_SOURCE_GAIN}.
* @property {Number} alpha Directivity alpha. Defaults to
* {@linkcode Utils.DEFAULT_DIRECTIVITY_ALPHA DEFAULT_DIRECTIVITY_ALPHA}.
* @property {Number} sharpness Directivity sharpness. Defaults to
* {@linkcode Utils.DEFAULT_DIRECTIVITY_SHARPNESS
* DEFAULT_DIRECTIVITY_SHARPNESS}.
* @property {Number} sourceWidth
* Source width (in degrees). Where 0 degrees is a point source and 360 degrees
* is an omnidirectional source. Defaults to
* {@linkcode Utils.DEFAULT_SOURCE_WIDTH DEFAULT_SOURCE_WIDTH}.
*/
/**
* @class Source
* @description Source model to spatialize an audio buffer.
* @param {ResonanceAudio} scene Associated {@link ResonanceAudio
* ResonanceAudio} instance.
* @param {Source~SourceOptions} options
* Options for constructing a new Source.
*/
declare class Source {
constructor(scene: any, options: any);
_scene: any;
_position: any;
_forward: any;
_up: any;
_dx: Float32Array;
_right: Float32Array;
input: any;
_directivity: Directivity;
_toEarly: any;
_toLate: any;
_attenuation: Attenuation;
_encoder: Encoder;
/**
* Set source's position (in meters), where origin is the center of
* the room.
* @param {Number} x
* @param {Number} y
* @param {Number} z
*/
setPosition(x: number, y: number, z: number): void;
_update(): void;
/**
* Set source's rolloff.
* @param {string} rolloff
* Rolloff model to use, chosen from options in
* {@linkcode Utils.ATTENUATION_ROLLOFFS ATTENUATION_ROLLOFFS}.
*/
setRolloff(rolloff: string): void;
/**
* Set source's minimum distance (in meters).
* @param {Number} minDistance
*/
setMinDistance(minDistance: number): void;
/**
* Set source's maximum distance (in meters).
* @param {Number} maxDistance
*/
setMaxDistance(maxDistance: number): void;
/**
* Set source's gain (linear).
* @param {Number} gain
*/
setGain(gain: number): void;
/**
* Set the source's orientation using forward and up vectors.
* @param {Number} forwardX
* @param {Number} forwardY
* @param {Number} forwardZ
* @param {Number} upX
* @param {Number} upY
* @param {Number} upZ
*/
setOrientation(forwardX: number, forwardY: number, forwardZ: number, upX: number, upY: number, upZ: number): void;
/**
* Set source's position and orientation using a
* Three.js modelViewMatrix object.
* @param {Float32Array} matrix4
* The Matrix4 representing the object position and rotation in world space.
*/
setFromMatrix(matrix4: Float32Array): void;
/**
* Set the source width (in degrees). Where 0 degrees is a point source and 360
* degrees is an omnidirectional source.
* @param {Number} sourceWidth (in degrees).
*/
setSourceWidth(sourceWidth: number): void;
/**
* Set source's directivity pattern (defined by alpha), where 0 is an
* omnidirectional pattern, 1 is a bidirectional pattern, 0.5 is a cardiod
* pattern. The sharpness of the pattern is increased exponentially.
* @param {Number} alpha
* Determines directivity pattern (0 to 1).
* @param {Number} sharpness
* Determines the sharpness of the directivity pattern (1 to Inf).
*/
setDirectivityPattern(alpha: number, sharpness: number): void;
}
import Directivity from "./directivity.js";
import Attenuation from "./attenuation.js";
import Encoder from "./encoder.js";

View File

@@ -0,0 +1,308 @@
/**
* @license
* Copyright 2017 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file Source model to spatialize an audio buffer.
* @author Andrew Allen <bitllama@google.com>
*/
'use strict';
// Internal dependencies.
import Directivity from './directivity.js';
import Attenuation from './attenuation.js';
import Encoder from './encoder.js';
import Utils from './utils.js';
/**
* Options for constructing a new Source.
* @typedef {Object} Source~SourceOptions
* @property {Float32Array} position
* The source's initial position (in meters), where origin is the center of
* the room. Defaults to {@linkcode Utils.DEFAULT_POSITION DEFAULT_POSITION}.
* @property {Float32Array} forward
* The source's initial forward vector. Defaults to
* {@linkcode Utils.DEFAULT_FORWARD DEFAULT_FORWARD}.
* @property {Float32Array} up
* The source's initial up vector. Defaults to
* {@linkcode Utils.DEFAULT_UP DEFAULT_UP}.
* @property {Number} minDistance
* Min. distance (in meters). Defaults to
* {@linkcode Utils.DEFAULT_MIN_DISTANCE DEFAULT_MIN_DISTANCE}.
* @property {Number} maxDistance
* Max. distance (in meters). Defaults to
* {@linkcode Utils.DEFAULT_MAX_DISTANCE DEFAULT_MAX_DISTANCE}.
* @property {string} rolloff
* Rolloff model to use, chosen from options in
* {@linkcode Utils.ATTENUATION_ROLLOFFS ATTENUATION_ROLLOFFS}. Defaults to
* {@linkcode Utils.DEFAULT_ATTENUATION_ROLLOFF DEFAULT_ATTENUATION_ROLLOFF}.
* @property {Number} gain Input gain (linear). Defaults to
* {@linkcode Utils.DEFAULT_SOURCE_GAIN DEFAULT_SOURCE_GAIN}.
* @property {Number} alpha Directivity alpha. Defaults to
* {@linkcode Utils.DEFAULT_DIRECTIVITY_ALPHA DEFAULT_DIRECTIVITY_ALPHA}.
* @property {Number} sharpness Directivity sharpness. Defaults to
* {@linkcode Utils.DEFAULT_DIRECTIVITY_SHARPNESS
* DEFAULT_DIRECTIVITY_SHARPNESS}.
* @property {Number} sourceWidth
* Source width (in degrees). Where 0 degrees is a point source and 360 degrees
* is an omnidirectional source. Defaults to
* {@linkcode Utils.DEFAULT_SOURCE_WIDTH DEFAULT_SOURCE_WIDTH}.
*/
/**
* @class Source
* @description Source model to spatialize an audio buffer.
* @param {ResonanceAudio} scene Associated {@link ResonanceAudio
* ResonanceAudio} instance.
* @param {Source~SourceOptions} options
* Options for constructing a new Source.
*/
class Source {
constructor(scene, options) {
// Public variables.
/**
* Mono (1-channel) input {@link
* https://developer.mozilla.org/en-US/docs/Web/API/AudioNode AudioNode}.
* @member {AudioNode} input
* @memberof Source
* @instance
*/
/**
*
*/
// Use defaults for undefined arguments.
if (options == undefined) {
options = {};
}
if (options.position == undefined) {
options.position = Utils.DEFAULT_POSITION.slice();
}
if (options.forward == undefined) {
options.forward = Utils.DEFAULT_FORWARD.slice();
}
if (options.up == undefined) {
options.up = Utils.DEFAULT_UP.slice();
}
if (options.minDistance == undefined) {
options.minDistance = Utils.DEFAULT_MIN_DISTANCE;
}
if (options.maxDistance == undefined) {
options.maxDistance = Utils.DEFAULT_MAX_DISTANCE;
}
if (options.rolloff == undefined) {
options.rolloff = Utils.DEFAULT_ROLLOFF;
}
if (options.gain == undefined) {
options.gain = Utils.DEFAULT_SOURCE_GAIN;
}
if (options.alpha == undefined) {
options.alpha = Utils.DEFAULT_DIRECTIVITY_ALPHA;
}
if (options.sharpness == undefined) {
options.sharpness = Utils.DEFAULT_DIRECTIVITY_SHARPNESS;
}
if (options.sourceWidth == undefined) {
options.sourceWidth = Utils.DEFAULT_SOURCE_WIDTH;
}
// Member variables.
this._scene = scene;
this._position = options.position;
this._forward = options.forward;
this._up = options.up;
this._dx = new Float32Array(3);
this._right = Utils.crossProduct(this._forward, this._up);
// Create audio nodes.
let context = scene._context;
this.input = context.createGain();
this._directivity = new Directivity(context, {
alpha: options.alpha,
sharpness: options.sharpness,
});
this._toEarly = context.createGain();
this._toLate = context.createGain();
this._attenuation = new Attenuation(context, {
minDistance: options.minDistance,
maxDistance: options.maxDistance,
rolloff: options.rolloff,
});
this._encoder = new Encoder(context, {
ambisonicOrder: scene._ambisonicOrder,
sourceWidth: options.sourceWidth,
});
// Connect nodes.
this.input.connect(this._toLate);
this._toLate.connect(scene._room.late.input);
this.input.connect(this._attenuation.input);
this._attenuation.output.connect(this._toEarly);
this._toEarly.connect(scene._room.early.input);
this._attenuation.output.connect(this._directivity.input);
this._directivity.output.connect(this._encoder.input);
this._encoder.output.connect(scene._listener.input);
// Assign initial conditions.
this.setPosition(options.position[0], options.position[1], options.position[2]);
this.input.gain.value = options.gain;
}
/**
* Set source's position (in meters), where origin is the center of
* the room.
* @param {Number} x
* @param {Number} y
* @param {Number} z
*/
setPosition(x, y, z) {
// Assign new position.
this._position[0] = x;
this._position[1] = y;
this._position[2] = z;
// Handle far-field effect.
let distance = this._scene._room.getDistanceOutsideRoom(this._position[0], this._position[1], this._position[2]);
let gain = _computeDistanceOutsideRoom(distance);
this._toLate.gain.value = gain;
this._toEarly.gain.value = gain;
this._update();
}
// Update the source when changing the listener's position.
_update() {
// Compute distance to listener.
for (let i = 0; i < 3; i++) {
this._dx[i] = this._position[i] - this._scene._listener.position[i];
}
let distance = Math.sqrt(this._dx[0] * this._dx[0] +
this._dx[1] * this._dx[1] + this._dx[2] * this._dx[2]);
if (distance > 0) {
// Normalize direction vector.
this._dx[0] /= distance;
this._dx[1] /= distance;
this._dx[2] /= distance;
}
// Compuete angle of direction vector.
let azimuth = Math.atan2(-this._dx[0], this._dx[2]) *
Utils.RADIANS_TO_DEGREES;
let elevation = Math.atan2(this._dx[1], Math.sqrt(this._dx[0] * this._dx[0] +
this._dx[2] * this._dx[2])) * Utils.RADIANS_TO_DEGREES;
// Set distance/directivity/direction values.
this._attenuation.setDistance(distance);
this._directivity.computeAngle(this._forward, this._dx);
this._encoder.setDirection(azimuth, elevation);
}
/**
* Set source's rolloff.
* @param {string} rolloff
* Rolloff model to use, chosen from options in
* {@linkcode Utils.ATTENUATION_ROLLOFFS ATTENUATION_ROLLOFFS}.
*/
setRolloff(rolloff) {
this._attenuation.setRolloff(rolloff);
}
/**
* Set source's minimum distance (in meters).
* @param {Number} minDistance
*/
setMinDistance(minDistance) {
this._attenuation.minDistance = minDistance;
}
/**
* Set source's maximum distance (in meters).
* @param {Number} maxDistance
*/
setMaxDistance(maxDistance) {
this._attenuation.maxDistance = maxDistance;
}
/**
* Set source's gain (linear).
* @param {Number} gain
*/
setGain(gain) {
this.input.gain.value = gain;
}
/**
* Set the source's orientation using forward and up vectors.
* @param {Number} forwardX
* @param {Number} forwardY
* @param {Number} forwardZ
* @param {Number} upX
* @param {Number} upY
* @param {Number} upZ
*/
setOrientation(forwardX, forwardY, forwardZ, upX, upY, upZ) {
this._forward[0] = forwardX;
this._forward[1] = forwardY;
this._forward[2] = forwardZ;
this._up[0] = upX;
this._up[1] = upY;
this._up[2] = upZ;
this._right = Utils.crossProduct(this._forward, this._up);
}
// TODO(bitllama): Make sure this works with Three.js as intended.
/**
* Set source's position and orientation using a
* Three.js modelViewMatrix object.
* @param {Float32Array} matrix4
* The Matrix4 representing the object position and rotation in world space.
*/
setFromMatrix(matrix4) {
this._right[0] = matrix4.elements[0];
this._right[1] = matrix4.elements[1];
this._right[2] = matrix4.elements[2];
this._up[0] = matrix4.elements[4];
this._up[1] = matrix4.elements[5];
this._up[2] = matrix4.elements[6];
this._forward[0] = matrix4.elements[8];
this._forward[1] = matrix4.elements[9];
this._forward[2] = matrix4.elements[10];
// Normalize to remove scaling.
this._right = Utils.normalizeVector(this._right);
this._up = Utils.normalizeVector(this._up);
this._forward = Utils.normalizeVector(this._forward);
// Update position.
this.setPosition(matrix4.elements[12], matrix4.elements[13], matrix4.elements[14]);
}
/**
* Set the source width (in degrees). Where 0 degrees is a point source and 360
* degrees is an omnidirectional source.
* @param {Number} sourceWidth (in degrees).
*/
setSourceWidth(sourceWidth) {
this._encoder.setSourceWidth(sourceWidth);
this.setPosition(this._position[0], this._position[1], this._position[2]);
}
/**
* Set source's directivity pattern (defined by alpha), where 0 is an
* omnidirectional pattern, 1 is a bidirectional pattern, 0.5 is a cardiod
* pattern. The sharpness of the pattern is increased exponentially.
* @param {Number} alpha
* Determines directivity pattern (0 to 1).
* @param {Number} sharpness
* Determines the sharpness of the directivity pattern (1 to Inf).
*/
setDirectivityPattern(alpha, sharpness) {
this._directivity.setPattern(alpha, sharpness);
this.setPosition(this._position[0], this._position[1], this._position[2]);
}
}
/**
* Determine the distance a source is outside of a room. Attenuate gain going
* to the reflections and reverb when the source is outside of the room.
* @param {Number} distance Distance in meters.
* @return {Number} Gain (linear) of source.
* @private
*/
function _computeDistanceOutsideRoom(distance) {
// We apply a linear ramp from 1 to 0 as the source is up to 1m outside.
let gain = 1;
if (distance > Utils.EPSILON_FLOAT) {
gain = 1 - distance / Utils.SOURCE_MAX_OUTSIDE_ROOM_DISTANCE;
// Clamp gain between 0 and 1.
gain = Math.max(0, Math.min(1, gain));
}
return gain;
}
export default Source;

View File

@@ -0,0 +1,38 @@
declare namespace _default {
export { SPHERICAL_HARMONICS };
export { SPHERICAL_HARMONICS_AZIMUTH_RESOLUTION };
export { SPHERICAL_HARMONICS_ELEVATION_RESOLUTION };
export { SPHERICAL_HARMONICS_MAX_ORDER };
export { MAX_RE_WEIGHTS };
export { MAX_RE_WEIGHTS_RESOLUTION };
}
export default _default;
/**
* Pre-computed Spherical Harmonics Coefficients.
*
* This function generates an efficient lookup table of SH coefficients. It
* exploits the way SHs are generated (i.e. Ylm = Nlm * Plm * Em). Since Nlm
* & Plm coefficients only depend on theta, and Em only depends on phi, we
* can separate the equation along these lines. Em does not depend on
* degree, so we only need to compute (2 * l) per azimuth Em total and
* Nlm * Plm is symmetrical across indexes, so only positive indexes are
* computed ((l + 1) * (l + 2) / 2 - 1) per elevation.
* @type {Float32Array}
*/
declare const SPHERICAL_HARMONICS: Float32Array;
/** @type {Number} */
declare const SPHERICAL_HARMONICS_AZIMUTH_RESOLUTION: number;
/** @type {Number} */
declare const SPHERICAL_HARMONICS_ELEVATION_RESOLUTION: number;
/**
* The maximum allowed ambisonic order.
* @type {Number}
*/
declare const SPHERICAL_HARMONICS_MAX_ORDER: number;
/**
* Pre-computed per-band weighting coefficients for producing energy-preserving
* Max-Re sources.
*/
declare const MAX_RE_WEIGHTS: number[][];
/** @type {Number} */
declare const MAX_RE_WEIGHTS_RESOLUTION: number;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,98 @@
export default Utils;
/**
* @class Utils
* @description A set of defaults, constants and utility functions.
*/
declare class Utils {
/**
* Properties describing the geometry of a room.
* @typedef {Object} Utils~RoomDimensions
* @property {Number} width (in meters).
* @property {Number} height (in meters).
* @property {Number} depth (in meters).
*/
/**
* Properties describing the wall materials (from
* {@linkcode Utils.ROOM_MATERIAL_COEFFICIENTS ROOM_MATERIAL_COEFFICIENTS})
* of a room.
* @typedef {Object} Utils~RoomMaterials
* @property {String} left Left-wall material name.
* @property {String} right Right-wall material name.
* @property {String} front Front-wall material name.
* @property {String} back Back-wall material name.
* @property {String} up Up-wall material name.
* @property {String} down Down-wall material name.
*/
/**
* ResonanceAudio library logging function.
* @type {Function}
* @param {any} Message to be printed out.
* @private
*/
private static log;
}
declare namespace Utils {
const DEFAULT_SOURCE_GAIN: number;
const LISTENER_MAX_OUTSIDE_ROOM_DISTANCE: number;
const SOURCE_MAX_OUTSIDE_ROOM_DISTANCE: number;
const DEFAULT_SOURCE_DISTANCE: number;
const DEFAULT_POSITION: Float32Array;
const DEFAULT_FORWARD: Float32Array;
const DEFAULT_UP: Float32Array;
const DEFAULT_RIGHT: Float32Array;
const DEFAULT_SPEED_OF_SOUND: number;
const ATTENUATION_ROLLOFFS: any[];
const DEFAULT_ATTENUATION_ROLLOFF: string;
const DEFAULT_MIN_DISTANCE: number;
const DEFAULT_MAX_DISTANCE: number;
const DEFAULT_DIRECTIVITY_ALPHA: number;
const DEFAULT_DIRECTIVITY_SHARPNESS: number;
const DEFAULT_AZIMUTH: number;
const DEFAULT_ELEVATION: number;
const DEFAULT_AMBISONIC_ORDER: number;
const DEFAULT_SOURCE_WIDTH: number;
const DEFAULT_REFLECTION_MAX_DURATION: number;
const DEFAULT_REFLECTION_CUTOFF_FREQUENCY: number;
const DEFAULT_REFLECTION_COEFFICIENTS: any;
const DEFAULT_REFLECTION_MIN_DISTANCE: number;
const DEFAULT_ROOM_DIMENSIONS: any;
const DEFAULT_REFLECTION_MULTIPLIER: number;
const DEFAULT_REVERB_BANDWIDTH: number;
const DEFAULT_REVERB_DURATION_MULTIPLIER: number;
const DEFAULT_REVERB_PREDELAY: number;
const DEFAULT_REVERB_TAIL_ONSET: number;
const DEFAULT_REVERB_GAIN: number;
const DEFAULT_REVERB_MAX_DURATION: number;
const DEFAULT_REVERB_FREQUENCY_BANDS: any[];
const NUMBER_REVERB_FREQUENCY_BANDS: number;
const DEFAULT_REVERB_DURATIONS: Float32Array;
const ROOM_MATERIAL_COEFFICIENTS: any;
const DEFAULT_ROOM_MATERIALS: any;
const NUMBER_REFLECTION_AVERAGING_BANDS: number;
const ROOM_STARTING_AVERAGING_BAND: number;
const ROOM_MIN_VOLUME: number;
const ROOM_AIR_ABSORPTION_COEFFICIENTS: Float32Array;
const ROOM_EYRING_CORRECTION_COEFFICIENT: number;
const TWO_PI: number;
const TWENTY_FOUR_LOG10: number;
const LOG1000: number;
const LOG2_DIV2: number;
const DEGREES_TO_RADIANS: number;
const RADIANS_TO_DEGREES: number;
const EPSILON_FLOAT: number;
/**
* Normalize a 3-d vector.
* @param {Float32Array} v 3-element vector.
* @return {Float32Array} 3-element vector.
* @private
*/
function normalizeVector(v: Float32Array): Float32Array;
/**
* Cross-product between two 3-d vectors.
* @param {Float32Array} a 3-element vector.
* @param {Float32Array} b 3-element vector.
* @return {Float32Array}
* @private
*/
function crossProduct(a: Float32Array, b: Float32Array): Float32Array;
}

View File

@@ -0,0 +1,379 @@
/**
* @license
* Copyright 2017 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file ResonanceAudio library common utilities, mathematical constants,
* and default values.
* @author Andrew Allen <bitllama@google.com>
*/
'use strict';
/**
* @class Utils
* @description A set of defaults, constants and utility functions.
*/
class Utils {
/**
* Properties describing the geometry of a room.
* @typedef {Object} Utils~RoomDimensions
* @property {Number} width (in meters).
* @property {Number} height (in meters).
* @property {Number} depth (in meters).
*/
/**
* Properties describing the wall materials (from
* {@linkcode Utils.ROOM_MATERIAL_COEFFICIENTS ROOM_MATERIAL_COEFFICIENTS})
* of a room.
* @typedef {Object} Utils~RoomMaterials
* @property {String} left Left-wall material name.
* @property {String} right Right-wall material name.
* @property {String} front Front-wall material name.
* @property {String} back Back-wall material name.
* @property {String} up Up-wall material name.
* @property {String} down Down-wall material name.
*/
/**
* ResonanceAudio library logging function.
* @type {Function}
* @param {any} Message to be printed out.
* @private
*/
static log() {
window.console.log.apply(window.console, [
'%c[ResonanceAudio]%c '
+ Array.prototype.slice.call(arguments).join(' ') + ' %c(@'
+ performance.now().toFixed(2) + 'ms)',
'background: #BBDEFB; color: #FF5722; font-weight: 700',
'font-weight: 400',
'color: #AAA',
]);
}
}
/**
* Default input gain (linear).
* @type {Number}
*/
Utils.DEFAULT_SOURCE_GAIN = 1;
/**
* Maximum outside-the-room distance to attenuate far-field listener by.
* @type {Number}
*/
Utils.LISTENER_MAX_OUTSIDE_ROOM_DISTANCE = 1;
/**
* Maximum outside-the-room distance to attenuate far-field sources by.
* @type {Number}
*/
Utils.SOURCE_MAX_OUTSIDE_ROOM_DISTANCE = 1;
/**
* Default distance from listener when setting angle.
* @type {Number}
*/
Utils.DEFAULT_SOURCE_DISTANCE = 1;
/** @type {Float32Array} */
Utils.DEFAULT_POSITION = [0, 0, 0];
/** @type {Float32Array} */
Utils.DEFAULT_FORWARD = [0, 0, -1];
/** @type {Float32Array} */
Utils.DEFAULT_UP = [0, 1, 0];
/** @type {Float32Array} */
Utils.DEFAULT_RIGHT = [1, 0, 0];
/**
* @type {Number}
*/
Utils.DEFAULT_SPEED_OF_SOUND = 343;
/** Rolloff models (e.g. 'logarithmic', 'linear', or 'none').
* @type {Array}
*/
Utils.ATTENUATION_ROLLOFFS = ['logarithmic', 'linear', 'none'];
/** Default rolloff model ('logarithmic').
* @type {string}
*/
Utils.DEFAULT_ATTENUATION_ROLLOFF = 'logarithmic';
/** @type {Number} */
Utils.DEFAULT_MIN_DISTANCE = 1;
/** @type {Number} */
Utils.DEFAULT_MAX_DISTANCE = 1000;
/**
* The default alpha (i.e. microphone pattern).
* @type {Number}
*/
Utils.DEFAULT_DIRECTIVITY_ALPHA = 0;
/**
* The default pattern sharpness (i.e. pattern exponent).
* @type {Number}
*/
Utils.DEFAULT_DIRECTIVITY_SHARPNESS = 1;
/**
* Default azimuth (in degrees). Suitable range is 0 to 360.
* @type {Number}
*/
Utils.DEFAULT_AZIMUTH = 0;
/**
* Default elevation (in degres).
* Suitable range is from -90 (below) to 90 (above).
* @type {Number}
*/
Utils.DEFAULT_ELEVATION = 0;
/**
* The default ambisonic order.
* @type {Number}
*/
Utils.DEFAULT_AMBISONIC_ORDER = 1;
/**
* The default source width.
* @type {Number}
*/
Utils.DEFAULT_SOURCE_WIDTH = 0;
/**
* The maximum delay (in seconds) of a single wall reflection.
* @type {Number}
*/
Utils.DEFAULT_REFLECTION_MAX_DURATION = 0.5;
/**
* The -12dB cutoff frequency (in Hertz) for the lowpass filter applied to
* all reflections.
* @type {Number}
*/
Utils.DEFAULT_REFLECTION_CUTOFF_FREQUENCY = 6400; // Uses -12dB cutoff.
/**
* The default reflection coefficients (where 0 = no reflection, 1 = perfect
* reflection, -1 = mirrored reflection (180-degrees out of phase)).
* @type {Object}
*/
Utils.DEFAULT_REFLECTION_COEFFICIENTS = {
left: 0, right: 0, front: 0, back: 0, down: 0, up: 0,
};
/**
* The minimum distance we consider the listener to be to any given wall.
* @type {Number}
*/
Utils.DEFAULT_REFLECTION_MIN_DISTANCE = 1;
/**
* Default room dimensions (in meters).
* @type {Object}
*/
Utils.DEFAULT_ROOM_DIMENSIONS = {
width: 0, height: 0, depth: 0,
};
/**
* The multiplier to apply to distances from the listener to each wall.
* @type {Number}
*/
Utils.DEFAULT_REFLECTION_MULTIPLIER = 1;
/** The default bandwidth (in octaves) of the center frequencies.
* @type {Number}
*/
Utils.DEFAULT_REVERB_BANDWIDTH = 1;
/** The default multiplier applied when computing tail lengths.
* @type {Number}
*/
Utils.DEFAULT_REVERB_DURATION_MULTIPLIER = 1;
/**
* The late reflections pre-delay (in milliseconds).
* @type {Number}
*/
Utils.DEFAULT_REVERB_PREDELAY = 1.5;
/**
* The length of the beginning of the impulse response to apply a
* half-Hann window to.
* @type {Number}
*/
Utils.DEFAULT_REVERB_TAIL_ONSET = 3.8;
/**
* The default gain (linear).
* @type {Number}
*/
Utils.DEFAULT_REVERB_GAIN = 0.01;
/**
* The maximum impulse response length (in seconds).
* @type {Number}
*/
Utils.DEFAULT_REVERB_MAX_DURATION = 3;
/**
* Center frequencies of the multiband late reflections.
* Nine bands are computed by: 31.25 * 2^(0:8).
* @type {Array}
*/
Utils.DEFAULT_REVERB_FREQUENCY_BANDS = [
31.25, 62.5, 125, 250, 500, 1000, 2000, 4000, 8000,
];
/**
* The number of frequency bands.
*/
Utils.NUMBER_REVERB_FREQUENCY_BANDS =
Utils.DEFAULT_REVERB_FREQUENCY_BANDS.length;
/**
* The default multiband RT60 durations (in seconds).
* @type {Float32Array}
*/
Utils.DEFAULT_REVERB_DURATIONS =
new Float32Array(Utils.NUMBER_REVERB_FREQUENCY_BANDS);
/**
* Pre-defined frequency-dependent absorption coefficients for listed materials.
* Currently supported materials are:
* <ul>
* <li>'transparent'</li>
* <li>'acoustic-ceiling-tiles'</li>
* <li>'brick-bare'</li>
* <li>'brick-painted'</li>
* <li>'concrete-block-coarse'</li>
* <li>'concrete-block-painted'</li>
* <li>'curtain-heavy'</li>
* <li>'fiber-glass-insulation'</li>
* <li>'glass-thin'</li>
* <li>'glass-thick'</li>
* <li>'grass'</li>
* <li>'linoleum-on-concrete'</li>
* <li>'marble'</li>
* <li>'metal'</li>
* <li>'parquet-on-concrete'</li>
* <li>'plaster-smooth'</li>
* <li>'plywood-panel'</li>
* <li>'polished-concrete-or-tile'</li>
* <li>'sheetrock'</li>
* <li>'water-or-ice-surface'</li>
* <li>'wood-ceiling'</li>
* <li>'wood-panel'</li>
* <li>'uniform'</li>
* </ul>
* @type {Object}
*/
Utils.ROOM_MATERIAL_COEFFICIENTS = {
'transparent': [1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000],
'acoustic-ceiling-tiles': [0.672, 0.675, 0.700, 0.660, 0.720, 0.920, 0.880, 0.750, 1.000],
'brick-bare': [0.030, 0.030, 0.030, 0.030, 0.030, 0.040, 0.050, 0.070, 0.140],
'brick-painted': [0.006, 0.007, 0.010, 0.010, 0.020, 0.020, 0.020, 0.030, 0.060],
'concrete-block-coarse': [0.360, 0.360, 0.360, 0.440, 0.310, 0.290, 0.390, 0.250, 0.500],
'concrete-block-painted': [0.092, 0.090, 0.100, 0.050, 0.060, 0.070, 0.090, 0.080, 0.160],
'curtain-heavy': [0.073, 0.106, 0.140, 0.350, 0.550, 0.720, 0.700, 0.650, 1.000],
'fiber-glass-insulation': [0.193, 0.220, 0.220, 0.820, 0.990, 0.990, 0.990, 0.990, 1.000],
'glass-thin': [0.180, 0.169, 0.180, 0.060, 0.040, 0.030, 0.020, 0.020, 0.040],
'glass-thick': [0.350, 0.350, 0.350, 0.250, 0.180, 0.120, 0.070, 0.040, 0.080],
'grass': [0.050, 0.050, 0.150, 0.250, 0.400, 0.550, 0.600, 0.600, 0.600],
'linoleum-on-concrete': [0.020, 0.020, 0.020, 0.030, 0.030, 0.030, 0.030, 0.020, 0.040],
'marble': [0.010, 0.010, 0.010, 0.010, 0.010, 0.010, 0.020, 0.020, 0.040],
'metal': [0.030, 0.035, 0.040, 0.040, 0.050, 0.050, 0.050, 0.070, 0.090],
'parquet-on-concrete': [0.028, 0.030, 0.040, 0.040, 0.070, 0.060, 0.060, 0.070, 0.140],
'plaster-rough': [0.017, 0.018, 0.020, 0.030, 0.040, 0.050, 0.040, 0.030, 0.060],
'plaster-smooth': [0.011, 0.012, 0.013, 0.015, 0.020, 0.030, 0.040, 0.050, 0.100],
'plywood-panel': [0.400, 0.340, 0.280, 0.220, 0.170, 0.090, 0.100, 0.110, 0.220],
'polished-concrete-or-tile': [0.008, 0.008, 0.010, 0.010, 0.015, 0.020, 0.020, 0.020, 0.040],
'sheet-rock': [0.290, 0.279, 0.290, 0.100, 0.050, 0.040, 0.070, 0.090, 0.180],
'water-or-ice-surface': [0.006, 0.006, 0.008, 0.008, 0.013, 0.015, 0.020, 0.025, 0.050],
'wood-ceiling': [0.150, 0.147, 0.150, 0.110, 0.100, 0.070, 0.060, 0.070, 0.140],
'wood-panel': [0.280, 0.280, 0.280, 0.220, 0.170, 0.090, 0.100, 0.110, 0.220],
'uniform': [0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500],
};
/**
* Default materials that use strings from
* {@linkcode Utils.MATERIAL_COEFFICIENTS MATERIAL_COEFFICIENTS}
* @type {Object}
*/
Utils.DEFAULT_ROOM_MATERIALS = {
left: 'transparent', right: 'transparent', front: 'transparent',
back: 'transparent', down: 'transparent', up: 'transparent',
};
/**
* The number of bands to average over when computing reflection coefficients.
* @type {Number}
*/
Utils.NUMBER_REFLECTION_AVERAGING_BANDS = 3;
/**
* The starting band to average over when computing reflection coefficients.
* @type {Number}
*/
Utils.ROOM_STARTING_AVERAGING_BAND = 4;
/**
* The minimum threshold for room volume.
* Room model is disabled if volume is below this value.
* @type {Number} */
Utils.ROOM_MIN_VOLUME = 1e-4;
/**
* Air absorption coefficients per frequency band.
* @type {Float32Array}
*/
Utils.ROOM_AIR_ABSORPTION_COEFFICIENTS =
[0.0006, 0.0006, 0.0007, 0.0008, 0.0010, 0.0015, 0.0026, 0.0060, 0.0207];
/**
* A scalar correction value to ensure Sabine and Eyring produce the same RT60
* value at the cross-over threshold.
* @type {Number}
*/
Utils.ROOM_EYRING_CORRECTION_COEFFICIENT = 1.38;
/**
* @type {Number}
* @private
*/
Utils.TWO_PI = 6.28318530717959;
/**
* @type {Number}
* @private
*/
Utils.TWENTY_FOUR_LOG10 = 55.2620422318571;
/**
* @type {Number}
* @private
*/
Utils.LOG1000 = 6.90775527898214;
/**
* @type {Number}
* @private
*/
Utils.LOG2_DIV2 = 0.346573590279973;
/**
* @type {Number}
* @private
*/
Utils.DEGREES_TO_RADIANS = 0.017453292519943;
/**
* @type {Number}
* @private
*/
Utils.RADIANS_TO_DEGREES = 57.295779513082323;
/**
* @type {Number}
* @private
*/
Utils.EPSILON_FLOAT = 1e-8;
/**
* Normalize a 3-d vector.
* @param {Float32Array} v 3-element vector.
* @return {Float32Array} 3-element vector.
* @private
*/
Utils.normalizeVector = v => {
let n = Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
if (n > Utils.EPSILON_FLOAT) {
n = 1 / n;
v[0] *= n;
v[1] *= n;
v[2] *= n;
}
return v;
};
/**
* Cross-product between two 3-d vectors.
* @param {Float32Array} a 3-element vector.
* @param {Float32Array} b 3-element vector.
* @return {Float32Array}
* @private
*/
Utils.crossProduct = (a, b) => {
return [
a[1] * b[2] - a[2] * b[1],
a[2] * b[0] - a[0] * b[2],
a[0] * b[1] - a[1] * b[0],
];
};
export default Utils;

View File

@@ -0,0 +1,2 @@
declare var _default: "1.0.0";
export default _default;

View File

@@ -0,0 +1,24 @@
/**
* Copyright 2017 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file ResonanceAudio version.
* @author Andrew Allen <bitllama@google.com>
*/
'use strict';
/**
* ResonanceAudio library version
* @type {String}
*/
export default '1.0.0';