一、简介

音频是多媒体中的一种重要媒体。我们能够听见的音频信号的频率范围大约是20Hz-2OkHz,其中语音大约分布在300Hz-4kHz之内,而音乐和其他自然声响是全范围分布的。声音经过模拟设备记录或再生,成为模拟音频,再经数字化成为数字音频。通过对常用数字滤波器的设计和实现,掌握数字信号处理的工作原理及设计方法。
利用已经学过的数字信号处理的知识,设计一数字调音台实现以下功能:
1) 通过菜单选择待处理的歌曲;
2) 实时显示处理前的信号时域波形文件,能够对波形文件进行缩放显示;
3) 实时显示处理前的信号频谱,采用柱状显示,类似于音乐播放器软件上的频谱显示;
4) 采用常见的低通、高通、带通、带阻(50Hz)滤波器对信号进行滤波,滤波器阶数可调,带宽可调,观察处理后的信号频谱的变化;
5) 通过扬声器播放处理过的信号,听信号有什么变化;
6) 编制 GUI 用户界面,在用户界面上实现上述所有功能。
7) 实现数字均衡器的功能。
1 音频滤波器设计概述
数字音频信号可以认为是一组时域离散的输入信号序列,我们可以基于FIR滤波器理论或者IIR滤波器理论对音频信号进行滤波。此外,在音频抽样分析中,奈奎斯特抽样定理有重要的应用。

2 FIR滤波器和IIR滤波器的比较选择
IIR滤波器可以用比FIR滤波器少的阶数来满足相同的技术指标,这样,IIR滤波器所用的存储单元和所用的运算次数都比FIR滤波器少。FIR滤波器可得到严格的相位,而IIR滤波器不能得到。事实上,IIR 滤波器的选频特性越好,它的相位的非线性就越严重。因此在需要严格线性相位的情况下应该选择FIR滤波器。
IIR滤波器可利用模拟滤波器现成的设计公式、数据和表格,因而计算工作量较小,对计算工具要求不高。IIR滤波器主要是设计规格化的、频率特性为分段常数的标准低通、高通、带通、带阻和全通滤波器,而FIR滤波器可设计出理想正交变换器、理想微分器、线性调频器等各种网络,适应性较广。
针对音频信号的滤波器,对相位要求不是主要的,因此选用IIR滤波器较为合适,可以充分发挥其经济和高效的特点。

2 基于巴特沃斯的低通、高通、带通、带阻滤波器设计
巴特沃斯滤波器是电子滤波器的一种。巴特沃斯滤波器的特点是通频带的频率响应曲线最平滑,没有起伏,而在阻频带则逐渐下降为零。在振幅的对数对角频率的波特图上,从某一边界角频率开始,振幅随着角频率的增加而逐步减少,趋向负无穷大。
巴特沃斯模拟滤波器的归一化原型滤波器的系统函数的系数以及根或二阶因式系数的数值可很方便查到。只要确定了所需滤波器在通带截止频率,阻带截止频率处的衰减,就可求出滤波器的阶数,然后可查表求得归一化低通原型滤波器的系统函数,经过频带变换关系求得所需(高通、带通、带阻)各类滤波器。

3 数字均衡器概述
3.1 均衡器概念
均衡器,是一种可以分别调节各种频率成分电信号放大量的电子设备,通过对各种不同频率的电信号的调节来补偿扬声器和声场的缺陷,补偿和修饰各种声源及其它特殊作用,一般调音台上的均衡器仅能对高频、中频、低频三段频率电信号分别进行调节。在通信系统中,在系带系统中插入均衡器能够减小码间干扰的影响。
一个好的均衡器能校正音频设备所产生的频率失真;校正由于建筑学共振性的不均匀所带来的传输增益的频率失真;调整某些易反馈的频率成分,抑制声反馈,提高会场增益;刻画演员和乐器的音色修改,提高艺术效果。

3.2 人耳对声音的感受
均衡音乐提高效果时需考虑人耳对各频段的音频信号不同的感受,具体音感特征见下表:

表1 频率的音感特征
30-60Hz 沉闷 如没有相当大的响度,人耳很难感觉。
60-100Hz 沉重 80Hz附近能产生极强的“重感”效果,响度很高也不会给人舒服的感觉,可给人以强烈的刺激作用。
100-200Hz 丰满
200-500Hz 力度 易引起嗡嗡声的烦闷心理。
500-1KHz 明朗 附近如提升10dB,会明显产生一种嘈杂感,狭窄感。
1K-2KHz 透亮 附近明亮感关系最大。
2K-4KHz 尖锐 形成尖啸,锐利的感觉。
8K-16KHz 纤细 >7.5KHz音感清彻纤细。

