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 54KB

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