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