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.

cmAudioFileMgr.c 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  1. #include "cmGlobal.h"
  2. #include "cmFloatTypes.h"
  3. #include "cmRpt.h"
  4. #include "cmErr.h"
  5. #include "cmCtx.h"
  6. #include "cmMem.h"
  7. #include "cmMallocDebug.h"
  8. #include "cmAudioFile.h"
  9. #include "cmVectOpsTemplateMain.h"
  10. #include "cmAudioFileMgr.h"
  11. struct cmAfm_str;
  12. typedef struct
  13. {
  14. cmSample_t* minV; // minV[summN]
  15. cmSample_t* maxV; // maxV[summN]
  16. unsigned summN; // lenght of minV[] and maxV[]
  17. } cmAfmSummary_t;
  18. typedef struct cmAfmFile_str
  19. {
  20. unsigned id;
  21. cmAudioFileH_t afH;
  22. cmAudioFileInfo_t afInfo;
  23. unsigned smpPerSummPt;
  24. cmAfmSummary_t* summArray; // summArray[ afInfo.chCnt ]
  25. cmSample_t* summMem; // memory used by summArray[] vectors
  26. struct cmAfm_str* p;
  27. struct cmAfmFile_str* next;
  28. struct cmAfmFile_str* prev;
  29. } cmAfmFile_t;
  30. typedef struct cmAfm_str
  31. {
  32. cmErr_t err;
  33. cmAfmFile_t* list;
  34. } cmAfm_t;
  35. cmAfmH_t cmAfmNullHandle = cmSTATIC_NULL_HANDLE;
  36. cmAfmFileH_t cmAfmFileNullHandle = cmSTATIC_NULL_HANDLE;
  37. cmAfm_t* _cmAfmHandleToPtr( cmAfmH_t h )
  38. {
  39. cmAfm_t* p = (cmAfm_t*)h.h;
  40. assert(p!=NULL);
  41. return p;
  42. }
  43. cmAfmFile_t* _cmAfmFileHandleToPtr( cmAfmFileH_t fh )
  44. {
  45. cmAfmFile_t* fp = (cmAfmFile_t*)fh.h;
  46. assert(fp!=NULL);
  47. return fp;
  48. }
  49. cmAfmRC_t _cmAfmFileClose( cmAfmFile_t* fp )
  50. {
  51. cmAfmRC_t rc = kOkAfmRC;
  52. if( cmAudioFileIsValid( fp->afH ) )
  53. if( cmAudioFileDelete( &fp->afH) != kOkAfRC )
  54. return cmErrMsg(&fp->p->err,kAudioFileFailAfmRC,"Audio file close failed on '%s'.",cmStringNullGuard(cmAudioFileName(fp->afH)));
  55. if( fp->next != NULL )
  56. fp->next->prev = fp->prev;
  57. if( fp->prev != NULL )
  58. fp->prev->next = fp->next;
  59. if( fp->p->list == fp )
  60. fp->p->list = fp->next;
  61. cmMemFree(fp->summArray);
  62. cmMemFree(fp->summMem);
  63. cmMemFree(fp);
  64. return rc;
  65. }
  66. cmAfmRC_t cmAfmFileOpen( cmAfmH_t h, cmAfmFileH_t* fhp, const cmChar_t* audioFn, unsigned id, cmAudioFileInfo_t* afInfo )
  67. {
  68. cmAfmRC_t rc;
  69. cmRC_t afRC;
  70. if((rc = cmAfmFileClose(fhp)) != kOkAfmRC )
  71. return rc;
  72. cmAfmFile_t* fp = cmMemAllocZ(cmAfmFile_t,1);
  73. fp->p = _cmAfmHandleToPtr(h);
  74. // open the audio file
  75. if( cmAudioFileIsValid(fp->afH = cmAudioFileNewOpen(audioFn, &fp->afInfo, &afRC, fp->p->err.rpt )) == false )
  76. {
  77. rc = cmErrMsg(&fp->p->err,kAudioFileFailAfmRC,"The audio file '%s' could not be opened.",cmStringNullGuard(audioFn));
  78. goto errLabel;
  79. }
  80. // prepend the new file to the mgr's file list
  81. if( fp->p->list != NULL )
  82. fp->p->list->prev = fp;
  83. fp->next = fp->p->list;
  84. fp->p->list = fp;
  85. fp->id = id;
  86. fhp->h = fp;
  87. if( afInfo != NULL )
  88. *afInfo = fp->afInfo;
  89. errLabel:
  90. if( rc != kOkAfmRC )
  91. _cmAfmFileClose(fp);
  92. return rc;
  93. }
  94. cmAfmRC_t cmAfmFileClose( cmAfmFileH_t* fhp )
  95. {
  96. cmAfmRC_t rc = kOkAfmRC;
  97. if( fhp==NULL || cmAfmFileIsValid(*fhp)==false)
  98. return rc;
  99. cmAfmFile_t* fp = _cmAfmFileHandleToPtr( *fhp );
  100. if((rc = _cmAfmFileClose(fp)) != kOkAfmRC )
  101. return rc;
  102. fhp->h = NULL;
  103. return rc;
  104. }
  105. bool cmAfmFileIsValid( cmAfmFileH_t fh )
  106. { return fh.h != NULL; }
  107. unsigned cmAfmFileId( cmAfmFileH_t fh )
  108. {
  109. cmAfmFile_t* fp = _cmAfmFileHandleToPtr( fh );
  110. return fp->id;
  111. }
  112. cmAudioFileH_t cmAfmFileHandle( cmAfmFileH_t fh )
  113. {
  114. cmAfmFile_t* fp = _cmAfmFileHandleToPtr( fh );
  115. return fp->afH;
  116. }
  117. const cmAudioFileInfo_t* cmAfmFileInfo( cmAfmFileH_t fh )
  118. {
  119. cmAfmFile_t* fp = _cmAfmFileHandleToPtr( fh );
  120. return &fp->afInfo;
  121. }
  122. cmAfmRC_t cmAfmFileSummarize( cmAfmFileH_t fh, unsigned smpPerSummPt )
  123. {
  124. cmAfmFile_t* fp = _cmAfmFileHandleToPtr(fh);
  125. cmAfmRC_t rc = kOkAfmRC;
  126. unsigned chCnt = fp->afInfo.chCnt;
  127. // summary points per channel per vector
  128. unsigned summN = (unsigned)ceil((double)fp->afInfo.frameCnt / smpPerSummPt );
  129. // total summary points in all channels and vectors
  130. unsigned n = chCnt*2*summN;
  131. // Calc the number of summary points per audio file read
  132. unsigned ptsPerRd = cmMax(1,cmMax(smpPerSummPt,8192) / smpPerSummPt);
  133. // Calc the number samples per audio file read as an integer multiple of ptsPerRd.
  134. unsigned frmCnt = ptsPerRd * smpPerSummPt;
  135. unsigned actualFrmCnt = 0;
  136. cmSample_t* chBuf[ chCnt ];
  137. cmSample_t buf[ frmCnt * chCnt ];
  138. unsigned i;
  139. // allocate the summary record array
  140. if( fp->summArray == NULL )
  141. fp->summArray = cmMemAllocZ( cmAfmSummary_t, chCnt );
  142. // allocate the summary vector memory for all channels
  143. fp->summMem = cmMemResizeZ( cmSample_t, fp->summMem, n);
  144. fp->smpPerSummPt = smpPerSummPt;
  145. // setup the summary record array and audio file read buffer
  146. for(i=0; i<chCnt; ++i)
  147. {
  148. // assign memory to the summary vectors
  149. fp->summArray[i].minV = fp->summMem + i * summN * 2;
  150. fp->summArray[i].maxV = fp->summArray[i].minV + summN;
  151. fp->summArray[i].summN = summN;
  152. // setup the audio file reading channel buffer
  153. chBuf[i] = buf + (i*frmCnt);
  154. }
  155. // read the entire file and calculate the summary vectors
  156. i = 0;
  157. do
  158. {
  159. unsigned chIdx = 0;
  160. unsigned j,k;
  161. // read the next frmCnt samples from the
  162. if( cmAudioFileReadSample(fp->afH, frmCnt, chIdx, chCnt, chBuf, &actualFrmCnt ) != kOkAfRC )
  163. {
  164. rc = cmErrMsg(&fp->p->err,kAudioFileFailAfmRC,"Audio file read failed on '%s'.",cmStringNullGuard(cmAudioFileName(fp->afH)));
  165. goto errLabel;
  166. }
  167. // for each summary point
  168. for(k=0; k<actualFrmCnt && i<summN; k+=smpPerSummPt,++i)
  169. {
  170. // cnt of samples in this summary report
  171. unsigned m = cmMin(smpPerSummPt,actualFrmCnt-k);
  172. // for each channel
  173. for(j=0; j<chCnt; ++j)
  174. {
  175. fp->summArray[j].minV[i] = cmVOS_Min(chBuf[j]+k,m,1);
  176. fp->summArray[j].maxV[i] = cmVOS_Max(chBuf[j]+k,m,1);
  177. }
  178. }
  179. }while( i<summN && actualFrmCnt==frmCnt );
  180. errLabel:
  181. return rc;
  182. }
  183. // Downsample the summary data to produce the output.
  184. // There must be 1 or more summary points per output point.
  185. cmAfmRC_t _cmAfmFileGetDownSummary(
  186. cmAfmFile_t* fp,
  187. unsigned chIdx,
  188. unsigned begSmpIdx,
  189. unsigned smpCnt,
  190. cmSample_t* minV,
  191. cmSample_t* maxV,
  192. unsigned outCnt )
  193. {
  194. assert( smpCnt >= outCnt );
  195. double smpPerOut = (double)smpCnt/outCnt;
  196. double summPerOut = smpPerOut/fp->smpPerSummPt;
  197. unsigned i;
  198. for(i=0; i<outCnt; ++i)
  199. {
  200. double fsbi = (begSmpIdx + (i*smpPerOut)) / fp->smpPerSummPt; // starting summary pt index
  201. double fsei = fsbi + summPerOut; // endiing summary pt index
  202. unsigned si = (unsigned)floor(fsbi);
  203. unsigned sn = (unsigned)floor(fsei - fsbi + 1);
  204. if( si > fp->summArray[chIdx].summN )
  205. {
  206. minV[i] = 0;
  207. maxV[i] = 0;
  208. }
  209. else
  210. {
  211. if( si + sn > fp->summArray[chIdx].summN )
  212. sn = fp->summArray[chIdx].summN - si;
  213. if( sn == 0 )
  214. {
  215. minV[i] = 0;
  216. maxV[i] = 0;
  217. }
  218. else
  219. {
  220. minV[i] = cmVOS_Min(fp->summArray[chIdx].minV+si,sn,1);
  221. maxV[i] = cmVOS_Max(fp->summArray[chIdx].maxV+si,sn,1);
  222. }
  223. }
  224. }
  225. return kOkAfmRC;
  226. }
  227. // Downsample the audio data to produce the output.
  228. cmAfmRC_t _cmAfmFileGetDownAudio(
  229. cmAfmFile_t* fp,
  230. unsigned chIdx,
  231. unsigned begSmpIdx,
  232. unsigned smpCnt,
  233. cmSample_t* minV,
  234. cmSample_t* maxV,
  235. unsigned outCnt )
  236. {
  237. assert( smpCnt >= outCnt );
  238. cmAfmRC_t rc = kOkAfmRC;
  239. unsigned actualFrmCnt = 0;
  240. unsigned chCnt = 1;
  241. unsigned i;
  242. cmSample_t buf[ smpCnt ];
  243. cmSample_t* chBuf[] = { buf };
  244. // seek to the read location
  245. if( cmAudioFileSeek( fp->afH, begSmpIdx ) != kOkAfRC )
  246. {
  247. rc = cmErrMsg(&fp->p->err,kAudioFileFailAfmRC,"Audio file seek failed on '%s'.",cmStringNullGuard(cmAudioFileName(fp->afH)));
  248. goto errLabel;
  249. }
  250. // read 'smpCnt' samples into chBuf[][]
  251. if( cmAudioFileReadSample(fp->afH, smpCnt, chIdx, chCnt, chBuf, &actualFrmCnt ) != kOkAfRC )
  252. {
  253. rc = cmErrMsg(&fp->p->err,kAudioFileFailAfmRC,"Audio file read failed on '%s' durnig upsample.",cmStringNullGuard(cmAudioFileName(fp->afH)));
  254. goto errLabel;
  255. }
  256. double smpPerOut = (double)smpCnt/outCnt;
  257. for(i=0; i<outCnt; ++i)
  258. {
  259. double fsbi = i*smpPerOut;
  260. double fsei = fsbi + smpPerOut;
  261. unsigned si = (unsigned)floor(fsbi);
  262. unsigned sn = (unsigned)floor(fsei - fsbi + 1);
  263. if( si > smpCnt )
  264. {
  265. minV[i] = 0;
  266. maxV[i] = 0;
  267. }
  268. if( si + sn > smpCnt )
  269. sn = smpCnt - si;
  270. minV[i] = cmVOS_Min(chBuf[chIdx]+si,sn,1);
  271. maxV[i] = cmVOS_Max(chBuf[chIdx]+si,sn,1);
  272. }
  273. errLabel:
  274. return rc;
  275. }
  276. // If there is one or less summary points per output
  277. cmAfmRC_t _cmAfmFileGetUpSummary(
  278. cmAfmFile_t* fp,
  279. unsigned chIdx,
  280. unsigned begSmpIdx,
  281. unsigned smpCnt,
  282. cmSample_t* minV,
  283. cmSample_t* maxV,
  284. unsigned outCnt )
  285. {
  286. assert( outCnt >= smpCnt );
  287. cmAfmRC_t rc = kOkAfmRC;
  288. unsigned actualFrmCnt = 0;
  289. unsigned chCnt = 1;
  290. unsigned i;
  291. cmSample_t buf[ smpCnt ];
  292. cmSample_t* chBuf[] = { buf };
  293. if( cmAudioFileSeek( fp->afH, begSmpIdx ) != kOkAfRC )
  294. {
  295. rc = cmErrMsg(&fp->p->err,kAudioFileFailAfmRC,"Audio file seek failed on '%s'.",cmStringNullGuard(cmAudioFileName(fp->afH)));
  296. goto errLabel;
  297. }
  298. if( cmAudioFileReadSample(fp->afH, smpCnt, chIdx, chCnt, chBuf, &actualFrmCnt ) != kOkAfRC )
  299. {
  300. rc = cmErrMsg(&fp->p->err,kAudioFileFailAfmRC,"Audio file read failed on '%s' durnig upsample.",cmStringNullGuard(cmAudioFileName(fp->afH)));
  301. goto errLabel;
  302. }
  303. for(i=0; i<outCnt; ++i)
  304. {
  305. unsigned si = cmMin(smpCnt-1, (unsigned)floor(i * smpCnt / outCnt));
  306. cmSample_t v = buf[si];
  307. minV[i] = v;
  308. maxV[i] = v;
  309. }
  310. errLabel:
  311. return rc;
  312. }
  313. cmAfmRC_t cmAfmFileGetSummary( cmAfmFileH_t fh, unsigned chIdx, unsigned begSmpIdx, unsigned smpCnt, cmSample_t* minV, cmSample_t* maxV, unsigned outCnt )
  314. {
  315. cmAfmRC_t rc = kOkAfmRC;
  316. cmAfmFile_t* fp = _cmAfmFileHandleToPtr(fh);
  317. double maxHiResDurSecs = 20.0;
  318. if( smpCnt <= outCnt )
  319. rc = _cmAfmFileGetUpSummary( fp, chIdx, begSmpIdx, smpCnt, minV, maxV, outCnt );
  320. else
  321. {
  322. if( smpCnt/fp->afInfo.srate < maxHiResDurSecs )
  323. rc = _cmAfmFileGetDownAudio( fp, chIdx, begSmpIdx, smpCnt, minV, maxV, outCnt );
  324. else
  325. rc = _cmAfmFileGetDownSummary( fp, chIdx, begSmpIdx, smpCnt, minV, maxV, outCnt );
  326. }
  327. return rc;
  328. }
  329. //----------------------------------------------------------------------------
  330. // Audio File Manager
  331. //----------------------------------------------------------------------------
  332. cmAfmRC_t _cmAfmDestroy( cmAfm_t* p )
  333. {
  334. cmAfmRC_t rc = kOkAfmRC;
  335. while( p->list != NULL )
  336. {
  337. if((rc = _cmAfmFileClose(p->list)) != kOkAfmRC )
  338. goto errLabel;
  339. }
  340. cmMemFree(p);
  341. errLabel:
  342. return rc;
  343. }
  344. cmAfmRC_t cmAfmCreate( cmCtx_t* ctx, cmAfmH_t* hp )
  345. {
  346. cmAfmRC_t rc;
  347. if((rc = cmAfmDestroy(hp)) != kOkAfmRC )
  348. return rc;
  349. cmAfm_t* p = cmMemAllocZ(cmAfm_t,1);
  350. cmErrSetup(&p->err,&ctx->rpt,"Audio File Mgr");
  351. hp->h = p;
  352. return rc;
  353. }
  354. cmAfmRC_t cmAfmDestroy( cmAfmH_t* hp )
  355. {
  356. cmAfmRC_t rc = kOkAfmRC;
  357. if( hp==NULL || cmAfmIsValid(*hp)==false)
  358. return rc;
  359. cmAfm_t* p = _cmAfmHandleToPtr(*hp);
  360. if((rc = _cmAfmDestroy(p)) != kOkAfmRC )
  361. return rc;
  362. hp->h = NULL;
  363. return rc;
  364. }
  365. bool cmAfmIsValid( cmAfmH_t h )
  366. { return h.h != NULL; }
  367. cmAfmFileH_t cmAfmIdToHandle( cmAfmH_t h, unsigned fileId )
  368. {
  369. cmAfm_t* p = _cmAfmHandleToPtr(h);
  370. cmAfmFile_t* fp = p->list;
  371. cmAfmFileH_t fh = cmAfmFileNullHandle;
  372. for(; fp!=NULL; fp=fp->next)
  373. if( fp->id == fileId )
  374. {
  375. fh.h = fp;
  376. break;
  377. }
  378. return fh;
  379. }