3.3 均衡器的本质
均衡器事实上是由许多个带通滤波器组成的。对各个频段的信号幅值的改变是通过对各个频段滤波然后再乘以滤波器增益值实现的。

二、源代码

function varargout = ClassDesign(varargin)
gui_Singleton = 1;
gui_State = struct('gui_Name',       mfilename, ...
                   'gui_Singleton',  gui_Singleton, ...
                   'gui_OpeningFcn', @ClassDesign_OpeningFcn, ...
                   'gui_OutputFcn',  @ClassDesign_OutputFcn, ...
                   'gui_LayoutFcn',  [] , ...
                   'gui_Callback',   []);
if nargin && ischar(varargin{1})
    gui_State.gui_Callback = str2func(varargin{1});
end

if nargout
    [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
else
    gui_mainfcn(gui_State, varargin{:});
end

function ClassDesign_OpeningFcn(hObject, eventdata, handles, varargin)
handles.output = hObject;

global timerPeriod timerFPeriod timerDEPeriod
timerPeriod=0.1;
timerFPeriod=0.2;
timerDEPeriod=timerPeriod;
handles.pTimer=timer;
set(handles.pTimer, 'ExecutionMode', 'FixedRate');
set(handles.pTimer, 'Period', timerPeriod); 
set(handles.pTimer, 'TimerFcn', {@showWave, handles}); 
handles.afTimer=timer;
set(handles.afTimer, 'ExecutionMode', 'FixedRate');
set(handles.afTimer, 'Period', timerFPeriod); 
set(handles.afTimer, 'TimerFcn', {@showFilterdWave, handles}); 
handles.deTimer=timer;
set(handles.deTimer, 'ExecutionMode', 'FixedRate');
set(handles.deTimer, 'Period', timerDEPeriod); 
set(handles.deTimer, 'TimerFcn', {@showDEWave, handles}); 

global STOP
global PAUSE
global PLAY
STOP=0;
PAUSE=1;
PLAY=2;

global playPnt playFPnt
playPnt=1;
playFPnt=1;

global filterState deState
filterState=0;
deState=0;
guidata(hObject, handles);

function varargout = ClassDesign_OutputFcn(hObject, eventdata, handles) 
varargout{1} = handles.output;

function openFileButton_CreateFcn(hObject, eventdata, handles)

function openFileButton_Callback(hObject, eventdata, handles)   
global PAUSE playState playFState STOP
global timerPeriod timerFPeriod
global frameNum fftFrame Frame
global NframeNum NFrame NfftFrame
global N NFFT FS NN NNFFT
global A DATA maxData
global fileName filePath FILE
A=1;
cd('music');
[fileName, filePath, ~] = uigetfile({'*.mp3';'*.wav';'*.mp4'}, 'Select Music File');
cd('..');
if fileName ~= 0 
    set(handles.deFilterMode, 'enable', 'on');
    set(handles.playButton, 'enable', 'on');
    set(handles.resetButton, 'enable', 'on');
    set(handles.playFButton, 'enable', 'off');
    set(handles.resetFButton, 'enable', 'off');
    FILE = [filePath fileName];
    set(handles.fileNameDisp, 'string', FILE);
    [DATA, FS] = audioread(FILE);
    DATA=DATA(:,1); 
    N=FS*timerPeriod;
    NN=FS*timerFPeriod;
    NFFT = 2^nextpow2(N);
    NNFFT = 2^nextpow2(NN);
    L=size(DATA, 1);
    r=mod(L,N);
    maxData  = max(DATA);
    frameNum = ceil(L/N);
    Frame    = zeros(N, frameNum); 
    fftFrame = zeros(N, frameNum);
    Nr=mod(L,NN);
    NframeNum = ceil(L/NN);
    NFrame    = zeros(NN, NframeNum); 
    NfftFrame = zeros(NN, NframeNum);
    for i=1:frameNum
        if i==frameNum
            Frame(:,i)=[DATA((L-r)+1:L); zeros(frameNum*N-L,1)];
        else
            Frame(:,i)=DATA(((i-1)*N+1):N*i);
        end
        fftFrame(:,i)=abs(fft(Frame(:,i))/max(fft(Frame(:,i))));
    end
    for i=1:NframeNum
        if i==NframeNum
            NFrame(:,i)=[DATA((L-Nr)+1:L); zeros(NframeNum*NN-L,1)];
        else
            NFrame(:,i)=DATA(((i-1)*NN+1):NN*i);
        end
        NfftFrame(:,i)=abs(fft(NFrame(:,i))/max(fft(NFrame(:,i))));
    end
    playState=PAUSE;
    playFState=STOP;
end

function resetButton_Callback(hObject, eventdata, handles)
global STOP playState playPnt filterState deState
playState=STOP;
set(handles.openFileButton, 'enable', 'on');
set(handles.playButton, 'string', 'play');
if filterState == 1
    set(handles.playFButton, 'enable', 'on');
    set(handles.resetFButton, 'enable', 'on');
end
if deState==1
	set(handles.depButton, 'enable', 'on');
	set(handles.derButton, 'enable', 'on');
end
stop(handles.pTimer);
playPnt=1;

function playButton_CreateFcn(hObject, eventdata, handles)

function playButton_Callback(hObject, eventdata, handles)
global PLAY STOP PAUSE playState filterState
if playState == PAUSE || playState == STOP
	set(handles.openFileButton, 'enable', 'off');
    set(handles.playButton, 'string', 'pause');
    set(handles.playFButton, 'enable', 'off');
    set(handles.resetFButton, 'enable', 'off');
	playState=PLAY;
	start(handles.pTimer);
elseif playState == PLAY
	set(handles.openFileButton, 'enable', 'on');
	set(handles.playButton, 'string', 'play');
    if filterState==1
        set(handles.playFButton, 'enable', 'on');
        set(handles.resetFButton, 'enable', 'on');
    end
	playState=PAUSE;   
	stop(handles.pTimer);
end

function showWave(hObject,eventdata,handles)  
global frameNum playPnt FS fftFrame Frame N NFFT maxData A
F=(0:NFFT/44-1)*FS/NFFT;
if playPnt>frameNum
    playPnt=1;
else 
    sound(Frame(:, playPnt),FS);
    plot(1:1:N, A*Frame(:, playPnt), 'parent', handles.originalSigT);
    set(handles.originalSigT, 'xlim', [0, N]);
    set(handles.originalSigT, 'ylim', [-maxData*1.2, maxData*1.2]);
    set(handles.originalSigT, 'fontsize', 7);
    xlabel('Samples', 'parent', handles.originalSigT);
    ylabel('Amplitude', 'parent', handles.originalSigT);
    fredo=fftFrame(:,playPnt);
    bar(F, A*abs(fredo(1: length(F))), 'parent', handles.originalSigF);
    set(handles.originalSigF, 'xlim', [0, max(F)]);
    set(handles.originalSigF, 'ylim', [0, 2]);
    set(handles.originalSigF, 'fontsize', 7);
    xlabel('frequency/Hz', 'parent', handles.originalSigF);
    ylabel('Amplitude', 'parent', handles.originalSigF);
    playPnt=playPnt+1;
end

function AScaleSlider_Callback(hObject, eventdata, handles)
global A
A=get(handles.AScaleSlider,'value');

function AScaleSlider_CreateFcn(hObject, eventdata, handles)
if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor',[.9 .9 .9]);
end

function fileNameDisp_Callback(hObject, eventdata, handles)
 
function fileNameDisp_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

function originalSigT_CreateFcn(hObject, eventdata, handles)

function filterOrderText_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

%% 设置滤波器参数
function filterOrderText_Callback(hObject, eventdata, handles)
updateFilterButton(handles);
function P1Text_Callback(hObject, eventdata, handles)
updateFilterButton(handles);
function P2Text_Callback(hObject, eventdata, handles)
updateFilterButton(handles);
function S1Text_Callback(hObject, eventdata, handles)
updateFilterButton(handles);
function S2Text_Callback(hObject, eventdata, handles)
updateFilterButton(handles);
function RpText_Callback(hObject, eventdata, handles)
updateFilterButton(handles);
function RsText_Callback(hObject, eventdata, handles)
updateFilterButton(handles);
function updateFilterButton(handles)
global DATA playFState PAUSE STOP
if playFState == PAUSE | playFState == STOP

三、运行结果

【数字信号处理】基于matlab GUI数字调音台【含Matlab源码 881期】_归一化