自己手动实现左右耳的声音区别
人之所以能通过听到的声音辨别音源的方向,主要就是因为左右耳的双重信号接收。
在左边的声音到达左耳会比到达右耳要快一些,同时,频率越高的音段会被头部遮挡一部分,导致右耳听到的声音要比左耳听到的声音小。
除此之外,人耳的形状实际上是一个滤波器,当声音从正面传来时,可以几乎无阻碍的被我们接收,而背面的则会被耳廓遮挡,相当于一个低通滤波器,我们就依据这个来编写脚本。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AudioStereo : MonoBehaviour
{
public GameObject leftPlayer;
public GameObject rightPlayer;
public Transform mainCamera;
public Transform leftEarPos;
public Transform rightEarPos;
public float strength = 0.3f;
public float reduction = 0.4f;
public float delayTimePerUnit = 0.02f;
private AudioSource leftStereo;
private AudioSource rightStereo;
private AudioLowPassFilter leftLowPass;
private AudioLowPassFilter rightLowPass;
private float standardVolume = 0.5f;
private Vector3 relativePos;
private float degree;
private float lastDegree;
private bool ifLeft;
private float timeDifference;
private float rightTime;
private float lowPassThreshord = 30 * Mathf.Deg2Rad;
private float lowPassMaxCutoff = 5000;
void Start()
{
leftStereo = leftPlayer.GetComponent<AudioSource>();
rightStereo = rightPlayer.GetComponent<AudioSource>();
leftLowPass = leftPlayer.GetComponent<AudioLowPassFilter>();
rightLowPass = rightPlayer.GetComponent<AudioLowPassFilter>();
leftStereo.volume = standardVolume;
rightStereo.volume = standardVolume;
leftStereo.panStereo = -1;
rightStereo.panStereo = -1;
relativePos = (mainCamera.position - transform.position).normalized;
degree = Mathf.Acos(Vector3.Dot(mainCamera.forward, relativePos));
lastDegree = degree;
}
void Update()
{
relativePos = (mainCamera.position - transform.position).normalized;
degree = Mathf.Acos(Vector3.Dot(mainCamera.forward, relativePos));
if (Vector3.Cross(relativePos, mainCamera.forward).Equals(mainCamera.up))
ifLeft = true;
else
ifLeft = false;
leftStereo.volume = standardVolume + (ifLeft ? strength : -reduction) * Mathf.Sin(degree);
leftStereo.volume = standardVolume + (!ifLeft ? strength : -reduction) * Mathf.Sin(degree);
if (degree != lastDegree)
{
timeDifference = (Vector3.Distance(leftEarPos.position, transform.position) - Vector3.Distance(rightEarPos.position, transform.position)) * delayTimePerUnit;
rightTime = leftStereo.time + timeDifference;
if (rightTime < 0)
rightTime += leftStereo.clip.length;
else if (rightTime > leftStereo.clip.length)
rightTime -= leftStereo.clip.length;
rightStereo.time = rightTime;
if (degree > lowPassThreshord)
{
leftLowPass.cutoffFrequency = lowPassMaxCutoff * (Mathf.Cos(degree) + Mathf.Cos(lowPassThreshord)) / (1 + Mathf.Cos(lowPassThreshord));
if (leftLowPass.cutoffFrequency <= 0)
leftLowPass.cutoffFrequency = 0;
rightLowPass.cutoffFrequency = leftLowPass.cutoffFrequency;
}
lastDegree = degree;
}
}
}
上面代码的原理就是通过创造两个AudioSource来模拟传播到左右耳的声音,音量衰减则通过Inspector界面的曲线绘制,而上面的代码则负责控制声音与方向的关系。
然而这个实现存在一些问题,首先是自己实现对于参数的控制十分困难,因为声音到达左右耳的时间实际上只相差很短的时间,十分难以把控数据,其次是并没有考虑到环境对声音的影响,包括障碍物的遮挡与回音,而这部分难度较高,我选择放弃自己编写,使用现有的音频插件。
ResournanceAudio
这是Google的开源音频插件,主要用来支持VR音效,可以较好的实现环绕立体声,不过这个插件也没法计算障碍物的影响,之后我们会介绍另一个插件SteamAudio,但现在我们先介绍一下ResournanceAudio的使用方式:
首先在Package Manager中下载插件,unity的官方插件库中即可下载,之后我们勾选AudioSource的Spatilize选框,让其可以被插件影响,之后新建一个AudioMixer,打开编辑页面,打开master选项:
之后在右侧选择AddEffect,添加Resournance Audio Renderer,之后在Audio Source中将mixer设置为Output,就可以使用Resournance Audio插件了。
具体使用不再介绍,有兴趣的读者可以去查看谷歌的官方API,我们着重介绍另一种插件——Steam Audio。
Steam Audio
这是Valve为解决VR空间音效而编写的插件,当然普通的3D项目也可以使用,我们需要先去官网下载对应的unitypackage:
Steam Audio官网地址
下载完毕后导入我们的项目中,我们需要先打开Windows-Steam Audio窗口:
之后我们将其关闭即可,这时在我们的Hierarchy页面就会出现一个Steam Audio Manager Settings。
之后我们为每一个挂载了AudioSource组件的物体添加Steam Audio Source脚本,为带有Audio Listener的摄像机添加Steam Audio Listener脚本,并为所有要纳入计算的障碍物添加Steam Audio Geometry脚本,之后回到Steam Audio Manager Settings物体,点击Pre-Export Scene,就可以实现环境对声音的影响了。
同时,我们还需要打开Project Settings→Audio,将Spatializer Plugin与Ambisonic Decoder Plugin调整为steamAudio,之后就能够听到明显的区别了。