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

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