rmeddis@0
|
1 function [audio, msg, time]=stimulusCreate(globalStimParams,...
|
rmeddis@0
|
2 stimComponents, doPlot)
|
rmeddis@0
|
3 % updated June 2007
|
rmeddis@0
|
4 % the only authorised version of stimulus create is the version to be found
|
rmeddis@0
|
5 % in MAP1_6. Other versions are copies!!
|
rmeddis@0
|
6 %
|
rmeddis@0
|
7 % for a simple tone you need
|
rmeddis@0
|
8 %
|
rmeddis@0
|
9 % % Mandatory structure fields
|
rmeddis@0
|
10 % globalStimParams.FS=100000;
|
rmeddis@0
|
11 % globalStimParams.overallDuration=.1; % s
|
rmeddis@0
|
12 % doPlot=1;
|
rmeddis@0
|
13 %
|
rmeddis@0
|
14 % stim.type='tone';
|
rmeddis@0
|
15 % stim.phases='sin';
|
rmeddis@0
|
16 % stim.toneDuration=.05;;
|
rmeddis@0
|
17 % stim.frequencies=500;
|
rmeddis@0
|
18 % stim.amplitudesdB=50;
|
rmeddis@0
|
19 % stim.beginSilence=.01;
|
rmeddis@0
|
20 % stim.endSilence=-1;
|
rmeddis@0
|
21 % stim.rampOnDur=.002;
|
rmeddis@0
|
22 % stim.rampOffDur=-1;
|
rmeddis@0
|
23 % [audio, msg]=stimulusCreate(globalStimParams, stim, doPlot);
|
rmeddis@0
|
24 %
|
rmeddis@0
|
25 % % or for multi-component stimuli
|
rmeddis@0
|
26 % % Mandatory structure fields
|
rmeddis@0
|
27 % globalStimParams.FS=100000;
|
rmeddis@0
|
28 % globalStimParams.overallDuration=.1; % s
|
rmeddis@0
|
29 % ear=1;
|
rmeddis@0
|
30 % componentNo=1;
|
rmeddis@0
|
31 %
|
rmeddis@0
|
32 % stimComponents(ear, componentNo).type='tone';
|
rmeddis@0
|
33 % stimComponents(ear, componentNo).phases='sin';
|
rmeddis@0
|
34 % stimComponents(ear, componentNo).toneDuration=.05;;
|
rmeddis@0
|
35 % stimComponents(ear, componentNo).frequencies=500;
|
rmeddis@0
|
36 % stimComponents(ear, componentNo).amplitudesdB=50;
|
rmeddis@0
|
37 % stimComponents(ear, componentNo).beginSilence=.01;
|
rmeddis@0
|
38 % stimComponents(ear, componentNo).endSilence=-1;
|
rmeddis@0
|
39 % stimComponents(ear, componentNo).rampOnDur=.002;
|
rmeddis@0
|
40 % stimComponents(ear, componentNo).rampOffDur=-1;
|
rmeddis@0
|
41 %
|
rmeddis@0
|
42 % % All components are forced to have the same overall duration and sample rate
|
rmeddis@0
|
43 %
|
rmeddis@0
|
44 %
|
rmeddis@0
|
45 % [audio, msg]=stimulusCreate(globalStimParams, stimComponents);
|
rmeddis@0
|
46 %
|
rmeddis@0
|
47 % Optional fields
|
rmeddis@0
|
48 % .ears overides ear setting by component
|
rmeddis@0
|
49 % globalStimParams.ears='monoticL'; % 'monoticL', 'monoticR', 'diotic', 'dichotic'
|
rmeddis@0
|
50 %
|
rmeddis@0
|
51 % globalStimParams.filter = [leftfl leftfu rightfl right fu]
|
rmeddis@0
|
52 % filter is applied separately to left and right combined sounds
|
rmeddis@0
|
53 %
|
rmeddis@0
|
54 % correction factor is applied to all signals to compensate for differences in output devices.
|
rmeddis@0
|
55 % audioOutCorrection is a scalar
|
rmeddis@0
|
56 % globalStimParams.audioOutCorrection=2;
|
rmeddis@0
|
57 %
|
rmeddis@0
|
58 % globalStimParams.FFT= 1; % {0, 1}
|
rmeddis@0
|
59 % globalStimParams.doPlot=1; % {0, 1}
|
rmeddis@0
|
60 % globalStimParams.doPlay=1; % {0, 1}
|
rmeddis@0
|
61 %
|
rmeddis@0
|
62 % stimComponents(ear, componentNo).filter=[100 10000 2] % [lower, upper, order] applies to an
|
rmeddis@0
|
63 % individual component
|
rmeddis@0
|
64 %
|
rmeddis@0
|
65 % Output arguments:
|
rmeddis@0
|
66 % audio is a stereo signal, a 2-column vector
|
rmeddis@0
|
67 %
|
rmeddis@0
|
68 %
|
rmeddis@0
|
69 % stim.type='noise'; % {'IRN', 'irn', 'noise', 'pinkNoise'}
|
rmeddis@0
|
70 % stim.toneDuration=.05;;
|
rmeddis@0
|
71 % stim.amplitudesdB=50;
|
rmeddis@0
|
72 % stim.beginSilence=.01;
|
rmeddis@0
|
73 % stim.endSilence=-1;
|
rmeddis@0
|
74 % stim.rampOnDur=.002;
|
rmeddis@0
|
75 % stim.rampOffDur=-1;
|
rmeddis@0
|
76 % [audio, msg]=stimulusCreate(globalStimParams, stim);
|
rmeddis@0
|
77 %
|
rmeddis@0
|
78 % % for IRN only
|
rmeddis@0
|
79 % stim.type='IRN';
|
rmeddis@0
|
80 % stim.toneDuration=.05;;
|
rmeddis@0
|
81 % stim.amplitudesdB=50;
|
rmeddis@0
|
82 % stim.beginSilence=.01;
|
rmeddis@0
|
83 % stim.endSilence=-1;
|
rmeddis@0
|
84 % stim.rampOnDur=.002;
|
rmeddis@0
|
85 % stim.rampOffDur=-1;
|
rmeddis@0
|
86 % stim.niterations = 8; %0 for white noise
|
rmeddis@0
|
87 % stim.delay = 1/150;
|
rmeddis@0
|
88 % stim.irnGain = 1;
|
rmeddis@0
|
89 % [audio, msg]=stimulusCreate(globalStimParams, stim);stimComponents.clickWidth;
|
rmeddis@0
|
90 %
|
rmeddis@0
|
91 % stim.type='clicks'; % uses frequencies for duty cycle
|
rmeddis@0
|
92 % stim.toneDuration=.05;;
|
rmeddis@0
|
93 % stim.frequencies=500;
|
rmeddis@0
|
94 % stim.amplitudesdB=50;
|
rmeddis@0
|
95 % stim.beginSilence=.01;
|
rmeddis@0
|
96 % stim.endSilence=-1;
|
rmeddis@0
|
97 % stim.rampOnDur=.002;
|
rmeddis@0
|
98 % stim.rampOffDur=-1;
|
rmeddis@0
|
99 % [audio, msg]=stimulusCreate(globalStimParams, stim);
|
rmeddis@0
|
100 % stimComponents.clickWidth=.0001; % default= dt
|
rmeddis@0
|
101 % stimComponents.clickHeight= 20e-6; % default= 28e-6 * 10^(stimComponents.amplitudesdB/20);
|
rmeddis@0
|
102 % stim.clickTimes=[.4 .6]; % times in cylce when clicks occur, default = 1
|
rmeddis@0
|
103 %
|
rmeddis@0
|
104 %
|
rmeddis@0
|
105
|
rmeddis@0
|
106 msg=''; %error messages; no message is a good message
|
rmeddis@0
|
107
|
rmeddis@0
|
108 % plotting can be set as a separate argument or as a globalstimParams
|
rmeddis@0
|
109 % variable. this is for backwards compatibility only
|
rmeddis@0
|
110 if nargin>2,
|
rmeddis@0
|
111 globalStimParams.doPlot=doPlot;
|
rmeddis@0
|
112 end
|
rmeddis@0
|
113
|
rmeddis@0
|
114 % stimComponents(1,1).endSilence=-1; % end silence is always computed
|
rmeddis@0
|
115
|
rmeddis@0
|
116 % perform checks and set defaults
|
rmeddis@0
|
117 [globalStimParams, stimComponents]=checkDescriptors(globalStimParams, stimComponents);
|
rmeddis@0
|
118
|
rmeddis@0
|
119
|
rmeddis@0
|
120 % create empty stereo audio of appropriate length
|
rmeddis@0
|
121 audio=zeros(globalStimParams.nSignalPoints, 2);
|
rmeddis@0
|
122 % signal=zeros(globalStimParams.nSignalPoints, 1);
|
rmeddis@0
|
123 dt=globalStimParams.dt;
|
rmeddis@0
|
124
|
rmeddis@0
|
125 [Nears nComponentSounds]=size(stimComponents);
|
rmeddis@0
|
126 for ear=1:Nears % left/ right
|
rmeddis@0
|
127 % combinedSignal is the sum of all sounds in one ear
|
rmeddis@0
|
128 % it is a column vector
|
rmeddis@0
|
129 combinedSignal=zeros(globalStimParams.nSignalPoints,1);
|
rmeddis@0
|
130
|
rmeddis@0
|
131 % find valid components
|
rmeddis@0
|
132 % if a component is empty, it is not a validComponent and is ignored
|
rmeddis@0
|
133 validStimComponents=[];
|
rmeddis@0
|
134 for i=1:nComponentSounds
|
rmeddis@0
|
135 if ~isempty(stimComponents(ear,i).type)
|
rmeddis@0
|
136 validStimComponents=[validStimComponents i];
|
rmeddis@0
|
137 end
|
rmeddis@0
|
138 end
|
rmeddis@0
|
139
|
rmeddis@0
|
140 for componentNo=validStimComponents
|
rmeddis@0
|
141 % compute individual components before adding
|
rmeddis@0
|
142 stim=stimComponents(ear,componentNo);
|
rmeddis@0
|
143 switch stim.type
|
rmeddis@0
|
144 case 'tone'
|
rmeddis@0
|
145 stimulus=maketone(globalStimParams, stim);
|
rmeddis@0
|
146
|
rmeddis@0
|
147 case 'fmTone'
|
rmeddis@0
|
148 stimulus=makeFMtone(globalStimParams, stim);
|
rmeddis@0
|
149
|
rmeddis@0
|
150 case 'OHIO'
|
rmeddis@0
|
151 stim.beginSilence=0;
|
rmeddis@0
|
152 stimulus=makeOHIOtones(globalStimParams, stim);
|
rmeddis@0
|
153
|
rmeddis@0
|
154 case 'transposedStimulus'
|
rmeddis@0
|
155 stim.beginSilence=0; % necessary because of recursion
|
rmeddis@0
|
156 stimulus=makeTransposedStimulus(globalStimParams, stim);
|
rmeddis@0
|
157
|
rmeddis@0
|
158 case { 'noise', 'pinkNoise'}
|
rmeddis@0
|
159 stimulus=makeNoise(globalStimParams, stim);
|
rmeddis@0
|
160
|
rmeddis@0
|
161 case { 'whiteNoise'}
|
rmeddis@0
|
162 stimulus=makeWhiteNoise(globalStimParams, stim);
|
rmeddis@0
|
163
|
rmeddis@0
|
164 case {'IRN', 'irn'}
|
rmeddis@0
|
165 stimulus=makeIRN(globalStimParams, stim);
|
rmeddis@0
|
166
|
rmeddis@0
|
167 case {'RPN'}
|
rmeddis@0
|
168 stimulus=makeRPN(globalStimParams, stim);
|
rmeddis@0
|
169
|
rmeddis@0
|
170 case 'clicks'
|
rmeddis@0
|
171 stimulus=makeClicks(globalStimParams, stim);
|
rmeddis@0
|
172
|
rmeddis@0
|
173 case 'PressnitzerClicks'
|
rmeddis@0
|
174 % kxx clicks
|
rmeddis@0
|
175 % k is 1/clickRepeatFrequency
|
rmeddis@0
|
176 stimulus=makePressnitzerClicks(globalStimParams, stimComponents);
|
rmeddis@0
|
177
|
rmeddis@0
|
178 case 'PressnitzerABxClicks'
|
rmeddis@0
|
179 % kxx clicks
|
rmeddis@0
|
180 % k is 1/clickRepeatFrequency
|
rmeddis@0
|
181 stimulus=makePressnitzerABxClicks(globalStimParams, stimComponents);
|
rmeddis@0
|
182
|
rmeddis@0
|
183 case 'ABxClicks'
|
rmeddis@0
|
184 % A=rand*k, B=k-A, x=rand*k.
|
rmeddis@0
|
185 stimulus=makeABxClicks(globalStimParams, stimComponents);
|
rmeddis@0
|
186
|
rmeddis@0
|
187 case 'YostClicks'
|
rmeddis@0
|
188 % kxx clicks
|
rmeddis@0
|
189 % k is 1/clickRepeatFrequency
|
rmeddis@0
|
190 stimulus=makeYostClicks(globalStimParams, stimComponents);
|
rmeddis@0
|
191
|
rmeddis@0
|
192 case 'kxxClicks'
|
rmeddis@0
|
193 % kxx clicks
|
rmeddis@0
|
194 % k is 1/clickRepeatFrequency
|
rmeddis@0
|
195 stimulus=makeKxxClicks(globalStimParams, stimComponents);
|
rmeddis@0
|
196
|
rmeddis@0
|
197
|
rmeddis@0
|
198 % case 'babble'
|
rmeddis@0
|
199 % % NB random start in a long file
|
rmeddis@0
|
200 % [stimulus sampleRate]= wavread('babble');
|
rmeddis@0
|
201 % nPoints=round(sampleRate*...
|
rmeddis@0
|
202 % stimComponents(ear,componentNo).toneDuration);
|
rmeddis@0
|
203 % start=round(rand(1,1)*(length(stimulus)-nPoints));
|
rmeddis@0
|
204 % stimulus=stimulus(start:start+nPoints-1);
|
rmeddis@0
|
205 % rms= 20*log10((mean(stimulus.^2).^0.5)/20e-6);
|
rmeddis@0
|
206 % dBSPLrms=stimComponents(ear,componentNo).amplitudesdB;
|
rmeddis@0
|
207 % gain=10.^((dBSPLrms-rms)/20);
|
rmeddis@0
|
208 % stimulus=stimulus'*gain;
|
rmeddis@0
|
209
|
rmeddis@0
|
210 case 'speech'
|
rmeddis@0
|
211 [stimulus sampleRate]= wavread('speech');
|
rmeddis@0
|
212 stimulus=stimulus';
|
rmeddis@0
|
213 nPoints=sampleRate*stimComponents(ear,componentNo).toneDuration;
|
rmeddis@0
|
214 if nPoints > length(stimulus)
|
rmeddis@0
|
215 initialSilence=zeros(1,nPoints-length(stimulus));
|
rmeddis@0
|
216 else
|
rmeddis@0
|
217 initialSilence=[];
|
rmeddis@0
|
218 start=round(rand(1,1)*(length(stimulus)-nPoints));
|
rmeddis@0
|
219 stimulus=stimulus(start:start+nPoints-1);
|
rmeddis@0
|
220 end
|
rmeddis@0
|
221 rms= 20*log10((mean(stimulus.^2).^0.5)/20e-6);
|
rmeddis@0
|
222 dBSPLrms=stimComponents(ear,componentNo).amplitudesdB;
|
rmeddis@0
|
223 gain=10.^((dBSPLrms-rms)/20);
|
rmeddis@0
|
224 stimulus=stimulus*gain;
|
rmeddis@0
|
225 stimulus=[stimulus initialSilence ];
|
rmeddis@0
|
226
|
rmeddis@0
|
227
|
rmeddis@0
|
228 case 'file'
|
rmeddis@0
|
229 % component already read from file and stored in stimulus. Insert it here
|
rmeddis@0
|
230 % additional code for establishing signal rms level
|
rmeddis@0
|
231 % NB signal is always mono at this stage
|
rmeddis@0
|
232
|
rmeddis@0
|
233 stimulus=stim.stimulus;
|
rmeddis@0
|
234 dBSPL=stim.amplitudesdB;
|
rmeddis@0
|
235
|
rmeddis@0
|
236 nPoints=round(stim.toneDuration/dt);
|
rmeddis@0
|
237 [r c]=size(stimulus);
|
rmeddis@0
|
238 if r>c, stimulus=stimulus'; end % secure horizontal vector
|
rmeddis@0
|
239 stimulus=stimulus(1,1:nPoints); % only mono taken from file
|
rmeddis@0
|
240
|
rmeddis@0
|
241 try
|
rmeddis@0
|
242 % dB rms
|
rmeddis@0
|
243 rms= 20*log10((mean(stimulus.^2).^0.5)/20e-6);
|
rmeddis@0
|
244 % special request to set fixed rms for stimulus
|
rmeddis@0
|
245 dBSPLrms=stimComponents(ear,componentNo).amplitudesdBrms;
|
rmeddis@0
|
246 if ~(dBSPLrms==-1)
|
rmeddis@0
|
247 gain=10.^((dBSPLrms-rms)/20);
|
rmeddis@0
|
248 stimulus=stimulus*gain;
|
rmeddis@0
|
249 end
|
rmeddis@0
|
250 catch
|
rmeddis@0
|
251 % If no request for rms level is made
|
rmeddis@0
|
252 % set dB as peak amp
|
rmeddis@0
|
253 [stimulus gain]= normalize(stimulus);
|
rmeddis@0
|
254 dBSPL=stimComponents(ear,componentNo).amplitudesdB;
|
rmeddis@0
|
255 if ~(dBSPL==-1)
|
rmeddis@0
|
256 amplitude=28e-6*10.^(dBSPL/20);
|
rmeddis@0
|
257 stimulus=stimulus*amplitude;
|
rmeddis@0
|
258 end
|
rmeddis@0
|
259 end
|
rmeddis@0
|
260
|
rmeddis@0
|
261 case 'none'
|
rmeddis@0
|
262 % no stimulus
|
rmeddis@0
|
263 stimulus=zeros(1,round(stim.toneDuration/dt));
|
rmeddis@0
|
264
|
rmeddis@0
|
265 case 'digitStrings'
|
rmeddis@0
|
266 % select a digit string at random anduse as target
|
rmeddis@0
|
267 files=dir(['..' filesep '..' filesep 'multithresholdResources\digitStrings']);
|
rmeddis@0
|
268 files=files(3:end);
|
rmeddis@0
|
269 nFiles=length(files);
|
rmeddis@0
|
270 fileNo=ceil(nFiles*rand);
|
rmeddis@0
|
271 fileData=files(fileNo);
|
rmeddis@0
|
272 fileName=['..\..\multithresholdResources\digitStrings\' fileData.name];
|
rmeddis@0
|
273 [stimulus sampleRate]=wavread(fileName);
|
rmeddis@0
|
274 stimulus=stimulus(:,1)'; % make into a row vector
|
rmeddis@0
|
275 % estimate the extend of endsilence padding
|
rmeddis@0
|
276 nPoints=sampleRate*...
|
rmeddis@0
|
277 stimComponents(ear,componentNo).toneDuration;
|
rmeddis@0
|
278 if nPoints > length(stimulus)
|
rmeddis@0
|
279 endSilence=zeros(1,nPoints-length(stimulus));
|
rmeddis@0
|
280 else
|
rmeddis@0
|
281 % or truncate the file
|
rmeddis@0
|
282 endSilence=[];
|
rmeddis@0
|
283 stimulus=stimulus(1:nPoints);
|
rmeddis@0
|
284 end
|
rmeddis@0
|
285 % compute rms before adding silence
|
rmeddis@0
|
286 rms= 20*log10((mean(stimulus.^2).^0.5)/20e-6);
|
rmeddis@0
|
287 dBSPLrms=stimComponents(ear,componentNo).amplitudesdB;
|
rmeddis@0
|
288 gain=10.^((dBSPLrms-rms)/20);
|
rmeddis@0
|
289 stimulus=stimulus*gain;
|
rmeddis@0
|
290 stimulus=[stimulus endSilence];
|
rmeddis@0
|
291 global stimulusParameters
|
rmeddis@0
|
292 stimulusParameters.digitString=fileName(end-7:end-5);
|
rmeddis@0
|
293
|
rmeddis@0
|
294 otherwise
|
rmeddis@0
|
295 switch stim.type(end-5:end)
|
rmeddis@0
|
296 % any file name with 'Babble' at the end is a
|
rmeddis@0
|
297 % multiThreshold file
|
rmeddis@0
|
298 case 'Babble'
|
rmeddis@0
|
299 % one of the many babbles is selected.
|
rmeddis@0
|
300 % NB random start in a long file
|
rmeddis@0
|
301 % stim.type should contain the name of the babble file
|
rmeddis@0
|
302 fileName=['..' filesep '..' filesep ...
|
rmeddis@0
|
303 'multithresholdResources' filesep ...
|
rmeddis@0
|
304 'backgrounds and maskers'...
|
rmeddis@0
|
305 filesep stim.type];
|
rmeddis@0
|
306
|
rmeddis@0
|
307 [stimulus sampleRate]= wavread(fileName);
|
rmeddis@0
|
308 if ~isequal(sampleRate, globalStimParams.FS)
|
rmeddis@0
|
309 % NB the file will read but will disagree with
|
rmeddis@0
|
310 % tone stimuli or other files read
|
rmeddis@0
|
311 msg= ['error: file sample rate disagrees ' ...
|
rmeddis@0
|
312 'with sample rate requested in paradigm'...
|
rmeddis@0
|
313 ' file (' ...
|
rmeddis@0
|
314 num2str(globalStimParams.FS) ').'];
|
rmeddis@0
|
315 error(msg);
|
rmeddis@0
|
316 end
|
rmeddis@0
|
317 nPoints=round(sampleRate*...
|
rmeddis@0
|
318 stimComponents(ear,componentNo).toneDuration);
|
rmeddis@0
|
319 start=round(rand(1,1)*(length(stimulus)-nPoints));
|
rmeddis@0
|
320 stimulus=stimulus(start:start+nPoints-1);
|
rmeddis@0
|
321 rms= 20*log10((mean(stimulus.^2).^0.5)/20e-6);
|
rmeddis@0
|
322 dBSPLrms=stimComponents(ear,componentNo).amplitudesdB;
|
rmeddis@0
|
323 gain=10.^((dBSPLrms-rms)/20);
|
rmeddis@0
|
324 stimulus=stimulus'*gain;
|
rmeddis@0
|
325
|
rmeddis@0
|
326 otherwise
|
rmeddis@0
|
327 % stim.type may be the name of a file to be read
|
rmeddis@0
|
328 % play from beginning for stimulus duration
|
rmeddis@0
|
329 try
|
rmeddis@0
|
330 [stimulus sampleRate]= wavread([stim.type '.wav']);
|
rmeddis@0
|
331 catch
|
rmeddis@0
|
332 error(['stimulusCreate: unrecognised stimulus type -> '...
|
rmeddis@0
|
333 stim.type])
|
rmeddis@0
|
334 end
|
rmeddis@0
|
335 if ~isequal(sampleRate, globalStimParams.FS)
|
rmeddis@0
|
336 % NB the file will read but will disagree with
|
rmeddis@0
|
337 % tone stimuli or other files read
|
rmeddis@0
|
338 msg= ['error: file sample rate disagrees ' ...
|
rmeddis@0
|
339 'with sample rate requested in paradigm'...
|
rmeddis@0
|
340 ' file (' ...
|
rmeddis@0
|
341 num2str(globalStimParams.FS) ').'];
|
rmeddis@0
|
342 error(msg);
|
rmeddis@0
|
343 end
|
rmeddis@0
|
344 stimulus=stimulus'; % make into a row vector
|
rmeddis@0
|
345 % estimate the extend of endsilence padding
|
rmeddis@0
|
346 nPoints=sampleRate*...
|
rmeddis@0
|
347 stimComponents(ear,componentNo).toneDuration;
|
rmeddis@0
|
348 if nPoints > length(stimulus)
|
rmeddis@0
|
349 endSilence=zeros(1,nPoints-length(stimulus));
|
rmeddis@0
|
350 else
|
rmeddis@0
|
351 % or truncate the file
|
rmeddis@0
|
352 endSilence=[];
|
rmeddis@0
|
353 stimulus=stimulus(1:nPoints);
|
rmeddis@0
|
354 end
|
rmeddis@0
|
355 % compute rms before adding silence
|
rmeddis@0
|
356 rms= 20*log10((mean(stimulus.^2).^0.5)/20e-6);
|
rmeddis@0
|
357 dBSPLrms=stimComponents(ear,componentNo).amplitudesdB;
|
rmeddis@0
|
358 gain=10.^((dBSPLrms-rms)/20);
|
rmeddis@0
|
359 stimulus=stimulus*gain;
|
rmeddis@0
|
360 stimulus=[stimulus endSilence];
|
rmeddis@0
|
361 end
|
rmeddis@0
|
362 end
|
rmeddis@0
|
363
|
rmeddis@0
|
364 % NB stimulus is a row vector now!
|
rmeddis@0
|
365 % audio and combinedSignal were column vectors
|
rmeddis@0
|
366 % signal will be a row vector
|
rmeddis@0
|
367
|
rmeddis@0
|
368 % filter stimulus
|
rmeddis@0
|
369 try
|
rmeddis@0
|
370 % if filter field is present, [lower upper order]
|
rmeddis@0
|
371 if stim.filter(1)>0 % 0 means don't filter
|
rmeddis@0
|
372 stimulus=Butterworth (stimulus, dt, stim.filter(1), ...
|
rmeddis@0
|
373 stim.filter(2), stim.filter(3));
|
rmeddis@0
|
374
|
rmeddis@0
|
375 end
|
rmeddis@0
|
376 catch
|
rmeddis@0
|
377 end
|
rmeddis@0
|
378
|
rmeddis@0
|
379
|
rmeddis@0
|
380 % apply amplitude modulation
|
rmeddis@0
|
381 if isfield(stim,'AMfrequency') & isfield(stim,'AMdepth')
|
rmeddis@0
|
382 if stim.AMfrequency>0 & stim.AMdepth>0
|
rmeddis@0
|
383 time=dt:dt:dt*length(stimulus);
|
rmeddis@0
|
384 modulator=sin(2*pi*stim.AMfrequency*time);
|
rmeddis@0
|
385 modulator=modulator*stim.AMdepth/100 + 1; % depth is percent
|
rmeddis@0
|
386 stimulus=stimulus.*modulator/2;
|
rmeddis@0
|
387 end
|
rmeddis@0
|
388 end
|
rmeddis@0
|
389
|
rmeddis@0
|
390 % Basic stimulus is now created.
|
rmeddis@0
|
391 % Add trappings, ramps, silences to main stimulus
|
rmeddis@0
|
392 rampOnTime=0; %ramp starts at the beginning of the stimulus
|
rmeddis@0
|
393 rampOffTime=stim.toneDuration-stim.rampOffDur;
|
rmeddis@0
|
394 if stim.rampOnDur>0.0001
|
rmeddis@0
|
395 stimulus=applyRampOn(stimulus, stim.rampOnDur, rampOnTime, 1/dt);
|
rmeddis@0
|
396 stimulus=applyRampOff(stimulus, stim.rampOffDur, rampOffTime, 1/dt);
|
rmeddis@0
|
397 end
|
rmeddis@0
|
398 if stim.rampOnDur<-0.0001 % apply Gaussian ramp
|
rmeddis@0
|
399 stimulus=applyGaussianRamps(stimulus, -stim.rampOnDur, 1/dt);
|
rmeddis@0
|
400 end
|
rmeddis@0
|
401
|
rmeddis@0
|
402 % Initial silence
|
rmeddis@0
|
403 % start with a signal of the right length consisting of zeros
|
rmeddis@0
|
404 signal=zeros(1, globalStimParams.nSignalPoints);
|
rmeddis@0
|
405 % identify start of stimulus
|
rmeddis@0
|
406 insertStimulusAt=round(stim.beginSilence/dt)+1;
|
rmeddis@0
|
407 % add stimulus
|
rmeddis@0
|
408 endOfStimulus=insertStimulusAt+length(stimulus)-1;
|
rmeddis@0
|
409 if endOfStimulus<=globalStimParams.nSignalPoints
|
rmeddis@0
|
410 signal(1, insertStimulusAt: endOfStimulus)=stimulus;
|
rmeddis@0
|
411 else
|
rmeddis@0
|
412 error('stimulusCreate: tone too long to fit into the overall duration')
|
rmeddis@0
|
413 end
|
rmeddis@0
|
414
|
rmeddis@0
|
415 % time signature
|
rmeddis@0
|
416 time=dt:dt:dt*length(signal);
|
rmeddis@0
|
417 % figure(22), plot(signal), title([num2str(ear) ' - ' num2str(componentNo)]),pause (1)
|
rmeddis@0
|
418
|
rmeddis@0
|
419 try
|
rmeddis@0
|
420 % create a column vector and trap if no signal has been created
|
rmeddis@0
|
421 signal=signal';
|
rmeddis@0
|
422 % also traps if signals are not the same length
|
rmeddis@0
|
423 combinedSignal=combinedSignal+signal;
|
rmeddis@0
|
424 % figure(21), plot(combinedSignal), title([num2str(ear) ' - ' num2str(componentNo)]),pause (1)
|
rmeddis@0
|
425 catch
|
rmeddis@0
|
426 % dump everything because there is a problem
|
rmeddis@0
|
427 globalStimParams
|
rmeddis@0
|
428 [ear componentNo]
|
rmeddis@0
|
429 stim
|
rmeddis@0
|
430 size(combinedSignal)
|
rmeddis@0
|
431 size(signal)
|
rmeddis@0
|
432 [ size(initialSilence) size(signal) size(endSilence)]
|
rmeddis@0
|
433 [ ear componentNo...
|
rmeddis@0
|
434 round(globalStimParams.overallDuration*globalStimParams.FS)...
|
rmeddis@0
|
435 round(stim.beginSilence*globalStimParams.FS)...
|
rmeddis@0
|
436 round(stim.toneDuration*globalStimParams.FS) ...
|
rmeddis@0
|
437 round(stim.endSilence*globalStimParams.FS)...
|
rmeddis@0
|
438 ( round(globalStimParams.overallDuration*globalStimParams.FS)...
|
rmeddis@0
|
439 -round(stim.beginSilence*globalStimParams.FS)...
|
rmeddis@0
|
440 -round(stim.toneDuration*globalStimParams.FS) ...
|
rmeddis@0
|
441 -round(stim.endSilence*globalStimParams.FS))...
|
rmeddis@0
|
442 ]
|
rmeddis@0
|
443 error(' trouble in stimulusCreate: signals are the wrong length ')
|
rmeddis@0
|
444 end
|
rmeddis@0
|
445
|
rmeddis@0
|
446 end % component no
|
rmeddis@0
|
447
|
rmeddis@0
|
448 audio(:,ear)= combinedSignal;
|
rmeddis@0
|
449
|
rmeddis@0
|
450 % FFT
|
rmeddis@0
|
451 try
|
rmeddis@0
|
452 if globalStimParams.FFT
|
rmeddis@0
|
453 showFFT (audio, dt)
|
rmeddis@0
|
454 end
|
rmeddis@0
|
455 catch
|
rmeddis@0
|
456 end
|
rmeddis@0
|
457
|
rmeddis@0
|
458
|
rmeddis@0
|
459 end % ear
|
rmeddis@0
|
460
|
rmeddis@0
|
461 switch globalStimParams.ears
|
rmeddis@0
|
462 % normally the signals are created in appropriate ears but .ears can
|
rmeddis@0
|
463 % overide this to produce a mono signal.
|
rmeddis@0
|
464 case 'monoticL';
|
rmeddis@0
|
465 % combine left and right ears to make a mono signal in the left ear
|
rmeddis@0
|
466 audio(:,1)=audio(:,1)+audio(:,2);
|
rmeddis@0
|
467 audio(:,2)=zeros(globalStimParams.nSignalPoints, 1);
|
rmeddis@0
|
468
|
rmeddis@0
|
469 case 'monoticR';
|
rmeddis@0
|
470 % combine left and right ears to make a mono signal in the right ear
|
rmeddis@0
|
471 audio(:,2)=audio(:,1)+audio(:,2);
|
rmeddis@0
|
472 audio(:,1)=zeros(globalStimParams.nSignalPoints, 1);
|
rmeddis@0
|
473
|
rmeddis@0
|
474 case 'diotic';
|
rmeddis@0
|
475 % combine left and right ears to make a mono signal in both ears
|
rmeddis@0
|
476 bothEarsCombined=audio(:,1)+audio(:,2);
|
rmeddis@0
|
477 audio(:,2)=bothEarsCombined;
|
rmeddis@0
|
478 audio(:,1)=bothEarsCombined;
|
rmeddis@0
|
479
|
rmeddis@0
|
480 otherwise
|
rmeddis@0
|
481 % Any other denomination produces no effect here
|
rmeddis@0
|
482 end
|
rmeddis@0
|
483
|
rmeddis@0
|
484 % Plotting as required
|
rmeddis@0
|
485 if globalStimParams.doPlot
|
rmeddis@0
|
486 figure(9), clf
|
rmeddis@0
|
487 plot(time,audio(:,1)'), hold on,
|
rmeddis@0
|
488 if Nears>1
|
rmeddis@0
|
489 offSet=(max(audio(:,1))+max(audio(:,2)))/10;
|
rmeddis@0
|
490 offSet=2*offSet+max(audio(:,1))+max(audio(:,2));
|
rmeddis@0
|
491 plot(time,audio(:,2)'+offSet,'r'), hold off
|
rmeddis@0
|
492 end
|
rmeddis@0
|
493 ylabel('left right')
|
rmeddis@0
|
494 end
|
rmeddis@0
|
495
|
rmeddis@0
|
496 % Calibration
|
rmeddis@0
|
497 % all signals are divided by this correction factor
|
rmeddis@0
|
498 % peakAmp=globalStimParams.audioOutCorrection; % microPascals = 100 dB SPL
|
rmeddis@0
|
499
|
rmeddis@0
|
500 audio=audio/globalStimParams.audioOutCorrection;
|
rmeddis@0
|
501
|
rmeddis@0
|
502 if globalStimParams.doPlay
|
rmeddis@35
|
503 if ispc
|
rmeddis@35
|
504 wavplay(audio,globalStimParams.FS)
|
rmeddis@35
|
505 else
|
rmeddis@35
|
506 sound(audio,globalStimParams.FS)
|
rmeddis@35
|
507 end
|
rmeddis@35
|
508
|
rmeddis@0
|
509 wavplay(audio,globalStimParams.FS)
|
rmeddis@0
|
510 end
|
rmeddis@0
|
511 % all Done
|
rmeddis@0
|
512 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
rmeddis@0
|
513
|
rmeddis@0
|
514
|
rmeddis@0
|
515
|
rmeddis@0
|
516 %---------------------------------------------------- maketone
|
rmeddis@0
|
517 function tone=maketone(globalStimParams, stimComponents)
|
rmeddis@0
|
518 % maketone generates a stimComponents tone
|
rmeddis@0
|
519 % tone=maketone(dt, frequencies, toneDuration, dBSPL, phases)
|
rmeddis@0
|
520 % tone is returned in Pascals
|
rmeddis@0
|
521 % frequencies is a list of frequencies
|
rmeddis@0
|
522 % phase is list of phases or 'sin', 'cos', 'alt'
|
rmeddis@0
|
523 %
|
rmeddis@0
|
524 % defaults:
|
rmeddis@0
|
525 % phase = sin
|
rmeddis@0
|
526 % dBSPL=56 dB SPL
|
rmeddis@0
|
527
|
rmeddis@0
|
528 dt=globalStimParams.dt;
|
rmeddis@0
|
529 frequencies=stimComponents.frequencies;
|
rmeddis@0
|
530 toneDuration=stimComponents.toneDuration;
|
rmeddis@0
|
531 dBSPL=stimComponents.amplitudesdB;
|
rmeddis@0
|
532 phases=stimComponents.phases;
|
rmeddis@0
|
533
|
rmeddis@0
|
534 if ischar(phases)
|
rmeddis@0
|
535 switch phases
|
rmeddis@0
|
536 case 'sin'
|
rmeddis@0
|
537 phases= zeros(1,length(frequencies));
|
rmeddis@0
|
538 case 'cos'
|
rmeddis@0
|
539 phases= pi/2*ones(1,length(frequencies));
|
rmeddis@0
|
540 case 'alt'
|
rmeddis@0
|
541 phases= repmat([0 pi/2], 1, floor(length(frequencies)/2));
|
rmeddis@0
|
542 if length(phases)<length(frequencies)
|
rmeddis@0
|
543 phases=[phases 0];
|
rmeddis@0
|
544 end
|
rmeddis@0
|
545 case {'ran', 'rand'}
|
rmeddis@0
|
546 phases= 2*pi*rand(1,length(frequencies));
|
rmeddis@0
|
547 end
|
rmeddis@0
|
548 end
|
rmeddis@0
|
549
|
rmeddis@0
|
550 if length(phases)==1, phases=repmat(phases(1), 1, length(frequencies)); end
|
rmeddis@0
|
551 if length(phases)<length(frequencies)
|
rmeddis@0
|
552 error('makeTone:phase specification must have the same length as frequencies')
|
rmeddis@0
|
553 end
|
rmeddis@0
|
554
|
rmeddis@0
|
555 if length(dBSPL)==1, dBSPL=repmat(dBSPL(1), 1, length(frequencies)); end
|
rmeddis@0
|
556 if length(dBSPL)<length(dBSPL)
|
rmeddis@0
|
557 error('makeTone:dBSPL specification must have the same length as frequencies')
|
rmeddis@0
|
558 end
|
rmeddis@0
|
559
|
rmeddis@0
|
560 time=dt:dt:toneDuration;
|
rmeddis@0
|
561 amplitudes=28e-6* 10.^(dBSPL/20);
|
rmeddis@0
|
562
|
rmeddis@0
|
563 tone=zeros(size(time));
|
rmeddis@0
|
564 for i=1:length(frequencies)
|
rmeddis@0
|
565 frequency=frequencies(i);
|
rmeddis@0
|
566 phase=phases(i);
|
rmeddis@0
|
567 tone=tone+amplitudes(i)*sin(2*pi*frequency*time+phase);
|
rmeddis@0
|
568 end
|
rmeddis@0
|
569
|
rmeddis@0
|
570 % figure(1), plot(time, signal)
|
rmeddis@0
|
571
|
rmeddis@0
|
572
|
rmeddis@0
|
573 %---------------------------------------------------- makeOHIOtones
|
rmeddis@0
|
574 function stimulus=makeOHIOtones(globalStimParams, stimComponents)
|
rmeddis@0
|
575
|
rmeddis@0
|
576 % Generates a stimulus consisting of one or more 10-ms tones
|
rmeddis@0
|
577 % Tones are presented at 10-ms intervals
|
rmeddis@0
|
578 % Each tone has its own amplitude and its own ramp.
|
rmeddis@0
|
579
|
rmeddis@0
|
580 frequencies=stimComponents.frequencies;
|
rmeddis@0
|
581 amplitudesdB=stimComponents.amplitudesdB;
|
rmeddis@0
|
582 nFrequencies=length(frequencies);
|
rmeddis@0
|
583
|
rmeddis@0
|
584 dt=globalStimParams.dt;
|
rmeddis@0
|
585 toneDuration=.010;
|
rmeddis@0
|
586 time=dt:dt:toneDuration;
|
rmeddis@0
|
587
|
rmeddis@0
|
588 rampDuration=stimComponents.rampOnDur;
|
rmeddis@0
|
589 rampTime=dt:dt:rampDuration;
|
rmeddis@0
|
590 ramp=[0.5*(1+cos(2*pi*rampTime/(2*rampDuration)+pi)) ones(1,length(time)-length(rampTime))];
|
rmeddis@0
|
591 ramp=ramp.*fliplr(ramp);
|
rmeddis@0
|
592
|
rmeddis@0
|
593 stimulus= zeros(1,round((toneDuration+globalStimParams.beginSilences(end))/dt)+1);
|
rmeddis@0
|
594
|
rmeddis@0
|
595 for i=1:nFrequencies
|
rmeddis@0
|
596 toneBeginPTR=round(globalStimParams.beginSilences(i)/dt)+1;
|
rmeddis@0
|
597
|
rmeddis@0
|
598 frequency=frequencies(i);
|
rmeddis@0
|
599 dBSPL=amplitudesdB(i);
|
rmeddis@0
|
600 amplitude=28e-6* 10.^(dBSPL/20);
|
rmeddis@0
|
601 tone=amplitude*sin(2*pi*frequency*time);
|
rmeddis@0
|
602 tone=tone.*ramp;
|
rmeddis@0
|
603 stimulus(toneBeginPTR:toneBeginPTR+length(tone)-1)=stimulus(toneBeginPTR:toneBeginPTR+length(tone)-1)+tone;
|
rmeddis@0
|
604 end
|
rmeddis@0
|
605 % figure(2), plot( stimulus')
|
rmeddis@0
|
606
|
rmeddis@0
|
607
|
rmeddis@0
|
608 %---------------------------------------------------- makeFMtone
|
rmeddis@0
|
609 function tone=makeFMtone(globalStimParams, stimComponents)
|
rmeddis@0
|
610 % maketone generates a stimComponents tone
|
rmeddis@0
|
611 % tone=maketone(dt, frequencies, toneDuration, dBSPL, phases)
|
rmeddis@0
|
612 % tone is returned in Pascals
|
rmeddis@0
|
613 % frequencies is a list of frequencies
|
rmeddis@0
|
614 % phase is list of phases or 'sin', 'cos', 'alt'
|
rmeddis@0
|
615 %
|
rmeddis@0
|
616 % defaults:
|
rmeddis@0
|
617 % phase = sin
|
rmeddis@0
|
618 % dBSPL=56 dB SPL
|
rmeddis@0
|
619
|
rmeddis@0
|
620 dt=globalStimParams.dt;
|
rmeddis@0
|
621 frequencies=stimComponents.frequencies;
|
rmeddis@0
|
622 toneDuration=stimComponents.toneDuration;
|
rmeddis@0
|
623 dBSPL=stimComponents.amplitudesdB;
|
rmeddis@0
|
624 phases=stimComponents.phases;
|
rmeddis@0
|
625 fmDepth=stimComponents.fmDepth;
|
rmeddis@0
|
626 fmFrequency=stimComponents.fmFrequency;
|
rmeddis@0
|
627
|
rmeddis@0
|
628 if ischar(phases)
|
rmeddis@0
|
629 switch phases
|
rmeddis@0
|
630 case 'sin'
|
rmeddis@0
|
631 phases= zeros(1,length(frequencies));
|
rmeddis@0
|
632 case 'cos'
|
rmeddis@0
|
633 phases= pi/2*ones(1,length(frequencies));
|
rmeddis@0
|
634 case 'alt'
|
rmeddis@0
|
635 phases= repmat([0 pi/2], 1, floor(length(frequencies)/2));
|
rmeddis@0
|
636 if length(phases)<length(frequencies)
|
rmeddis@0
|
637 phases=[phases 0];
|
rmeddis@0
|
638 end
|
rmeddis@0
|
639 end
|
rmeddis@0
|
640 end
|
rmeddis@0
|
641
|
rmeddis@0
|
642 if length(phases)==1, phases=repmat(phases(1), 1, length(frequencies)); end
|
rmeddis@0
|
643 if length(phases)<length(frequencies)
|
rmeddis@0
|
644 error('makeTone:phase specification must have the same length as frequencies')
|
rmeddis@0
|
645 end
|
rmeddis@0
|
646
|
rmeddis@0
|
647 if length(dBSPL)==1, dBSPL=repmat(dBSPL(1), 1, length(frequencies)); end
|
rmeddis@0
|
648 if length(dBSPL)<length(dBSPL)
|
rmeddis@0
|
649 error('makeTone:dBSPL specification must have the same length as frequencies')
|
rmeddis@0
|
650 end
|
rmeddis@0
|
651
|
rmeddis@0
|
652 time=dt:dt:toneDuration;
|
rmeddis@0
|
653 amplitudes=28e-6* 10.^(dBSPL/20);
|
rmeddis@0
|
654
|
rmeddis@0
|
655 tone=zeros(size(time));
|
rmeddis@0
|
656 for i=1:length(frequencies)
|
rmeddis@0
|
657 frequency=frequencies(i);
|
rmeddis@0
|
658 phase=phases(i);
|
rmeddis@0
|
659 tone=tone+amplitudes(i)*sin(2*pi*frequency*time+phase + fmDepth*sin(2*pi*fmFrequency*time));
|
rmeddis@0
|
660 end
|
rmeddis@0
|
661
|
rmeddis@0
|
662 % figure(1), plot(time, signal)
|
rmeddis@0
|
663
|
rmeddis@0
|
664 %----------------------------------------------------makeTransposedStimulus
|
rmeddis@0
|
665 function [transposedStimulus]=makeTransposedStimulus(globalStimParams, stim)
|
rmeddis@0
|
666 % globalStimParams.FS=100000;
|
rmeddis@0
|
667 % globalStimParams.overallDuration=.1; % s
|
rmeddis@0
|
668 % globalStimParams.doPlot=1;
|
rmeddis@0
|
669 %
|
rmeddis@0
|
670 % stim.type='transposedStimulus';
|
rmeddis@0
|
671 % stim.phases='sin';
|
rmeddis@0
|
672 % stim.toneDuration=.05;;
|
rmeddis@0
|
673 % stim.frequencies=500;
|
rmeddis@0
|
674 % stim.amplitudesdB=50;
|
rmeddis@0
|
675 % stim.beginSilence=.01;
|
rmeddis@0
|
676 % stim.endSilence=-1;
|
rmeddis@0
|
677 % stim.rampOnDur=.002;
|
rmeddis@0
|
678 % stim.rampOffDur=-1;
|
rmeddis@0
|
679 %
|
rmeddis@0
|
680 % stim.transCarrierFreq=4000;
|
rmeddis@0
|
681 % stim.transModFreq=100;
|
rmeddis@0
|
682
|
rmeddis@0
|
683 transposedStimulus=[];
|
rmeddis@0
|
684 % make envelope of transposed tone
|
rmeddis@0
|
685 for i=1:length(stim.transModFreq)
|
rmeddis@0
|
686 stim.type='tone';
|
rmeddis@0
|
687 stim.frequencies=stim.transModFreq(i);
|
rmeddis@0
|
688 stim.endsilence=-1; stim.beginSilence=0;
|
rmeddis@0
|
689 [envelope, msg]=stimulusCreate(globalStimParams, stim); % NB recursive
|
rmeddis@0
|
690 envelope=envelope(:,1); % mono
|
rmeddis@0
|
691 % HW rectify
|
rmeddis@0
|
692 envelope(find(envelope<0))=0;
|
rmeddis@0
|
693 % LP filter
|
rmeddis@0
|
694 maxEnvelope=max(envelope);
|
rmeddis@0
|
695 envelope=UTIL_Butterworth (envelope, globalStimParams.dt, 10, .2*stim.transModFreq(i), 2);
|
rmeddis@0
|
696 envelope=envelope*maxEnvelope/max(envelope);
|
rmeddis@0
|
697
|
rmeddis@0
|
698 % make the carrier
|
rmeddis@0
|
699 stim.frequencies=stim.transCarrierFreq(i);
|
rmeddis@0
|
700 stim.endsilence=-1; stim.beginSilence=0;
|
rmeddis@0
|
701 [audio, msg]=stimulusCreate(globalStimParams, stim);
|
rmeddis@0
|
702 carrier=audio(:,1);
|
rmeddis@0
|
703 x= (carrier.*envelope)';
|
rmeddis@0
|
704 % base amplitude on peak of unmodulated carrier
|
rmeddis@0
|
705 x=x/max(carrier);
|
rmeddis@0
|
706 transposedStimulus=[transposedStimulus; x];
|
rmeddis@0
|
707 end
|
rmeddis@0
|
708 transposedStimulus=sum(transposedStimulus,1);
|
rmeddis@0
|
709 % clf,plot(transposedStimulus)
|
rmeddis@0
|
710
|
rmeddis@0
|
711 %--------------------------------------------------------------------makeClicks
|
rmeddis@0
|
712 function clickTrain=makeClicks(globalStimParams, stimComponents)
|
rmeddis@0
|
713 % makeClicks(F0, clickTimes, duration, FS);
|
rmeddis@0
|
714 % F0 specifies the repetition rate of the click sequence
|
rmeddis@0
|
715 % If F0=-1, a single click is generated at the start of the duration of the signal
|
rmeddis@0
|
716 %
|
rmeddis@0
|
717 % clickTimes a are fractions of the period
|
rmeddis@0
|
718 % and specify when the click appears in the period
|
rmeddis@0
|
719 % A click time of 1 is reset to zero.
|
rmeddis@0
|
720 % if the clicktime plus the click width is greater than the period, no click is generated
|
rmeddis@0
|
721 % clicks are treated as 20 microPascal high before amplification
|
rmeddis@0
|
722 % unless otherwise specified in stimComponents.clickHeight
|
rmeddis@0
|
723 % click width is dt unless specified in stimComponents.clickWidth
|
rmeddis@0
|
724 %
|
rmeddis@0
|
725 % for regular pulse train set clicktimes=1 or zero;
|
rmeddis@0
|
726 % FS is the sampling interval;
|
rmeddis@0
|
727 % CarlyonClickTrain(100, [.4 1], 40, 441000);
|
rmeddis@0
|
728
|
rmeddis@0
|
729 FS=globalStimParams.FS; % sample rate
|
rmeddis@0
|
730 dt=1/FS;
|
rmeddis@0
|
731
|
rmeddis@0
|
732 try,clickWidth=stimComponents.clickWidth;catch, clickWidth=dt; end
|
rmeddis@0
|
733 try,clickHeight=stimComponents.clickHeight; catch, clickHeight=28e-6 * 10^(stimComponents.amplitudesdB/20); end
|
rmeddis@0
|
734 try, clickTimes=stimComponents.clickTimes; catch, clickTimes=1; end
|
rmeddis@0
|
735
|
rmeddis@0
|
736 % clickTimes are the times in a cycle that the click
|
rmeddis@0
|
737 % occurs
|
rmeddis@0
|
738 checkClickTimes=clickTimes-1;
|
rmeddis@0
|
739 if max(checkClickTimes)>1
|
rmeddis@0
|
740 msg= 'click times must be <= than 1 (period)';
|
rmeddis@0
|
741 return
|
rmeddis@0
|
742 end
|
rmeddis@0
|
743
|
rmeddis@0
|
744 if clickWidth>=1/stimComponents.clickRepeatFrequency
|
rmeddis@0
|
745 msg= 'click width is too great for frequency';
|
rmeddis@0
|
746 return
|
rmeddis@0
|
747 end
|
rmeddis@0
|
748
|
rmeddis@0
|
749 duration=stimComponents.toneDuration;
|
rmeddis@0
|
750 totalLength=round(stimComponents.toneDuration/dt);
|
rmeddis@0
|
751 F0=stimComponents.clickRepeatFrequency;
|
rmeddis@0
|
752 F0=round(F0/dt)*dt;
|
rmeddis@0
|
753 if F0==-1 % single click required
|
rmeddis@0
|
754 F0=1/duration;
|
rmeddis@0
|
755 repetitions=1;
|
rmeddis@0
|
756 clickStartTimes=1; %clicktimes are fractions of a period
|
rmeddis@0
|
757 else
|
rmeddis@0
|
758 repetitions=round(F0*duration)-1;
|
rmeddis@0
|
759 duration=repetitions/F0;
|
rmeddis@0
|
760 clickStartTimes=clickTimes;
|
rmeddis@0
|
761 end
|
rmeddis@0
|
762 % if a clickTime=1 (end of duty period) set it to the beginning
|
rmeddis@0
|
763 clickStartTimes(clickStartTimes==1)=0;
|
rmeddis@0
|
764
|
rmeddis@0
|
765 period=1/F0;
|
rmeddis@0
|
766 time=dt:dt:period;
|
rmeddis@0
|
767 nPoints=length(time);
|
rmeddis@0
|
768 signal=zeros(1,nPoints);
|
rmeddis@0
|
769 dBSPL=stimComponents.amplitudesdB;
|
rmeddis@0
|
770
|
rmeddis@0
|
771 % compute click train for a single cycle
|
rmeddis@0
|
772 clickWidthNpoints=round(clickWidth*FS);
|
rmeddis@0
|
773 for i=1:length(clickStartTimes)
|
rmeddis@0
|
774 % if clickStartTimes(i)<clickWidth
|
rmeddis@0
|
775 % clickStartTimes(i)=dt;
|
rmeddis@0
|
776 % end
|
rmeddis@0
|
777 clickTime=round(period*clickStartTimes(i)/dt -dt);
|
rmeddis@0
|
778 % clicks are treated as 20 microPascal high
|
rmeddis@0
|
779 if clickTime+clickWidthNpoints<length(signal)
|
rmeddis@0
|
780 signal(clickTime+1:clickTime+clickWidthNpoints)=clickHeight;
|
rmeddis@0
|
781 end
|
rmeddis@0
|
782 end
|
rmeddis@0
|
783
|
rmeddis@0
|
784 clickTrain=repmat(signal, 1, repetitions);
|
rmeddis@0
|
785
|
rmeddis@0
|
786 if length(clickTrain)>totalLength
|
rmeddis@0
|
787 clickTrain=clickTrain(1:totalLength);
|
rmeddis@0
|
788 elseif length(clickTrain)<totalLength
|
rmeddis@0
|
789 timeToAdd=zeros(1,round((totalLength-length(clickTrain))));
|
rmeddis@0
|
790 clickTrain=[clickTrain timeToAdd];
|
rmeddis@0
|
791 % figure (1), plot(clickTrain)
|
rmeddis@0
|
792 end
|
rmeddis@0
|
793
|
rmeddis@0
|
794 %----------------------------------------------------------------makePressnitzerClicks
|
rmeddis@0
|
795 function signal=makePressnitzerClicks(globalStimParams, stimComponents)
|
rmeddis@0
|
796 % PressnitzerClicks(k,duration,dt)
|
rmeddis@0
|
797 % Generates a sequence of clicks with intervals kxx
|
rmeddis@0
|
798 % where x= rand*k/2
|
rmeddis@0
|
799 % This is not the same as Kaernbach and Demany clicks
|
rmeddis@0
|
800
|
rmeddis@0
|
801 FS=globalStimParams.FS; % sample rate
|
rmeddis@0
|
802 dt=1/FS;
|
rmeddis@0
|
803
|
rmeddis@0
|
804 % obligatory parameter
|
rmeddis@0
|
805 try
|
rmeddis@0
|
806 k=stimComponents.k;
|
rmeddis@0
|
807 catch
|
rmeddis@0
|
808 error('PressnitzerClicks: field ''k'' is missing from stimcomponent')
|
rmeddis@0
|
809 end
|
rmeddis@0
|
810
|
rmeddis@0
|
811 % optional parameters
|
rmeddis@0
|
812 if isfield(stimComponents,'clickWidth')
|
rmeddis@0
|
813 clickWidth=stimComponents.clickWidth;
|
rmeddis@0
|
814 else
|
rmeddis@0
|
815 clickWidth=dt;
|
rmeddis@0
|
816 end
|
rmeddis@0
|
817 clickWidthNpoints=round(clickWidth*FS);
|
rmeddis@0
|
818
|
rmeddis@0
|
819 if isfield(stimComponents,'clickHeight')
|
rmeddis@0
|
820 clickHeight=stimComponents.clickHeight;
|
rmeddis@0
|
821 else
|
rmeddis@0
|
822 clickHeight=28e-6 * 10^(stimComponents.amplitudesdB/20);
|
rmeddis@0
|
823 end
|
rmeddis@0
|
824
|
rmeddis@0
|
825 duration=stimComponents.toneDuration;
|
rmeddis@0
|
826
|
rmeddis@0
|
827 signalLength=round(duration/dt);
|
rmeddis@0
|
828 signal=zeros(1,signalLength);
|
rmeddis@0
|
829 kInterval=round(k/dt);
|
rmeddis@0
|
830 halfk=k/2;
|
rmeddis@0
|
831 signal(1)=clickHeight;
|
rmeddis@0
|
832 timeIdx=0;
|
rmeddis@0
|
833 while timeIdx<signalLength
|
rmeddis@0
|
834 % first interval = k
|
rmeddis@0
|
835 clickTime=timeIdx+kInterval;
|
rmeddis@0
|
836 signal(clickTime:clickTime+clickWidthNpoints)=clickHeight;
|
rmeddis@0
|
837 timeIdx=timeIdx+kInterval;
|
rmeddis@0
|
838
|
rmeddis@0
|
839 % second interval = 0 : k/2
|
rmeddis@0
|
840 intervalx1=round(rand*halfk/dt);
|
rmeddis@0
|
841 clickTime=timeIdx+intervalx1;
|
rmeddis@0
|
842 signal(clickTime:clickTime+clickWidthNpoints)=clickHeight;
|
rmeddis@0
|
843 timeIdx=timeIdx+intervalx1;
|
rmeddis@0
|
844
|
rmeddis@0
|
845 % third interval = 0 : k/2
|
rmeddis@0
|
846 intervalx1=round(rand*halfk/dt);
|
rmeddis@0
|
847 clickTime=timeIdx+intervalx1;
|
rmeddis@0
|
848 signal(clickTime:clickTime+clickWidthNpoints)=clickHeight;
|
rmeddis@0
|
849 timeIdx=timeIdx+intervalx1;
|
rmeddis@0
|
850 end
|
rmeddis@0
|
851
|
rmeddis@0
|
852 signal=signal(1:signalLength);
|
rmeddis@0
|
853 % figure(1), plot(dt:dt:duration,signal)
|
rmeddis@0
|
854
|
rmeddis@0
|
855 %----------------------------------------------------------------makePressnitzerABXClicks
|
rmeddis@0
|
856 function signal=makePressnitzerABxClicks(globalStimParams, stimComponents)
|
rmeddis@0
|
857 % Generates a sequence of clicks with intervals ABx
|
rmeddis@0
|
858 % AB interval is 2*k
|
rmeddis@0
|
859 % where A= rand* k
|
rmeddis@0
|
860 % B= k-A
|
rmeddis@0
|
861 % x= k/2
|
rmeddis@0
|
862 % These are second order clicks
|
rmeddis@0
|
863
|
rmeddis@0
|
864 FS=globalStimParams.FS; % sample rate
|
rmeddis@0
|
865 dt=1/FS;
|
rmeddis@0
|
866
|
rmeddis@0
|
867 % obligatory parameter
|
rmeddis@0
|
868 try
|
rmeddis@0
|
869 k=stimComponents.k;
|
rmeddis@0
|
870 catch
|
rmeddis@0
|
871 error('PressnitzerClicks: field ''k'' is missing from stimcomponent')
|
rmeddis@0
|
872 end
|
rmeddis@0
|
873
|
rmeddis@0
|
874 % optional parameters
|
rmeddis@0
|
875 if isfield(stimComponents,'clickWidth')
|
rmeddis@0
|
876 clickWidth=stimComponents.clickWidth;
|
rmeddis@0
|
877 else
|
rmeddis@0
|
878 clickWidth=dt;
|
rmeddis@0
|
879 end
|
rmeddis@0
|
880 clickWidthNpoints=round(clickWidth*FS);
|
rmeddis@0
|
881
|
rmeddis@0
|
882 if isfield(stimComponents,'clickHeight')
|
rmeddis@0
|
883 clickHeight=stimComponents.clickHeight;
|
rmeddis@0
|
884 else
|
rmeddis@0
|
885 clickHeight=28e-6 * 10^(stimComponents.amplitudesdB/20);
|
rmeddis@0
|
886 end
|
rmeddis@0
|
887
|
rmeddis@0
|
888 duration=stimComponents.toneDuration;
|
rmeddis@0
|
889
|
rmeddis@0
|
890 signalLength=round(duration/dt);
|
rmeddis@0
|
891 signal=zeros(1,2*signalLength); % allow for overrun
|
rmeddis@0
|
892 ABinterval=k/dt; % i.e. the number of dt steps
|
rmeddis@0
|
893 randomInterval=ABinterval/2;
|
rmeddis@0
|
894 signal(1)=clickHeight;
|
rmeddis@0
|
895 time=0;
|
rmeddis@0
|
896 while time<signalLength
|
rmeddis@0
|
897 % first interval = A
|
rmeddis@0
|
898 intervalA=rand*ABinterval;
|
rmeddis@0
|
899 clickTime=round(time+intervalA)+1; % +1 avoids zero index
|
rmeddis@0
|
900 signal(clickTime:clickTime+clickWidthNpoints)=clickHeight;
|
rmeddis@0
|
901 time=time+intervalA;
|
rmeddis@0
|
902
|
rmeddis@0
|
903 % second interval = B
|
rmeddis@0
|
904 intervalB=ABinterval-intervalA;
|
rmeddis@0
|
905 clickTime=round(time+intervalB)+1;
|
rmeddis@0
|
906 signal(clickTime:clickTime+clickWidthNpoints-1)=clickHeight;
|
rmeddis@0
|
907 time=time+intervalB;
|
rmeddis@0
|
908
|
rmeddis@0
|
909 % third interval = 0 : k/2
|
rmeddis@0
|
910 intervalx1=rand*randomInterval; % mean random interval=k
|
rmeddis@0
|
911 clickTime=round(time+intervalx1)+1;
|
rmeddis@0
|
912 signal(clickTime:clickTime+clickWidthNpoints-1)=clickHeight;
|
rmeddis@0
|
913 time=time+intervalx1;
|
rmeddis@0
|
914 end
|
rmeddis@0
|
915
|
rmeddis@0
|
916 signal=signal(1:signalLength);
|
rmeddis@0
|
917 % figure(1), plot(dt:dt:duration,signal)
|
rmeddis@0
|
918
|
rmeddis@0
|
919 %-----------------------------------------------------makeABxClicks
|
rmeddis@0
|
920 function signal=makeABxClicks(globalStimParams, stimComponents)
|
rmeddis@0
|
921 % Generates a sequence of clicks with intervals ABx
|
rmeddis@0
|
922 % AB interval is 2*k
|
rmeddis@0
|
923 % where A= rand* k
|
rmeddis@0
|
924 % B= k-A
|
rmeddis@0
|
925 % x= rand*2*k
|
rmeddis@0
|
926 % These are second order clicks
|
rmeddis@0
|
927
|
rmeddis@0
|
928 FS=globalStimParams.FS; % sample rate
|
rmeddis@0
|
929 dt=1/FS;
|
rmeddis@0
|
930
|
rmeddis@0
|
931 % obligatory parameter
|
rmeddis@0
|
932 try
|
rmeddis@0
|
933 k=stimComponents.k;
|
rmeddis@0
|
934 catch
|
rmeddis@0
|
935 error('PressnitzerClicks: field ''k'' is missing from stimcomponent')
|
rmeddis@0
|
936 end
|
rmeddis@0
|
937
|
rmeddis@0
|
938 % optional parameters
|
rmeddis@0
|
939 if isfield(stimComponents,'clickWidth')
|
rmeddis@0
|
940 clickWidth=stimComponents.clickWidth;
|
rmeddis@0
|
941 else
|
rmeddis@0
|
942 clickWidth=dt;
|
rmeddis@0
|
943 end
|
rmeddis@0
|
944 clickWidthNpoints=round(clickWidth*FS);
|
rmeddis@0
|
945
|
rmeddis@0
|
946 if isfield(stimComponents,'clickHeight')
|
rmeddis@0
|
947 clickHeight=stimComponents.clickHeight;
|
rmeddis@0
|
948 else
|
rmeddis@0
|
949 clickHeight=28e-6 * 10^(stimComponents.amplitudesdB/20);
|
rmeddis@0
|
950 end
|
rmeddis@0
|
951
|
rmeddis@0
|
952 duration=stimComponents.toneDuration;
|
rmeddis@0
|
953
|
rmeddis@0
|
954 signalLength=round(duration/dt);
|
rmeddis@0
|
955 signal=zeros(1,2*signalLength); % allow for overrun
|
rmeddis@0
|
956 ABinterval=2*k/dt;
|
rmeddis@0
|
957 randomInterval=ABinterval;
|
rmeddis@0
|
958 signal(1)=clickHeight;
|
rmeddis@0
|
959 timeIdx=0;
|
rmeddis@0
|
960 while timeIdx<signalLength
|
rmeddis@0
|
961 % first interval = A
|
rmeddis@0
|
962 intervalA=round(rand*ABinterval);
|
rmeddis@0
|
963 clickTime=timeIdx+intervalA+1;
|
rmeddis@0
|
964 signal(clickTime:clickTime+clickWidthNpoints-1)=clickHeight;
|
rmeddis@0
|
965 timeIdx=timeIdx+intervalA;
|
rmeddis@0
|
966
|
rmeddis@0
|
967 % second interval = B
|
rmeddis@0
|
968 intervalB=round(ABinterval-intervalA);
|
rmeddis@0
|
969 clickTime=timeIdx+intervalB;
|
rmeddis@0
|
970 signal(clickTime:clickTime+clickWidthNpoints-1)=clickHeight;
|
rmeddis@0
|
971 timeIdx=timeIdx+intervalB;
|
rmeddis@0
|
972
|
rmeddis@0
|
973 % third interval = 0 : k
|
rmeddis@0
|
974 intervalx1=round(rand*randomInterval); % mean random interval=k
|
rmeddis@0
|
975 clickTime=timeIdx+intervalx1;
|
rmeddis@0
|
976 signal(clickTime:clickTime+clickWidthNpoints-1)=clickHeight;
|
rmeddis@0
|
977 timeIdx=timeIdx+intervalx1;
|
rmeddis@0
|
978 end
|
rmeddis@0
|
979
|
rmeddis@0
|
980 signal=signal(1:signalLength);
|
rmeddis@0
|
981 % figure(1), plot(dt:dt:duration,signal)
|
rmeddis@0
|
982
|
rmeddis@0
|
983 %----------------------------------------------------------------makeYostClicks
|
rmeddis@0
|
984 function signal=makeYostClicks(globalStimParams, stimComponents)
|
rmeddis@0
|
985 % Generates a shuffled sequence of clicks with intervals kxxxx
|
rmeddis@0
|
986 % where max(x)= 2*k
|
rmeddis@0
|
987 % and there are n occurrences of x
|
rmeddis@0
|
988 % this section requires:
|
rmeddis@0
|
989 % stimComponents.k
|
rmeddis@0
|
990 % stimComponents.nXs
|
rmeddis@0
|
991 % stimComponents.toneDuration
|
rmeddis@0
|
992 % optional:
|
rmeddis@0
|
993 % stimComponents.clickWidth %useful because width depends on dt
|
rmeddis@0
|
994 % stimComponents.clickHeight %best left to amplitude rescaling later
|
rmeddis@0
|
995
|
rmeddis@0
|
996 FS=globalStimParams.FS; % sample rate
|
rmeddis@0
|
997 dt=1/FS;
|
rmeddis@0
|
998
|
rmeddis@0
|
999 % obligatory parameters
|
rmeddis@0
|
1000 try
|
rmeddis@0
|
1001 k=stimComponents.k;
|
rmeddis@0
|
1002 catch
|
rmeddis@0
|
1003 error('makeYostClicks: field ''k'' is missing from stimComponents')
|
rmeddis@0
|
1004 end
|
rmeddis@0
|
1005
|
rmeddis@0
|
1006 try
|
rmeddis@0
|
1007 nXs=stimComponents.nXs;
|
rmeddis@0
|
1008 catch
|
rmeddis@0
|
1009 error('makeYostClicks: field ''nXs'' is missing from stimComponents')
|
rmeddis@0
|
1010 end
|
rmeddis@0
|
1011
|
rmeddis@0
|
1012 try
|
rmeddis@0
|
1013 shuffled=stimComponents.shuffled;
|
rmeddis@0
|
1014 catch
|
rmeddis@0
|
1015 error('makeYostClicks: field ''shuffled'' is missing from stimComponents')
|
rmeddis@0
|
1016 end
|
rmeddis@0
|
1017
|
rmeddis@0
|
1018 try
|
rmeddis@0
|
1019 duration=stimComponents.toneDuration;
|
rmeddis@0
|
1020 catch
|
rmeddis@0
|
1021 error('makeYostClicks: field ''toneDuration'' is missing from stimComponents')
|
rmeddis@0
|
1022 end
|
rmeddis@0
|
1023
|
rmeddis@0
|
1024 % optional parameters
|
rmeddis@0
|
1025 if isfield(stimComponents,'clickWidth')
|
rmeddis@0
|
1026 clickWidth=stimComponents.clickWidth;
|
rmeddis@0
|
1027 else
|
rmeddis@0
|
1028 clickWidth=dt;
|
rmeddis@0
|
1029 end
|
rmeddis@0
|
1030 clickWidthNpoints=round(clickWidth*FS);
|
rmeddis@0
|
1031
|
rmeddis@0
|
1032 if isfield(stimComponents,'clickHeight')
|
rmeddis@0
|
1033 clickHeight=stimComponents.clickHeight;
|
rmeddis@0
|
1034 else
|
rmeddis@0
|
1035 clickHeight=28e-6 * 10^(stimComponents.amplitudesdB/20);
|
rmeddis@0
|
1036 end
|
rmeddis@0
|
1037
|
rmeddis@0
|
1038 kInterval=round(k/dt);
|
rmeddis@0
|
1039 twoK=k*2; % max width of x interval
|
rmeddis@0
|
1040
|
rmeddis@0
|
1041 signalLength=round(duration/dt);
|
rmeddis@0
|
1042 signal=zeros(1,signalLength);
|
rmeddis@0
|
1043
|
rmeddis@0
|
1044 timeIdx=0;
|
rmeddis@0
|
1045 intervalCount=0;
|
rmeddis@0
|
1046 while timeIdx<signalLength
|
rmeddis@0
|
1047 timeIdx=timeIdx+kInterval;
|
rmeddis@0
|
1048 if timeIdx>signalLength, break,end
|
rmeddis@0
|
1049 intervalCount=intervalCount+1;
|
rmeddis@0
|
1050 intervals(intervalCount)=kInterval;
|
rmeddis@0
|
1051
|
rmeddis@0
|
1052 % repeat x intervals as required
|
rmeddis@0
|
1053 if nXs>0
|
rmeddis@0
|
1054 for nX=1:nXs
|
rmeddis@0
|
1055 xInterval=round(rand*twoK/dt);
|
rmeddis@0
|
1056 timeIdx=timeIdx+xInterval;
|
rmeddis@0
|
1057 if timeIdx>signalLength, break,end
|
rmeddis@0
|
1058 intervalCount=intervalCount+1;
|
rmeddis@0
|
1059 intervals(intervalCount)=xInterval;
|
rmeddis@0
|
1060 end
|
rmeddis@0
|
1061 end
|
rmeddis@0
|
1062 if timeIdx>signalLength, break,end
|
rmeddis@0
|
1063 end
|
rmeddis@0
|
1064
|
rmeddis@0
|
1065 % shuffle intervals
|
rmeddis@0
|
1066 if shuffled
|
rmeddis@0
|
1067 randomNumbers=rand(1,length(intervals));
|
rmeddis@0
|
1068 [randomNumbers idx]=sort(randomNumbers);
|
rmeddis@0
|
1069 intervals=intervals(idx);
|
rmeddis@0
|
1070 idx=intervals>0;
|
rmeddis@0
|
1071 intervals=intervals(idx);
|
rmeddis@0
|
1072 end
|
rmeddis@0
|
1073
|
rmeddis@0
|
1074 intervalCount=length(intervals);
|
rmeddis@0
|
1075 signal(1)=clickHeight;
|
rmeddis@0
|
1076 clickTime=0;
|
rmeddis@0
|
1077 for i=1:intervalCount
|
rmeddis@0
|
1078 clickTime=clickTime+intervals(i);
|
rmeddis@0
|
1079 signal(clickTime:clickTime+clickWidthNpoints)=clickHeight;
|
rmeddis@0
|
1080 end
|
rmeddis@0
|
1081 signal=signal(1:signalLength);
|
rmeddis@0
|
1082 % figure(1), plot(dt:dt:duration,signal)
|
rmeddis@0
|
1083
|
rmeddis@0
|
1084 %--------------------------------------------------------------------makeKxxClicks
|
rmeddis@0
|
1085 function signal=makeKxxClicks(globalStimParams, stimComponents)
|
rmeddis@0
|
1086 % Click train consists of kkxxx.. sequences
|
rmeddis@0
|
1087 % k is the duration of a fixed interval (seconds)
|
rmeddis@0
|
1088 % random intervals are distributed 0 : 2* k (NB not like Pressnitzer clicks)
|
rmeddis@0
|
1089 % nKs is the number of successive 'k' intervals
|
rmeddis@0
|
1090 % nXs is the number of random intervals between k sequences
|
rmeddis@0
|
1091 % sequences of 3 x intervals > k are replaced with new sequences
|
rmeddis@0
|
1092 % shuffled causes all intervals to be reordered randomly
|
rmeddis@0
|
1093 % NB 1/k is the mean click rate
|
rmeddis@0
|
1094
|
rmeddis@0
|
1095 FS=globalStimParams.FS; % sample rate
|
rmeddis@0
|
1096 dt=1/FS;
|
rmeddis@0
|
1097
|
rmeddis@0
|
1098 try
|
rmeddis@0
|
1099 k=stimComponents.k; % duration (s) of fixed intervals
|
rmeddis@0
|
1100 catch
|
rmeddis@0
|
1101 error('makeYostClicks: field ''k'' is missing from stimComponents')
|
rmeddis@0
|
1102 end
|
rmeddis@0
|
1103
|
rmeddis@0
|
1104 try
|
rmeddis@0
|
1105 duration=stimComponents.toneDuration;
|
rmeddis@0
|
1106 catch
|
rmeddis@0
|
1107 error('makeYostClicks: field ''duration'' is missing from stimComponents')
|
rmeddis@0
|
1108 end
|
rmeddis@0
|
1109
|
rmeddis@0
|
1110 if isfield(stimComponents,'clickWidth')
|
rmeddis@0
|
1111 clickWidth=stimComponents.clickWidth;
|
rmeddis@0
|
1112 else
|
rmeddis@0
|
1113 clickWidth=dt;
|
rmeddis@0
|
1114 end
|
rmeddis@0
|
1115
|
rmeddis@0
|
1116 if isfield(stimComponents,'clickHeight')
|
rmeddis@0
|
1117 clickHeight=stimComponents.clickHeight;
|
rmeddis@0
|
1118 else
|
rmeddis@0
|
1119 clickHeight=28e-6 * 10^(stimComponents.amplitudesdB/20);
|
rmeddis@0
|
1120 end
|
rmeddis@0
|
1121
|
rmeddis@0
|
1122
|
rmeddis@0
|
1123 if isfield(stimComponents,'order')
|
rmeddis@0
|
1124 order=stimComponents.order;
|
rmeddis@0
|
1125 else
|
rmeddis@0
|
1126 order=1;
|
rmeddis@0
|
1127 end
|
rmeddis@0
|
1128
|
rmeddis@0
|
1129 if isfield(stimComponents,'nKs')
|
rmeddis@0
|
1130 nKs=stimComponents.nKs;
|
rmeddis@0
|
1131 else
|
rmeddis@0
|
1132 nKs=1;
|
rmeddis@0
|
1133 end
|
rmeddis@0
|
1134
|
rmeddis@0
|
1135 if isfield(stimComponents,'nXs')
|
rmeddis@0
|
1136 nXs=stimComponents.nXs;
|
rmeddis@0
|
1137 else
|
rmeddis@0
|
1138 nXs=1;
|
rmeddis@0
|
1139 end
|
rmeddis@0
|
1140
|
rmeddis@0
|
1141 if isfield(stimComponents,'shuffled')
|
rmeddis@0
|
1142 shuffled=stimComponents.shuffled;
|
rmeddis@0
|
1143 else
|
rmeddis@0
|
1144 shuffled=1;
|
rmeddis@0
|
1145 end
|
rmeddis@0
|
1146
|
rmeddis@0
|
1147 kLength=round(k/dt); % fixed interval
|
rmeddis@0
|
1148 xLength=2*kLength; % maximum random interval
|
rmeddis@0
|
1149 requiredSignalLength=round(duration/dt);
|
rmeddis@0
|
1150 intervalsPerCycle=(nKs+nXs);
|
rmeddis@0
|
1151 cycleLength=nKs*kLength+nXs*xLength;
|
rmeddis@0
|
1152 % more cycles to allow for uncertainty
|
rmeddis@0
|
1153 nCycles=5*round(requiredSignalLength/cycleLength);
|
rmeddis@0
|
1154 nIntervals=nCycles*intervalsPerCycle;
|
rmeddis@0
|
1155
|
rmeddis@0
|
1156 % random intervals
|
rmeddis@0
|
1157 if nXs>0
|
rmeddis@0
|
1158 xIntervals=floor(rand(1,nIntervals)*2*kLength);
|
rmeddis@0
|
1159 % weed out triple intervals > 2*k
|
rmeddis@0
|
1160 rogues=1;
|
rmeddis@0
|
1161 while sum(rogues)
|
rmeddis@0
|
1162 y=(xIntervals>kLength);
|
rmeddis@0
|
1163 rogues=(sum([y(1:end-2)' y(2:end-1)' y(3:end)'],2)>2);
|
rmeddis@0
|
1164 xIntervals(rogues)=floor(rand*2*kLength);
|
rmeddis@0
|
1165 end
|
rmeddis@0
|
1166 xIntervals=reshape(xIntervals,nCycles,intervalsPerCycle);
|
rmeddis@0
|
1167 else
|
rmeddis@0
|
1168 xIntervals=[];
|
rmeddis@0
|
1169 end
|
rmeddis@0
|
1170
|
rmeddis@0
|
1171 % insert constrained (k) intervals
|
rmeddis@0
|
1172 if nKs>0
|
rmeddis@0
|
1173 switch order
|
rmeddis@0
|
1174 case 1
|
rmeddis@0
|
1175 kIntervals=floor(ones(nCycles,nKs)*kLength);
|
rmeddis@0
|
1176 case 2
|
rmeddis@0
|
1177 nKs=1; % force this
|
rmeddis@0
|
1178 kIntervals=floor(rand(nCycles,1)*kLength);
|
rmeddis@0
|
1179 kIntervals=[kIntervals kLength-kIntervals];
|
rmeddis@0
|
1180 end
|
rmeddis@0
|
1181 else
|
rmeddis@0
|
1182 kIntervals=[];
|
rmeddis@0
|
1183 end
|
rmeddis@0
|
1184
|
rmeddis@0
|
1185 % combine fixed and random
|
rmeddis@0
|
1186 intervals=[kIntervals xIntervals(:,nKs+1:end)];
|
rmeddis@0
|
1187 % make a single array;
|
rmeddis@0
|
1188 [r c]=size(intervals);
|
rmeddis@0
|
1189 intervals=reshape(intervals',1,r*c);
|
rmeddis@0
|
1190
|
rmeddis@0
|
1191 % shuffle intervals
|
rmeddis@0
|
1192 if shuffled
|
rmeddis@0
|
1193 randomNumbers=rand(1,length(intervals));
|
rmeddis@0
|
1194 [randomNumbers idx]=sort(randomNumbers);
|
rmeddis@0
|
1195 intervals=intervals(idx);
|
rmeddis@0
|
1196 idx=intervals>0;
|
rmeddis@0
|
1197 intervals=intervals(idx);
|
rmeddis@0
|
1198 end
|
rmeddis@0
|
1199
|
rmeddis@0
|
1200 % convert intervals to clicks
|
rmeddis@0
|
1201 clickTimes=cumsum(intervals);
|
rmeddis@0
|
1202 signal(clickTimes)=clickHeight;
|
rmeddis@0
|
1203 signal=signal(1:requiredSignalLength);
|
rmeddis@0
|
1204 % figure(1), clf, plot(signal)
|
rmeddis@0
|
1205
|
rmeddis@0
|
1206
|
rmeddis@0
|
1207
|
rmeddis@0
|
1208 %--------------------------------------------------------------------makeNoise
|
rmeddis@0
|
1209 function noise=makeNoise(globalStimParams, stimComponents)
|
rmeddis@0
|
1210 % FS in Hz, noiseDuration in s, delay in s;
|
rmeddis@0
|
1211 % noise is returned with overall level dB(rms) = amplitudesdB
|
rmeddis@0
|
1212 %
|
rmeddis@0
|
1213 % % You need
|
rmeddis@0
|
1214 %
|
rmeddis@0
|
1215 % stim.type='noise'; % or 'IRN', or 'pinkNoise'
|
rmeddis@0
|
1216 % stim.toneDuration=.05;
|
rmeddis@0
|
1217 % stim.amplitudesdB=50;
|
rmeddis@0
|
1218 % stim.beginSilence=.01;
|
rmeddis@0
|
1219 % stim.endSilence=-1;
|
rmeddis@0
|
1220 % stim.rampOnDur=.002;
|
rmeddis@0
|
1221 % stim.rampOffDur=-1;
|
rmeddis@0
|
1222 %
|
rmeddis@0
|
1223 % % Mandatory structure fields
|
rmeddis@0
|
1224 % globalStimParams.FS=100000;
|
rmeddis@0
|
1225 % globalStimParams.overallDuration=.1; % s
|
rmeddis@0
|
1226 % globalStimParams.doPlot=1;
|
rmeddis@0
|
1227 % globalStimParams.doPlay=1;
|
rmeddis@0
|
1228 %
|
rmeddis@0
|
1229 % [audio, msg]=stimulusCreate(globalStimParams, stim, );
|
rmeddis@0
|
1230 %
|
rmeddis@0
|
1231 % % local
|
rmeddis@0
|
1232 % stim.type='noise'; % or 'IRN'
|
rmeddis@0
|
1233 %
|
rmeddis@0
|
1234 FS=globalStimParams.FS;
|
rmeddis@0
|
1235 noiseDuration= stimComponents.toneDuration;
|
rmeddis@0
|
1236 npts=round(noiseDuration*FS);
|
rmeddis@0
|
1237 noise=randn(1,npts); % NB randn (normally distributed)
|
rmeddis@0
|
1238
|
rmeddis@0
|
1239 switch stimComponents.type
|
rmeddis@0
|
1240 case 'pinkNoise'
|
rmeddis@0
|
1241 % noise=UTIL_lowpassFilterFreq(noise, 100, 1/FS);
|
rmeddis@0
|
1242 noise=UTIL_bandPassFilter(noise, 1, 100, 200, 1/FS,[]);
|
rmeddis@0
|
1243 end
|
rmeddis@0
|
1244
|
rmeddis@0
|
1245 rms=(mean(noise.^2)^.5); %should be 20 microPascals for 0 dB SPL
|
rmeddis@0
|
1246 adjust=20e-6/rms;
|
rmeddis@0
|
1247 noise=noise*adjust;
|
rmeddis@0
|
1248 rms=(mean(noise.^2)^.5);
|
rmeddis@0
|
1249 amplitude=10.^(stimComponents.amplitudesdB/20);
|
rmeddis@0
|
1250 noise=amplitude*noise;
|
rmeddis@0
|
1251 % rms=(mean(noise.^2)^.5);
|
rmeddis@0
|
1252 % dBnoise=20*log10(rms/20e-6)
|
rmeddis@0
|
1253
|
rmeddis@0
|
1254
|
rmeddis@0
|
1255 %--------------------------------------------------------------------makeWhiteNoise
|
rmeddis@0
|
1256 function noise=makeWhiteNoise(globalStimParams, stimComponents)
|
rmeddis@0
|
1257 % FS in Hz, noiseDuration in s, delay in s;
|
rmeddis@0
|
1258 % noise is bandpass filtered between 100 and 10000 Hz
|
rmeddis@0
|
1259 % spectrum level (dB/Hz) is 40 dB below nominal level.
|
rmeddis@0
|
1260 % noise is returned with dB(rms) = amplitudesdB
|
rmeddis@0
|
1261 %
|
rmeddis@0
|
1262 % % You need
|
rmeddis@0
|
1263 %
|
rmeddis@0
|
1264 % stim.type='noise'; % or 'IRN', or 'pinkNoise'
|
rmeddis@0
|
1265 % stim.toneDuration=.05;
|
rmeddis@0
|
1266 % stim.amplitudesdB=50;
|
rmeddis@0
|
1267 % stim.beginSilence=.01;
|
rmeddis@0
|
1268 % stim.endSilence=-1;
|
rmeddis@0
|
1269 % stim.rampOnDur=.002;
|
rmeddis@0
|
1270 % stim.rampOffDur=-1;
|
rmeddis@0
|
1271 %
|
rmeddis@0
|
1272 % % Mandatory structure fields
|
rmeddis@0
|
1273 % globalStimParams.FS=100000;
|
rmeddis@0
|
1274 % globalStimParams.overallDuration=.1; % s
|
rmeddis@0
|
1275 % globalStimParams.doPlot=1;
|
rmeddis@0
|
1276 % globalStimParams.doPlay=1;
|
rmeddis@0
|
1277 %
|
rmeddis@0
|
1278 % [audio, msg]=stimulusCreate(globalStimParams, stim, );
|
rmeddis@0
|
1279 %
|
rmeddis@0
|
1280 % % local
|
rmeddis@0
|
1281 % stim.type='noise'; % or 'IRN'
|
rmeddis@0
|
1282 %
|
rmeddis@0
|
1283 FS=globalStimParams.FS;
|
rmeddis@0
|
1284 noiseDuration= stimComponents.toneDuration;
|
rmeddis@0
|
1285 npts=round(noiseDuration*FS);
|
rmeddis@0
|
1286 noise=randn(1,npts);
|
rmeddis@0
|
1287
|
rmeddis@0
|
1288 noise=UTIL_bandPassFilter (noise, 6, 100, 10000, 1/FS, []);
|
rmeddis@0
|
1289
|
rmeddis@0
|
1290 rms=(mean(noise.^2)^.5); %should be 20 microPascals for 0 dB SPL
|
rmeddis@0
|
1291 adjust=20e-6/rms;
|
rmeddis@0
|
1292 noise=noise*adjust;
|
rmeddis@0
|
1293 rms=(mean(noise.^2)^.5);
|
rmeddis@0
|
1294 amplitude=10.^(stimComponents.amplitudesdB/20);
|
rmeddis@0
|
1295 noise=amplitude*noise;
|
rmeddis@0
|
1296 % rms=(mean(noise.^2)^.5);
|
rmeddis@0
|
1297 % dBnoise=20*log10(rms/20e-6)
|
rmeddis@0
|
1298
|
rmeddis@0
|
1299
|
rmeddis@0
|
1300 %-----------------------------------------------------------------makeIRN
|
rmeddis@0
|
1301 function noise=makeIRN(globalStimParams, stimComponents)
|
rmeddis@0
|
1302 % FS in Hz, noiseDuration in s, delay in s;
|
rmeddis@0
|
1303 % noise is returned with dB(rms) = amplitudesdB
|
rmeddis@0
|
1304 %
|
rmeddis@0
|
1305 % % You need
|
rmeddis@0
|
1306 %
|
rmeddis@0
|
1307 % stim.type='noise'; % or 'IRN', or 'pinkNoise'
|
rmeddis@0
|
1308 % stim.toneDuration=.05;
|
rmeddis@0
|
1309 % stim.amplitudesdB=50;
|
rmeddis@0
|
1310 % stim.beginSilence=.01;
|
rmeddis@0
|
1311 % stim.endSilence=-1;
|
rmeddis@0
|
1312 % stim.rampOnDur=.002;
|
rmeddis@0
|
1313 % stim.rampOffDur=-1;
|
rmeddis@0
|
1314 %
|
rmeddis@0
|
1315 % % Mandatory structure fields
|
rmeddis@0
|
1316 % globalStimParams.FS=100000;
|
rmeddis@0
|
1317 % globalStimParams.overallDuration=.1; % s
|
rmeddis@0
|
1318 % globalStimParams.doPlot=1;
|
rmeddis@0
|
1319 % globalStimParams.doPlay=1;
|
rmeddis@0
|
1320 %
|
rmeddis@0
|
1321 % [audio, msg]=stimulusCreate(globalStimParams, stim, );
|
rmeddis@0
|
1322 %
|
rmeddis@0
|
1323 % % local
|
rmeddis@0
|
1324 % stim.type='noise'; % or 'IRN'
|
rmeddis@0
|
1325 % % for IRN only
|
rmeddis@0
|
1326 % stim.niterations = 8; %0 for white noise
|
rmeddis@0
|
1327 % stim.delay = 1/150;
|
rmeddis@0
|
1328 % stim.irnGain = 1;
|
rmeddis@0
|
1329 %
|
rmeddis@0
|
1330 FS=globalStimParams.FS;
|
rmeddis@0
|
1331 noiseDuration= stimComponents.toneDuration;
|
rmeddis@0
|
1332
|
rmeddis@0
|
1333 nIterations=stimComponents.niterations;
|
rmeddis@0
|
1334 if nIterations==0
|
rmeddis@0
|
1335 % white noise is specified as nIterations=1
|
rmeddis@0
|
1336 nIterations=1;
|
rmeddis@0
|
1337 IRNgain=0;
|
rmeddis@0
|
1338 delay=0.01; % dummy
|
rmeddis@0
|
1339 else
|
rmeddis@0
|
1340 % IRN
|
rmeddis@0
|
1341 delay=stimComponents.delay;
|
rmeddis@0
|
1342 IRNgain=stimComponents.irnGain;
|
rmeddis@0
|
1343 end
|
rmeddis@0
|
1344
|
rmeddis@0
|
1345 npts=round(noiseDuration*FS);
|
rmeddis@0
|
1346 dels=round(delay*FS);
|
rmeddis@0
|
1347 noise=randn(1,npts);
|
rmeddis@0
|
1348
|
rmeddis@0
|
1349 %fringe=nIterations*dels;
|
rmeddis@0
|
1350 %npts=npts+fringe;
|
rmeddis@0
|
1351
|
rmeddis@0
|
1352 for i=1:nIterations,
|
rmeddis@0
|
1353 dnoise=[noise(dels+1:npts) noise(1:dels)];
|
rmeddis@0
|
1354 dnoise=dnoise.*IRNgain;
|
rmeddis@0
|
1355 noise=noise+dnoise;
|
rmeddis@0
|
1356 end;
|
rmeddis@0
|
1357
|
rmeddis@0
|
1358 switch stimComponents.type
|
rmeddis@0
|
1359 case 'pinkNoise'
|
rmeddis@0
|
1360 noise=UTIL_lowpassFilterFreq(noise, 10000, 1/FS);
|
rmeddis@0
|
1361 end
|
rmeddis@0
|
1362
|
rmeddis@0
|
1363 rms=(mean(noise.^2)^.5); %should be 20 microPascals for 0 dB SPL
|
rmeddis@0
|
1364 adjust=20e-6/rms;
|
rmeddis@0
|
1365 noise=noise*adjust;
|
rmeddis@0
|
1366 rms=(mean(noise.^2)^.5);
|
rmeddis@0
|
1367 amplitude=10.^(stimComponents.amplitudesdB/20);
|
rmeddis@0
|
1368 noise=amplitude*noise;
|
rmeddis@0
|
1369 % rms=(mean(noise.^2)^.5);
|
rmeddis@0
|
1370 % dBnoise=20*log10(rms/20e-6)
|
rmeddis@0
|
1371
|
rmeddis@0
|
1372 %------------------------------------------------------------------ makeRPN
|
rmeddis@0
|
1373 function RPN=makeRPN(globalStimParams, stim)
|
rmeddis@0
|
1374 % 'period' is a collection of samples - AAABCD
|
rmeddis@0
|
1375 % you need
|
rmeddis@0
|
1376 %
|
rmeddis@0
|
1377 % stim.type='RPN';
|
rmeddis@0
|
1378 % stim.toneDuration=.2;
|
rmeddis@0
|
1379 % stim.amplitudesdB=50;
|
rmeddis@0
|
1380 % stim.beginSilence=.01;
|
rmeddis@0
|
1381 % stim.rampOnDur=.002;
|
rmeddis@0
|
1382 % stim.rampOffDur=-1;
|
rmeddis@0
|
1383 %
|
rmeddis@0
|
1384 % stim.sampleDuration=.005; %200 Hz pitch
|
rmeddis@0
|
1385 % stim.nSimilarSamples=5; % pitch strength
|
rmeddis@0
|
1386 % stim.nIndependentSamples=1% dilutes strength
|
rmeddis@0
|
1387 %
|
rmeddis@0
|
1388 % % Mandatory structure fields
|
rmeddis@0
|
1389 % globalStimParams.FS=44100;
|
rmeddis@0
|
1390 % globalStimParams.overallDuration=.21; % s
|
rmeddis@0
|
1391 %
|
rmeddis@0
|
1392 % globalStimParams.doPlot=1;
|
rmeddis@0
|
1393 % globalStimParams.doPlay=1;
|
rmeddis@0
|
1394 % [audio, msg]=stimulusCreate(globalStimParams, stim);
|
rmeddis@0
|
1395
|
rmeddis@0
|
1396 FS=globalStimParams.FS;
|
rmeddis@0
|
1397 ptsPerSample=floor(stim.sampleDuration*FS);
|
rmeddis@0
|
1398
|
rmeddis@0
|
1399 samplesPerPeriod=stim.nSimilarSamples+stim.nIndependentSamples;
|
rmeddis@0
|
1400 periodDuration=samplesPerPeriod*stim.sampleDuration;
|
rmeddis@0
|
1401
|
rmeddis@0
|
1402 totalNumPeriods=2*floor(stim.toneDuration/periodDuration); % longer than necessary
|
rmeddis@0
|
1403 if totalNumPeriods<1
|
rmeddis@0
|
1404 error('stimulusCreate: RPN, stimulus duration needs to be longer')
|
rmeddis@0
|
1405 end
|
rmeddis@0
|
1406
|
rmeddis@0
|
1407 RPN=[];
|
rmeddis@0
|
1408 for j=1:totalNumPeriods
|
rmeddis@0
|
1409 noise=randn(1,ptsPerSample);
|
rmeddis@0
|
1410 for i=1:stim.nSimilarSamples
|
rmeddis@0
|
1411 RPN=[RPN noise];
|
rmeddis@0
|
1412 end
|
rmeddis@0
|
1413
|
rmeddis@0
|
1414 for i=1:stim.nIndependentSamples
|
rmeddis@0
|
1415 noise=randn(1,ptsPerSample);
|
rmeddis@0
|
1416 RPN=[RPN noise];
|
rmeddis@0
|
1417 end
|
rmeddis@0
|
1418 end
|
rmeddis@0
|
1419
|
rmeddis@0
|
1420 targetStimulusLength=round(stim.toneDuration/FS);
|
rmeddis@0
|
1421 RPN=RPN(1:floor(stim.toneDuration*FS)); % take enough for stimulus
|
rmeddis@0
|
1422
|
rmeddis@0
|
1423 rms=(mean(RPN.^2)^.5); %should be 20 microPascals for 0 dB SPL
|
rmeddis@0
|
1424 adjust=20e-6/rms;
|
rmeddis@0
|
1425 RPN=RPN*adjust;
|
rmeddis@0
|
1426 rms=(mean(RPN.^2)^.5);
|
rmeddis@0
|
1427 amplitude=10.^(stim.amplitudesdB/20);
|
rmeddis@0
|
1428 RPN=amplitude*RPN;
|
rmeddis@0
|
1429 % rms=(mean(noise.^2)^.5);
|
rmeddis@0
|
1430 % dBnoise=20*log10(rms/20e-6)
|
rmeddis@0
|
1431
|
rmeddis@0
|
1432 %--------------------------------------------------------------------applyRampOn
|
rmeddis@0
|
1433 function signal=applyRampOn(signal, rampDur, rampOnTime, sampleRate)
|
rmeddis@0
|
1434 %applyRampOn applies raised cosine ramp
|
rmeddis@0
|
1435 %rampOntime is the time at which the ramp begins
|
rmeddis@0
|
1436 %At all other times the mask has a value of 1
|
rmeddis@0
|
1437 %signal=applyRampOn(signal, rampDur, rampOnTime, sampleRate)
|
rmeddis@0
|
1438
|
rmeddis@0
|
1439 rampDurPoints=round(rampDur*sampleRate);
|
rmeddis@0
|
1440 rampOn= (1+cos(pi:pi/(rampDurPoints-1): 2*pi))/2';
|
rmeddis@0
|
1441
|
rmeddis@0
|
1442 sigDurPoints=length(signal);
|
rmeddis@0
|
1443 mask(1:sigDurPoints)=1;
|
rmeddis@0
|
1444 rampOnStartIndex=round(rampOnTime*sampleRate+1);
|
rmeddis@0
|
1445 mask(rampOnStartIndex: rampOnStartIndex+ rampDurPoints-1)=rampOn;
|
rmeddis@0
|
1446 signal=signal.*mask;
|
rmeddis@0
|
1447 %plot(mask)
|
rmeddis@0
|
1448
|
rmeddis@0
|
1449 %--------------------------------------------------------------------applyRampOff
|
rmeddis@0
|
1450 function signal=applyRampOff(signal, rampDur, rampOffTime, sampleRate)
|
rmeddis@0
|
1451 %applyRampOn applies raised cosine squared ramp
|
rmeddis@0
|
1452 %rampOffTime is the time at which the ramp begins
|
rmeddis@0
|
1453 %At all other times the mask has a value of 1
|
rmeddis@0
|
1454 % signal=applyRampOff(signal, rampDur, rampOffTime, sampleRate)
|
rmeddis@0
|
1455
|
rmeddis@0
|
1456 rampDurPoints=round(rampDur*sampleRate);
|
rmeddis@0
|
1457 rampOff= (1+cos(0:pi/(rampDurPoints-1): pi))/2';
|
rmeddis@0
|
1458
|
rmeddis@0
|
1459 sigDurPoints=length(signal);
|
rmeddis@0
|
1460 mask=ones(1,sigDurPoints);
|
rmeddis@0
|
1461 rampOffStartIndex=round(rampOffTime*sampleRate+1);
|
rmeddis@0
|
1462 mask(rampOffStartIndex: round(rampOffStartIndex+ rampDurPoints-1))=rampOff;
|
rmeddis@0
|
1463 if length(mask)>sigDurPoints, mask=mask(1:sigDurPoints); end
|
rmeddis@0
|
1464 signal=signal.*mask;
|
rmeddis@0
|
1465 %plot(mask)
|
rmeddis@0
|
1466
|
rmeddis@0
|
1467 function signal=applyGaussianRamps(signal, sigma, sampleRate)
|
rmeddis@0
|
1468 dt=1/sampleRate;
|
rmeddis@0
|
1469 time=dt:dt:dt*length(signal);
|
rmeddis@0
|
1470 ramp=1-exp(-time.^2/(2*sigma^2));
|
rmeddis@0
|
1471 % apply onset ramp
|
rmeddis@0
|
1472 signal=signal.*ramp;
|
rmeddis@0
|
1473 % apply offset ramp
|
rmeddis@0
|
1474 ramp=fliplr(ramp);
|
rmeddis@0
|
1475 signal=signal.*ramp;
|
rmeddis@0
|
1476
|
rmeddis@0
|
1477
|
rmeddis@0
|
1478
|
rmeddis@0
|
1479 %--------------------------------------------------------------------checkDescriptors
|
rmeddis@30
|
1480 function [globalStimParams, stimComponents]=...
|
rmeddis@30
|
1481 checkDescriptors(globalStimParams, stimComponents)
|
rmeddis@0
|
1482
|
rmeddis@0
|
1483 try
|
rmeddis@0
|
1484 % if FS exists, it takes priority
|
rmeddis@0
|
1485 globalStimParams.dt=1/globalStimParams.FS;
|
rmeddis@0
|
1486 catch
|
rmeddis@0
|
1487 % otherwise set FS using dt
|
rmeddis@0
|
1488 globalStimParams.FS=1/globalStimParams.dt;
|
rmeddis@0
|
1489 end
|
rmeddis@0
|
1490
|
rmeddis@30
|
1491 sampleRate=globalStimParams.FS;
|
rmeddis@30
|
1492
|
rmeddis@30
|
1493 globalStimParams.nSignalPoints=...
|
rmeddis@30
|
1494 round(globalStimParams.overallDuration* sampleRate);
|
rmeddis@0
|
1495
|
rmeddis@0
|
1496 % optional field (ears)
|
rmeddis@0
|
1497 try
|
rmeddis@0
|
1498 globalStimParams.ears;
|
rmeddis@0
|
1499 catch
|
rmeddis@0
|
1500 % default: dichotic.
|
rmeddis@0
|
1501 globalStimParams.ears='dichotic';
|
rmeddis@0
|
1502 end
|
rmeddis@0
|
1503
|
rmeddis@0
|
1504 % audioOutCorrection is optional
|
rmeddis@0
|
1505 % audioOutCorrection is a scalar for reducing the sound
|
rmeddis@0
|
1506 try
|
rmeddis@0
|
1507 globalStimParams.audioOutCorrection;
|
rmeddis@0
|
1508 catch
|
rmeddis@0
|
1509 % set to 1 if omitted
|
rmeddis@0
|
1510 globalStimParams.audioOutCorrection=1;
|
rmeddis@0
|
1511 end
|
rmeddis@0
|
1512
|
rmeddis@0
|
1513 try
|
rmeddis@0
|
1514 globalStimParams.doPlay;
|
rmeddis@0
|
1515 catch
|
rmeddis@0
|
1516 % default plays sound only if explicitly requested
|
rmeddis@0
|
1517 globalStimParams.doPlay=0;
|
rmeddis@0
|
1518 end
|
rmeddis@0
|
1519
|
rmeddis@0
|
1520 try
|
rmeddis@0
|
1521 globalStimParams.doPlot;
|
rmeddis@0
|
1522 catch
|
rmeddis@0
|
1523 % no plotting unless explicitly requested
|
rmeddis@0
|
1524 globalStimParams.doPlot=0;
|
rmeddis@0
|
1525 end
|
rmeddis@0
|
1526
|
rmeddis@0
|
1527 [ears nComponentSounds]=size(stimComponents);
|
rmeddis@0
|
1528
|
rmeddis@0
|
1529 for ear=1:2 % 1=left/ 2=right
|
rmeddis@0
|
1530
|
rmeddis@0
|
1531 % create a list of components whose type is specified
|
rmeddis@0
|
1532 % if no type is specified assume that it is an empty specification
|
rmeddis@0
|
1533 % this is allowed
|
rmeddis@0
|
1534 validStimComponents=[];
|
rmeddis@0
|
1535 for i=1:nComponentSounds
|
rmeddis@0
|
1536 try
|
rmeddis@0
|
1537 if ~isempty(stimComponents(ear,i).type)
|
rmeddis@0
|
1538 validStimComponents=[validStimComponents i];
|
rmeddis@0
|
1539 end
|
rmeddis@0
|
1540 catch
|
rmeddis@0
|
1541 end
|
rmeddis@0
|
1542 end
|
rmeddis@0
|
1543
|
rmeddis@0
|
1544 for componentNo=validStimComponents
|
rmeddis@0
|
1545 % If no AM filed is present, create it for completeness
|
rmeddis@0
|
1546 if ~isfield(stimComponents(ear,componentNo),'AMfrequency') |...
|
rmeddis@0
|
1547 ~isfield(stimComponents(ear,componentNo),'AMdepth')
|
rmeddis@0
|
1548 stimComponents(ear,componentNo).AMfrequency=0;
|
rmeddis@0
|
1549 stimComponents(ear,componentNo).AMdepth=0;
|
rmeddis@0
|
1550 end
|
rmeddis@0
|
1551
|
rmeddis@0
|
1552 % all signals must have durations, amplitudes and ramps
|
rmeddis@0
|
1553 if ...
|
rmeddis@0
|
1554 isempty(stimComponents(ear,componentNo).type) |...
|
rmeddis@0
|
1555 isempty(stimComponents(ear,componentNo).toneDuration) |...
|
rmeddis@0
|
1556 isempty(stimComponents(ear,componentNo).amplitudesdB) |...
|
rmeddis@0
|
1557 isempty(stimComponents(ear,componentNo).rampOnDur)
|
rmeddis@0
|
1558 descriptorError( 'missing stimComponent descriptor', stimComponents, ear, componentNo)
|
rmeddis@0
|
1559 end
|
rmeddis@0
|
1560
|
rmeddis@0
|
1561 try, stimComponents(ear,componentNo).endSilence; catch, stimComponents(ear,componentNo).endSilence=-1; end
|
rmeddis@0
|
1562
|
rmeddis@0
|
1563 % ramp checks do not apply to file input
|
rmeddis@0
|
1564 if ~strcmp(stimComponents(ear,componentNo).type, 'file')
|
rmeddis@0
|
1565 % match offset ramp to onset if not explicitly specified
|
rmeddis@0
|
1566 if stimComponents(ear,componentNo).rampOffDur==-1,
|
rmeddis@0
|
1567 stimComponents(ear,componentNo).rampOffDur=stimComponents(ear,componentNo).rampOnDur;
|
rmeddis@0
|
1568 end
|
rmeddis@0
|
1569 % ramps must be shorter than the stimulus
|
rmeddis@0
|
1570 if stimComponents(ear,componentNo).rampOffDur> stimComponents(ear,componentNo).toneDuration | ...
|
rmeddis@0
|
1571 stimComponents(ear,componentNo).rampOnDur> stimComponents(ear,componentNo).toneDuration
|
rmeddis@0
|
1572 descriptorError( 'ramp longer than sound component', stimComponents, ear, componentNo)
|
rmeddis@0
|
1573 end
|
rmeddis@0
|
1574 end
|
rmeddis@0
|
1575
|
rmeddis@0
|
1576 % end silence is measured to fit into the global duration
|
rmeddis@0
|
1577 if stimComponents(ear,componentNo).endSilence==-1,
|
rmeddis@30
|
1578 stimComponents(ear,componentNo).endSilence=...
|
rmeddis@30
|
1579 globalStimParams.overallDuration-...
|
rmeddis@30
|
1580 stimComponents(ear,componentNo).beginSilence -...
|
rmeddis@30
|
1581 stimComponents(ear,componentNo).toneDuration;
|
rmeddis@30
|
1582
|
rmeddis@30
|
1583 endSilenceNpoints=stimComponents(ear,componentNo).endSilence...
|
rmeddis@30
|
1584 *sampleRate;
|
rmeddis@30
|
1585 end
|
rmeddis@30
|
1586 if stimComponents(ear,componentNo).endSilence<0
|
rmeddis@30
|
1587 globalStimParams
|
rmeddis@30
|
1588 descriptorError( 'component durations greater than overallDuration', stimComponents, ear, componentNo)
|
rmeddis@0
|
1589 end
|
rmeddis@0
|
1590
|
rmeddis@0
|
1591 % check overall duration of this component against global duration
|
rmeddis@0
|
1592 totalDuration= ...
|
rmeddis@0
|
1593 stimComponents(ear,componentNo).beginSilence+...
|
rmeddis@0
|
1594 stimComponents(ear,componentNo).toneDuration+...
|
rmeddis@0
|
1595 stimComponents(ear,componentNo).endSilence;
|
rmeddis@0
|
1596
|
rmeddis@0
|
1597 % avoid annoying error message for single stimulus component
|
rmeddis@0
|
1598 if ears==1 && nComponentSounds==1
|
rmeddis@0
|
1599 globalStimParams.overallDuration= totalDuration;
|
rmeddis@0
|
1600 end
|
rmeddis@0
|
1601
|
rmeddis@0
|
1602 % check total duration
|
rmeddis@30
|
1603 totalSignalPoints= round(totalDuration* sampleRate);
|
rmeddis@0
|
1604 if totalSignalPoints >globalStimParams.nSignalPoints
|
rmeddis@0
|
1605 descriptorError( 'Signal component duration does not match overall duration ', stimComponents, ear, componentNo)
|
rmeddis@0
|
1606 end
|
rmeddis@0
|
1607
|
rmeddis@0
|
1608 if isfield(stimComponents(ear,componentNo), 'filter')
|
rmeddis@0
|
1609 if ~isequal(length(stimComponents(ear,componentNo).filter), 3)
|
rmeddis@0
|
1610 descriptorError( 'Filter parameter must have three elements ', stimComponents, ear, componentNo)
|
rmeddis@0
|
1611 end
|
rmeddis@0
|
1612 end
|
rmeddis@0
|
1613 end % component
|
rmeddis@0
|
1614 % ??
|
rmeddis@0
|
1615 if strcmp(globalStimParams.ears,'monoticL') | strcmp(globalStimParams.ears, 'monoticR'), break, end
|
rmeddis@0
|
1616 end % ear
|
rmeddis@0
|
1617
|
rmeddis@0
|
1618
|
rmeddis@0
|
1619 %-------------------------------------------------------------------- descriptorError
|
rmeddis@0
|
1620 function descriptorError( msg, stimComponents, ear, componentNo)
|
rmeddis@0
|
1621 stimComponents(ear, componentNo)
|
rmeddis@0
|
1622
|
rmeddis@0
|
1623 disp(' *********** **************** ************')
|
rmeddis@0
|
1624 disp([ '...Error in stimComponents description: '])
|
rmeddis@0
|
1625 disp([msg ])
|
rmeddis@0
|
1626 disp(['Ear = ' num2str(ear) ' component No ' num2str(componentNo)])
|
rmeddis@0
|
1627 disp(' *********** **************** ************')
|
rmeddis@0
|
1628 error('myError ')
|
rmeddis@0
|
1629
|
rmeddis@0
|
1630
|
rmeddis@0
|
1631 %-------------------------------------------------------------------- normalize
|
rmeddis@0
|
1632 function [normalizedSignal, gain]= normalize(signal)
|
rmeddis@0
|
1633 % normalize (signal)
|
rmeddis@0
|
1634 maxSignal=max(max(signal));
|
rmeddis@0
|
1635 minSignal=min(min(signal));
|
rmeddis@0
|
1636 if -minSignal>maxSignal, normalizingFactor=-minSignal; else normalizingFactor=maxSignal; end
|
rmeddis@0
|
1637 normalizingFactor=1.01*normalizingFactor;
|
rmeddis@0
|
1638 gain= 20*log10(normalizingFactor);
|
rmeddis@0
|
1639 normalizedSignal=signal/normalizingFactor;
|
rmeddis@0
|
1640
|
rmeddis@0
|
1641
|
rmeddis@0
|
1642 %--------------------------------------------------------------------Butterworth
|
rmeddis@0
|
1643 function y=Butterworth (x, dt, fl, fu, order)
|
rmeddis@0
|
1644 % Butterworth (x, dt, fu, fl, order)
|
rmeddis@0
|
1645 % Taken from Yuel and beauchamp page 261
|
rmeddis@0
|
1646 % NB error in their table for K (see their text)
|
rmeddis@0
|
1647 % x is original signal
|
rmeddis@0
|
1648 % fu, fl upper and lower cutoff
|
rmeddis@0
|
1649 % order is the number of times the filter is applied
|
rmeddis@0
|
1650
|
rmeddis@0
|
1651 q=(pi*dt*(fu-fl));
|
rmeddis@0
|
1652 J=1/(1+ cot(q));
|
rmeddis@0
|
1653 K= (2*cos(pi*dt*(fu+fl)))/(1+tan(q)*cos(q));
|
rmeddis@0
|
1654 L= (tan(q)-1)/(tan(q)+1);
|
rmeddis@0
|
1655 b=[J -J];
|
rmeddis@0
|
1656 a=[1 -K -L];
|
rmeddis@0
|
1657 for i=1:order
|
rmeddis@0
|
1658 y=filter(b, a, x);
|
rmeddis@0
|
1659 x=y;
|
rmeddis@0
|
1660 end
|
rmeddis@0
|
1661
|
rmeddis@0
|
1662
|
rmeddis@0
|
1663 % -------------------------------------------------------- UTIL_amp2dB
|
rmeddis@0
|
1664 function [y] = UTIL_amp2dB (x, ref)
|
rmeddis@0
|
1665 % Calculates a dB (ref. ref) value 'y' from a peak amplitude number 'x'.
|
rmeddis@0
|
1666 % if ref omitted treat as dB
|
rmeddis@0
|
1667 % Check the number of arguments that are passed in.
|
rmeddis@0
|
1668 if (nargin < 2)
|
rmeddis@0
|
1669 ref=1;
|
rmeddis@0
|
1670 end
|
rmeddis@0
|
1671 if (nargin > 2)
|
rmeddis@0
|
1672 error ('Too many arguments');
|
rmeddis@0
|
1673 end
|
rmeddis@0
|
1674
|
rmeddis@0
|
1675 % Check arguments.
|
rmeddis@0
|
1676 if x < 0.0
|
rmeddis@0
|
1677 error ('Can not calculate the log10 of a negative number');
|
rmeddis@0
|
1678 elseif x == 0.0
|
rmeddis@0
|
1679 warning ('log10 of zero. The result is set to -1000.0');
|
rmeddis@0
|
1680 y = -1000.0;
|
rmeddis@0
|
1681 else
|
rmeddis@0
|
1682 % Do calculations.
|
rmeddis@0
|
1683 y = 20.0 * log10(x/(sqrt(2)*ref));
|
rmeddis@0
|
1684
|
rmeddis@0
|
1685 end
|
rmeddis@0
|
1686
|
rmeddis@0
|
1687 %-------------------------------------------------------------------- FFT
|
rmeddis@0
|
1688 function showFFT (getFFTsignal, dt)
|
rmeddis@0
|
1689 color='r';
|
rmeddis@0
|
1690 figure(2), clf,
|
rmeddis@0
|
1691 hold off
|
rmeddis@0
|
1692
|
rmeddis@0
|
1693 % trim initial silence
|
rmeddis@0
|
1694 idx=find(getFFTsignal>0);
|
rmeddis@0
|
1695 if ~isempty(idx)
|
rmeddis@0
|
1696 getFFTsignal=getFFTsignal(idx(1):end);
|
rmeddis@0
|
1697 end
|
rmeddis@0
|
1698 %trim final silence
|
rmeddis@0
|
1699 getFFTsignal=getFFTsignal(end:-1:1);
|
rmeddis@0
|
1700 idx=find(getFFTsignal>0);
|
rmeddis@0
|
1701 if ~isempty(idx)
|
rmeddis@0
|
1702 getFFTsignal=getFFTsignal(idx(1):end);
|
rmeddis@0
|
1703 getFFTsignal=getFFTsignal(end:-1:1);
|
rmeddis@0
|
1704 end
|
rmeddis@0
|
1705
|
rmeddis@0
|
1706 % Analyse make stimComponents length a power of 2
|
rmeddis@0
|
1707 x=length(getFFTsignal);
|
rmeddis@0
|
1708 squareLength=2;
|
rmeddis@0
|
1709 while squareLength<=x
|
rmeddis@0
|
1710 squareLength=squareLength*2;
|
rmeddis@0
|
1711 end
|
rmeddis@0
|
1712 squareLength=round(squareLength/2);
|
rmeddis@0
|
1713 getFFTsignal=getFFTsignal(1:squareLength);
|
rmeddis@0
|
1714 n=length(getFFTsignal);
|
rmeddis@0
|
1715
|
rmeddis@0
|
1716 minf=100; maxf=20000;
|
rmeddis@0
|
1717
|
rmeddis@0
|
1718 fft_result = fft(getFFTsignal, n); % Compute FFT of the input signal.
|
rmeddis@0
|
1719 fft_power = fft_result .* conj(fft_result);% / n; % Compute power spectrum. Dividing by 'n' we get the power spectral density.
|
rmeddis@0
|
1720 fft_phase = angle(fft_result); % Compute the phase spectrum.
|
rmeddis@0
|
1721
|
rmeddis@0
|
1722 frequencies = (1/dt)*(1:n/2)/n;
|
rmeddis@0
|
1723 fft_power=fft_power(1:length(fft_power)/2); % remove mirror frequencies
|
rmeddis@0
|
1724 fft_phase=fft_phase(1:length(fft_phase)/2); % remove mirror frequencies
|
rmeddis@0
|
1725 fft_powerdB = UTIL_amp2dB (fft_power, max(fft_power)); % convert to dB
|
rmeddis@0
|
1726 % jags=find(diff(fft_phase)>0); % unwrap phase
|
rmeddis@0
|
1727 % for i=jags, fft_phase(i+1:end)=fft_phase(i+1:end)-2*pi; end
|
rmeddis@0
|
1728
|
rmeddis@0
|
1729 xlim([minf maxf])
|
rmeddis@0
|
1730 semilogx(frequencies, fft_powerdB-max(fft_powerdB), color)
|
rmeddis@0
|
1731 ylim([ -20 5])
|
rmeddis@0
|
1732
|
rmeddis@0
|
1733
|
rmeddis@0
|
1734
|
rmeddis@0
|
1735 function y=UTIL_lowpassFilterFreq(x, cutOffFrequency, dt)
|
rmeddis@0
|
1736 % UTIL_lowpassFilterFreq multi-channel filter
|
rmeddis@0
|
1737 %
|
rmeddis@0
|
1738 % Usage:
|
rmeddis@0
|
1739 % output=UTIL_lowpassFilterFreq(input, cutOffFrequency, dt)
|
rmeddis@0
|
1740 %
|
rmeddis@0
|
1741 % cutoff should be around 100 Hz for audio work
|
rmeddis@0
|
1742 % dt should be <1/50000 s for audio work
|
rmeddis@0
|
1743 %
|
rmeddis@0
|
1744 % Attenuation is - 6 dB per octave above cutoff.
|
rmeddis@0
|
1745
|
rmeddis@0
|
1746
|
rmeddis@0
|
1747 sampleRate=1/dt;
|
rmeddis@0
|
1748
|
rmeddis@0
|
1749 if 4*cutOffFrequency>sampleRate
|
rmeddis@0
|
1750 warning(['UTIL_lowpassFilterFreq: sample rate ' num2str(1/dt) ' is too low for this BF. Sampling rate should be >' num2str(4*cutOffFrequency) 'or cutoff (' num2str(4*cutOffFrequency) ') should be lower' ])
|
rmeddis@0
|
1751 cutOffFrequency=sampleRate/4;
|
rmeddis@0
|
1752 end
|
rmeddis@0
|
1753
|
rmeddis@0
|
1754 tau=1/(2*pi*cutOffFrequency);
|
rmeddis@0
|
1755
|
rmeddis@0
|
1756 y=zeros(size(x));
|
rmeddis@0
|
1757 [numChannels numPoints]=size(x);
|
rmeddis@0
|
1758 for i=1:numChannels
|
rmeddis@0
|
1759 y(i,:)=filter([dt/tau], [1 -(1-dt/tau)], x(i,:));
|
rmeddis@0
|
1760 end
|
rmeddis@0
|
1761
|
rmeddis@0
|
1762
|