为了正常的体验网站,请在浏览器设置里面开启Javascript功能!

音乐合成实验报告

2011-12-14 39页 pdf 1MB 172阅读

用户头像

is_207371

暂无简介

举报
音乐合成实验报告 实验一 音乐合成  背景知识: 乐音的基本特征可以用基波频率、谐波频率和包络波形三个方面来描述, 我们用大写英文字母 CDEFGAB 表示每个音的“音名”(或称为“音调”),当 指定某一音名时,它对应固定的基波信号频率。 图 1 表示钢琴的键盘结构,并注明了每个琴键对应的音名和基波频率值。 这些频率值是按“十二平均律”计算导出,下面解释计算规则: 图 1 钢琴键盘和相应频率 从图 1 可以看到,靠下边的 A 键称为小字组 A,它的频率值 fA0 = 220Hz, 而靠上面的另一个 A 键是小字一组 ...
音乐合成实验报告
实验一 音乐合成  背景知识: 乐音的基本特征可以用基波频率、谐波频率和包络波形三个方面来描述, 我们用大写英文字母 CDEFGAB 示每个音的“音名”(或称为“音调”),当 指定某一音名时,它对应固定的基波信号频率。 图 1 表示钢琴的键盘结构,并注明了每个琴键对应的音名和基波频率值。 这些频率值是按“十二平均律”计算导出,下面解释计算规则: 图 1 钢琴键盘和相应频率 从图 1 可以看到,靠下边的 A 键称为小字组 A,它的频率值 fA0 = 220Hz, 而靠上面的另一个 A 键是小字一组 A,它的频率值是 fA1 = 440Hz。两者为二 倍频率关系,即 fA1 相当于 fA0 的二次谐波。也称为 8 度音或倍频程 Octave(即 我们画频响特性图时所用的术语“倍频程”)。 从小字组 A 到小字一组 A 共有 12 个键,其中 7 个白色键,5 个黑色键, 其频率计算规律为相邻音倍乘系数 K = 21/12 = 1.05946309,由此可求出图中各 琴键对应之频率值。 从图 1 可以看出 7 个白建之间插入了 5 个黑键。在 EF 之间和 BC 之间没 有黑键,也即这两组相邻的白键之间的基波频率倍乘系数为 21/12,也称为相隔 半音,而在其他白键之间都有黑键相隔,因而他们的频率倍乘系数为 22/12, 也称为相隔全音(如 CD、DE、FG……)。若以白键英文字母为基准,则升高半 音以“#”符号表示,降低半音则以“b”符号表示。于是,可以依次写出 12 个音名从低到高的字母表示为: C,bD,D,bE,E,F,bG,G,bA,A,bB,B 当然,若改用“#”号表示黑键,则 bD 改为#C,bE 改为#D…… 下面给出“唱名”的概念。所谓唱名是指平时读乐谱唱出的 do,re,mi…… 每个唱名并未固定基波频率。当指定乐曲的音调时才能知道此时唱名对应的 音名,也即确定了对应的频率值。 下面给出一个乐曲的实例,练习写出每个唱名对应的基波频率值,如图 2,这是《东方红》的开头两句曲谱,用简谱写出。曲调定为 F,于是可查出 第一个音 5 对应 C,频率值为 523.25Hz,其他音之频率可依次写出。稍后, 我们将以此为基础进行音乐合成练习。  练习题: 1.2.1 简单的音乐合成: (1) 请根据《东方红》片段的简谱和“十二平均律”计算出该片段中各个乐音的 频率,在 MATLAB 中生成幅度为 1、抽样频率为 8kHz 的正弦信号表示这些乐 音。请用 sound 函数播放每个乐音,听一听音调是否正确,最后用这一系列 乐音信号拼出《东方红》片段,注意控制每个乐音持续的时间要符合节拍, 用 sound 函数播放你合成的乐音,听起来感觉如何? 答:根据 F 调与 C 调的基本规律,F 调的七个基本乐音唱名的对应基本频率列于 下表: 唱名 1 2 3 4 5 6 7 频率/Hz 349.23 392 440 466.16 523.25 587.33 659.25 音名 F G A bB C D E 表 1 F 调七个音名的基频 由此可得到《东方红》的各个唱名所对应的频率: 唱名 5 5 6 2 1 1 6 2 音名 C C D G F F D C 频率 /Hz 523.25 523.25 587.33 392 349.23 349.23 293.66 523.25 表 2 《东方红》乐谱信息 对于乐谱的信息,我进行了归类,主要分为基本音名,升降调信息,持续时间。 对于基本音名,按照乐谱中唱名所对应的数字表示,这样可以与表 1 中基本频率 相对应,如果该乐音比唱名表示的数字高一个八度(上方加点)则在升降调信息 中表示为 1,若低一个八度,则在升降调信息中表示为-1,若与实际相同,则表 示为 0,持续时间(节拍)信息即为对应的每个乐音的持续时间,将此三类信息 存入结构体中,即可得到完整的乐谱信息。 实现代码如下(由于注释复制粘贴显示为乱码,此处略去): clear all; close all; clc; score = struct('name',{5,5,6,2,1,1,6,2},'range',{0,0,0,0,0,0,-1,0},'time',{1, 0.5,0.5,2,1,0.5,0.5,2}); t = 0:1/8000:4; speed = 0.5; stop = 0.1*speed; standard = [349.23,392,440,466.16,523.25,587.33,659.25]; wave = zeros(1,length(t)); freq = 0.0; t_start = 0.0; t_end = 0.0; for k = 1:8 t_end = score(k).time.*speed + t_start; freq = standard(score(k).name).*2.^score(k).range; wave = sin(2*pi*freq*t).*(t>=t_start & t<= (t_end-stop)) + wave; t_start = t_end; end sound(wave,8000); 初步合成的音乐音调符合曲谱,能听出《东方红》的旋律。但是每个乐音之间有 “啪”的杂声,且无法分辨是何种乐器演奏。 2、你一定注意到(1)的乐曲中相邻乐音之间有“啪”的杂声,这是由于相位不连 续产生了高频分量。这种噪声严重影响合成音乐的质量,丧失真实感。为了消除 它,我们可以用图 3 所示包络修正每个乐音,以保证在乐音的邻接处信号幅度为 零。此外建议用指数衰减的包络来表示。 答:实现代码如下: clear all; close all; clc; score = struct('name',{5,5,6,2,1,1,6,2},'range',{0,0,0,0,0,0,-1,0},'time',{1, 0.5,0.5,2,1,0.5,0.5,2}); t = 0:1/8000:4; speed = 0.5; stop = 0.1*speed; standard = [349.23,392,440,466.16,523.25,587.33,659.25]; wave = zeros(1,length(t)); freq = 0.0; t_start = 0.0; t_end = 0.0; for k = 1:8 t_end = score(k).time.*speed + t_start; envelope = enve3(t_start,t_end,t); freq = standard(score(k).name).*2.^score(k).range; wave = envelope.*sin(2*pi*freq*t).*(t>=t_start & t<(t_end)) + wave; plot(t,wave); t_start = t_end; end title('wave of task2'); xlabel('time'); ylabel('amplititude'); sound(wave,8000); 此处我设计了六个波形包络函数,分别为enve1,enve2.enve3,enve4,enve5,enve6: enve1 即为上设计的包络波形: function envelope = enve1(t_start,t_end,time) envelope = zeros(1,length(time)); t_single = t_end - t_start; state1 = 0.05; state2 = -0.10; state4 = -0.45; t_end1 = (state1.*t_single)+t_start; t_end2 = (-state2.*t_single)+t_end1; t_end3 = t_end + state4.*t_single; t_end4 = t_end; envelope = envelope + 1./(t_end1 - t_start).*(time-t_start).*(time>=t_start & time< t_end1); envelope = envelope + ((-0.4)./(t_end2-t_end1).*(time-t_end1)+1.0).*(time>=t_end1 & time< t_end2); envelope = envelope + 0.6.*(time>=t_end2 & time< t_end3); envelope = envelope + ((-0.6)./(t_end4-t_end3).*(time-t_end3)+0.6).*(time>=t_end3 & time<= t_end4); end 波形如下图所示: 0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1 0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1 envelope1 time a m p lit it u d e enve2 为书上建议的指数衰减的包络波形,效果听起来像吉他等弹拨乐器发出的 声音: function envelope = enve2(t_start,t_end,time) envelope = zeros(1,length(time)); t_single = t_end - t_start; state1 = 0.01; t_end1 = t_start + state1 .* t_single; envelope = envelope + 1./(t_end1 - t_start).*(time - t_start).*(time >= t_start & time < t_end1); envelope = envelope + exp(-3.*(time - t_end1)./(t_end - t_end1)).*(time >= t_end1 & time <= t_end); end 波形如下图所示: enve3为参考书中钢琴包络波形自己模拟的包络函数,分为三段,第一段为直线, 第二段为抛物线,第三段为指数衰减,整体效果不错,与钢琴相似: function envelope = enve3(t_start,t_end,time) envelope = zeros(1,length(time)); t_single = t_end - t_start; state1 = 0.01; state2 = 0.3; t_end1 = t_start + state1.*t_single; t_end2 = t_end1 + state2.*t_single; envelope = envelope + 0.6./(t_end1 - t_start).*(time - t_start).*(time>=t_start & time=t_end1 & time=t_end1 & time=t_end1 & time=t_end2 & time<=t_end).*(0.8.*exp(-10.*(time - t_end2))); end 波形如下图所示: enve4 为仿真吉他的改进版,改用抛物线替代指数衰减阶段,此时弹拨声听起来 比较悠扬,停留时间较长: function envelope = enve4(t_start,t_end,time) envelope = zeros(1,length(time)); t_single = t_end - t_start; state1 = 0.05; t_end1 = t_start + state1 .* t_single; envelope = envelope + 1./(t_end1 - t_start).*(time - t_start).*(time >= t_start & time < t_end1); envelope = envelope + (time >= t_end1 & time <= 0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1 0 0.2 0.4 0.6 0.8 1 1.2 1.4 envelope3 time a m p lit it u d e t_end).*(0.01+(0.99./0.95./0.95)./t_single./t_single.*(time - t_end).*(time - t_end)); end 波形如下图所示: enve5 为仿真钢琴声的改进版,用 t*exp(t)替代分段函数,此时效果在较短音时 很像钢琴,但是在弹奏持续时间较长音时有些失真: function envelope = enve5(t_start,t_end,time) envelope = zeros(1,length(time)); envelope = envelope + (time-t_start)./(t_end - t_start).*exp(-5.*(time-t_start)./(t_end-t_start))./... (max((time-t_start)./(t_end - t_start).*exp(-5.*(time-t_start)./(t_end-t_start)))).*(time>=t_start & time<=t_end); end 波形如下图所示: 0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1 0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1 envelope4 time a m p lit it u d e enve6 是专门为后续仿真 fmt.wav 中的吉他声所设置的包络波形,将 enve2 中修 改了一些具体参数后实现: function envelope = enve6(t_start,t_end,time) envelope = zeros(1,length(time)); t_single = t_end - t_start; state1 = 0.02; t_end1 = t_start + state1 .* t_single; envelope = envelope + 1./(t_end1 - t_start).*(time - t_start).*(time >= t_start & time < t_end1); envelope = envelope + exp(-4.*(time - t_end1)./(t_end - t_end1)).*(time >= t_end1 & time <= t_end); end 波形实现如下图所示: 0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1 0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1 envelope5 time a m p lit it u d e 0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1 0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1 envelope6 time a m p lit it u d e 利用各种包络函数合成的音乐,以 0.5s 为一个节拍播放后,音符之间衔接感更 加自然,同时由于相位原因造成的“啪”的杂音以完全消除,使得《东方红》听 起来更加自然,但是仍然缺少一种“立体感”。 3、请用最简单的办法将(2)中的音乐分别升高和降低一个八度。(提示:音乐播放 的时间可以变化)再难一些,请用 resample 函数(也可以用 interp 和 decimate 函数) 将上述音乐升高半个音阶。(提示:视计算复杂度,不必特别精确) 答:由于将原来的波形进行快放可以达到使得原来波形中各频率成比例的升高, 慢放可以使得原来波形中各个频率成比例的降低,因此使用 resample 延长或缩 短播放时间的相应倍数即可达到升高或降低音乐的音调,此外,由于我在预处理 时已经将每个乐音的升降调信息存入 score.range 中,因此,只要修改 score.range 中的数据即可达到不进行加减速就能实现升降调的功能。 具体实现代码如下: clear all;close all;clc; %getwave()函数用来获取task2中的音乐的波形 wave = getwav(); %原波形的播放 sound(wave,8000); %低音1/2倍速 wave1 = resample(wave,2,1); sound(wave1,8000); %高音2倍速 wave2 = resample(wave,1,2); sound(wave2,8000); %高半音 wave4 = resample(wave,10000,round(10000.*2.^(1/12))); sound(wave4,8000); 4、 试着在(2)的音乐中增加一些谐波分量,听一听音乐是否更有“厚度”了? 注意谐波分量的能量要小,否则掩盖住基音反而听不清音调了。(如果选择基波 幅度为 1,二次谐波幅度为 0.2,三次谐波幅度为 0.3,听起来像不像风琴?) 答:此处我只是简单的在生成波形的代码中“手动”加入了谐波分量的代码,没 有使用矩阵数据,具体实现代码如下: clear all;close all;clc; score = struct('name',{5,5,6,2,1,1,6,2},'range',{0,0,0,0,0,0,-1,0},'time',{1, 0.5,0.5,2,1,0.5,0.5,2}); t = 0:1/8000:4; speed = 0.5; stop = 0.1*speed; standard = [349.23,392,440,466.16,523.25,587.33,659.25]; wave = zeros(1,length(t)); freq = 0.0; t_start = 0.0; t_end = 0.0; for k = 1:8 t_end = score(k).time.*speed + t_start; envelope = enve3(t_start,t_end,t); freq = standard(score(k).name).*2.^score(k).range; wave = envelope.*sin(2*pi*freq*t).*(t>=t_start & t<(t_end)) + wave; %添加谐波分量,乘以相应的幅度系数,使得音色更加逼真 wave = 0.2.*envelope.*sin(2.*2*pi*freq*t).*(t>=t_start & t<(t_end)) + wave; wave = 0.10.*envelope.*sin(3.*2*pi*freq*t).*(t>=t_start & t<(t_end)) + wave; wave = 0.10.*envelope.*sin(4.*2*pi*freq*t).*(t>=t_start & t<(t_end)) + wave; wave = 0.05.*envelope.*sin(5.*2*pi*freq*t).*(t>=t_start & t<(t_end)) + wave; t_start = t_end; end sound(wave,8000); 我加入的谐波分量为:2 次谐波系数 0.2,3 次谐波系数 0.1,4 次谐波系数 0.1, 5 次谐波系数 0.05,总体感觉更像钢琴发出的声音,更富有立体感。 (5)自选音乐的合成,例如贝多芬第五交响乐的开头两小节。 答:此处我合成的是钢琴曲《卡农》(cannon)的高潮部分的 8 小节,乐谱如下: 我采用 enve3 包络函数,同时使用 task4 中谐波分量的幅度系数,具体实现代码 如下: clear all;close all;clc; %乐曲《cannon》部分片段的乐谱信息 score = struct('name',{5,3,4,5,3,4,5,5,6,7,1,2,3,4,3,1,2,3,3,4,5,6,5,4,5,3,4, 5,... 4,6,5,4,3,2,3,2,1,2,3,4,5,6,4,6,5,6,7,1,5,6,7,1,2,3,4,5,3,1,2,3,2,1,2 ,7,1,2,3,2,1,7,... 1,6,7,1,1,2,3,4,3,2,3,1,7,1,6,1,7,6,5,4,5,4,3,4,5,6,7,1,6,1,7,1,7,6,7 ,1,2,1,7,1,6,7,1},... 'range',{1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,... 0,0,0,0,0,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,0,1,0,0,0 ,0,0,0,0,1,0,1,... 0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,1,0,0,0,1,1,1,0,1,0,0,1},'time',... {1,0.5,0.5,1,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,1,0.5,0.5,1,0.5, 0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,... 1,0.5,0.5,1,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,1,0.5,0.5,1,0.5,0 .5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,... 1,0.5,0.5,1,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,1,0.5,0.5,1,0.5,0 .5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,... 1,0.5,0.5,1,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,1,0.5,0.5,1,0.5,0 .5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,1}); t = 0:1/8000:33; speed = 0.5; stop = 0.1*speed; standard = [293.66,329.63,369.99,392,440,493.88,554.36]; wave = zeros(1,length(t)); freq = 0.0; t_start = 0.0; t_end = 0.0; for k = 1:113 t_end = score(k).time.*speed + t_start; envelope = enve3(t_start,t_end,t); freq = standard(score(k).name).*2.^score(k).range; wave = envelope.*sin(2*pi*freq*t).*(t>=t_start & t<(t_end)) + wave; wave = 0.15.*envelope.*sin(2.*2*pi*freq*t).*(t>=t_start & t<(t_end)) + wave; wave = 0.08.*envelope.*sin(3.*2*pi*freq*t).*(t>=t_start & t<(t_end)) + wave; wave = 0.05.*envelope.*sin(4.*2*pi*freq*t).*(t>=t_start & t<(t_end)) + wave; wave = 0.03.*envelope.*sin(5.*2*pi*freq*t).*(t>=t_start & t<(t_end)) + wave; t_start = t_end; %plot(t,wave); end sound(wave,8000); wavwrite(wave, 8000, 'cannon.wav'); 1.2.2 用傅里叶级数音乐: (6)先用 wavread 函数再如 fmt.wav 文件,播放出来听听效果如何?是否比刚才的 合成音乐真实多了? 答:载入 fmt.wav 文件并使用 sound 函数播放那个后发现该片段是吉他演奏的音 乐,十分逼真。 实现代码如下: clear all; close all;clc; wave = wavread('fmt.wav'); sound(wave,8000); load('Guitar.MAT'); len = length(realwave); t = 0:0.03/len:0.03-0.03/len; figure;plot(t,realwave,t,wave2proc); legend('realwave','wave2proc'); title('两种波形的对比'); xlabel('time'); ylabel('amplititude'); 同时,在代码中,我也绘出 realwave 和 wave2proc 两个数据的波形,如下图所示: 0 0.005 0.01 0.015 0.02 0.025 0.03 -0.2 -0.15 -0.1 -0.05 0 0.05 0.1 0.15 0.2 0.25 time a m p lit it u d e 两种波形的对比 realwave wave2proc 7、你知道待处理的 wave2proc 是如何从真实值 realwave 中得到的么?这个预处 理过程可以去除真实乐曲中的非线性谐波和噪声,对于正确分析音调是非常重要 的。提示:从时域做,可以继续使用 resample 函数。 答:观察波形可以发现,该波形课大致平均分为 10 个周期,我采用对于这 10 个周期求平均的方法得到预处理的结果,使用 resample 函数将采样率增大为原 先的 10 倍,再等分成 10 份,取平均后在使用 resample 函数将采样率缩小为处 理后的 1/10 即可获得预处理的结果: 实现代码如下: clear all;close all;clc; wave = wavread('fmt.wav'); load('Guitar.MAT'); len = length(realwave); t = 0:0.03/len:0.03-0.03/len; len0 = round(length(realwave)); %将realwave延迟10倍,取每一份周期 realwave1 = resample(realwave,10,1); len1 = length(realwave1); wave1 = zeros(len0,1); wave2 = zeros(len1,1); wave3 = zeros(len1,1); %将10份周期取平均得到一个周期的波形 for k=1:10 wave1 = realwave1((k-1).*len1/10+1:k.*len1/10) + wave1; end wave2 = wave1./10; %将处理好后的周期波形延拓到整个波形,得到处理好的波形,并将其与wave2proc对比 for k=1:10 wave3((k-1).*len1/10+1:k.*len1/10) = wave2; end wave4 = resample(wave3,1,10); %绘制两波形的对比函数 plot_task7(wave4,wave2proc); 得到的波形对比如下图所示: 从下图可以看出,绿色的 wave2proc 完全覆盖了蓝色的 mywave,说明两者重合 度相当的好。 8、这段音乐的基频时多少?时那个音调?请用傅里叶级数或者变换的方法分析 它的谐波分量分别是什么。提示:简单的方法是近似的取出一个周期求傅里叶级 数,但这样明显不准确,因为你应该已经发现基音周期不是整数(这里不允许使 用 resample 函数)。复杂些的办法是对整个信号求傅里叶变换回忆周期性信号的 傅里叶变换),但你可能发现无论你如何提高频率的分辨率,也得不到精确的包 络(应该近似于冲激函数而不是 sinc 函数),可选的方法是增加时域的数据量,即 再把时域信号重复若干次,看看这样是否效果好多了?请解释之。 答:观察 realwave 波形可知,波形共 243 个采样点,约为 10 个周期的重复,采 样频率为 8000Hz,因此粗略的估计该波形的基因频率约为 8000/(243/10) = 329.22Hz,分析频域特性时,我首先直接对整个信号进行快速傅里叶变换(fft 函 数),发现果然得不到精确的包络(有很多的毛刺),之后我按照提示所述,使用 repmat 函数,将信号在时域上重复了 100 遍后再进行分析,发现此时基频以及 谐波的信号相当明显,呈现出十分的冲激函数的图像,具体实现代码如下: clear all;close all; clc; wave = wavread('fmt.wav'); load('Guitar.MAT'); wave1 = repmat(wave2proc,100,1); %将原波形在时域上重复100次 F_s = 8000; %抽样频率为8000Hz %对wave1进行快速傅里叶变换 0 0.005 0.01 0.015 0.02 0.025 0.03 -0.2 0 0.2 0.4 0.6 mywave 0 0.005 0.01 0.015 0.02 0.025 0.03 -0.2 0 0.2 0.4 0.6 comparison mywave wave2proc result4 = fft(wave1); len = length(result4); %由于变换后的结果关于频域对称(离散傅里叶变换的特性),将对称的两部分叠加后去一半的 频域作为结果 result = result4./len; result1 = zeros(1,len/2); omg = F_s/len:F_s/len:F_s/2; result1 = 2.*result(1:len/2); %对原波形进行快速傅里叶变换,并进行相同的操作 r2 = fft(wave); len1 = length(r2); r = r2./len1; r1 = zeros(1,len1/2); r1 = r(1:len1/2).*2; omg1 = F_s/len1:F_s/len1:F_s/2; figure; subplot(2,1,1); plot(omg,abs(result1)); title('时域重复100次的频域分布'); xlabel('frequency'); ylabel('amplititude'); subplot(2,1,2); plot(omg1,abs(r1)); title('时域未进行操作的频域分布'); xlabel('frequency'); ylabel('amplititude'); 得到的分析结果如下图所示: 0 500 1000 1500 2000 2500 3000 3500 4000 0 0.01 0.02 0.03 0.04 0.05 0.06 0.07 0.08 时域重复 100次的频域分布 frequency a m p lit it u d e 测量结果发现,基频(频率最低的冲激函数)为 329.5Hz,查表可知该基频为 C 大 调中的 E(329.63Hz),之所以在时域上重复波形得到的频谱分析结果更加接近冲 激函数而不是 sinc 函数,是因为傅里叶变换的特性,时域上重复的越多,频域上 离散程度越高,具体来说,时域重复相当于对一个原本是周期信号的波形进行窗 函数截取的长度越多,使得本例中的窗函数的傅里叶变换得到的 sinc 函数与一系 列的冲激函数卷积,窗开的越大,卷积的数目越多,使得 sinc 函数的叠加度越高, 而: lim k→∞ k π sinc kt = δ(t) 所以分析的结果更加接近冲激函数。 9、再次载入 fmt.wav,现在要求你写一段程序,自动分析出这段乐曲的音调和节 拍,如果你觉得太难就允许手工标定出每个音调的起止时间,再不行你就把每个 音调的数据都单独保存成一个文件,然后让 MATLAB 对这些文件进行处理。注意: 不允许逐一地手工分析音调。编辑音乐文件,推荐使用”CoolEdit”软件。 答:对于此问题,我分为三个部分进行处理:(1)节拍的划分(2)数据的分析(3)结 果的验证,下面将详细阐述三个部分: (1) 节拍的划分: 首先观察时域波形,我发现基本每个节拍都是以一个陡然增高的峰值开始,之后 以近似的指数衰减的波形结束,这是吉他乐器波形包络决定的。根据此特性,我 采用的方法是:首先进行”粗扫描”,即以 0-100 个单位为一个时段(此处波形的长 0 500 1000 1500 2000 2500 3000 3500 4000 0 0.002 0.004 0.006 0.008 0.01 0.012 0.014 0.016 0.018 时域未进行操作的频域分布 frequency a m p lit it u d e 度为 24300,一个单位的长度为 24300/8000),在每个时段中利用 max 函数寻找 到该段中最大峰的峰值和位置,找到该峰后,对其进行判定:利用“回扫”,即 设定一个回扫范围(我设定的是此峰之前的 60 到 55 个单位),在此范围内利用 max 函数找到第二个峰,如果最大峰大于此峰的 1.5 倍,且此最大峰又大于整个 波形的平均峰值,则判定此峰为节拍的截止点。这样做的目的是因为最大峰之前 可能存在一些高度接近的峰,并且也存在峰值十分小的点,若直接对其前面的点 进行比较大小的判断往往不能得到有效信息,因此根据波形特征,我回扫到前面 找到比较整齐的用于比较的最大峰进行判定,判定为截止点后,由于该点后可能 波形也不太整齐,故我设置了“忽略范围”(50 个单位),在此范围后的第一个点 作为下一次“粗扫描”的起点,若不是截止点,则将此点下一个点作为下一次的 “粗扫描”的起点。实验的结果证明此种方法划分的时段正确率非常高,仅出现 了一次疏漏,一次多余的划分,划分波形如下: 从上图可以发现第 19 个节拍截止点没有划分出来(信号十分微弱所致),而第 25 个划分处为多余,为了后续分析结果的准确性,我设置的 revise 函数对划分进行 的稍微的修正,修正结果如下图所示: 0 1000 2000 3000 4000 5000 6000 7000 8000 -0.6 -0.4 -0.2 0 0.2 0.4 0.6 0.8 1 处理结果 (红色线为划分时段处 ) time a m p lit it u d e (2) 数据的处理: 得到了时域划分的结果,之后便可以按照第 8 问的分析方法对每个时段中的波形 进行逐一分析,此处我没有优化代码,直接采用了 for 循环的方式,导致程序运行 有些缓慢,此处有提高的空间。分析完毕后,需要进行数据提取的工作,此时我 发现即使采用第 8 问的处理方法,输出的结果仍然存在大量的毛刺,因此我设置 了一个对结果进行“精加工”的函数 deal_wave,加工的方式为:首先滤除小于 最大峰值 1/3 的频率,之后按照从 a 到 b2 每半音一个频率共 36 个频率逐一扫描 分析,我设置了一个分析范围,即每个标准频率处正负 0.5 度,例如分析 c1(f0 =261.63Hz)我设置的范围便是[f0-0.5*f0*2^(1/12), f0+0.5*f0*2^(1/12)],,然后分析此 范围内的最大峰值,分析完此基频后,立即将此范围内的频率幅度全部设置为 0, 防止后续操作进行重复提取,此外,我也分析了 36 个基频的 2~7 次谐波分量的 频率幅度,最终将数据结果存入的一个 36*7 的矩阵 ratio.mat 中,为了后续任务 的需要,我将此矩阵保存了下来。分析完 36 个基频及其谐波分量的幅度信息, 配以合适的包络,等于获得了该吉他的各个音的”演奏方式”。此后,我进行了各 个时段的音调分析,有了之前的分析结果及处理函数,此时工作变得简单许多, 由于得到了 30 个截止点,我设置 30*36 的矩阵 score.mat 用于获取各个时段 36 个音名是否存在的信息矩阵,即在之前逐段提取基频幅度时,如该段的峰值幅度 不为 0,则对应于 score 矩阵位置中填入 1,否则填入 0,如此便获得了每个时段 的音调信息,将结果保存,同时我设置了 data.mat 的结构体用于记录乐谱信息, data 的结构分工与第一问相同。具体的结果列表如下: 0 1000 2000 3000 4000 5000 6000 7000 8000 -0.6 -0.4 -0.2 0 0.2 0.4 0.6 0.8 1 处理结果 (红色线为划分时段处 ) time a m p lit it u d e 时段 1 2 3 4 5 6 音调 a a c1 e1 b a b a b d1 a e1 时段 7 8 9 10 11 12 音调 g f a f d1 f d1 e g ba b e ba b 时段 13 14 15 16 17 18 音调 e a c1 e1 a e1 a a c1 g1 时段 19 20 21 22 23 24 音调 c1 g1 f1 e1 d1 c1 e1 b b d1 a1 时段 25 26 27 28 29 30 音调 d f b c1a1 f b a e a b a e a ba (3) 结果的验证: 将之前保存的 score.mat、data.mat 和 ratio.mat 重新载入,然后按照每个时段将 各个乐音的幅度信息、包络信息、谐波信息依次叠加,最后用 sound 函数重新播 放,发现结果虽不如真实的 fmt.wav 那样逼真,但保留了绝大部分的信息,说明 此方法得到的信息是可靠的。 具体实现代码如下: %task9.m 用于完成前两个任务 clear all;close all;clc; load('Guitar.MAT'); wave1 = wavread('fmt.wav'); %获取划分时段的数据部分 %由于比较幅度,此处只关注波形的绝对值 wave = abs(wave1); len = length(wave); F_sample = 8000; %记录波形的平均幅度,作为判定是否为音符的截止点的依据之一 av_energy = norm(wave)./sqrt(len); data = struct('start',[],'max_amp',[]); %data 结构体用于存储每个时段的开始 位置和本时段的最大峰的幅度 num = 1; %num 用于记录划分的时段的 个数 t = 0:F_sample/len:F_sample - F_sample/len; %step 用于每次判断后记录下一次判断点到当前点的距离 step = 1; %rate 用于记录最大峰是否大于之前峰的倍数,是判断是否为音符截止点的依据 之一 rate = 1.5; %每次以 100*len/F_sample(s)的范围扫描该范围内的最大峰(粗扫描) max_range = round(100/F_sample*len); %找到最大峰后,找到其之前 60*len/F_sample(s)到 65*len/F_sample(s)之间的最 大峰 ign_range1 = round(60/F_sample*len); %ign_range1 为回扫距离 ign_range2 = round(50/F_sample*len); %ign_range2 为确认该点是音符的截止 点后,向后忽略的距离 ign_range3 = round(5/F_sample*len); %ign_range3 为回扫范围 t_start = 1; %寻找最大峰的起止时间 t_end = t_start + max_range; t_max1 = 0; %记录最大峰的位置 t_max2 = 0; %记录回扫范围内的最大峰的位置 max_amp1 = 0; %待判定的最大峰 max_amp2 = av_energy; %最大峰之前的回扫范围内的最大 峰 t_start2 = 0; %回扫范围的起止时间 t_end2 = 0; while t_start <= len t_end = t_start + max_range - 1; %对于范围溢出的处理 if(t_end > len) t_end = len; end [max_amp1,t_max1] = max(wave(t_start:t_end)); %记录下最大峰的位置和 幅度 if(t_max1 <= ign_range1) t_start2 = t_start; else t_start2 = t_start+t_max1-ign_range1; end t_end2 = t_start2 + ign_range3; if(t_end2 > len) t_end2 = len; end [max_amp2,t_max2] = max(wave(t_start2:t_end2)); %记录下回扫范围内 的最大峰的位置和幅度 %如果该最大峰超过回扫范围内的最大峰的 1.5 倍且大于平均幅度,则 判定此处为音符的截止点 if(max_amp1 >= rate*max_amp2 && max_amp1 >= av_energy ) data(num).start = (t_max1 + t_start -1); data(num).max_amp
/
本文档为【音乐合成实验报告】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。 本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。 网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。

历史搜索

    清空历史搜索