libcm is a C development framework with an emphasis on audio signal processing applications.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

cmAudioPortAlsa.c 50KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692
  1. #include "cmPrefix.h"
  2. #include "cmGlobal.h"
  3. #include "cmRpt.h"
  4. #include "cmTime.h"
  5. #include "cmAudioPort.h"
  6. #include "cmMem.h"
  7. #include "cmTime.h"
  8. #include "cmMallocDebug.h"
  9. #include "cmAudioPort.h"
  10. #include "cmAudioPortAlsa.h"
  11. #include "cmThread.h"
  12. #include "alsa/asoundlib.h"
  13. #include <unistd.h> // usleep
  14. #define NAME_CHAR_CNT (255)
  15. #define DESC_CHAR_CNT (255)
  16. #define INIT_DEV_CNT (5)
  17. enum { kDfltPeriodsPerBuf = 2, kPollfdsArrayCnt=2 };
  18. enum { kInFl=0x01, kOutFl=0x02 };
  19. struct cmApRoot_str;
  20. typedef struct devRecd_str
  21. {
  22. struct cmApRoot_str* rootPtr;
  23. unsigned devIdx;
  24. cmChar_t nameStr[ NAME_CHAR_CNT ];
  25. cmChar_t descStr[ DESC_CHAR_CNT ];
  26. unsigned flags;
  27. unsigned framesPerCycle; // samples per sub-buffer
  28. unsigned periodsPerBuf; // sub-buffers per buffer
  29. snd_async_handler_t* ahandler;
  30. unsigned srate; // device sample rate
  31. unsigned iChCnt; // ch count
  32. unsigned oChCnt;
  33. unsigned iBits; // bits per sample
  34. unsigned oBits;
  35. unsigned iSigBits; // significant bits in each sample beginning
  36. unsigned oSigBits; // with the most sig. bit.
  37. cmApSample_t* iBuf; // iBuf[ iFpc * iChCnt ]
  38. cmApSample_t* oBuf; // oBuf[ oFpc * oChCnt ]
  39. unsigned iFpC; // buffer frames per cycle (in ALSA this is call period_size)
  40. unsigned oFpC;
  41. snd_pcm_t* iPcmH; // device handle
  42. snd_pcm_t* oPcmH;
  43. unsigned iCbCnt; // callback count
  44. unsigned oCbCnt;
  45. unsigned iErrCnt; // error count
  46. unsigned oErrCnt;
  47. cmApCallbackPtr_t cbPtr; // user callback
  48. void* userCbPtr;
  49. } cmApDevRecd_t;
  50. typedef struct cmApPoll_str
  51. {
  52. cmApDevRecd_t* devPtr;
  53. bool inputFl;
  54. unsigned fdsCnt;
  55. } cmApPollfdsDesc_t;
  56. typedef struct cmApRoot_str
  57. {
  58. cmRpt_t* rpt; //
  59. cmApDevRecd_t* devArray; // array of device records
  60. unsigned devCnt; // count of actual dev recds in devArray[]
  61. unsigned devAllocCnt; // count of dev recds allocated in devArray[]
  62. bool asyncFl; // true=use async callback false=use polling thread
  63. cmThreadH_t thH; // polling thread
  64. unsigned pollfdsAllocCnt; // 2*devCnt (max possible in+out handles)
  65. struct pollfd* pollfds; // pollfds[ pollfdsAllocCnt ]
  66. cmApPollfdsDesc_t *pollfdsDesc; // pollfdsDesc[ pollfdsAllocCnt ]
  67. unsigned pollfdsCnt; // count of active recds in pollfds[] and pollfdsDesc[]
  68. } cmApRoot_t;
  69. cmApRoot_t _cmApRoot = { NULL, NULL, 0, 0 };
  70. //===============================================================================================
  71. enum
  72. {
  73. kReadErrRId,
  74. kWriteErrRId
  75. };
  76. #undef cmALSA_RECD
  77. #ifdef cmALSA_RECD
  78. enum
  79. {
  80. kNotUsedRId,
  81. kStartRId,
  82. kCbRId,
  83. kSysErrRId,
  84. kAppErrRId,
  85. kRecoverRId
  86. };
  87. typedef struct
  88. {
  89. int code;
  90. char* label;
  91. } recdErrMap_t;
  92. typedef struct
  93. {
  94. unsigned devIdx;
  95. unsigned typeId;
  96. cmTimeSpec_t t;
  97. bool inputFl;
  98. unsigned arg;
  99. unsigned arg1;
  100. } recd;
  101. recd* recdArray = NULL;
  102. unsigned recdCnt = 0;
  103. unsigned recdIdx = 0;
  104. cmTimeSpec_t recdTime;
  105. recdErrMap_t recdSysErrMap[] =
  106. {
  107. { -EPIPE, "EPIPE" },
  108. { -ESTRPIPE, "ESTRPIPE" },
  109. { -EBADFD, "EBADFD" },
  110. { 0, NULL }
  111. };
  112. recdErrMap_t recdAppErrMap[] =
  113. {
  114. { kReadErrRId, "Read Error"},
  115. { kWriteErrRId, "Write Error"},
  116. { 0, NULL }
  117. };
  118. const char* _recdSysErrToLabel( int err )
  119. {
  120. unsigned i;
  121. for(i=0; recdSysErrMap[i].label != NULL; ++i)
  122. if( recdSysErrMap[i].code == err )
  123. return recdSysErrMap[i].label;
  124. return "<Unknown sys error>";
  125. }
  126. const char* _recdAppErrToLabel( int err )
  127. {
  128. unsigned i;
  129. for(i=0; recdAppErrMap[i].label != NULL; ++i)
  130. if( recdAppErrMap[i].code == err )
  131. return recdAppErrMap[i].label;
  132. return "<Unknown app error>";
  133. }
  134. void recdSetup()
  135. {
  136. recdCnt = 100;
  137. recdIdx = 0;
  138. clock_gettime(CLOCK_REALTIME,&recdTime);
  139. recdArray = cmMemAllocZ(recd,recdCnt);
  140. }
  141. void recdPush( unsigned typeId, unsigned devIdx, bool inputFl, unsigned arg, unsigned arg1 )
  142. {
  143. //if( recdIdx == recdCnt )
  144. // return;
  145. if( recdIdx == recdCnt )
  146. recdIdx = 0;
  147. recd* r = recdArray + recdIdx;
  148. r->typeId = typeId;
  149. r->devIdx = devIdx;
  150. r->inputFl = inputFl;
  151. r->arg = arg;
  152. r->arg1 = arg1;
  153. clock_gettime(CLOCK_REALTIME,&r->t);
  154. ++recdIdx;
  155. }
  156. void recdStart( const cmApDevRecd_t* drp, bool inputFl )
  157. { recdPush(kStartRId,drp->devIdx,inputFl,0,0); }
  158. void recdCb( const cmApDevRecd_t* drp, bool inputFl, unsigned frmCnt )
  159. { recdPush(kCbRId,drp->devIdx,inputFl, inputFl ? drp->iCbCnt : drp->oCbCnt, 0 ); }
  160. void recdSysErr( bool inputFl, int err )
  161. { recdPush(kSysErrRId,cmInvalidIdx,inputFl,err,0); }
  162. void recdAppErr( const cmApDevRecd_t* drp, bool inputFl, int app, int err )
  163. { recdPush(kAppErrRId,drp->devIdx,inputFl,app,err); }
  164. void recdRecover( const cmApDevRecd_t* drp, bool inputFl, int err, int line )
  165. { recdPush(kRecoverRId,drp->devIdx,inputFl,err,line); }
  166. void recdPrint()
  167. {
  168. unsigned i;
  169. cmTimeSpec_t t0 = recdTime;
  170. unsigned j = recdIdx;
  171. for(i=0; i<recdCnt; ++i)
  172. {
  173. recd* r = recdArray + j;
  174. ++j;
  175. if( j == recdCnt )
  176. j = 0;
  177. double ms = cmTimeElapsedMicros(&recdTime,&r->t)/1000.0;
  178. double dms = cmTimeElapsedMicros(&t0,&r->t)/1000.0;
  179. t0 = r->t;
  180. printf("%5i %8.3f %8.3f %i %s: ",i,ms,dms,r->devIdx,r->inputFl ? "in ":"out");
  181. switch(r->typeId)
  182. {
  183. case kStartRId:
  184. printf("start");
  185. break;
  186. case kCbRId:
  187. printf("callback %i",r->arg );
  188. break;
  189. case kSysErrRId:
  190. printf("sys err %s ",_recdSysErrToLabel(r->arg));
  191. break;
  192. case kAppErrRId:
  193. printf("app err %s %s",_recdAppErrToLabel(r->arg),_recdSysErrToLabel(r->arg1));
  194. break;
  195. case kRecoverRId:
  196. printf("Recover %s %i",_recdSysErrToLabel(r->arg),r->arg1);
  197. break;
  198. default:
  199. printf("Unknown recd type id.\n");
  200. break;
  201. }
  202. printf("\n");
  203. }
  204. }
  205. void recdFree()
  206. {
  207. recdPrint();
  208. cmMemFree(recdArray);
  209. }
  210. #else
  211. void recdSetup(){}
  212. void recdPush( unsigned typeId, unsigned devIdx, bool inputFl, unsigned arg, unsigned arg1 ){}
  213. void recdStart( const cmApDevRecd_t* drp, bool inputFl ){}
  214. void recdCb( const cmApDevRecd_t* drp, bool inputFl, unsigned frmCnt ){}
  215. void recdSysErr( bool inputFl, int err ){}
  216. void recdAppErr( const cmApDevRecd_t* drp, bool inputFl, int app, int err ){}
  217. void recdRecover( const cmApDevRecd_t* drp, bool inputFl, int err, int line ){}
  218. void recdPrint(){}
  219. void recdFree(){}
  220. #endif
  221. //===================================================================================================
  222. cmApRC_t _cmApOsError( cmApRoot_t* p, int err, const char* fmt, ... )
  223. {
  224. va_list vl;
  225. va_start(vl,fmt);
  226. int msgN = 255;
  227. char msg[ msgN+1];
  228. vsnprintf(msg,msgN,fmt,vl);
  229. if( err )
  230. cmRptPrintf(p->rpt,"%s ALSA Error:%s. ",msg,snd_strerror(err));
  231. else
  232. cmRptPrintf(p->rpt,"%s ",msg);
  233. cmRptPrintf(p->rpt,"\n");
  234. va_end(vl);
  235. return kSysErrApRC;
  236. }
  237. bool _cmApDevSetupError( cmApRoot_t* p, int err, bool inputFl, const cmApDevRecd_t* drp, const char* fmt, ... )
  238. {
  239. va_list vl;
  240. int msgN = 255;
  241. char msg[ msgN + 1];
  242. va_start(vl,fmt);
  243. vsnprintf(msg,msgN,fmt,vl);
  244. _cmApOsError(p,err,"%s for %s '%s' : '%s'.",msg,inputFl ? "INPUT" : "OUTPUT", drp->nameStr, drp->descStr);
  245. va_end(vl);
  246. return false;
  247. }
  248. const char* _cmApPcmStateToString( snd_pcm_state_t state )
  249. {
  250. switch( state )
  251. {
  252. case SND_PCM_STATE_OPEN: return "open";
  253. case SND_PCM_STATE_SETUP: return "setup";
  254. case SND_PCM_STATE_PREPARED: return "prepared";
  255. case SND_PCM_STATE_RUNNING: return "running";
  256. case SND_PCM_STATE_XRUN: return "xrun";
  257. case SND_PCM_STATE_DRAINING: return "draining";
  258. case SND_PCM_STATE_PAUSED: return "paused";
  259. case SND_PCM_STATE_SUSPENDED: return "suspended";
  260. case SND_PCM_STATE_DISCONNECTED: return "disconnected";
  261. }
  262. return "<invalid>";
  263. }
  264. void _cmApDevRtReport( cmRpt_t* rpt, cmApDevRecd_t* drp )
  265. {
  266. cmRptPrintf(rpt,"cb i:%i o:%i err i:%i o:%i",drp->iCbCnt,drp->oCbCnt,drp->iErrCnt,drp->oErrCnt);
  267. if( drp->iPcmH != NULL )
  268. cmRptPrintf(rpt," state i:%s",_cmApPcmStateToString(snd_pcm_state(drp->iPcmH)));
  269. if( drp->oPcmH != NULL )
  270. cmRptPrintf(rpt," o:%s",_cmApPcmStateToString(snd_pcm_state(drp->oPcmH)));
  271. cmRptPrint(rpt,"\n ");
  272. }
  273. void _cmApDevReport( cmRpt_t* rpt, cmApDevRecd_t* drp )
  274. {
  275. bool inputFl = true;
  276. snd_pcm_t* pcmH;
  277. int err;
  278. unsigned i;
  279. cmApRoot_t* p = drp->rootPtr;
  280. cmRptPrintf(rpt,"%s %s Device:%s Desc:%s\n", drp->flags & kInFl ? "IN ":"", drp->flags & kOutFl ? "OUT ":"", drp->nameStr, drp->descStr);
  281. for(i=0; i<2; i++,inputFl=!inputFl)
  282. {
  283. if( ((inputFl==true) && (drp->flags&kInFl)) || (((inputFl==false) && (drp->flags&kOutFl))))
  284. {
  285. const char* ioLabel = inputFl ? "In" : "Out";
  286. // attempt to open the sub-device
  287. if((err = snd_pcm_open(&pcmH,drp->nameStr,inputFl ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,0)) < 0 )
  288. _cmApDevSetupError(p,err,inputFl,drp,"Attempt to open the PCM handle failed");
  289. else
  290. {
  291. snd_pcm_hw_params_t* hwParams;
  292. snd_pcm_hw_params_alloca(&hwParams);
  293. memset(hwParams,0,snd_pcm_hw_params_sizeof());
  294. // load the parameter record
  295. if((err = snd_pcm_hw_params_any(pcmH,hwParams)) < 0 )
  296. _cmApDevSetupError(p,err,inputFl,drp,"Error obtaining hw param record");
  297. else
  298. {
  299. unsigned minChCnt=0,maxChCnt=0,minSrate=0,maxSrate=0;
  300. snd_pcm_uframes_t minPeriodFrmCnt=0,maxPeriodFrmCnt=0,minBufFrmCnt=0,maxBufFrmCnt=0;
  301. int dir;
  302. // extract the min channel count
  303. if((err = snd_pcm_hw_params_get_channels_min(hwParams, &minChCnt )) < 0 )
  304. _cmApDevSetupError(p,err,inputFl,drp,"Error getting min. channel count.");
  305. // extract the max channel count
  306. if((err = snd_pcm_hw_params_get_channels_max(hwParams, &maxChCnt )) < 0 )
  307. _cmApDevSetupError(p,err,inputFl,drp,"Error getting max. channel count.");
  308. // extract the min srate
  309. if((err = snd_pcm_hw_params_get_rate_min(hwParams, &minSrate,&dir )) < 0 )
  310. _cmApDevSetupError(p,err,inputFl,drp,"Error getting min. sample rate.");
  311. // extract the max srate
  312. if((err = snd_pcm_hw_params_get_rate_max(hwParams, &maxSrate,&dir )) < 0 )
  313. _cmApDevSetupError(p,err,inputFl,drp,"Error getting max. sample rate.");
  314. // extract the min period
  315. if((err = snd_pcm_hw_params_get_period_size_min(hwParams, &minPeriodFrmCnt,&dir )) < 0 )
  316. _cmApDevSetupError(p,err,inputFl,drp,"Error getting min. period frame count.");
  317. // extract the max period
  318. if((err = snd_pcm_hw_params_get_period_size_max(hwParams, &maxPeriodFrmCnt,&dir )) < 0 )
  319. _cmApDevSetupError(p,err,inputFl,drp,"Error getting max. period frame count.");
  320. // extract the min buf
  321. if((err = snd_pcm_hw_params_get_buffer_size_min(hwParams, &minBufFrmCnt )) < 0 )
  322. _cmApDevSetupError(p,err,inputFl,drp,"Error getting min. period frame count.");
  323. // extract the max buffer
  324. if((err = snd_pcm_hw_params_get_buffer_size_max(hwParams, &maxBufFrmCnt )) < 0 )
  325. _cmApDevSetupError(p,err,inputFl,drp,"Error getting max. period frame count.");
  326. cmRptPrintf(rpt,"%s chs:%i - %i srate:%i - %i period:%i - %i buf:%i - %i half duplex only:%s joint duplex:%s\n",
  327. ioLabel,minChCnt,maxChCnt,minSrate,maxSrate,minPeriodFrmCnt,maxPeriodFrmCnt,minBufFrmCnt,maxBufFrmCnt,
  328. (snd_pcm_hw_params_is_half_duplex(hwParams) ? "yes" : "no"),
  329. (snd_pcm_hw_params_is_joint_duplex(hwParams) ? "yes" : "no"));
  330. }
  331. if((err = snd_pcm_close(pcmH)) < 0)
  332. _cmApDevSetupError(p,err,inputFl,drp,"Error closing PCM handle");
  333. }
  334. }
  335. }
  336. }
  337. // Called by cmApInitialize() to append a cmApDevRecd to the _cmApRoot.devArray[].
  338. void _cmApDevAppend( cmApRoot_t* p, cmApDevRecd_t* drp )
  339. {
  340. if( p->devCnt == p->devAllocCnt )
  341. {
  342. p->devArray = cmMemResizePZ( cmApDevRecd_t, p->devArray, p->devAllocCnt + INIT_DEV_CNT );
  343. p->devAllocCnt += INIT_DEV_CNT;
  344. }
  345. drp->devIdx = p->devCnt; // set the device index
  346. drp->rootPtr = p; // set the pointer back to the root
  347. memcpy(p->devArray + p->devCnt, drp, sizeof(cmApDevRecd_t));
  348. ++p->devCnt;
  349. }
  350. cmApRC_t _cmApDevShutdown( cmApRoot_t* p, cmApDevRecd_t* drp, bool inputFl )
  351. {
  352. int err;
  353. snd_pcm_t** pcmH = inputFl ? &drp->iPcmH : &drp->oPcmH;
  354. if( *pcmH != NULL )
  355. {
  356. if((err = snd_pcm_close(*pcmH)) < 0 )
  357. {
  358. _cmApDevSetupError(p,err,inputFl,drp,"Error closing device handle.");
  359. return kSysErrApRC;
  360. }
  361. *pcmH = NULL;
  362. }
  363. return kOkApRC;
  364. }
  365. int _cmApDevOpen( snd_pcm_t** pcmHPtr, const char* devNameStr, bool inputFl )
  366. {
  367. int cnt = 0;
  368. int err;
  369. do
  370. {
  371. if((err = snd_pcm_open(pcmHPtr,devNameStr,inputFl ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,0)) < 0 )
  372. {
  373. cnt++;
  374. usleep(10000); // sleep for 10 milliseconds
  375. }
  376. }while(cnt<100 && err == -EBUSY );
  377. return err;
  378. }
  379. void _cmApXrun_recover( snd_pcm_t* pcmH, int err, cmApDevRecd_t* drp, bool inputFl, int line )
  380. {
  381. char dirCh = inputFl ? 'I' : 'O';
  382. inputFl ? drp->iErrCnt++ : drp->oErrCnt++;
  383. recdRecover(drp,inputFl,err,line);
  384. // -EPIPE signals and over/underrun (see pcm.c example xrun_recovery())
  385. switch( err )
  386. {
  387. case -EPIPE:
  388. {
  389. int silentFl = 1;
  390. if((err = snd_pcm_recover( pcmH, err, silentFl )) < 0 )
  391. _cmApDevSetupError(drp->rootPtr,err,inputFl,drp,"recover failed.");
  392. if( inputFl )
  393. {
  394. if((err= snd_pcm_prepare(pcmH)) < 0 )
  395. _cmApDevSetupError(drp->rootPtr,err,inputFl,drp,"re-prepare failed.");
  396. else
  397. if((err = snd_pcm_start(pcmH)) < 0 )
  398. _cmApDevSetupError(drp->rootPtr,err,inputFl,drp,"restart failed.");
  399. }
  400. else
  401. {
  402. /*
  403. _cmApWriteBuf(pcmH, NULL, drp->oChCnt, drp->oFpC );
  404. _cmApWriteBuf(pcmH, NULL, drp->oChCnt, drp->oFpC );
  405. if((err = snd_pcm_prepare(pcmH))<0)
  406. _cmApDevSetupError(drp->rootPtr,err,inputFl,drp,"Recovery failed.\n");
  407. else
  408. if((err = snd_pcm_resume(pcmH))<0)
  409. _cmApDevSetupError(drp->rootPtr,err,inputFl,drp,"Resume failed.\n");
  410. */
  411. }
  412. printf("EPIPE %c %i %i %i\n",dirCh,drp->devIdx,drp->oCbCnt,line);
  413. //if((err = snd_pcm_prepare(pcmH))<0)
  414. // _devSetupError(err,inputFl,*drp,"Recovery failed.\n");
  415. //else
  416. // if((err = snd_pcm_resume(pcmH))<0)
  417. // _devSetupError(err,inputFl,*drp,"Resume failed.\n");
  418. break;
  419. }
  420. case -ESTRPIPE:
  421. {
  422. int silentFl = 1;
  423. if((err = snd_pcm_recover( pcmH, err, silentFl )) < 0 )
  424. _cmApDevSetupError(drp->rootPtr,err,inputFl,drp,"recover failed.");
  425. printf("audio port impl ESTRPIPE:%c\n",dirCh);
  426. break;
  427. }
  428. case -EBADFD:
  429. {
  430. _cmApDevSetupError(drp->rootPtr,err,inputFl,drp,"%s failed.",inputFl ? "Read" : "Write" );
  431. break;
  432. }
  433. default:
  434. _cmApDevSetupError(drp->rootPtr,err,inputFl,drp,"Unknown rd/wr error.\n");
  435. } // switch
  436. }
  437. void _cmApStateRecover( snd_pcm_t* pcmH, cmApDevRecd_t* drp, bool inputFl )
  438. {
  439. int err = 0;
  440. switch( snd_pcm_state(pcmH))
  441. {
  442. case SND_PCM_STATE_XRUN:
  443. err = -EPIPE;
  444. break;
  445. case SND_PCM_STATE_SUSPENDED:
  446. err = -ESTRPIPE;
  447. break;
  448. case SND_PCM_STATE_OPEN:
  449. case SND_PCM_STATE_SETUP:
  450. case SND_PCM_STATE_PREPARED:
  451. case SND_PCM_STATE_RUNNING:
  452. case SND_PCM_STATE_DRAINING:
  453. case SND_PCM_STATE_PAUSED:
  454. case SND_PCM_STATE_DISCONNECTED:
  455. //case SND_PCM_STATE_LAST:
  456. break;
  457. }
  458. if( err < 0 )
  459. _cmApXrun_recover( pcmH, err, drp, inputFl, __LINE__ );
  460. }
  461. // Returns count of frames written on success or < 0 on error;
  462. // set smpPtr to NULL to write a buffer of silence
  463. int _cmApWriteBuf( const cmApDevRecd_t* drp, snd_pcm_t* pcmH, const cmApSample_t* sp, unsigned chCnt, unsigned frmCnt, unsigned bits, unsigned sigBits )
  464. {
  465. int err = 0;
  466. unsigned bytesPerSmp = (bits==24 ? 32 : bits)/8;
  467. unsigned smpCnt = chCnt * frmCnt;
  468. unsigned byteCnt = bytesPerSmp * smpCnt;
  469. const cmApSample_t* ep = sp + smpCnt;
  470. char obuf[ byteCnt ];
  471. // if no output was given then fill the device buffer with zeros
  472. if( sp == NULL )
  473. memset(obuf,0,byteCnt);
  474. else
  475. {
  476. // otherwise convert the floating point samples to integers
  477. switch( bits )
  478. {
  479. case 8:
  480. {
  481. char* dp = (char*)obuf;
  482. while( sp < ep )
  483. *dp++ = (char)(*sp++ * 0x7f);
  484. }
  485. break;
  486. case 16:
  487. {
  488. short* dp = (short*)obuf;
  489. while( sp < ep )
  490. *dp++ = (short)(*sp++ * 0x7fff);
  491. }
  492. break;
  493. case 24:
  494. {
  495. int* dp = (int*)obuf;
  496. while( sp < ep )
  497. *dp++ = (int)(*sp++ * 0x7fffff);
  498. }
  499. break;
  500. case 32:
  501. {
  502. int* dp = (int*)obuf;
  503. while( sp < ep )
  504. *dp++ = (int)(*sp++ * 0x7fffffff);
  505. }
  506. break;
  507. }
  508. }
  509. // send the bytes to the device
  510. err = snd_pcm_writei( pcmH, obuf, frmCnt );
  511. if( err < 0 )
  512. {
  513. recdAppErr(drp,false,kWriteErrRId,err);
  514. _cmApDevSetupError(drp->rootPtr, err, false, drp, "ALSA write error" );
  515. }
  516. else
  517. if( err > 0 && err != frmCnt )
  518. {
  519. _cmApDevSetupError(drp->rootPtr, 0, false, drp, "Actual count of bytes written did not match the count provided." );
  520. }
  521. return err;
  522. }
  523. // Returns frames read on success or < 0 on error.
  524. // Set smpPtr to NULL to read the incoming buffer and discard it
  525. int _cmApReadBuf( const cmApDevRecd_t* drp, snd_pcm_t* pcmH, cmApSample_t* smpPtr, unsigned chCnt, unsigned frmCnt, unsigned bits, unsigned sigBits )
  526. {
  527. int err = 0;
  528. unsigned bytesPerSmp = (bits==24 ? 32 : bits)/8;
  529. unsigned smpCnt = chCnt * frmCnt;
  530. unsigned byteCnt = smpCnt * bytesPerSmp;
  531. char buf[ byteCnt ];
  532. // get the incoming samples into buf[] ...
  533. err = snd_pcm_readi(pcmH,buf,frmCnt);
  534. // if a read error occurred
  535. if( err < 0 )
  536. {
  537. recdAppErr(drp,true,kReadErrRId,err);
  538. _cmApDevSetupError(drp->rootPtr, err, false, drp, "ALSA read error" );
  539. }
  540. else
  541. if( err > 0 && err != frmCnt )
  542. {
  543. _cmApDevSetupError(drp->rootPtr, 0, false, drp, "Actual count of bytes read did not match the count requested." );
  544. }
  545. // if no buffer was given then there is nothing else to do
  546. if( smpPtr == NULL )
  547. return err;
  548. // setup the return buffer
  549. cmApSample_t* dp = smpPtr;
  550. cmApSample_t* ep = dp + cmMin(smpCnt,err*chCnt);
  551. switch(bits)
  552. {
  553. case 8:
  554. {
  555. char* sp = buf;
  556. while(dp < ep)
  557. *dp++ = ((cmApSample_t)*sp++) / 0x7f;
  558. }
  559. break;
  560. case 16:
  561. {
  562. short* sp = (short*)buf;
  563. while(dp < ep)
  564. *dp++ = ((cmApSample_t)*sp++) / 0x7fff;
  565. }
  566. break;
  567. case 24:
  568. {
  569. int* sp = (int*)buf;
  570. while(dp < ep)
  571. *dp++ = ((cmApSample_t)*sp++) / 0x7fffff;
  572. }
  573. break;
  574. case 32:
  575. {
  576. int* sp = (int*)buf;
  577. // The delta1010 (ICE1712) uses only the 24 highest bits according to
  578. //
  579. // http://www.alsa-project.org/alsa-doc/alsa-lib/pcm.html
  580. // <snip> The example: ICE1712 chips support 32-bit sample processing,
  581. // but low byte is ignored (playback) or zero (capture).
  582. //
  583. int mv = sigBits==24 ? 0x7fffff00 : 0x7fffffff;
  584. while(dp < ep)
  585. *dp++ = ((cmApSample_t)*sp++) / mv;
  586. }
  587. break;
  588. default:
  589. { assert(0); }
  590. }
  591. return err;
  592. }
  593. void _cmApStaticAsyncHandler( snd_async_handler_t* ahandler )
  594. {
  595. int err;
  596. snd_pcm_sframes_t avail;
  597. cmApDevRecd_t* drp = (cmApDevRecd_t*)snd_async_handler_get_callback_private(ahandler);
  598. snd_pcm_t* pcmH = snd_async_handler_get_pcm(ahandler);
  599. bool inputFl = snd_pcm_stream(pcmH) == SND_PCM_STREAM_CAPTURE;
  600. cmApSample_t* b = inputFl ? drp->iBuf : drp->oBuf;
  601. unsigned chCnt = inputFl ? drp->iChCnt : drp->oChCnt;
  602. unsigned frmCnt = inputFl ? drp->iFpC : drp->oFpC;
  603. cmApAudioPacket_t pkt;
  604. inputFl ? drp->iCbCnt++ : drp->oCbCnt++;
  605. pkt.devIdx = drp->devIdx;
  606. pkt.begChIdx = 0;
  607. pkt.chCnt = chCnt;
  608. pkt.audioFramesCnt = frmCnt;
  609. pkt.bitsPerSample = 32;
  610. pkt.flags = kInterleavedApFl | kFloatApFl;
  611. pkt.audioBytesPtr = b;
  612. pkt.userCbPtr = drp->userCbPtr;
  613. recdCb(drp,inputFl,0);
  614. _cmApStateRecover( pcmH, drp, inputFl );
  615. while( (avail = snd_pcm_avail_update(pcmH)) >= (snd_pcm_sframes_t)frmCnt )
  616. {
  617. // Handle inpuut
  618. if( inputFl )
  619. {
  620. // read samples from the device
  621. if((err = _cmApReadBuf(drp,pcmH,drp->iBuf,chCnt,frmCnt,drp->iBits,drp->oBits)) > 0 )
  622. {
  623. pkt.audioFramesCnt = err;
  624. drp->cbPtr(&pkt,1,NULL,0 ); // send the samples to the application
  625. }
  626. }
  627. // Handle output
  628. else
  629. {
  630. // callback to fill the buffer
  631. drp->cbPtr(NULL,0,&pkt,1);
  632. // note that the application may return fewer samples than were requested
  633. err = _cmApWriteBuf(drp, pcmH, pkt.audioFramesCnt < frmCnt ? NULL : drp->oBuf,chCnt,frmCnt,drp->oBits,drp->oSigBits);
  634. }
  635. // Handle read/write errors
  636. if( err < 0 )
  637. {
  638. inputFl ? drp->iErrCnt++ : drp->oErrCnt++;
  639. _cmApXrun_recover( pcmH, err, drp, inputFl, __LINE__ );
  640. }
  641. else
  642. {
  643. // _cmApStateRecover( pcmH, drp, inputFl );
  644. }
  645. } // while
  646. }
  647. bool _cmApThreadFunc(void* param)
  648. {
  649. cmApRoot_t* p = (cmApRoot_t*)param;
  650. int result;
  651. bool retFl = true;
  652. switch( result = poll(p->pollfds, p->pollfdsCnt, 250) )
  653. {
  654. case 0:
  655. // time out
  656. break;
  657. case -1:
  658. _cmApOsError(p,errno,"Poll fail.");
  659. break;
  660. default:
  661. {
  662. assert( result > 0 );
  663. unsigned i;
  664. // for each i/o stream
  665. for(i=0; i<p->pollfdsCnt; ++i)
  666. {
  667. cmApDevRecd_t* drp = p->pollfdsDesc[i].devPtr;
  668. bool inputFl = p->pollfdsDesc[i].inputFl;
  669. snd_pcm_t* pcmH = inputFl ? drp->iPcmH : drp->oPcmH;
  670. unsigned chCnt = inputFl ? drp->iChCnt : drp->oChCnt;
  671. unsigned frmCnt = inputFl ? drp->iFpC : drp->oFpC;
  672. cmApSample_t* b = inputFl ? drp->iBuf : drp->oBuf;
  673. unsigned short revents = 0;
  674. int err;
  675. cmApAudioPacket_t pkt;
  676. snd_pcm_uframes_t avail_frames;
  677. inputFl ? drp->iCbCnt++ : drp->oCbCnt++;
  678. pkt.devIdx = drp->devIdx;
  679. pkt.begChIdx = 0;
  680. pkt.chCnt = chCnt;
  681. pkt.audioFramesCnt = frmCnt;
  682. pkt.bitsPerSample = 32;
  683. pkt.flags = kInterleavedApFl | kFloatApFl;
  684. pkt.audioBytesPtr = b;
  685. pkt.userCbPtr = drp->userCbPtr;
  686. inputFl ? drp->iCbCnt++ : drp->oCbCnt++;
  687. // get the timestamp for this buffer
  688. if((err = snd_pcm_htimestamp(pcmH,&avail_frames,&pkt.timeStamp)) != 0 )
  689. {
  690. _cmApDevSetupError(p, err, p->pollfdsDesc[i].inputFl, drp, "Get timestamp error.");
  691. pkt.timeStamp.tv_sec = 0;
  692. pkt.timeStamp.tv_nsec = 0;
  693. }
  694. // Note that based on experimenting with the timestamp and the current
  695. // clock_gettime(CLOCK_MONOTONIC) time it appears that the time stamp
  696. // marks the end of the current buffer - so in fact the time stamp should
  697. // be backed up by the availble sample count period to get the time of the
  698. // first sample in the buffer
  699. /*
  700. unsigned avail_nano_secs = (unsigned)(avail_frames * (1000000000.0/drp->srate));
  701. if( pkt.timeStamp.tv_nsec > avail_nano_secs )
  702. pkt.timeStamp.tv_nsec -= avail_nano_secs;
  703. else
  704. {
  705. pkt.timeStamp.tv_sec -= 1;
  706. pkt.timeStamp.tv_nsec = 1000000000 - avail_nano_secs;
  707. }
  708. */
  709. //printf("AUDI: %ld %ld\n",pkt.timeStamp.tv_sec,pkt.timeStamp.tv_nsec);
  710. //cmTimeSpec_t t;
  711. //clock_gettime(CLOCK_PROCESS_CPUTIME_ID,&t);
  712. //printf("AUDI: %ld %ld\n",t.tv_sec,t.tv_nsec);
  713. switch( snd_pcm_state(pcmH) )
  714. {
  715. case SND_PCM_STATE_OPEN:
  716. case SND_PCM_STATE_SETUP:
  717. case SND_PCM_STATE_PREPARED:
  718. case SND_PCM_STATE_DRAINING:
  719. case SND_PCM_STATE_PAUSED:
  720. case SND_PCM_STATE_DISCONNECTED:
  721. continue;
  722. case SND_PCM_STATE_RUNNING:
  723. case SND_PCM_STATE_XRUN:
  724. case SND_PCM_STATE_SUSPENDED:
  725. break;
  726. }
  727. if(( err = snd_pcm_poll_descriptors_revents(pcmH, p->pollfds + i, 1 , &revents)) != 0 )
  728. {
  729. _cmApDevSetupError(p, err, p->pollfdsDesc[i].inputFl, drp, "Return poll events failed.");
  730. retFl = false;
  731. goto errLabel;
  732. }
  733. if(revents & POLLERR)
  734. {
  735. _cmApDevSetupError(p, err, p->pollfdsDesc[i].inputFl, drp, "Poll error.");
  736. _cmApStateRecover( pcmH, drp, inputFl );
  737. //goto errLabel;
  738. }
  739. if( inputFl && (revents & POLLIN) )
  740. {
  741. if((err = _cmApReadBuf(drp,pcmH,drp->iBuf,chCnt,frmCnt,drp->iBits,drp->oBits)) > 0 )
  742. {
  743. pkt.audioFramesCnt = err;
  744. drp->cbPtr(&pkt,1,NULL,0 ); // send the samples to the application
  745. }
  746. }
  747. if( !inputFl && (revents & POLLOUT) )
  748. {
  749. /*
  750. unsigned srate = 96;
  751. cmTimeSpec_t t1;
  752. static cmTimeSpec_t t0 = {0,0};
  753. clock_gettime(CLOCK_MONOTONIC,&t1);
  754. // time since the time-stamp was generated
  755. unsigned smp = (srate * (t1.tv_nsec - pkt.timeStamp.tv_nsec)) / 1000000;
  756. // time since the last output buffer was sent
  757. unsigned dsmp = (srate * (t1.tv_nsec - t0.tv_nsec)) / 1000000;
  758. printf("%i %ld %i : %ld %ld -> %ld %ld\n",smp,avail_frames,dsmp,pkt.timeStamp.tv_sec,pkt.timeStamp.tv_nsec,t1.tv_sec,t1.tv_nsec);
  759. t0 = t1;
  760. */
  761. // callback to fill the buffer
  762. drp->cbPtr(NULL,0,&pkt,1);
  763. // note that the application may return fewer samples than were requested
  764. err = _cmApWriteBuf(drp, pcmH, pkt.audioFramesCnt < frmCnt ? NULL : drp->oBuf,chCnt,frmCnt,drp->oBits,drp->oSigBits);
  765. }
  766. }
  767. }
  768. }
  769. errLabel:
  770. return retFl;
  771. }
  772. bool _cmApDevSetup( cmApDevRecd_t *drp, unsigned srate, unsigned framesPerCycle, unsigned periodsPerBuf )
  773. {
  774. int err;
  775. int dir;
  776. unsigned i;
  777. bool retFl = true;
  778. bool inputFl = true;
  779. snd_pcm_uframes_t periodFrameCnt = framesPerCycle;
  780. snd_pcm_uframes_t bufferFrameCnt;
  781. unsigned bits = 0;
  782. int sig_bits = 0;
  783. cmApRoot_t* p = drp->rootPtr;
  784. // setup input, then output device
  785. for(i=0; i<2; i++,inputFl=!inputFl)
  786. {
  787. unsigned chCnt = inputFl ? drp->iChCnt : drp->oChCnt;
  788. snd_pcm_uframes_t actFpC = 0;
  789. // if this is the in/out pass and the in/out flag is set
  790. if( ((inputFl==true) && (drp->flags & kInFl)) || ((inputFl==false) && (drp->flags & kOutFl)) )
  791. {
  792. snd_pcm_t* pcmH = NULL;
  793. if( _cmApDevShutdown(p, drp, inputFl ) != kOkApRC )
  794. retFl = false;
  795. // attempt to open the sub-device
  796. if((err = snd_pcm_open(&pcmH,drp->nameStr, inputFl ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK, 0)) < 0 )
  797. retFl = _cmApDevSetupError(p,err,inputFl,drp,"Unable to open the PCM handle");
  798. else
  799. {
  800. snd_pcm_hw_params_t* hwParams;
  801. snd_pcm_sw_params_t* swParams;
  802. // prepare the hwParam recd
  803. snd_pcm_hw_params_alloca(&hwParams);
  804. memset(hwParams,0,snd_pcm_hw_params_sizeof());
  805. // load the hw parameter record
  806. if((err = snd_pcm_hw_params_any(pcmH,hwParams)) < 0 )
  807. retFl = _cmApDevSetupError(p,err,inputFl,drp,"Error obtaining hw param record");
  808. else
  809. {
  810. if((err = snd_pcm_hw_params_set_rate_resample(pcmH,hwParams,0)) < 0 )
  811. retFl = _cmApDevSetupError(p,err,inputFl, drp,"Unable to disable the ALSA sample rate converter.");
  812. if((err = snd_pcm_hw_params_set_channels(pcmH,hwParams,chCnt)) < 0 )
  813. retFl = _cmApDevSetupError(p,err,inputFl, drp,"Unable to set channel count to: %i",chCnt);
  814. if((err = snd_pcm_hw_params_set_rate(pcmH,hwParams,srate,0)) < 0 )
  815. retFl = _cmApDevSetupError(p,err,inputFl, drp, "Unable to set sample rate to: %i",srate);
  816. if((err = snd_pcm_hw_params_set_access(pcmH,hwParams,SND_PCM_ACCESS_RW_INTERLEAVED )) < 0 )
  817. retFl = _cmApDevSetupError(p,err,inputFl, drp, "Unable to set access to: RW Interleaved");
  818. // select the widest possible sample width
  819. if((err = snd_pcm_hw_params_set_format(pcmH,hwParams,SND_PCM_FORMAT_S32)) >= 0 )
  820. bits = 32;
  821. else
  822. {
  823. if((err = snd_pcm_hw_params_set_format(pcmH,hwParams,SND_PCM_FORMAT_S24)) >= 0 )
  824. bits = 24;
  825. else
  826. {
  827. if((err = snd_pcm_hw_params_set_format(pcmH,hwParams,SND_PCM_FORMAT_S16)) >= 0 )
  828. bits = 16;
  829. else
  830. retFl = _cmApDevSetupError(p,err,inputFl, drp, "Unable to set format to: S16");
  831. }
  832. }
  833. sig_bits = snd_pcm_hw_params_get_sbits(hwParams);
  834. snd_pcm_uframes_t ps_min,ps_max;
  835. if((err = snd_pcm_hw_params_get_period_size_min(hwParams,&ps_min,NULL)) < 0 )
  836. retFl = _cmApDevSetupError(p,err,inputFl, drp, "Unable to get the minimum period size.");
  837. if((err = snd_pcm_hw_params_get_period_size_max(hwParams,&ps_max,NULL)) < 0 )
  838. retFl = _cmApDevSetupError(p,err,inputFl, drp, "Unable to get the maximum period size.");
  839. if( periodFrameCnt < ps_min )
  840. periodFrameCnt = ps_min;
  841. else
  842. if( periodFrameCnt > ps_max )
  843. periodFrameCnt = ps_max;
  844. if((err = snd_pcm_hw_params_set_period_size_near(pcmH,hwParams,&periodFrameCnt,NULL)) < 0 )
  845. retFl = _cmApDevSetupError(p,err,inputFl, drp, "Unable to set period to %i.",periodFrameCnt);
  846. bufferFrameCnt = periodFrameCnt * periodsPerBuf + 1;
  847. if((err = snd_pcm_hw_params_set_buffer_size_near(pcmH,hwParams,&bufferFrameCnt)) < 0 )
  848. retFl = _cmApDevSetupError(p,err,inputFl, drp, "Unable to set buffer to %i.",bufferFrameCnt);
  849. // Note: snd_pcm_hw_params() automatically calls snd_pcm_prepare()
  850. if((err = snd_pcm_hw_params(pcmH,hwParams)) < 0 )
  851. retFl = _cmApDevSetupError(p,err,inputFl, drp, "Parameter application failed.");
  852. //_reportActualParams( hwParams, inputFl, dr, srate, periodFrameCnt, bufferFrameCnt );
  853. }
  854. // prepare the sw param recd
  855. snd_pcm_sw_params_alloca(&swParams);
  856. memset(swParams,0,snd_pcm_sw_params_sizeof());
  857. // load the sw param recd
  858. if((err = snd_pcm_sw_params_current(pcmH,swParams)) < 0 )
  859. retFl = _cmApDevSetupError(p,err,inputFl,drp,"Error obtaining sw param record.");
  860. else
  861. {
  862. if((err = snd_pcm_sw_params_set_start_threshold(pcmH,swParams, inputFl ? 0x7fffffff : periodFrameCnt)) < 0 )
  863. retFl = _cmApDevSetupError(p,err,inputFl,drp,"Error seting the start threshold.");
  864. // setting the stop-threshold to twice the buffer frame count is intended to stop spurious
  865. // XRUN states - it will also mean that there will have no direct way of knowing about a
  866. // in/out buffer over/under run.
  867. if((err = snd_pcm_sw_params_set_stop_threshold(pcmH,swParams,bufferFrameCnt*2)) < 0 )
  868. retFl = _cmApDevSetupError(p,err,inputFl,drp,"Error setting the stop threshold.");
  869. if((err = snd_pcm_sw_params_set_avail_min(pcmH,swParams,periodFrameCnt)) < 0 )
  870. retFl = _cmApDevSetupError(p,err,inputFl,drp,"Error setting the avail. min. setting.");
  871. if((err = snd_pcm_sw_params_set_tstamp_mode(pcmH,swParams,SND_PCM_TSTAMP_MMAP)) < 0 )
  872. retFl = _cmApDevSetupError(p,err,inputFl,drp,"Error setting the time samp mode.");
  873. if((err = snd_pcm_sw_params(pcmH,swParams)) < 0 )
  874. retFl = _cmApDevSetupError(p,err,inputFl,drp,"Error applying sw params.");
  875. }
  876. // setup the callback
  877. if( p->asyncFl )
  878. if((err = snd_async_add_pcm_handler(&drp->ahandler,pcmH,_cmApStaticAsyncHandler, drp )) < 0 )
  879. retFl = _cmApDevSetupError(p,err,inputFl,drp,"Error assigning callback handler.");
  880. // get the actual frames per cycle
  881. if((err = snd_pcm_hw_params_get_period_size(hwParams,&actFpC,&dir)) < 0 )
  882. retFl = _cmApDevSetupError(p,err,inputFl,drp,"Unable to get the actual period.");
  883. // store the device handle
  884. if( inputFl )
  885. {
  886. drp->iBits = bits;
  887. drp->iSigBits = sig_bits;
  888. drp->iPcmH = pcmH;
  889. drp->iBuf = cmMemResizeZ( cmApSample_t, drp->iBuf, actFpC * drp->iChCnt );
  890. drp->iFpC = actFpC;
  891. }
  892. else
  893. {
  894. drp->oBits = bits;
  895. drp->oSigBits = sig_bits;
  896. drp->oPcmH = pcmH;
  897. drp->oBuf = cmMemResizeZ( cmApSample_t, drp->oBuf, actFpC * drp->oChCnt );
  898. drp->oFpC = actFpC;
  899. }
  900. if( p->asyncFl == false )
  901. {
  902. assert( p->pollfdsCnt < p->pollfdsAllocCnt );
  903. unsigned incrFdsCnt = 0;
  904. unsigned fdsCnt = 0;
  905. // locate the pollfd associated with this device/direction
  906. unsigned j;
  907. for(j=0; j<p->pollfdsCnt; j+=p->pollfdsDesc[j].fdsCnt)
  908. if( p->pollfdsDesc[j].devPtr == drp && inputFl == p->pollfdsDesc[j].inputFl )
  909. break;
  910. // get the count of descriptors for this device/direction
  911. fdsCnt = snd_pcm_poll_descriptors_count(pcmH);
  912. // if the device was not found
  913. if( j == p->pollfdsCnt )
  914. {
  915. j = p->pollfdsCnt;
  916. incrFdsCnt = fdsCnt;
  917. // if the pollfds[] needs more memroy
  918. if( p->pollfdsCnt + fdsCnt > p->pollfdsAllocCnt )
  919. {
  920. p->pollfds = cmMemResizePZ(struct pollfd, p->pollfds, p->pollfdsCnt + fdsCnt );
  921. p->pollfdsDesc = cmMemResizePZ(cmApPollfdsDesc_t, p->pollfdsDesc, p->pollfdsCnt + fdsCnt );
  922. p->pollfdsAllocCnt += fdsCnt;
  923. }
  924. }
  925. // get the poll descriptors for this device/dir
  926. if( snd_pcm_poll_descriptors(pcmH,p->pollfds + j,fdsCnt) != 1 )
  927. retFl = _cmApDevSetupError(p,0,inputFl,drp,"Poll descriptor assignment failed.");
  928. else
  929. {
  930. // store the desc. record assicoated with the poll descriptor
  931. p->pollfdsDesc[ j ].fdsCnt = fdsCnt;
  932. p->pollfdsDesc[ j ].devPtr = drp;
  933. p->pollfdsDesc[ j ].inputFl = inputFl;
  934. }
  935. p->pollfdsCnt += incrFdsCnt;
  936. }
  937. printf("%s %s period:%i %i buffer:%i bits:%i sig_bits:%i\n",inputFl?"in ":"out",drp->nameStr,(unsigned)periodFrameCnt,(unsigned)actFpC,(unsigned)bufferFrameCnt,bits,sig_bits);
  938. }
  939. //_dumpAlsaDevice(pcmH);
  940. } // end if
  941. } // end for
  942. return retFl;
  943. }
  944. #ifdef NOTDEF
  945. #define TRY(e) while(e<0){ printf("LINE:%i ALSA ERROR:%s\n",__LINE__,snd_strerror(e)); exit(EXIT_FAILURE); }
  946. void open_device( const char* device_name, bool inputFl )
  947. {
  948. snd_pcm_t *pcm_handle = NULL;
  949. snd_pcm_hw_params_t *hw_params;
  950. snd_pcm_uframes_t bs_min,bs_max,ps_min,ps_max;
  951. unsigned rt_min,rt_max,ch_min,ch_max;
  952. const char* ioLabel = inputFl ? "in" : "out";
  953. // Open the device
  954. TRY( snd_pcm_open (&pcm_handle, device_name, inputFl ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK, 0));
  955. TRY( snd_pcm_hw_params_malloc (&hw_params) );
  956. TRY( snd_pcm_hw_params_any (pcm_handle, hw_params));
  957. TRY( snd_pcm_hw_params_test_format(pcm_handle, hw_params, SND_PCM_FORMAT_S16_LE));
  958. // get the sample rate range
  959. TRY(snd_pcm_hw_params_get_rate_min(hw_params,&rt_min,NULL));
  960. TRY(snd_pcm_hw_params_get_rate_max(hw_params,&rt_max,NULL));
  961. TRY(snd_pcm_hw_params_get_channels_min(hw_params,&ch_min));
  962. TRY(snd_pcm_hw_params_get_channels_max(hw_params,&ch_max));
  963. // set the basic device format - setting the format may influence the size of the possible
  964. // buffer and period size
  965. //TRY( snd_pcm_hw_params_set_access (pcm_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED));
  966. //TRY( snd_pcm_hw_params_set_format (pcm_handle, hw_params, SND_PCM_FORMAT_S16_LE));
  967. //TRY( snd_pcm_hw_params_set_rate_near(pcm_handle, hw_params, &srate, NULL));
  968. //TRY( snd_pcm_hw_params_set_channels(pcm_handle, hw_params, ch_cnt));
  969. // get the range of possible buffer and period sizes
  970. TRY(snd_pcm_hw_params_get_buffer_size_min(hw_params,&bs_min));
  971. TRY(snd_pcm_hw_params_get_buffer_size_max(hw_params,&bs_max));
  972. TRY(snd_pcm_hw_params_get_period_size_min(hw_params,&ps_min,NULL));
  973. TRY(snd_pcm_hw_params_get_period_size_max(hw_params,&ps_max,NULL));
  974. //printf("%s %s bs min:%u max:%u ps min:%u max:%u rate min:%u max:%u ch min:%u max:%u\n",device_name,ioLabel,bs_min,bs_max,ps_min,ps_max,rt_min,rt_max,ch_min,ch_max);
  975. printf("%s %s rate min:%u max:%u ch min:%u max:%u\n",device_name,ioLabel,rt_min,rt_max,ch_min,ch_max);
  976. snd_pcm_hw_params_free(hw_params);
  977. snd_pcm_close(pcm_handle);
  978. }
  979. #endif
  980. cmApRC_t cmApAlsaInitialize( cmRpt_t* rpt, unsigned baseApDevIdx )
  981. {
  982. cmApRC_t rc = kOkApRC;
  983. int err;
  984. int cardNum = -1;
  985. if((rc = cmApAlsaFinalize()) != kOkApRC )
  986. return rc;
  987. recdSetup();
  988. cmApRoot_t* p = &_cmApRoot;
  989. memset(p,0,sizeof(cmApRoot_t));
  990. p->rpt = rpt;
  991. p->asyncFl = false;
  992. // for each sound card
  993. while(1)
  994. {
  995. snd_ctl_t* cardH;
  996. char* cardNamePtr = NULL;
  997. char* cardLongNamePtr = NULL;
  998. int devNum = -1;
  999. int devStrN = 31;
  1000. char devStr[devStrN+1];
  1001. // get the next card handle
  1002. if((err = snd_card_next(&cardNum)) < 0 )
  1003. {
  1004. _cmApOsError(p,err,"Error getting sound card handle");
  1005. return kSysErrApRC;
  1006. }
  1007. // if no more card's to get
  1008. if( cardNum < 0 )
  1009. break;
  1010. // get the card short name
  1011. if(((err = snd_card_get_name(cardNum,&cardNamePtr)) < 0) || (cardNamePtr == NULL))
  1012. {
  1013. _cmApOsError(p,err,"Unable to get card name for card number %i\n",cardNum);
  1014. goto releaseCard;
  1015. }
  1016. // get the card long name
  1017. if((err = snd_card_get_longname(cardNum,&cardLongNamePtr)) < 0 || cardLongNamePtr == NULL )
  1018. {
  1019. _cmApOsError(p,err,"Unable to get long card name for card number %i\n",cardNum);
  1020. goto releaseCard;
  1021. }
  1022. // form the device name for this card
  1023. if(snprintf(devStr,devStrN,"hw:%i",cardNum) > devStrN )
  1024. {
  1025. _cmApOsError(p,0,"Device name is too long for buffer.");
  1026. goto releaseCard;
  1027. }
  1028. // open the card device driver
  1029. if((err = snd_ctl_open(&cardH, devStr, 0)) < 0 )
  1030. {
  1031. _cmApOsError(p,err,"Error opening sound card %i.",cardNum);
  1032. goto releaseCard;
  1033. }
  1034. // for each device on this card
  1035. while(1)
  1036. {
  1037. snd_pcm_info_t* info;
  1038. int subDevCnt = 1;
  1039. int i,j;
  1040. // get the next device on this card
  1041. if((err = snd_ctl_pcm_next_device(cardH,&devNum)) < 0 )
  1042. {
  1043. _cmApOsError(p,err,"Error gettign next device on card %i",cardNum);
  1044. break;
  1045. }
  1046. // if no more devices to get
  1047. if( devNum < 0 )
  1048. break;
  1049. // allocate a pcmInfo record
  1050. snd_pcm_info_alloca(&info);
  1051. memset(info, 0, snd_pcm_info_sizeof());
  1052. // set the device to query
  1053. snd_pcm_info_set_device(info, devNum );
  1054. for(i=0; i<subDevCnt; i++)
  1055. {
  1056. cmApDevRecd_t dr;
  1057. bool inputFl = false;
  1058. memset(&dr,0,sizeof(dr));
  1059. for(j=0; j<2; j++,inputFl=!inputFl)
  1060. {
  1061. snd_pcm_t* pcmH = NULL;
  1062. dr.devIdx = -1;
  1063. // set the subdevice and I/O direction to query
  1064. snd_pcm_info_set_subdevice(info,i);
  1065. snd_pcm_info_set_stream(info,inputFl ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK);
  1066. // if this device does not use this sub-device
  1067. if((err = snd_ctl_pcm_info(cardH,info)) < 0 )
  1068. continue;
  1069. // get the count of subdevices this device uses
  1070. if(i == 0 )
  1071. subDevCnt = snd_pcm_info_get_subdevices_count(info);
  1072. // if this device has no sub-devices
  1073. if(subDevCnt == 0 )
  1074. continue;
  1075. // form the device name and desc. string
  1076. if(strlen(dr.nameStr) == 0)
  1077. snprintf(dr.nameStr,NAME_CHAR_CNT,"hw:%i,%i,%i",cardNum,devNum,i);
  1078. if(strlen(dr.descStr) == 0)
  1079. {
  1080. snprintf(dr.descStr,DESC_CHAR_CNT,"%s %s",cardNamePtr,snd_pcm_info_get_name(info));
  1081. //snprintf(dr.descStr,DESC_CHAR_CNT,"Name:%s Card:[%s] [%s] Device:%s Subdevice:%s",dr.nameStr, cardNamePtr,cardLongNamePtr,snd_pcm_info_get_id(info),snd_pcm_info_get_name(info));
  1082. }
  1083. // attempt to open the sub-device
  1084. if((err = _cmApDevOpen(&pcmH,dr.nameStr,inputFl)) < 0 )
  1085. _cmApDevSetupError(p,err,inputFl,&dr,"Unable to open the PCM handle");
  1086. else
  1087. {
  1088. snd_pcm_hw_params_t* hwParams;
  1089. // allocate the parameter record
  1090. snd_pcm_hw_params_alloca(&hwParams);
  1091. memset( hwParams,0,snd_pcm_hw_params_sizeof());
  1092. // load the parameter record
  1093. if((err = snd_pcm_hw_params_any(pcmH,hwParams)) < 0 )
  1094. _cmApDevSetupError(p,err,inputFl,&dr,"Error obtaining hw param record");
  1095. else
  1096. {
  1097. unsigned* chCntPtr = inputFl ? &dr.iChCnt : &dr.oChCnt;
  1098. unsigned rate;
  1099. snd_pcm_hw_params_get_rate_max(hwParams,&rate,NULL);
  1100. // extract the channel count
  1101. if((err = snd_pcm_hw_params_get_channels_max(hwParams, chCntPtr )) < 0 )
  1102. _cmApDevSetupError(p,err,inputFl,&dr,"Error getting channel count.");
  1103. else
  1104. // this device uses this subdevice in the current direction
  1105. dr.flags += inputFl ? kInFl : kOutFl;
  1106. printf("%s in:%i chs:%i rate:%i\n",dr.nameStr,inputFl,*chCntPtr,rate);
  1107. }
  1108. // close the sub-device
  1109. snd_pcm_close(pcmH);
  1110. }
  1111. } // in/out loop
  1112. // insert the device in the device array
  1113. if( dr.flags != 0 )
  1114. _cmApDevAppend(p,&dr);
  1115. } // sub-dev loop
  1116. } // device loop
  1117. releaseCard:
  1118. snd_ctl_close(cardH);
  1119. } // card loop
  1120. if( rc == kOkApRC && p->asyncFl==false )
  1121. {
  1122. p->pollfdsCnt = 0;
  1123. p->pollfdsAllocCnt = 2*p->devCnt;
  1124. p->pollfds = cmMemAllocZ(struct pollfd, p->pollfdsAllocCnt );
  1125. p->pollfdsDesc = cmMemAllocZ(cmApPollfdsDesc_t, p->pollfdsAllocCnt );
  1126. if( cmThreadCreate(&p->thH,_cmApThreadFunc,p,rpt) != kOkThRC )
  1127. {
  1128. _cmApOsError(p,0,"Thread create failed.");
  1129. rc = kThreadFailApRC;
  1130. }
  1131. }
  1132. return rc;
  1133. }
  1134. cmApRC_t cmApAlsaFinalize()
  1135. {
  1136. cmApRoot_t* p = &_cmApRoot;
  1137. int i;
  1138. cmApRC_t rc = kOkApRC;
  1139. if( p->asyncFl==false && cmThreadIsValid(p->thH) )
  1140. if( cmThreadDestroy(&p->thH) != kOkThRC )
  1141. {
  1142. _cmApOsError(p,0,"Thread destroy failed.");
  1143. rc = kThreadFailApRC;
  1144. }
  1145. for(i=0; i<p->devCnt; ++i)
  1146. {
  1147. _cmApDevShutdown(p,p->devArray+i,true);
  1148. _cmApDevShutdown(p,p->devArray+i,false);
  1149. cmMemPtrFree(&p->devArray[i].iBuf);
  1150. cmMemPtrFree(&p->devArray[i].oBuf);
  1151. }
  1152. cmMemPtrFree(&p->pollfds);
  1153. cmMemPtrFree(&p->pollfdsDesc);
  1154. cmMemPtrFree(&p->devArray);
  1155. p->devAllocCnt = 0;
  1156. p->devCnt = 0;
  1157. recdFree();
  1158. //write_rec(2);
  1159. return rc;
  1160. }
  1161. cmApRC_t cmApAlsaDeviceCount()
  1162. { return _cmApRoot.devCnt; }
  1163. const char* cmApAlsaDeviceLabel( unsigned devIdx )
  1164. {
  1165. assert(devIdx < cmApAlsaDeviceCount());
  1166. return _cmApRoot.devArray[devIdx].descStr;
  1167. }
  1168. unsigned cmApAlsaDeviceChannelCount( unsigned devIdx, bool inputFl )
  1169. {
  1170. assert(devIdx < cmApAlsaDeviceCount());
  1171. return inputFl ? _cmApRoot.devArray[devIdx].iChCnt : _cmApRoot.devArray[devIdx].oChCnt;
  1172. }
  1173. double cmApAlsaDeviceSampleRate( unsigned devIdx )
  1174. {
  1175. assert(devIdx < cmApAlsaDeviceCount());
  1176. return (double)_cmApRoot.devArray[devIdx].srate;
  1177. }
  1178. unsigned cmApAlsaDeviceFramesPerCycle( unsigned devIdx, bool inputFl )
  1179. {
  1180. assert(devIdx < cmApAlsaDeviceCount());
  1181. return _cmApRoot.devArray[devIdx].framesPerCycle;
  1182. }
  1183. cmApRC_t cmApAlsaDeviceSetup(
  1184. unsigned devIdx,
  1185. double srate,
  1186. unsigned framesPerCycle,
  1187. cmApCallbackPtr_t callbackPtr,
  1188. void* userCbPtr )
  1189. {
  1190. assert( devIdx < cmApAlsaDeviceCount());
  1191. cmApRoot_t* p = &_cmApRoot;
  1192. cmApDevRecd_t* drp = _cmApRoot.devArray + devIdx;
  1193. unsigned periodsPerBuf = kDfltPeriodsPerBuf;
  1194. if( p->asyncFl == false )
  1195. if( cmThreadPause(p->thH,kWaitThFl | kPauseThFl) != kOkThRC )
  1196. {
  1197. _cmApOsError(p,0,"Audio thread pause failed.");
  1198. return kThreadFailApRC;
  1199. }
  1200. if( _cmApDevSetup(drp, srate, framesPerCycle, periodsPerBuf ) )
  1201. {
  1202. drp->srate = srate;
  1203. drp->framesPerCycle = framesPerCycle;
  1204. drp->periodsPerBuf = periodsPerBuf;
  1205. drp->cbPtr = callbackPtr;
  1206. drp->userCbPtr = userCbPtr;
  1207. return kOkApRC;
  1208. }
  1209. return kSysErrApRC;
  1210. }
  1211. cmApRC_t cmApAlsaDeviceStart( unsigned devIdx )
  1212. {
  1213. assert( devIdx < cmApAlsaDeviceCount());
  1214. int err;
  1215. cmApRoot_t* p = &_cmApRoot;
  1216. cmApDevRecd_t* drp = p->devArray + devIdx;
  1217. bool retFl = true;
  1218. bool inputFl = true;
  1219. unsigned i;
  1220. for(i=0; i<2; ++i,inputFl=!inputFl)
  1221. {
  1222. snd_pcm_t* pcmH = inputFl ? drp->iPcmH : drp->oPcmH;
  1223. if( pcmH != NULL )
  1224. {
  1225. snd_pcm_state_t state = snd_pcm_state(pcmH);
  1226. if( state != SND_PCM_STATE_RUNNING )
  1227. {
  1228. unsigned chCnt = inputFl ? drp->iChCnt : drp->oChCnt;
  1229. unsigned frmCnt = inputFl ? drp->iFpC : drp->oFpC;
  1230. const char* ioLabel = inputFl ? "Input" : "Output";
  1231. //printf("%i %s state:%s %i %i\n",drp->devIdx, ioLabel,_pcmStateToString(snd_pcm_state(pcmH)),chCnt,frmCnt);
  1232. // preparing may not always be necessary because the earlier call to snd_pcm_hw_params()
  1233. // may have left the device prepared - the redundant call however doesn't seem to hurt
  1234. if((err= snd_pcm_prepare(pcmH)) < 0 )
  1235. retFl = _cmApDevSetupError(p,err,inputFl,drp,"Error preparing the %i device.",ioLabel);
  1236. else
  1237. {
  1238. recdStart(drp,inputFl);
  1239. if( inputFl == false )
  1240. {
  1241. int j;
  1242. for(j=0; j<1; ++j)
  1243. if((err = _cmApWriteBuf( drp, pcmH, NULL, chCnt, frmCnt, drp->oBits, drp->oSigBits )) < 0 )
  1244. {
  1245. retFl = _cmApDevSetupError(p,err,inputFl,drp,"Write before start failed.");
  1246. break;
  1247. }
  1248. }
  1249. else
  1250. {
  1251. if((err = snd_pcm_start(pcmH)) < 0 )
  1252. retFl = _cmApDevSetupError(p,err,inputFl,drp,"'%s' start failed.",ioLabel);
  1253. }
  1254. // wait 500 microseconds between starting and stopping - this prevents
  1255. // input and output and other device callbacks from landing on top of
  1256. // each other - when this happens callbacks are dropped.
  1257. if( p->asyncFl )
  1258. usleep(500);
  1259. }
  1260. //printf("%i %s state:%s %i %i\n",drp->devIdx, ioLabel,_cmApPcmStateToString(snd_pcm_state(pcmH)),chCnt,frmCnt);
  1261. }
  1262. }
  1263. }
  1264. if( p->asyncFl == false )
  1265. if( cmThreadPause(p->thH,0) != kOkThRC )
  1266. {
  1267. _cmApOsError(p,0,"Audio thread start failed.");
  1268. retFl = false;
  1269. }
  1270. return retFl ? kOkApRC : kSysErrApRC;
  1271. }
  1272. cmApRC_t cmApAlsaDeviceStop( unsigned devIdx )
  1273. {
  1274. int err;
  1275. bool retFl = true;
  1276. cmApRoot_t* p = &_cmApRoot;
  1277. cmApDevRecd_t* drp = p->devArray + devIdx;
  1278. if( drp->iPcmH != NULL )
  1279. if((err = snd_pcm_drop(drp->iPcmH)) < 0 )
  1280. retFl = _cmApDevSetupError(p,err,true,drp,"Input stop failed.");
  1281. if( drp->oPcmH != NULL )
  1282. if((err = snd_pcm_drop(drp->oPcmH)) < 0 )
  1283. retFl = _cmApDevSetupError(p,err,false,drp,"Output stop failed.");
  1284. if( p->asyncFl == false )
  1285. if( cmThreadPause(p->thH,kPauseThFl) != kOkThRC )
  1286. {
  1287. _cmApOsError(p,0,"Audio thread pause failed.");
  1288. retFl = false;
  1289. }
  1290. return retFl ? kOkApRC : kSysErrApRC;
  1291. }
  1292. bool cmApAlsaDeviceIsStarted( unsigned devIdx )
  1293. {
  1294. assert( devIdx < cmApAlsaDeviceCount());
  1295. bool iFl = false;
  1296. bool oFl = false;
  1297. const cmApDevRecd_t* drp = _cmApRoot.devArray + devIdx;
  1298. if( drp->iPcmH != NULL )
  1299. iFl = snd_pcm_state(drp->iPcmH) == SND_PCM_STATE_RUNNING;
  1300. if( drp->oPcmH != NULL )
  1301. oFl = snd_pcm_state(drp->oPcmH) == SND_PCM_STATE_RUNNING;
  1302. return iFl || oFl;
  1303. }
  1304. //{ { label:alsaDevRpt }
  1305. //(
  1306. // Here's an example of generating a report of available
  1307. // ALSA devices.
  1308. //)
  1309. //[
  1310. void cmApAlsaDeviceReport( cmRpt_t* rpt )
  1311. {
  1312. unsigned i;
  1313. for(i=0; i<_cmApRoot.devCnt; i++)
  1314. {
  1315. cmRptPrintf(rpt,"%i : ",i);
  1316. _cmApDevReport(rpt,_cmApRoot.devArray + i );
  1317. }
  1318. }
  1319. //]
  1320. //}
  1321. void cmApAlsaDeviceRtReport( cmRpt_t* rpt, unsigned devIdx )
  1322. {
  1323. _cmApDevRtReport(rpt, _cmApRoot.devArray + devIdx );
  1324. }