libcm is a C development framework with an emphasis on audio signal processing applications.
Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

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. }