Loudness

From Unify Community Wiki
(Difference between revisions)
Jump to: navigation, search
m (Instructions (C# only*))
 
(24 intermediate revisions by 4 users not shown)
Line 4: Line 4:
 
==Description==
 
==Description==
  
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.)
+
Unity's [http://unity3d.com/support/documentation/ScriptReference/AudioSource-volume.html AudioSource.volume] and [http://unity3d.com/support/documentation/ScriptReference/AudioListener-volume.html AudioListener.volume] use what is known as a [http://www.angelfire.com/electronic/funwithtubes/Amp-Volume.html linear taper].  Although that is ideal for performance, it means that the 0-1 values that those properties utilize do not match up well with human perception, with loud values taking up a disproportionate amount of the rangeThis script is designed to make working with loudness more intuitive.
  
== JavaScript - Loudness.js ==
+
Loudness is a complex phenomenon, and this simple script does not, for instance, take [http://en.wikipedia.org/wiki/Equal-loudness_contours equal-loudness contours] into account, but it should yield better results than a linear taper in every real-world case.  I've found the result described [http://www.animations.physics.unsw.edu.au/jw/dB.htm here, that "a 10 dB increase in sound level corresponds approximately to a perceived doubling of loudness"], leads the way to a highly usable loudness control.
<javascript>
+
var loudness : float = 1;
+
  
private var exponent = Mathf.Log(Mathf.Sqrt(10), 2);
+
==Instructions (C# only*)==
  
// I would not leave this in Update() for a release.   
+
Add this script to your project, wherever you likeIt's a static class, so you neither need nor have the ability to attach it to a Game Object.
// Put it wherever you need it, to change the volume in-game.
+
  
function Update ()
 
{
 
audio.volume = Mathf.Pow(loudness, exponent);
 
}
 
</javascript>
 
  
==Why this works==
+
Loudness.OfListener (which alters AudioListener.volume) is a [http://msdn.microsoft.com/en-us/library/x9fsa0sw.aspx property], so you can assign a 0-1 value to it, or get one back, using the equals sign.  
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...
+
<syntaxhighlight lang="csharp">Loudness.OfListener = .5F;  // Half as loud as it can get</syntaxhighlight>
  
  
exactly what is input, for a value of 1,
+
Extension properties do not exist in C# yet, so for instances of Audio Sources, you have to use separate get and set methods.
 +
<syntaxhighlight lang="csharp">audio.SetLoudness(.5F);  // Affects the AudioSource attached to this Game Object
 +
someAudioSource.GetLoudness(); // gives you a better idea of the portion of maximum loudness than someAudioSource.volume</syntaxhighlight>
  
half of the input, for a value of .5,
 
  
one fourth of the input, for a value of .25,
+
*A UnityScript alternative would have to be more verbose, due to lack of properties and extension methods in that language (at present), but feel free to add a JS solution to this page. ;-)
  
etc. 
+
==Loudness.cs==
 
+
<syntaxhighlight lang="csharp">using UnityEngine;
 
+
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 [http://www.phys.unsw.edu.au/jw/dB.html "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,
+
 
+
etc. 
+
 
+
 
+
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 code 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 am still trying to find a proof for why this is a property of logarithms in exponents - please let me know if you have it.
+
  
 +
public static class Loudness
 +
{
 +
// A 20 dB increase sounds about 4x louder.
 +
// A signal needs an amplitude that is 10^(dB/20) greater,
 +
// to be increased by 'dB' decibels.
 +
class Exponents
 +
{
 +
public static readonly float Set = Mathf.Log(10, 4);
 +
public static readonly float Get = 1 / Set;
 +
}
  
 +
public static float GetLoudness(this AudioSource audioSource)
 +
{
 +
return Mathf.Pow(audioSource.volume, Exponents.Get);
 +
}
  
* 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 [http://forum.unity3d.com/viewtopic.php?t=19455&postdays=0&postorder=asc&start=15 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.
+
public static float SetLoudness(this AudioSource audioSource, float value)
 +
{
 +
return audioSource.volume = Mathf.Pow(Mathf.Clamp01(value), Exponents.Set);
 +
}
  
* 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. ;-)
+
public static float OfListener
 +
{
 +
get
 +
{
 +
return Mathf.Pow(AudioListener.volume, Exponents.Get);
 +
}
 +
set
 +
{
 +
AudioListener.volume = Mathf.Pow(Mathf.Clamp01(value), Exponents.Set);
 +
}
 +
}
 +
}</syntaxhighlight>

Latest revision as of 01:23, 29 October 2013

Author: Jessy

[edit] Description

Unity's AudioSource.volume and AudioListener.volume use what is known as a linear taper. Although that is ideal for performance, it means that the 0-1 values that those properties utilize do not match up well with human perception, with loud values taking up a disproportionate amount of the range. This script is designed to make working with loudness more intuitive.

Loudness is a complex phenomenon, and this simple script does not, for instance, take equal-loudness contours into account, but it should yield better results than a linear taper in every real-world case. I've found the result described here, that "a 10 dB increase in sound level corresponds approximately to a perceived doubling of loudness", leads the way to a highly usable loudness control.

[edit] Instructions (C# only*)

Add this script to your project, wherever you like. It's a static class, so you neither need nor have the ability to attach it to a Game Object.


Loudness.OfListener (which alters AudioListener.volume) is a property, so you can assign a 0-1 value to it, or get one back, using the equals sign.

Loudness.OfListener = .5F;  // Half as loud as it can get


Extension properties do not exist in C# yet, so for instances of Audio Sources, you have to use separate get and set methods.

audio.SetLoudness(.5F);  // Affects the AudioSource attached to this Game Object
someAudioSource.GetLoudness(); // gives you a better idea of the portion of maximum loudness than someAudioSource.volume


  • A UnityScript alternative would have to be more verbose, due to lack of properties and extension methods in that language (at present), but feel free to add a JS solution to this page.  ;-)

[edit] Loudness.cs

using UnityEngine;
 
public static class Loudness 
{
	// A 20 dB increase sounds about 4x louder.
	// A signal needs an amplitude that is 10^(dB/20) greater, 
	// to be increased by 'dB' decibels.
	class Exponents 
	{
		public static readonly float Set = Mathf.Log(10, 4);
		public static readonly float Get = 1 / Set;
	}
 
	public static float GetLoudness(this AudioSource audioSource) 
	{
		return Mathf.Pow(audioSource.volume, Exponents.Get);
	}
 
	public static float SetLoudness(this AudioSource audioSource, float value) 
	{
		return audioSource.volume = Mathf.Pow(Mathf.Clamp01(value), Exponents.Set);
	}
 
	public static float OfListener 
	{
		get 
		{
			return Mathf.Pow(AudioListener.volume, Exponents.Get);
		}
		set 
		{
			AudioListener.volume = Mathf.Pow(Mathf.Clamp01(value), Exponents.Set);
		}
	}
}
Personal tools
Namespaces

Variants
Actions
Navigation
Extras
Toolbox