聲場 | 音頻波形顯示探究

圖1 封面

引言

本文將持續(xù)研究隱藏在電音合成軟件背后的數(shù)學(xué)問題:線性代數(shù)、信號與系統(tǒng)、和聲學(xué)基礎(chǔ)。以上的數(shù)學(xué)知識與游戲開發(fā)、計(jì)算機(jī)音頻、計(jì)算機(jī)圖像聯(lián)系緊密。正因?yàn)閾碛袕?qiáng)大的數(shù)學(xué)基礎(chǔ),計(jì)算機(jī)模擬的音像世界才可以像今天這樣逼真。數(shù)學(xué)在音頻編輯器的開發(fā)中占有重要的位置。對此數(shù)學(xué)單獨(dú)進(jìn)行研究,由此顯得非常重要。本文將對聲音和合成和顯示進(jìn)行探究,進(jìn)而使用這一些專業(yè)知識編寫音頻編輯器或圖形程序。

一.線性代數(shù)

1.點(diǎn)乘

我們知道點(diǎn)乘在數(shù)學(xué)中,又稱數(shù)量積。只需要記住,向量點(diǎn)乘就是對應(yīng)分量乘積的和,其結(jié)果是一個標(biāo)量。在平面中,兩個向量的點(diǎn)乘可表示為:

設(shè)a = \begin{bmatrix} x_1\\ y_1\\ \end{bmatrix} ,b = \begin{bmatrix} x_2\\ y_2\\ \end{bmatrix}
a \cdot b = [x_1*y_1 + x_2*y_2]

一般來說,點(diǎn)乘的結(jié)果描述了兩個向量的相似程度,點(diǎn)乘結(jié)果越大,兩向量越接近。點(diǎn)乘等于向量的大小與向量夾角cos值的積。其公式為:

a \cdot b =||a||*||b||cos\theta \tag{1}

實(shí)際上,我們在計(jì)算中,更重要的是求出兩個向量的之間的夾角。對上述的公式(1)稍作變換,可得:

\theta = arccos(\frac{a \cdot b}{||a||*||b||})

值得注意的是,此公式十分重要。在本文中被用于Unity仿真中的向量夾角計(jì)算。

2.線性空間變換

方陣能描述任意的線性變換。其中,線性變換,從感性上講,指的是對圖片或函數(shù)圖像等其所在空間的,拉伸、旋轉(zhuǎn)、仿射等變換。
想象任意一個2維空間的向量,比如說:a = \begin{bmatrix}3\\5\\\end{bmatrix}。那么,向量a其實(shí)可以表示為:a = 3*\begin{bmatrix}1\\0\\ \end{bmatrix} + 5*\begin{bmatrix}0\\1\\ \end{bmatrix}。由于向量可視為空間中的一個點(diǎn),向量a表示從原點(diǎn)向x走3個單位后,再向y5個單位。更進(jìn)一步地,對于任意一個向量:b = \begin{bmatrix}x_1\\y_1\\\end{bmatrix},可以表示為:從原點(diǎn)出發(fā),向x軸走x_1個單位后,再向y軸方向走y_1個單位,即到達(dá)向量b的所在地。那么,任意一個二維空間中的向量,可以表示為基向量的線性組合

一個任意的向量a,被描述為了兩個基向量x= \begin{bmatrix}1\\0\\ \end{bmatrix} , y = \begin{bmatrix}0\\1\\ \end{bmatrix}的線性組合。但實(shí)際上,基向量不一定要相互垂直(講人話:x軸和y軸不一定要相互垂直)。只要兩個向量不在一條直線上,就可以構(gòu)成一組基向量來描述二維空間中的任意一個點(diǎn)。

以下是幾個不同基向量所張成的空間。為了方便觀察,我在其中放置一樣的y=3.5sin(5x)函數(shù)??梢杂^察到,隨著選取的基向量的不同,空間可以發(fā)生放大、縮小、切變等線性變換。存在于里面的函數(shù)圖像,雖然位置沒有發(fā)生改變,但是由于函數(shù)所處空間的改變,函數(shù)“被迫”發(fā)生了改變。

圖2 空間變換

設(shè)基向量X = \begin{bmatrix}x_1\\y_1\\\end{bmatrix},Y = \begin{bmatrix}x_2\\y_2\\\end{bmatrix}張成了一個二維平面空間。使用基向量X,Y構(gòu)成一個2\times2的矩陣M:
M = \begin{bmatrix} x_1 & y_1 \\ x_2 & y_2 \\ \end{bmatrix} \tag{2}
用一個向量a = \begin{bmatrix}x&y\end{bmatrix}乘以該矩陣M,則稱為對向量a的一次“變換”。
a*M = \begin{bmatrix}x&y\end{bmatrix} \begin{bmatrix} x_1 & y_1 \\ x_2 & y_2 \\ \end{bmatrix} \tag{2} = \begin{bmatrix}x*x_1 + y*x_2 & x*y_1 + y*y_2\end{bmatrix}

3. 旋轉(zhuǎn)

問題的提出
將一個函數(shù)圖像逆時針旋轉(zhuǎn)30°

具體求解步驟
Step 1:隨機(jī)生成一個函數(shù)序列
Step 2:構(gòu)造方陣M
Step 3:函數(shù)序列每一點(diǎn)的坐標(biāo),乘以方陣M
Step 4:顯示該函數(shù)圖像

函數(shù)序列生成
使用matlab自帶的rand()函數(shù)生成隨機(jī)離散序列X(n),使用smooth()函數(shù)對X(n)進(jìn)行數(shù)據(jù)平滑。
其代碼如下:

