Script1_Extraction.m 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. %% This script extracts calls from recordings for a specific bat in a
  2. %% specific timeframe. Parameters of all calls are extracted and saved in a
  3. %% structure.
  4. %set up/clear the workspace
  5. clear all;
  6. close all;
  7. plot_on=0; %select if results should be plotted
  8. save_on=1; %select if results should be saved
  9. %set rootpath of the raw recordings
  10. rootpath='D:\Documents\Manuskripte\PitchShift_artificial_template\recordings';
  11. %% fixed variables
  12. fs=192000; %sampling rate (Hz)
  13. fhigh=1000; %highpass filter frequency (Hz)
  14. nfft=256; %FFT samples
  15. env_mad=0.005; %duration of the boxcar kernel(for moving average) to smooth envelope (s)
  16. env_flow=500;
  17. pk_thrs=-60; %peak threshold (dB)
  18. pk_dist=0.06; %peak-to-peak distance (seconds)
  19. dur_thrs=-20; %call duration threshold (-20 dB from peak)
  20. freq_thrs=-20; %dB
  21. %select bat and timeframe to be analysed
  22. batnum = input('Enter bat number to be analysed! (i.e. 1-6) ','s');
  23. if batnum=='2',
  24. batname='DAM'; % Bat ID
  25. startdate='17/03/08';
  26. stopdate='17/08/10';
  27. elseif batnum=='4',
  28. batname='DID';
  29. startdate='16/12/13';
  30. stopdate='17/07/28';
  31. elseif batnum=='5',
  32. batname='MAR';
  33. startdate='16/12/15';
  34. stopdate='17/07/28';
  35. elseif batnum=='6',
  36. batname='PAU';
  37. startdate='17/02/07';
  38. stopdate='17/08/10';
  39. elseif batnum=='1',
  40. batname='ARN'; % Bat ID
  41. startdate='17/03/10';
  42. stopdate='17/07/19';
  43. elseif batnum=='3',
  44. batname='DAV'; % Bat ID
  45. startdate='17/03/21';
  46. stopdate='17/08/01';
  47. end
  48. startday=datenum(startdate); %reformat start date
  49. stopday=datenum(stopdate); %reformat stop date
  50. current_call=0; %call number counter
  51. for day=startday:stopday, %repeat within selected time frame
  52. folder=[rootpath '\' strrep(datestr(day,25),'/','')]; %access folder with raw recordings
  53. if isdir(folder),
  54. d=dir(folder); %get filenames in folder
  55. filenames=strvcat(d.name);
  56. for nn=3:size(filenames,1), %for all files in the selected folder
  57. filename=deblank(filenames(nn,:));
  58. if 1-isempty(findstr(filename,batname)), %if the folder contains files from the selected bat
  59. clear('call_samples');
  60. abort_analysis=0; %do not abort analysis
  61. load([folder '\' filename]); %load file from folder
  62. prev_ana=0; %set indicator: not previously analysed
  63. clear('call_samples');
  64. disp(filename) %show filename
  65. disp(prev_ana) %show if previously analysed
  66. %% high pass filtering of full recording
  67. [b,a]=butter(2,2*fhigh/fs,'high'); %generate filter
  68. if isinteger(xrec), %convert recording if not integer
  69. fxrec=double(xrec)/32767;
  70. else
  71. fxrec=xrec; %do not overwrite variable with recording
  72. end
  73. fxrec=filtfilt(b,a,fxrec); %filter the recording
  74. %if plotting is selected the next paragraph is active
  75. if plot_on,
  76. figure(1),
  77. spectrogram(fxrec,64,60,nfft,fs,'yaxis');
  78. set(gcf,'Position',[7 758 1317 220]);
  79. title('Spectrogram');
  80. end
  81. if prev_ana==0, %if recording has not previously been analysed
  82. %smooth the envelope of the recording
  83. [eb,ea]=butter(2,2*env_flow/fs);
  84. env_dB=20*log10(max(filtfilt(eb,ea,abs(fxrec)),1e-5));
  85. %find calls in the recording
  86. t=[0:length(env_dB)-1]/fs;
  87. if plot_on,
  88. figure(2),
  89. plot(t,env_dB)
  90. set(gcf,'Position',[7 463 1317 220]);
  91. xlabel('Rec time (s)');
  92. ylabel('Amplitude (arb. units)');
  93. hold on
  94. ylim([min(env_dB) max(env_dB)]);
  95. xs=get(gca,'xlim');
  96. ys=[pk_thrs pk_thrs];
  97. lh=line(xs,ys);
  98. set(lh,'color','r')
  99. hold off
  100. end
  101. %identify peaks and save value and location in recording
  102. [pk_val pk_pos]=findpeaks(env_dB,'minpeakh',pk_thrs,'minpeakdist',round(pk_dist*fs));
  103. %check if last detected call is still fully recorded, otherwise ignore it
  104. if env_dB(end)>pk_thrs,
  105. if length(pk_pos)>1,
  106. pk_pos=pk_pos(1:end-1);
  107. pk_val=pk_val(1:end-1);
  108. else
  109. pk_pos=[];
  110. pk_val=[];
  111. end
  112. end
  113. % check if first detected call is fully recorded, otherwise ignore it
  114. if env_dB(1)>pk_thrs,
  115. if length(pk_pos)>1,
  116. pk_pos=pk_pos(2:end);
  117. pk_val=pk_val(2:end);
  118. else
  119. pk_pos=[];
  120. pk_val=[];
  121. end
  122. end
  123. end
  124. if prev_ana==1, %if recording has previously been analysed
  125. if isempty(call_samples)==0,
  126. pk_pos=call_samples(:,1);
  127. abort_analysis=0;
  128. else %file was analysed but no calls found
  129. abort_analysis=1;
  130. end
  131. end
  132. %if you analysis was not marked as 'to be aborted'
  133. if abort_analysis==0,
  134. for call_num=1:length(pk_pos),
  135. if prev_ana==0,
  136. % determine start and stop of each call as -x dB duration
  137. env_dB=env_dB-env_dB(pk_pos(call_num));
  138. % find start of call
  139. startsample=max(find(env_dB(1:pk_pos(call_num))<dur_thrs+(pk_thrs-pk_val(call_num))));
  140. % calculate IPI to previous call
  141. if call_num>1,
  142. ipi=(startsample-stopsample)/fs;
  143. else
  144. ipi=nan;
  145. end
  146. stopsample=min(find(env_dB(pk_pos(call_num):end)<dur_thrs+(pk_thrs-pk_val(call_num))))+pk_pos(call_num);
  147. else
  148. startsample=call_samples(call_num,1);
  149. stopsample=call_samples(call_num,2);
  150. end
  151. if startsample>1000 & stopsample>startsample & stopsample < length(fxrec),
  152. call=fxrec(startsample:stopsample);
  153. t=0:length(call)-1;
  154. t=t/fs;
  155. if plot_on,
  156. figure(3)
  157. spectrogram(call,64,60,nfft,fs,'yaxis');
  158. set(gcf,'Position',[415 243 400 220]);
  159. title(['Record Start time = ' sprintf('%4.2f',(startsample+recpos)/fs) ' s']);
  160. xlabel('Time (s)');
  161. ylabel('Frequency (Hz)');
  162. end
  163. call_ok=1;
  164. if call_ok,
  165. if prev_ana==0,
  166. call_samples(call_num,1)=startsample;
  167. call_samples(call_num,2)=stopsample;
  168. end
  169. current_call=current_call+1; %add 1 to call counter
  170. % call levels for the call on all 45 channels
  171. call_level=20*log10(std(call));
  172. % max dB value and microphone number with the max. level
  173. call_dur=max(t);
  174. %% --- extract call frequency parameter ----
  175. %% spectrogram method
  176. spects=pwelch(call,256,250,nfft,fs); %PSD estimate of current call
  177. spectsdB=10*log10(spects); %PSD estimate in dB
  178. [ampl_pf pos_pf]=max(spectsdB);
  179. r=find(spectsdB >= ampl_pf+freq_thrs);
  180. faxis=(0:1/nfft:1/2).*fs;
  181. %% --- extract fundamental frequency
  182. % use yin to calculate f0 as a function of time
  183. p.sr=fs;
  184. R=yin(call,p,0);
  185. f0s=R.f0;
  186. nframes=length(f0s);
  187. f0ts=(1:nframes)/(fs/R.hop);
  188. aperiodicity=R.ap0;
  189. if plot_on,
  190. figure(4)
  191. set(gcf,'position',[ 822 36 438 429])
  192. subplot(3,1,1), plot(f0ts,f0s);
  193. title('Fundamental frequency analysis');
  194. xlabel('Time (s)');
  195. ylabel('Fundamental frequency (Hz)');
  196. subplot(3,1,2), plot(f0ts,aperiodicity);
  197. title('Aperiodicity analysis');
  198. xlabel('Time (s)');
  199. ylabel('Gross aperiodicity');
  200. end
  201. % check if f0 corrections are necessary
  202. % ('harmonic jumps' of yin algorithm)
  203. fcin=[f0ts' f0s' aperiodicity'];
  204. fdiff=abs(diff(fcin(:,2)));
  205. finds=find(fdiff>9000);
  206. if 1-isempty(finds) % corrections are necessary
  207. f0_corr=fcin(:,2);
  208. % check start frequency of the irregularity
  209. if f0_corr(finds(1))>30e3;
  210. % correct first jump
  211. f0_corr(1:finds(1))=f0_corr(1:finds(1))/2;
  212. if length(finds)>1,
  213. finds=finds(2:end);
  214. end
  215. end
  216. while length(finds)>1
  217. xfinds=finds(1:2);
  218. f0_corr(xfinds(1)+1:xfinds(2))=f0_corr(xfinds(1)+1:xfinds(2))/2;
  219. if length(finds)>2,
  220. finds=finds(3:end);
  221. elseif length(finds)==2,
  222. finds=[];
  223. break
  224. else
  225. break
  226. end
  227. end
  228. if length(finds)>0,
  229. f0_corr(finds(1)+1:end)=f0_corr(finds(1)+1:end)/2;
  230. end
  231. if plot_on,
  232. figure(8),
  233. subplot(3,1,3),
  234. plot(fcin(:,1),fcin(:,2),'b',fcin(:,1),f0_corr,'r');
  235. end
  236. corr_ok=1;
  237. if corr_ok==1,
  238. fcin(:,2)=f0_corr;
  239. end
  240. end
  241. drawnow;
  242. result.call(current_call).level=call_level;
  243. result.call(current_call).dur=call_dur;
  244. result.call(current_call).f0=fcin;
  245. result.call(current_call).day=datestr(day,25);
  246. result.call(current_call).time=filename(end-10:end-4);
  247. end
  248. end
  249. end
  250. if prev_ana==0 & isempty(pk_pos)==0,
  251. % overwrite raw data file with version including
  252. % sample information of calls
  253. if exist('call_samples','var')==0,
  254. call_samples=[];
  255. end
  256. if save_on==1,
  257. save([folder '\' filename],'xrec','recpos','call_samples');
  258. end
  259. end
  260. end
  261. end
  262. end
  263. end
  264. end
  265. result_filename=['D:\Documents\Manuskripte\PitchShift_artificial_template\scripts\Calls_bat' num2str(batnum) '_' strrep(startdate,'/','') '_to_' strrep(stopdate,'/','')];
  266. if save_on==1,
  267. save(result_filename,'result');
  268. end