/src/rj_barkflux_accum~.c

http://github.com/rjdj/rjlib · C · 372 lines · 229 code · 82 blank · 61 comment · 13 complexity · c236b841df2631016b9157e6c658c962 MD5 · raw file

  1. #include "m_pd.h"
  2. #include <math.h>
  3. #include <stdlib.h>
  4. #ifdef NT
  5. #pragma warning( disable : 4244 )
  6. #pragma warning( disable : 4305 )
  7. #endif
  8. #define NFILTERS 12
  9. #define LF 40.f
  10. #define HF 8000.f
  11. #define WINSIZE 513
  12. /* ----------------------- frequency domain conversion utilities -----------------------*/
  13. // Bark <-> Hz conversion functions
  14. // // See: Traunmüller (1990) "Analytical expressions for the tonotopic sensory scale" J. Acoust. Soc. Am. 88: 97-100.
  15. float freq2bark(float freq){
  16. return (26.81f / (1.f + 1960.f / freq ) - 0.53f);
  17. }
  18. float bark2freq(float z){
  19. return (1960.f / (26.81f / (z + 0.53f) - 1.f));
  20. }
  21. // Mel <-> Hz conversion functions
  22. // // See: Stevens, Volkman and Newman in 1937 (J. Acoust. Soc. Am 8(3) 185--190)
  23. float freq2mel(float freq){
  24. return (1127.01048f * log (1.f+(freq/700.f)));
  25. }
  26. float mel2freq(float m){
  27. return (700.f*(exp(m/1127.01048f)-1));
  28. }
  29. // FFTBin <-> Hz conversion functions
  30. int freq2bin(int windowSize,int sampleRate, float freq){
  31. return (int) ceil((windowSize*2*freq)/(float)sampleRate);
  32. }
  33. float bin2freq(int windowSize,int sampleRate, int bin){
  34. return ((float) (bin*sampleRate))/(2.f*(float) windowSize);
  35. }
  36. /* ------------------------ Filter objects ------------------------ */
  37. typedef struct {
  38. float lf;
  39. float hf;
  40. float gain;
  41. } RectSpecFilter;
  42. typedef RectSpecFilter * RectSpecFilter_ptr;
  43. float getCf(RectSpecFilter m) {
  44. return (m.hf+m.lf)*.5f;
  45. }
  46. float getEnergy(RectSpecFilter m ,float * input, int winSize, int sampleRate) {
  47. int lf_index=freq2bin(winSize, sampleRate, m.lf);
  48. int hf_index=freq2bin(winSize, sampleRate, m.hf);
  49. float res=0.;
  50. int i;
  51. for (i=lf_index; i<=hf_index; i++) {
  52. res+=input[i]*input[i];
  53. }
  54. return res;
  55. }
  56. /* ------------------------ Filterbank ---------------------------- */
  57. typedef struct {
  58. int winSize;
  59. int nFilters;
  60. int sampleRate;
  61. float lowF;
  62. float highF;
  63. RectSpecFilter_ptr bankFilters;
  64. // bank energies
  65. float * bankEn;
  66. } Filterbank;
  67. typedef Filterbank * Filterbank_ptr;
  68. Filterbank_ptr allocFilterbank(int nFilters) {
  69. Filterbank_ptr fb=malloc(sizeof(Filterbank));
  70. fb->bankFilters=malloc(nFilters*sizeof(RectSpecFilter));
  71. fb->bankEn=malloc(nFilters*sizeof(float));
  72. return fb;
  73. }
  74. void deleteFilterbank(Filterbank_ptr fb) {
  75. free(fb->bankFilters);
  76. free(fb->bankEn);
  77. free(fb);
  78. }
  79. void initFilterbank(Filterbank_ptr m, int nFilters, int winSize, int sampleRate, float lowF, float highF) {
  80. m->lowF=lowF;
  81. m->highF=highF;
  82. m->sampleRate=sampleRate;
  83. m->nFilters=nFilters;
  84. m->winSize=winSize;
  85. float zBeg, zEnd, zBw;
  86. zBeg=freq2bark(lowF);
  87. zEnd=freq2bark(highF);
  88. zBw=(zEnd-zBeg)/(float) nFilters;
  89. // linear separation on the bark scale
  90. int i;
  91. for (i=0; i<nFilters; i++) {
  92. float lf=bark2freq(zBeg+zBw*((float) i));
  93. float hf=bark2freq(zBeg+zBw*((float) i + 1.f));
  94. float gain=1.f;
  95. // set filters lf, hf, gain
  96. m->bankFilters[i].lf=lf;
  97. m->bankFilters[i].hf=hf;
  98. m->bankFilters[i].gain=lf;
  99. }
  100. };
  101. void computeFilterEnergies(Filterbank_ptr m, float * input){
  102. int i;
  103. for (i=0; i< m->nFilters; i++){
  104. m->bankEn[i]=getEnergy(m->bankFilters[i],input, m->winSize, m->sampleRate) /((float) m->winSize);
  105. }
  106. };
  107. /* ------------------------ rj_barkflux_accum~ for pd----------------------------- */
  108. /* tilde object to take absolute value. */
  109. static t_class *rj_barkflux_accum_class;
  110. typedef struct _rj_barkflux_accum
  111. {
  112. t_object x_obj; /* obligatory header */
  113. t_float x_f; /* place to hold inlet's value if it's set by message */
  114. Filterbank_ptr fb; /* fb: place to hold the filterbank structure */
  115. // configuration values
  116. float blocksize;
  117. float samplerate;
  118. // short term signal variables
  119. // frame counter
  120. int st_buffercnt;
  121. // desired duration of short trem description
  122. float st_duration;
  123. // number of frames needed to describe st signal
  124. int st_buffersize;
  125. // short term bark spectrum
  126. float * st_spectrum;
  127. float * st_spectrum_norm;
  128. // long term signal variables
  129. // frame counter
  130. int lt_buffercnt;
  131. // desired duration of short trem description
  132. float lt_duration;
  133. // number of frames needed to describe st signal
  134. int lt_buffersize;
  135. // short term bark spectrum
  136. float * lt_spectrum;
  137. float * lt_spectrum_norm;
  138. // error mode
  139. // 0=rms
  140. // 1-kl divergence
  141. int error_mode;
  142. t_outlet* rj_barkflux_accum; /* m: place for outlet */
  143. } t_rj_barkflux_accum;
  144. /* this is the actual performance routine which acts on the samples.
  145. It's called with a single pointer "w" which is our location in the
  146. DSP call list. We return a new "w" which will point to the next item
  147. after us. Meanwhile, w[0] is just a pointer to dsp-perform itself
  148. (no use to us), w[1] and w[2] are the input and output vector locations,
  149. and w[3] is the number of points to calculate. */
  150. static t_int *rj_barkflux_accum_perform(t_int *w)
  151. {
  152. t_rj_barkflux_accum *x = (t_rj_barkflux_accum *)(w[1]);
  153. t_float *in = (t_float *)(w[2]);
  154. int size=(int)(w[3]);
  155. int ifilter;
  156. float tmp_err, err=0.f;
  157. float st_sum=0.f, lt_sum=0.f;
  158. if (size>=WINSIZE){
  159. // compute filterbank energies
  160. computeFilterEnergies(x->fb, in);
  161. // compute accumulation parameters - short term
  162. if (x->st_buffercnt< x->st_buffersize-1) x->st_buffercnt++;
  163. float st_before_weight= (float) x->st_buffercnt / ((float) x->st_buffercnt +1.f);
  164. float st_after_weight= 1.f / ((float) x->st_buffercnt +1.f);
  165. // compute accumulation parameters - long term
  166. if (x->lt_buffercnt< x->lt_buffersize-1) x->lt_buffercnt++;
  167. float lt_before_weight= (float) x->lt_buffercnt / ((float) x->lt_buffercnt +1.f);
  168. float lt_after_weight= 1.f / ((float) x->lt_buffercnt +1.f);
  169. // actualize average st and lt spectral distribution
  170. for (ifilter=0; ifilter<x->fb->nFilters; ifilter++)
  171. {
  172. x->st_spectrum[ifilter]=x->st_spectrum[ifilter]*st_before_weight + x->fb->bankEn[ifilter]*st_after_weight;
  173. x->lt_spectrum[ifilter]=x->lt_spectrum[ifilter]*lt_before_weight + x->fb->bankEn[ifilter]*lt_after_weight;
  174. st_sum+=x->st_spectrum[ifilter];
  175. lt_sum+=x->lt_spectrum[ifilter];
  176. if(x->error_mode==0)
  177. {
  178. tmp_err=x->st_spectrum[ifilter]-x->lt_spectrum[ifilter];
  179. err+=tmp_err*tmp_err;
  180. }
  181. }
  182. if(x->error_mode==1)
  183. {
  184. // getting normalized st and lt
  185. for (ifilter=0; ifilter<x->fb->nFilters; ifilter++)
  186. {
  187. x->st_spectrum_norm[ifilter]=x->st_spectrum[ifilter];
  188. x->lt_spectrum_norm[ifilter]=x->lt_spectrum[ifilter];
  189. }
  190. //computing kl
  191. for (ifilter=0; ifilter<x->fb->nFilters; ifilter++)
  192. {
  193. err+=x->st_spectrum_norm[ifilter]*log(x->st_spectrum_norm[ifilter]/x->lt_spectrum_norm[ifilter]);
  194. }
  195. }
  196. outlet_float(x->rj_barkflux_accum, err);
  197. }
  198. else post("rj_barkflux_accum: input buffer too small. expected spectrum size is %d, so block size should be",WINSIZE,(WINSIZE-1)*2);
  199. return (w+4);
  200. }
  201. /* called to start DSP. Here we call Pd back to add our perform
  202. routine to a linear callback list which Pd in turn calls to grind
  203. out the samples. */
  204. static void rj_barkflux_accum_dsp(t_rj_barkflux_accum *x, t_signal **sp)
  205. {
  206. dsp_add(rj_barkflux_accum_perform, 3, x, sp[0]->s_vec, sp[0]->s_n);
  207. }
  208. void accum_set_st(t_rj_barkflux_accum *x, t_floatarg g)
  209. {
  210. post("short term duration fixed to %f", g);
  211. x->st_duration=g;
  212. // determining st buffer size
  213. x->st_buffersize= (int) ((x->st_duration*x->samplerate)/x->blocksize);
  214. x->st_buffercnt=0;
  215. post("short term number of frames %d", x->st_buffersize);
  216. }
  217. void accum_set_lt(t_rj_barkflux_accum *x, t_floatarg g)
  218. {
  219. post("long term duration fixed to %f", g);
  220. x->lt_duration=g;
  221. // determining st buffer size
  222. x->lt_buffersize= (int) ((x->lt_duration*x->samplerate)/x->blocksize);
  223. x->lt_buffercnt=0;
  224. post("long term number of frames %d", x->lt_buffersize);
  225. }
  226. static void *rj_barkflux_accum_new(void)
  227. {
  228. int ifilter;
  229. t_rj_barkflux_accum *x = (t_rj_barkflux_accum *)pd_new(rj_barkflux_accum_class);
  230. x->rj_barkflux_accum=outlet_new(&x->x_obj, &s_float);
  231. x->x_f = 0;
  232. inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("st"), gensym("float"));
  233. inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("lt"), gensym("float"));
  234. x->st_duration=5;
  235. x->lt_duration=30;
  236. x->blocksize=1024;
  237. x->samplerate=sys_getsr();
  238. // determining st buffer size
  239. x->st_buffersize= (int) ((x->st_duration*x->samplerate)/x->blocksize);
  240. x->st_buffercnt=0;
  241. // determining lt buffer size
  242. x->lt_buffersize= (int) ((x->lt_duration*x->samplerate)/x->blocksize);
  243. x->lt_buffercnt=0;
  244. // filterbank alloc and init
  245. x->fb=allocFilterbank(NFILTERS);
  246. initFilterbank(x->fb, NFILTERS, WINSIZE, sys_getsr(), LF, HF);
  247. // buffers for st and lt spectrum
  248. x->st_spectrum=malloc(NFILTERS*sizeof(float));
  249. x->lt_spectrum=malloc(NFILTERS*sizeof(float));
  250. for (ifilter=0; ifilter<NFILTERS; ifilter++)
  251. {
  252. x->st_spectrum[ifilter]=0;
  253. x->lt_spectrum[ifilter]=0;
  254. }
  255. x->st_spectrum_norm=malloc(NFILTERS*sizeof(float));
  256. x->lt_spectrum_norm=malloc(NFILTERS*sizeof(float));
  257. // error mode
  258. // 0: rms
  259. // 1: kl divergence
  260. x->error_mode=0;
  261. post("rj_barkflux_accum: init ok");
  262. return (x);
  263. }
  264. static void rj_barkflux_accum_free(t_rj_barkflux_accum *x) {
  265. deleteFilterbank(x->fb);
  266. free(x->st_spectrum);
  267. free(x->lt_spectrum);
  268. free(x->st_spectrum_norm);
  269. free(x->lt_spectrum_norm);
  270. }
  271. /* this routine, which must have exactly this name (with the "~" replaced
  272. by "_tilde) is called when the code is first loaded, and tells Pd how
  273. to build the "class". */
  274. void rj_barkflux_accum_tilde_setup(void)
  275. {
  276. rj_barkflux_accum_class = class_new(gensym("rj_barkflux_accum~"), (t_newmethod)rj_barkflux_accum_new, (t_method)rj_barkflux_accum_free,
  277. sizeof(t_rj_barkflux_accum), 0, A_DEFFLOAT, 0);
  278. // gimme init format ... for later
  279. //rj_barkflux_accum_class = class_new(gensym("rj_barkflux_accum~"), (t_newmethod)rj_barkflux_accum_new, (t_method)rj_barkflux_accum_free,
  280. // sizeof(t_rj_barkflux_accum), 0, A_GIMME, 0);
  281. /* this is magic to declare that the leftmost, "main" inlet
  282. takes signals; other signal inlets are done differently... */
  283. CLASS_MAINSIGNALIN(rj_barkflux_accum_class, t_rj_barkflux_accum, x_f);
  284. /* here we tell Pd about the "dsp" method, which is called back
  285. when DSP is turned on. */
  286. class_addmethod(rj_barkflux_accum_class, (t_method)rj_barkflux_accum_dsp, gensym("dsp"), 0);
  287. class_addmethod(rj_barkflux_accum_class, (t_method)accum_set_st, gensym("st"), A_FLOAT, 0);
  288. class_addmethod(rj_barkflux_accum_class, (t_method)accum_set_lt, gensym("lt"), A_FLOAT, 0);
  289. post("rj_barkflux_accum version 0.1");
  290. }