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
|