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 24KB

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