📄 psola.m
字号:
%=================================================================
% PSOLA算法实现程序
%=================================================================
%
%具体的算法在毕业论文《基于PSOLA的汉语文语转换技术研究》P44中有详细的介绍。
%
%需要注意的问题:目前对声母(清音)还没有修改,只是修改韵母(浊音),在必要的时候可以适当加以处理声母。
%对声母的处理只能调整音长和音强,也就是复制一段接在声母后面以增长音长,乘以某个系数以提高音强。
%=================================================================
%2006年7月 郭锋 版本 1.0
%=================================================================
clear all
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%产生4个声调所对应的声调函数,数据通过对声调曲线的图像来采样获得
%实验证实:11个采样点已经可以得到实验要求。
t=0:0.1:1.0;
f1=[0.453 0.470 0.472 0.469 0.466 0.465 0.468 0.473 0.476 0.470 0.447];
f2=[0.011 0.081 0.10 0.128 0.163 0.236 0.305 0.384 0.460 0.520 0.544];
f3=[-0.185 -0.106 -0.151 -0.205 -0.2309 -0.20 -0.105 0.001 0.124 0.220 0.324];
f4=[0.463 0.531 0.504 0.438 0.363 0.267 0.184 0.120 0.046 0.001 -0.064];
f1=f1+0.6; f2=f2+0.6; f3=f3+0.6; f4=f4+0.6; %往上提升0.6,把数据全部化为正数
kp1=polyfit(t,f1,4); %拟和得到四个声调的声调函数,它的图像形式就是调型曲线,分别用kp1~kp4来标示。
kp2=polyfit(t,f2,4);
kp3=polyfit(t,f3,4);
kp4=polyfit(t,f4,4);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%产生基音变化序列
[pitch,Fs]=wavread('a.wav'); %读入一个基音周期,例如a的一个基音。
OriginPitchLength=length(pitch);
PithNum=40; %基音数目从此输入,对应着音长。
z=linspace(0,1,PithNum);
Fn=polyval(kp4,z); %通过调型函数来获得需要的声调序列。
Fn=1./Fn; %计算基频数列
KK=log(10+10.*Fn);
scale=OriginPitchLength/KK(PithNum/2); %scale表示单位数据代表的基音点数
ResultPitchLength=round(scale.*KK); %获得该声调对应的基音周期序列,准备用PSOLA调整基音周期。
%以下四行注释为早期的一些实验结果,这里不用理会。
%chazhiSerial=round(PitchSerial-PitchSerial(15)) %获得插值点数序列
%测试二声的序列 最大基音和最小基音相差50个 169.6154--122.5000
%三声 最大基音和最小基音相差40个 183.7500--147
%四声 最大基音和最小基音相差60个 122.5000--183.7500
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%开始基音同步叠加,改变声调
a=pitch';
resultpitch=a; %开始调整基音resultpitch。
reginpitch=a; %reginpitch留作最后来和调整过的resultpitch进行比较。
K=zeros(1,PithNum);
for ii=1:PithNum
if (OriginPitchLength==ResultPitchLength(ii)) %如果需要的基音数值NeedNum与原始基音数值OriginNum相等
c=a; %那么就不作修改,直接把原始基音a给存储变量c。
else %否则的话,就对原始基音进行先插值再抽取。
K(ii)=lcm(OriginPitchLength,ResultPitchLength(ii)); %K为原始基音数值,例如5,和需要的基音数值,例如7,的最小公倍数。
D1(ii)=K(ii)/OriginPitchLength;
D2(ii)=K(ii)/ResultPitchLength(ii);
b=zeros(1,K(ii));
num=1;
%%%%%%%%%%%%%%%%%%%%%%%%%%% 插值
for i=1:K(ii)
if(rem(i,D1(ii))==1) %D1(ii)整倍,就直接赋值,不作修改
b(i)=a(num);
num=num+1;
elseif (num<=OriginPitchLength) %否则,进行线性插值,扩充点数。
b(i)=b(i-1)+(a(num)-a(num-1))/(D1(ii)-1);
elseif (num>OriginPitchLength)
b(i)=a(num-1); %如果是最后一个数据点,就直接幅值,不需要再插值。
end
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 按照需要的间隔进行数据抽取
count=1;
c=zeros(1,ResultPitchLength(ii));
for j=1:K(ii)
if (rem(j,D2(ii))==0)
c(count)=b(j);
count=count+1;
end
end
end
reginpitch=[reginpitch a];
resultpitch=[resultpitch c];
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%比较结果
[am,an]=size(reginpitch);
[cm,cn]=size(resultpitch);
%reginpitch=[a zeros(1,abs(cn-an))];
[qingyin,Fs]=wavread('t.wav'); %读入清音音节
%%%%%%%%%%%%%%%%%%%%%%%%%用曲线进行衰减语音波形,使得整个浊音语音波形中间音强高,首尾低,方才符合人类自然语音。
tjidian=1/100;
xjidian=0.8/65;
t0=[3 12 21 30 56 76 85 93 97];
x0=[23 32 43 41 37 33 28 22 19];
T0=tjidian.*t0;
X0=xjidian.*x0+0.2;
s1=polyfit(T0,X0,5); %采用的衰减曲线s1为开口向下的抛物线。
%z=linspace(0,1,100);
%Fn=polyval(p1,z);
%plot(z,Fn) %可以观测衰减曲线
k1=linspace(0,1,an);
k2=linspace(0,1,cn);
sjn1=polyval(s1,k1); %获得原始基音对应的衰减序列。
sjn2=polyval(s1,k2); %获得合成基音对应的衰减序列。
sum1=reginpitch.*sjn1; %衰减原始基音序列
sum2=resultpitch.*sjn2; %衰减合成基音序列
outSpeechResult=[qingyin' sum2];
outSpeechOrign=[qingyin' sum1]; %给outSpeechOrign乘以系数K就可以修改音强。
wavplay(outSpeechResult,Fs) %读出合成语音
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%把结果以图形的形式显示出来。
subplot(2,1,1)
plot(outSpeechOrign)
grid on
title('汉语“他(ta1)”的时域波形图')
wavplay(outSpeechOrign,Fs)
subplot(2,1,2)
plot(outSpeechResult)
title('TD-PSOLA处理后变为“踏(ta4)”的时域波形图')
grid on
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -