From Unify Community Wiki
Revision as of 03:42, 23 April 2009 by Jessy (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Author: Jessy


This is a script that replaces most properties of Audio Sources. I feel that its controls are more intuitive then those of the Audio Source component itself, and it slightly extends functionality as well. I intend for it to be easy to switch from scripting Audio Sources directly, to utilizing this script. Where you normally would type "audio" in your code, use "Audio" instead.

If you are using only one Audio Source per Game Object, then this script will automatically control it, when attached. Otherwise, you can programmatically assign what it controls via the "audioSource" property.

JavaScript - Audio.js

<javascript>// because the variables of the controlled Audio Source will probably be visisble in the Inspector @script ExecuteInEditMode ()

// If there is only one Audio Source attached to this game object, // and there are no other instances of this script attached, // automatically link this script to the Audio Source, // when the script is attached. private var audioSource : AudioSource; function Awake () { if (GetComponents(AudioSource).length < 2 && GetComponents(typeof this).length < 2) audioSource = audio; }

// automatically links the first attached instance of this script // to an Audio Source, if none were present @script RequireComponent(AudioSource)

var volume : float = 1; var minVolume : float = 0; var maxVolume : float = 1; var rolloffFactor : float = 1; var rolloffThreshold : float = 1; var ignoreDistance = false;

private var rolloffExponent;

// Turn this script on and off to refresh these variables. // This code is not in Update() because // I assume that almost no one will need it to be dynamic. function OnEnable () { // Unity only allows values between 0 and 1 for volume minVolume = Mathf.Clamp01(minVolume); maxVolume = Mathf.Clamp01(maxVolume);

// ensure maxVolume is not less than minVolume maxVolume = Mathf.Max(minVolume, maxVolume);

// negative values don't make sense for these rolloffFactor = Mathf.Max(rolloffFactor, 0); rolloffThreshold = Mathf.Max(rolloffThreshold, 0);

// These variables are not useful; // initializing them this way keeps this script as tidy as possible. audioSource.minVolume = 0; audioSource.maxVolume = 1; audioSource.rolloffFactor = 0;

rolloffExponent = Mathf.Log(.5, (1 / rolloffFactor + 1)); } private var audioListener : Transform;

function Start() { var listenerComponent : Component = FindObjectOfType(AudioListener); if (listenerComponent) audioListener = listenerComponent.gameObject.transform; }

private var loudnessExponent = Mathf.Log(Mathf.Sqrt(10), 2);

function Update () { if (audioSource && audioListener) { // You can move this out of Update() for performance over convenience var loudness = Mathf.Pow(volume, loudnessExponent); if (ignoreDistance == true) audioSource.volume = Mathf.Clamp(loudness, minVolume, maxVolume);

else { var distanceToListener = Vector3.Distance(audioListener.position, transform.position); var base = distanceToListener / rolloffThreshold; print(base); var positionalVolume = Mathf.Pow(base, rolloffExponent) * loudness; audioSource.volume = Mathf.Clamp(positionalVolume, minVolume, maxVolume); } } }</javascript>

Why this works

To simplify things a bit, a volume control is a multiplier for amplitude of audio waveforms. It makes intuitive sense for a volume control to output...

exactly what is input, for a value of 1,

half of the input, for a value of .5,

one fourth of the input, for a value of .25,


Unity's volume control does exactly this (see below for exceptions). The problem is that amplitude of a waveform does not correlate directly with human perception of loudness. Loudness perception is complex, but "experimentally it was found that a 10 dB increase in sound level corresponds approximately to a perceived doubling of loudness.". Therefore, it makes sense for a volume control to yield an amplitude that is...

exactly what is input, for a value of 1,

10 decibels below the input, for a value of .5,

20 decibels below the input, for a value of .25,


Amplitudinal difference, as a multiplicative factor, based on a difference in decibels, can be found with the expression 10^(dB / 20). Inputting the value of 10 dB results in 10^(10/20), which "Mathf.Sqrt(10)" in the code represents.

