Loudness

From Unify Community Wiki
(Difference between revisions)
Jump to: navigation, search
(Why this works)
m (Instructions (C# only*))
 
(33 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 sqrtOf10 = Mathf.Sqrt(10);
+
  
// I would not leave this in Update() for a release. 
+
==Instructions (C# only*)==
// Put it wherever you need it, to change the volume in-game.
+
  
function Update ()
+
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.
{
+
audio.volume = Mathf.Pow(sqrtOf10, Mathf.Log(loudness, 2));
+
}
+
</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...
 
  
 +
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.
 +
<syntaxhighlight lang="csharp">Loudness.OfListener = .5F;  // Half as loud as it can get</syntaxhighlight>
  
exactly what is input, for a value of 1,
 
  
half of the input, for a value of .5,
+
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>
  
twice the input, for a value of 2,
 
  
etc.   
+
*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;-)
  
 +
==Loudness.cs==
 +
<syntaxhighlight lang="csharp">using UnityEngine;
  
Unity's volume control does exactly this (see below for an exception).  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...
+
public static class Loudness  
 
+
{
exactly what is input, for a value of 1,
+
// A 20 dB increase sounds about 4x louder.
 
+
// A signal needs an amplitude that is 10^(dB/20) greater,  
10 decibels below the input, for a value of .5,  
+
// to be increased by 'dB' decibels.
 
+
class Exponents
10 decibels above the input, for a value of 2,
+
{
 
+
public static readonly float Set = Mathf.Log(10, 4);
etc.
+
public static readonly float Get = 1 / Set;
 
+
}
 
+
Amplitudinal difference, as a multiplicative factor, based on a difference in decibels, can be found with the equation 10^(dB / 20). Inputting the value of 10 dB into this equation results in 10^(10/20), which the sqrtOf10 variable in the code represents.
+
 
+
 
+
Raising this value to a base 2 logarithm gives exactly the behavior we want for a volume control.
+
 
+
  
 +
public static float GetLoudness(this AudioSource audioSource)
 +
{
 +
return Mathf.Pow(audioSource.volume, Exponents.Get);
 +
}
  
* for 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. 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