annotate aim-mat/tools/@signal/gen_vowel.m @ 4:537f939baef0 tip

various bug fixes and changed copyright message
author Stefan Bleeck <bleeck@gmail.com>
date Tue, 16 Aug 2011 14:37:17 +0100
parents 20ada0af3d7d
children
rev   line source
bleeck@3 1 % This external file is included as part of the 'aim-mat' distribution package
bleeck@3 2 % (c) 2011, University of Southampton
bleeck@3 3 % Maintained by Stefan Bleeck (bleeck@gmail.com)
bleeck@3 4 % download of current version is on the soundsoftware site:
bleeck@3 5 % http://code.soundsoftware.ac.uk/projects/aimmat
bleeck@3 6 % documentation and everything is on http://www.acousticscale.org
bleeck@3 7 % function to generate an artificial vowel
bleeck@3 8 function sig=gen_vowel(sig,options)
bleeck@3 9 % vowel,pitch,scale,halflifeconstant,onsettimeconstant,pitch_jitter,formant_jitter,formant_jitter_bw);%
bleeck@3 10
bleeck@3 11 if nargin < 2
bleeck@3 12 options=[];
bleeck@3 13 end
bleeck@3 14
bleeck@3 15
bleeck@3 16 % jitterwidth of jitter in formant frequencies
bleeck@3 17 %formant_jitter_bw=1; %bandwidth specified in octaves over which the formants are jittered
bleeck@3 18 if isfield(options,'formant_jitter_bw')
bleeck@3 19 formant_jitter_bw=options.formant_jitter_bw;
bleeck@3 20 else
bleeck@3 21 formant_jitter_bw=1; % in octaves
bleeck@3 22 end
bleeck@3 23 % jitter in percent of bandwidth
bleeck@3 24 %formant_jitter=0.0; %------- range is 0 to 1, representing 0% to 100%
bleeck@3 25 if isfield(options,'formant_jitter')
bleeck@3 26 formant_jitter=options.formant_jitter;
bleeck@3 27 else
bleeck@3 28 formant_jitter=0.03; %
bleeck@3 29 end
bleeck@3 30 % jitter in pitch
bleeck@3 31 %pitch_jitter=0.0; %------- range is 0 to 1, representing 0% to 100%
bleeck@3 32 if isfield(options,'pitch_jitter')
bleeck@3 33 pitch_jitter=options.pitch_jitter;
bleeck@3 34 else
bleeck@3 35 pitch_jitter=0.01;
bleeck@3 36 end
bleeck@3 37 % the rise of a damped sinusoid is not instantaneous, but like a gamma
bleeck@3 38 % functin with the following power of t:
bleeck@3 39 if isfield(options,'onsettimeconstant')
bleeck@3 40 onsettimeconstant=options.onsettimeconstant;
bleeck@3 41 else
bleeck@3 42 onsettimeconstant=0.5;
bleeck@3 43 end% halflife of each formant is calculated by dividing this number by the
bleeck@3 44 % formant frequency:
bleeck@3 45 % if halflifeconstant<=0, then fixed to 4 ms
bleeck@3 46 if isfield(options,'halflifeconstant')
bleeck@3 47 halflifeconstant=options.halflifeconstant;
bleeck@3 48 else
bleeck@3 49 halflifeconstant=3;
bleeck@3 50 end
bleeck@3 51 % scaling = 1: normal speaker
bleeck@3 52 if isfield(options,'scale')
bleeck@3 53 scale=options.scale;
bleeck@3 54 else
bleeck@3 55 scale=1;
bleeck@3 56 end
bleeck@3 57 % f0
bleeck@3 58 if isfield(options,'pitch')
bleeck@3 59 pitch=options.pitch;
bleeck@3 60 else
bleeck@3 61 pitch=100;
bleeck@3 62 end
bleeck@3 63 % how long the decay should continue longer then the reprate
bleeck@3 64 if isfield(options,'carry_decay')
bleeck@3 65 carry_decay_parameter=options.carry_decay;
bleeck@3 66 else
bleeck@3 67 carry_decay_parameter=1;
bleeck@3 68 end
bleeck@3 69 % normal or is it the octave? In this case, we jump over each second
bleeck@3 70 if isfield(options,'do_octave')
bleeck@3 71 do_octave=options.do_octave;
bleeck@3 72 else
bleeck@3 73 do_octave=0;
bleeck@3 74 end
bleeck@3 75 % type of vowel: 'a','e','i','o','u'
bleeck@3 76 if isfield(options,'vowel')
bleeck@3 77 vowel=options.vowel;
bleeck@3 78 else
bleeck@3 79 vowel='a';
bleeck@3 80 end
bleeck@3 81
bleeck@3 82 %
bleeck@3 83 % which formants
bleeck@3 84 if isfield(options,'use_formants')
bleeck@3 85 use_formants=options.use_formants;
bleeck@3 86 else
bleeck@3 87 use_formants=[1 2 3 4];
bleeck@3 88 end
bleeck@3 89
bleeck@3 90 if nargin < 1
bleeck@3 91 sig=signal(0.5,16000);
bleeck@3 92 end
bleeck@3 93 sr=getsr(sig);
bleeck@3 94 dur_time=getlength(sig);
bleeck@3 95
bleeck@3 96
bleeck@3 97 nr_formants=length(use_formants);
bleeck@3 98
bleeck@3 99
bleeck@3 100 switch vowel
bleeck@3 101 case 'a';
bleeck@3 102 formfre_org(1)=730; formfre_org(2)=1090; formfre_org(3)=2440; formfre_org(4)=3300;% standard formant freqs
bleeck@3 103 case 'e';
bleeck@3 104 formfre_org(1)=400; formfre_org(2)=1990; formfre_org(3)=2550; formfre_org(4)=3700; % standard formant freqs
bleeck@3 105 case 'i';
bleeck@3 106 formfre_org(1)=270; formfre_org(2)=2260; formfre_org(3)=3000; formfre_org(4)=3700;% standard formant freqs
bleeck@3 107 case 'o';
bleeck@3 108 formfre_org(1)=570; formfre_org(2)=850; formfre_org(3)=2430; formfre_org(4)=3300;% standard formant freqs
bleeck@3 109 case 'u';
bleeck@3 110 formfre_org(1)=300; formfre_org(2)=850; formfre_org(3)=2260; formfre_org(4)=3700;% standard formant freqs
bleeck@3 111 otherwise
bleeck@3 112 error('signal::gen_vowel: dont know vowel');
bleeck@3 113 end
bleeck@3 114
bleeck@3 115 for i=1:length(use_formants)
bleeck@3 116 formfre(i)=formfre_org(use_formants(i));
bleeck@3 117 end
bleeck@3 118
bleeck@3 119
bleeck@3 120 % fix all formants to the nearest harmonic of the fundamental
bleeck@3 121 if isfield(options,'adjust_to_nearest_harmonic')
bleeck@3 122 if options.adjust_to_nearest_harmonic==1
bleeck@3 123 f0=options.pitch;
bleeck@3 124 for formant=1:nr_formants
bleeck@3 125 fre=formfre(formant);
bleeck@3 126 newfre=round(fre/f0)*f0;
bleeck@3 127 formfre(formant)=newfre;
bleeck@3 128 end
bleeck@3 129 end
bleeck@3 130 end
bleeck@3 131
bleeck@3 132
bleeck@3 133 formfre=formfre.*scale;
bleeck@3 134 dur_samp=dur_time.*sr; %length of vowel defined in sample points
bleeck@3 135 sgpp=1/pitch; %standard glottal pulse period.
bleeck@3 136 pitch_jitter=max(0,min(1,pitch_jitter));
bleeck@3 137 t=[0:1/sr:(dur_time-(1/sr))]; %our time sequence
bleeck@3 138 dur_samp=dur_time.*sr; %length of vowel defined in sample points
bleeck@3 139
bleeck@3 140
bleeck@3 141 %we now generate a sequence, specified in sample points, which
bleeck@3 142 %define the spacing of glottal pulses. With zero pitch_jitter the sequence
bleeck@3 143 %is regular (1/pitch). With a pitch_jitter value of 1 the spacing of glottal
bleeck@3 144 %pulses fall in a range with an upper limit of double the period of
bleeck@3 145 %the pitch and a lower limit of 1/sampling rate.
bleeck@3 146 %The average spacing of glottal pulses is approx 1/pitch
bleeck@3 147 lwr_pth_jit=0.5-(pitch_jitter/2);
bleeck@3 148 upr_pth_jit=0.5+(pitch_jitter/2);
bleeck@3 149
bleeck@3 150 gp=cell(nr_formants,1); %for each formant will store pulse time
bleeck@3 151 for formant=1:nr_formants
bleeck@3 152 enough_pulses=0;
bleeck@3 153 pulse_number=0;
bleeck@3 154 while enough_pulses==0
bleeck@3 155 pulse_number=pulse_number+1;
bleeck@3 156 pulse_spacing(pulse_number)=max(1,floor(sr.*(sgpp.*2.*(lwr_pth_jit+(upr_pth_jit-lwr_pth_jit)*rand(1)))));
bleeck@3 157 pulse_time(pulse_number)=sum(pulse_spacing)-pulse_spacing(1)+1;
bleeck@3 158 if pulse_time(pulse_number)>=dur_samp
bleeck@3 159 pulse_time=pulse_time(1:pulse_number-1);
bleeck@3 160 pulse_number=pulse_number-1;
bleeck@3 161 enough_pulses=1;
bleeck@3 162 else
bleeck@3 163 end
bleeck@3 164 end
bleeck@3 165
bleeck@3 166
bleeck@3 167 gp{formant}=pulse_time;
bleeck@3 168 clear pulse_time;
bleeck@3 169 clear pulse_spacing;
bleeck@3 170 no_pulses(formant)=length(gp{formant});
bleeck@3 171 pulse_times{formant}=gp{formant};
bleeck@3 172 end%formant
bleeck@3 173
bleeck@3 174 formant_jitter=max(0,min(1,formant_jitter));
bleeck@3 175
bleeck@3 176 % if no gamma tone, then precalculate the damping function once for all
bleeck@3 177 % if onsettimeconstant <= 0 && halflifeconstant==0
bleeck@3 178 hl=0.04;
bleeck@3 179 damping=exp(t.*log(0.5)/hl); %this decays to 0.5 after hl seconds
bleeck@3 180 damping=damping/max(damping);
bleeck@3 181 % end
bleeck@3 182
bleeck@3 183 onsettimes=power(t,onsettimeconstant);
bleeck@3 184
bleeck@3 185
bleeck@3 186 %--------------------------------------------------------------------------
bleeck@3 187 for formant=1:nr_formants
bleeck@3 188 lw_log(formant)=max(log10(150), (log10(formfre(formant))- ((formant_jitter_bw.*.3)/2) ) ); %cannot go below 150Hz
bleeck@3 189 lw_log_adj(formant)=log10(formfre(formant))-((log10(formfre(formant))-lw_log(formant)).*formant_jitter);
bleeck@3 190 up_log(formant)=min(log10(4500),log10(formfre(formant))+ ((formant_jitter_bw.*.3)/2)) ;
bleeck@3 191 up_log_adj(formant)=log10(formfre(formant))+((up_log(formant)-log10(formfre(formant))).*formant_jitter);
bleeck@3 192 end
bleeck@3 193
bleeck@3 194
bleeck@3 195 % if we want to generate the octave, we take exactly the same pulses and
bleeck@3 196 % frequencies, but only every second one:
bleeck@3 197 if do_octave
bleeck@3 198 pulse_step=2;
bleeck@3 199 else
bleeck@3 200 pulse_step=1;
bleeck@3 201 end
bleeck@3 202
bleeck@3 203 nr_points=length(t);
bleeck@3 204 final_wave=zeros(dur_samp,nr_formants);
bleeck@3 205 for formant=1:nr_formants
bleeck@3 206 for count=1:pulse_step:no_pulses(formant)-1
bleeck@3 207 oneortwo=randperm(2);
bleeck@3 208 if oneortwo(1)==1
bleeck@3 209 freq=10.^((lw_log_adj(formant)+(log10(formfre(formant))-lw_log_adj(formant))*rand(1))); %lower range
bleeck@3 210 else
bleeck@3 211 freq=10.^((log10(formfre(formant))+(up_log_adj(formant)-log10(formfre(formant)))*rand(1))); %upper range
bleeck@3 212 end
bleeck@3 213
bleeck@3 214 if freq>1000
bleeck@3 215 no_octave=((log10(freq)-3))/.3;
bleeck@3 216 lev=-12.*no_octave;
bleeck@3 217 else
bleeck@3 218 lev=0;
bleeck@3 219 end
bleeck@3 220 amp=10.^(lev/20);
bleeck@3 221
bleeck@3 222 if halflifeconstant<=0
bleeck@3 223 hl=0.004;
bleeck@3 224 else
bleeck@3 225 hl=halflifeconstant/freq;
bleeck@3 226 end
bleeck@3 227
bleeck@3 228 nr_relevant_points=pulse_times{formant}(count+1)-pulse_times{formant}(count);
bleeck@3 229
bleeck@3 230 nr_relevant_points=nr_relevant_points*carry_decay_parameter;
bleeck@3 231
bleeck@3 232 if onsettimeconstant > 0
bleeck@3 233 damping=zeros(nr_relevant_points,1);
bleeck@3 234 for jj=1:nr_relevant_points
bleeck@3 235 damping(jj)=onsettimes(jj)*exp(t(jj)*log(0.5)/hl); %this decays to 0.5 after hl seconds
bleeck@3 236 end
bleeck@3 237 damping=damping/max(damping);
bleeck@3 238 end
bleeck@3 239 grafix=0;
bleeck@3 240 if grafix==1
bleeck@3 241 figure(234)
bleeck@3 242 plot(damping);
bleeck@3 243 end
bleeck@3 244
bleeck@3 245 this_formant=zeros(nr_relevant_points,1);
bleeck@3 246 for jj=1:nr_relevant_points
bleeck@3 247 sinsin=sin(2*pi*freq*t(jj));
bleeck@3 248 this_formant(jj)=amp*sinsin*damping(jj);
bleeck@3 249 end
bleeck@3 250 start_nr=pulse_times{formant}(count);
bleeck@3 251 stop_nr=start_nr+nr_relevant_points-1;
bleeck@3 252
bleeck@3 253 if stop_nr> nr_points
bleeck@3 254 nr_new=nr_points-start_nr+1;
bleeck@3 255 this_formant=this_formant(1:nr_new);
bleeck@3 256 stop_nr=nr_points;
bleeck@3 257 final_wave(start_nr:stop_nr,formant)= final_wave(start_nr:stop_nr,formant)+this_formant;
bleeck@3 258 else
bleeck@3 259 final_wave(start_nr:stop_nr,formant)= final_wave(start_nr:stop_nr,formant)+this_formant;
bleeck@3 260 end
bleeck@3 261 end
bleeck@3 262 end
bleeck@3 263 final_wave_total=zeros(dur_samp,1);
bleeck@3 264 for formant=1:nr_formants
bleeck@3 265 final_wave_total=final_wave_total+final_wave(:,formant);
bleeck@3 266 end
bleeck@3 267
bleeck@3 268 % sig=signal(final_wave_total,sr);
bleeck@3 269
bleeck@3 270 if isfield(options,'rise_time')
bleeck@3 271 rise_time=options.rise_time;
bleeck@3 272 else
bleeck@3 273 rise_time=0.015;
bleeck@3 274 end
bleeck@3 275
bleeck@3 276 if isfield(options,'fall_time')
bleeck@3 277 fall_time=options.fall_time;
bleeck@3 278 else
bleeck@3 279 fall_time=0.1;
bleeck@3 280 end
bleeck@3 281
bleeck@3 282 % sigvals=
bleeck@3 283 final_wave_total=gate_on_off(rise_time,fall_time,final_wave_total,sr);
bleeck@3 284 sig=signal(final_wave_total,sr);
bleeck@3 285 %
bleeck@3 286 % hold_time=length(sig)-rise_time-fall_time;
bleeck@3 287 % attack=linspace(0,1,round(rise_time*sr));
bleeck@3 288 %
bleeck@3 289 % hold=ones(1,round(hold_time*sr));
bleeck@3 290 % release=linspace(1,0,round(fall_time*sr));
bleeck@3 291 % envelope=[attack hold release];
bleeck@3 292 % envelope=envelope(1:getnrpoints(sig));
bleeck@3 293 % envelope=signal(envelope,sr);
bleeck@3 294 % sig=sig*envelope;
bleeck@3 295
bleeck@3 296
bleeck@3 297 function amp=calc_level(freq);
bleeck@3 298 %determines the level of a signal at a given frequency after it
bleeck@3 299 %has been filtered with a low-pass filter of 12dB/octave at 1kHz
bleeck@3 300 %first calculate if frequency is above 1kHz and if so by how many octaves
bleeck@3 301 %then multiply by slope.
bleeck@3 302 if freq>1000
bleeck@3 303 no_octave=((log10(freq)-3))/.3;
bleeck@3 304 lev=-12.*no_octave;
bleeck@3 305 else
bleeck@3 306 lev=0;
bleeck@3 307 end
bleeck@3 308 amp=10.^(lev/20);
bleeck@3 309
bleeck@3 310
bleeck@3 311
bleeck@3 312 function output=gate_on_off(onset,offset,input,sr)
bleeck@3 313 %check duration of signal is large enough for both the onset and offset
bleeck@3 314 %values.
bleeck@3 315
bleeck@3 316 sig_length_samp=length(input);
bleeck@3 317 onset_length_samp=floor(onset.*sr);
bleeck@3 318 offset_length_samp=floor(offset.*sr);
bleeck@3 319
bleeck@3 320 if (onset_length_samp+offset_length_samp)>sig_length_samp
bleeck@3 321 output=0;
bleeck@3 322 disp('---ERROR--- onset and offset duration too large for signal');
bleeck@3 323 return
bleeck@3 324 else end
bleeck@3 325
bleeck@3 326
bleeck@3 327 %generate onset and offset amplitudes
bleeck@3 328 n_on=onset_length_samp;
bleeck@3 329 k=[1:1:n_on];
bleeck@3 330 onset_win=(1-cos(2.*pi.*(k./(2.*(n_on-1)))))./2;
bleeck@3 331
bleeck@3 332 n_off=offset_length_samp;
bleeck@3 333 k=[1:1:n_off];
bleeck@3 334 offset_win=0.5+((cos(2.*pi.*(k./(2.*(n_off-1)))))./2);
bleeck@3 335
bleeck@3 336
bleeck@3 337
bleeck@3 338 total_window=ones(sig_length_samp,1);
bleeck@3 339 total_window(1:onset_length_samp)=onset_win;
bleeck@3 340 total_window(sig_length_samp-offset_length_samp+1:sig_length_samp)=offset_win;
bleeck@3 341
bleeck@3 342
bleeck@3 343
bleeck@3 344 output=input.*total_window;
bleeck@3 345
bleeck@3 346 return