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

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