So I am currently developing a horror game in Unity. Currently I have a (working) system where your voice volume shows up on a UI Slider. So say for example you are quiet, it show's that you're being quiet on the slider. If you are loud, it shows how loud you're being.
I have that system working. But I have been really trying to figure out a way so that if your voice volume (mic volume) goes past a certain level or loudness, an event happens.
So say, I want to activate gameobject with how loud my voice is. If I am too loud, it triggers an event, activating that gameobject. What I'm trying to get basically is a way for if the UI slider goes past a certain value on said Slider, an event triggers.
I have code here displaying all of the scripts I currently have for this system. I would really appreciate the help!
https://gist.github.com/AidenStickel/3a58f497d41cd44b320fecfcc5ea850d
(Ignore The script labeled "ScaleFromAudioClip)
CodePudding user response:
This is an interesting feature. As such, I looked at your initial detection code a modified it a little to provide the features you request.
This is the code I came up with:
public class AudioLoudnessDetection : MonoBehaviour
{
public event Action<float> BelowLowThreshold;
public event Action<float> AboveHighThreshold;
[SerializeField] private int _sampleWindow = 64;
[SerializeField] private float _lowThreshold;
[SerializeField] private float _highThreshold;
private AudioClip _microphoneClip;
private string _microphoneName;
private bool _listening;
private float _currentLoudness;
public float lowThreshold => _lowThreshold;
public float highThreshold => _highThreshold;
public bool isListening=> _listening;
public float currentLoudness => _currentLoudness;
void Awake ( )
{
_microphoneName = Microphone.devices [ 0 ];
_microphoneClip = Microphone.Start ( _microphoneName, true, 20, AudioSettings.outputSampleRate );
if ( _microphoneClip != null )
_listening = true;
}
void Update ( )
{
_currentLoudness = GetLoudnessFromAudioClip ( Microphone.GetPosition ( _microphoneName ), _microphoneClip, _sampleWindow );
if ( _currentLoudness < _lowThreshold )
BelowLowThreshold?.Invoke ( _currentLoudness );
else if ( _currentLoudness > _highThreshold )
AboveHighThreshold?.Invoke ( _currentLoudness );
}
public static float GetLoudnessFromAudioClip ( int clipPosition, AudioClip clip, int sampleWindow )
{
var startPosition = clipPosition - sampleWindow;
if ( startPosition < 0 ) return 0;
var waveData = new float[sampleWindow];
clip.GetData ( waveData, startPosition );
//compute loudness
var totalLoudness = 0f;
for ( var i = 0; i < sampleWindow; i )
totalLoudness = Mathf.Abs ( waveData [ i ] );
return totalLoudness / sampleWindow;
}
}
I first added some properties, the most important being currentLoudness
. This is calculated during each frame, so there's no need to specifically call a method to calculate it. You can just read this property in your slider or anywhere else you want to know the microphone lourdness. With this property, there was no need for GetLoudnessFromMicrophone
.
There's also to values, a low threshold and a high threshold. These are used to determine if events are triggered. The two events are BelowLowThreshold
and AboveHighThreshold
.
The code makes an assumption that the Microphone won't change during the course of the game, and caches to Microphone.devices [ 0 ]
name so as to try and reduce garbage production.
Lastly, you can register to these events like this:
public class Test : MonoBehaviour
{
[SerializeField] private AudioLoudnessDetection _loudnessListener;
void Start ( )
{
if ( _loudnessListener != null && _loudnessListener.listening )
{
// Uncomment if you'd also like to know if the loudness goes below a threshold.
// _loudnessListener.BelowLowThreshold = OnBelowThreshold;
_loudnessListener.AboveHighThreshold = OnAboveThreshold;
}
}
private void OnAboveThreshold ( float loudness )
{
// loudness above threshold.
}
}