extract_adult_call_parameters.m 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. function result = extract_adult_call_parameters(recbuf)
  2. % fixed variables
  3. fs=192e3;
  4. env_flow=500;
  5. pk_thrs=-45; % dB
  6. pk_dist=0.005; % in seconds (changed from 0.02 in Meike's extraction script)
  7. dur_thrs=-20; % dB
  8. freq_thrs=-20; % dB
  9. hpf_display=50e3;
  10. nfft = 128;
  11. [eb,ea]=butter(2,2*env_flow/fs);
  12. [hpb,hpa] = butter(8,2*hpf_display/fs,'high'); % 50 (!) kHz high pass
  13. % parameters for envelope (SAM) analysis
  14. fftpts=2^16;
  15. env_f=linspace(0,fs,fftpts);
  16. amf_min=50;
  17. amf_max=800;
  18. amf_ind1=min(find(env_f>amf_min));
  19. amf_ind2=max(find(env_f<amf_max));
  20. amf_inds=[amf_ind1 amf_ind2];
  21. amfs=env_f(amf_inds(1):amf_inds(2));
  22. am_mindur=10e-3;
  23. am_pk_thrs=-90; % dB
  24. am_pk_prom=9; % dB
  25. recbuf=double(recbuf(:,1:16))/32767;
  26. % highpass filter
  27. recbuf=filtfilt(hpb,hpa,recbuf);
  28. aud_time=(0:size(recbuf,1)-1)'/fs;
  29. % search for calls
  30. sumblockbuf=sum(abs(recbuf),2);
  31. % smooth the envelope
  32. env_dB=20*log10(max(filtfilt(eb,ea,sumblockbuf),1e-4));
  33. % find calls
  34. t=[0:length(env_dB)-1]/fs;
  35. [pk_val pk_pos]=findpeaks(env_dB,'minpeakheight',pk_thrs,'minpeakdistance',round(pk_dist*fs));
  36. cn=0;
  37. if isempty(pk_pos)
  38. return;
  39. end
  40. % call cutting and saving
  41. call_num=1;
  42. while call_num<=length(pk_pos),
  43. % determine start and stop of each call as -x dB duration
  44. env_dB=env_dB-env_dB(pk_pos(call_num));
  45. % find start of call
  46. start=max(find(env_dB(1:pk_pos(call_num))<dur_thrs));
  47. stop=min(find(env_dB(pk_pos(call_num):end)<dur_thrs))+pk_pos(call_num);
  48. if 1-isempty(stop) & 1-isempty(start)
  49. cn=cn+1;
  50. call_dur=(stop-start)/fs;
  51. % SET UP RESULTS STRUCTURE
  52. result.call(cn).call_start_sample=pk_pos(cn);
  53. result.call(cn).call_dur=call_dur;
  54. call=recbuf(start:stop,1:16);
  55. call_levels=20*log10(std(call));
  56. [call_level_max call_level_max_pos]=max(call_levels);
  57. xcall=call(:,call_level_max_pos);
  58. xct=(0:length(xcall)-1)'/fs*1e3;
  59. result.call(cn).call_levels=call_levels;
  60. result.call(cn).loudest_call=xcall;
  61. result.call(cn).loudest_mic=call_level_max_pos;
  62. % --- call frequency parameter ----
  63. % spectrogram method
  64. spects = pwelch(xcall,256,250,nfft,fs);
  65. spectsdB = 10*log10(spects);
  66. [ampl_pf pos_pf]= max(spectsdB);
  67. r = find(spectsdB >= ampl_pf+freq_thrs);
  68. faxis = (0:1/nfft:1/2).*fs;
  69. PF = faxis(pos_pf); % peak frequency
  70. Fmin = faxis(r(1)); % min frequency
  71. Fmax= faxis(r(end)); % max frequency
  72. BW= Fmax - Fmin; % bandwidth
  73. % --- spectral centroid frequency
  74. SCF = sum(spects.*faxis')/sum(spects);
  75. SCF_dB = sum(spectsdB.*faxis')/sum(spectsdB);
  76. result.call(cn).PF=PF;
  77. result.call(cn).Fmin=Fmin;
  78. result.call(cn).Fmax=Fmax;
  79. result.call(cn).BW=BW;
  80. result.call(cn).SCF=SCF;
  81. result.call(cn).SCF_dB=SCF_dB;
  82. % use yin to calculate f0 as a function of time
  83. p.sr = fs;
  84. R=yin(xcall,p,0);
  85. f0s=R.f0;
  86. nframes=length(f0s);
  87. f0ts=(1:nframes)/(fs/R.hop);
  88. aperiodicity=R.ap0;
  89. % correct for f0 jumps
  90. fcin=[f0ts' f0s' aperiodicity'];
  91. fdiff=abs(diff(fcin(:,2)));
  92. finds=find(fdiff>9000);
  93. if 1-isempty(finds) % corrections are necessary
  94. f0_corrected=1;
  95. f0_corr=fcin(:,2);
  96. % check start frequency of the irregularity
  97. if f0_corr(finds(1))>30e3;
  98. % correct first jump
  99. f0_corr(1:finds(1))=f0_corr(1:finds(1))/2;
  100. if length(finds)>1,
  101. finds=finds(2:end);
  102. end
  103. end
  104. while length(finds)>1
  105. xfinds=finds(1:2);
  106. f0_corr(xfinds(1)+1:xfinds(2))=f0_corr(xfinds(1)+1:xfinds(2))/2;
  107. if length(finds)>2,
  108. finds=finds(3:end);
  109. elseif length(finds)==2,
  110. finds=[];
  111. break
  112. else
  113. break
  114. end
  115. end
  116. if length(finds)>0,
  117. f0_corr(finds(1)+1:end)=f0_corr(finds(1)+1:end)/2;
  118. end
  119. % figure(10), plot(fcin(:,1),fcin(:,2),'b',fcin(:,1),f0_corr,'r');
  120. %corr_ok=input('correction (1) ok or (0) not ok');
  121. corr_ok=1;
  122. if corr_ok==1,
  123. fcin(:,2)=f0_corr;
  124. end
  125. else
  126. f0_corrected=0;
  127. end
  128. result.call(cn).f0=fcin;
  129. result.call(cn).f0_corrected=f0_corrected;
  130. % fit f0(t)
  131. f0inds=find(1-isnan(fcin(:,2)));
  132. f0data=fcin(f0inds,1:2);
  133. [f0slope,sfm_amp,sfm_frq,sfm_phase]=fit_f0(f0data,0);
  134. result.call(cn).f0_slope=f0slope;
  135. result.call(cn).sfm_amp=sfm_amp;
  136. result.call(cn).sfm_frq=sfm_frq;
  137. result.call(cn).sfm_phase=sfm_phase;
  138. % quantify AM in call envelope
  139. if call_dur>=am_mindur,
  140. if length(xcall)<fftpts,
  141. lxcall=[xcall;zeros(fftpts-length(xcall),1)];
  142. end
  143. [modspec,mfs]=pwelch(abs(lxcall),fftpts,0.5*fftpts,fftpts,fs);
  144. modspec(1:amf_inds(1))=0;
  145. modspec(amf_inds(2):end)=0;
  146. modspecdb=10*log10(modspec);
  147. [AM_val AM_pos]=findpeaks(modspecdb,'minpeakheight',am_pk_thrs,'MinPeakProminence',am_pk_prom);
  148. if isempty(AM_pos),
  149. result.call(cn).amf=0;
  150. else
  151. % select the highest peak
  152. [mist,ind]=max(AM_val);
  153. maxAMfreq=mfs(AM_pos(ind));
  154. result.call(cn).amf=maxAMfreq;
  155. end
  156. else
  157. result.call(cn).amf=0;
  158. end
  159. end
  160. call_num=call_num+1;
  161. start_sec = start/fs;
  162. stop_sec = stop/fs;
  163. disp(sprintf('DONE: call %u, %f-%f s', cn, start_sec, stop_sec))
  164. end