libcm is a C development framework with an emphasis on audio signal processing applications.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

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