libcm is a C development framework with an emphasis on audio signal processing applications.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

cmPickup.c 24KB

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