libcm is a C development framework with an emphasis on audio signal processing applications.
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

cmRtSys.c 49KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687
  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 "cmRpt.h"
  7. #include "cmErr.h"
  8. #include "cmCtx.h"
  9. #include "cmMem.h"
  10. #include "cmMallocDebug.h"
  11. #include "cmTime.h"
  12. #include "cmAudioPort.h"
  13. #include "cmAudioNrtDev.h"
  14. #include "cmAudioPortFile.h"
  15. #include "cmApBuf.h"
  16. #include "cmJson.h"
  17. #include "cmThread.h"
  18. #include "cmUdpPort.h"
  19. #include "cmUdpNet.h"
  20. #include "cmRtSysMsg.h"
  21. #include "cmRtNet.h"
  22. #include "cmRtSys.h"
  23. #include "cmMidi.h"
  24. #include "cmMidiPort.h"
  25. #include "cmMath.h"
  26. typedef enum
  27. {
  28. kNoCmdId,
  29. kEnableCbCmdId,
  30. kDisableCbCmdId
  31. } kRtCmdId_t;
  32. cmRtSysH_t cmRtSysNullHandle = cmSTATIC_NULL_HANDLE;
  33. struct cmRt_str;
  34. typedef struct
  35. {
  36. struct cmRt_str* p; // pointer to the real-time system instance which owns this sub-system
  37. cmRtSysSubSys_t ss; // sub-system configuration record
  38. cmRtSysCtx_t ctx; // DSP context
  39. cmRtSysStatus_t status; // current runtime status of this sub-system
  40. cmThreadH_t threadH; // real-time system thread
  41. cmTsMp1cH_t htdQueueH; // host-to-dsp thread safe msg queue
  42. cmThreadMutexH_t engMutexH; // thread mutex and condition variable
  43. cmRtNetH_t netH;
  44. bool runFl; // false during finalization otherwise true
  45. bool statusFl; // true if regular status notifications should be sent
  46. bool syncInputFl;
  47. kRtCmdId_t cmdId; // written by app thread, read by rt thread
  48. unsigned cbEnableFl; // written by rt thread, read by app thread
  49. bool noBlockEnaFl; //
  50. unsigned noBlockSleepMs;
  51. double* iMeterArray; //
  52. double* oMeterArray; //
  53. unsigned statusUpdateSmpCnt; // transmit a state update msg every statusUpdateSmpCnt samples
  54. unsigned statusUpdateSmpIdx; // state update phase
  55. } _cmRtCfg_t;
  56. typedef struct cmRt_str
  57. {
  58. cmErr_t err;
  59. cmCtx_t* ctx;
  60. _cmRtCfg_t* ssArray;
  61. unsigned ssCnt;
  62. unsigned waitRtSubIdx; // index of the next sub-system to try with cmRtSysIsMsgWaiting().
  63. cmTsMp1cH_t dthQueH;
  64. bool initFl; // true if the real-time system is initialized
  65. cmTsQueueCb_t clientCbFunc; // These fields are only used during configuration.
  66. void* clientCbArg; // See cmRtBeginCfg() and cmRtCfg().
  67. } cmRt_t;
  68. cmRt_t* _cmRtHandleToPtr( cmRtSysH_t h )
  69. {
  70. cmRt_t* p = (cmRt_t*)h.h;
  71. assert(p != NULL);
  72. return p;
  73. }
  74. cmRtRC_t _cmRtError( cmRt_t* p, cmRtRC_t rc, const char* fmt, ... )
  75. {
  76. va_list vl;
  77. va_start(vl,fmt);
  78. cmErrVMsg(&p->err,rc,fmt,vl);
  79. va_end(vl);
  80. return rc;
  81. }
  82. // Wrapper function to put msgs into thread safe queues and handle related errors.
  83. cmRtRC_t _cmRtEnqueueMsg( cmRt_t* p, cmTsMp1cH_t qH, const void* msgDataPtrArray[], unsigned msgCntArray[], unsigned segCnt, const char* queueLabel )
  84. {
  85. cmRtRC_t rc = kOkRtRC;
  86. switch( cmTsMp1cEnqueueSegMsg(qH, msgDataPtrArray, msgCntArray, segCnt) )
  87. {
  88. case kOkThRC:
  89. break;
  90. case kBufFullThRC:
  91. {
  92. unsigned i;
  93. unsigned byteCnt = 0;
  94. for(i=0; i<segCnt; ++i)
  95. byteCnt += msgCntArray[i];
  96. rc = _cmRtError(p,kMsgEnqueueFailRtRC,"The %s queue was unable to load a msg containing %i bytes. The queue is currently allocated %i bytes and has %i bytes available.",queueLabel,byteCnt,cmTsMp1cAllocByteCount(qH),cmTsMp1cAvailByteCount(qH));
  97. }
  98. break;
  99. default:
  100. rc = _cmRtError(p,kMsgEnqueueFailRtRC,"A %s msg. enqueue failed.",queueLabel);
  101. }
  102. return rc;
  103. }
  104. // This is the function pointed to by ctx->dspToHostFunc.
  105. // It is called by the DSP proces to pass msgs to the host.
  106. // therefore it is always called from inside of _cmRtDspExecCallback().
  107. cmRtRC_t _cmRtDspToHostMsgCallback(struct cmRtSysCtx_str* ctx, const void* msgDataPtrArray[], unsigned msgByteCntArray[], unsigned msgSegCnt)
  108. {
  109. cmRt_t* p = (cmRt_t*)ctx->reserved;
  110. assert( ctx->rtSubIdx < p->ssCnt );
  111. return _cmRtEnqueueMsg(p,p->dthQueH,msgDataPtrArray,msgByteCntArray,msgSegCnt,"DSP-to-Host");
  112. }
  113. cmRtRC_t _cmRtSysDspToHostSegMsg( cmRt_t* p, const void* msgDataPtrArray[], unsigned msgByteCntArray[], unsigned msgSegCnt)
  114. {
  115. return _cmRtEnqueueMsg(p,p->dthQueH,msgDataPtrArray,msgByteCntArray,msgSegCnt,"DSP-to-Host");
  116. }
  117. cmRtRC_t cmRtSysDspToHostSegMsg( cmRtSysH_t h, const void* msgDataPtrArray[], unsigned msgByteCntArray[], unsigned msgSegCnt)
  118. {
  119. cmRt_t* p = _cmRtHandleToPtr(h);
  120. return _cmRtSysDspToHostSegMsg(p,msgDataPtrArray,msgByteCntArray,msgSegCnt);
  121. }
  122. cmRtRC_t cmRtSysDspToHost( cmRtSysH_t h, const void* msgDataPtr, unsigned msgByteCnt)
  123. {
  124. const void* msgDataArray[] = { msgDataPtr };
  125. unsigned msgByteCntArray[] = { msgByteCnt };
  126. return cmRtSysDspToHostSegMsg(h,msgDataArray,msgByteCntArray,1);
  127. }
  128. cmRtRC_t _cmRtParseNonSubSysMsg( cmRt_t* p, const void* msg, unsigned msgByteCnt )
  129. {
  130. cmRtRC_t rc = kOkRtRC;
  131. cmRtSysMstr_t* m = (cmRtSysMstr_t*)msg;
  132. /*
  133. unsigned devIdx = cmRtSysUiInstIdToDevIndex(h->instId);
  134. unsigned chIdx = cmRtSysUiInstIdToChIndex(h->instId);
  135. unsigned inFl = cmRtSysUiInstIdToInFlag(h->instId);
  136. unsigned ctlId = cmRtSysUiInstIdToCtlId(h->instId);
  137. */
  138. // if the valuu associated with this msg is a mtx then set
  139. // its mtx data area pointer to just after the msg header.
  140. //if( cmDsvIsMtx(&h->value) )
  141. // h->value.u.m.u.vp = ((char*)msg) + sizeof(cmDspUiHdr_t);
  142. unsigned flags = m->inFl ? kInApFl : kOutApFl;
  143. switch( m->ctlId )
  144. {
  145. case kSliderUiRtId: // slider
  146. cmApBufSetGain(m->devIdx,m->chIdx, flags, m->value);
  147. break;
  148. case kMeterUiRtId: // meter
  149. break;
  150. case kMuteUiRtId: // mute
  151. flags += m->value == 0 ? kEnableApFl : 0;
  152. cmApBufEnableChannel(m->devIdx,m->chIdx,flags);
  153. break;
  154. case kToneUiRtId: // tone
  155. flags += m->value > 0 ? kEnableApFl : 0;
  156. cmApBufEnableTone(m->devIdx,m->chIdx,flags);
  157. break;
  158. case kPassUiRtId: // pass
  159. flags += m->value > 0 ? kEnableApFl : 0;
  160. cmApBufEnablePass(m->devIdx,m->chIdx,flags);
  161. break;
  162. default:
  163. { assert(0); }
  164. }
  165. return rc;
  166. }
  167. // Process a UI msg sent from the host to the real-time system
  168. cmRtRC_t _cmRtHandleNonSubSysMsg( cmRt_t* p, const void* msgDataPtrArray[], unsigned msgByteCntArray[], unsigned msgSegCnt )
  169. {
  170. cmRtRC_t rc = kOkRtRC;
  171. // if the message is contained in a single segment it can be dispatched immediately ...
  172. if( msgSegCnt == 1 )
  173. rc = _cmRtParseNonSubSysMsg(p,msgDataPtrArray[0],msgByteCntArray[0]);
  174. else
  175. {
  176. // ... otherwise deserialize the message into contiguous memory ....
  177. unsigned byteCnt = 0;
  178. unsigned i;
  179. for(i=0; i<msgSegCnt; ++i)
  180. byteCnt += msgByteCntArray[i];
  181. char buf[ byteCnt ];
  182. char* b = buf;
  183. for(i=0; i<msgSegCnt; ++i)
  184. {
  185. memcpy(b, msgDataPtrArray[i], msgByteCntArray[i] );
  186. b += msgByteCntArray[i];
  187. }
  188. // ... and then dispatch it
  189. rc = _cmRtParseNonSubSysMsg(p,buf,byteCnt);
  190. }
  191. return rc;
  192. }
  193. cmRtRC_t _cmRtSendStateStatusToHost( _cmRtCfg_t* cp )
  194. {
  195. cmRtRC_t rc = kOkRtRC;
  196. cp->status.hdr.rtSubIdx = cp->ctx.rtSubIdx;
  197. cp->status.hdr.selId = kStatusSelRtId;
  198. cmApBufGetStatus( cp->ss.args.inDevIdx, kInApFl, cp->iMeterArray, cp->status.iMeterCnt, &cp->status.overflowCnt );
  199. cmApBufGetStatus( cp->ss.args.outDevIdx, kOutApFl, cp->oMeterArray, cp->status.oMeterCnt, &cp->status.underflowCnt );
  200. unsigned iMeterByteCnt = sizeof(cp->iMeterArray[0]) * cp->status.iMeterCnt;
  201. unsigned oMeterByteCnt = sizeof(cp->oMeterArray[0]) * cp->status.oMeterCnt;
  202. const void* msgDataPtrArray[] = { &cp->status, cp->iMeterArray, cp->oMeterArray };
  203. unsigned msgByteCntArray[] = { sizeof(cp->status), iMeterByteCnt, oMeterByteCnt };
  204. unsigned segCnt = sizeof(msgByteCntArray)/sizeof(unsigned);
  205. _cmRtSysDspToHostSegMsg(cp->p,msgDataPtrArray,msgByteCntArray, segCnt );
  206. return rc;
  207. }
  208. // This function is called, within the real-time thread,
  209. // with _cmRtRecd.engMutexH locked, to deliver
  210. // messages to the real-time DSP processes via cp->ss.cbFunc()
  211. cmRtRC_t _cmRtDeliverMsgsWithLock( _cmRtCfg_t* cp )
  212. {
  213. int i;
  214. cmRtRC_t rc = kOkThRC;
  215. // as long msg's are in the queue incoming msg queue
  216. for(i=0; rc == kOkThRC; ++i)
  217. {
  218. // if a msg is waiting transmit it via cp->ss.cbFunc()
  219. if((rc = cmTsMp1cDequeueMsg(cp->htdQueueH,NULL,0)) == kOkThRC)
  220. ++cp->status.msgCbCnt;
  221. }
  222. return rc;
  223. }
  224. // This funciton is _cmRtDspExecCallback()->cmRtNetReceive() in the
  225. // real-time thread to deliver msg's to the DSP process.
  226. void _cmRtSysNetRecv( void* cbArg, const char* data, unsigned dataByteCnt, const struct sockaddr_in* fromAddr )
  227. {
  228. _cmRtCfg_t* cp = (_cmRtCfg_t*)cbArg;
  229. if( cp->cbEnableFl )
  230. {
  231. cmRtSysH_t h;
  232. h.h = cp->p;
  233. cmRtSysDeliverMsg(h,data,dataByteCnt,cmInvalidId);
  234. }
  235. }
  236. // The DSP execution callback happens through this function.
  237. // This function is only called from inside _cmRtThreadCallback()
  238. // with the engine mutex locked.
  239. void _cmRtDspExecCallback( _cmRtCfg_t* cp )
  240. {
  241. // Fill iChArray[] and oChArray[] with pointers to the incoming and outgoing sample buffers.
  242. // Notes:
  243. // 1) Buffers associated with disabled input/output channels will be set to NULL in iChArray[]/oChArray[].
  244. // 2) Buffers associated with channels marked for pass-through will be set to NULL in oChArray[].
  245. // 3) All samples returned in oChArray[] buffers will be set to zero.
  246. if( cp->noBlockEnaFl == false )
  247. cmApBufGetIO(cp->ss.args.inDevIdx, cp->ctx.iChArray, cp->ctx.iChCnt, &cp->ctx.iTimeStamp,
  248. cp->ss.args.outDevIdx, cp->ctx.oChArray, cp->ctx.oChCnt, &cp->ctx.oTimeStamp );
  249. // calling this function results in callbacks to _cmRtSysNetRecv()
  250. // which in turn calls cmRtSysDeliverMsg() which queues any incoming messages
  251. // which are then transferred to the DSP processes by the the call to
  252. // _cmRtDeliverMsgWithLock() below.
  253. if( cmRtNetIsValid(cp->netH) )
  254. if( cmRtNetReceive(cp->netH) != kOkNetRC )
  255. _cmRtError(cp->p,kNetErrRtRC,"Network receive failed.");
  256. // NOTE: BY DEQUEUEING MSGS FIRST AND THEN SERVICING THE NETWORK
  257. // WE COULD ELIMINATE QUEUEING NETWORK MESSAGES - THEY COULD BE
  258. // SEND DIRECTLY THROUGH TO THE DSP PROCESSES
  259. // if there are msgs waiting to be sent to the DSP process send them.
  260. if( cp->cbEnableFl )
  261. if( cmTsMp1cMsgWaiting(cp->htdQueueH) )
  262. _cmRtDeliverMsgsWithLock(cp);
  263. // call the application provided DSP process
  264. if( cp->cbEnableFl )
  265. {
  266. cp->ctx.audioRateFl = true;
  267. cp->ss.cbFunc( &cp->ctx, 0, NULL );
  268. cp->ctx.audioRateFl = false;
  269. }
  270. // Notice client callback enable/disable
  271. // requests from the client thread
  272. switch( cp->cmdId )
  273. {
  274. case kNoCmdId:
  275. break;
  276. case kDisableCbCmdId:
  277. if( cp->cbEnableFl )
  278. cmThUIntDecr(&cp->cbEnableFl,1);
  279. break;
  280. case kEnableCbCmdId:
  281. if( cp->cbEnableFl==0)
  282. cmThUIntIncr(&cp->cbEnableFl,1);
  283. break;
  284. }
  285. // advance the audio buffer
  286. if( cp->noBlockEnaFl == false )
  287. {
  288. cmApBufAdvance( cp->ss.args.outDevIdx, kOutApFl );
  289. cmApBufAdvance( cp->ss.args.inDevIdx, kInApFl );
  290. }
  291. // handle periodic status messages to the host
  292. if( (cp->statusUpdateSmpIdx += cp->ss.args.dspFramesPerCycle) >= cp->statusUpdateSmpCnt )
  293. {
  294. cp->statusUpdateSmpIdx -= cp->statusUpdateSmpCnt;
  295. if( cp->statusFl )
  296. _cmRtSendStateStatusToHost(cp);
  297. }
  298. }
  299. // Returns true if audio buffer is has waiting incoming samples and
  300. // available outgoing space.
  301. bool _cmRtBufIsReady( const _cmRtCfg_t* cp )
  302. {
  303. // if there neither the input or output device is valid
  304. if( cp->ss.args.inDevIdx==cmInvalidIdx && cp->ss.args.outDevIdx == cmInvalidIdx )
  305. return false;
  306. bool ibFl = cmApBufIsDeviceReady(cp->ss.args.inDevIdx, kInApFl);
  307. bool obFl = cmApBufIsDeviceReady(cp->ss.args.outDevIdx, kOutApFl);
  308. bool iFl = (cp->ss.args.inDevIdx == cmInvalidIdx) || ibFl;
  309. bool oFl = (cp->ss.args.outDevIdx == cmInvalidIdx) || obFl;
  310. //printf("br: %i %i %i %i\n",ibFl,obFl,iFl,oFl);
  311. return iFl && oFl;
  312. }
  313. // This is the main real-time system loop (and thread callback function).
  314. // It blocks by waiting on a cond. var (which simultaneously unlocks a mutex).
  315. // With the mutex unlocked messages can pass directly to the DSP process
  316. // via calls to cmRtDeliverMsg().
  317. // When the audio buffers need to be serviced the audio device callback
  318. // signals the cond. var. which results in this thread waking up (and
  319. // simultaneously locking the mutex) as soon as the mutex is available.
  320. bool _cmRtThreadCallback(void* arg)
  321. {
  322. cmRtRC_t rc;
  323. _cmRtCfg_t* cp = (_cmRtCfg_t*)arg;
  324. bool noBlockFl = false;
  325. // lock the cmRtSys mutex
  326. if((rc = cmThreadMutexLock(cp->engMutexH)) != kOkRtRC )
  327. {
  328. _cmRtError(cp->p,rc,"The cmRtSys thread mutex lock failed.");
  329. return false;
  330. }
  331. // runFl is always set except during finalization
  332. while( cp->runFl )
  333. {
  334. // if the buffer is NOT ready or the cmRtSys is disabled
  335. if(_cmRtBufIsReady(cp) == false || cp->cbEnableFl==false )
  336. {
  337. // block on the cond var and unlock the mutex
  338. if( noBlockFl )
  339. cmSleepMs(cp->noBlockSleepMs);
  340. else
  341. {
  342. if( (rc = cmThreadMutexWaitOnCondVar(cp->engMutexH,false)) != kOkRtRC )
  343. {
  344. cmThreadMutexUnlock(cp->engMutexH);
  345. _cmRtError(cp->p,rc,"The cmRtSys cond. var. wait failed.");
  346. return false;
  347. }
  348. }
  349. //
  350. // the cond var was signaled and the mutex is now locked
  351. //
  352. ++cp->status.wakeupCnt;
  353. }
  354. noBlockFl = cp->noBlockEnaFl;
  355. // be sure we are still enabled and the buffer is still ready
  356. while( cp->runFl && _cmRtBufIsReady(cp) )
  357. {
  358. ++cp->status.audioCbCnt;
  359. // make the cmRtSys callback
  360. _cmRtDspExecCallback( cp );
  361. // update the signal time
  362. cp->ctx.begSmpIdx += cp->ss.args.dspFramesPerCycle;
  363. }
  364. }
  365. // unlock the mutex
  366. cmThreadMutexUnlock(cp->engMutexH);
  367. return true;
  368. }
  369. void _cmRtGenSignal( cmApAudioPacket_t* outPktArray, unsigned outPktCnt, bool sineFl )
  370. {
  371. static unsigned rtPhase = 0;
  372. //fill output with noise
  373. unsigned i = 0,j =0, k = 0, phs = 0;
  374. for(; i<outPktCnt; ++i)
  375. {
  376. cmApAudioPacket_t* a = outPktArray + i;
  377. cmApSample_t* dp = (cmApSample_t*)a->audioBytesPtr;
  378. phs = a->audioFramesCnt;
  379. if( sineFl )
  380. {
  381. for(j=0; j<a->audioFramesCnt; ++j)
  382. {
  383. cmApSample_t v = (cmApSample_t)(0.7 * sin(2*M_PI/44100.0 * rtPhase + j ));
  384. for(k=0; k<a->chCnt; ++k,++dp)
  385. *dp = v;
  386. }
  387. }
  388. else
  389. {
  390. for(j=0; j<a->audioFramesCnt*a->chCnt; ++j,++dp)
  391. *dp = (cmApSample_t)(rand() - (RAND_MAX/2))/(RAND_MAX/2);
  392. }
  393. }
  394. rtPhase += phs;
  395. }
  396. // This is the audio port callback function.
  397. //
  398. // _cmRtSysAudioUpdate() assumes that at most two audio device threads
  399. // (input and output) may call it. cmApBufUpdate() is safe under these conditions
  400. // since the input and output buffers are updated separately.
  401. // p->syncInputFl is used to allow either the input or output thread to signal
  402. // the condition variable. This flag is necessary to prevent both threads from simultaneously
  403. // attempting to signal the condition variable (which will lock the system).
  404. //
  405. // If more than two audio device threads call the function then this function is not safe.
  406. void _cmRtSysAudioUpdate( cmApAudioPacket_t* inPktArray, unsigned inPktCnt, cmApAudioPacket_t* outPktArray, unsigned outPktCnt )
  407. {
  408. _cmRtCfg_t* cp = (_cmRtCfg_t*)(inPktArray!=NULL ? inPktArray[0].userCbPtr : outPktArray[0].userCbPtr);
  409. ++cp->status.updateCnt;
  410. if( cp->runFl )
  411. {
  412. // transfer incoming/outgoing samples from/to the audio device
  413. cmApBufUpdate(inPktArray,inPktCnt,outPktArray,outPktCnt);
  414. // generate a test signal
  415. //_cmRtGenSignal( cmApAudioPacket_t* outPktArray, unsigned outPktCnt, bool sineFl );
  416. //return;
  417. bool testBufFl = (cp->syncInputFl==true && inPktCnt>0) || (cp->syncInputFl==false && outPktCnt>0);
  418. //printf("%i %i %i %i\n",testBufFl,cp->syncInputFl,inPktCnt,outPktCnt);
  419. // if the input/output buffer contain samples to be processed then signal the condition variable
  420. // - this will cause the real-time system thread to unblock and the used defined DSP process will be called.
  421. if( testBufFl && _cmRtBufIsReady(cp) )
  422. {
  423. if( cmThreadMutexSignalCondVar(cp->engMutexH) != kOkThRC )
  424. _cmRtError(cp->p,kMutexErrRtRC,"CmRtSys signal cond. var. failed.");
  425. }
  426. if( cp->noBlockEnaFl )
  427. {
  428. cmApBufGetIO(cp->ss.args.inDevIdx, cp->ctx.iChArray, cp->ctx.iChCnt, &cp->ctx.iTimeStamp,
  429. cp->ss.args.outDevIdx, cp->ctx.oChArray, cp->ctx.oChCnt, &cp->ctx.oTimeStamp );
  430. cmApBufAdvance( cp->ss.args.outDevIdx, kOutApFl );
  431. cmApBufAdvance( cp->ss.args.inDevIdx, kInApFl );
  432. }
  433. }
  434. }
  435. // Called when MIDI messages arrive from external MIDI ports.
  436. void _cmRtSysMidiCallback( const cmMidiPacket_t* pktArray, unsigned pktCnt )
  437. {
  438. unsigned i;
  439. for(i=0; i<pktCnt; ++i)
  440. {
  441. const cmMidiPacket_t* pkt = pktArray + i;
  442. _cmRtCfg_t* cp = (_cmRtCfg_t*)(pkt->cbDataPtr);
  443. if( !cp->runFl )
  444. continue;
  445. cmRtSysH_t asH;
  446. asH.h = cp->p;
  447. cmRtSysMidi_t m;
  448. m.hdr.rtSubIdx = cp->ctx.rtSubIdx;
  449. m.hdr.selId = kMidiMsgArraySelRtId;
  450. m.devIdx = pkt->devIdx;
  451. m.portIdx = pkt->portIdx;
  452. m.msgCnt = pkt->msgCnt;
  453. /*
  454. unsigned selId = kMidiMsgArraySelRtId;
  455. const void* msgPtrArray[] = { &cp->ctx.rtSubIdx, &selId, &pkt->devIdx, &pkt->portIdx, &pkt->msgCnt, pkt->msgArray };
  456. unsigned msgByteCntArray[] = { sizeof(cp->ctx.rtSubIdx), sizeof(selId), sizeof(pkt->devIdx), sizeof(pkt->portIdx), sizeof(pkt->msgCnt), pkt->msgCnt*sizeof(cmMidiMsg) };
  457. unsigned msgSegCnt = sizeof(msgByteCntArray)/sizeof(unsigned);
  458. */
  459. const void* msgPtrArray[] = { &m, pkt->msgArray };
  460. unsigned msgByteCntArray[] = { sizeof(m), pkt->msgCnt*sizeof(cmMidiMsg) };
  461. unsigned msgSegCnt = sizeof(msgByteCntArray)/sizeof(unsigned);
  462. cmRtSysDeliverSegMsg(asH,msgPtrArray,msgByteCntArray,msgSegCnt,cmInvalidId);
  463. }
  464. }
  465. cmRtRC_t cmRtSysAllocate( cmRtSysH_t* hp, cmCtx_t* ctx )
  466. {
  467. cmRtRC_t rc;
  468. if((rc = cmRtSysFree(hp)) != kOkRtRC )
  469. return rc;
  470. cmRt_t* p = cmMemAllocZ( cmRt_t, 1 );
  471. cmErrSetup(&p->err,&ctx->rpt,"Real-Time System");
  472. p->ctx = ctx;
  473. hp->h = p;
  474. return rc;
  475. }
  476. cmRtRC_t cmRtSysFree( cmRtSysH_t* hp )
  477. {
  478. cmRtRC_t rc;
  479. if( hp == NULL || hp->h == NULL )
  480. return kOkRtRC;
  481. if((rc = cmRtSysFinalize(*hp)) != kOkRtRC )
  482. return rc;
  483. cmRt_t* p = _cmRtHandleToPtr(*hp);
  484. cmMemFree(p);
  485. hp->h = NULL;
  486. return rc;
  487. }
  488. cmRtRC_t _cmRtSysEnable( cmRt_t* p, bool enableFl )
  489. {
  490. cmRtRC_t rc = kOkRtRC;
  491. unsigned i;
  492. unsigned n;
  493. unsigned tickMs = 20;
  494. unsigned timeOutMs = 10000;
  495. for(i=0; i<p->ssCnt; ++i)
  496. {
  497. _cmRtCfg_t* cp = p->ssArray + i;
  498. cmApBufOnPortEnable(cp->ss.args.inDevIdx,enableFl);
  499. cmApBufOnPortEnable(cp->ss.args.outDevIdx,enableFl);
  500. if( enableFl )
  501. {
  502. cp->cmdId = kNoCmdId;
  503. cmThUIntIncr(&cp->cmdId,kEnableCbCmdId);
  504. for(n=0; n<timeOutMs && cp->cbEnableFl==false; n+=tickMs )
  505. cmSleepMs(tickMs);
  506. cmThUIntDecr(&cp->cmdId,kEnableCbCmdId);
  507. }
  508. else
  509. {
  510. cp->cmdId = kNoCmdId;
  511. cmThUIntIncr(&cp->cmdId,kDisableCbCmdId);
  512. // wait for the rt thread to return from a client callbacks
  513. for(n=0; n<timeOutMs && cp->cbEnableFl; n+=tickMs )
  514. cmSleepMs(tickMs);
  515. cmThUIntDecr(&cp->cmdId,kDisableCbCmdId);
  516. }
  517. if( n >= timeOutMs )
  518. rc = cmErrMsg(&p->err,kTimeOutErrRtRC,"RT System %s timed out after %i milliseconds.",enableFl?"enable":"disable",timeOutMs);
  519. }
  520. // enable network sync mode
  521. if( enableFl)
  522. for(i=0; i<p->ssCnt; ++i)
  523. {
  524. _cmRtCfg_t* cp = p->ssArray + i;
  525. if( cmRtNetIsValid(cp->netH) )
  526. if( cmRtNetDoSync(cp->netH) != kOkNetRC )
  527. rc = cmErrMsg(&p->err,kNetErrRtRC,"Network Mgr. failed on entering sync mode.");
  528. }
  529. return rc;
  530. }
  531. cmRtRC_t _cmRtSysFinalize( cmRt_t* p )
  532. {
  533. cmRtRC_t rc = kOkRtRC;
  534. unsigned i;
  535. // mark the real-time system as NOT initialized
  536. p->initFl = false;
  537. // be sure all audio callbacks are disabled before continuing.
  538. if((rc = _cmRtSysEnable(p,false)) != kOkRtRC )
  539. return _cmRtError(p,rc,"real-time system finalize failed because device halting failed.");
  540. // stop the audio devices
  541. for(i=0; i<p->ssCnt; ++i)
  542. {
  543. _cmRtCfg_t* cp = p->ssArray + i;
  544. // stop the input device
  545. if((rc = cmApDeviceStop( cp->ss.args.inDevIdx )) != kOkRtRC )
  546. return _cmRtError(p,kAudioDevStopFailRtRC,"The audio input device stop failed.");
  547. // stop the output device
  548. if((rc = cmApDeviceStop( cp->ss.args.outDevIdx )) != kOkRtRC )
  549. return _cmRtError(p,kAudioDevStopFailRtRC,"The audio output device stop failed.");
  550. }
  551. for(i=0; i<p->ssCnt; ++i)
  552. {
  553. _cmRtCfg_t* cp = p->ssArray + i;
  554. if( cmThreadIsValid( cp->threadH ))
  555. {
  556. // inform the thread that it should exit
  557. cp->runFl = false;
  558. cp->statusFl = false;
  559. // signal the cond var to cause the thread to run
  560. if((rc = cmThreadMutexSignalCondVar(cp->engMutexH)) != kOkThRC )
  561. _cmRtError(p,kMutexErrRtRC,"Finalize signal cond. var. failed.");
  562. // wait to take control of the mutex - this will occur when the thread function exits
  563. if((rc = cmThreadMutexLock(cp->engMutexH)) != kOkThRC )
  564. _cmRtError(p,kMutexErrRtRC,"Finalize lock failed.");
  565. // unlock the mutex because it is no longer needed and must be unlocked to be destroyed
  566. if((rc = cmThreadMutexUnlock(cp->engMutexH)) != kOkThRC )
  567. _cmRtError(p,kMutexErrRtRC,"Finalize unlock failed.");
  568. // destroy the thread
  569. if((rc = cmThreadDestroy( &cp->threadH )) != kOkThRC )
  570. _cmRtError(p,kThreadErrRtRC,"Thread destroy failed.");
  571. }
  572. // destroy the mutex
  573. if( cmThreadMutexIsValid(cp->engMutexH) )
  574. if((rc = cmThreadMutexDestroy( &cp->engMutexH )) != kOkThRC )
  575. _cmRtError(p,kMutexErrRtRC,"Mutex destroy failed.");
  576. // release the network mgr
  577. if( cmRtNetFree(&cp->netH) != kOkNetRC )
  578. _cmRtError(p,kNetErrRtRC,"Network Mrr. release failed.");
  579. // remove the MIDI callback
  580. if( cmMpIsInitialized() && cmMpUsesCallback(-1,-1, _cmRtSysMidiCallback, cp) )
  581. if( cmMpRemoveCallback( -1, -1, _cmRtSysMidiCallback, cp ) != kOkMpRC )
  582. _cmRtError(p,kMidiSysFailRtRC,"MIDI callback removal failed.");
  583. // destroy the host-to-dsp msg queue
  584. if( cmTsMp1cIsValid(cp->htdQueueH ) )
  585. if((rc = cmTsMp1cDestroy( &cp->htdQueueH )) != kOkThRC )
  586. _cmRtError(p,kTsQueueErrRtRC,"Host-to-DSP msg queue destroy failed.");
  587. // destroy the dsp-to-host msg queue
  588. if( cmTsMp1cIsValid(p->dthQueH) )
  589. if((rc = cmTsMp1cDestroy( &p->dthQueH )) != kOkThRC )
  590. _cmRtError(p,kTsQueueErrRtRC,"DSP-to-Host msg queue destroy failed.");
  591. cmMemPtrFree(&cp->ctx.iChArray);
  592. cmMemPtrFree(&cp->ctx.oChArray);
  593. cp->ctx.iChCnt = 0;
  594. cp->ctx.oChCnt = 0;
  595. cmMemPtrFree(&cp->iMeterArray);
  596. cmMemPtrFree(&cp->oMeterArray);
  597. cp->status.iMeterCnt = 0;
  598. cp->status.oMeterCnt = 0;
  599. }
  600. cmMemPtrFree(&p->ssArray);
  601. p->ssCnt = 0;
  602. return rc;
  603. }
  604. // A given device may be used as an input device exactly once and an
  605. // output device exactly once. When the input to a given device is used
  606. // by one sub-system and the output is used by another then both sub-systems
  607. // must use the same srate,devFramesPerCycle, audioBufCnt and dspFramesPerCycle.
  608. cmRtRC_t _cmRtSysValidate( cmRt_t* p )
  609. {
  610. unsigned i,j,k;
  611. for(i=0; i<2; ++i)
  612. {
  613. // examine input devices - then output devices
  614. bool inputFl = i==0;
  615. bool outputFl = !inputFl;
  616. for(j=0; j<p->ssCnt; ++j)
  617. {
  618. cmRtSysArgs_t* s0 = &p->ssArray[j].ss.args;
  619. unsigned devIdx = inputFl ? s0->inDevIdx : s0->outDevIdx;
  620. for(k=0; k<p->ssCnt && devIdx != cmInvalidIdx; ++k)
  621. if( k != j )
  622. {
  623. cmRtSysArgs_t* s1 = &p->ssArray[k].ss.args;
  624. // if the device was used as input or output multple times then signal an error
  625. if( (inputFl && (s1->inDevIdx == devIdx) && s1->inDevIdx != cmInvalidIdx) || (outputFl && (s1->outDevIdx == devIdx) && s1->outDevIdx != cmInvalidIdx) )
  626. return cmErrMsg(&p->err,kInvalidArgRtRC,"The device %i was used as an %s by multiple sub-systems.", devIdx, inputFl ? "input" : "output");
  627. // if this device is being used by another subsystem ...
  628. if( (inputFl && (s1->outDevIdx == devIdx) && s1->inDevIdx != cmInvalidIdx) || (outputFl && (s1->outDevIdx == devIdx) && s1->outDevIdx != cmInvalidIdx ) )
  629. {
  630. // ... then some of its buffer spec's must match
  631. if( s0->srate != s1->srate || s0->audioBufCnt != s1->audioBufCnt || s0->dspFramesPerCycle != s1->dspFramesPerCycle || s0->devFramesPerCycle != s1->devFramesPerCycle )
  632. return cmErrMsg(&p->err,kInvalidArgRtRC,"The device %i is used by different sub-system with different audio buffer parameters.",devIdx);
  633. }
  634. }
  635. }
  636. }
  637. return kOkRtRC;
  638. }
  639. cmRtRC_t cmRtSysBeginCfg( cmRtSysH_t h, cmTsQueueCb_t clientCbFunc, void* clientCbArg, unsigned meterMs, unsigned ssCnt )
  640. {
  641. cmRt_t* p = _cmRtHandleToPtr(h);
  642. cmRtRC_t rc;
  643. // always finalize before iniitalize
  644. if((rc = cmRtSysFinalize(h)) != kOkRtRC )
  645. return rc;
  646. p->ssArray = cmMemAllocZ( _cmRtCfg_t, ssCnt );
  647. p->ssCnt = ssCnt;
  648. p->clientCbFunc = clientCbFunc;
  649. p->clientCbArg = clientCbArg;
  650. return rc;
  651. }
  652. cmRtRC_t cmRtSysCfg( cmRtSysH_t h, const cmRtSysSubSys_t* ss, unsigned rtSubIdx )
  653. {
  654. cmRtRC_t rc;
  655. unsigned j;
  656. cmRt_t* p = _cmRtHandleToPtr(h);
  657. assert( rtSubIdx < p->ssCnt);
  658. _cmRtCfg_t* cp = p->ssArray + rtSubIdx;;
  659. cp->p = p;
  660. cp->ss = *ss; // copy the cfg into the internal real-time system state
  661. cp->runFl = false;
  662. cp->statusFl = false;
  663. cp->ctx.reserved = p;
  664. cp->ctx.rtSubIdx = rtSubIdx;
  665. cp->ctx.ss = &cp->ss;
  666. cp->ctx.begSmpIdx = 0;
  667. cp->ctx.dspToHostFunc = _cmRtDspToHostMsgCallback;
  668. // validate the input device index
  669. if( ss->args.inDevIdx != cmInvalidIdx && ss->args.inDevIdx >= cmApDeviceCount() )
  670. {
  671. rc = _cmRtError(p,kAudioDevSetupErrRtRC,"The audio input device index %i is invalid.",ss->args.inDevIdx);
  672. goto errLabel;
  673. }
  674. // validate the output device index
  675. if( ss->args.outDevIdx != cmInvalidIdx && ss->args.outDevIdx >= cmApDeviceCount() )
  676. {
  677. rc = _cmRtError(p,kAudioDevSetupErrRtRC,"The audio output device index %i is invalid.",ss->args.outDevIdx);
  678. goto errLabel;
  679. }
  680. // setup the input device
  681. if( ss->args.inDevIdx != cmInvalidIdx )
  682. if((rc = cmApDeviceSetup( ss->args.inDevIdx, ss->args.srate, ss->args.devFramesPerCycle, _cmRtSysAudioUpdate, cp )) != kOkRtRC )
  683. {
  684. rc = _cmRtError(p,kAudioDevSetupErrRtRC,"Audio input device setup failed.");
  685. goto errLabel;
  686. }
  687. // setup the output device
  688. if( ss->args.outDevIdx != ss->args.inDevIdx && ss->args.outDevIdx != cmInvalidIdx )
  689. if((rc = cmApDeviceSetup( ss->args.outDevIdx, ss->args.srate, ss->args.devFramesPerCycle, _cmRtSysAudioUpdate, cp )) != kOkRtRC )
  690. {
  691. rc = _cmRtError(p,kAudioDevSetupErrRtRC,"Audio output device setup failed.");
  692. goto errLabel;
  693. }
  694. // setup the input device buffer
  695. if( ss->args.inDevIdx != cmInvalidIdx )
  696. if((rc = cmApBufSetup( ss->args.inDevIdx, ss->args.srate, ss->args.dspFramesPerCycle, ss->args.audioBufCnt, cmApDeviceChannelCount(ss->args.inDevIdx, true), ss->args.devFramesPerCycle, cmApDeviceChannelCount(ss->args.inDevIdx, false), ss->args.devFramesPerCycle, ss->args.srateMult )) != kOkRtRC )
  697. {
  698. rc = _cmRtError(p,kAudioBufSetupErrRtRC,"Audio buffer input setup failed.");
  699. goto errLabel;
  700. }
  701. cmApBufEnableMeter(ss->args.inDevIdx, -1, kInApFl | kEnableApFl );
  702. cmApBufEnableMeter(ss->args.outDevIdx,-1, kOutApFl | kEnableApFl );
  703. // setup the input audio buffer ptr array - used to send input audio to the DSP system in _cmRtDspExecCallback()
  704. if((cp->ctx.iChCnt = cmApDeviceChannelCount(ss->args.inDevIdx, true)) != 0 )
  705. cp->ctx.iChArray = cmMemAllocZ( cmSample_t*, cp->ctx.iChCnt );
  706. // setup the output device buffer
  707. if( ss->args.outDevIdx != ss->args.inDevIdx )
  708. if((rc = cmApBufSetup( ss->args.outDevIdx, ss->args.srate, ss->args.dspFramesPerCycle, ss->args.audioBufCnt, cmApDeviceChannelCount(ss->args.outDevIdx, true), ss->args.devFramesPerCycle, cmApDeviceChannelCount(ss->args.outDevIdx, false), ss->args.devFramesPerCycle, ss->args.srateMult )) != kOkRtRC )
  709. return _cmRtError(p,kAudioBufSetupErrRtRC,"Audio buffer ouput device setup failed.");
  710. // setup the output audio buffer ptr array - used to recv output audio from the DSP system in _cmRtDspExecCallback()
  711. if((cp->ctx.oChCnt = cmApDeviceChannelCount(ss->args.outDevIdx, false)) != 0 )
  712. cp->ctx.oChArray = cmMemAllocZ( cmSample_t*, cp->ctx.oChCnt );
  713. // determine the sync source
  714. cp->syncInputFl = ss->args.syncInputFl;
  715. // if sync'ing to an unavailable device then sync to the available device
  716. if( ss->args.syncInputFl && cp->ctx.iChCnt == 0 )
  717. cp->syncInputFl = false;
  718. if( ss->args.syncInputFl==false && cp->ctx.oChCnt == 0 )
  719. cp->syncInputFl = true;
  720. // setup the status record
  721. cp->status.hdr.rtSubIdx = cp->ctx.rtSubIdx;
  722. cp->status.iDevIdx = ss->args.inDevIdx;
  723. cp->status.oDevIdx = ss->args.outDevIdx;
  724. cp->status.iMeterCnt = cp->ctx.iChCnt;
  725. cp->status.oMeterCnt = cp->ctx.oChCnt;
  726. cp->iMeterArray = cmMemAllocZ( double, cp->status.iMeterCnt );
  727. cp->oMeterArray = cmMemAllocZ( double, cp->status.oMeterCnt );
  728. cp->noBlockEnaFl = false;
  729. // create the real-time system thread
  730. if((rc = cmThreadCreate( &cp->threadH, _cmRtThreadCallback, cp, ss->args.rpt )) != kOkThRC )
  731. {
  732. rc = _cmRtError(p,kThreadErrRtRC,"Thread create failed.");
  733. goto errLabel;
  734. }
  735. // create the real-time system mutex
  736. if((rc = cmThreadMutexCreate( &cp->engMutexH, ss->args.rpt )) != kOkThRC )
  737. {
  738. rc = _cmRtError(p,kMutexErrRtRC,"Thread mutex create failed.");
  739. goto errLabel;
  740. }
  741. // create the host-to-dsp thread safe msg queue
  742. if((rc = cmTsMp1cCreate( &cp->htdQueueH, ss->args.msgQueueByteCnt, ss->cbFunc, &cp->ctx, ss->args.rpt )) != kOkThRC )
  743. {
  744. rc = _cmRtError(p,kTsQueueErrRtRC,"Host-to-DSP msg queue create failed.");
  745. goto errLabel;
  746. }
  747. // create the dsp-to-host thread safe msg queue
  748. if( cmTsMp1cIsValid( p->dthQueH ) == false )
  749. {
  750. if((rc = cmTsMp1cCreate( &p->dthQueH, ss->args.msgQueueByteCnt, p->clientCbFunc, p->clientCbArg, ss->args.rpt )) != kOkThRC )
  751. {
  752. rc = _cmRtError(p,kTsQueueErrRtRC,"DSP-to-Host msg queue create failed.");
  753. goto errLabel;
  754. }
  755. }
  756. // install an external MIDI port callback handler for incoming MIDI messages
  757. if( cmMpIsInitialized() )
  758. if( cmMpInstallCallback( -1, -1, _cmRtSysMidiCallback, cp ) != kOkMpRC )
  759. {
  760. rc = _cmRtError(p,kMidiSysFailRtRC,"MIDI system callback installation failed.");
  761. goto errLabel;
  762. }
  763. // setup the sub-system status notification
  764. cp->statusUpdateSmpCnt = floor(cmApBufMeterMs() * cp->ss.args.srate / 1000.0 );
  765. cp->statusUpdateSmpIdx = 0;
  766. // allocate the network mgr
  767. if( cmRtNetAlloc(p->ctx,&cp->netH, cp->ctx.rtSubIdx, _cmRtSysNetRecv, cp ) != kOkNetRC )
  768. {
  769. rc = _cmRtError(p,kNetErrRtRC,"Network allocation failed.");
  770. goto errLabel;
  771. }
  772. if( cmRtNetInitialize( cp->netH, ss->bcastAddr, ss->localNodeLabel, ss->localIpAddr, ss->localIpPort) != kOkNetRC )
  773. {
  774. rc = _cmRtError(p,kNetErrRtRC,"Network node initialization failed on label:%s addr:%s port:%i.",cmStringNullGuard(ss->localNodeLabel),cmStringNullGuard(ss->localIpAddr),ss->localIpPort);
  775. goto errLabel;
  776. }
  777. // register the local endpoints
  778. for(j=0; j<ss->endptCnt; ++j)
  779. {
  780. cmRtSysNetEndpt_t* ep = ss->endptArray + j;
  781. if( cmRtNetRegisterEndPoint( cp->netH, ep->label, ep->id ) != kOkNetRC )
  782. {
  783. rc = _cmRtError(p,kNetErrRtRC,"Network end point allocation failed on label:%s id:%i.",cmStringNullGuard(ep->label),ep->id);
  784. goto errLabel;
  785. }
  786. }
  787. errLabel:
  788. if( rc != kOkRtRC )
  789. _cmRtSysFinalize(p);
  790. return rc;
  791. }
  792. cmRtRC_t cmRtSysEndCfg( cmRtSysH_t h )
  793. {
  794. cmRtRC_t rc;
  795. cmRt_t* p = _cmRtHandleToPtr(h);
  796. unsigned i;
  797. if((rc = _cmRtSysValidate(p)) != kOkRtRC )
  798. goto errLabel;
  799. for(i=0; i<p->ssCnt; ++i)
  800. {
  801. _cmRtCfg_t* cp = p->ssArray + i;
  802. cp->runFl = true;
  803. // start the real-time system thread
  804. if( cmThreadPause( cp->threadH, 0 ) != kOkThRC )
  805. {
  806. rc = _cmRtError(p,kThreadErrRtRC,"Thread start failed.");
  807. goto errLabel;
  808. }
  809. // start the input device
  810. if((rc = cmApDeviceStart( cp->ss.args.inDevIdx )) != kOkRtRC )
  811. return _cmRtError(p,kAudioDevStartFailRtRC,"The audio input device start failed.");
  812. // start the output device
  813. if( cmApDeviceStart( cp->ss.args.outDevIdx ) != kOkRtRC )
  814. return _cmRtError(p,kAudioDevStartFailRtRC,"The audio ouput device start failed.");
  815. }
  816. p->initFl = true;
  817. errLabel:
  818. if( rc != kOkRtRC )
  819. _cmRtSysFinalize(p);
  820. return rc;
  821. }
  822. cmRtRC_t cmRtSysFinalize(cmRtSysH_t h )
  823. {
  824. cmRtRC_t rc = kOkRtRC;
  825. if( cmRtSysHandleIsValid(h) == false )
  826. return rc;
  827. cmRt_t* p = _cmRtHandleToPtr(h);
  828. rc = _cmRtSysFinalize(p);
  829. h.h = NULL;
  830. return rc;
  831. }
  832. bool cmRtSysIsInitialized( cmRtSysH_t h )
  833. {
  834. cmRt_t* p = _cmRtHandleToPtr(h);
  835. return p->initFl;
  836. }
  837. cmRtRC_t _cmRtSysVerifyInit( cmRt_t* p, bool errFl )
  838. {
  839. if( p->initFl == false )
  840. {
  841. // if the last msg generated was also a not init msg then don't
  842. // generate another message - just return the error
  843. if( errFl )
  844. if( cmErrLastRC(&p->err) != kNotInitRtRC )
  845. cmErrMsg(&p->err,kNotInitRtRC,"The real-time system is not initialized.");
  846. return kNotInitRtRC;
  847. }
  848. return kOkRtRC;
  849. }
  850. bool cmRtSysIsEnabled( cmRtSysH_t h )
  851. {
  852. if( cmRtSysIsInitialized(h) == false )
  853. return false;
  854. cmRt_t* p = _cmRtHandleToPtr(h);
  855. unsigned i;
  856. for(i=0; i<p->ssCnt; ++i)
  857. if( p->ssArray[i].cbEnableFl )
  858. return true;
  859. return false;
  860. }
  861. cmRtRC_t cmRtSysEnable( cmRtSysH_t h, bool enableFl )
  862. {
  863. cmRt_t* p = _cmRtHandleToPtr(h);
  864. return _cmRtSysEnable(p,enableFl);
  865. }
  866. cmRtRC_t cmRtSysDeliverSegMsg( cmRtSysH_t h, const void* msgDataPtrArray[], unsigned msgByteCntArray[], unsigned msgSegCnt, unsigned srcNetNodeId )
  867. {
  868. cmRt_t* p = _cmRtHandleToPtr(h);
  869. cmRtRC_t rc;
  870. // the system must be initialized to use this function
  871. if((rc = _cmRtSysVerifyInit(p,true)) != kOkRtRC )
  872. return rc;
  873. if( msgSegCnt == 0 )
  874. return kOkRtRC;
  875. // BUG BUG BUG - there is no reason that both the rtSubIdx and the selId must
  876. // be in the first segment but it would be nice.
  877. assert( msgByteCntArray[0] >= 2*sizeof(unsigned) || (msgSegCnt>1 && msgByteCntArray[0]==sizeof(unsigned) && msgByteCntArray[1]>=sizeof(unsigned)) );
  878. // The audio sub-system index is always the first field of the msg
  879. // and the msg selector id is always the second field
  880. unsigned* array = (unsigned*)msgDataPtrArray[0];
  881. unsigned rtSubIdx = array[0];
  882. unsigned selId = array[1];
  883. if( selId == kUiMstrSelRtId )
  884. return _cmRtHandleNonSubSysMsg( p, msgDataPtrArray, msgByteCntArray, msgSegCnt );
  885. // if rtSubIdx == kInvalidIdx then send the msg to all sub-systems
  886. // otherwise send it to the specified sub-system.
  887. unsigned i = 0;
  888. unsigned n = 1;
  889. if( rtSubIdx == cmInvalidIdx )
  890. n = p->ssCnt;
  891. for(; i<n; ++i)
  892. {
  893. unsigned j = rtSubIdx==cmInvalidIdx ? i : rtSubIdx;
  894. if((rc = _cmRtEnqueueMsg(p,p->ssArray[j].htdQueueH,msgDataPtrArray,msgByteCntArray,msgSegCnt,"Host-to-DSP")) != kOkRtRC )
  895. break;
  896. }
  897. return rc;
  898. }
  899. cmRtRC_t cmRtSysDeliverMsg( cmRtSysH_t h, const void* msgPtr, unsigned msgByteCnt, unsigned srcNetNodeId )
  900. {
  901. const void* msgDataPtrArray[] = { msgPtr };
  902. unsigned msgByteCntArray[] = { msgByteCnt };
  903. return cmRtSysDeliverSegMsg(h,msgDataPtrArray,msgByteCntArray,1,srcNetNodeId);
  904. }
  905. cmRtRC_t cmRtSysDeliverIdMsg( cmRtSysH_t h, unsigned rtSubIdx, unsigned id, const void* msgPtr, unsigned msgByteCnt, unsigned srcNetNodeId )
  906. {
  907. cmRtRC_t rc;
  908. cmRt_t* p = _cmRtHandleToPtr(h);
  909. // the system must be initialized to use this function
  910. if((rc = _cmRtSysVerifyInit(p,true)) != kOkRtRC )
  911. return rc;
  912. const void* msgDataPtrArray[] = { &rtSubIdx, &id, msgPtr };
  913. unsigned msgByteCntArray[] = { sizeof(rtSubIdx), sizeof(id), msgByteCnt };
  914. return cmRtSysDeliverSegMsg(h,msgDataPtrArray,msgByteCntArray,3,srcNetNodeId);
  915. }
  916. unsigned cmRtSysIsMsgWaiting( cmRtSysH_t h )
  917. {
  918. cmRtRC_t rc;
  919. cmRt_t* p = _cmRtHandleToPtr(h);
  920. // the system must be initialized to use this function
  921. if((rc = _cmRtSysVerifyInit(p,false)) != kOkRtRC )
  922. return 0;
  923. unsigned n = 0;
  924. unsigned retByteCnt;
  925. for(n=0; n < p->ssCnt; ++n )
  926. {
  927. if( (retByteCnt = cmTsMp1cDequeueMsgByteCount(p->dthQueH)) > 0 )
  928. return retByteCnt;
  929. p->waitRtSubIdx = (p->waitRtSubIdx + 1) % p->ssCnt;
  930. }
  931. return 0;
  932. }
  933. cmRtRC_t cmRtSysReceiveMsg( cmRtSysH_t h, void* msgDataPtr, unsigned msgByteCnt )
  934. {
  935. cmRtRC_t rc;
  936. cmRt_t* p = _cmRtHandleToPtr(h);
  937. // the system must be initialized to use this function
  938. if((rc = _cmRtSysVerifyInit(p,true)) != kOkRtRC )
  939. return rc;
  940. //switch( cmTsMp1cDequeueMsg(p->ssArray[p->waitRtSubIdx].dthQueueH,msgDataPtr,msgByteCnt) )
  941. switch( cmTsMp1cDequeueMsg(p->dthQueH,msgDataPtr,msgByteCnt) )
  942. {
  943. case kOkThRC:
  944. p->waitRtSubIdx = (p->waitRtSubIdx + 1) % p->ssCnt;
  945. return kOkRtRC;
  946. case kBufTooSmallThRC:
  947. return kBufTooSmallRtRC;
  948. case kBufEmptyThRC:
  949. return kNoMsgWaitingRtRC;
  950. }
  951. return _cmRtError(p,kTsQueueErrRtRC,"A deque operation failed on the DSP-to-Host message queue.");
  952. }
  953. void cmRtSysStatus( cmRtSysH_t h, unsigned rtSubIdx, cmRtSysStatus_t* statusPtr )
  954. {
  955. cmRt_t* p = _cmRtHandleToPtr(h);
  956. // the system must be initialized to use this function
  957. if( _cmRtSysVerifyInit(p,true) != kOkRtRC )
  958. return;
  959. if( rtSubIdx < p->ssCnt )
  960. *statusPtr = p->ssArray[rtSubIdx].status;
  961. }
  962. void cmRtSysStatusNotifyEnable( cmRtSysH_t h, unsigned rtSubIdx, bool enableFl )
  963. {
  964. cmRt_t* p = _cmRtHandleToPtr(h);
  965. // the system must be initialized to use this function
  966. if( _cmRtSysVerifyInit(p,true) != kOkRtRC )
  967. return;
  968. unsigned i = rtSubIdx == cmInvalidIdx ? 0 : rtSubIdx;
  969. unsigned n = rtSubIdx == cmInvalidIdx ? p->ssCnt : rtSubIdx+1;
  970. for(; i<n; ++i)
  971. p->ssArray[i].statusFl = enableFl;
  972. }
  973. bool cmRtSysHandleIsValid( cmRtSysH_t h )
  974. { return h.h != NULL; }
  975. cmRtSysCtx_t* cmRtSysContext( cmRtSysH_t h, unsigned rtSubIdx )
  976. {
  977. cmRt_t* p = _cmRtHandleToPtr(h);
  978. if( _cmRtSysVerifyInit(p,true) != kOkRtRC )
  979. return NULL;
  980. if( rtSubIdx >= p->ssCnt )
  981. return NULL;
  982. return &p->ssArray[rtSubIdx].ctx;
  983. }
  984. cmRtRC_t cmRtSysEnableNoBlockMode( cmRtSysH_t h, unsigned rtSubIdx, bool enaFl, unsigned noBlockSleepMs )
  985. {
  986. cmRt_t* p = _cmRtHandleToPtr(h);
  987. cmRtRC_t rc = kOkRtRC;
  988. if((rc = _cmRtSysVerifyInit(p,true)) != kOkRtRC )
  989. return rc;
  990. if( rtSubIdx >= p->ssCnt )
  991. return cmErrMsg(&p->err,kInvalidArgRtRC,"Invalid 'rtSubIdx'. Enable non-block mode failed.");
  992. p->ssArray[rtSubIdx].noBlockSleepMs = noBlockSleepMs;
  993. p->ssArray[rtSubIdx].noBlockEnaFl = enaFl;
  994. return kOkRtRC;
  995. }
  996. unsigned cmRtSysSubSystemCount( cmRtSysH_t h )
  997. {
  998. cmRt_t* p = _cmRtHandleToPtr(h);
  999. if( _cmRtSysVerifyInit(p,true) != kOkRtRC )
  1000. return 0;
  1001. return p->ssCnt;
  1002. }
  1003. bool cmRtSysNetIsInitialized( cmRtSysH_t h )
  1004. {
  1005. cmRt_t* p = _cmRtHandleToPtr(h);
  1006. unsigned i = 0;
  1007. for(; i<p->ssCnt; ++i)
  1008. if( cmRtNetIsInitialized(p->ssArray[i].netH) )
  1009. return true;
  1010. return false;
  1011. }
  1012. cmRtRC_t cmRtSysNetDoSync( cmRtSysH_t h )
  1013. {
  1014. cmRtRC_t rc = kOkRtRC;
  1015. cmRt_t* p = _cmRtHandleToPtr(h);
  1016. unsigned i = 0;
  1017. for(; i<p->ssCnt; ++i)
  1018. if( cmRtNetIsInitialized(p->ssArray[i].netH) )
  1019. cmRtNetDoSync(p->ssArray[i].netH);
  1020. return rc;
  1021. }
  1022. cmRtRC_t cmRtSysNetReport( cmRtSysH_t h )
  1023. {
  1024. cmRtRC_t rc = kOkRtRC;
  1025. cmRt_t* p = _cmRtHandleToPtr(h);
  1026. unsigned i = 0;
  1027. for(; i<p->ssCnt; ++i)
  1028. {
  1029. cmRptPrintf(p->err.rpt,"Sub-system:%i\n",i);
  1030. if( cmRtNetIsValid(p->ssArray[i].netH))
  1031. cmRtNetReport(p->ssArray[i].netH);
  1032. }
  1033. return rc;
  1034. }
  1035. cmRtRC_t cmRtSysNetReportSyncEnable( cmRtSysH_t h, bool enableFl )
  1036. {
  1037. cmRtRC_t rc = kOkRtRC;
  1038. cmRt_t* p = _cmRtHandleToPtr(h);
  1039. unsigned i = 0;
  1040. for(; i<p->ssCnt; ++i)
  1041. if( cmRtNetIsValid(p->ssArray[i].netH))
  1042. cmRtNetReportSyncEnable(p->ssArray[i].netH,enableFl);
  1043. return rc;
  1044. }
  1045. cmRtRC_t cmRtSysNetGetHandle( cmRtSysH_t h, unsigned rtSubIdx, cmRtNetH_t* hp )
  1046. {
  1047. cmRtRC_t rc = kOkRtRC;
  1048. cmRt_t* p = _cmRtHandleToPtr(h);
  1049. assert( rtSubIdx < p->ssCnt );
  1050. if( rtSubIdx < p->ssCnt )
  1051. {
  1052. *hp = p->ssArray[rtSubIdx].netH;
  1053. return rc;
  1054. }
  1055. return cmErrMsg(&p->err,kInvalidArgRtRC,"The rtSubIdx %i is out of range %i.",rtSubIdx,p->ssCnt);
  1056. }
  1057. //===========================================================================================================================
  1058. //
  1059. // cmRtTest()
  1060. //
  1061. /// [cmRtSysTest]
  1062. typedef struct
  1063. {
  1064. double hz; // current synth frq
  1065. long phs; // current synth phase
  1066. double srate; // audio sample rate
  1067. unsigned cbCnt; // DSP cycle count
  1068. bool synthFl; // true=synth false=pass through
  1069. } _cmRtTestCbRecd;
  1070. typedef struct
  1071. {
  1072. unsigned rtSubIdx; // rtSubIdx must always be the first field in the msg
  1073. unsigned id; // 0 = set DSP Hz, 1 = report cbCount to host
  1074. double hz;
  1075. unsigned uint;
  1076. } _cmRtTestMsg;
  1077. long _cmRtSynthSine( _cmRtTestCbRecd* r, cmApSample_t* p, unsigned chCnt, unsigned frmCnt )
  1078. {
  1079. long ph = 0;
  1080. unsigned i;
  1081. for(i=0; i<chCnt; ++i)
  1082. {
  1083. unsigned j;
  1084. cmApSample_t* op = p + i;
  1085. ph = r->phs;
  1086. for(j=0; j<frmCnt; j++, op+=chCnt, ph++)
  1087. *op = (cmApSample_t)(0.9 * sin( 2.0 * M_PI * r->hz * ph / r->srate ));
  1088. }
  1089. return ph;
  1090. }
  1091. unsigned _cmRtTestChIdx = 0;
  1092. cmRC_t _cmRtTestCb( void* cbPtr, unsigned msgByteCnt, const void* msgDataPtr )
  1093. {
  1094. cmRC_t rc = cmOkRC;
  1095. cmRtSysCtx_t* ctx = (cmRtSysCtx_t*)cbPtr;
  1096. cmRtSysSubSys_t* ss = ctx->ss;
  1097. _cmRtTestCbRecd* r = (_cmRtTestCbRecd*)ss->cbDataPtr;
  1098. // update the calback counter
  1099. ++r->cbCnt;
  1100. // if this is an audio update request
  1101. if( msgByteCnt == 0 )
  1102. {
  1103. unsigned i;
  1104. if( r->synthFl )
  1105. {
  1106. long phs = 0;
  1107. if(0)
  1108. {
  1109. for(i=0; i<ctx->oChCnt; ++i)
  1110. if( ctx->oChArray[i] != NULL )
  1111. phs = _cmRtSynthSine(r, ctx->oChArray[i], 1, ss->args.dspFramesPerCycle );
  1112. }
  1113. else
  1114. {
  1115. if( _cmRtTestChIdx < ctx->oChCnt )
  1116. phs = _cmRtSynthSine(r, ctx->oChArray[_cmRtTestChIdx], 1, ss->args.dspFramesPerCycle );
  1117. }
  1118. r->phs = phs;
  1119. }
  1120. else
  1121. {
  1122. // BUG BUG BUG - this assumes that the input and output channels are the same.
  1123. unsigned chCnt = cmMin(ctx->oChCnt,ctx->iChCnt);
  1124. for(i=0; i<chCnt; ++i)
  1125. memcpy(ctx->oChArray[i],ctx->iChArray[i],sizeof(cmSample_t)*ss->args.dspFramesPerCycle);
  1126. }
  1127. }
  1128. else // ... otherwise it is a msg for the DSP process from the host
  1129. {
  1130. _cmRtTestMsg* msg = (_cmRtTestMsg*)msgDataPtr;
  1131. msg->rtSubIdx = ctx->rtSubIdx;
  1132. switch(msg->id)
  1133. {
  1134. case 0:
  1135. r->hz = msg->hz;
  1136. break;
  1137. case 1:
  1138. msg->uint = r->cbCnt;
  1139. msgByteCnt = sizeof(_cmRtTestMsg);
  1140. rc = ctx->dspToHostFunc(ctx,(const void **)&msg,&msgByteCnt,1);
  1141. break;
  1142. }
  1143. }
  1144. return rc;
  1145. }
  1146. // print the usage message for cmAudioPortTest.c
  1147. void _cmRtPrintUsage( cmRpt_t* rpt )
  1148. {
  1149. char msg[] =
  1150. "cmRtSysTest() command switches:\n"
  1151. "-r <srate> -c <chcnt> -b <bufcnt> -f <frmcnt> -i <idevidx> -o <odevidx> -m <msgqsize> -d <dspsize> -t -p -h \n"
  1152. "\n"
  1153. "-r <srate> = sample rate (48000)\n"
  1154. "-c <chcnt> = audio channels (2)\n"
  1155. "-b <bufcnt> = count of buffers (3)\n"
  1156. "-f <frmcnt> = count of samples per buffer (512)\n"
  1157. "-i <idevidx> = input device index (0)\n"
  1158. "-o <odevidx> = output device index (2)\n"
  1159. "-m <msgqsize> = message queue byte count (1024)\n"
  1160. "-d <dspsize> = samples per DSP frame (64)\n"
  1161. "-s = true: sync to input port false: sync to output port\n"
  1162. "-t = copy input to output otherwise synthesize a 1000 Hz sine (false)\n"
  1163. "-p = report but don't start audio devices\n"
  1164. "-h = print this usage message\n";
  1165. cmRptPrintf(rpt,"%s",msg);
  1166. }
  1167. // Get a command line option.
  1168. int _cmRtGetOpt( int argc, const char* argv[], const char* label, int defaultVal, bool boolFl )
  1169. {
  1170. int i = 0;
  1171. for(; i<argc; ++i)
  1172. if( strcmp(label,argv[i]) == 0 )
  1173. {
  1174. if(boolFl)
  1175. return 1;
  1176. if( i == (argc-1) )
  1177. return defaultVal;
  1178. return atoi(argv[i+1]);
  1179. }
  1180. return defaultVal;
  1181. }
  1182. bool _cmRtGetBoolOpt( int argc, const char* argv[], const char* label, bool defaultVal )
  1183. { return _cmRtGetOpt(argc,argv,label,defaultVal?1:0,true)!=0; }
  1184. int _cmRtGetIntOpt( int argc, const char* argv[], const char* label, int defaultVal )
  1185. { return _cmRtGetOpt(argc,argv,label,defaultVal,false); }
  1186. void cmRtSysTest( cmCtx_t* ctx, int argc, const char* argv[] )
  1187. {
  1188. cmRtSysSubSys_t ss;
  1189. cmRtSysH_t h = cmRtSysNullHandle;
  1190. cmRtSysStatus_t status;
  1191. _cmRtTestCbRecd cbRecd = {1000.0,0,48000.0,0};
  1192. cmRpt_t* rpt = &ctx->rpt;
  1193. memset(&status,0,sizeof(status));
  1194. unsigned meterMs = 50;
  1195. unsigned ssCnt = 1;
  1196. unsigned rtSubIdx = 0;
  1197. if(_cmRtGetBoolOpt(argc,argv,"-h",false))
  1198. _cmRtPrintUsage(rpt);
  1199. cbRecd.srate = _cmRtGetIntOpt(argc,argv,"-r",48000);
  1200. cbRecd.synthFl = _cmRtGetBoolOpt(argc,argv,"-t",false)==false;
  1201. ss.args.rpt = rpt;
  1202. ss.args.inDevIdx = _cmRtGetIntOpt( argc,argv,"-i",0);
  1203. ss.args.outDevIdx = _cmRtGetIntOpt( argc,argv,"-o",2);
  1204. ss.args.syncInputFl = _cmRtGetBoolOpt(argc,argv,"-s",true);
  1205. ss.args.msgQueueByteCnt = _cmRtGetIntOpt( argc,argv,"-m",8192);
  1206. ss.args.devFramesPerCycle = _cmRtGetIntOpt( argc,argv,"-f",512);
  1207. ss.args.dspFramesPerCycle = _cmRtGetIntOpt( argc,argv,"-d",64);;
  1208. ss.args.audioBufCnt = _cmRtGetIntOpt( argc,argv,"-b",3);
  1209. ss.args.srate = cbRecd.srate;
  1210. ss.cbFunc = _cmRtTestCb; // set the DSP entry function
  1211. ss.cbDataPtr = &cbRecd; // set the DSP function argument record
  1212. cmRptPrintf(rpt,"in:%i out:%i syncFl:%i que:%i fpc:%i dsp:%i bufs:%i sr:%f\n",ss.args.inDevIdx,ss.args.outDevIdx,ss.args.syncInputFl,
  1213. ss.args.msgQueueByteCnt,ss.args.devFramesPerCycle,ss.args.dspFramesPerCycle,ss.args.audioBufCnt,ss.args.srate);
  1214. if( cmApNrtAllocate(rpt) != kOkApRC )
  1215. goto errLabel;
  1216. if( cmApFileAllocate(rpt) != kOkApRC )
  1217. goto errLabel;
  1218. // initialize the audio device system
  1219. if( cmApInitialize(rpt) != kOkApRC )
  1220. goto errLabel;
  1221. cmApReport(rpt);
  1222. // initialize the audio buffer
  1223. if( cmApBufInitialize( cmApDeviceCount(), meterMs ) != kOkApRC )
  1224. goto errLabel;
  1225. // initialize the real-time system
  1226. if( cmRtSysAllocate(&h,ctx) != kOkRtRC )
  1227. goto errLabel;
  1228. if( cmRtSysBeginCfg(h,NULL,NULL,meterMs,ssCnt) != kOkRtRC )
  1229. goto errLabel;
  1230. if( cmRtSysCfg(h,&ss,rtSubIdx) != kOkRtRC )
  1231. goto errLabel;
  1232. if( cmRtSysEndCfg(h) != kOkRtRC )
  1233. goto errLabel;
  1234. // start the real-time system
  1235. cmRtSysEnable(h,true);
  1236. char c = 0;
  1237. printf("q=quit a-g=note n=ch r=rqst s=status\n");
  1238. // simulate a host event loop
  1239. while(c != 'q')
  1240. {
  1241. _cmRtTestMsg msg = {0,0,0,0};
  1242. bool fl = true;
  1243. // wait here for a key press
  1244. c =(char)fgetc(stdin);
  1245. fflush(stdin);
  1246. switch(c)
  1247. {
  1248. case 'c': msg.hz = cmMidiToHz(60); break;
  1249. case 'd': msg.hz = cmMidiToHz(62); break;
  1250. case 'e': msg.hz = cmMidiToHz(64); break;
  1251. case 'f': msg.hz = cmMidiToHz(65); break;
  1252. case 'g': msg.hz = cmMidiToHz(67); break;
  1253. case 'a': msg.hz = cmMidiToHz(69); break;
  1254. case 'b': msg.hz = cmMidiToHz(71); break;
  1255. case 'r': msg.id = 1; break; // request DSP process to send a callback count
  1256. case 'n': ++_cmRtTestChIdx; printf("ch:%i\n",_cmRtTestChIdx); break;
  1257. case 's':
  1258. // report the real-time system status
  1259. cmRtSysStatus(h,0,&status);
  1260. printf("phs:%li cb count:%i (upd:%i wake:%i acb:%i msgs:%i)\n",cbRecd.phs, cbRecd.cbCnt, status.updateCnt, status.wakeupCnt, status.audioCbCnt, status.msgCbCnt);
  1261. //printf("%f \n",status.oMeterArray[0]);
  1262. fl = false;
  1263. break;
  1264. default:
  1265. fl=false;
  1266. }
  1267. if( fl )
  1268. {
  1269. // transmit a command to the DSP process
  1270. cmRtSysDeliverMsg(h,&msg, sizeof(msg), cmInvalidId);
  1271. }
  1272. // check if messages are waiting to be delivered from the DSP process
  1273. unsigned msgByteCnt;
  1274. if((msgByteCnt = cmRtSysIsMsgWaiting(h)) > 0 )
  1275. {
  1276. char buf[ msgByteCnt ];
  1277. // rcv a msg from the DSP process
  1278. if( cmRtSysReceiveMsg(h,buf,msgByteCnt) == kOkRtRC )
  1279. {
  1280. _cmRtTestMsg* msg = (_cmRtTestMsg*)buf;
  1281. switch(msg->id)
  1282. {
  1283. case 1:
  1284. printf("RCV: Callback count:%i\n",msg->uint);
  1285. break;
  1286. }
  1287. }
  1288. }
  1289. // report the audio buffer status
  1290. //cmApBufReport(ss.args.rpt);
  1291. }
  1292. // stop the real-time system
  1293. cmRtSysEnable(h,false);
  1294. goto exitLabel;
  1295. errLabel:
  1296. printf("REAL-TIME SYSTEM TEST ERROR\n");
  1297. exitLabel:
  1298. cmRtSysFree(&h);
  1299. cmApFinalize();
  1300. cmApFileFree();
  1301. cmApNrtFree();
  1302. cmApBufFinalize();
  1303. }
  1304. /// [cmRtSysTest]