一、频谱图分析
无意中在b站看到有up在某项目中实现了常见的音乐跳动特效,想到这就是音频的频谱图,于是想试着分析并模仿这种特效的制作。

所谓频谱图,即以横轴纵轴的波纹方式,记录画出信号在各种频率的图形资料。横轴为频率,纵轴为强度,即信号在这个频率的能量是多还是少。符合认知的是,当观察音乐特效时,高音部分出现后右边的音乐条会变高,左边会变低,是不同频率强度不同导致的。
因此要得到会变化的特效图,只要不断地画频谱图即可。因为同步和计算力的原因,我设计每次画出0.2秒信号的频谱图,0.2秒更新一次。
为了得到0.2秒的频谱,我使用快速傅立叶变换(FFT)。

二、Matlab实现
(随便下载一首歌"汪苏泷 - 年轮.mp3")
%% begin
clc;
clear;
close all;
%% settings
div = 0.2; % 设置间隔
%% read file
[y, Fs] = audioread("汪苏泷 - 年轮.mp3");
T = 1/Fs;
L = length(y)-mod(length(y),2); % 防止索引报错
t = (0:L-1)*T;
l = div*Fs;
%% play music
sound(y,Fs);
%% FFT
for i=1:L/(l-1)
t1 = clock; % 计时
x = y(i*l:(i+1)*l);
FFT(x,l,Fs); % 变换、绘画
t2 = etime(clock,t1); % 计时
pause(div-t2); % 确保0.2秒同步,同时给电脑绘画的时间
end
function [] = FFT(x,l,Fs) % 对0.2秒信号进行FFT
Y = fft(x);
P2 = abs(Y/l);
P1 = P2(1:l/2+1);
P1(2:end-1) = 2*P1(2:end-1);
f = Fs*(0:(l/2))/l;
plot(f,P1); % 绘画频谱图
axis([1e3 2.5e4 0 0.15]);
end
效果:

看得出来不是很好,原因在于低频太多,强度太大,因此用高通滤波器滤掉这部分。用工具设计一个巴特沃斯高通滤波器,滤掉0-1500Hz左右的频率。

function Hd = btw
%BTW 返回离散时间滤波器对象。
% MATLAB Code
% Generated by MATLAB(R) 9.8 and Signal Processing Toolbox 8.4.
% Generated on: 29-Aug-2021 01:48:30
% Butterworth Highpass filter designed using FDESIGN.HIGHPASS.
% All frequency values are in Hz.
Fs = 44100; % Sampling Frequency
Fstop = 1000; % Stopband Frequency
Fpass = 2000; % Passband Frequency
Astop = 80; % Stopband Attenuation (dB)
Apass = 1; % Passband Ripple (dB)
match = 'stopband'; % Band to match exactly
% Construct an FDESIGN object and call its BUTTER method.
h = fdesign.highpass(Fstop, Fpass, Astop, Apass, Fs);
Hd = design(h, 'butter', 'MatchExactly', match);
% [EOF]
在源代码中添加:
%% HPF
Hlp = btw;
y1 = filtfilt(Hlp.sosMatrix, 1, y);
效果图:

这次就好看很多。完整代码:
%% begin
clc;
clear;
close all;
%% settings
div = 0.2;
%% read file
[y, Fs] = audioread("汪苏泷 - 年轮.mp3");
T = 1/Fs;
L = length(y)-mod(length(y),2);
t = (0:L-1)*T;
l = div*Fs;
%% HPF
Hlp = btw;
y1 = filtfilt(Hlp.sosMatrix, 1, y);
%% play music
sound(y,Fs);
%% FFT
for i=1:L/(l-1)
t1 = clock;
x = y1(i*l:(i+1)*l);
FFT(x,l,Fs);
t2 = etime(clock,t1);
pause(div-t2);
end
function [] = FFT(x,l,Fs)
Y = fft(x);
P2 = abs(Y/l);
P1 = P2(1:l/2+1);
P1(2:end-1) = 2*P1(2:end-1);
f = Fs*(0:(l/2))/l;
plot(f,P1);
%axis([1e3 1.8e4 0 0.15]);
end
运行代码:

三、进阶
到这里就只剩下采样和上色等事情了,不是Matlab需要做的(笑),要实现音乐条,只需要从FFT的横坐标(共20000个点)中均匀取十几个点,然后放上不同长度的矩形即可,这点计算量对电脑来说还是很轻松的。
要想把图形变得更好看一点,可以尝试带通滤波器,可以有效修饰两端不美观的部分。