cmtools is a collection of utilities for generating and processing machine readable musical scores based on MusicXML, MIDI and other data files.
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.

audiodev.c 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. #include "cmPrefix.h"
  2. #include "cmGlobal.h"
  3. #include "cmRpt.h"
  4. #include "cmErr.h"
  5. #include "cmCtx.h"
  6. #include "cmMem.h"
  7. #include "cmMallocDebug.h"
  8. #include "cmLinkedHeap.h"
  9. #include "cmFileSys.h"
  10. #include "cmText.h"
  11. #include "cmPgmOpts.h"
  12. #include "cmTime.h"
  13. #include "cmAudioPort.h"
  14. #include "cmApBuf.h" // only needed for cmApBufTest().
  15. #include "cmAudioPortFile.h"
  16. #include "cmAudioAggDev.h"
  17. #include "cmAudioNrtDev.h"
  18. #include "cmFloatTypes.h"
  19. #include "cmAudioFile.h"
  20. #include "cmFile.h"
  21. enum
  22. {
  23. kOkAdRC = cmOkRC,
  24. kAudioPortFailAdRC,
  25. kAudioPortFileFailAdRC,
  26. kAudioPortNrtFailAdRC,
  27. kAudioBufFailAdRC
  28. };
  29. const cmChar_t* poBegHelpStr =
  30. "audiodev Test the real-time audio ports"
  31. "\n"
  32. "audiodev -i<in_dev_index> -o <out_dev_index -s <sample_rate>\n"
  33. "\n"
  34. "All arguments are optional. The default input and output device index is 0.\n"
  35. "\n";
  36. const cmChar_t* poEndHelpStr = "";
  37. /// [cmAudioPortExample]
  38. // See cmApPortTest() below for the main point of entry.
  39. // Data structure used to hold the parameters for cpApPortTest()
  40. // and the user defined data record passed to the host from the
  41. // audio port callback functions.
  42. typedef struct
  43. {
  44. unsigned bufCnt; // 2=double buffering 3=triple buffering
  45. unsigned chIdx; // first test channel
  46. unsigned chCnt; // count of channels to test
  47. unsigned framesPerCycle; // DSP frames per cycle
  48. unsigned bufFrmCnt; // count of DSP frames used by the audio buffer (bufCnt * framesPerCycle)
  49. unsigned bufSmpCnt; // count of samples used by the audio buffer (chCnt * bufFrmCnt)
  50. unsigned inDevIdx; // input device index
  51. unsigned outDevIdx; // output device index
  52. double srate; // audio sample rate
  53. unsigned meterMs; // audio meter buffer length
  54. // param's and state for cmApSynthSine()
  55. unsigned phase; // sine synth phase
  56. double frqHz; // sine synth frequency in Hz
  57. // buffer and state for cmApCopyIn/Out()
  58. cmApSample_t* buf; // buf[bufSmpCnt] - circular interleaved audio buffer
  59. unsigned bufInIdx; // next input buffer index
  60. unsigned bufOutIdx; // next output buffer index
  61. unsigned bufFullCnt; // count of full samples
  62. // debugging log data arrays
  63. unsigned logCnt; // count of elements in log[] and ilong[]
  64. char* log; // log[logCnt]
  65. unsigned* ilog; // ilog[logCnt]
  66. unsigned logIdx; // current log index
  67. unsigned cbCnt; // count the callback
  68. } cmApPortTestRecd;
  69. unsigned _cmGlobalInDevIdx = 0;
  70. unsigned _cmGlobalOutDevIdx = 0;
  71. unsigned _cmGlobalCbCnt = 0;
  72. #define aSrate 48000
  73. #define aFrmN aSrate*10
  74. #define aChN 2
  75. #define abufN aFrmN*aChN
  76. cmApSample_t abuf[ abufN ];
  77. unsigned abufi = 0;
  78. void _abuf_copy_in( cmApAudioPacket_t* pktArray, unsigned pktN )
  79. {
  80. unsigned i,j,k;
  81. for(i=0; i<pktN; ++i)
  82. {
  83. cmApSample_t* sp = (cmApSample_t*)(pktArray[i].audioBytesPtr);
  84. unsigned frmN = pktArray[i].audioFramesCnt;
  85. unsigned chN = cmMin(pktArray[i].chCnt,aChN);
  86. for(j=0; abufi<aFrmN && j<frmN; ++j, ++abufi)
  87. for(k=0; k<chN; ++k)
  88. abuf[ (k*aFrmN) + abufi ] = *sp++;
  89. }
  90. }
  91. void _abuf_write_audio_file(cmCtx_t* ctx )
  92. {
  93. cmApSample_t* sigVV[ ] = { abuf, abuf + aFrmN };
  94. cmAudioFileWriteFileFloat( "/home/kevin/temp/temp.wav", aSrate, 16, aFrmN, 2, sigVV, &ctx->rpt );
  95. }
  96. void _abuf_write_csv_file(cmCtx_t* ctx )
  97. {
  98. cmFileH_t fH = cmFileNullHandle;
  99. unsigned i = 0,j;
  100. cmFileOpen( &fH, "/home/kevin/temp/temp.csv", kWriteFileFl, &ctx->rpt );
  101. for(i=0; i<aFrmN; ++i)
  102. {
  103. for(j=0; j<aChN; ++j)
  104. {
  105. char comma = j==aChN-1 ? ' ':',';
  106. cmFilePrintf(fH, "%f%c",abuf[ aFrmN*j + i ], comma );
  107. }
  108. cmFilePrintf( fH, "\n");
  109. }
  110. cmFileClose(&fH);
  111. }
  112. void _cmApPortCb2( cmApAudioPacket_t* inPktArray, unsigned inPktCnt, cmApAudioPacket_t* outPktArray, unsigned outPktCnt )
  113. {
  114. cmApBufInputToOutput( _cmGlobalInDevIdx, _cmGlobalOutDevIdx );
  115. cmApBufUpdate( inPktArray, inPktCnt, outPktArray, outPktCnt );
  116. if( outPktArray != NULL )
  117. _abuf_copy_in(outPktArray,outPktCnt);
  118. _cmGlobalCbCnt += 1;
  119. }
  120. cmRC_t audio_port_test( cmCtx_t* ctx, cmApPortTestRecd* r, bool runFl )
  121. {
  122. cmRC_t rc = kOkAdRC;
  123. unsigned i = 0;
  124. int srateMult = 0;
  125. cmRpt_t* rpt = &ctx->rpt;
  126. cmApSample_t buf[r->bufSmpCnt];
  127. char log[r->logCnt];
  128. unsigned ilog[r->logCnt];
  129. r->buf = buf;
  130. r->log = log;
  131. r->ilog = ilog;
  132. r->cbCnt = 0;
  133. _cmGlobalInDevIdx = r->inDevIdx;
  134. _cmGlobalOutDevIdx= r->outDevIdx;
  135. cmRptPrintf(rpt,"%s in:%i out:%i chidx:%i chs:%i bufs=%i frm=%i rate=%f\n",runFl?"exec":"rpt",r->inDevIdx,r->outDevIdx,r->chIdx,r->chCnt,r->bufCnt,r->framesPerCycle,r->srate);
  136. if( cmApFileAllocate(rpt) != kOkApRC )
  137. {
  138. rc = cmErrMsg(&ctx->err, kAudioPortFileFailAdRC,"Audio port file allocation failed.");
  139. goto errLabel;
  140. }
  141. // allocate the non-real-time port
  142. if( cmApNrtAllocate(rpt) != kOkApRC )
  143. {
  144. rc = cmErrMsg(&ctx->err, kAudioPortNrtFailAdRC,"Non-real-time system allocation failed.");
  145. goto errLabel;
  146. }
  147. // initialize the audio device interface
  148. if( cmApInitialize(rpt) != kOkApRC )
  149. {
  150. rc = cmErrMsg(&ctx->err, kAudioPortFailAdRC,"Port initialize failed.\n");
  151. goto errLabel;
  152. }
  153. // report the current audio device configuration
  154. for(i=0; i<cmApDeviceCount(); ++i)
  155. {
  156. cmRptPrintf(rpt,"%i [in: chs=%i frames=%i] [out: chs=%i frames=%i] srate:%f %s\n",i,cmApDeviceChannelCount(i,true),cmApDeviceFramesPerCycle(i,true),cmApDeviceChannelCount(i,false),cmApDeviceFramesPerCycle(i,false),cmApDeviceSampleRate(i),cmApDeviceLabel(i));
  157. }
  158. // report the current audio devices using the audio port interface function
  159. cmApReport(rpt);
  160. if( runFl )
  161. {
  162. // initialize the audio buffer
  163. cmApBufInitialize( cmApDeviceCount(), r->meterMs );
  164. // setup the buffer for the output device
  165. cmApBufSetup( r->outDevIdx, r->srate, r->framesPerCycle, r->bufCnt, cmApDeviceChannelCount(r->outDevIdx,true), r->framesPerCycle, cmApDeviceChannelCount(r->outDevIdx,false), r->framesPerCycle, srateMult );
  166. // setup the buffer for the input device
  167. if( r->inDevIdx != r->outDevIdx )
  168. cmApBufSetup( r->inDevIdx, r->srate, r->framesPerCycle, r->bufCnt, cmApDeviceChannelCount(r->inDevIdx,true), r->framesPerCycle, cmApDeviceChannelCount(r->inDevIdx,false), r->framesPerCycle, srateMult );
  169. // setup an output device
  170. if(cmApDeviceSetup(r->outDevIdx,r->srate,r->framesPerCycle,_cmApPortCb2,&r) != kOkApRC )
  171. rc = cmErrMsg(&ctx->err,kAudioPortFailAdRC,"Out audio device setup failed.\n");
  172. else
  173. // setup an input device
  174. if( cmApDeviceSetup(r->inDevIdx,r->srate,r->framesPerCycle,_cmApPortCb2,&r) != kOkApRC )
  175. rc = cmErrMsg(&ctx->err,kAudioPortFailAdRC,"In audio device setup failed.\n");
  176. else
  177. // start the input device
  178. if( cmApDeviceStart(r->inDevIdx) != kOkApRC )
  179. rc = cmErrMsg(&ctx->err,kAudioPortFailAdRC,"In audio device start failed.\n");
  180. else
  181. // start the output device
  182. if( cmApDeviceStart(r->outDevIdx) != kOkApRC )
  183. rc = cmErrMsg(&ctx->err, kAudioPortFailAdRC,"Out audio device start failed.\n");
  184. else
  185. cmRptPrintf(rpt,"Started...");
  186. cmApBufEnableChannel(r->inDevIdx, -1, kEnableApFl | kInApFl );
  187. cmApBufEnableMeter( r->inDevIdx, -1, kEnableApFl | kInApFl );
  188. cmApBufEnableChannel(r->outDevIdx, -1, kEnableApFl | kOutApFl );
  189. cmApBufEnableMeter( r->outDevIdx, -1, kEnableApFl | kOutApFl );
  190. cmRptPrintf(rpt,"q=quit O/o=output tone, I/i=input tone P/p=pass s=buf report\n");
  191. char c;
  192. while((c=getchar()) != 'q')
  193. {
  194. //cmApAlsaDeviceRtReport(rpt,r->outDevIdx);
  195. switch(c)
  196. {
  197. case 'i':
  198. case 'I':
  199. cmApBufEnableTone(r->inDevIdx,-1,kInApFl | (c=='I'?kEnableApFl:0));
  200. break;
  201. case 'o':
  202. case 'O':
  203. cmApBufEnableTone(r->outDevIdx,-1,kOutApFl | (c=='O'?kEnableApFl:0));
  204. break;
  205. case 'p':
  206. case 'P':
  207. cmApBufEnablePass(r->outDevIdx,-1,kOutApFl | (c=='P'?kEnableApFl:0));
  208. break;
  209. case 's':
  210. cmApBufReport(rpt);
  211. cmRptPrintf(rpt,"CB:%i\n",_cmGlobalCbCnt);
  212. break;
  213. }
  214. }
  215. // stop the input device
  216. if( cmApDeviceIsStarted(r->inDevIdx) )
  217. if( cmApDeviceStop(r->inDevIdx) != kOkApRC )
  218. cmRptPrintf(rpt,"In device stop failed.\n");
  219. // stop the output device
  220. if( cmApDeviceIsStarted(r->outDevIdx) )
  221. if( cmApDeviceStop(r->outDevIdx) != kOkApRC )
  222. cmRptPrintf(rpt,"Out device stop failed.\n");
  223. }
  224. errLabel:
  225. // release any resources held by the audio port interface
  226. if( cmApFinalize() != kOkApRC )
  227. rc = cmErrMsg(&ctx->err,kAudioPortFailAdRC,"Finalize failed.\n");
  228. cmApBufFinalize();
  229. cmApNrtFree();
  230. cmApFileFree();
  231. // report the count of audio buffer callbacks
  232. cmRptPrintf(rpt,"cb count:%i\n", r->cbCnt );
  233. //for(i=0; i<_logCnt; ++i)
  234. // cmRptPrintf(rpt,"%c(%i)",_log[i],_ilog[i]);
  235. //cmRptPrintf(rpt,"\n");
  236. return rc;
  237. }
  238. void print( void* arg, const char* text )
  239. {
  240. printf("%s",text);
  241. }
  242. int main( int argc, char* argv[] )
  243. {
  244. enum
  245. {
  246. kSratePoId = kBasePoId,
  247. kHzPoId,
  248. kChIdxPoId,
  249. kChCntPoId,
  250. kBufCntPoId,
  251. kFrmCntPoId,
  252. kFrmsPerBufPoId,
  253. kInDevIdxPoId,
  254. kOutDevIdxPoId,
  255. kReportFlagPoId
  256. };
  257. cmRC_t rc = cmOkRC;
  258. bool memDebugFl = cmDEBUG_FL;
  259. unsigned memGuardByteCnt = memDebugFl ? 8 : 0;
  260. unsigned memAlignByteCnt = 16;
  261. unsigned memFlags = memDebugFl ? kTrackMmFl | kDeferFreeMmFl | kFillUninitMmFl : 0;
  262. cmPgmOptH_t poH = cmPgmOptNullHandle;
  263. const cmChar_t* appTitle = "audiodev";
  264. unsigned reportFl = 0;
  265. cmCtx_t ctx;
  266. cmApPortTestRecd r;
  267. memset(&r,0,sizeof(r));
  268. r.meterMs = 50;
  269. r.logCnt = 100;
  270. memset(abuf,0,sizeof(cmApSample_t)*abufN);
  271. cmCtxSetup(&ctx,appTitle,print,print,NULL,memGuardByteCnt,memAlignByteCnt,memFlags);
  272. cmMdInitialize( memGuardByteCnt, memAlignByteCnt, memFlags, &ctx.rpt );
  273. cmFsInitialize( &ctx, appTitle );
  274. cmTsInitialize(&ctx );
  275. cmPgmOptInitialize(&ctx, &poH, poBegHelpStr, poEndHelpStr );
  276. cmPgmOptInstallDbl( poH, kSratePoId, 's', "srate", 0, 48000, &r.srate, 1,
  277. "Audio system sample rate." );
  278. cmPgmOptInstallDbl( poH, kHzPoId, 'z', "hz", 0, 1000, &r.frqHz, 1,
  279. "Tone frequency in Hertz." );
  280. cmPgmOptInstallUInt( poH, kChIdxPoId, 'x', "ch_index", 0, 0, &r.chIdx, 1,
  281. "Index of first channel index." );
  282. cmPgmOptInstallUInt( poH, kChCntPoId, 'c', "ch_cnt", 0, 2, &r.chCnt, 1,
  283. "Count of audio channels." );
  284. cmPgmOptInstallUInt( poH, kBufCntPoId, 'b', "buf_cnt", 0, 3, &r.bufCnt, 1,
  285. "Count of audio buffers. (e.g. 2=double buffering, 3=triple buffering)" );
  286. cmPgmOptInstallUInt( poH, kFrmsPerBufPoId, 'f', "frames_per_buf",0, 512, &r.framesPerCycle, 1,
  287. "Count of audio channels." );
  288. cmPgmOptInstallUInt( poH, kInDevIdxPoId, 'i', "in_dev_index",0, 0, &r.inDevIdx, 1,
  289. "Input device index as taken from the audio device report." );
  290. cmPgmOptInstallUInt( poH, kOutDevIdxPoId, 'o', "out_dev_index",0, 0, &r.outDevIdx, 1,
  291. "Output device index as taken from the audio device report." );
  292. cmPgmOptInstallFlag( poH, kReportFlagPoId, 'r', "report_flag", 0, 1, &reportFl, 1,
  293. "Print an audio device report." );
  294. // parse the command line arguments
  295. if( cmPgmOptParse(poH, argc, argv ) == kOkPoRC )
  296. {
  297. // handle the built-in arg's (e.g. -v,-p,-h)
  298. // (returns false if only built-in options were selected)
  299. if( cmPgmOptHandleBuiltInActions(poH, &ctx.rpt ) == false )
  300. goto errLabel;
  301. rc = audio_port_test( &ctx, &r, !reportFl );
  302. }
  303. errLabel:
  304. _abuf_write_audio_file(&ctx);
  305. cmPgmOptFinalize(&poH);
  306. cmTsFinalize();
  307. cmFsFinalize();
  308. cmMdReport( kIgnoreNormalMmFl );
  309. cmMdFinalize();
  310. return rc;
  311. }