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.

cmAudioSys.c 45KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474
  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 "cmSerialPort.h"
  21. #include "cmAudioSysMsg.h"
  22. #include "cmAudioSys.h"
  23. #include "cmMidi.h"
  24. #include "cmMidiPort.h"
  25. #include "cmMath.h"
  26. cmAudioSysH_t cmAudioSysNullHandle = { NULL };
  27. struct cmAs_str;
  28. typedef struct
  29. {
  30. struct cmAs_str* p; // pointer to the audio system instance which owns this sub-system
  31. cmAudioSysSubSys_t ss; // sub-system configuration record
  32. cmAudioSysCtx_t ctx; // DSP context
  33. cmAudioSysStatus_t status; // current runtime status of this sub-system
  34. cmThreadH_t threadH; // audio system thread
  35. cmTsMp1cH_t htdQueueH; // host-to-dsp thread safe msg queue
  36. cmThreadMutexH_t engMutexH; // thread mutex and condition variable
  37. cmUdpNetH_t netH;
  38. cmSeH_t serialPortH;
  39. bool enableFl; // application controlled pause flag
  40. bool runFl; // false during finalization otherwise true
  41. bool statusFl; // true if regular status notifications should be sent
  42. unsigned audCbLock; //
  43. bool syncInputFl;
  44. double* iMeterArray; //
  45. double* oMeterArray; //
  46. unsigned statusUpdateSmpCnt; // transmit a state update msg every statusUpdateSmpCnt samples
  47. unsigned statusUpdateSmpIdx; // state update phase
  48. } _cmAsCfg_t;
  49. typedef struct cmAs_str
  50. {
  51. cmErr_t err;
  52. _cmAsCfg_t* ssArray;
  53. unsigned ssCnt;
  54. unsigned waitAsSubIdx; // index of the next sub-system to try with cmAudioSysIsMsgWaiting().
  55. cmTsMp1cH_t dthQueH;
  56. bool initFl; // true if the audio system is initialized
  57. } cmAs_t;
  58. cmAs_t* _cmAsHandleToPtr( cmAudioSysH_t h )
  59. {
  60. cmAs_t* p = (cmAs_t*)h.h;
  61. assert(p != NULL);
  62. return p;
  63. }
  64. cmAsRC_t _cmAsError( cmAs_t* p, cmAsRC_t rc, const char* fmt, ... )
  65. {
  66. va_list vl;
  67. va_start(vl,fmt);
  68. cmErrVMsg(&p->err,rc,fmt,vl);
  69. va_end(vl);
  70. return rc;
  71. }
  72. // Wrapper function to put msgs into thread safe queues and handle related errors.
  73. cmAsRC_t _cmAsEnqueueMsg( cmAs_t* p, cmTsMp1cH_t qH, const void* msgDataPtrArray[], unsigned msgCntArray[], unsigned segCnt, const char* queueLabel )
  74. {
  75. cmAsRC_t rc = kOkAsRC;
  76. switch( cmTsMp1cEnqueueSegMsg(qH, msgDataPtrArray, msgCntArray, segCnt) )
  77. {
  78. case kOkThRC:
  79. break;
  80. case kBufFullThRC:
  81. {
  82. unsigned i;
  83. unsigned byteCnt = 0;
  84. for(i=0; i<segCnt; ++i)
  85. byteCnt += msgCntArray[i];
  86. rc = _cmAsError(p,kMsgEnqueueFailAsRC,"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));
  87. }
  88. break;
  89. default:
  90. rc = _cmAsError(p,kMsgEnqueueFailAsRC,"A %s msg. enqueue failed.",queueLabel);
  91. }
  92. return rc;
  93. }
  94. // This is the function pointed to by ctx->dspToHostFunc.
  95. // It is called by the DSP proces to pass msgs to the host.
  96. // therefore it is always called from inside of _cmAsDspExecCallback().
  97. cmAsRC_t _cmAsDspToHostMsgCallback(struct cmAudioSysCtx_str* ctx, const void* msgDataPtrArray[], unsigned msgByteCntArray[], unsigned msgSegCnt)
  98. {
  99. cmAs_t* p = (cmAs_t*)ctx->reserved;
  100. assert( ctx->asSubIdx < p->ssCnt );
  101. //return _cmAsEnqueueMsg(p,p->ssArray[ctx->asSubIdx].dthQueueH,msgDataPtrArray,msgByteCntArray,msgSegCnt,"DSP-to-Host");
  102. return _cmAsEnqueueMsg(p,p->dthQueH,msgDataPtrArray,msgByteCntArray,msgSegCnt,"DSP-to-Host");
  103. }
  104. cmAsRC_t _cmAsHostInitNotify( cmAs_t* p )
  105. {
  106. cmAsRC_t rc = kOkAsRC;
  107. unsigned i;
  108. for(i=0; i<p->ssCnt; ++i)
  109. {
  110. cmAudioSysSsInitMsg_t m;
  111. _cmAsCfg_t* cp = p->ssArray + i;
  112. const char* inDevLabel = cp->ss.args.inDevIdx == cmInvalidIdx ? "" : cmApDeviceLabel( cp->ss.args.inDevIdx );
  113. const char* outDevLabel = cp->ss.args.outDevIdx == cmInvalidIdx ? "" : cmApDeviceLabel( cp->ss.args.outDevIdx );
  114. m.asSubIdx = i;
  115. m.selId = kSsInitSelAsId;
  116. m.asSubCnt = p->ssCnt;
  117. m.inDevIdx = cp->ss.args.inDevIdx;
  118. m.outDevIdx = cp->ss.args.outDevIdx;
  119. m.dspFramesPerCycle = cp->ss.args.dspFramesPerCycle;
  120. m.srate = cp->ss.args.srate;
  121. m.inChCnt = cp->status.iMeterCnt;
  122. m.outChCnt = cp->status.oMeterCnt;
  123. unsigned segCnt = 3;
  124. const void* msgDataPtrArray[] = { &m, inDevLabel, outDevLabel };
  125. unsigned msgByteCntArray[] = { sizeof(m), strlen(cmStringNullGuard(inDevLabel))+1, strlen(cmStringNullGuard(outDevLabel))+1 };
  126. assert( sizeof(msgDataPtrArray)/sizeof(void*) == segCnt);
  127. assert( sizeof(msgByteCntArray)/sizeof(unsigned) == segCnt);
  128. if((rc = _cmAsDspToHostMsgCallback(&cp->ctx, msgDataPtrArray, msgByteCntArray, segCnt)) != kOkAsRC )
  129. return rc;
  130. }
  131. return rc;
  132. }
  133. cmAsRC_t _cmAsParseNonSubSysMsg( cmAs_t* p, const void* msg, unsigned msgByteCnt )
  134. {
  135. cmAsRC_t rc = kOkAsRC;
  136. cmAudioSysMstr_t* h = (cmAudioSysMstr_t*)msg;
  137. unsigned devIdx = cmAudioSysUiInstIdToDevIndex(h->instId);
  138. unsigned chIdx = cmAudioSysUiInstIdToChIndex(h->instId);
  139. unsigned inFl = cmAudioSysUiInstIdToInFlag(h->instId);
  140. unsigned ctlId = cmAudioSysUiInstIdToCtlId(h->instId);
  141. // if the valuu associated with this msg is a mtx then set
  142. // its mtx data area pointer to just after the msg header.
  143. //if( cmDsvIsMtx(&h->value) )
  144. // h->value.u.m.u.vp = ((char*)msg) + sizeof(cmDspUiHdr_t);
  145. unsigned flags = inFl ? kInApFl : kOutApFl;
  146. switch( ctlId )
  147. {
  148. case kSliderUiAsId: // slider
  149. cmApBufSetGain(devIdx,chIdx, flags, h->value);
  150. break;
  151. case kMeterUiAsId: // meter
  152. break;
  153. case kMuteUiAsId: // mute
  154. flags += h->value == 0 ? kEnableApFl : 0;
  155. cmApBufEnableChannel(devIdx,chIdx,flags);
  156. break;
  157. case kToneUiAsId: // tone
  158. flags += h->value > 0 ? kEnableApFl : 0;
  159. cmApBufEnableTone(devIdx,chIdx,flags);
  160. break;
  161. case kPassUiAsId: // pass
  162. flags += h->value > 0 ? kEnableApFl : 0;
  163. cmApBufEnablePass(devIdx,chIdx,flags);
  164. break;
  165. default:
  166. { assert(0); }
  167. }
  168. return rc;
  169. }
  170. // Process a UI msg sent from the host to the audio system
  171. cmAsRC_t _cmAsHandleNonSubSysMsg( cmAs_t* p, const void* msgDataPtrArray[], unsigned msgByteCntArray[], unsigned msgSegCnt )
  172. {
  173. cmAsRC_t rc = kOkAsRC;
  174. // if the message is contained in a single segment it can be dispatched immediately ...
  175. if( msgSegCnt == 1 )
  176. rc = _cmAsParseNonSubSysMsg(p,msgDataPtrArray[0],msgByteCntArray[0]);
  177. else
  178. {
  179. // ... otherwise deserialize the message into contiguous memory ....
  180. unsigned byteCnt = 0;
  181. unsigned i;
  182. for(i=0; i<msgSegCnt; ++i)
  183. byteCnt += msgByteCntArray[i];
  184. char buf[ byteCnt ];
  185. char* b = buf;
  186. for(i=0; i<msgSegCnt; ++i)
  187. {
  188. memcpy(b, msgDataPtrArray[i], msgByteCntArray[i] );
  189. b += msgByteCntArray[i];
  190. }
  191. // ... and then dispatch it
  192. rc = _cmAsParseNonSubSysMsg(p,buf,byteCnt);
  193. }
  194. return rc;
  195. }
  196. cmAsRC_t _cmAsSendStateStatusToHost( _cmAsCfg_t* cp )
  197. {
  198. cmAsRC_t rc = kOkAsRC;
  199. unsigned hdr[] = { cp->ctx.asSubIdx, kStatusSelAsId };
  200. cmApBufGetStatus( cp->ss.args.inDevIdx, kInApFl, cp->iMeterArray, cp->status.iMeterCnt, &cp->status.overflowCnt );
  201. cmApBufGetStatus( cp->ss.args.outDevIdx, kOutApFl, cp->oMeterArray, cp->status.oMeterCnt, &cp->status.underflowCnt );
  202. unsigned iMeterByteCnt = sizeof(cp->iMeterArray[0]) * cp->status.iMeterCnt;
  203. unsigned oMeterByteCnt = sizeof(cp->oMeterArray[0]) * cp->status.oMeterCnt;
  204. const void* msgDataPtrArray[] = { &hdr, &cp->status, cp->iMeterArray, cp->oMeterArray };
  205. unsigned msgByteCntArray[] = { sizeof(hdr), sizeof(cp->status), iMeterByteCnt, oMeterByteCnt };
  206. unsigned segCnt = sizeof(msgByteCntArray)/sizeof(unsigned);
  207. _cmAsDspToHostMsgCallback(&cp->ctx,msgDataPtrArray,msgByteCntArray, segCnt );
  208. return rc;
  209. }
  210. // The DSP execution callback happens through this function.
  211. // This function is only called from inside _cmAsThreadCallback() with the engine mutex locked.
  212. void _cmAsDspExecCallback( _cmAsCfg_t* cp )
  213. {
  214. /*
  215. unsigned i;
  216. // get pointers to a set of audio out buffers - pointers to disabled channels will be set to NULL
  217. cmApBufGet( cp->ss.args.outDevIdx, kOutApFl, cp->ctx.oChArray, cp->ctx.oChCnt );
  218. cmApBufGet( cp->ss.args.inDevIdx, kInApFl, cp->ctx.iChArray, cp->ctx.iChCnt );
  219. // zero the output buffers on all enabled channels
  220. for(i=0; i<cp->ctx.oChCnt; ++i)
  221. if( cp->ctx.oChArray[i] != NULL )
  222. memset( cp->ctx.oChArray[i], 0, cp->ss.args.dspFramesPerCycle * sizeof(cmSample_t));
  223. */
  224. // Fill iChArray[] and oChArray[] with pointers to the incoming and outgoing sample buffers.
  225. // Notes:
  226. // 1) Buffers associated with disabled input/output channels will be set to NULL in iChArray[]/oChArray[].
  227. // 2) Buffers associated with channels marked for pass-through will be set to NULL in oChArray[].
  228. // 3) All samples returned in oChArray[] buffers will be set to zero.
  229. cmApBufGetIO(cp->ss.args.inDevIdx, cp->ctx.iChArray, cp->ctx.iChCnt, &cp->ctx.iTimeStamp,
  230. cp->ss.args.outDevIdx, cp->ctx.oChArray, cp->ctx.oChCnt, &cp->ctx.oTimeStamp );
  231. // call the application provided DSP process
  232. cp->ctx.audioRateFl = true;
  233. cp->ss.cbFunc( &cp->ctx, 0, NULL );
  234. cp->ctx.audioRateFl = false;
  235. // advance the audio buffer
  236. cmApBufAdvance( cp->ss.args.outDevIdx, kOutApFl );
  237. cmApBufAdvance( cp->ss.args.inDevIdx, kInApFl );
  238. // handle periodic status messages to the host
  239. if( (cp->statusUpdateSmpIdx += cp->ss.args.dspFramesPerCycle) >= cp->statusUpdateSmpCnt )
  240. {
  241. cp->statusUpdateSmpIdx -= cp->statusUpdateSmpCnt;
  242. if( cp->statusFl )
  243. _cmAsSendStateStatusToHost(cp);
  244. }
  245. }
  246. // Returns true if audio buffer is has waiting incoming samples and
  247. // available outgoing space.
  248. bool _cmAsBufIsReady( const _cmAsCfg_t* cp )
  249. {
  250. // if there neither the input or output device is valid
  251. if( cp->ss.args.inDevIdx==cmInvalidIdx && cp->ss.args.outDevIdx == cmInvalidIdx )
  252. return false;
  253. bool ibFl = cmApBufIsDeviceReady(cp->ss.args.inDevIdx, kInApFl);
  254. bool obFl = cmApBufIsDeviceReady(cp->ss.args.outDevIdx, kOutApFl);
  255. bool iFl = (cp->ss.args.inDevIdx == cmInvalidIdx) || ibFl;
  256. bool oFl = (cp->ss.args.outDevIdx == cmInvalidIdx) || obFl;
  257. //printf("br: %i %i %i %i\n",ibFl,obFl,iFl,oFl);
  258. return iFl && oFl;
  259. }
  260. // This is only called with _cmAsRecd.engMutexH locked
  261. cmAsRC_t _cmAsDeliverMsgsWithLock( _cmAsCfg_t* cp )
  262. {
  263. int i;
  264. cmAsRC_t rc = kOkThRC;
  265. // as long as their may be a msg wating in the incoming msg queue
  266. for(i=0; rc == kOkThRC; ++i)
  267. {
  268. // if a msg is waiting transmit it via cfg->cbFunc()
  269. if((rc = cmTsMp1cDequeueMsg(cp->htdQueueH,NULL,0)) == kOkThRC)
  270. ++cp->status.msgCbCnt;
  271. }
  272. return rc;
  273. }
  274. // This is the main audio system loop (and thread callback function).
  275. // It blocks by waiting on a cond. var (which simultaneously unlocks a mutex).
  276. // With the mutex unlocked messages can pass directly to the DSP process
  277. // via calls to cmAsDeliverMsg().
  278. // When the audio buffers need to be serviced the audio device callback
  279. // signals the cond. var. which results in this thread waking up (and
  280. // simultaneously locking the mutex) as soon as the mutex is available.
  281. bool _cmAsThreadCallback(void* arg)
  282. {
  283. cmAsRC_t rc;
  284. _cmAsCfg_t* cp = (_cmAsCfg_t*)arg;
  285. // lock the cmAudioSys mutex
  286. if((rc = cmThreadMutexLock(cp->engMutexH)) != kOkAsRC )
  287. {
  288. _cmAsError(cp->p,rc,"The cmAudioSys thread mutex lock failed.");
  289. return false;
  290. }
  291. // runFl is always set except during finalization
  292. while( cp->runFl )
  293. {
  294. // if the buffer is NOT ready or the cmAudioSys is disabled
  295. if(_cmAsBufIsReady(cp) == false || cp->enableFl==false )
  296. {
  297. // block on the cond var and unlock the mutex
  298. if( cmThreadMutexWaitOnCondVar(cp->engMutexH,false) != kOkAsRC )
  299. {
  300. cmThreadMutexUnlock(cp->engMutexH);
  301. _cmAsError(cp->p,rc,"The cmAudioSys cond. var. wait failed.");
  302. return false;
  303. }
  304. //
  305. // the cond var was signaled and the mutex is now locked
  306. //
  307. ++cp->status.wakeupCnt;
  308. }
  309. // be sure we are still enabled and the buffer is still ready
  310. if( cp->enableFl && cp->runFl )
  311. {
  312. while( _cmAsBufIsReady(cp) )
  313. {
  314. ++cp->status.audioCbCnt;
  315. // calling this function results in callbacks to cmAudDsp.c:_cmAdUdpNetCallback()
  316. // which in turn calls cmAudioSysDeliverMsg() which queues any incoming messages
  317. // which are then transferred to the DSP processes by the the call to
  318. // _cmAsDeliverMsgWithLock() below.
  319. cmUdpNetReceive(cp->netH,NULL);
  320. // if there are msgs waiting to be sent to the DSP process send them.
  321. if( cmTsMp1cMsgWaiting(cp->htdQueueH) )
  322. _cmAsDeliverMsgsWithLock(cp);
  323. // make the cmAudioSys callback
  324. _cmAsDspExecCallback( cp );
  325. // update the signal time
  326. cp->ctx.begSmpIdx += cp->ss.args.dspFramesPerCycle;
  327. }
  328. }
  329. }
  330. // unlock the mutex
  331. cmThreadMutexUnlock(cp->engMutexH);
  332. return true;
  333. }
  334. // This is the audio port callback function.
  335. //
  336. // _cmAudioSysAudioUpdate() assumes that at most two audio device threads (input and output) may call it.
  337. // cmApBufUpdate() is safe under these conditions since the input and output buffers are updated separately.
  338. // p->audCbLock is used to allow either the input or output thread to signal
  339. // the condition variable. This flag is necessary to prevent both threads from simultaneously
  340. // attempting to signal the condition variable (which will lock the system).
  341. //
  342. // If more than two audio device threads call the function then this function is not safe.
  343. unsigned phase = 0;
  344. void _cmAudioSysAudioUpdate( cmApAudioPacket_t* inPktArray, unsigned inPktCnt, cmApAudioPacket_t* outPktArray, unsigned outPktCnt )
  345. {
  346. _cmAsCfg_t* cp = (_cmAsCfg_t*)(inPktArray!=NULL ? inPktArray[0].userCbPtr : outPktArray[0].userCbPtr);
  347. ++cp->status.updateCnt;
  348. if( cp->runFl )
  349. {
  350. // transfer incoming/outgoing samples from/to the audio device
  351. cmApBufUpdate(inPktArray,inPktCnt,outPktArray,outPktCnt);
  352. /*
  353. //fill output with noise
  354. unsigned i = 0,j =0, k = 0, phs = 0;
  355. for(; i<outPktCnt; ++i)
  356. {
  357. cmApAudioPacket_t* a = outPktArray + i;
  358. cmApSample_t* dp = (cmApSample_t*)a->audioBytesPtr;
  359. phs = a->audioFramesCnt;
  360. for(j=0; j<a->audioFramesCnt; ++j)
  361. {
  362. cmApSample_t v = (cmApSample_t)(0.7 * sin(2*M_PI/44100.0 * phase + j ));
  363. for(k=0; k<a->chCnt; ++k,++dp)
  364. *dp = v;
  365. }
  366. //for(j=0; j<a->audioFramesCnt*a->chCnt; ++j,++dp)
  367. // *dp = (cmApSample_t)(rand() - (RAND_MAX/2))/(RAND_MAX/2);
  368. }
  369. phase += phs;
  370. return;
  371. */
  372. //++p->audCbLock;
  373. bool testBufFl = (cp->syncInputFl==true && inPktCnt>0) || (cp->syncInputFl==false && outPktCnt>0);
  374. //printf("%i %i %i %i\n",testBufFl,cp->syncInputFl,inPktCnt,outPktCnt);
  375. // if the input/output buffer contain samples to be processed then signal the condition variable
  376. // - this will cause the audio system thread to unblock and the used defined DSP process will be called.
  377. if( testBufFl && _cmAsBufIsReady(cp) )
  378. {
  379. if( cmThreadMutexSignalCondVar(cp->engMutexH) != kOkThRC )
  380. _cmAsError(cp->p,kMutexErrAsRC,"CmAudioSys signal cond. var. failed.");
  381. }
  382. //--p->audCbLock;
  383. }
  384. }
  385. // Called when MIDI messages arrive from external MIDI ports.
  386. void _cmAudioSysMidiCallback( const cmMidiPacket_t* pktArray, unsigned pktCnt )
  387. {
  388. unsigned i;
  389. for(i=0; i<pktCnt; ++i)
  390. {
  391. const cmMidiPacket_t* pkt = pktArray + i;
  392. _cmAsCfg_t* cp = (_cmAsCfg_t*)(pkt->cbDataPtr);
  393. if( !cp->runFl )
  394. continue;
  395. cmAudioSysH_t asH;
  396. asH.h = cp->p;
  397. unsigned selId = kMidiMsgArraySelAsId;
  398. const void* msgPtrArray[] = { &cp->ctx.asSubIdx, &selId, &pkt->devIdx, &pkt->portIdx, &pkt->msgCnt, pkt->msgArray };
  399. unsigned msgByteCntArray[] = { sizeof(cp->ctx.asSubIdx), sizeof(selId), sizeof(pkt->devIdx), sizeof(pkt->portIdx), sizeof(pkt->msgCnt), pkt->msgCnt*sizeof(cmMidiMsg) };
  400. unsigned msgSegCnt = sizeof(msgByteCntArray)/sizeof(unsigned);
  401. cmAudioSysDeliverSegMsg(asH,msgPtrArray,msgByteCntArray,msgSegCnt,cmInvalidId);
  402. }
  403. }
  404. void _cmAudioSysSerialPortCallback( void* cbArg, const void* byteA, unsigned byteN )
  405. {
  406. //_cmAsCfg_t* p (_cmAsCfg_t*)cbArg;
  407. // TODO: handle serial receive
  408. /*
  409. int i;
  410. for(i=0; i<byteN; ++i)
  411. {
  412. printf("%02x ",((const uint8_t*)byteA)[i]);
  413. fflush(stdout);
  414. }
  415. */
  416. }
  417. cmAsRC_t cmAudioSysAllocate( cmAudioSysH_t* hp, cmRpt_t* rpt, const cmAudioSysCfg_t* cfg )
  418. {
  419. cmAsRC_t rc;
  420. if((rc = cmAudioSysFree(hp)) != kOkAsRC )
  421. return rc;
  422. cmAs_t* p = cmMemAllocZ( cmAs_t, 1 );
  423. cmErrSetup(&p->err,rpt,"Audio System");
  424. hp->h = p;
  425. if( cfg != NULL )
  426. if((rc = cmAudioSysInitialize( *hp, cfg )) != kOkAsRC )
  427. cmAudioSysFree(hp);
  428. return rc;
  429. }
  430. cmAsRC_t cmAudioSysFree( cmAudioSysH_t* hp )
  431. {
  432. cmAsRC_t rc;
  433. if( hp == NULL || hp->h == NULL )
  434. return kOkAsRC;
  435. if((rc = cmAudioSysFinalize(*hp)) != kOkAsRC )
  436. return rc;
  437. cmAs_t* p = _cmAsHandleToPtr(*hp);
  438. cmMemFree(p);
  439. hp->h = NULL;
  440. return rc;
  441. }
  442. cmAsRC_t _cmAudioSysEnable( cmAs_t* p, bool enableFl )
  443. {
  444. cmAsRC_t rc;
  445. unsigned i;
  446. for(i=0; i<p->ssCnt; ++i)
  447. {
  448. _cmAsCfg_t* cp = p->ssArray + i;
  449. cmApBufOnPortEnable(cp->ss.args.inDevIdx,enableFl);
  450. cmApBufOnPortEnable(cp->ss.args.outDevIdx,enableFl);
  451. if( enableFl )
  452. {
  453. //cmApBufPrimeOutput( cp->ss.args.outDevIdx, 2 );
  454. // start the input device
  455. if((rc = cmApDeviceStart( cp->ss.args.inDevIdx )) != kOkAsRC )
  456. return _cmAsError(p,kAudioDevStartFailAsRC,"The audio input device start failed.");
  457. // start the output device
  458. if( cmApDeviceStart( cp->ss.args.outDevIdx ) != kOkAsRC )
  459. return _cmAsError(p,kAudioDevStartFailAsRC,"The audio ouput device start failed.");
  460. }
  461. else
  462. {
  463. // stop the input device
  464. if((rc = cmApDeviceStop( cp->ss.args.inDevIdx )) != kOkAsRC )
  465. return _cmAsError(p,kAudioDevStopFailAsRC,"The audio input device stop failed.");
  466. // stop the output device
  467. if((rc = cmApDeviceStop( cp->ss.args.outDevIdx )) != kOkAsRC )
  468. return _cmAsError(p,kAudioDevStopFailAsRC,"The audio output device stop failed.");
  469. }
  470. cp->enableFl = enableFl;
  471. }
  472. return kOkAsRC;
  473. }
  474. cmAsRC_t _cmAudioSysFinalize( cmAs_t* p )
  475. {
  476. cmAsRC_t rc = kOkAsRC;
  477. unsigned i;
  478. // mark the audio system as NOT initialized
  479. p->initFl = false;
  480. // be sure all audio callbacks are disabled before continuing.
  481. if((rc = _cmAudioSysEnable(p,false)) != kOkAsRC )
  482. return _cmAsError(p,rc,"Audio system finalize failed because device halting failed.");
  483. for(i=0; i<p->ssCnt; ++i)
  484. {
  485. _cmAsCfg_t* cp = p->ssArray + i;
  486. if( cmThreadIsValid( cp->threadH ))
  487. {
  488. // inform the thread that it should exit
  489. cp->enableFl = false;
  490. cp->runFl = false;
  491. cp->statusFl = false;
  492. // WARNING: be sure that the audio thread cannot simultaneously signal the
  493. // cond variable from _cmAsAudioUpdate() otherwise the system may crash
  494. while( cp->audCbLock != 0 )
  495. { cmSleepUs(100000); }
  496. // signal the cond var to cause the thread to run
  497. if((rc = cmThreadMutexSignalCondVar(cp->engMutexH)) != kOkThRC )
  498. _cmAsError(p,kMutexErrAsRC,"Finalize signal cond. var. failed.");
  499. // wait to take control of the mutex - this will occur when the thread function exits
  500. if((rc = cmThreadMutexLock(cp->engMutexH)) != kOkThRC )
  501. _cmAsError(p,kMutexErrAsRC,"Finalize lock failed.");
  502. // unlock the mutex because it is no longer needed and must be unlocked to be destroyed
  503. if((rc = cmThreadMutexUnlock(cp->engMutexH)) != kOkThRC )
  504. _cmAsError(p,kMutexErrAsRC,"Finalize unlock failed.");
  505. // destroy the thread
  506. if((rc = cmThreadDestroy( &cp->threadH )) != kOkThRC )
  507. _cmAsError(p,kThreadErrAsRC,"Thread destroy failed.");
  508. }
  509. // destroy the mutex
  510. if( cmThreadMutexIsValid(cp->engMutexH) )
  511. if((rc = cmThreadMutexDestroy( &cp->engMutexH )) != kOkThRC )
  512. _cmAsError(p,kMutexErrAsRC,"Mutex destroy failed.");
  513. // remove the MIDI callback
  514. if( cmMpIsInitialized() && cmMpUsesCallback(-1,-1, _cmAudioSysMidiCallback, cp) )
  515. if( cmMpRemoveCallback( -1, -1, _cmAudioSysMidiCallback, cp ) != kOkMpRC )
  516. _cmAsError(p,kMidiSysFailAsRC,"MIDI callback removal failed.");
  517. // destroy the host-to-dsp msg queue
  518. if( cmTsMp1cIsValid(cp->htdQueueH ) )
  519. if((rc = cmTsMp1cDestroy( &cp->htdQueueH )) != kOkThRC )
  520. _cmAsError(p,kTsQueueErrAsRC,"Host-to-DSP msg queue destroy failed.");
  521. // destroy the dsp-to-host msg queue
  522. if( cmTsMp1cIsValid(p->dthQueH) )
  523. if((rc = cmTsMp1cDestroy( &p->dthQueH )) != kOkThRC )
  524. _cmAsError(p,kTsQueueErrAsRC,"DSP-to-Host msg queue destroy failed.");
  525. cmMemPtrFree(&cp->ctx.iChArray);
  526. cmMemPtrFree(&cp->ctx.oChArray);
  527. cp->ctx.iChCnt = 0;
  528. cp->ctx.oChCnt = 0;
  529. cmMemPtrFree(&cp->iMeterArray);
  530. cmMemPtrFree(&cp->oMeterArray);
  531. cp->status.iMeterCnt = 0;
  532. cp->status.oMeterCnt = 0;
  533. }
  534. cmMemPtrFree(&p->ssArray);
  535. p->ssCnt = 0;
  536. return rc;
  537. }
  538. // A given device may be used as an input device exactly once and an output device exactly once.
  539. // When the input to a given device is used by one sub-system and the output is used by another
  540. // then both sub-systems must use the same srate,devFramesPerCycle, audioBufCnt and dspFramesPerCycle.
  541. cmAsRC_t _cmAsSysValidate( cmErr_t* err, const cmAudioSysCfg_t* cfg )
  542. {
  543. unsigned i,j,k;
  544. for(i=0; i<2; ++i)
  545. {
  546. // examine input devices - then output devices
  547. bool inputFl = i==0;
  548. bool outputFl = !inputFl;
  549. for(j=0; j<cfg->ssCnt; ++j)
  550. {
  551. cmAudioSysArgs_t* s0 = &cfg->ssArray[j].args;
  552. unsigned devIdx = inputFl ? s0->inDevIdx : s0->outDevIdx;
  553. for(k=0; k<cfg->ssCnt && devIdx != cmInvalidIdx; ++k)
  554. if( k != j )
  555. {
  556. cmAudioSysArgs_t* s1 = &cfg->ssArray[k].args;
  557. // if the device was used as input or output multple times then signal an error
  558. if( (inputFl && (s1->inDevIdx == devIdx) && s1->inDevIdx != cmInvalidIdx) || (outputFl && (s1->outDevIdx == devIdx) && s1->outDevIdx != cmInvalidIdx) )
  559. return cmErrMsg(err,kInvalidArgAsRC,"The device %i was used as an %s by multiple sub-systems.", devIdx, inputFl ? "input" : "output");
  560. // if this device is being used by another subsystem ...
  561. if( (inputFl && (s1->outDevIdx == devIdx) && s1->inDevIdx != cmInvalidIdx) || (outputFl && (s1->outDevIdx == devIdx) && s1->outDevIdx != cmInvalidIdx ) )
  562. {
  563. // ... then some of its buffer spec's must match
  564. if( s0->srate != s1->srate || s0->audioBufCnt != s1->audioBufCnt || s0->dspFramesPerCycle != s1->dspFramesPerCycle || s0->devFramesPerCycle != s1->devFramesPerCycle )
  565. return cmErrMsg(err,kInvalidArgAsRC,"The device %i is used by different sub-system with different audio buffer parameters.",devIdx);
  566. }
  567. }
  568. }
  569. }
  570. return kOkAsRC;
  571. }
  572. cmAsRC_t cmAudioSysInitialize( cmAudioSysH_t h, const cmAudioSysCfg_t* cfg )
  573. {
  574. cmAsRC_t rc;
  575. unsigned i;
  576. cmAs_t* p = _cmAsHandleToPtr(h);
  577. // validate the device setup
  578. if((rc =_cmAsSysValidate(&p->err, cfg )) != kOkAsRC )
  579. return rc;
  580. // always finalize before iniitalize
  581. if((rc = cmAudioSysFinalize(h)) != kOkAsRC )
  582. return rc;
  583. // create the audio file devices
  584. /*
  585. for(i=0; i<cfg->afpCnt; ++i)
  586. {
  587. const cmAudioSysFilePort_t* afp = cfg->afpArray + i;
  588. cmApFileDeviceCreate( afp->devLabel, afp->inAudioFn, afp->outAudioFn, afp->oBits, afp->oChCnt );
  589. }
  590. */
  591. p->ssArray = cmMemAllocZ( _cmAsCfg_t, cfg->ssCnt );
  592. p->ssCnt = cfg->ssCnt;
  593. for(i=0; i<p->ssCnt; ++i)
  594. {
  595. _cmAsCfg_t* cp = p->ssArray + i;
  596. const cmAudioSysSubSys_t* ss = cfg->ssArray + i;
  597. cp->p = p;
  598. cp->ss = *ss; // copy the cfg into the internal audio system state
  599. cp->runFl = false;
  600. cp->enableFl = false;
  601. cp->statusFl = false;
  602. cp->ctx.reserved = p;
  603. cp->ctx.asSubIdx = i;
  604. cp->ctx.ss = &cp->ss;
  605. cp->ctx.begSmpIdx = 0;
  606. cp->ctx.dspToHostFunc = _cmAsDspToHostMsgCallback;
  607. // validate the input device index
  608. if( ss->args.inDevIdx != cmInvalidIdx && ss->args.inDevIdx >= cmApDeviceCount() )
  609. {
  610. rc = _cmAsError(p,kAudioDevSetupErrAsRC,"The audio input device index %i is invalid.",ss->args.inDevIdx);
  611. goto errLabel;
  612. }
  613. // validate the output device index
  614. if( ss->args.outDevIdx != cmInvalidIdx && ss->args.outDevIdx >= cmApDeviceCount() )
  615. {
  616. rc = _cmAsError(p,kAudioDevSetupErrAsRC,"The audio output device index %i is invalid.",ss->args.outDevIdx);
  617. goto errLabel;
  618. }
  619. // setup the input device
  620. if( ss->args.inDevIdx != cmInvalidIdx )
  621. if((rc = cmApDeviceSetup( ss->args.inDevIdx, ss->args.srate, ss->args.devFramesPerCycle, _cmAudioSysAudioUpdate, cp )) != kOkAsRC )
  622. {
  623. rc = _cmAsError(p,kAudioDevSetupErrAsRC,"Audio input device setup failed.");
  624. goto errLabel;
  625. }
  626. // setup the output device
  627. if( ss->args.outDevIdx != ss->args.inDevIdx && ss->args.outDevIdx != cmInvalidIdx )
  628. if((rc = cmApDeviceSetup( ss->args.outDevIdx, ss->args.srate, ss->args.devFramesPerCycle, _cmAudioSysAudioUpdate, cp )) != kOkAsRC )
  629. {
  630. rc = _cmAsError(p,kAudioDevSetupErrAsRC,"Audio output device setup failed.");
  631. goto errLabel;
  632. }
  633. // setup the input device buffer
  634. if( ss->args.inDevIdx != cmInvalidIdx )
  635. 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 )) != kOkAsRC )
  636. {
  637. rc = _cmAsError(p,kAudioBufSetupErrAsRC,"Audio buffer input setup failed.");
  638. goto errLabel;
  639. }
  640. cmApBufEnableMeter(ss->args.inDevIdx, -1, kInApFl | kEnableApFl );
  641. cmApBufEnableMeter(ss->args.outDevIdx,-1, kOutApFl | kEnableApFl );
  642. // setup the input audio buffer ptr array - used to send input audio to the DSP system in _cmAsDspExecCallback()
  643. if((cp->ctx.iChCnt = cmApDeviceChannelCount(ss->args.inDevIdx, true)) != 0 )
  644. cp->ctx.iChArray = cmMemAllocZ( cmSample_t*, cp->ctx.iChCnt );
  645. // setup the output device buffer
  646. if( ss->args.outDevIdx != ss->args.inDevIdx )
  647. 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 )) != kOkAsRC )
  648. return _cmAsError(p,kAudioBufSetupErrAsRC,"Audio buffer ouput device setup failed.");
  649. // setup the output audio buffer ptr array - used to recv output audio from the DSP system in _cmAsDspExecCallback()
  650. if((cp->ctx.oChCnt = cmApDeviceChannelCount(ss->args.outDevIdx, false)) != 0 )
  651. cp->ctx.oChArray = cmMemAllocZ( cmSample_t*, cp->ctx.oChCnt );
  652. // determine the sync source
  653. cp->syncInputFl = ss->args.syncInputFl;
  654. // if sync'ing to an unavailable device then sync to the available device
  655. if( ss->args.syncInputFl && cp->ctx.iChCnt == 0 )
  656. cp->syncInputFl = false;
  657. if( ss->args.syncInputFl==false && cp->ctx.oChCnt == 0 )
  658. cp->syncInputFl = true;
  659. // setup the status record
  660. cp->status.asSubIdx = cp->ctx.asSubIdx;
  661. cp->status.iDevIdx = ss->args.inDevIdx;
  662. cp->status.oDevIdx = ss->args.outDevIdx;
  663. cp->status.iMeterCnt = cp->ctx.iChCnt;
  664. cp->status.oMeterCnt = cp->ctx.oChCnt;
  665. cp->iMeterArray = cmMemAllocZ( double, cp->status.iMeterCnt );
  666. cp->oMeterArray = cmMemAllocZ( double, cp->status.oMeterCnt );
  667. cp->netH = cfg->netH;
  668. cp->serialPortH = cfg->serialPortH;
  669. // create the audio System thread
  670. if((rc = cmThreadCreate( &cp->threadH, _cmAsThreadCallback, cp, ss->args.rpt )) != kOkThRC )
  671. {
  672. rc = _cmAsError(p,kThreadErrAsRC,"Thread create failed.");
  673. goto errLabel;
  674. }
  675. // create the audio System mutex
  676. if((rc = cmThreadMutexCreate( &cp->engMutexH, ss->args.rpt )) != kOkThRC )
  677. {
  678. rc = _cmAsError(p,kMutexErrAsRC,"Thread mutex create failed.");
  679. goto errLabel;
  680. }
  681. // create the host-to-dsp thread safe msg queue
  682. if((rc = cmTsMp1cCreate( &cp->htdQueueH, ss->args.msgQueueByteCnt, ss->cbFunc, &cp->ctx, ss->args.rpt )) != kOkThRC )
  683. {
  684. rc = _cmAsError(p,kTsQueueErrAsRC,"Host-to-DSP msg queue create failed.");
  685. goto errLabel;
  686. }
  687. // create the dsp-to-host thread safe msg queue
  688. if( cmTsMp1cIsValid( p->dthQueH ) == false )
  689. {
  690. if((rc = cmTsMp1cCreate( &p->dthQueH, ss->args.msgQueueByteCnt, cfg->clientCbFunc, cfg->clientCbData, ss->args.rpt )) != kOkThRC )
  691. {
  692. rc = _cmAsError(p,kTsQueueErrAsRC,"DSP-to-Host msg queue create failed.");
  693. goto errLabel;
  694. }
  695. }
  696. //cp->dthQueueH = p->dthQueH;
  697. // install an external MIDI port callback handler for incoming MIDI messages
  698. if( cmMpIsInitialized() )
  699. if( cmMpInstallCallback( -1, -1, _cmAudioSysMidiCallback, cp ) != kOkMpRC )
  700. {
  701. rc = _cmAsError(p,kMidiSysFailAsRC,"MIDI system callback installation failed.");
  702. goto errLabel;
  703. }
  704. // install the serial port
  705. if( cmSeIsOpen(cp->serialPortH) )
  706. {
  707. if( cmSeSetCallback(cp->serialPortH, _cmAudioSysSerialPortCallback, cp ) != kOkSeRC )
  708. {
  709. rc = _cmAsError(p,kSerialPortFailAsRC,"Serial port callback installation failed.");
  710. goto errLabel;
  711. }
  712. }
  713. // setup the sub-system status notification
  714. cp->statusUpdateSmpCnt = floor(cmApBufMeterMs() * cp->ss.args.srate / 1000.0 );
  715. cp->statusUpdateSmpIdx = 0;
  716. cp->runFl = true;
  717. // start the audio System thread
  718. if( cmThreadPause( cp->threadH, 0 ) != kOkThRC )
  719. {
  720. rc = _cmAsError(p,kThreadErrAsRC,"Thread start failed.");
  721. goto errLabel;
  722. }
  723. if( cmSeIsOpen(cp->serialPortH) )
  724. {
  725. if( cmSeStart( cp->serialPortH ) != kOkSeRC )
  726. {
  727. rc = _cmAsError(p,kSerialPortFailAsRC,"Serial port start failed.");
  728. goto errLabel;
  729. }
  730. }
  731. }
  732. _cmAsHostInitNotify(p);
  733. p->initFl = true;
  734. errLabel:
  735. if( rc != kOkAsRC )
  736. _cmAudioSysFinalize(p);
  737. return rc;
  738. }
  739. cmAsRC_t cmAudioSysFinalize(cmAudioSysH_t h )
  740. {
  741. cmAsRC_t rc = kOkAsRC;
  742. if( cmAudioSysHandleIsValid(h) == false )
  743. return rc;
  744. cmAs_t* p = _cmAsHandleToPtr(h);
  745. rc = _cmAudioSysFinalize(p);
  746. h.h = NULL;
  747. return rc;
  748. }
  749. bool cmAudioSysIsInitialized( cmAudioSysH_t h )
  750. {
  751. cmAs_t* p = _cmAsHandleToPtr(h);
  752. return p->initFl;
  753. }
  754. cmAsRC_t _cmAudioSysVerifyInit( cmAs_t* p )
  755. {
  756. if( p->initFl == false )
  757. {
  758. // if the last msg generated was also a not init msg then don't
  759. // generate another message - just return the error
  760. if( cmErrLastRC(&p->err) != kNotInitAsRC )
  761. cmErrMsg(&p->err,kNotInitAsRC,"The audio system is not initialized.");
  762. return kNotInitAsRC;
  763. }
  764. return kOkAsRC;
  765. }
  766. bool cmAudioSysIsEnabled( cmAudioSysH_t h )
  767. {
  768. if( cmAudioSysIsInitialized(h) == false )
  769. return false;
  770. cmAs_t* p = _cmAsHandleToPtr(h);
  771. unsigned i;
  772. for(i=0; i<p->ssCnt; ++i)
  773. if( p->ssArray[i].enableFl )
  774. return true;
  775. return false;
  776. }
  777. cmAsRC_t cmAudioSysEnable( cmAudioSysH_t h, bool enableFl )
  778. {
  779. cmAs_t* p = _cmAsHandleToPtr(h);
  780. return _cmAudioSysEnable(p,enableFl);
  781. }
  782. cmAsRC_t cmAudioSysDeliverSegMsg( cmAudioSysH_t h, const void* msgDataPtrArray[], unsigned msgByteCntArray[], unsigned msgSegCnt, unsigned srcNetNodeId )
  783. {
  784. cmAs_t* p = _cmAsHandleToPtr(h);
  785. cmAsRC_t rc;
  786. // the system must be initialized to use this function
  787. if((rc = _cmAudioSysVerifyInit(p)) != kOkAsRC )
  788. return rc;
  789. if( msgSegCnt == 0 )
  790. return kOkAsRC;
  791. // BUG BUG BUG - there is no reason that both the asSubIdx and the selId must
  792. // be in the first segment but it would be nice.
  793. assert( msgByteCntArray[0] >= 2*sizeof(unsigned) || (msgSegCnt>1 && msgByteCntArray[0]==sizeof(unsigned) && msgByteCntArray[1]>=sizeof(unsigned)) );
  794. // The audio sub-system index is always the first field of the msg
  795. // and the msg selector id is always the second field
  796. unsigned* array = (unsigned*)msgDataPtrArray[0];
  797. unsigned asSubIdx = array[0];
  798. unsigned selId = array[1];
  799. if( selId == kUiMstrSelAsId )
  800. return _cmAsHandleNonSubSysMsg( p, msgDataPtrArray, msgByteCntArray, msgSegCnt );
  801. if( selId == kNetSyncSelAsId )
  802. {
  803. assert( msgSegCnt==1);
  804. assert( asSubIdx < p->ssCnt );
  805. p->ssArray[asSubIdx].ctx.srcNetNodeId = srcNetNodeId;
  806. p->ssArray[asSubIdx].ss.cbFunc(&p->ssArray[asSubIdx].ctx,msgByteCntArray[0],msgDataPtrArray[0]);
  807. return kOkAsRC;
  808. }
  809. return _cmAsEnqueueMsg(p,p->ssArray[asSubIdx].htdQueueH,msgDataPtrArray,msgByteCntArray,msgSegCnt,"Host-to-DSP");
  810. }
  811. cmAsRC_t cmAudioSysDeliverMsg( cmAudioSysH_t h, const void* msgPtr, unsigned msgByteCnt, unsigned srcNetNodeId )
  812. {
  813. const void* msgDataPtrArray[] = { msgPtr };
  814. unsigned msgByteCntArray[] = { msgByteCnt };
  815. return cmAudioSysDeliverSegMsg(h,msgDataPtrArray,msgByteCntArray,1,srcNetNodeId);
  816. }
  817. cmAsRC_t cmAudioSysDeliverIdMsg( cmAudioSysH_t h, unsigned asSubIdx, unsigned id, const void* msgPtr, unsigned msgByteCnt, unsigned srcNetNodeId )
  818. {
  819. cmAsRC_t rc;
  820. cmAs_t* p = _cmAsHandleToPtr(h);
  821. // the system must be initialized to use this function
  822. if((rc = _cmAudioSysVerifyInit(p)) != kOkAsRC )
  823. return rc;
  824. const void* msgDataPtrArray[] = { &asSubIdx, &id, msgPtr };
  825. unsigned msgByteCntArray[] = { sizeof(asSubIdx), sizeof(id), msgByteCnt };
  826. return cmAudioSysDeliverSegMsg(h,msgDataPtrArray,msgByteCntArray,3,srcNetNodeId);
  827. }
  828. unsigned cmAudioSysIsMsgWaiting( cmAudioSysH_t h )
  829. {
  830. cmAsRC_t rc;
  831. cmAs_t* p = _cmAsHandleToPtr(h);
  832. // the system must be initialized to use this function
  833. if((rc = _cmAudioSysVerifyInit(p)) != kOkAsRC )
  834. return 0;
  835. unsigned n = 0;
  836. unsigned retByteCnt;
  837. for(n=0; n < p->ssCnt; ++n )
  838. {
  839. //if( (retByteCnt = cmTsMp1cDequeueMsgByteCount(p->ssArray[p->waitAsSubIdx].dthQueueH)) > 0 )
  840. if( (retByteCnt = cmTsMp1cDequeueMsgByteCount(p->dthQueH)) > 0 )
  841. return retByteCnt;
  842. p->waitAsSubIdx = (p->waitAsSubIdx + 1) % p->ssCnt;
  843. }
  844. return 0;
  845. }
  846. cmAsRC_t cmAudioSysReceiveMsg( cmAudioSysH_t h, void* msgDataPtr, unsigned msgByteCnt )
  847. {
  848. cmAsRC_t rc;
  849. cmAs_t* p = _cmAsHandleToPtr(h);
  850. // the system must be initialized to use this function
  851. if((rc = _cmAudioSysVerifyInit(p)) != kOkAsRC )
  852. return rc;
  853. //switch( cmTsMp1cDequeueMsg(p->ssArray[p->waitAsSubIdx].dthQueueH,msgDataPtr,msgByteCnt) )
  854. switch( cmTsMp1cDequeueMsg(p->dthQueH,msgDataPtr,msgByteCnt) )
  855. {
  856. case kOkThRC:
  857. p->waitAsSubIdx = (p->waitAsSubIdx + 1) % p->ssCnt;
  858. return kOkAsRC;
  859. case kBufTooSmallThRC:
  860. return kBufTooSmallAsRC;
  861. case kBufEmptyThRC:
  862. return kNoMsgWaitingAsRC;
  863. }
  864. return _cmAsError(p,kTsQueueErrAsRC,"A deque operation failed on the DSP-to-Host message queue.");
  865. }
  866. void cmAudioSysStatus( cmAudioSysH_t h, unsigned asSubIdx, cmAudioSysStatus_t* statusPtr )
  867. {
  868. cmAs_t* p = _cmAsHandleToPtr(h);
  869. // the system must be initialized to use this function
  870. if( _cmAudioSysVerifyInit(p) != kOkAsRC )
  871. return;
  872. if( asSubIdx < p->ssCnt )
  873. *statusPtr = p->ssArray[asSubIdx].status;
  874. }
  875. void cmAudioSysStatusNotifyEnable( cmAudioSysH_t h, unsigned asSubIdx, bool enableFl )
  876. {
  877. cmAs_t* p = _cmAsHandleToPtr(h);
  878. // the system must be initialized to use this function
  879. if( _cmAudioSysVerifyInit(p) != kOkAsRC )
  880. return;
  881. unsigned i = asSubIdx == cmInvalidIdx ? 0 : asSubIdx;
  882. unsigned n = asSubIdx == cmInvalidIdx ? p->ssCnt : asSubIdx+1;
  883. for(; i<n; ++i)
  884. p->ssArray[i].statusFl = enableFl;
  885. }
  886. bool cmAudioSysHandleIsValid( cmAudioSysH_t h )
  887. { return h.h != NULL; }
  888. cmAudioSysCtx_t* cmAudioSysContext( cmAudioSysH_t h, unsigned asSubIdx )
  889. {
  890. cmAs_t* p = _cmAsHandleToPtr(h);
  891. if( _cmAudioSysVerifyInit(p) != kOkAsRC )
  892. return NULL;
  893. return &p->ssArray[asSubIdx].ctx;
  894. }
  895. unsigned cmAudioSysSubSystemCount( cmAudioSysH_t h )
  896. {
  897. cmAs_t* p = _cmAsHandleToPtr(h);
  898. if( _cmAudioSysVerifyInit(p) != kOkAsRC )
  899. return 0;
  900. return p->ssCnt;
  901. }
  902. //===========================================================================================================================
  903. //
  904. // cmAudioSysTest()
  905. //
  906. //{ { label:cmAudioSysTest }
  907. //(
  908. // cmAudioSysTest() demonstrates the audio system usage.
  909. //)
  910. //(
  911. typedef struct
  912. {
  913. double hz; // current synth frq
  914. long phs; // current synth phase
  915. double srate; // audio sample rate
  916. unsigned cbCnt; // DSP cycle count
  917. bool synthFl; // true=synth false=pass through
  918. } _cmAsTestCbRecd;
  919. typedef struct
  920. {
  921. unsigned asSubIdx; // asSubIdx must always be the first field in the msg
  922. unsigned id; // 0 = set DSP Hz, 1 = report cbCount to host
  923. double hz;
  924. unsigned uint;
  925. } _cmAsTestMsg;
  926. long _cmAsSynthSine( _cmAsTestCbRecd* r, cmApSample_t* p, unsigned chCnt, unsigned frmCnt )
  927. {
  928. long ph = 0;
  929. unsigned i;
  930. for(i=0; i<chCnt; ++i)
  931. {
  932. unsigned j;
  933. cmApSample_t* op = p + i;
  934. ph = r->phs;
  935. for(j=0; j<frmCnt; j++, op+=chCnt, ph++)
  936. *op = (cmApSample_t)(0.9 * sin( 2.0 * M_PI * r->hz * ph / r->srate ));
  937. }
  938. return ph;
  939. }
  940. unsigned _cmAsTestChIdx = 0;
  941. cmRC_t _cmAsTestCb( void* cbPtr, unsigned msgByteCnt, const void* msgDataPtr )
  942. {
  943. cmRC_t rc = cmOkRC;
  944. cmAudioSysCtx_t* ctx = (cmAudioSysCtx_t*)cbPtr;
  945. cmAudioSysSubSys_t* ss = ctx->ss;
  946. _cmAsTestCbRecd* r = (_cmAsTestCbRecd*)ss->cbDataPtr;
  947. // update the calback counter
  948. ++r->cbCnt;
  949. // if this is an audio update request
  950. if( msgByteCnt == 0 )
  951. {
  952. unsigned i;
  953. if( r->synthFl )
  954. {
  955. long phs = 0;
  956. if(0)
  957. {
  958. for(i=0; i<ctx->oChCnt; ++i)
  959. if( ctx->oChArray[i] != NULL )
  960. phs = _cmAsSynthSine(r, ctx->oChArray[i], 1, ss->args.dspFramesPerCycle );
  961. }
  962. else
  963. {
  964. if( _cmAsTestChIdx < ctx->oChCnt )
  965. phs = _cmAsSynthSine(r, ctx->oChArray[_cmAsTestChIdx], 1, ss->args.dspFramesPerCycle );
  966. }
  967. r->phs = phs;
  968. }
  969. else
  970. {
  971. // BUG BUG BUG - this assumes that the input and output channels are the same.
  972. unsigned chCnt = cmMin(ctx->oChCnt,ctx->iChCnt);
  973. for(i=0; i<chCnt; ++i)
  974. memcpy(ctx->oChArray[i],ctx->iChArray[i],sizeof(cmSample_t)*ss->args.dspFramesPerCycle);
  975. }
  976. }
  977. else // ... otherwise it is a msg for the DSP process from the host
  978. {
  979. _cmAsTestMsg* msg = (_cmAsTestMsg*)msgDataPtr;
  980. msg->asSubIdx = ctx->asSubIdx;
  981. switch(msg->id)
  982. {
  983. case 0:
  984. r->hz = msg->hz;
  985. break;
  986. case 1:
  987. msg->uint = r->cbCnt;
  988. msgByteCnt = sizeof(_cmAsTestMsg);
  989. rc = ctx->dspToHostFunc(ctx,(const void **)&msg,&msgByteCnt,1);
  990. break;
  991. }
  992. }
  993. return rc;
  994. }
  995. // print the usage message for cmAudioPortTest.c
  996. void _cmAsPrintUsage( cmRpt_t* rpt )
  997. {
  998. char msg[] =
  999. "cmAudioSysTest() command switches:\n"
  1000. "-r <srate> -c <chcnt> -b <bufcnt> -f <frmcnt> -i <idevidx> -o <odevidx> -m <msgqsize> -d <dspsize> -t -p -h \n"
  1001. "\n"
  1002. "-r <srate> = sample rate (48000)\n"
  1003. "-c <chcnt> = audio channels (2)\n"
  1004. "-b <bufcnt> = count of buffers (3)\n"
  1005. "-f <frmcnt> = count of samples per buffer (512)\n"
  1006. "-i <idevidx> = input device index (0)\n"
  1007. "-o <odevidx> = output device index (2)\n"
  1008. "-m <msgqsize> = message queue byte count (1024)\n"
  1009. "-d <dspsize> = samples per DSP frame (64)\n"
  1010. "-s = true: sync to input port false: sync to output port\n"
  1011. "-t = copy input to output otherwise synthesize a 1000 Hz sine (false)\n"
  1012. "-p = report but don't start audio devices\n"
  1013. "-h = print this usage message\n";
  1014. cmRptPrintf(rpt,"%s",msg);
  1015. }
  1016. // Get a command line option.
  1017. int _cmAsGetOpt( int argc, const char* argv[], const char* label, int defaultVal, bool boolFl )
  1018. {
  1019. int i = 0;
  1020. for(; i<argc; ++i)
  1021. if( strcmp(label,argv[i]) == 0 )
  1022. {
  1023. if(boolFl)
  1024. return 1;
  1025. if( i == (argc-1) )
  1026. return defaultVal;
  1027. return atoi(argv[i+1]);
  1028. }
  1029. return defaultVal;
  1030. }
  1031. bool _cmAsGetBoolOpt( int argc, const char* argv[], const char* label, bool defaultVal )
  1032. { return _cmAsGetOpt(argc,argv,label,defaultVal?1:0,true)!=0; }
  1033. int _cmAsGetIntOpt( int argc, const char* argv[], const char* label, int defaultVal )
  1034. { return _cmAsGetOpt(argc,argv,label,defaultVal,false); }
  1035. void cmAudioSysTest( cmRpt_t* rpt, int argc, const char* argv[] )
  1036. {
  1037. cmAudioSysCfg_t cfg;
  1038. cmAudioSysSubSys_t ss;
  1039. cmAudioSysH_t h = cmAudioSysNullHandle;
  1040. cmAudioSysStatus_t status;
  1041. _cmAsTestCbRecd cbRecd = {1000.0,0,48000.0,0};
  1042. cfg.ssArray = &ss;
  1043. cfg.ssCnt = 1;
  1044. //cfg.afpArray= NULL;
  1045. //cfg.afpCnt = 0;
  1046. cfg.meterMs = 50;
  1047. if(_cmAsGetBoolOpt(argc,argv,"-h",false))
  1048. _cmAsPrintUsage(rpt);
  1049. cbRecd.srate = _cmAsGetIntOpt(argc,argv,"-r",48000);
  1050. cbRecd.synthFl = _cmAsGetBoolOpt(argc,argv,"-t",false)==false;
  1051. ss.args.rpt = rpt;
  1052. ss.args.inDevIdx = _cmAsGetIntOpt( argc,argv,"-i",0);
  1053. ss.args.outDevIdx = _cmAsGetIntOpt( argc,argv,"-o",2);
  1054. ss.args.syncInputFl = _cmAsGetBoolOpt(argc,argv,"-s",true);
  1055. ss.args.msgQueueByteCnt = _cmAsGetIntOpt( argc,argv,"-m",8192);
  1056. ss.args.devFramesPerCycle = _cmAsGetIntOpt( argc,argv,"-f",512);
  1057. ss.args.dspFramesPerCycle = _cmAsGetIntOpt( argc,argv,"-d",64);;
  1058. ss.args.audioBufCnt = _cmAsGetIntOpt( argc,argv,"-b",3);
  1059. ss.args.srate = cbRecd.srate;
  1060. ss.cbFunc = _cmAsTestCb; // set the DSP entry function
  1061. ss.cbDataPtr = &cbRecd; // set the DSP function argument record
  1062. 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,
  1063. ss.args.msgQueueByteCnt,ss.args.devFramesPerCycle,ss.args.dspFramesPerCycle,ss.args.audioBufCnt,ss.args.srate);
  1064. if( cmApNrtAllocate(rpt) != kOkApRC )
  1065. goto errLabel;
  1066. if( cmApFileAllocate(rpt) != kOkApRC )
  1067. goto errLabel;
  1068. // initialize the audio device system
  1069. if( cmApInitialize(rpt) != kOkApRC )
  1070. goto errLabel;
  1071. cmApReport(rpt);
  1072. // initialize the audio buffer
  1073. if( cmApBufInitialize( cmApDeviceCount(), cfg.meterMs ) != kOkApRC )
  1074. goto errLabel;
  1075. // initialize the audio system
  1076. if( cmAudioSysAllocate(&h,rpt,&cfg) != kOkAsRC )
  1077. goto errLabel;
  1078. // start the audio system
  1079. cmAudioSysEnable(h,true);
  1080. char c = 0;
  1081. printf("q=quit a-g=note n=ch r=rqst s=status\n");
  1082. // simulate a host event loop
  1083. while(c != 'q')
  1084. {
  1085. _cmAsTestMsg msg = {0,0,0,0};
  1086. bool fl = true;
  1087. // wait here for a key press
  1088. c =(char)fgetc(stdin);
  1089. fflush(stdin);
  1090. switch(c)
  1091. {
  1092. case 'c': msg.hz = cmMidiToHz(60); break;
  1093. case 'd': msg.hz = cmMidiToHz(62); break;
  1094. case 'e': msg.hz = cmMidiToHz(64); break;
  1095. case 'f': msg.hz = cmMidiToHz(65); break;
  1096. case 'g': msg.hz = cmMidiToHz(67); break;
  1097. case 'a': msg.hz = cmMidiToHz(69); break;
  1098. case 'b': msg.hz = cmMidiToHz(71); break;
  1099. case 'r': msg.id = 1; break; // request DSP process to send a callback count
  1100. case 'n': ++_cmAsTestChIdx; printf("ch:%i\n",_cmAsTestChIdx); break;
  1101. case 's':
  1102. // report the audio system status
  1103. cmAudioSysStatus(h,0,&status);
  1104. 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);
  1105. //printf("%f \n",status.oMeterArray[0]);
  1106. fl = false;
  1107. break;
  1108. default:
  1109. fl=false;
  1110. }
  1111. if( fl )
  1112. {
  1113. // transmit a command to the DSP process
  1114. cmAudioSysDeliverMsg(h,&msg, sizeof(msg), cmInvalidId);
  1115. }
  1116. // check if messages are waiting to be delivered from the DSP process
  1117. unsigned msgByteCnt;
  1118. if((msgByteCnt = cmAudioSysIsMsgWaiting(h)) > 0 )
  1119. {
  1120. char buf[ msgByteCnt ];
  1121. // rcv a msg from the DSP process
  1122. if( cmAudioSysReceiveMsg(h,buf,msgByteCnt) == kOkAsRC )
  1123. {
  1124. _cmAsTestMsg* msg = (_cmAsTestMsg*)buf;
  1125. switch(msg->id)
  1126. {
  1127. case 1:
  1128. printf("RCV: Callback count:%i\n",msg->uint);
  1129. break;
  1130. }
  1131. }
  1132. }
  1133. // report the audio buffer status
  1134. //cmApBufReport(ss.args.rpt);
  1135. }
  1136. // stop the audio system
  1137. cmAudioSysEnable(h,false);
  1138. goto exitLabel;
  1139. errLabel:
  1140. printf("AUDIO SYSTEM TEST ERROR\n");
  1141. exitLabel:
  1142. cmAudioSysFree(&h);
  1143. cmApFinalize();
  1144. cmApFileFree();
  1145. cmApNrtFree();
  1146. cmApBufFinalize();
  1147. }
  1148. //)
  1149. //}