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.

cmAudioFileMgr.c 12KB

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