Raising this value to a base 2 logarithm gives exactly the behavior we want for a volume control. This expression would be "(10^.5)^log[2]loudness". However, the algorithm will execute faster in the form that I gave in the code, which is "loudness^(log[2](10^.5))", due to only having to calculate a logarithm one time. (I will be adding proof of this soon.)

  • AudioSource.volume only responds to values of 1 or less. There is the potential to easily induce digital distortion if non-clipped waveforms can be amplified, so apparently the choice was made to disallow this. I think this may have been done before it easily allows for 3D positional audio not to distort within a distance of less than one world meter, using the current distance model of volume = 1.0 / (1.0 + rolloff * (distance – 1.0)). Boosting the level of audio clips can only be done outside Unity at the moment. My conversion script will continue to function properly, above values of 1, if the restriction is ever dropped.
  • Values of less than zero, for either this loudness script, or audio.volume, both equate to multiplying the audio amplitude by zero. It is possible, that if Unity ever allows us the ability to invert the phase of a signal, that negative values could be useful, but this script would not account for that as is. I will update it if necessary, but hopefully, by the time Unity allows for phase inversion, this script will be obsolete.  ;-)

Tweaking the Audio Source controls for Audio Sources in Unity is not predictable. Here are the rogue properties of Audio Sources, with their descriptions as provided by the Unity Reference Manual, and with descriptions as they have actually behaved in my tests.

in Unity's documentation. 

Volume: How loud the sound is at a distance of 1 world unit (1 meter) from the Audio Listener.

This is true for the Audio Listener's Y and Z, but not for X. A sound traveling along the Audio Listener's X is 3 dB louder than it should be, according to this description, and it only plays through one speaker, at least in a two-speaker environment. Obviously, this is due to the 3D positional audio algorithm, but the description of the property is not great, and the algorithm is highly unrealistic along the Audio Listener's X axis. (Fortunately, it gets better if more axes are involved.)

This is the one case where Unity can boost the level of an audio clip. However, it can only boost the clip by a maximum of 3 dB. To avoid distortion, it seems that all mono clips imported into Unity are reduced in level by 3 dB. The Unity 2.5 Audio Importer's "Audio Channels" box also assures that no clips that are converted from stereo to mono have any peaks above -3 dB. Unfortunately, although this is apparently not the case for everyone, on my system, Unity actually clips all samples above -3 dB to exactly -3 dB, instead of multiplying all samples by 10^(-3/20), as would be a better solution, if the 3 dB boost along the Audio Listener's X axis can not be avoided.

Also, while AudioSource.volume can accept values between 0 and infinity, values above 1 do the same thing as 1. That is to say, an Audio Clip will never play back louder than it was recorded.

Min Volume: The minimum value of the sound. No matter how far away you get, the sound will not get softer than this value.

Max Volume: How loud the sound gets at the loudest. No matter how close you get, the sound will never get louder than this value.

These variables actually have nothing to do with distance. Instead, they are just clamps for AudioSource.volume. The following odd behavior ensues, if Min Volume is greater than Max Volume:

Unity iPhone:

If Volume is less than Min Volume, then Volume behaves as if it were set to Min Volume. If Volume is greater than or equal to Min Volume, then Volume behaves as if it were set to Max Volume.

Unity 2.5:

If Volume is less than Min Volume, and Volume is less than 1, then Volume behaves as if it were set to Min Volume. If Volume is greater than or equal to Min Volume, or greater than or equal to 1, then Volume behaves as if it were set to Max Volume.

Like Volume, although Min Volume and Max Volume can accept values between 0 and infinity, values above 1 do the same thing as 1.

Rolloff Factor: How fast the sound fades. The higher the value, the closer the Listener has to be before hearing the sound.

Unity's standard volume control uses a "linear taper", which does not correspond with human hearing. "Loudness", unlike the Audio Source's "Volume", will yield perceptually equivalent loudness changes for equivalent value changes. (Changing the Loudness from 0.2 to 0.3 will yield a similar difference in loudness between 0.7 to 0.8, etc.)

Personal tools