0%

VR抓蚊子01-HRTF算法

距离上一次写博客,已经是三个星期之前的事情了。这段时间我在看操作系统的代码以及相关的背景知识,最后惊讶的发现居然是把一切都写下来效率更高。于是乎几乎一整个笔记上都写满了各种代码。

我是觉得这样做不一定比用打字记录的方式效率更高,但是无疑在那段困难的时光中,踏实的在纸上用笔书写这种方式,能够让我暂时忘记我要面对的是怎样一种绝望,而是更加集中精力到眼下的事情。

我可以说服自己我真的努力过了,虽然结果相比不尽如人意,但是确定不会挂掉也让我松了一口气。天知道我之前都经历了什么啊。有些困境如果你没有亲身经历过的话,是无论如何也不可能体会到那种绝望的。

就不提那时候的事情了。操作系统相关的笔记应该都会以某种方式整理到我最终的 rcore tutorial 中,也不会在某天不小心扔进垃圾桶的。这里还是将 rCore 改成 rcore,毕竟我要感谢帮助我的那位同学。

VR 第二次小作业快到 ddl 了,那我就看一下。

立体声

从百度百科来看,声道 (Sound Channel) 是指声音在录制和播放时在不同空间位置采集或回放的相互独立的音频信号,所以声道数也就是声音录制时的音源数量或回放时相应的扬声器数量。

除非进行了某种合成,那么播放时的声道数应不超过录制时的声道数。

使用单声道播放的声音被称为单声,我们带着耳机虽然两只耳机都能发出声音,但是其信号是完全相同,所以也只能称为单声;

使用双声道播放,如果两只耳机里面播放的音频信号不同,使得我们可以从中感知到虚拟的音源相对于我们的空间位置,那么无疑这种声音更为立体,就好像我们就处在音源附近一样,这种声音被称为立体声;

如果播放的声道数继续增加,比如在房间内的各个地方都放上音响,这样就称为环绕声。

HRTF 算法

HRTF (head-related transfer function) 可以帮助我们将单声转化为立体声。确切的说,是将原本单声道的一个信号,加上我们拥有的虚拟音源相对于我们的角度 $\theta,\phi$ 以及虚拟音源到我们的距离 $d$ 这些信息,生成针对于耳机的左耳、右耳两个声道的信号。

其中 $\theta$ 是水平的,从正左的 到正右的 $90^o$ ;

$\phi$ 是俯仰的,从正前方的 $0^o$ ,到正上方的 $90^0$ ,再到正下方的 $270^o(-90^o)$ 。

确定 $\theta,\phi$ 后,我们就可以获得两个信号 (左、右声道),我们将这个信号与单声道信号卷积(事实上在 Matlab 中通过 filter 一维滤波器)之后就可得到生成的左、右声道的两个信号。

这个信号可以在数据库 CIPIC 里面查到,文档 CIPIC doc 。里面 $\theta,\phi$ 都是离散的,$\theta$ 有 $25$ 个点, $\phi$ 有 $50$ 个点,获得的信号长度 $200$ 。

实时 HRTF

预先对于所有的 $\theta$ ,以及 $\phi={0^o,180^o}$ 使用 Matlab 合成好所有的双声道音频。

一段长度为 $5$ 秒钟的 $\text{wav}$ 格式单声道蚊子音频 $500\text{KiB}$ 。总共需要 $\text{500KiB}\times 25\times2\times2=50\text{MiB}$。

设置每只蚊子的声音都是循环播放的,我们只需在 onDrawEye 时根据当前的 $\theta,\phi$ 切换我们所要播放的音频即可。如果不用切换的话还好,要切换的话则需要从当前的时间开始播放。我们可以使用 seekTo 函数。

我们先看一下 android.media.MediaPlayer

多只蚊子的话好像不太行。就做成一只蚊子吧。

游戏环境

你处在一个封闭环境中,场景中每次会刷出 恰好一只 蚊子,只有它的位置距离你的视线小于一定数值才能被你看见,这迫使你必须通过立体声来定位蚊子的位置。

你可以看到以虚线形式出现的你的视线。

这样蚊子好像被局限到一个平面上去了… 但是也没办法,就这样吧。

方便起见,蚊子是一个球,你的视线距离球心小于 $R$ 可以看见球,小于 $r$ 球变为红色,此时按一下 trigger 球消失,游戏 reset 。

最简单的方式是:在 onNewFrame 里面判断要切换音频的话,就开始新音频的异步载入,回调函数中停止老音频,新音频从老音频停的位置继续播放。

断裂感

不知道怎样做才能减少切换音频时的断裂感。

如果要进行淡入淡出的话,这里面是不是通过锁帧比较方便?这样我们就固定知道音频段的固定播放长度。

这个实时淡入淡出感觉好复杂。效果实在不行的话再考虑吧。