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