本次项目为数字信号处理课程项目,项目全部采用matlab GUI编程,已上传至github/git_dsp
项目要求如下:
- 至少实现 1 种音效测试分析功能(频率响应,瀑布频谱图,相位响应曲线,抗阻曲线,谐波失真曲线,互调失真曲线,音压曲线等);
- 至少实现 1 种语音处理功能(声纹识别,关键词识别,语音去噪,声源分离等),并分别对其进行实际应用;
为了方便修改,本次项目使用了git,并托管到github上。
matlab对Git有很好的支持,文件栏处右击源代码管理,可以直接对文件进行添加、提交、推送到github。
名称 | 格式 | 项目内用法 |
---|---|---|
abs | abs(fft_sample) | 求fft后样本模值 |
angel | angle(ph_y0) | 求fft后样本相角 |
audioplayer | h.player=audioplayer(h.CSample,h.Fs) | 创建音频播放器 |
audioread | [h.Sample,h.Fs]=audioread(h.Filepath) | 读取音频文件 |
audiowrite | audiowrite() | 输出音频 |
colorbar | colorbar(ax) | 创建颜色栏 |
colormap | colormap(hot) | 改变语谱图颜色 |
delete | delete(gcf) | 关闭窗口 |
fft | fft(sample,nfft) | 对样本进行快速傅里叶变换 |
fftshift | fftshift(y) | 以中心为零,频域循环移位 |
gcf | set(gcf, | 当前窗口句柄 |
guidata | guidata(hObject, handles) | 更新句柄 |
isempty | isempty(handles.Sample) | 判断样本是否为空 |
isstrprop | str(isstrprop(str,’digit’)) | 求字符串内数字 |
length | length(sample) | 求样本长度 |
mean | mean(handles.Sample) | 求样本均值 |
movegui | movegui(gcf,’center’) | 窗口居中 |
nargin | nargin | GUI输入参数个数 |
nargout | varargout{1:nargout} | GUI输出参数个数 |
pow2 | pow2(nextpow2(sample_length)) | 求2的次方 |
nextpow2 | nextpow2(sample_length) | 求最小nfft使,2^nfft≥len |
round | round(get(hObject,’Value’),2) | 求指定有效数位约值 |
size | size(handles.Sample) | 求音频通道数 |
spectrogram | spectrogram(sample,1024,512,nfft,fs) | 绘制语谱图 |
std | std(handles.CSample) | 求样本标准差 |
struct | struct(‘gui_Name’,mfilename, … | 构造GUI参数、回调函数 |
varargin | varargin{:} | GUI可变长度输入,GUI之间参数传递 |
varargout | varargput{:} | GUI可变长度输出,GUI之间参数传递 |
uigetdir | handles.foldername=uigetdir() | 获取文件夹位置 |
uigetfile | [filename,pathname]=uigetfile({‘*.wav; | 获取路径和文件名 |
uiwait | OpeningFcn | 等待响应 |
uiresume | 响应,继续运行 | |
unwrap | unwrap(angle(ph_y0)) | 矫正相角跳变由2pi为pi |
GUI函数中使用了大量的句柄,句柄类似指针,可以自己创建指向某一函数,GUI中使用句柄指向图形对象。
GUi的建立基本上有两种方式,一种是使用figure
创建新窗口,但这种需要对GUI部件与属性十分了解,并且不利于复杂部件的排版;第二种是使用guide可视化界面,这种方式容易上手,利于排版,部件的属性可以直观地修改,但是由于在创建时会自动生成默认参数,不利于在生成后对于一些默认属性如文件名地修改。
强烈建议在命名时一定不要随意,我在创建主窗口时比较随意地使用了
dsp
作为文件名,然而在之后了解到matlab本身有一个dsp类的工具箱,虽然在使用上没有什么问题,但是感觉还是不好,在改名后会出现很多问题,比如回调函数、属性等都没有改变,很难受。
在创建好一个GUI后,会自动生成两个同名文件,即.fig
和.m
,.fig
应该是保存了设计界面时的默认属性,右击可以对界面进行修改,.m
文件会自动生成窗口的参数和函数以及部件的回调函数等,下面对其中生成的代码介绍一下我浅薄的认识,这里输出音频窗口为例:
- 主程序
function varargout = putfile(varargin)
% --- 输出音频
gui_Singleton = 1;
gui_State = struct('gui_Name', mfilename, ...
'gui_Singleton', gui_Singleton, ...
'gui_OpeningFcn', @putfile_OpeningFcn, ...
'gui_OutputFcn', @putfile_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
varargout
与varargin
可以用于多个GUI直接进行参数传递,并且输入与输出的个数是可变的,具体在之后的GUI参数传递中介绍。gui_Singleton
表示单一运行实例,可以在GUI选择项中设置,为1时,如果之前窗口未关闭,下次运行时仍会使用该窗口;为0时,下次运行会新打开一个窗口。struct
创建结构数组,定义了gui_state
GUI状态,即名字、运行实例、打开函数、输出函数、布局以及回调函数str2func(varargin{1})
取得创建figure和部件的CreateFcn回调函数。最后是执行gui_mainfcn
函数,输入参数为gui_State
和回调函数句柄
,生成整个界面,判断条件为输出参数个数,如果有输出就将gui_mainfcn
结果分别输出。
function putfile_OpeningFcn(hObject, eventdata, handles, varargin)
movegui(gcf,'center');
handles.putSample=varargin{1}; %保存输入
% 初始化采样率为输入音频采样率
handles.putFs=varargin{2};
str=get(handles.Fs_popupmenu,'String');
for val=1:5
if str2double(str{val})==handles.putFs
break
end
end
set(handles.Fs_popupmenu,'Value',val);
% Update handles structure
guidata(hObject, handles);
% UIWAIT makes putfile wait for user response (see UIRESUME)
% uiwait(handles.figure1); % 等待输出响应
hObject
为该部件的句柄(这里指该figure),eventdata
存储事件信息(面向对象),handles
是一个句柄类,包含了所有部件,可以用于回调函数之间的参数传递 。guidata(hObject, handles)
用于函数之间参数传递,如果有handles类的参数生成或改变,必须添加。
function varargout = putfile_OutputFcn(hObject, eventdata, handles)
varargout{1} = 0; % 不需要输出
该函数传递窗口输出,默认为varargout{1}=handles.output
,指向的是原OpeningFcn
中的handles.output = hObject
(我删掉了),由于我的这个窗口不需要输出,设置为0。
详细参考Matlab的GUI参数传递方式总结
global
定义全局变量,需要在每次使用前定义一次。handles
类,我基本上全部使用了handles,这样方便、清晰,但是一定要在后面加上guidata(hObject, handles)
,不然参数传递不出去。这里以主figuredsp
和输出音频putfile
为例,需要对dsp中的样本handles.CSample
输出,在dsp中
putfile(handles.CSample,handles.Fs); % 传递样本与采样率
这时putfile接收到了这两个数据,需要在OpeningFcn(如上面的程序)添加
handles.putSample=varargin{1};
handles.putFs=varargin{2};
这样就可以在putfile调用这两个数据。
由于我这里putfile没有输出,就将handles.out
去掉,varargout{1} = 0
,保证关闭窗口时不会报错,如果需要输出的话,需要在putfile的OpeningFcn
中添加
uiwait(handles.figure1); % handles.figure1为putfile窗口句柄,该语句被默认注释掉
这样可以保证再打开窗口后不会直接运行OutputFcn
,在后面需要输出参数的地方加上uiresume(handles.figure1)
。
function varargout = putfile_OutputFcn(hObject, eventdata, handles)
varargout{1} = handles.out1;
function out(hObject, eventdata, handles))
handles.out1=out1;
uiresume(handles.figure1);
这样在输出参数设置好后,接着会运行OutputFcn
进行输出。
注意如果使用了uiwait,在输出参数没有设定时(即没有运行uiresume),关闭窗口会报错。
参考关于GUI中uiwait关闭出错的问题
如果GUI不需要输出,OutputFcn中可以写varargout{1} = 0;
如果GUI需要有输出,OutputFcn中可以做一个判断isempty(handles)
,如果返回值是1,说明是点击右上角的关闭按钮退出的,如果返回值是0,说明是使用uiresume退出的。这两种情况返回的varargout根据需要分别设定。
GUI中部件属性使用类来创建的,涉及到了matlab opp编程,这里先不细研究。
在编写程序时使用面向过程有些方法比较繁琐,很多函数需要重复调用,考虑之后探究一下matlab opp来优化这些问题,比如使用监听器之类。