libcm is a C development framework with an emphasis on audio signal processing applications.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

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. }