clc;
clear;
%%
%%隨機(jī)生成一個起點(diǎn)和終點(diǎn)都在0附近,均值為0的函數(shù)波形
n = 300;
x = 0.5*rand(1,n);
x = x - mean(x);
x(1:10) = 0;
x(n-10:n) = 0;
for i = 1:10
    x = smooth(x,10);
end

x(1) = 0;
x(length(x)) = 0;
X = zeros(n,2);
X(:,1) = linspace(0,1,n);
X(:,2) = x;
plot(X(:,1),X(:,2),'.')

構(gòu)造方陣M
在笛卡爾坐標(biāo)系中,基向量X = \begin{bmatrix}1&0\\\end{bmatrix},Y = \begin{bmatrix}0&1\\\end{bmatrix}.使得向量X、Y向逆時針旋轉(zhuǎn)30°,可得:
X = \begin{bmatrix}cos30°&sin30°\\\end{bmatrix}
Y = \begin{bmatrix}cos(30° + 90°)&sin(30° + 90°)\\\end{bmatrix}
則,M = \begin{bmatrix} cos30° & sin30° \\ cos(30° + 90°) & sin(30° + 90°) \\ \end{bmatrix} \tag{2}

結(jié)果演示
根據(jù)上文求解步驟編寫代碼,實(shí)現(xiàn)函數(shù)旋轉(zhuǎn)算法。代碼運(yùn)行結(jié)果如下所示。

圖3 函數(shù)旋轉(zhuǎn)

穩(wěn)定性測試
使用Matlab,隨機(jī)生成一個函數(shù)序列,使用開發(fā)的旋轉(zhuǎn)算法持續(xù)旋轉(zhuǎn)函數(shù)圖像,并將結(jié)果打印出來,觀察算法的穩(wěn)定性。由結(jié)果可見,代碼對函數(shù)圖像的旋轉(zhuǎn)穩(wěn)定可靠。

核心代碼

for angle = 1:10:360
%目標(biāo)向量
pos1 = [cosd(angle);sind(angle)];
pos2 = [0;0];
pos3 = pos1 - pos2;
pos0 = [1;0];
%%
if(pos3(2)>=0)
    alpha = acos(sum(pos3.*pos0)/(norm(pos3)*norm(pos0)));
else
    alpha = -1*acos(sum(pos3.*pos0)/(norm(pos3)*norm(pos0)));
end

%(alpha/pi)*180
temp = [cos(alpha) sin(alpha);...
        cos(alpha + pi/2) sin(alpha+ pi/2);];
A = norm(pos3)/1;
result = X*(A*temp) + pos2';
plot(result(:,1),result(:,2),'.');
hold on
end

其結(jié)果如下:

圖4 算法穩(wěn)定性測試

模型的改進(jìn)
令人興奮的是,將圖片放置在空間中,使用方陣M對空間進(jìn)行線性變換,可以很方便實(shí)現(xiàn)圖片的放大、縮小、旋轉(zhuǎn)操作。只需寫一點(diǎn)點(diǎn)matlab代碼。便可得到以下結(jié)果:
圖5 圖像放大、縮小、旋轉(zhuǎn)代碼實(shí)現(xiàn)結(jié)果

Unity仿真

在matlab完成算法探究以后,在游戲開發(fā)引擎Unity上實(shí)現(xiàn)該算法,完成算法的落地。

具體求解步驟
Step 1:使用Unity搭建參加場景,創(chuàng)建地面(Plane)、圓柱(Cylinder)等物體
Step 2:編寫腳本,綁定組件。腳本邏輯是:當(dāng)用戶按下鼠標(biāo)時,攝像頭坐標(biāo)系中發(fā)出的射線與地面相互碰撞。根據(jù)此來確定基向量以及方陣M。
Step 3:根據(jù)方陣M計(jì)算組件線段渲染器(LineRender)應(yīng)該所處的位置
Step 4:顯示該函數(shù)圖像

穩(wěn)定性測試
多次地、隨機(jī)地點(diǎn)擊游戲畫面,測試算法的穩(wěn)定性和性能。實(shí)驗(yàn)結(jié)果表明,算法穩(wěn)定可靠地運(yùn)行,完成了對算法的落地。.

圖6 Unity仿真測試1

圖7 Unity仿真測試2

代碼

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class LineTest : MonoBehaviour
{
    public float length = 0;
    public float height = 0;//線段的高度
    public float w = 0;    //角頻率
    public float A = 0;     //震動幅度
    private Vector3 Vec;  //用于存儲每一點(diǎn)的坐標(biāo)
    private LineRenderer lineRenderer;

    // Start is called before the first frame update
    void Start()
    {
        Vec = new Vector3();
        //設(shè)置長度
        GetComponent<LineRenderer>().positionCount = (int)length;
    }
    public void Run(float x,float y){
        float l = (float)Math.Sqrt(x*x + y*y);//模長,假設(shè)0為原點(diǎn)
        float alpha = 0;                      //用于求向量夾角
        for (int i=0;i<length;i++){
            
            float posX = (float)(i/length);
            float posY = A*(float)Math.Sin(w*posX);

            alpha = 0;
            if(y>=0){
                alpha = (float)Math.Acos(x/l);
            }
            else{
                alpha = -1*(float)Math.Acos(x/l);
            }
            Vec.x = 0.1f*(float)(posX*l*Math.Cos(alpha) + posY*l*Math.Cos(alpha + Math.PI/2));
            Vec.y = height;
            Vec.z = 0.1f*(float)(posX*l*Math.Sin(alpha) + posY*l*Math.Sin(alpha + Math.PI/2));

            GetComponent<LineRenderer>().SetPosition(i,Vec);
        }
    }

    // Update is called once per frame
    void Update()
    {

    }
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容