libcm is a C development framework with an emphasis on audio signal processing applications.
Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

cmPickup.c 23KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876
  1. #include "cmPrefix.h"
  2. #include "cmGlobal.h"
  3. #include "cmFloatTypes.h"
  4. #include "cmComplexTypes.h"
  5. #include "cmRpt.h"
  6. #include "cmErr.h"
  7. #include "cmCtx.h"
  8. #include "cmMem.h"
  9. #include "cmMallocDebug.h"
  10. #include "cmLinkedHeap.h"
  11. #include "cmSymTbl.h"
  12. #include "cmJson.h"
  13. #include "cmMidi.h"
  14. #include "cmAudioFile.h"
  15. #include "cmFile.h"
  16. #include "cmFileSys.h"
  17. #include "cmProcObj.h"
  18. #include "cmProcTemplate.h"
  19. #include "cmVectOpsTemplateMain.h"
  20. #include "cmProc.h"
  21. #include "cmProc2.h"
  22. #include "cmProc3.h"
  23. #include "cmPickup.h"
  24. #include "cmAudLabelFile.h"
  25. enum
  26. {
  27. kRmsPuId,
  28. kMedPuId,
  29. kDifPuId,
  30. kAvgPuId,
  31. kOnsPuId,
  32. kFltPuId,
  33. kSupPuId,
  34. kAtkPuId,
  35. kRlsPuId,
  36. kSegPuId,
  37. kPuCnt
  38. };
  39. typedef struct
  40. {
  41. cmErr_t err;
  42. unsigned chCnt;
  43. cmPuCh_t* chArray;
  44. cmCtx_t ctx; // stored ctx used by cmAudLabelFileAllocOpen()
  45. // cmProc objects
  46. cmCtx* ctxp;
  47. cmAudioFileRd* afrp;
  48. cmShiftBuf* sbp;
  49. cmGateDetect2* gdp;
  50. cmBinMtxFile_t* mfp;
  51. const cmChar_t* inAudFn;
  52. const cmChar_t* inLabelFn;
  53. const cmChar_t* outMtx0Fn;
  54. const cmChar_t* outMtx1Fn;
  55. const cmChar_t* outAudFn;
  56. cmReal_t hopMs;
  57. cmGateDetectParams gd0Args;
  58. cmGateDetectParams gd1Args;
  59. } cmPu_t;
  60. cmPuH_t cmPuNullHandle = cmSTATIC_NULL_HANDLE;
  61. cmPu_t* _cmPuHandleToPtr( cmPuH_t h )
  62. {
  63. cmPu_t* p = (cmPu_t*)h.h;
  64. assert(p!=NULL);
  65. return p;
  66. }
  67. cmPuRC_t cmPuAlloc( cmCtx_t* ctx, cmPuH_t* hp )
  68. {
  69. cmPuRC_t rc;
  70. if((rc = cmPuFree(hp)) != kOkPuRC )
  71. return rc;
  72. cmPu_t* p = cmMemAllocZ(cmPu_t,1);
  73. cmErrSetup(&p->err,&ctx->rpt,"Pickup");
  74. p->ctx = *ctx;
  75. hp->h = p;
  76. return rc;
  77. }
  78. cmPuRC_t cmPuFree( cmPuH_t* hp )
  79. {
  80. if( hp == NULL || cmPuIsValid(*hp) == false )
  81. return kOkPuRC;
  82. cmPu_t* p = _cmPuHandleToPtr(*hp);
  83. cmMemPtrFree(&p->chArray);
  84. cmMemPtrFree(&p);
  85. hp->h = NULL;
  86. return kOkPuRC;
  87. }
  88. bool cmPuIsValid( cmPuH_t h )
  89. { return h.h != NULL; }
  90. cmPuRC_t _cmPuReadLabelsAndCreateArray( cmPu_t* p, const cmChar_t* labelFn, cmReal_t srate )
  91. {
  92. cmPuRC_t rc = kOkPuRC;
  93. cmAlfH_t h = cmAlfNullHandle;
  94. unsigned i;
  95. if( cmAudLabelFileAllocOpen(&p->ctx, &h, labelFn) != kOkAlfRC )
  96. return cmErrMsg(&p->err,kAlfFileFailPuRC,"The auto-tune audio label file open failed on '%s'",cmStringNullGuard(labelFn));
  97. if((p->chCnt = cmAudLabelFileCount(h)) == 0 )
  98. {
  99. rc = cmErrMsg(&p->err,kAlfFileFailPuRC,"The auto-tune audio label file '%s' does not contain any segment labels.",cmStringNullGuard(labelFn));
  100. goto errLabel;
  101. }
  102. p->chArray = cmMemResizeZ(cmPuCh_t,p->chArray,p->chCnt);
  103. for(i=0; i<p->chCnt; ++i)
  104. {
  105. const cmAlfLabel_t* lp;
  106. if(( lp = cmAudLabelFileLabel(h,i)) == NULL )
  107. {
  108. rc = cmErrMsg(&p->err,kAlfFileFailPuRC,"The auto-tune label in '%s' at row %i could not be read.",cmStringNullGuard(labelFn),i+1);
  109. goto errLabel;
  110. }
  111. p->chArray[i].begSmpIdx = floor(srate * lp->begSecs);
  112. p->chArray[i].endSmpIdx = p->chArray[i].begSmpIdx; // default the segment to have 0 length.
  113. }
  114. errLabel:
  115. if( cmAudLabelFileFree(&h) != kOkAlfRC )
  116. rc = cmErrMsg(&p->err,kAlfFileFailPuRC,"The auto-tune label file close failed.");
  117. return rc;
  118. }
  119. cmPuRC_t _cmPuWriteMtxFile(cmPu_t* p, bool segFl )
  120. {
  121. cmPuRC_t rc = kOkPuRC;
  122. cmReal_t outV[ kPuCnt ];
  123. outV[ kRmsPuId ] = p->gdp->rms;
  124. outV[ kMedPuId ] = p->gdp->med;
  125. outV[ kDifPuId ] = p->gdp->dif;
  126. outV[ kAvgPuId ] = p->gdp->avg;
  127. outV[ kOnsPuId ] = p->gdp->ons;
  128. outV[ kFltPuId ] = p->gdp->flt;
  129. outV[ kSupPuId ] = p->gdp->sup;
  130. outV[ kAtkPuId ] = p->gdp->onFl;
  131. outV[ kRlsPuId ] = p->gdp->offFl;
  132. outV[ kSegPuId ] = segFl;
  133. // write the output file - plot with cmGateDetectPlot.m
  134. if( cmBinMtxFileExecR(p->mfp,outV,kPuCnt) != cmOkRC )
  135. rc = cmErrMsg(&p->err,kProcFailPuRC,"Matrix file write failed.");
  136. return rc;
  137. }
  138. void _cmPuCalcGains( cmPu_t* p )
  139. {
  140. unsigned i;
  141. cmReal_t avg = 0;
  142. if( p->chCnt == 0 )
  143. return;
  144. for(i=0; i<p->chCnt; ++i)
  145. avg += p->chArray[i].gateMaxAvg;
  146. avg /= p->chCnt;
  147. for(i=0; i<p->chCnt; ++i)
  148. {
  149. cmReal_t d = p->chArray[i].gateMaxAvg==0 ? 1.0 : p->chArray[i].gateMaxAvg;
  150. p->chArray[i].gain = avg / d;
  151. }
  152. }
  153. cmPuCh_t* _cmPuIncrCh( cmPu_t* p, cmPuCh_t* chp, unsigned* segSmpIdxPtr )
  154. {
  155. if( *segSmpIdxPtr != p->chArray[0].begSmpIdx )
  156. ++chp;
  157. if( chp >= p->chArray + p->chCnt )
  158. return NULL;
  159. if( chp+1 == p->chArray + p->chCnt )
  160. *segSmpIdxPtr = p->afrp->info.frameCnt;
  161. else
  162. *segSmpIdxPtr = (chp+1)->begSmpIdx;
  163. return chp;
  164. }
  165. cmPuRC_t _cmPuCalcRerunGateDetectors(
  166. cmPu_t* p,
  167. const cmChar_t* outMtxFn,
  168. const cmChar_t* outAudFn,
  169. const cmGateDetectParams* gdArgs,
  170. unsigned procSmpCnt,
  171. unsigned wndSmpCnt,
  172. unsigned hopSmpCnt )
  173. {
  174. cmPuRC_t rc = kOkPuRC;
  175. cmAudioFileWr* afwp = NULL;
  176. unsigned outChCnt = 1;
  177. unsigned outChIdx = 0;
  178. unsigned bitsPerSmp = 16;
  179. unsigned smpIdx = 0;
  180. cmSample_t* smpV = NULL;
  181. // rewind the audio file reader
  182. if( cmAudioFileRdSeek(p->afrp,0) != cmOkRC )
  183. {
  184. cmErrMsg(&p->err,kProcFailPuRC,"Audio file seek failed.");
  185. goto errLabel;
  186. }
  187. // reset the shift buffer
  188. if( cmShiftBufInit( p->sbp, procSmpCnt, wndSmpCnt, hopSmpCnt ) != cmOkRC )
  189. {
  190. cmErrMsg(&p->err,kProcFailPuRC,"Shift buffer reset failed.");
  191. goto errLabel;
  192. }
  193. // reset the gate detector
  194. if( cmGateDetectInit2( p->gdp, procSmpCnt, gdArgs ) != cmOkRC )
  195. {
  196. cmErrMsg(&p->err,kProcFailPuRC,"Gate detector reset failed.");
  197. goto errLabel;
  198. }
  199. // create an new matrix output file
  200. if( cmBinMtxFileInit( p->mfp, outMtxFn ) != cmOkRC )
  201. {
  202. rc = cmErrMsg(&p->err,kProcFailPuRC,"Output matrix file '%s' initialization failed.",cmStringNullGuard(outMtxFn));
  203. goto errLabel;
  204. }
  205. // create an audio output file
  206. if( (afwp = cmAudioFileWrAlloc(p->ctxp, NULL, procSmpCnt, outAudFn, p->afrp->info.srate, outChCnt, bitsPerSmp )) == NULL )
  207. {
  208. rc = cmErrMsg(&p->err,kProcFailPuRC,"Output audio file '%s' initialization failed.",cmStringNullGuard(outAudFn));
  209. goto errLabel;
  210. }
  211. smpV = cmMemAllocZ(cmSample_t,procSmpCnt);
  212. cmPuCh_t* chp = p->chArray;
  213. unsigned segSmpIdx = chp->begSmpIdx;
  214. bool segFl = false;
  215. // for each procSmpCnt samples
  216. for(; cmAudioFileRdRead(p->afrp) != cmEofRC; smpIdx += procSmpCnt )
  217. {
  218. // apply auto-gain to the audio vector
  219. cmVOS_MultVVS(smpV,p->afrp->outN,p->afrp->outV,chp->gain);
  220. // is this a segment boundary
  221. if( smpIdx+procSmpCnt >= p->afrp->info.frameCnt || (smpIdx <= segSmpIdx && segSmpIdx < smpIdx + procSmpCnt) )
  222. {
  223. segFl = true;
  224. if((chp = _cmPuIncrCh(p,chp, &segSmpIdx )) == NULL )
  225. break;
  226. }
  227. // shift the new samples into the shift buffer
  228. while(cmShiftBufExec(p->sbp,smpV,p->afrp->outN))
  229. {
  230. // update the gate detector
  231. cmGateDetectExec2(p->gdp,p->sbp->outV,p->sbp->outN);
  232. if( _cmPuWriteMtxFile(p,segFl) != kOkPuRC )
  233. goto errLabel;
  234. segFl =false;
  235. }
  236. // write the audio output file
  237. if( cmAudioFileWrExec(afwp, outChIdx,smpV,p->afrp->outN ) != cmOkRC )
  238. {
  239. cmErrMsg(&p->err,kProcFailPuRC,"A write failed to the audio output file '%s'.",outAudFn);
  240. goto errLabel;
  241. }
  242. }
  243. errLabel:
  244. cmMemPtrFree(&smpV);
  245. if( cmAudioFileWrFree(&afwp) != cmOkRC )
  246. {
  247. rc = cmErrMsg(&p->err,kProcFailPuRC,"Output audio file '%s' close failed.",cmStringNullGuard(outAudFn));
  248. goto errLabel;
  249. }
  250. return rc;
  251. }
  252. cmPuRC_t cmPuAutoGainCfg(
  253. cmPuH_t h,
  254. const cmChar_t* audioFn,
  255. const cmChar_t* labelFn,
  256. const cmChar_t* outMtx0Fn,
  257. const cmChar_t* outMtx1Fn,
  258. const cmChar_t* outAudFn,
  259. unsigned procSmpCnt,
  260. cmReal_t hopMs,
  261. const cmGateDetectParams* gd0Args,
  262. const cmGateDetectParams* gd1Args )
  263. {
  264. cmPuRC_t rc;
  265. cmPu_t* p = _cmPuHandleToPtr(h);
  266. int smpIdx = 0;
  267. int chIdx = 0;
  268. const cmReal_t rmsMax = 1.0;
  269. cmReal_t minRms = rmsMax;
  270. cmReal_t gateMax = 0;
  271. cmReal_t gateSum = 0;
  272. unsigned gateCnt = 0;
  273. cmPuCh_t* chp = NULL;
  274. bool segFl = false;
  275. unsigned segSmpIdx = cmInvalidIdx;
  276. // create a cmProc context
  277. if((p->ctxp = cmCtxAlloc(NULL, p->err.rpt, cmLHeapNullHandle, cmSymTblNullHandle )) == NULL )
  278. {
  279. rc = cmErrMsg(&p->err,kProcFailPuRC,"Proc context create failed.");
  280. goto errLabel;
  281. }
  282. // create a cmProc audio file reader
  283. if((p->afrp = cmAudioFileRdAlloc(p->ctxp, NULL, procSmpCnt, audioFn, chIdx, 0, cmInvalidIdx)) == NULL )
  284. {
  285. rc = cmErrMsg(&p->err,kProcFailPuRC,"Audio file reader creation failed on '%s'.",cmStringNullGuard(audioFn));
  286. goto errLabel;
  287. }
  288. // given the sample rate calculate the hop and window size in samples
  289. unsigned hopSmpCnt = floor(p->afrp->info.srate * hopMs / 1000);
  290. unsigned wndSmpCnt = hopSmpCnt * gd0Args->medCnt;
  291. // create a shift buffer to maintain the RMS window for the gate detector
  292. if((p->sbp = cmShiftBufAlloc(p->ctxp,NULL,procSmpCnt,wndSmpCnt,hopSmpCnt )) == NULL )
  293. {
  294. rc = cmErrMsg(&p->err,kProcFailPuRC,"Shift buffer create failed.");
  295. goto errLabel;
  296. }
  297. // create a gate detector
  298. if((p->gdp = cmGateDetectAlloc2(p->ctxp,NULL,procSmpCnt,gd0Args)) == NULL )
  299. {
  300. rc = cmErrMsg(&p->err,kProcFailPuRC,"Gate detect create failed.");
  301. goto errLabel;
  302. }
  303. // create an output file to hold the results of the gate detector
  304. if( (p->mfp = cmBinMtxFileAlloc(p->ctxp,NULL,outMtx0Fn)) == NULL )
  305. {
  306. rc = cmErrMsg(&p->err,kProcFailPuRC,"Binary matrix file create failed.");
  307. goto errLabel;
  308. }
  309. // read the label file and create p->chArray
  310. if((rc = _cmPuReadLabelsAndCreateArray(p, labelFn, p->afrp->info.srate)) != kOkPuRC )
  311. goto errLabel;
  312. chp = p->chArray;
  313. segSmpIdx = chp->begSmpIdx;
  314. // for each procSmpCnt samples
  315. for(; cmAudioFileRdRead(p->afrp) != cmEofRC; smpIdx += procSmpCnt )
  316. {
  317. // if this audio frame marks a segment beginning or
  318. // the end-of-audio-file will occur on the next frame
  319. if( smpIdx+procSmpCnt >= p->afrp->info.frameCnt || (smpIdx <= segSmpIdx && segSmpIdx < smpIdx + procSmpCnt) )
  320. {
  321. segFl = true;
  322. // if no ending offset was located then update the gate sum
  323. if( gateMax != 0 )
  324. {
  325. gateSum += gateMax;
  326. gateCnt += 1;
  327. gateMax = 0;
  328. }
  329. // calc the avg max RMS value for this segment
  330. chp->gateMaxAvg = gateCnt == 0 ? 0.0 : gateSum / gateCnt;
  331. gateCnt = 0;
  332. gateSum = 0;
  333. gateMax = 0;
  334. minRms = rmsMax; // force the segment end to be after the segment beginning
  335. if((chp = _cmPuIncrCh(p,chp, &segSmpIdx )) == NULL )
  336. break;
  337. }
  338. // shift the new samples into the shift buffer
  339. while(cmShiftBufExec(p->sbp,p->afrp->outV,p->afrp->outN))
  340. {
  341. // update the gate detector
  342. cmGateDetectExec2(p->gdp,p->sbp->outV,p->sbp->outN);
  343. // write the output matrix file
  344. if( _cmPuWriteMtxFile(p,segFl) != kOkPuRC )
  345. goto errLabel;
  346. segFl = false;
  347. // if this frame is an RMS minimum or onset or offset
  348. // then select it as a possible segment end.
  349. // Note that for onsets this will effectively force the end to
  350. // come after the onset because the onset will not be an energy minimum
  351. // relative to subsequent frames.
  352. if( p->gdp->rms < minRms || p->gdp->onFl || p->gdp->offFl )
  353. {
  354. minRms = p->gdp->rms;
  355. chp->endSmpIdx = smpIdx;
  356. // count onsets
  357. if( p->gdp->onFl )
  358. ++chp->onCnt;
  359. // count offsets
  360. if( p->gdp->offFl )
  361. {
  362. ++chp->offCnt;
  363. // update the gate sum and count
  364. gateSum += gateMax;
  365. gateCnt += 1;
  366. gateMax = 0;
  367. }
  368. }
  369. // track the max RMS value during this gate
  370. if( p->gdp->gateFl && p->gdp->rms > gateMax )
  371. gateMax = p->gdp->rms;
  372. }
  373. }
  374. // calculate the channel gains
  375. if( rc == kOkPuRC )
  376. _cmPuCalcGains(p);
  377. rc = _cmPuCalcRerunGateDetectors(p,outMtx1Fn,outAudFn,gd1Args,procSmpCnt,wndSmpCnt,hopSmpCnt);
  378. p->gd0Args = *gd0Args;
  379. p->gd1Args = *gd1Args;
  380. errLabel:
  381. if( p->mfp != NULL )
  382. cmBinMtxFileFree(&p->mfp);
  383. if( p->gdp != NULL )
  384. cmGateDetectFree2(&p->gdp);
  385. if( p->sbp != NULL )
  386. cmShiftBufFree(&p->sbp);
  387. if( p->afrp != NULL )
  388. cmAudioFileRdFree(&p->afrp);
  389. if( p->ctxp != NULL )
  390. cmCtxFree(&p->ctxp);
  391. return rc;
  392. }
  393. cmPuRC_t cmPuAutoGainExec( cmPuH_t h, const cmChar_t* fileDir, unsigned procSmpCnt )
  394. {
  395. cmPu_t* p = _cmPuHandleToPtr(h);
  396. const cmChar_t* inAudFn = cmFsMakeFn(fileDir, p->inAudFn, NULL, NULL );
  397. const cmChar_t* inLabelFn = cmFsMakeFn(fileDir, p->inLabelFn, NULL, NULL );
  398. const cmChar_t* outMtx0Fn = cmFsMakeFn(fileDir, p->outMtx0Fn, NULL, NULL );
  399. const cmChar_t* outMtx1Fn = cmFsMakeFn(fileDir, p->outMtx1Fn, NULL, NULL );
  400. const cmChar_t* outAudFn = cmFsMakeFn(fileDir, p->outAudFn, NULL, NULL );
  401. cmPuRC_t rc = cmPuAutoGainCfg(h,inAudFn,inLabelFn,outMtx0Fn,outMtx1Fn,outAudFn,procSmpCnt,p->hopMs,&p->gd0Args,&p->gd1Args);
  402. cmFsFreeFn(outAudFn);
  403. cmFsFreeFn(outMtx1Fn);
  404. cmFsFreeFn(outMtx0Fn);
  405. cmFsFreeFn(inLabelFn);
  406. cmFsFreeFn(inAudFn);
  407. return rc;
  408. }
  409. cmPuRC_t cmPuAutoGainCfgFromJson(
  410. cmPuH_t h,
  411. const cmChar_t* cfgDir,
  412. cmJsonH_t jsH,
  413. cmJsonNode_t* onp,
  414. unsigned procSmpCnt )
  415. {
  416. cmPuRC_t rc;
  417. if((rc = cmPuReadJson(h,jsH,onp)) != kOkPuRC )
  418. return rc;
  419. return cmPuAutoGainExec(h,cfgDir,procSmpCnt);
  420. }
  421. void cmPuReport( cmPuH_t h, cmRpt_t* rpt )
  422. {
  423. cmPu_t* p = _cmPuHandleToPtr(h);
  424. unsigned i;
  425. for(i=0; i<p->chCnt; ++i)
  426. {
  427. const cmPuCh_t* chp = p->chArray + i;
  428. cmRptPrintf(rpt,"beg:%i end:%i on:%i off:%i max:%f gain:%f\n",
  429. chp->begSmpIdx, chp->endSmpIdx, chp->onCnt, chp->offCnt, chp->gateMaxAvg, chp->gain );
  430. }
  431. }
  432. cmPuRC_t _cmPuJsonGainRead( cmPu_t* p, cmJsonH_t jsH, cmJsonNode_t* onp, const cmChar_t* label )
  433. {
  434. cmPuRC_t rc = kOkPuRC;
  435. cmJsonNode_t* arp;
  436. // locate the JSON 'gain' array
  437. if(( arp = cmJsonFindValue(jsH,label,onp,kArrayTId)) == NULL )
  438. {
  439. rc = cmErrMsg(&p->err,kJsonFailPuRC,"Unable to locate the JSON array node %s.",cmStringNullGuard(label));
  440. goto errLabel;
  441. }
  442. // get the count of elements in the 'gain' array
  443. unsigned arrCnt = cmJsonChildCount(arp);
  444. cmPuCh_t* arr = NULL;
  445. if( arrCnt > 0 )
  446. {
  447. arr = cmMemAllocZ(cmPuCh_t,arrCnt);
  448. unsigned i;
  449. for(i=0; i<arrCnt; ++i)
  450. {
  451. if( i<p->chCnt )
  452. arr[i] = p->chArray[i];
  453. if( cmJsonRealValue( cmJsonArrayElement(arp,i), &arr[i].gain ) != kOkJsRC )
  454. {
  455. rc = cmErrMsg(&p->err,kJsonFailPuRC,"An error occurred while accessing a JSON 'gain' element.");
  456. goto errLabel;
  457. }
  458. }
  459. }
  460. errLabel:
  461. if( rc != kOkPuRC )
  462. cmMemPtrFree(&arr);
  463. cmMemPtrFree(&p->chArray);
  464. p->chArray = arr;
  465. p->chCnt = arrCnt;
  466. return rc;
  467. }
  468. cmPuRC_t _cmPuJsonGdParmsRead( cmPu_t* p, cmJsonH_t jsH, cmJsonNode_t* onp, const cmChar_t* label, cmGateDetectParams* gdParms )
  469. {
  470. cmPuRC_t rc = kOkPuRC;
  471. cmJsonNode_t* gdp;
  472. const char* errLabelPtr;
  473. if(( gdp = cmJsonFindValue(jsH,label,onp,kObjectTId)) == NULL )
  474. {
  475. rc = cmErrMsg(&p->err,kJsonFailPuRC,"Unable to locate the JSON object node %s.",cmStringNullGuard(label));
  476. goto errLabel;
  477. }
  478. if( cmJsonMemberValues(gdp, &errLabelPtr,
  479. "medCnt", kIntTId, &gdParms->medCnt,
  480. "avgCnt", kIntTId, &gdParms->avgCnt,
  481. "suprCnt", kIntTId, &gdParms->suprCnt,
  482. "offCnt", kIntTId, &gdParms->offCnt,
  483. "suprCoeff", kRealTId, &gdParms->suprCoeff,
  484. "onThreshDb", kRealTId, &gdParms->onThreshDb,
  485. "offThreshDb", kRealTId, &gdParms->offThreshDb,
  486. NULL ) != kOkJsRC )
  487. {
  488. rc = cmErrMsg(&p->err,kJsonFailPuRC,"Gate detect parameter restore failed for '%s'.",cmStringNullGuard(label));
  489. goto errLabel;
  490. }
  491. errLabel:
  492. return rc;
  493. }
  494. cmPuRC_t _cmPuJsonCfgParmsRead(cmPu_t* p, cmJsonH_t jsH, cmJsonNode_t* onp)
  495. {
  496. cmPuRC_t rc = kOkPuRC;
  497. cmJsonNode_t* gdp = onp;
  498. const char* errLabelPtr;
  499. //if(( gdp = cmJsonFindValue(jsH,label,onp,kObjectTId)) == NULL )
  500. //{
  501. // rc = cmErrMsg(&p->err,kJsonFailPuRC,"Unable to locate the JSON object node %s.",cmStringNullGuard(label));
  502. // goto errLabel;
  503. // }
  504. if( cmJsonMemberValues(gdp, &errLabelPtr,
  505. "audioFn", kStringTId, &p->inAudFn,
  506. "labelFn", kStringTId, &p->inLabelFn,
  507. "outMtx0Fn", kStringTId, &p->outMtx0Fn,
  508. "outMtx1Fn", kStringTId, &p->outMtx1Fn,
  509. "outAudFn", kStringTId, &p->outAudFn,
  510. "hopMs", kRealTId, &p->hopMs,
  511. NULL ) != kOkJsRC )
  512. {
  513. rc = cmErrMsg(&p->err,kJsonFailPuRC,"Autotune cfg parameter read failed.");
  514. goto errLabel;
  515. }
  516. errLabel:
  517. return rc;
  518. }
  519. cmPuRC_t cmPuReadJson( cmPuH_t h, cmJsonH_t jsH, cmJsonNode_t* onp )
  520. {
  521. cmPuRC_t rc = kOkPuRC;
  522. cmPu_t* p = _cmPuHandleToPtr(h);
  523. cmJsonNode_t* atp;
  524. if(( atp = cmJsonFindValue(jsH,"cfg",onp,kObjectTId)) == NULL )
  525. {
  526. rc = cmErrMsg(&p->err,kJsonFailPuRC,"The JSON 'autotune' object was not found.");
  527. goto errLabel;
  528. }
  529. if((rc = _cmPuJsonCfgParmsRead(p,jsH,atp)) != kOkPuRC )
  530. goto errLabel;
  531. if((rc = _cmPuJsonGdParmsRead(p,jsH,atp,"gdParms0",&p->gd0Args)) != kOkPuRC )
  532. goto errLabel;
  533. if((rc = _cmPuJsonGdParmsRead(p,jsH,atp,"gdParms1",&p->gd1Args)) != kOkPuRC )
  534. goto errLabel;
  535. if((rc = _cmPuJsonGainRead(p,jsH,atp,"gain")) != kOkPuRC )
  536. goto errLabel;
  537. errLabel:
  538. return rc;
  539. }
  540. unsigned cmPuChannelCount( cmPuH_t h )
  541. {
  542. cmPu_t* p = _cmPuHandleToPtr(h);
  543. return p->chCnt;
  544. }
  545. const cmPuCh_t* cmPuChannel( cmPuH_t h, unsigned chIdx )
  546. {
  547. cmPu_t* p = _cmPuHandleToPtr(h);
  548. assert( chIdx < p->chCnt );
  549. return p->chArray + chIdx;
  550. }
  551. cmPuRC_t _cmPuJsonSetInt( cmPu_t* p, cmJsonH_t jsH, cmJsonNode_t* gdp, const cmChar_t* label, int val )
  552. {
  553. cmPuRC_t rc = kOkPuRC;
  554. if( cmJsonReplacePairInt(jsH, gdp, label, kIntTId | kRealTId, val ) != kOkJsRC )
  555. rc = cmErrMsg(&p->err,kJsonFailPuRC,"Error setting integer JSON field: %s.",cmStringNullGuard(label));
  556. return rc;
  557. }
  558. cmPuRC_t _cmPuJsonSetReal( cmPu_t* p, cmJsonH_t jsH, cmJsonNode_t* gdp, const cmChar_t* label, cmReal_t val )
  559. {
  560. cmPuRC_t rc = kOkPuRC;
  561. if( cmJsonReplacePairReal(jsH, gdp, label, kIntTId | kRealTId, val ) != kOkJsRC )
  562. rc = cmErrMsg(&p->err,kJsonFailPuRC,"Error setting real JSON field: %s.",cmStringNullGuard(label));
  563. return rc;
  564. }
  565. cmPuRC_t _cmPuJsonGdParmsUpdate( cmPu_t* p, cmJsonH_t jsH, cmJsonNode_t* onp, const cmChar_t* label, const cmGateDetectParams* gdParms )
  566. {
  567. cmPuRC_t rc = kOkPuRC;
  568. cmJsonNode_t* gdp;
  569. if(( gdp = cmJsonFindValue(jsH,label,onp,kObjectTId)) == NULL )
  570. {
  571. rc = cmErrMsg(&p->err,kJsonFailPuRC,"Unable to locate the JSON object node %s.",cmStringNullGuard(label));
  572. goto errLabel;
  573. }
  574. _cmPuJsonSetInt( p, jsH, gdp, "medCnt", gdParms->medCnt);
  575. _cmPuJsonSetInt( p, jsH, gdp, "avgCnt", gdParms->avgCnt);
  576. _cmPuJsonSetInt( p, jsH, gdp, "suprCnt", gdParms->suprCnt);
  577. _cmPuJsonSetInt( p, jsH, gdp, "offCnt", gdParms->offCnt);
  578. _cmPuJsonSetReal( p, jsH, gdp, "suprCoeff", gdParms->suprCoeff);
  579. _cmPuJsonSetReal( p, jsH, gdp, "onThreshDb", gdParms->onThreshDb);
  580. _cmPuJsonSetReal( p, jsH, gdp, "offThresDb", gdParms->offThreshDb);
  581. rc = cmErrLastRC(&p->err);
  582. errLabel:
  583. return rc;
  584. }
  585. cmPuRC_t _cmPuJsonGainUpdate( cmPu_t* p, cmJsonH_t jsH, cmJsonNode_t* onp, const cmChar_t* label )
  586. {
  587. cmPuRC_t rc = kOkPuRC;
  588. cmJsonNode_t* arp;
  589. unsigned i;
  590. // locate the JSON 'gain' array
  591. if(( arp = cmJsonFindValue(jsH,label,onp,kArrayTId)) == NULL )
  592. {
  593. rc = cmErrMsg(&p->err,kJsonFailPuRC,"Unable to locate the JSON array node %s.",cmStringNullGuard(label));
  594. goto errLabel;
  595. }
  596. // get the count of elements in the 'gain' array
  597. unsigned arrCnt = cmJsonChildCount(arp);
  598. // update the existing 'gain' array elmements from p->chArray[]
  599. for(i=0; i<arrCnt && i<p->chCnt; ++i)
  600. {
  601. if(cmJsonSetReal( jsH, cmJsonArrayElement(arp,i), p->chArray[i].gain ) != kOkPuRC )
  602. {
  603. rc = cmErrMsg(&p->err,kJsonFailPuRC,"Set JSON 'gain' array elment failed.");
  604. goto errLabel;
  605. }
  606. }
  607. // create new elements if the array was not long enough
  608. for(; i<p->chCnt; ++i)
  609. if( cmJsonCreateReal(jsH,arp,p->chArray[i].gain) != kOkJsRC )
  610. {
  611. rc = cmErrMsg(&p->err,kJsonFailPuRC,"JSON 'gain' element create failed.");
  612. goto errLabel;
  613. }
  614. // remove elements if the array begain with extra elements.
  615. if( arrCnt > p->chCnt )
  616. {
  617. if( cmJsonRemoveNode( jsH, cmJsonArrayElement(arp,i), true ) != kOkJsRC )
  618. {
  619. rc = cmErrMsg(&p->err,kJsonFailPuRC,"JSON 'gain' element removal failed.");
  620. goto errLabel;
  621. }
  622. }
  623. errLabel:
  624. return rc;
  625. }
  626. cmPuRC_t cmPuWriteJson( cmPuH_t h, cmJsonH_t jsH, cmJsonNode_t* onp )
  627. {
  628. cmPuRC_t rc = kOkPuRC;
  629. cmPu_t* p = _cmPuHandleToPtr(h);
  630. cmJsonNode_t* atp;
  631. if(( atp = cmJsonFindValue(jsH,"autoTune",onp,kObjectTId)) == NULL )
  632. {
  633. rc = cmErrMsg(&p->err,kJsonFailPuRC,"The JSON 'autotune' object was not found.");
  634. goto errLabel;
  635. }
  636. if((rc = _cmPuJsonGdParmsUpdate(p,jsH,atp,"gdParms0",&p->gd0Args)) != kOkPuRC )
  637. goto errLabel;
  638. if((rc = _cmPuJsonGdParmsUpdate(p,jsH,atp,"gdParms1",&p->gd1Args)) != kOkPuRC )
  639. goto errLabel;
  640. if((rc = _cmPuJsonGainUpdate(p,jsH,atp,"gain")) != kOkPuRC )
  641. goto errLabel;
  642. errLabel:
  643. return rc;
  644. }
  645. cmPuRC_t cmPuWriteJsonFile( cmPuH_t h, const cmChar_t* jsonFn )
  646. {
  647. cmPuRC_t rc = kOkPuRC;
  648. cmJsonH_t jsH = cmJsonNullHandle;
  649. cmPu_t* p = _cmPuHandleToPtr(h);
  650. // initialize a JSON tree from 'jsonFn'.
  651. if( cmJsonInitializeFromFile(&jsH,jsonFn,&p->ctx) != kOkJsRC )
  652. {
  653. rc = cmErrMsg(&p->err,kJsonFailPuRC,"JSON file initialization failed on '%s'.",cmStringNullGuard(jsonFn));
  654. goto errLabel;
  655. }
  656. // update the 'autoTune' object in the JSON tree
  657. if((rc = cmPuWriteJson(h,jsH,cmJsonRoot(jsH))) != kOkPuRC )
  658. goto errLabel;
  659. // write the JSON tree back to 'jsonFn'.
  660. if( cmJsonWrite(jsH,cmJsonRoot(jsH),jsonFn) != kOkJsRC )
  661. {
  662. rc = cmErrMsg(&p->err,kJsonFailPuRC,"JSON file save failed on '%s'.",cmStringNullGuard(jsonFn));
  663. goto errLabel;
  664. }
  665. errLabel:
  666. // release the JSON tree
  667. if( cmJsonFinalize(&jsH) != kOkJsRC )
  668. rc = cmErrMsg(&p->err,kJsonFailPuRC,"JSON file finalization failed on '%s'.",cmStringNullGuard(jsonFn));
  669. return rc;
  670. }
  671. void cmPuTest(cmCtx_t* ctx)
  672. {
  673. cmPuH_t h = cmPuNullHandle;
  674. cmGateDetectParams gd0Args;
  675. cmGateDetectParams gd1Args;
  676. const cmChar_t* audioFn = "/home/kevin/media/audio/gate_detect/gate_detect0.aif";
  677. const cmChar_t* labelFn = "/home/kevin/media/audio/gate_detect/gate_detect0_labels.txt";
  678. const cmChar_t* outMtx0Fn = "/home/kevin/media/audio/gate_detect/gd0.mtx";
  679. const cmChar_t* outMtx1Fn = "/home/kevin/media/audio/gate_detect/gd1.mtx";
  680. const cmChar_t* outAudFn = "/home/kevin/media/audio/gate_detect/gd_gain0.aif";
  681. const cmChar_t* jsonFn = "/home/kevin/src/kc/src/data/rsrc1.txt";
  682. unsigned procSmpCnt = 64;
  683. cmReal_t hopMs = 10;
  684. gd0Args.medCnt = 5;
  685. gd0Args.avgCnt = 9;
  686. gd0Args.suprCnt = 6;
  687. gd0Args.offCnt = 3;
  688. gd0Args.suprCoeff = 1.4;
  689. gd0Args.onThreshDb = -53.0;
  690. gd0Args.offThreshDb = -80.0;
  691. gd1Args = gd0Args;
  692. gd1Args.onThreshDb = -45;
  693. if( cmPuAlloc(ctx, &h ) != kOkPuRC )
  694. goto errLabel;
  695. if( cmPuAutoGainCfg(h,audioFn,labelFn,outMtx0Fn,outMtx1Fn,outAudFn,procSmpCnt,hopMs,&gd0Args,&gd1Args) == kOkPuRC )
  696. {
  697. cmPuReport(h,&ctx->rpt);
  698. cmPuWriteJsonFile( h, jsonFn );
  699. }
  700. errLabel:
  701. cmPuFree(&h);
  702. }