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@0
|
503 wavplay(audio,globalStimParams.FS)
|
rmeddis@0
|
504 end
|
rmeddis@0
|
505 % all Done
|
rmeddis@0
|
506 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
rmeddis@0
|
507
|
rmeddis@0
|
508
|
rmeddis@0
|
509
|
rmeddis@0
|
510 %---------------------------------------------------- maketone
|
rmeddis@0
|
511 function tone=maketone(globalStimParams, stimComponents)
|
rmeddis@0
|
512 % maketone generates a stimComponents tone
|
rmeddis@0
|
513 % tone=maketone(dt, frequencies, toneDuration, dBSPL, phases)
|
rmeddis@0
|
514 % tone is returned in Pascals
|
rmeddis@0
|
515 % frequencies is a list of frequencies
|
rmeddis@0
|
516 % phase is list of phases or 'sin', 'cos', 'alt'
|
rmeddis@0
|
517 %
|
rmeddis@0
|
518 % defaults:
|
rmeddis@0
|
519 % phase = sin
|
rmeddis@0
|
520 % dBSPL=56 dB SPL
|
rmeddis@0
|
521
|
rmeddis@0
|
522 dt=globalStimParams.dt;
|
rmeddis@0
|
523 frequencies=stimComponents.frequencies;
|
rmeddis@0
|
524 toneDuration=stimComponents.toneDuration;
|
rmeddis@0
|
525 dBSPL=stimComponents.amplitudesdB;
|
rmeddis@0
|
526 phases=stimComponents.phases;
|
rmeddis@0
|
527
|
rmeddis@0
|
528 if ischar(phases)
|
rmeddis@0
|
529 switch phases
|
rmeddis@0
|
530 case 'sin'
|
rmeddis@0
|
531 phases= zeros(1,length(frequencies));
|
rmeddis@0
|
532 case 'cos'
|
rmeddis@0
|
533 phases= pi/2*ones(1,length(frequencies));
|
rmeddis@0
|
534 case 'alt'
|
rmeddis@0
|
535 phases= repmat([0 pi/2], 1, floor(length(frequencies)/2));
|
rmeddis@0
|
536 if length(phases)<length(frequencies)
|
rmeddis@0
|
537 phases=[phases 0];
|
rmeddis@0
|
538 end
|
rmeddis@0
|
539 case {'ran', 'rand'}
|
rmeddis@0
|
540 phases= 2*pi*rand(1,length(frequencies));
|
rmeddis@0
|
541 end
|
rmeddis@0
|
542 end
|
rmeddis@0
|
543
|
rmeddis@0
|
544 if length(phases)==1, phases=repmat(phases(1), 1, length(frequencies)); end
|
rmeddis@0
|
545 if length(phases)<length(frequencies)
|
rmeddis@0
|
546 error('makeTone:phase specification must have the same length as frequencies')
|
rmeddis@0
|
547 end
|
rmeddis@0
|
548
|
rmeddis@0
|
549 if length(dBSPL)==1, dBSPL=repmat(dBSPL(1), 1, length(frequencies)); end
|
rmeddis@0
|
550 if length(dBSPL)<length(dBSPL)
|
rmeddis@0
|
551 error('makeTone:dBSPL specification must have the same length as frequencies')
|
rmeddis@0
|
552 end
|
rmeddis@0
|
553
|
rmeddis@0
|
554 time=dt:dt:toneDuration;
|
rmeddis@0
|
555 amplitudes=28e-6* 10.^(dBSPL/20);
|
rmeddis@0
|
556
|
rmeddis@0
|
557 tone=zeros(size(time));
|
rmeddis@0
|
558 for i=1:length(frequencies)
|
rmeddis@0
|
559 frequency=frequencies(i);
|
rmeddis@0
|
560 phase=phases(i);
|
rmeddis@0
|
561 tone=tone+amplitudes(i)*sin(2*pi*frequency*time+phase);
|
rmeddis@0
|
562 end
|
rmeddis@0
|
563
|
rmeddis@0
|
564 % figure(1), plot(time, signal)
|
rmeddis@0
|
565
|
rmeddis@0
|
566
|
rmeddis@0
|
567 %---------------------------------------------------- makeOHIOtones
|
rmeddis@0
|
568 function stimulus=makeOHIOtones(globalStimParams, stimComponents)
|
rmeddis@0
|
569
|
rmeddis@0
|
570 % Generates a stimulus consisting of one or more 10-ms tones
|
rmeddis@0
|
571 % Tones are presented at 10-ms intervals
|
rmeddis@0
|
572 % Each tone has its own amplitude and its own ramp.
|
rmeddis@0
|
573
|
rmeddis@0
|
574 frequencies=stimComponents.frequencies;
|
rmeddis@0
|
575 amplitudesdB=stimComponents.amplitudesdB;
|
rmeddis@0
|
576 nFrequencies=length(frequencies);
|
rmeddis@0
|
577
|
rmeddis@0
|
578 dt=globalStimParams.dt;
|
rmeddis@0
|
579 toneDuration=.010;
|
rmeddis@0
|
580 time=dt:dt:toneDuration;
|
rmeddis@0
|
581
|
rmeddis@0
|
582 rampDuration=stimComponents.rampOnDur;
|
rmeddis@0
|
583 rampTime=dt:dt:rampDuration;
|
rmeddis@0
|
584 ramp=[0.5*(1+cos(2*pi*rampTime/(2*rampDuration)+pi)) ones(1,length(time)-length(rampTime))];
|
rmeddis@0
|
585 ramp=ramp.*fliplr(ramp);
|
rmeddis@0
|
586
|
rmeddis@0
|
587 stimulus= zeros(1,round((toneDuration+globalStimParams.beginSilences(end))/dt)+1);
|
rmeddis@0
|
588
|
rmeddis@0
|
589 for i=1:nFrequencies
|
rmeddis@0
|
590 toneBeginPTR=round(globalStimParams.beginSilences(i)/dt)+1;
|
rmeddis@0
|
591
|
rmeddis@0
|
592 frequency=frequencies(i);
|
rmeddis@0
|
593 dBSPL=amplitudesdB(i);
|
rmeddis@0
|
594 amplitude=28e-6* 10.^(dBSPL/20);
|
rmeddis@0
|
595 tone=amplitude*sin(2*pi*frequency*time);
|
rmeddis@0
|
596 tone=tone.*ramp;
|
rmeddis@0
|
597 stimulus(toneBeginPTR:toneBeginPTR+length(tone)-1)=stimulus(toneBeginPTR:toneBeginPTR+length(tone)-1)+tone;
|
rmeddis@0
|
598 end
|
rmeddis@0
|
599 % figure(2), plot( stimulus')
|
rmeddis@0
|
600
|
rmeddis@0
|
601
|
rmeddis@0
|
602 %---------------------------------------------------- makeFMtone
|
rmeddis@0
|
603 function tone=makeFMtone(globalStimParams, stimComponents)
|
rmeddis@0
|
604 % maketone generates a stimComponents tone
|
rmeddis@0
|
605 % tone=maketone(dt, frequencies, toneDuration, dBSPL, phases)
|
rmeddis@0
|
606 % tone is returned in Pascals
|
rmeddis@0
|
607 % frequencies is a list of frequencies
|
rmeddis@0
|
608 % phase is list of phases or 'sin', 'cos', 'alt'
|
rmeddis@0
|
609 %
|
rmeddis@0
|
610 % defaults:
|
rmeddis@0
|
611 % phase = sin
|
rmeddis@0
|
612 % dBSPL=56 dB SPL
|
rmeddis@0
|
613
|
rmeddis@0
|
614 dt=globalStimParams.dt;
|
rmeddis@0
|
615 frequencies=stimComponents.frequencies;
|
rmeddis@0
|
616 toneDuration=stimComponents.toneDuration;
|
rmeddis@0
|
617 dBSPL=stimComponents.amplitudesdB;
|
rmeddis@0
|
618 phases=stimComponents.phases;
|
rmeddis@0
|
619 fmDepth=stimComponents.fmDepth;
|
rmeddis@0
|
620 fmFrequency=stimComponents.fmFrequency;
|
rmeddis@0
|
621
|
rmeddis@0
|
622 if ischar(phases)
|
rmeddis@0
|
623 switch phases
|
rmeddis@0
|
624 case 'sin'
|
rmeddis@0
|
625 phases= zeros(1,length(frequencies));
|
rmeddis@0
|
626 case 'cos'
|
rmeddis@0
|
627 phases= pi/2*ones(1,length(frequencies));
|
rmeddis@0
|
628 case 'alt'
|
rmeddis@0
|
629 phases= repmat([0 pi/2], 1, floor(length(frequencies)/2));
|
rmeddis@0
|
630 if length(phases)<length(frequencies)
|
rmeddis@0
|
631 phases=[phases 0];
|
rmeddis@0
|
632 end
|
rmeddis@0
|
633 end
|
rmeddis@0
|
634 end
|
rmeddis@0
|
635
|
rmeddis@0
|
636 if length(phases)==1, phases=repmat(phases(1), 1, length(frequencies)); end
|
rmeddis@0
|
637 if length(phases)<length(frequencies)
|
rmeddis@0
|
638 error('makeTone:phase specification must have the same length as frequencies')
|
rmeddis@0
|
639 end
|
rmeddis@0
|
640
|
rmeddis@0
|
641 if length(dBSPL)==1, dBSPL=repmat(dBSPL(1), 1, length(frequencies)); end
|
rmeddis@0
|
642 if length(dBSPL)<length(dBSPL)
|
rmeddis@0
|
643 error('makeTone:dBSPL specification must have the same length as frequencies')
|
rmeddis@0
|
644 end
|
rmeddis@0
|
645
|
rmeddis@0
|
646 time=dt:dt:toneDuration;
|
rmeddis@0
|
647 amplitudes=28e-6* 10.^(dBSPL/20);
|
rmeddis@0
|
648
|
rmeddis@0
|
649 tone=zeros(size(time));
|
rmeddis@0
|
650 for i=1:length(frequencies)
|
rmeddis@0
|
651 frequency=frequencies(i);
|
rmeddis@0
|
652 phase=phases(i);
|
rmeddis@0
|
653 tone=tone+amplitudes(i)*sin(2*pi*frequency*time+phase + fmDepth*sin(2*pi*fmFrequency*time));
|
rmeddis@0
|
654 end
|
rmeddis@0
|
655
|
rmeddis@0
|
656 % figure(1), plot(time, signal)
|
rmeddis@0
|
657
|
rmeddis@0
|
658 %----------------------------------------------------makeTransposedStimulus
|
rmeddis@0
|
659 function [transposedStimulus]=makeTransposedStimulus(globalStimParams, stim)
|
rmeddis@0
|
660 % globalStimParams.FS=100000;
|
rmeddis@0
|
661 % globalStimParams.overallDuration=.1; % s
|
rmeddis@0
|
662 % globalStimParams.doPlot=1;
|
rmeddis@0
|
663 %
|
rmeddis@0
|
664 % stim.type='transposedStimulus';
|
rmeddis@0
|
665 % stim.phases='sin';
|
rmeddis@0
|
666 % stim.toneDuration=.05;;
|
rmeddis@0
|
667 % stim.frequencies=500;
|
rmeddis@0
|
668 % stim.amplitudesdB=50;
|
rmeddis@0
|
669 % stim.beginSilence=.01;
|
rmeddis@0
|
670 % stim.endSilence=-1;
|
rmeddis@0
|
671 % stim.rampOnDur=.002;
|
rmeddis@0
|
672 % stim.rampOffDur=-1;
|
rmeddis@0
|
673 %
|
rmeddis@0
|
674 % stim.transCarrierFreq=4000;
|
rmeddis@0
|
675 % stim.transModFreq=100;
|
rmeddis@0
|
676
|
rmeddis@0
|
677 transposedStimulus=[];
|
rmeddis@0
|
678 % make envelope of transposed tone
|
rmeddis@0
|
679 for i=1:length(stim.transModFreq)
|
rmeddis@0
|
680 stim.type='tone';
|
rmeddis@0
|
681 stim.frequencies=stim.transModFreq(i);
|
rmeddis@0
|
682 stim.endsilence=-1; stim.beginSilence=0;
|
rmeddis@0
|
683 [envelope, msg]=stimulusCreate(globalStimParams, stim); % NB recursive
|
rmeddis@0
|
684 envelope=envelope(:,1); % mono
|
rmeddis@0
|
685 % HW rectify
|
rmeddis@0
|
686 envelope(find(envelope<0))=0;
|
rmeddis@0
|
687 % LP filter
|
rmeddis@0
|
688 maxEnvelope=max(envelope);
|
rmeddis@0
|
689 envelope=UTIL_Butterworth (envelope, globalStimParams.dt, 10, .2*stim.transModFreq(i), 2);
|
rmeddis@0
|
690 envelope=envelope*maxEnvelope/max(envelope);
|
rmeddis@0
|
691
|
rmeddis@0
|
692 % make the carrier
|
rmeddis@0
|
693 stim.frequencies=stim.transCarrierFreq(i);
|
rmeddis@0
|
694 stim.endsilence=-1; stim.beginSilence=0;
|
rmeddis@0
|
695 [audio, msg]=stimulusCreate(globalStimParams, stim);
|
rmeddis@0
|
696 carrier=audio(:,1);
|
rmeddis@0
|
697 x= (carrier.*envelope)';
|
rmeddis@0
|
698 % base amplitude on peak of unmodulated carrier
|
rmeddis@0
|
699 x=x/max(carrier);
|
rmeddis@0
|
700 transposedStimulus=[transposedStimulus; x];
|
rmeddis@0
|
701 end
|
rmeddis@0
|
702 transposedStimulus=sum(transposedStimulus,1);
|
rmeddis@0
|
703 % clf,plot(transposedStimulus)
|
rmeddis@0
|
704
|
rmeddis@0
|
705 %--------------------------------------------------------------------makeClicks
|
rmeddis@0
|
706 function clickTrain=makeClicks(globalStimParams, stimComponents)
|
rmeddis@0
|
707 % makeClicks(F0, clickTimes, duration, FS);
|
rmeddis@0
|
708 % F0 specifies the repetition rate of the click sequence
|
rmeddis@0
|
709 % If F0=-1, a single click is generated at the start of the duration of the signal
|
rmeddis@0
|
710 %
|
rmeddis@0
|
711 % clickTimes a are fractions of the period
|
rmeddis@0
|
712 % and specify when the click appears in the period
|
rmeddis@0
|
713 % A click time of 1 is reset to zero.
|
rmeddis@0
|
714 % if the clicktime plus the click width is greater than the period, no click is generated
|
rmeddis@0
|
715 % clicks are treated as 20 microPascal high before amplification
|
rmeddis@0
|
716 % unless otherwise specified in stimComponents.clickHeight
|
rmeddis@0
|
717 % click width is dt unless specified in stimComponents.clickWidth
|
rmeddis@0
|
718 %
|
rmeddis@0
|
719 % for regular pulse train set clicktimes=1 or zero;
|
rmeddis@0
|
720 % FS is the sampling interval;
|
rmeddis@0
|
721 % CarlyonClickTrain(100, [.4 1], 40, 441000);
|
rmeddis@0
|
722
|
rmeddis@0
|
723 FS=globalStimParams.FS; % sample rate
|
rmeddis@0
|
724 dt=1/FS;
|
rmeddis@0
|
725
|
rmeddis@0
|
726 try,clickWidth=stimComponents.clickWidth;catch, clickWidth=dt; end
|
rmeddis@0
|
727 try,clickHeight=stimComponents.clickHeight; catch, clickHeight=28e-6 * 10^(stimComponents.amplitudesdB/20); end
|
rmeddis@0
|
728 try, clickTimes=stimComponents.clickTimes; catch, clickTimes=1; end
|
rmeddis@0
|
729
|
rmeddis@0
|
730 % clickTimes are the times in a cycle that the click
|
rmeddis@0
|
731 % occurs
|
rmeddis@0
|
732 checkClickTimes=clickTimes-1;
|
rmeddis@0
|
733 if max(checkClickTimes)>1
|
rmeddis@0
|
734 msg= 'click times must be <= than 1 (period)';
|
rmeddis@0
|
735 return
|
rmeddis@0
|
736 end
|
rmeddis@0
|
737
|
rmeddis@0
|
738 if clickWidth>=1/stimComponents.clickRepeatFrequency
|
rmeddis@0
|
739 msg= 'click width is too great for frequency';
|
rmeddis@0
|
740 return
|
rmeddis@0
|
741 end
|
rmeddis@0
|
742
|
rmeddis@0
|
743 duration=stimComponents.toneDuration;
|
rmeddis@0
|
744 totalLength=round(stimComponents.toneDuration/dt);
|
rmeddis@0
|
745 F0=stimComponents.clickRepeatFrequency;
|
rmeddis@0
|
746 F0=round(F0/dt)*dt;
|
rmeddis@0
|
747 if F0==-1 % single click required
|
rmeddis@0
|
748 F0=1/duration;
|
rmeddis@0
|
749 repetitions=1;
|
rmeddis@0
|
750 clickStartTimes=1; %clicktimes are fractions of a period
|
rmeddis@0
|
751 else
|
rmeddis@0
|
752 repetitions=round(F0*duration)-1;
|
rmeddis@0
|
753 duration=repetitions/F0;
|
rmeddis@0
|
754 clickStartTimes=clickTimes;
|
rmeddis@0
|
755 end
|
rmeddis@0
|
756 % if a clickTime=1 (end of duty period) set it to the beginning
|
rmeddis@0
|
757 clickStartTimes(clickStartTimes==1)=0;
|
rmeddis@0
|
758
|
rmeddis@0
|
759 period=1/F0;
|
rmeddis@0
|
760 time=dt:dt:period;
|
rmeddis@0
|
761 nPoints=length(time);
|
rmeddis@0
|
762 signal=zeros(1,nPoints);
|
rmeddis@0
|
763 dBSPL=stimComponents.amplitudesdB;
|
rmeddis@0
|
764
|
rmeddis@0
|
765 % compute click train for a single cycle
|
rmeddis@0
|
766 clickWidthNpoints=round(clickWidth*FS);
|
rmeddis@0
|
767 for i=1:length(clickStartTimes)
|
rmeddis@0
|
768 % if clickStartTimes(i)<clickWidth
|
rmeddis@0
|
769 % clickStartTimes(i)=dt;
|
rmeddis@0
|
770 % end
|
rmeddis@0
|
771 clickTime=round(period*clickStartTimes(i)/dt -dt);
|
rmeddis@0
|
772 % clicks are treated as 20 microPascal high
|
rmeddis@0
|
773 if clickTime+clickWidthNpoints<length(signal)
|
rmeddis@0
|
774 signal(clickTime+1:clickTime+clickWidthNpoints)=clickHeight;
|
rmeddis@0
|
775 end
|
rmeddis@0
|
776 end
|
rmeddis@0
|
777
|
rmeddis@0
|
778 clickTrain=repmat(signal, 1, repetitions);
|
rmeddis@0
|
779
|
rmeddis@0
|
780 if length(clickTrain)>totalLength
|
rmeddis@0
|
781 clickTrain=clickTrain(1:totalLength);
|
rmeddis@0
|
782 elseif length(clickTrain)<totalLength
|
rmeddis@0
|
783 timeToAdd=zeros(1,round((totalLength-length(clickTrain))));
|
rmeddis@0
|
784 clickTrain=[clickTrain timeToAdd];
|
rmeddis@0
|
785 % figure (1), plot(clickTrain)
|
rmeddis@0
|
786 end
|
rmeddis@0
|
787
|
rmeddis@0
|
788 %----------------------------------------------------------------makePressnitzerClicks
|
rmeddis@0
|
789 function signal=makePressnitzerClicks(globalStimParams, stimComponents)
|
rmeddis@0
|
790 % PressnitzerClicks(k,duration,dt)
|
rmeddis@0
|
791 % Generates a sequence of clicks with intervals kxx
|
rmeddis@0
|
792 % where x= rand*k/2
|
rmeddis@0
|
793 % This is not the same as Kaernbach and Demany clicks
|
rmeddis@0
|
794
|
rmeddis@0
|
795 FS=globalStimParams.FS; % sample rate
|
rmeddis@0
|
796 dt=1/FS;
|
rmeddis@0
|
797
|
rmeddis@0
|
798 % obligatory parameter
|
rmeddis@0
|
799 try
|
rmeddis@0
|
800 k=stimComponents.k;
|
rmeddis@0
|
801 catch
|
rmeddis@0
|
802 error('PressnitzerClicks: field ''k'' is missing from stimcomponent')
|
rmeddis@0
|
803 end
|
rmeddis@0
|
804
|
rmeddis@0
|
805 % optional parameters
|
rmeddis@0
|
806 if isfield(stimComponents,'clickWidth')
|
rmeddis@0
|
807 clickWidth=stimComponents.clickWidth;
|
rmeddis@0
|
808 else
|
rmeddis@0
|
809 clickWidth=dt;
|
rmeddis@0
|
810 end
|
rmeddis@0
|
811 clickWidthNpoints=round(clickWidth*FS);
|
rmeddis@0
|
812
|
rmeddis@0
|
813 if isfield(stimComponents,'clickHeight')
|
rmeddis@0
|
814 clickHeight=stimComponents.clickHeight;
|
rmeddis@0
|
815 else
|
rmeddis@0
|
816 clickHeight=28e-6 * 10^(stimComponents.amplitudesdB/20);
|
rmeddis@0
|
817 end
|
rmeddis@0
|
818
|
rmeddis@0
|
819 duration=stimComponents.toneDuration;
|
rmeddis@0
|
820
|
rmeddis@0
|
821 signalLength=round(duration/dt);
|
rmeddis@0
|
822 signal=zeros(1,signalLength);
|
rmeddis@0
|
823 kInterval=round(k/dt);
|
rmeddis@0
|
824 halfk=k/2;
|
rmeddis@0
|
825 signal(1)=clickHeight;
|
rmeddis@0
|
826 timeIdx=0;
|
rmeddis@0
|
827 while timeIdx<signalLength
|
rmeddis@0
|
828 % first interval = k
|
rmeddis@0
|
829 clickTime=timeIdx+kInterval;
|
rmeddis@0
|
830 signal(clickTime:clickTime+clickWidthNpoints)=clickHeight;
|
rmeddis@0
|
831 timeIdx=timeIdx+kInterval;
|
rmeddis@0
|
832
|
rmeddis@0
|
833 % second interval = 0 : k/2
|
rmeddis@0
|
834 intervalx1=round(rand*halfk/dt);
|
rmeddis@0
|
835 clickTime=timeIdx+intervalx1;
|
rmeddis@0
|
836 signal(clickTime:clickTime+clickWidthNpoints)=clickHeight;
|
rmeddis@0
|
837 timeIdx=timeIdx+intervalx1;
|
rmeddis@0
|
838
|
rmeddis@0
|
839 % third 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 end
|
rmeddis@0
|
845
|
rmeddis@0
|
846 signal=signal(1:signalLength);
|
rmeddis@0
|
847 % figure(1), plot(dt:dt:duration,signal)
|
rmeddis@0
|
848
|
rmeddis@0
|
849 %----------------------------------------------------------------makePressnitzerABXClicks
|
rmeddis@0
|
850 function signal=makePressnitzerABxClicks(globalStimParams, stimComponents)
|
rmeddis@0
|
851 % Generates a sequence of clicks with intervals ABx
|
rmeddis@0
|
852 % AB interval is 2*k
|
rmeddis@0
|
853 % where A= rand* k
|
rmeddis@0
|
854 % B= k-A
|
rmeddis@0
|
855 % x= k/2
|
rmeddis@0
|
856 % These are second order clicks
|
rmeddis@0
|
857
|
rmeddis@0
|
858 FS=globalStimParams.FS; % sample rate
|
rmeddis@0
|
859 dt=1/FS;
|
rmeddis@0
|
860
|
rmeddis@0
|
861 % obligatory parameter
|
rmeddis@0
|
862 try
|
rmeddis@0
|
863 k=stimComponents.k;
|
rmeddis@0
|
864 catch
|
rmeddis@0
|
865 error('PressnitzerClicks: field ''k'' is missing from stimcomponent')
|
rmeddis@0
|
866 end
|
rmeddis@0
|
867
|
rmeddis@0
|
868 % optional parameters
|
rmeddis@0
|
869 if isfield(stimComponents,'clickWidth')
|
rmeddis@0
|
870 clickWidth=stimComponents.clickWidth;
|
rmeddis@0
|
871 else
|
rmeddis@0
|
872 clickWidth=dt;
|
rmeddis@0
|
873 end
|
rmeddis@0
|
874 clickWidthNpoints=round(clickWidth*FS);
|
rmeddis@0
|
875
|
rmeddis@0
|
876 if isfield(stimComponents,'clickHeight')
|
rmeddis@0
|
877 clickHeight=stimComponents.clickHeight;
|
rmeddis@0
|
878 else
|
rmeddis@0
|
879 clickHeight=28e-6 * 10^(stimComponents.amplitudesdB/20);
|
rmeddis@0
|
880 end
|
rmeddis@0
|
881
|
rmeddis@0
|
882 duration=stimComponents.toneDuration;
|
rmeddis@0
|
883
|
rmeddis@0
|
884 signalLength=round(duration/dt);
|
rmeddis@0
|
885 signal=zeros(1,2*signalLength); % allow for overrun
|
rmeddis@0
|
886 ABinterval=k/dt; % i.e. the number of dt steps
|
rmeddis@0
|
887 randomInterval=ABinterval/2;
|
rmeddis@0
|
888 signal(1)=clickHeight;
|
rmeddis@0
|
889 time=0;
|
rmeddis@0
|
890 while time<signalLength
|
rmeddis@0
|
891 % first interval = A
|
rmeddis@0
|
892 intervalA=rand*ABinterval;
|
rmeddis@0
|
893 clickTime=round(time+intervalA)+1; % +1 avoids zero index
|
rmeddis@0
|
894 signal(clickTime:clickTime+clickWidthNpoints)=clickHeight;
|
rmeddis@0
|
895 time=time+intervalA;
|
rmeddis@0
|
896
|
rmeddis@0
|
897 % second interval = B
|
rmeddis@0
|
898 intervalB=ABinterval-intervalA;
|
rmeddis@0
|
899 clickTime=round(time+intervalB)+1;
|
rmeddis@0
|
900 signal(clickTime:clickTime+clickWidthNpoints-1)=clickHeight;
|
rmeddis@0
|
901 time=time+intervalB;
|
rmeddis@0
|
902
|
rmeddis@0
|
903 % third interval = 0 : k/2
|
rmeddis@0
|
904 intervalx1=rand*randomInterval; % mean random interval=k
|
rmeddis@0
|
905 clickTime=round(time+intervalx1)+1;
|
rmeddis@0
|
906 signal(clickTime:clickTime+clickWidthNpoints-1)=clickHeight;
|
rmeddis@0
|
907 time=time+intervalx1;
|
rmeddis@0
|
908 end
|
rmeddis@0
|
909
|
rmeddis@0
|
910 signal=signal(1:signalLength);
|
rmeddis@0
|
911 % figure(1), plot(dt:dt:duration,signal)
|
rmeddis@0
|
912
|
rmeddis@0
|
913 %-----------------------------------------------------makeABxClicks
|
rmeddis@0
|
914 function signal=makeABxClicks(globalStimParams, stimComponents)
|
rmeddis@0
|
915 % Generates a sequence of clicks with intervals ABx
|
rmeddis@0
|
916 % AB interval is 2*k
|
rmeddis@0
|
917 % where A= rand* k
|
rmeddis@0
|
918 % B= k-A
|
rmeddis@0
|
919 % x= rand*2*k
|
rmeddis@0
|
920 % These are second order clicks
|
rmeddis@0
|
921
|
rmeddis@0
|
922 FS=globalStimParams.FS; % sample rate
|
rmeddis@0
|
923 dt=1/FS;
|
rmeddis@0
|
924
|
rmeddis@0
|
925 % obligatory parameter
|
rmeddis@0
|
926 try
|
rmeddis@0
|
927 k=stimComponents.k;
|
rmeddis@0
|
928 catch
|
rmeddis@0
|
929 error('PressnitzerClicks: field ''k'' is missing from stimcomponent')
|
rmeddis@0
|
930 end
|
rmeddis@0
|
931
|
rmeddis@0
|
932 % optional parameters
|
rmeddis@0
|
933 if isfield(stimComponents,'clickWidth')
|
rmeddis@0
|
934 clickWidth=stimComponents.clickWidth;
|
rmeddis@0
|
935 else
|
rmeddis@0
|
936 clickWidth=dt;
|
rmeddis@0
|
937 end
|
rmeddis@0
|
938 clickWidthNpoints=round(clickWidth*FS);
|
rmeddis@0
|
939
|
rmeddis@0
|
940 if isfield(stimComponents,'clickHeight')
|
rmeddis@0
|
941 clickHeight=stimComponents.clickHeight;
|
rmeddis@0
|
942 else
|
rmeddis@0
|
943 clickHeight=28e-6 * 10^(stimComponents.amplitudesdB/20);
|
rmeddis@0
|
944 end
|
rmeddis@0
|
945
|
rmeddis@0
|
946 duration=stimComponents.toneDuration;
|
rmeddis@0
|
947
|
rmeddis@0
|
948 signalLength=round(duration/dt);
|
rmeddis@0
|
949 signal=zeros(1,2*signalLength); % allow for overrun
|
rmeddis@0
|
950 ABinterval=2*k/dt;
|
rmeddis@0
|
951 randomInterval=ABinterval;
|
rmeddis@0
|
952 signal(1)=clickHeight;
|
rmeddis@0
|
953 timeIdx=0;
|
rmeddis@0
|
954 while timeIdx<signalLength
|
rmeddis@0
|
955 % first interval = A
|
rmeddis@0
|
956 intervalA=round(rand*ABinterval);
|
rmeddis@0
|
957 clickTime=timeIdx+intervalA+1;
|
rmeddis@0
|
958 signal(clickTime:clickTime+clickWidthNpoints-1)=clickHeight;
|
rmeddis@0
|
959 timeIdx=timeIdx+intervalA;
|
rmeddis@0
|
960
|
rmeddis@0
|
961 % second interval = B
|
rmeddis@0
|
962 intervalB=round(ABinterval-intervalA);
|
rmeddis@0
|
963 clickTime=timeIdx+intervalB;
|
rmeddis@0
|
964 signal(clickTime:clickTime+clickWidthNpoints-1)=clickHeight;
|
rmeddis@0
|
965 timeIdx=timeIdx+intervalB;
|
rmeddis@0
|
966
|
rmeddis@0
|
967 % third interval = 0 : k
|
rmeddis@0
|
968 intervalx1=round(rand*randomInterval); % mean random interval=k
|
rmeddis@0
|
969 clickTime=timeIdx+intervalx1;
|
rmeddis@0
|
970 signal(clickTime:clickTime+clickWidthNpoints-1)=clickHeight;
|
rmeddis@0
|
971 timeIdx=timeIdx+intervalx1;
|
rmeddis@0
|
972 end
|
rmeddis@0
|
973
|
rmeddis@0
|
974 signal=signal(1:signalLength);
|
rmeddis@0
|
975 % figure(1), plot(dt:dt:duration,signal)
|
rmeddis@0
|
976
|
rmeddis@0
|
977 %----------------------------------------------------------------makeYostClicks
|
rmeddis@0
|
978 function signal=makeYostClicks(globalStimParams, stimComponents)
|
rmeddis@0
|
979 % Generates a shuffled sequence of clicks with intervals kxxxx
|
rmeddis@0
|
980 % where max(x)= 2*k
|
rmeddis@0
|
981 % and there are n occurrences of x
|
rmeddis@0
|
982 % this section requires:
|
rmeddis@0
|
983 % stimComponents.k
|
rmeddis@0
|
984 % stimComponents.nXs
|
rmeddis@0
|
985 % stimComponents.toneDuration
|
rmeddis@0
|
986 % optional:
|
rmeddis@0
|
987 % stimComponents.clickWidth %useful because width depends on dt
|
rmeddis@0
|
988 % stimComponents.clickHeight %best left to amplitude rescaling later
|
rmeddis@0
|
989
|
rmeddis@0
|
990 FS=globalStimParams.FS; % sample rate
|
rmeddis@0
|
991 dt=1/FS;
|
rmeddis@0
|
992
|
rmeddis@0
|
993 % obligatory parameters
|
rmeddis@0
|
994 try
|
rmeddis@0
|
995 k=stimComponents.k;
|
rmeddis@0
|
996 catch
|
rmeddis@0
|
997 error('makeYostClicks: field ''k'' is missing from stimComponents')
|
rmeddis@0
|
998 end
|
rmeddis@0
|
999
|
rmeddis@0
|
1000 try
|
rmeddis@0
|
1001 nXs=stimComponents.nXs;
|
rmeddis@0
|
1002 catch
|
rmeddis@0
|
1003 error('makeYostClicks: field ''nXs'' is missing from stimComponents')
|
rmeddis@0
|
1004 end
|
rmeddis@0
|
1005
|
rmeddis@0
|
1006 try
|
rmeddis@0
|
1007 shuffled=stimComponents.shuffled;
|
rmeddis@0
|
1008 catch
|
rmeddis@0
|
1009 error('makeYostClicks: field ''shuffled'' is missing from stimComponents')
|
rmeddis@0
|
1010 end
|
rmeddis@0
|
1011
|
rmeddis@0
|
1012 try
|
rmeddis@0
|
1013 duration=stimComponents.toneDuration;
|
rmeddis@0
|
1014 catch
|
rmeddis@0
|
1015 error('makeYostClicks: field ''toneDuration'' is missing from stimComponents')
|
rmeddis@0
|
1016 end
|
rmeddis@0
|
1017
|
rmeddis@0
|
1018 % optional parameters
|
rmeddis@0
|
1019 if isfield(stimComponents,'clickWidth')
|
rmeddis@0
|
1020 clickWidth=stimComponents.clickWidth;
|
rmeddis@0
|
1021 else
|
rmeddis@0
|
1022 clickWidth=dt;
|
rmeddis@0
|
1023 end
|
rmeddis@0
|
1024 clickWidthNpoints=round(clickWidth*FS);
|
rmeddis@0
|
1025
|
rmeddis@0
|
1026 if isfield(stimComponents,'clickHeight')
|
rmeddis@0
|
1027 clickHeight=stimComponents.clickHeight;
|
rmeddis@0
|
1028 else
|
rmeddis@0
|
1029 clickHeight=28e-6 * 10^(stimComponents.amplitudesdB/20);
|
rmeddis@0
|
1030 end
|
rmeddis@0
|
1031
|
rmeddis@0
|
1032 kInterval=round(k/dt);
|
rmeddis@0
|
1033 twoK=k*2; % max width of x interval
|
rmeddis@0
|
1034
|
rmeddis@0
|
1035 signalLength=round(duration/dt);
|
rmeddis@0
|
1036 signal=zeros(1,signalLength);
|
rmeddis@0
|
1037
|
rmeddis@0
|
1038 timeIdx=0;
|
rmeddis@0
|
1039 intervalCount=0;
|
rmeddis@0
|
1040 while timeIdx<signalLength
|
rmeddis@0
|
1041 timeIdx=timeIdx+kInterval;
|
rmeddis@0
|
1042 if timeIdx>signalLength, break,end
|
rmeddis@0
|
1043 intervalCount=intervalCount+1;
|
rmeddis@0
|
1044 intervals(intervalCount)=kInterval;
|
rmeddis@0
|
1045
|
rmeddis@0
|
1046 % repeat x intervals as required
|
rmeddis@0
|
1047 if nXs>0
|
rmeddis@0
|
1048 for nX=1:nXs
|
rmeddis@0
|
1049 xInterval=round(rand*twoK/dt);
|
rmeddis@0
|
1050 timeIdx=timeIdx+xInterval;
|
rmeddis@0
|
1051 if timeIdx>signalLength, break,end
|
rmeddis@0
|
1052 intervalCount=intervalCount+1;
|
rmeddis@0
|
1053 intervals(intervalCount)=xInterval;
|
rmeddis@0
|
1054 end
|
rmeddis@0
|
1055 end
|
rmeddis@0
|
1056 if timeIdx>signalLength, break,end
|
rmeddis@0
|
1057 end
|
rmeddis@0
|
1058
|
rmeddis@0
|
1059 % shuffle intervals
|
rmeddis@0
|
1060 if shuffled
|
rmeddis@0
|
1061 randomNumbers=rand(1,length(intervals));
|
rmeddis@0
|
1062 [randomNumbers idx]=sort(randomNumbers);
|
rmeddis@0
|
1063 intervals=intervals(idx);
|
rmeddis@0
|
1064 idx=intervals>0;
|
rmeddis@0
|
1065 intervals=intervals(idx);
|
rmeddis@0
|
1066 end
|
rmeddis@0
|
1067
|
rmeddis@0
|
1068 intervalCount=length(intervals);
|
rmeddis@0
|
1069 signal(1)=clickHeight;
|
rmeddis@0
|
1070 clickTime=0;
|
rmeddis@0
|
1071 for i=1:intervalCount
|
rmeddis@0
|
1072 clickTime=clickTime+intervals(i);
|
rmeddis@0
|
1073 signal(clickTime:clickTime+clickWidthNpoints)=clickHeight;
|
rmeddis@0
|
1074 end
|
rmeddis@0
|
1075 signal=signal(1:signalLength);
|
rmeddis@0
|
1076 % figure(1), plot(dt:dt:duration,signal)
|
rmeddis@0
|
1077
|
rmeddis@0
|
1078 %--------------------------------------------------------------------makeKxxClicks
|
rmeddis@0
|
1079 function signal=makeKxxClicks(globalStimParams, stimComponents)
|
rmeddis@0
|
1080 % Click train consists of kkxxx.. sequences
|
rmeddis@0
|
1081 % k is the duration of a fixed interval (seconds)
|
rmeddis@0
|
1082 % random intervals are distributed 0 : 2* k (NB not like Pressnitzer clicks)
|
rmeddis@0
|
1083 % nKs is the number of successive 'k' intervals
|
rmeddis@0
|
1084 % nXs is the number of random intervals between k sequences
|
rmeddis@0
|
1085 % sequences of 3 x intervals > k are replaced with new sequences
|
rmeddis@0
|
1086 % shuffled causes all intervals to be reordered randomly
|
rmeddis@0
|
1087 % NB 1/k is the mean click rate
|
rmeddis@0
|
1088
|
rmeddis@0
|
1089 FS=globalStimParams.FS; % sample rate
|
rmeddis@0
|
1090 dt=1/FS;
|
rmeddis@0
|
1091
|
rmeddis@0
|
1092 try
|
rmeddis@0
|
1093 k=stimComponents.k; % duration (s) of fixed intervals
|
rmeddis@0
|
1094 catch
|
rmeddis@0
|
1095 error('makeYostClicks: field ''k'' is missing from stimComponents')
|
rmeddis@0
|
1096 end
|
rmeddis@0
|
1097
|
rmeddis@0
|
1098 try
|
rmeddis@0
|
1099 duration=stimComponents.toneDuration;
|
rmeddis@0
|
1100 catch
|
rmeddis@0
|
1101 error('makeYostClicks: field ''duration'' is missing from stimComponents')
|
rmeddis@0
|
1102 end
|
rmeddis@0
|
1103
|
rmeddis@0
|
1104 if isfield(stimComponents,'clickWidth')
|
rmeddis@0
|
1105 clickWidth=stimComponents.clickWidth;
|
rmeddis@0
|
1106 else
|
rmeddis@0
|
1107 clickWidth=dt;
|
rmeddis@0
|
1108 end
|
rmeddis@0
|
1109
|
rmeddis@0
|
1110 if isfield(stimComponents,'clickHeight')
|
rmeddis@0
|
1111 clickHeight=stimComponents.clickHeight;
|
rmeddis@0
|
1112 else
|
rmeddis@0
|
1113 clickHeight=28e-6 * 10^(stimComponents.amplitudesdB/20);
|
rmeddis@0
|
1114 end
|
rmeddis@0
|
1115
|
rmeddis@0
|
1116
|
rmeddis@0
|
1117 if isfield(stimComponents,'order')
|
rmeddis@0
|
1118 order=stimComponents.order;
|
rmeddis@0
|
1119 else
|
rmeddis@0
|
1120 order=1;
|
rmeddis@0
|
1121 end
|
rmeddis@0
|
1122
|
rmeddis@0
|
1123 if isfield(stimComponents,'nKs')
|
rmeddis@0
|
1124 nKs=stimComponents.nKs;
|
rmeddis@0
|
1125 else
|
rmeddis@0
|
1126 nKs=1;
|
rmeddis@0
|
1127 end
|
rmeddis@0
|
1128
|
rmeddis@0
|
1129 if isfield(stimComponents,'nXs')
|
rmeddis@0
|
1130 nXs=stimComponents.nXs;
|
rmeddis@0
|
1131 else
|
rmeddis@0
|
1132 nXs=1;
|
rmeddis@0
|
1133 end
|
rmeddis@0
|
1134
|
rmeddis@0
|
1135 if isfield(stimComponents,'shuffled')
|
rmeddis@0
|
1136 shuffled=stimComponents.shuffled;
|
rmeddis@0
|
1137 else
|
rmeddis@0
|
1138 shuffled=1;
|
rmeddis@0
|
1139 end
|
rmeddis@0
|
1140
|
rmeddis@0
|
1141 kLength=round(k/dt); % fixed interval
|
rmeddis@0
|
1142 xLength=2*kLength; % maximum random interval
|
rmeddis@0
|
1143 requiredSignalLength=round(duration/dt);
|
rmeddis@0
|
1144 intervalsPerCycle=(nKs+nXs);
|
rmeddis@0
|
1145 cycleLength=nKs*kLength+nXs*xLength;
|
rmeddis@0
|
1146 % more cycles to allow for uncertainty
|
rmeddis@0
|
1147 nCycles=5*round(requiredSignalLength/cycleLength);
|
rmeddis@0
|
1148 nIntervals=nCycles*intervalsPerCycle;
|
rmeddis@0
|
1149
|
rmeddis@0
|
1150 % random intervals
|
rmeddis@0
|
1151 if nXs>0
|
rmeddis@0
|
1152 xIntervals=floor(rand(1,nIntervals)*2*kLength);
|
rmeddis@0
|
1153 % weed out triple intervals > 2*k
|
rmeddis@0
|
1154 rogues=1;
|
rmeddis@0
|
1155 while sum(rogues)
|
rmeddis@0
|
1156 y=(xIntervals>kLength);
|
rmeddis@0
|
1157 rogues=(sum([y(1:end-2)' y(2:end-1)' y(3:end)'],2)>2);
|
rmeddis@0
|
1158 xIntervals(rogues)=floor(rand*2*kLength);
|
rmeddis@0
|
1159 end
|
rmeddis@0
|
1160 xIntervals=reshape(xIntervals,nCycles,intervalsPerCycle);
|
rmeddis@0
|
1161 else
|
rmeddis@0
|
1162 xIntervals=[];
|
rmeddis@0
|
1163 end
|
rmeddis@0
|
1164
|
rmeddis@0
|
1165 % insert constrained (k) intervals
|
rmeddis@0
|
1166 if nKs>0
|
rmeddis@0
|
1167 switch order
|
rmeddis@0
|
1168 case 1
|
rmeddis@0
|
1169 kIntervals=floor(ones(nCycles,nKs)*kLength);
|
rmeddis@0
|
1170 case 2
|
rmeddis@0
|
1171 nKs=1; % force this
|
rmeddis@0
|
1172 kIntervals=floor(rand(nCycles,1)*kLength);
|
rmeddis@0
|
1173 kIntervals=[kIntervals kLength-kIntervals];
|
rmeddis@0
|
1174 end
|
rmeddis@0
|
1175 else
|
rmeddis@0
|
1176 kIntervals=[];
|
rmeddis@0
|
1177 end
|
rmeddis@0
|
1178
|
rmeddis@0
|
1179 % combine fixed and random
|
rmeddis@0
|
1180 intervals=[kIntervals xIntervals(:,nKs+1:end)];
|
rmeddis@0
|
1181 % make a single array;
|
rmeddis@0
|
1182 [r c]=size(intervals);
|
rmeddis@0
|
1183 intervals=reshape(intervals',1,r*c);
|
rmeddis@0
|
1184
|
rmeddis@0
|
1185 % shuffle intervals
|
rmeddis@0
|
1186 if shuffled
|
rmeddis@0
|
1187 randomNumbers=rand(1,length(intervals));
|
rmeddis@0
|
1188 [randomNumbers idx]=sort(randomNumbers);
|
rmeddis@0
|
1189 intervals=intervals(idx);
|
rmeddis@0
|
1190 idx=intervals>0;
|
rmeddis@0
|
1191 intervals=intervals(idx);
|
rmeddis@0
|
1192 end
|
rmeddis@0
|
1193
|
rmeddis@0
|
1194 % convert intervals to clicks
|
rmeddis@0
|
1195 clickTimes=cumsum(intervals);
|
rmeddis@0
|
1196 signal(clickTimes)=clickHeight;
|
rmeddis@0
|
1197 signal=signal(1:requiredSignalLength);
|
rmeddis@0
|
1198 % figure(1), clf, plot(signal)
|
rmeddis@0
|
1199
|
rmeddis@0
|
1200
|
rmeddis@0
|
1201
|
rmeddis@0
|
1202 %--------------------------------------------------------------------makeNoise
|
rmeddis@0
|
1203 function noise=makeNoise(globalStimParams, stimComponents)
|
rmeddis@0
|
1204 % FS in Hz, noiseDuration in s, delay in s;
|
rmeddis@0
|
1205 % noise is returned with overall level dB(rms) = amplitudesdB
|
rmeddis@0
|
1206 %
|
rmeddis@0
|
1207 % % You need
|
rmeddis@0
|
1208 %
|
rmeddis@0
|
1209 % stim.type='noise'; % or 'IRN', or 'pinkNoise'
|
rmeddis@0
|
1210 % stim.toneDuration=.05;
|
rmeddis@0
|
1211 % stim.amplitudesdB=50;
|
rmeddis@0
|
1212 % stim.beginSilence=.01;
|
rmeddis@0
|
1213 % stim.endSilence=-1;
|
rmeddis@0
|
1214 % stim.rampOnDur=.002;
|
rmeddis@0
|
1215 % stim.rampOffDur=-1;
|
rmeddis@0
|
1216 %
|
rmeddis@0
|
1217 % % Mandatory structure fields
|
rmeddis@0
|
1218 % globalStimParams.FS=100000;
|
rmeddis@0
|
1219 % globalStimParams.overallDuration=.1; % s
|
rmeddis@0
|
1220 % globalStimParams.doPlot=1;
|
rmeddis@0
|
1221 % globalStimParams.doPlay=1;
|
rmeddis@0
|
1222 %
|
rmeddis@0
|
1223 % [audio, msg]=stimulusCreate(globalStimParams, stim, );
|
rmeddis@0
|
1224 %
|
rmeddis@0
|
1225 % % local
|
rmeddis@0
|
1226 % stim.type='noise'; % or 'IRN'
|
rmeddis@0
|
1227 %
|
rmeddis@0
|
1228 FS=globalStimParams.FS;
|
rmeddis@0
|
1229 noiseDuration= stimComponents.toneDuration;
|
rmeddis@0
|
1230 npts=round(noiseDuration*FS);
|
rmeddis@0
|
1231 noise=randn(1,npts); % NB randn (normally distributed)
|
rmeddis@0
|
1232
|
rmeddis@0
|
1233 switch stimComponents.type
|
rmeddis@0
|
1234 case 'pinkNoise'
|
rmeddis@0
|
1235 % noise=UTIL_lowpassFilterFreq(noise, 100, 1/FS);
|
rmeddis@0
|
1236 noise=UTIL_bandPassFilter(noise, 1, 100, 200, 1/FS,[]);
|
rmeddis@0
|
1237 end
|
rmeddis@0
|
1238
|
rmeddis@0
|
1239 rms=(mean(noise.^2)^.5); %should be 20 microPascals for 0 dB SPL
|
rmeddis@0
|
1240 adjust=20e-6/rms;
|
rmeddis@0
|
1241 noise=noise*adjust;
|
rmeddis@0
|
1242 rms=(mean(noise.^2)^.5);
|
rmeddis@0
|
1243 amplitude=10.^(stimComponents.amplitudesdB/20);
|
rmeddis@0
|
1244 noise=amplitude*noise;
|
rmeddis@0
|
1245 % rms=(mean(noise.^2)^.5);
|
rmeddis@0
|
1246 % dBnoise=20*log10(rms/20e-6)
|
rmeddis@0
|
1247
|
rmeddis@0
|
1248
|
rmeddis@0
|
1249 %--------------------------------------------------------------------makeWhiteNoise
|
rmeddis@0
|
1250 function noise=makeWhiteNoise(globalStimParams, stimComponents)
|
rmeddis@0
|
1251 % FS in Hz, noiseDuration in s, delay in s;
|
rmeddis@0
|
1252 % noise is bandpass filtered between 100 and 10000 Hz
|
rmeddis@0
|
1253 % spectrum level (dB/Hz) is 40 dB below nominal level.
|
rmeddis@0
|
1254 % noise is returned with dB(rms) = amplitudesdB
|
rmeddis@0
|
1255 %
|
rmeddis@0
|
1256 % % You need
|
rmeddis@0
|
1257 %
|
rmeddis@0
|
1258 % stim.type='noise'; % or 'IRN', or 'pinkNoise'
|
rmeddis@0
|
1259 % stim.toneDuration=.05;
|
rmeddis@0
|
1260 % stim.amplitudesdB=50;
|
rmeddis@0
|
1261 % stim.beginSilence=.01;
|
rmeddis@0
|
1262 % stim.endSilence=-1;
|
rmeddis@0
|
1263 % stim.rampOnDur=.002;
|
rmeddis@0
|
1264 % stim.rampOffDur=-1;
|
rmeddis@0
|
1265 %
|
rmeddis@0
|
1266 % % Mandatory structure fields
|
rmeddis@0
|
1267 % globalStimParams.FS=100000;
|
rmeddis@0
|
1268 % globalStimParams.overallDuration=.1; % s
|
rmeddis@0
|
1269 % globalStimParams.doPlot=1;
|
rmeddis@0
|
1270 % globalStimParams.doPlay=1;
|
rmeddis@0
|
1271 %
|
rmeddis@0
|
1272 % [audio, msg]=stimulusCreate(globalStimParams, stim, );
|
rmeddis@0
|
1273 %
|
rmeddis@0
|
1274 % % local
|
rmeddis@0
|
1275 % stim.type='noise'; % or 'IRN'
|
rmeddis@0
|
1276 %
|
rmeddis@0
|
1277 FS=globalStimParams.FS;
|
rmeddis@0
|
1278 noiseDuration= stimComponents.toneDuration;
|
rmeddis@0
|
1279 npts=round(noiseDuration*FS);
|
rmeddis@0
|
1280 noise=randn(1,npts);
|
rmeddis@0
|
1281
|
rmeddis@0
|
1282 noise=UTIL_bandPassFilter (noise, 6, 100, 10000, 1/FS, []);
|
rmeddis@0
|
1283
|
rmeddis@0
|
1284 rms=(mean(noise.^2)^.5); %should be 20 microPascals for 0 dB SPL
|
rmeddis@0
|
1285 adjust=20e-6/rms;
|
rmeddis@0
|
1286 noise=noise*adjust;
|
rmeddis@0
|
1287 rms=(mean(noise.^2)^.5);
|
rmeddis@0
|
1288 amplitude=10.^(stimComponents.amplitudesdB/20);
|
rmeddis@0
|
1289 noise=amplitude*noise;
|
rmeddis@0
|
1290 % rms=(mean(noise.^2)^.5);
|
rmeddis@0
|
1291 % dBnoise=20*log10(rms/20e-6)
|
rmeddis@0
|
1292
|
rmeddis@0
|
1293
|
rmeddis@0
|
1294 %-----------------------------------------------------------------makeIRN
|
rmeddis@0
|
1295 function noise=makeIRN(globalStimParams, stimComponents)
|
rmeddis@0
|
1296 % FS in Hz, noiseDuration in s, delay in s;
|
rmeddis@0
|
1297 % noise is returned with dB(rms) = amplitudesdB
|
rmeddis@0
|
1298 %
|
rmeddis@0
|
1299 % % You need
|
rmeddis@0
|
1300 %
|
rmeddis@0
|
1301 % stim.type='noise'; % or 'IRN', or 'pinkNoise'
|
rmeddis@0
|
1302 % stim.toneDuration=.05;
|
rmeddis@0
|
1303 % stim.amplitudesdB=50;
|
rmeddis@0
|
1304 % stim.beginSilence=.01;
|
rmeddis@0
|
1305 % stim.endSilence=-1;
|
rmeddis@0
|
1306 % stim.rampOnDur=.002;
|
rmeddis@0
|
1307 % stim.rampOffDur=-1;
|
rmeddis@0
|
1308 %
|
rmeddis@0
|
1309 % % Mandatory structure fields
|
rmeddis@0
|
1310 % globalStimParams.FS=100000;
|
rmeddis@0
|
1311 % globalStimParams.overallDuration=.1; % s
|
rmeddis@0
|
1312 % globalStimParams.doPlot=1;
|
rmeddis@0
|
1313 % globalStimParams.doPlay=1;
|
rmeddis@0
|
1314 %
|
rmeddis@0
|
1315 % [audio, msg]=stimulusCreate(globalStimParams, stim, );
|
rmeddis@0
|
1316 %
|
rmeddis@0
|
1317 % % local
|
rmeddis@0
|
1318 % stim.type='noise'; % or 'IRN'
|
rmeddis@0
|
1319 % % for IRN only
|
rmeddis@0
|
1320 % stim.niterations = 8; %0 for white noise
|
rmeddis@0
|
1321 % stim.delay = 1/150;
|
rmeddis@0
|
1322 % stim.irnGain = 1;
|
rmeddis@0
|
1323 %
|
rmeddis@0
|
1324 FS=globalStimParams.FS;
|
rmeddis@0
|
1325 noiseDuration= stimComponents.toneDuration;
|
rmeddis@0
|
1326
|
rmeddis@0
|
1327 nIterations=stimComponents.niterations;
|
rmeddis@0
|
1328 if nIterations==0
|
rmeddis@0
|
1329 % white noise is specified as nIterations=1
|
rmeddis@0
|
1330 nIterations=1;
|
rmeddis@0
|
1331 IRNgain=0;
|
rmeddis@0
|
1332 delay=0.01; % dummy
|
rmeddis@0
|
1333 else
|
rmeddis@0
|
1334 % IRN
|
rmeddis@0
|
1335 delay=stimComponents.delay;
|
rmeddis@0
|
1336 IRNgain=stimComponents.irnGain;
|
rmeddis@0
|
1337 end
|
rmeddis@0
|
1338
|
rmeddis@0
|
1339 npts=round(noiseDuration*FS);
|
rmeddis@0
|
1340 dels=round(delay*FS);
|
rmeddis@0
|
1341 noise=randn(1,npts);
|
rmeddis@0
|
1342
|
rmeddis@0
|
1343 %fringe=nIterations*dels;
|
rmeddis@0
|
1344 %npts=npts+fringe;
|
rmeddis@0
|
1345
|
rmeddis@0
|
1346 for i=1:nIterations,
|
rmeddis@0
|
1347 dnoise=[noise(dels+1:npts) noise(1:dels)];
|
rmeddis@0
|
1348 dnoise=dnoise.*IRNgain;
|
rmeddis@0
|
1349 noise=noise+dnoise;
|
rmeddis@0
|
1350 end;
|
rmeddis@0
|
1351
|
rmeddis@0
|
1352 switch stimComponents.type
|
rmeddis@0
|
1353 case 'pinkNoise'
|
rmeddis@0
|
1354 noise=UTIL_lowpassFilterFreq(noise, 10000, 1/FS);
|
rmeddis@0
|
1355 end
|
rmeddis@0
|
1356
|
rmeddis@0
|
1357 rms=(mean(noise.^2)^.5); %should be 20 microPascals for 0 dB SPL
|
rmeddis@0
|
1358 adjust=20e-6/rms;
|
rmeddis@0
|
1359 noise=noise*adjust;
|
rmeddis@0
|
1360 rms=(mean(noise.^2)^.5);
|
rmeddis@0
|
1361 amplitude=10.^(stimComponents.amplitudesdB/20);
|
rmeddis@0
|
1362 noise=amplitude*noise;
|
rmeddis@0
|
1363 % rms=(mean(noise.^2)^.5);
|
rmeddis@0
|
1364 % dBnoise=20*log10(rms/20e-6)
|
rmeddis@0
|
1365
|
rmeddis@0
|
1366 %------------------------------------------------------------------ makeRPN
|
rmeddis@0
|
1367 function RPN=makeRPN(globalStimParams, stim)
|
rmeddis@0
|
1368 % 'period' is a collection of samples - AAABCD
|
rmeddis@0
|
1369 % you need
|
rmeddis@0
|
1370 %
|
rmeddis@0
|
1371 % stim.type='RPN';
|
rmeddis@0
|
1372 % stim.toneDuration=.2;
|
rmeddis@0
|
1373 % stim.amplitudesdB=50;
|
rmeddis@0
|
1374 % stim.beginSilence=.01;
|
rmeddis@0
|
1375 % stim.rampOnDur=.002;
|
rmeddis@0
|
1376 % stim.rampOffDur=-1;
|
rmeddis@0
|
1377 %
|
rmeddis@0
|
1378 % stim.sampleDuration=.005; %200 Hz pitch
|
rmeddis@0
|
1379 % stim.nSimilarSamples=5; % pitch strength
|
rmeddis@0
|
1380 % stim.nIndependentSamples=1% dilutes strength
|
rmeddis@0
|
1381 %
|
rmeddis@0
|
1382 % % Mandatory structure fields
|
rmeddis@0
|
1383 % globalStimParams.FS=44100;
|
rmeddis@0
|
1384 % globalStimParams.overallDuration=.21; % s
|
rmeddis@0
|
1385 %
|
rmeddis@0
|
1386 % globalStimParams.doPlot=1;
|
rmeddis@0
|
1387 % globalStimParams.doPlay=1;
|
rmeddis@0
|
1388 % [audio, msg]=stimulusCreate(globalStimParams, stim);
|
rmeddis@0
|
1389
|
rmeddis@0
|
1390 FS=globalStimParams.FS;
|
rmeddis@0
|
1391 ptsPerSample=floor(stim.sampleDuration*FS);
|
rmeddis@0
|
1392
|
rmeddis@0
|
1393 samplesPerPeriod=stim.nSimilarSamples+stim.nIndependentSamples;
|
rmeddis@0
|
1394 periodDuration=samplesPerPeriod*stim.sampleDuration;
|
rmeddis@0
|
1395
|
rmeddis@0
|
1396 totalNumPeriods=2*floor(stim.toneDuration/periodDuration); % longer than necessary
|
rmeddis@0
|
1397 if totalNumPeriods<1
|
rmeddis@0
|
1398 error('stimulusCreate: RPN, stimulus duration needs to be longer')
|
rmeddis@0
|
1399 end
|
rmeddis@0
|
1400
|
rmeddis@0
|
1401 RPN=[];
|
rmeddis@0
|
1402 for j=1:totalNumPeriods
|
rmeddis@0
|
1403 noise=randn(1,ptsPerSample);
|
rmeddis@0
|
1404 for i=1:stim.nSimilarSamples
|
rmeddis@0
|
1405 RPN=[RPN noise];
|
rmeddis@0
|
1406 end
|
rmeddis@0
|
1407
|
rmeddis@0
|
1408 for i=1:stim.nIndependentSamples
|
rmeddis@0
|
1409 noise=randn(1,ptsPerSample);
|
rmeddis@0
|
1410 RPN=[RPN noise];
|
rmeddis@0
|
1411 end
|
rmeddis@0
|
1412 end
|
rmeddis@0
|
1413
|
rmeddis@0
|
1414 targetStimulusLength=round(stim.toneDuration/FS);
|
rmeddis@0
|
1415 RPN=RPN(1:floor(stim.toneDuration*FS)); % take enough for stimulus
|
rmeddis@0
|
1416
|
rmeddis@0
|
1417 rms=(mean(RPN.^2)^.5); %should be 20 microPascals for 0 dB SPL
|
rmeddis@0
|
1418 adjust=20e-6/rms;
|
rmeddis@0
|
1419 RPN=RPN*adjust;
|
rmeddis@0
|
1420 rms=(mean(RPN.^2)^.5);
|
rmeddis@0
|
1421 amplitude=10.^(stim.amplitudesdB/20);
|
rmeddis@0
|
1422 RPN=amplitude*RPN;
|
rmeddis@0
|
1423 % rms=(mean(noise.^2)^.5);
|
rmeddis@0
|
1424 % dBnoise=20*log10(rms/20e-6)
|
rmeddis@0
|
1425
|
rmeddis@0
|
1426 %--------------------------------------------------------------------applyRampOn
|
rmeddis@0
|
1427 function signal=applyRampOn(signal, rampDur, rampOnTime, sampleRate)
|
rmeddis@0
|
1428 %applyRampOn applies raised cosine ramp
|
rmeddis@0
|
1429 %rampOntime is the time at which the ramp begins
|
rmeddis@0
|
1430 %At all other times the mask has a value of 1
|
rmeddis@0
|
1431 %signal=applyRampOn(signal, rampDur, rampOnTime, sampleRate)
|
rmeddis@0
|
1432
|
rmeddis@0
|
1433 rampDurPoints=round(rampDur*sampleRate);
|
rmeddis@0
|
1434 rampOn= (1+cos(pi:pi/(rampDurPoints-1): 2*pi))/2';
|
rmeddis@0
|
1435
|
rmeddis@0
|
1436 sigDurPoints=length(signal);
|
rmeddis@0
|
1437 mask(1:sigDurPoints)=1;
|
rmeddis@0
|
1438 rampOnStartIndex=round(rampOnTime*sampleRate+1);
|
rmeddis@0
|
1439 mask(rampOnStartIndex: rampOnStartIndex+ rampDurPoints-1)=rampOn;
|
rmeddis@0
|
1440 signal=signal.*mask;
|
rmeddis@0
|
1441 %plot(mask)
|
rmeddis@0
|
1442
|
rmeddis@0
|
1443 %--------------------------------------------------------------------applyRampOff
|
rmeddis@0
|
1444 function signal=applyRampOff(signal, rampDur, rampOffTime, sampleRate)
|
rmeddis@0
|
1445 %applyRampOn applies raised cosine squared ramp
|
rmeddis@0
|
1446 %rampOffTime is the time at which the ramp begins
|
rmeddis@0
|
1447 %At all other times the mask has a value of 1
|
rmeddis@0
|
1448 % signal=applyRampOff(signal, rampDur, rampOffTime, sampleRate)
|
rmeddis@0
|
1449
|
rmeddis@0
|
1450 rampDurPoints=round(rampDur*sampleRate);
|
rmeddis@0
|
1451 rampOff= (1+cos(0:pi/(rampDurPoints-1): pi))/2';
|
rmeddis@0
|
1452
|
rmeddis@0
|
1453 sigDurPoints=length(signal);
|
rmeddis@0
|
1454 mask=ones(1,sigDurPoints);
|
rmeddis@0
|
1455 rampOffStartIndex=round(rampOffTime*sampleRate+1);
|
rmeddis@0
|
1456 mask(rampOffStartIndex: round(rampOffStartIndex+ rampDurPoints-1))=rampOff;
|
rmeddis@0
|
1457 if length(mask)>sigDurPoints, mask=mask(1:sigDurPoints); end
|
rmeddis@0
|
1458 signal=signal.*mask;
|
rmeddis@0
|
1459 %plot(mask)
|
rmeddis@0
|
1460
|
rmeddis@0
|
1461 function signal=applyGaussianRamps(signal, sigma, sampleRate)
|
rmeddis@0
|
1462 dt=1/sampleRate;
|
rmeddis@0
|
1463 time=dt:dt:dt*length(signal);
|
rmeddis@0
|
1464 ramp=1-exp(-time.^2/(2*sigma^2));
|
rmeddis@0
|
1465 % apply onset ramp
|
rmeddis@0
|
1466 signal=signal.*ramp;
|
rmeddis@0
|
1467 % apply offset ramp
|
rmeddis@0
|
1468 ramp=fliplr(ramp);
|
rmeddis@0
|
1469 signal=signal.*ramp;
|
rmeddis@0
|
1470
|
rmeddis@0
|
1471
|
rmeddis@0
|
1472
|
rmeddis@0
|
1473 %--------------------------------------------------------------------checkDescriptors
|
rmeddis@0
|
1474 function [globalStimParams, stimComponents]=checkDescriptors(globalStimParams, stimComponents);
|
rmeddis@0
|
1475
|
rmeddis@0
|
1476 try
|
rmeddis@0
|
1477 % if FS exists, it takes priority
|
rmeddis@0
|
1478 globalStimParams.dt=1/globalStimParams.FS;
|
rmeddis@0
|
1479 catch
|
rmeddis@0
|
1480 % otherwise set FS using dt
|
rmeddis@0
|
1481 globalStimParams.FS=1/globalStimParams.dt;
|
rmeddis@0
|
1482 end
|
rmeddis@0
|
1483
|
rmeddis@0
|
1484 globalStimParams.nSignalPoints=round(globalStimParams.overallDuration/globalStimParams.dt);
|
rmeddis@0
|
1485
|
rmeddis@0
|
1486 % optional field (ears)
|
rmeddis@0
|
1487 try
|
rmeddis@0
|
1488 globalStimParams.ears;
|
rmeddis@0
|
1489 catch
|
rmeddis@0
|
1490 % default: dichotic.
|
rmeddis@0
|
1491 globalStimParams.ears='dichotic';
|
rmeddis@0
|
1492 end
|
rmeddis@0
|
1493
|
rmeddis@0
|
1494 % audioOutCorrection is optional
|
rmeddis@0
|
1495 % audioOutCorrection is a scalar for reducing the sound
|
rmeddis@0
|
1496 try
|
rmeddis@0
|
1497 globalStimParams.audioOutCorrection;
|
rmeddis@0
|
1498 catch
|
rmeddis@0
|
1499 % set to 1 if omitted
|
rmeddis@0
|
1500 globalStimParams.audioOutCorrection=1;
|
rmeddis@0
|
1501 end
|
rmeddis@0
|
1502
|
rmeddis@0
|
1503 try
|
rmeddis@0
|
1504 globalStimParams.doPlay;
|
rmeddis@0
|
1505 catch
|
rmeddis@0
|
1506 % default plays sound only if explicitly requested
|
rmeddis@0
|
1507 globalStimParams.doPlay=0;
|
rmeddis@0
|
1508 end
|
rmeddis@0
|
1509
|
rmeddis@0
|
1510 try
|
rmeddis@0
|
1511 globalStimParams.doPlot;
|
rmeddis@0
|
1512 catch
|
rmeddis@0
|
1513 % no plotting unless explicitly requested
|
rmeddis@0
|
1514 globalStimParams.doPlot=0;
|
rmeddis@0
|
1515 end
|
rmeddis@0
|
1516
|
rmeddis@0
|
1517
|
rmeddis@0
|
1518
|
rmeddis@0
|
1519 [ears nComponentSounds]=size(stimComponents);
|
rmeddis@0
|
1520
|
rmeddis@0
|
1521 for ear=1:2 % 1=left/ 2=right
|
rmeddis@0
|
1522
|
rmeddis@0
|
1523 % create a list of components whose type is specified
|
rmeddis@0
|
1524 % if no type is specified assume that it is an empty specification
|
rmeddis@0
|
1525 % this is allowed
|
rmeddis@0
|
1526 validStimComponents=[];
|
rmeddis@0
|
1527 for i=1:nComponentSounds
|
rmeddis@0
|
1528 try
|
rmeddis@0
|
1529 if ~isempty(stimComponents(ear,i).type)
|
rmeddis@0
|
1530 validStimComponents=[validStimComponents i];
|
rmeddis@0
|
1531 end
|
rmeddis@0
|
1532 catch
|
rmeddis@0
|
1533 end
|
rmeddis@0
|
1534 end
|
rmeddis@0
|
1535
|
rmeddis@0
|
1536 for componentNo=validStimComponents
|
rmeddis@0
|
1537 % If no AM filed is present, create it for completeness
|
rmeddis@0
|
1538 if ~isfield(stimComponents(ear,componentNo),'AMfrequency') |...
|
rmeddis@0
|
1539 ~isfield(stimComponents(ear,componentNo),'AMdepth')
|
rmeddis@0
|
1540 stimComponents(ear,componentNo).AMfrequency=0;
|
rmeddis@0
|
1541 stimComponents(ear,componentNo).AMdepth=0;
|
rmeddis@0
|
1542 end
|
rmeddis@0
|
1543
|
rmeddis@0
|
1544 % all signals must have durations, amplitudes and ramps
|
rmeddis@0
|
1545 if ...
|
rmeddis@0
|
1546 isempty(stimComponents(ear,componentNo).type) |...
|
rmeddis@0
|
1547 isempty(stimComponents(ear,componentNo).toneDuration) |...
|
rmeddis@0
|
1548 isempty(stimComponents(ear,componentNo).amplitudesdB) |...
|
rmeddis@0
|
1549 isempty(stimComponents(ear,componentNo).rampOnDur)
|
rmeddis@0
|
1550 descriptorError( 'missing stimComponent descriptor', stimComponents, ear, componentNo)
|
rmeddis@0
|
1551 end
|
rmeddis@0
|
1552
|
rmeddis@0
|
1553 try, stimComponents(ear,componentNo).endSilence; catch, stimComponents(ear,componentNo).endSilence=-1; end
|
rmeddis@0
|
1554
|
rmeddis@0
|
1555 % ramp checks do not apply to file input
|
rmeddis@0
|
1556 if ~strcmp(stimComponents(ear,componentNo).type, 'file')
|
rmeddis@0
|
1557 % match offset ramp to onset if not explicitly specified
|
rmeddis@0
|
1558 if stimComponents(ear,componentNo).rampOffDur==-1,
|
rmeddis@0
|
1559 stimComponents(ear,componentNo).rampOffDur=stimComponents(ear,componentNo).rampOnDur;
|
rmeddis@0
|
1560 end
|
rmeddis@0
|
1561 % ramps must be shorter than the stimulus
|
rmeddis@0
|
1562 if stimComponents(ear,componentNo).rampOffDur> stimComponents(ear,componentNo).toneDuration | ...
|
rmeddis@0
|
1563 stimComponents(ear,componentNo).rampOnDur> stimComponents(ear,componentNo).toneDuration
|
rmeddis@0
|
1564 descriptorError( 'ramp longer than sound component', stimComponents, ear, componentNo)
|
rmeddis@0
|
1565 end
|
rmeddis@0
|
1566 end
|
rmeddis@0
|
1567
|
rmeddis@0
|
1568 % end silence is measured to fit into the global duration
|
rmeddis@0
|
1569 if stimComponents(ear,componentNo).endSilence==-1,
|
rmeddis@0
|
1570 endSilenceNpoints=...
|
rmeddis@0
|
1571 globalStimParams.nSignalPoints ...
|
rmeddis@0
|
1572 -round(stimComponents(ear,componentNo).beginSilence*globalStimParams.FS)...
|
rmeddis@0
|
1573 -round(stimComponents(ear,componentNo).toneDuration*globalStimParams.FS);
|
rmeddis@0
|
1574 stimComponents(ear,componentNo).endSilence=endSilenceNpoints/globalStimParams.FS;
|
rmeddis@0
|
1575 % if endSilence < 0, we have a problem
|
rmeddis@0
|
1576 if stimComponents(ear,componentNo).endSilence<0
|
rmeddis@0
|
1577 globalStimParams
|
rmeddis@0
|
1578 descriptorError( 'component durations greater than overallDuration', stimComponents, ear, componentNo)
|
rmeddis@0
|
1579 end
|
rmeddis@0
|
1580 end
|
rmeddis@0
|
1581
|
rmeddis@0
|
1582 % check overall duration of this component against global duration
|
rmeddis@0
|
1583 totalDuration= ...
|
rmeddis@0
|
1584 stimComponents(ear,componentNo).beginSilence+...
|
rmeddis@0
|
1585 stimComponents(ear,componentNo).toneDuration+...
|
rmeddis@0
|
1586 stimComponents(ear,componentNo).endSilence;
|
rmeddis@0
|
1587
|
rmeddis@0
|
1588 % avoid annoying error message for single stimulus component
|
rmeddis@0
|
1589 if ears==1 && nComponentSounds==1
|
rmeddis@0
|
1590 globalStimParams.overallDuration= totalDuration;
|
rmeddis@0
|
1591 end
|
rmeddis@0
|
1592
|
rmeddis@0
|
1593
|
rmeddis@0
|
1594 if round(totalDuration*globalStimParams.FS)>round(globalStimParams.overallDuration*globalStimParams.FS)
|
rmeddis@0
|
1595 globalStimParams
|
rmeddis@0
|
1596 descriptorError( 'Component durations greater than overallDuration', stimComponents, ear, componentNo)
|
rmeddis@0
|
1597 end
|
rmeddis@0
|
1598
|
rmeddis@0
|
1599 % check total duration
|
rmeddis@0
|
1600 totalSignalPoints= round((stimComponents(ear,componentNo).beginSilence+ stimComponents(ear,componentNo).toneDuration+...
|
rmeddis@0
|
1601 stimComponents(ear,componentNo).endSilence)/globalStimParams.dt);
|
rmeddis@0
|
1602 if totalSignalPoints >globalStimParams.nSignalPoints
|
rmeddis@0
|
1603 descriptorError( 'Signal component duration does not match overall duration ', stimComponents, ear, componentNo)
|
rmeddis@0
|
1604 end
|
rmeddis@0
|
1605
|
rmeddis@0
|
1606 % no ramps for clicks please!
|
rmeddis@0
|
1607 % if strcmp(stimComponents(ear,componentNo).type, 'clicks') & stimComponents(ear,componentNo).clickRepeatFrequency==-1
|
rmeddis@0
|
1608 % if strcmp(stimComponents(ear,componentNo).type, 'clicks')
|
rmeddis@0
|
1609 % stimComponents(ear,componentNo).rampOnDur=0;
|
rmeddis@0
|
1610 % stimComponents(ear,componentNo).rampOffDur=0;
|
rmeddis@0
|
1611 % end
|
rmeddis@0
|
1612
|
rmeddis@0
|
1613 if isfield(stimComponents(ear,componentNo), 'filter')
|
rmeddis@0
|
1614 if ~isequal(length(stimComponents(ear,componentNo).filter), 3)
|
rmeddis@0
|
1615 descriptorError( 'Filter parameter must have three elements ', stimComponents, ear, componentNo)
|
rmeddis@0
|
1616 end
|
rmeddis@0
|
1617 end
|
rmeddis@0
|
1618 end % component
|
rmeddis@0
|
1619 % ??
|
rmeddis@0
|
1620 if strcmp(globalStimParams.ears,'monoticL') | strcmp(globalStimParams.ears, 'monoticR'), break, end
|
rmeddis@0
|
1621 end % ear
|
rmeddis@0
|
1622
|
rmeddis@0
|
1623
|
rmeddis@0
|
1624 %-------------------------------------------------------------------- descriptorError
|
rmeddis@0
|
1625 function descriptorError( msg, stimComponents, ear, componentNo)
|
rmeddis@0
|
1626 stimComponents(ear, componentNo)
|
rmeddis@0
|
1627
|
rmeddis@0
|
1628 disp(' *********** **************** ************')
|
rmeddis@0
|
1629 disp([ '...Error in stimComponents description: '])
|
rmeddis@0
|
1630 disp([msg ])
|
rmeddis@0
|
1631 disp(['Ear = ' num2str(ear) ' component No ' num2str(componentNo)])
|
rmeddis@0
|
1632 disp(' *********** **************** ************')
|
rmeddis@0
|
1633 error('myError ')
|
rmeddis@0
|
1634
|
rmeddis@0
|
1635
|
rmeddis@0
|
1636 %-------------------------------------------------------------------- normalize
|
rmeddis@0
|
1637 function [normalizedSignal, gain]= normalize(signal)
|
rmeddis@0
|
1638 % normalize (signal)
|
rmeddis@0
|
1639 maxSignal=max(max(signal));
|
rmeddis@0
|
1640 minSignal=min(min(signal));
|
rmeddis@0
|
1641 if -minSignal>maxSignal, normalizingFactor=-minSignal; else normalizingFactor=maxSignal; end
|
rmeddis@0
|
1642 normalizingFactor=1.01*normalizingFactor;
|
rmeddis@0
|
1643 gain= 20*log10(normalizingFactor);
|
rmeddis@0
|
1644 normalizedSignal=signal/normalizingFactor;
|
rmeddis@0
|
1645
|
rmeddis@0
|
1646
|
rmeddis@0
|
1647 %--------------------------------------------------------------------Butterworth
|
rmeddis@0
|
1648 function y=Butterworth (x, dt, fl, fu, order)
|
rmeddis@0
|
1649 % Butterworth (x, dt, fu, fl, order)
|
rmeddis@0
|
1650 % Taken from Yuel and beauchamp page 261
|
rmeddis@0
|
1651 % NB error in their table for K (see their text)
|
rmeddis@0
|
1652 % x is original signal
|
rmeddis@0
|
1653 % fu, fl upper and lower cutoff
|
rmeddis@0
|
1654 % order is the number of times the filter is applied
|
rmeddis@0
|
1655
|
rmeddis@0
|
1656 q=(pi*dt*(fu-fl));
|
rmeddis@0
|
1657 J=1/(1+ cot(q));
|
rmeddis@0
|
1658 K= (2*cos(pi*dt*(fu+fl)))/(1+tan(q)*cos(q));
|
rmeddis@0
|
1659 L= (tan(q)-1)/(tan(q)+1);
|
rmeddis@0
|
1660 b=[J -J];
|
rmeddis@0
|
1661 a=[1 -K -L];
|
rmeddis@0
|
1662 for i=1:order
|
rmeddis@0
|
1663 y=filter(b, a, x);
|
rmeddis@0
|
1664 x=y;
|
rmeddis@0
|
1665 end
|
rmeddis@0
|
1666
|
rmeddis@0
|
1667
|
rmeddis@0
|
1668 % -------------------------------------------------------- UTIL_amp2dB
|
rmeddis@0
|
1669 function [y] = UTIL_amp2dB (x, ref)
|
rmeddis@0
|
1670 % Calculates a dB (ref. ref) value 'y' from a peak amplitude number 'x'.
|
rmeddis@0
|
1671 % if ref omitted treat as dB
|
rmeddis@0
|
1672 % Check the number of arguments that are passed in.
|
rmeddis@0
|
1673 if (nargin < 2)
|
rmeddis@0
|
1674 ref=1;
|
rmeddis@0
|
1675 end
|
rmeddis@0
|
1676 if (nargin > 2)
|
rmeddis@0
|
1677 error ('Too many arguments');
|
rmeddis@0
|
1678 end
|
rmeddis@0
|
1679
|
rmeddis@0
|
1680 % Check arguments.
|
rmeddis@0
|
1681 if x < 0.0
|
rmeddis@0
|
1682 error ('Can not calculate the log10 of a negative number');
|
rmeddis@0
|
1683 elseif x == 0.0
|
rmeddis@0
|
1684 warning ('log10 of zero. The result is set to -1000.0');
|
rmeddis@0
|
1685 y = -1000.0;
|
rmeddis@0
|
1686 else
|
rmeddis@0
|
1687 % Do calculations.
|
rmeddis@0
|
1688 y = 20.0 * log10(x/(sqrt(2)*ref));
|
rmeddis@0
|
1689
|
rmeddis@0
|
1690 end
|
rmeddis@0
|
1691
|
rmeddis@0
|
1692 %-------------------------------------------------------------------- FFT
|
rmeddis@0
|
1693 function showFFT (getFFTsignal, dt)
|
rmeddis@0
|
1694 color='r';
|
rmeddis@0
|
1695 figure(2), clf,
|
rmeddis@0
|
1696 hold off
|
rmeddis@0
|
1697
|
rmeddis@0
|
1698 % trim initial silence
|
rmeddis@0
|
1699 idx=find(getFFTsignal>0);
|
rmeddis@0
|
1700 if ~isempty(idx)
|
rmeddis@0
|
1701 getFFTsignal=getFFTsignal(idx(1):end);
|
rmeddis@0
|
1702 end
|
rmeddis@0
|
1703 %trim final silence
|
rmeddis@0
|
1704 getFFTsignal=getFFTsignal(end:-1:1);
|
rmeddis@0
|
1705 idx=find(getFFTsignal>0);
|
rmeddis@0
|
1706 if ~isempty(idx)
|
rmeddis@0
|
1707 getFFTsignal=getFFTsignal(idx(1):end);
|
rmeddis@0
|
1708 getFFTsignal=getFFTsignal(end:-1:1);
|
rmeddis@0
|
1709 end
|
rmeddis@0
|
1710
|
rmeddis@0
|
1711 % Analyse make stimComponents length a power of 2
|
rmeddis@0
|
1712 x=length(getFFTsignal);
|
rmeddis@0
|
1713 squareLength=2;
|
rmeddis@0
|
1714 while squareLength<=x
|
rmeddis@0
|
1715 squareLength=squareLength*2;
|
rmeddis@0
|
1716 end
|
rmeddis@0
|
1717 squareLength=round(squareLength/2);
|
rmeddis@0
|
1718 getFFTsignal=getFFTsignal(1:squareLength);
|
rmeddis@0
|
1719 n=length(getFFTsignal);
|
rmeddis@0
|
1720
|
rmeddis@0
|
1721 minf=100; maxf=20000;
|
rmeddis@0
|
1722
|
rmeddis@0
|
1723 fft_result = fft(getFFTsignal, n); % Compute FFT of the input signal.
|
rmeddis@0
|
1724 fft_power = fft_result .* conj(fft_result);% / n; % Compute power spectrum. Dividing by 'n' we get the power spectral density.
|
rmeddis@0
|
1725 fft_phase = angle(fft_result); % Compute the phase spectrum.
|
rmeddis@0
|
1726
|
rmeddis@0
|
1727 frequencies = (1/dt)*(1:n/2)/n;
|
rmeddis@0
|
1728 fft_power=fft_power(1:length(fft_power)/2); % remove mirror frequencies
|
rmeddis@0
|
1729 fft_phase=fft_phase(1:length(fft_phase)/2); % remove mirror frequencies
|
rmeddis@0
|
1730 fft_powerdB = UTIL_amp2dB (fft_power, max(fft_power)); % convert to dB
|
rmeddis@0
|
1731 % jags=find(diff(fft_phase)>0); % unwrap phase
|
rmeddis@0
|
1732 % for i=jags, fft_phase(i+1:end)=fft_phase(i+1:end)-2*pi; end
|
rmeddis@0
|
1733
|
rmeddis@0
|
1734 xlim([minf maxf])
|
rmeddis@0
|
1735 semilogx(frequencies, fft_powerdB-max(fft_powerdB), color)
|
rmeddis@0
|
1736 ylim([ -20 5])
|
rmeddis@0
|
1737
|
rmeddis@0
|
1738
|
rmeddis@0
|
1739
|
rmeddis@0
|
1740 function y=UTIL_lowpassFilterFreq(x, cutOffFrequency, dt)
|
rmeddis@0
|
1741 % UTIL_lowpassFilterFreq multi-channel filter
|
rmeddis@0
|
1742 %
|
rmeddis@0
|
1743 % Usage:
|
rmeddis@0
|
1744 % output=UTIL_lowpassFilterFreq(input, cutOffFrequency, dt)
|
rmeddis@0
|
1745 %
|
rmeddis@0
|
1746 % cutoff should be around 100 Hz for audio work
|
rmeddis@0
|
1747 % dt should be <1/50000 s for audio work
|
rmeddis@0
|
1748 %
|
rmeddis@0
|
1749 % Attenuation is - 6 dB per octave above cutoff.
|
rmeddis@0
|
1750
|
rmeddis@0
|
1751
|
rmeddis@0
|
1752 sampleRate=1/dt;
|
rmeddis@0
|
1753
|
rmeddis@0
|
1754 if 4*cutOffFrequency>sampleRate
|
rmeddis@0
|
1755 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
|
1756 cutOffFrequency=sampleRate/4;
|
rmeddis@0
|
1757 end
|
rmeddis@0
|
1758
|
rmeddis@0
|
1759 tau=1/(2*pi*cutOffFrequency);
|
rmeddis@0
|
1760
|
rmeddis@0
|
1761 y=zeros(size(x));
|
rmeddis@0
|
1762 [numChannels numPoints]=size(x);
|
rmeddis@0
|
1763 for i=1:numChannels
|
rmeddis@0
|
1764 y(i,:)=filter([dt/tau], [1 -(1-dt/tau)], x(i,:));
|
rmeddis@0
|
1765 end
|
rmeddis@0
|
1766
|
rmeddis@0
|
1767
|