引入
在某短视频平台上,有一种令我很震撼的音乐效果,戴上耳机听时,感觉声音在头顶盘旋;闭上眼睛,用心感受,这效果尤为明显,于是就思考着怎么实现这一效果。
工具
电脑软件cool edit pro(具体用法本文不赘述)或者 Matlab(自己动手实现,见下文)
· 基于Matlab的效果实现 :
-
预期实现效果:
音乐在头顶盘旋
https://pan.baidu.com/s/1caX4NybRQSn23m3BfqLq_A
音乐在头顶直线飞过
https://pan.baidu.com/s/1WtiCStS-qSvc7VlgRhNQXA
音乐在头顶任意自定义轨迹飞过
(见下文吧) -
原理
-
实现方法
①利用双声道粗暴的时延方法(两个文件,一个是主函数,另一个是构造的函数)
main.m
%实现音乐头顶盘旋的效果
clc,clear;
%% 参数设置
%人为定义
T=3;%一个圆弧的运动时间
d1=[-0.1 0];%左耳坐标
d2=[0.1 0];%右耳坐标
r=1;%圆弧半径
c=340;%空气中声速
[y,fs]=audioread('m6.mp3');%加载音频
%常数参数
Fs=44100;
N=T*Fs;%每一次环绕的点数
theta=linspace(-pi,0,N);
d3=[r*cos(theta);r*sin(theta)]';%移动声源的坐标
%% 主体程序
for i=1:N
delay(i)=(norm(d1-d3(i,:))-norm(d2-d3(i,:)))/c*Fs;%两耳相对时延采样点数
end
%记录要插值的位置
m=1;
for i=N-1:-1:2
if(delay(end)-delay(i+1)-m<0&&delay(end)-delay(i-1)-m>0)
s(m)=i;
m=m+1;
end
end
%数据拼接(因为数据长度太大)
row_all=size(y,1);
col=floor(row_all/N);
index_max=col*N;
y(index_max+1:end,:)=[];
T=reshape(y(:,1),N,col);
%插值实现时延
zz=[];
for j=1:col
y1=T(:,j);
y2=y1;
if(j==1)
t=[0,y1(1),y1(end),T(1,j+1)];
elseif(j==col)
t=[T(end,j-1),y1(1),y1(end),0];
else
t=[T(end,j-1),y1(1),y1(end),T(1,j+1)];
end
if(mod(j,2)==0)
[y1,y2]=insert_values(y1,y2,s,t);
else
[y2,y1]=insert_values(y1,y2,s,t);
end
z=[y1,y2];
zz=[zz;z];
end
%% 结果
p=audioplayer(zz,Fs);
play(p);%音频播放
audiowrite('faded_circle.wav',zz,Fs);%写入到文件
insert_values.m
%插值实现时延(此为插值函数)
function [y1,y2]=insert_values(y1,y2,s,t)
m=length(s);
for i=m:-1:1
y1=[y1(1:s(i));(y1(s(i))+y1(s(i)+1))/2;y1(s(i)+1:end)];
end
tmp1=linspace(t(1),t(2),ceil(m/2)+2);
tmp1(end)=[];tmp1(1)=[];
tmp2=linspace(t(3),t(4),m-ceil(m/2)+2);
tmp2(end)=[];tmp2(1)=[];
y2=[tmp1';y2;tmp2'];
②利用物理建模的方法(分析每个采样时刻,两耳实际接收到的信号)
main.m
%分析每个采样时刻,两耳实际接收到的信号。已做过插值,问题是没能彻底消除杂点。
clc,clear;
%% 参数设置
%人为定义
[y,Fs]=audioread('m6.mp3');%加载音频
T=3;%一个圆弧的运动时间
c=340;%空气中声速
r=1;%圆弧半径
d1=[-0.1 0];%左耳坐标
d2=[0.1 0];%右耳坐标
%常数参数
z=y(:,1);
N=length(z);
x1=zeros(N+200,1);%左耳波形记录
flag1=zeros(N+200,1);%左耳记录位
x2=zeros(N+200,1);
flag2=zeros(N+200,1);
n=T*Fs;
theta=linspace(0,2*pi,n);
d3=[r*cos(theta);r*sin(theta)]';%%移动声源的坐标,两列
%% 主体程序
%一位一位的将声音信号记录到左右耳
for i=1:N
n1(i)=floor(norm(d1-d3(mod(i,n)+1,:))/c*Fs);
x1(i+n1(i))=x1(i+n1(i))+z(i);
flag1(i+n1(i))=1;
if(i>1&&n1(i)>n1(i-1))
x1(i+n1(i-1))=(x1(i-1+n1(i-1))+x1(i+1+n1(i-1)))/2;
flag1(i+n1(i-1))=1;
end
n2(i)=floor(norm(d2-d3(mod(i,n)+1,:))/c*Fs);
x2(i+n2(i))=x2(i+n2(i))+z(i);
flag2(i+n2(i))=1;
if(i>1&&n2(i)>n2(i-1))
x1(i+n2(i-1))=(x1(i-1+n2(i-1))+x1(i+1+n2(i-1)))/2;
flag2(i+n2(i-1))=1;
end
end
%插值法去零值(试图消除杂点)
j=1;
for i=2:length(x1)-1
if(i>j)
if(flag1(i)==0)
j=i+1;
while(flag1(j)==0&&j<length(x1))
j=j+1;
end
x1(i-1:j)=linspace(x1(i-1),x1(j),j-i+2);
end
if(flag2(i)==0)
j=i+1;
while(flag2(j)==0&&j<length(x2))
j=j+1;
end
x2(i-1:j)=linspace(x2(i-1),x2(j),j-i+2);
end
end
end
x=[x1,x2];
%% 结果
p=audioplayer(x,Fs);
play(p);%音频播放
audiowrite('faded_circle.wav',x,Fs);%写入到文件
④音乐在头顶任意自定义轨迹飞过
上文有两种实现途径,均只需要改动声源轨迹部分为自定义轨迹,这里以第一种为例,列出程序。
· 预期轨迹
· 程序(两个文件,一个是主函数,另一个是构造的函数)
main.m
%实现音乐头顶盘旋的效果
clc,clear;
%% 参数设置
%人为定义
T=3;%半边V形的运动时间
d1=[-0.1 0];%左耳坐标
d2=[0.1 0];%右耳坐标
a=1;%短边
b=1;%长边
c=340;%空气中声速
[y,fs]=audioread('m6.mp3');%加载音频
%常数参数
Fs=44100;
N=T*Fs;%每一次环绕的点数
xx=[linspace(-a,a,N)]';
yy=[linspace(0,b,N/2) linspace(b,0,N/2)]';
d3=[xx,yy];%移动声源的坐标,两列
%% 主体程序
for i=1:N
delay(i)=(norm(d1-d3(i,:))-norm(d2-d3(i,:)))/c*Fs;%两耳相对时延采样点数
end
%记录要插值的位置
m=1;
for i=N-1:-1:2
if(delay(end)-delay(i+1)-m<0&&delay(end)-delay(i-1)-m>0)
s(m)=i;
m=m+1;
end
end
%数据拼接(因为数据长度太大)
row_all=size(y,1);
col=floor(row_all/N);
index_max=col*N;
y(index_max+1:end,:)=[];
T=reshape(y(:,1),N,col);
%插值实现时延
zz=[];
for j=1:col
y1=T(:,j);
y2=y1;
if(j==1)
t=[0,y1(1),y1(end),T(1,j+1)];
elseif(j==col)
t=[T(end,j-1),y1(1),y1(end),0];
else
t=[T(end,j-1),y1(1),y1(end),T(1,j+1)];
end
if(mod(j,2)==0)
[y1,y2]=insert_values(y1,y2,s,t);
else
[y2,y1]=insert_values(y1,y2,s,t);
end
z=[y1,y2];
zz=[zz;z];
end
%% 结果
p=audioplayer(zz,Fs);
play(p);%音频播放
audiowrite('faded_V.wav',zz,Fs);%写入到文件
构造函数insert_values.m同上。实现效果见文件:https://pan.baidu.com/s/1KAScd_GCOtBVlbtEy8hx3Q
- 结论
1.实现效果还不错,处理一些电音音频效果甚好。
2.第二种实现方法,虽然插值过,但是音频听起来还是有些杂点。
3.文中方法比较简陋,欢迎交流讨论,改进现有方法,探求更多方法。