123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692 |
-
- #include "cmPrefix.h"
- #include "cmGlobal.h"
- #include "cmRpt.h"
- #include "cmTime.h"
- #include "cmAudioPort.h"
- #include "cmMem.h"
- #include "cmTime.h"
- #include "cmMallocDebug.h"
- #include "cmAudioPort.h"
- #include "cmAudioPortAlsa.h"
- #include "cmThread.h"
-
- #include "alsa/asoundlib.h"
- #include <unistd.h> // usleep
-
- #define NAME_CHAR_CNT (255)
- #define DESC_CHAR_CNT (255)
- #define INIT_DEV_CNT (5)
-
- enum { kDfltPeriodsPerBuf = 2, kPollfdsArrayCnt=2 };
-
- enum { kInFl=0x01, kOutFl=0x02 };
-
- struct cmApRoot_str;
-
-
- typedef struct devRecd_str
- {
- struct cmApRoot_str* rootPtr;
- unsigned devIdx;
- cmChar_t nameStr[ NAME_CHAR_CNT ];
- cmChar_t descStr[ DESC_CHAR_CNT ];
- unsigned flags;
-
- unsigned framesPerCycle; // samples per sub-buffer
- unsigned periodsPerBuf; // sub-buffers per buffer
- snd_async_handler_t* ahandler;
- unsigned srate; // device sample rate
-
- unsigned iChCnt; // ch count
- unsigned oChCnt;
-
- unsigned iBits; // bits per sample
- unsigned oBits;
-
- unsigned iSigBits; // significant bits in each sample beginning
- unsigned oSigBits; // with the most sig. bit.
-
-
- cmApSample_t* iBuf; // iBuf[ iFpc * iChCnt ]
- cmApSample_t* oBuf; // oBuf[ oFpc * oChCnt ]
-
- unsigned iFpC; // buffer frames per cycle (in ALSA this is call period_size)
- unsigned oFpC;
-
- snd_pcm_t* iPcmH; // device handle
- snd_pcm_t* oPcmH;
-
- unsigned iCbCnt; // callback count
- unsigned oCbCnt;
-
- unsigned iErrCnt; // error count
- unsigned oErrCnt;
-
- cmApCallbackPtr_t cbPtr; // user callback
- void* userCbPtr;
-
- } cmApDevRecd_t;
-
- typedef struct cmApPoll_str
- {
- cmApDevRecd_t* devPtr;
- bool inputFl;
- unsigned fdsCnt;
- } cmApPollfdsDesc_t;
-
- typedef struct cmApRoot_str
- {
- cmRpt_t* rpt; //
- cmApDevRecd_t* devArray; // array of device records
- unsigned devCnt; // count of actual dev recds in devArray[]
- unsigned devAllocCnt; // count of dev recds allocated in devArray[]
-
- bool asyncFl; // true=use async callback false=use polling thread
-
- cmThreadH_t thH; // polling thread
- unsigned pollfdsAllocCnt; // 2*devCnt (max possible in+out handles)
- struct pollfd* pollfds; // pollfds[ pollfdsAllocCnt ]
- cmApPollfdsDesc_t *pollfdsDesc; // pollfdsDesc[ pollfdsAllocCnt ]
- unsigned pollfdsCnt; // count of active recds in pollfds[] and pollfdsDesc[]
-
- } cmApRoot_t;
-
- cmApRoot_t _cmApRoot = { NULL, NULL, 0, 0 };
-
- //===============================================================================================
- enum
- {
- kReadErrRId,
- kWriteErrRId
- };
-
-
- #undef cmALSA_RECD
- #ifdef cmALSA_RECD
-
- enum
- {
- kNotUsedRId,
- kStartRId,
- kCbRId,
- kSysErrRId,
- kAppErrRId,
- kRecoverRId
- };
-
- typedef struct
- {
- int code;
- char* label;
- } recdErrMap_t;
-
- typedef struct
- {
- unsigned devIdx;
- unsigned typeId;
- cmTimeSpec_t t;
- bool inputFl;
- unsigned arg;
- unsigned arg1;
- } recd;
-
- recd* recdArray = NULL;
- unsigned recdCnt = 0;
- unsigned recdIdx = 0;
- cmTimeSpec_t recdTime;
-
- recdErrMap_t recdSysErrMap[] =
- {
- { -EPIPE, "EPIPE" },
- { -ESTRPIPE, "ESTRPIPE" },
- { -EBADFD, "EBADFD" },
- { 0, NULL }
- };
-
- recdErrMap_t recdAppErrMap[] =
- {
- { kReadErrRId, "Read Error"},
- { kWriteErrRId, "Write Error"},
- { 0, NULL }
- };
-
- const char* _recdSysErrToLabel( int err )
- {
- unsigned i;
- for(i=0; recdSysErrMap[i].label != NULL; ++i)
- if( recdSysErrMap[i].code == err )
- return recdSysErrMap[i].label;
-
- return "<Unknown sys error>";
- }
-
- const char* _recdAppErrToLabel( int err )
- {
- unsigned i;
- for(i=0; recdAppErrMap[i].label != NULL; ++i)
- if( recdAppErrMap[i].code == err )
- return recdAppErrMap[i].label;
-
- return "<Unknown app error>";
- }
-
- void recdSetup()
- {
- recdCnt = 100;
- recdIdx = 0;
- clock_gettime(CLOCK_REALTIME,&recdTime);
- recdArray = cmMemAllocZ(recd,recdCnt);
- }
-
- void recdPush( unsigned typeId, unsigned devIdx, bool inputFl, unsigned arg, unsigned arg1 )
- {
- //if( recdIdx == recdCnt )
- // return;
-
- if( recdIdx == recdCnt )
- recdIdx = 0;
-
- recd* r = recdArray + recdIdx;
- r->typeId = typeId;
- r->devIdx = devIdx;
- r->inputFl = inputFl;
- r->arg = arg;
- r->arg1 = arg1;
- clock_gettime(CLOCK_REALTIME,&r->t);
- ++recdIdx;
- }
-
-
- void recdStart( const cmApDevRecd_t* drp, bool inputFl )
- { recdPush(kStartRId,drp->devIdx,inputFl,0,0); }
-
- void recdCb( const cmApDevRecd_t* drp, bool inputFl, unsigned frmCnt )
- { recdPush(kCbRId,drp->devIdx,inputFl, inputFl ? drp->iCbCnt : drp->oCbCnt, 0 ); }
-
- void recdSysErr( bool inputFl, int err )
- { recdPush(kSysErrRId,cmInvalidIdx,inputFl,err,0); }
-
- void recdAppErr( const cmApDevRecd_t* drp, bool inputFl, int app, int err )
- { recdPush(kAppErrRId,drp->devIdx,inputFl,app,err); }
-
- void recdRecover( const cmApDevRecd_t* drp, bool inputFl, int err, int line )
- { recdPush(kRecoverRId,drp->devIdx,inputFl,err,line); }
-
- void recdPrint()
- {
- unsigned i;
- cmTimeSpec_t t0 = recdTime;
- unsigned j = recdIdx;
- for(i=0; i<recdCnt; ++i)
- {
-
- recd* r = recdArray + j;
- ++j;
- if( j == recdCnt )
- j = 0;
-
- double ms = cmTimeElapsedMicros(&recdTime,&r->t)/1000.0;
- double dms = cmTimeElapsedMicros(&t0,&r->t)/1000.0;
-
- t0 = r->t;
-
- printf("%5i %8.3f %8.3f %i %s: ",i,ms,dms,r->devIdx,r->inputFl ? "in ":"out");
-
- switch(r->typeId)
- {
- case kStartRId:
- printf("start");
- break;
-
- case kCbRId:
- printf("callback %i",r->arg );
- break;
-
- case kSysErrRId:
- printf("sys err %s ",_recdSysErrToLabel(r->arg));
- break;
-
- case kAppErrRId:
- printf("app err %s %s",_recdAppErrToLabel(r->arg),_recdSysErrToLabel(r->arg1));
- break;
-
- case kRecoverRId:
- printf("Recover %s %i",_recdSysErrToLabel(r->arg),r->arg1);
- break;
-
- default:
- printf("Unknown recd type id.\n");
- break;
- }
-
- printf("\n");
- }
- }
-
- void recdFree()
- {
- recdPrint();
- cmMemFree(recdArray);
- }
- #else
-
- void recdSetup(){}
- void recdPush( unsigned typeId, unsigned devIdx, bool inputFl, unsigned arg, unsigned arg1 ){}
- void recdStart( const cmApDevRecd_t* drp, bool inputFl ){}
- void recdCb( const cmApDevRecd_t* drp, bool inputFl, unsigned frmCnt ){}
- void recdSysErr( bool inputFl, int err ){}
- void recdAppErr( const cmApDevRecd_t* drp, bool inputFl, int app, int err ){}
- void recdRecover( const cmApDevRecd_t* drp, bool inputFl, int err, int line ){}
- void recdPrint(){}
- void recdFree(){}
-
- #endif
-
- //===================================================================================================
-
-
- cmApRC_t _cmApOsError( cmApRoot_t* p, int err, const char* fmt, ... )
- {
- va_list vl;
- va_start(vl,fmt);
- int msgN = 255;
- char msg[ msgN+1];
-
- vsnprintf(msg,msgN,fmt,vl);
-
- if( err )
- cmRptPrintf(p->rpt,"%s ALSA Error:%s. ",msg,snd_strerror(err));
- else
- cmRptPrintf(p->rpt,"%s ",msg);
-
- cmRptPrintf(p->rpt,"\n");
-
- va_end(vl);
-
- return kSysErrApRC;
- }
-
- bool _cmApDevSetupError( cmApRoot_t* p, int err, bool inputFl, const cmApDevRecd_t* drp, const char* fmt, ... )
- {
- va_list vl;
- int msgN = 255;
- char msg[ msgN + 1];
-
- va_start(vl,fmt);
- vsnprintf(msg,msgN,fmt,vl);
- _cmApOsError(p,err,"%s for %s '%s' : '%s'.",msg,inputFl ? "INPUT" : "OUTPUT", drp->nameStr, drp->descStr);
- va_end(vl);
- return false;
- }
-
- const char* _cmApPcmStateToString( snd_pcm_state_t state )
- {
- switch( state )
- {
- case SND_PCM_STATE_OPEN: return "open";
- case SND_PCM_STATE_SETUP: return "setup";
- case SND_PCM_STATE_PREPARED: return "prepared";
- case SND_PCM_STATE_RUNNING: return "running";
- case SND_PCM_STATE_XRUN: return "xrun";
- case SND_PCM_STATE_DRAINING: return "draining";
- case SND_PCM_STATE_PAUSED: return "paused";
- case SND_PCM_STATE_SUSPENDED: return "suspended";
- case SND_PCM_STATE_DISCONNECTED: return "disconnected";
-
- }
- return "<invalid>";
- }
-
- void _cmApDevRtReport( cmRpt_t* rpt, cmApDevRecd_t* drp )
- {
- cmRptPrintf(rpt,"cb i:%i o:%i err i:%i o:%i",drp->iCbCnt,drp->oCbCnt,drp->iErrCnt,drp->oErrCnt);
-
- if( drp->iPcmH != NULL )
- cmRptPrintf(rpt," state i:%s",_cmApPcmStateToString(snd_pcm_state(drp->iPcmH)));
-
- if( drp->oPcmH != NULL )
- cmRptPrintf(rpt," o:%s",_cmApPcmStateToString(snd_pcm_state(drp->oPcmH)));
-
- cmRptPrint(rpt,"\n ");
-
- }
-
- void _cmApDevReport( cmRpt_t* rpt, cmApDevRecd_t* drp )
- {
- bool inputFl = true;
- snd_pcm_t* pcmH;
- int err;
- unsigned i;
-
- cmApRoot_t* p = drp->rootPtr;
-
- cmRptPrintf(rpt,"%s %s Device:%s Desc:%s\n", drp->flags & kInFl ? "IN ":"", drp->flags & kOutFl ? "OUT ":"", drp->nameStr, drp->descStr);
-
- for(i=0; i<2; i++,inputFl=!inputFl)
- {
- if( ((inputFl==true) && (drp->flags&kInFl)) || (((inputFl==false) && (drp->flags&kOutFl))))
- {
- const char* ioLabel = inputFl ? "In" : "Out";
-
- // attempt to open the sub-device
- if((err = snd_pcm_open(&pcmH,drp->nameStr,inputFl ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,0)) < 0 )
- _cmApDevSetupError(p,err,inputFl,drp,"Attempt to open the PCM handle failed");
- else
- {
- snd_pcm_hw_params_t* hwParams;
-
- snd_pcm_hw_params_alloca(&hwParams);
- memset(hwParams,0,snd_pcm_hw_params_sizeof());
-
- // load the parameter record
- if((err = snd_pcm_hw_params_any(pcmH,hwParams)) < 0 )
- _cmApDevSetupError(p,err,inputFl,drp,"Error obtaining hw param record");
- else
- {
- unsigned minChCnt=0,maxChCnt=0,minSrate=0,maxSrate=0;
- snd_pcm_uframes_t minPeriodFrmCnt=0,maxPeriodFrmCnt=0,minBufFrmCnt=0,maxBufFrmCnt=0;
- int dir;
-
-
- // extract the min channel count
- if((err = snd_pcm_hw_params_get_channels_min(hwParams, &minChCnt )) < 0 )
- _cmApDevSetupError(p,err,inputFl,drp,"Error getting min. channel count.");
-
- // extract the max channel count
- if((err = snd_pcm_hw_params_get_channels_max(hwParams, &maxChCnt )) < 0 )
- _cmApDevSetupError(p,err,inputFl,drp,"Error getting max. channel count.");
-
- // extract the min srate
- if((err = snd_pcm_hw_params_get_rate_min(hwParams, &minSrate,&dir )) < 0 )
- _cmApDevSetupError(p,err,inputFl,drp,"Error getting min. sample rate.");
-
- // extract the max srate
- if((err = snd_pcm_hw_params_get_rate_max(hwParams, &maxSrate,&dir )) < 0 )
- _cmApDevSetupError(p,err,inputFl,drp,"Error getting max. sample rate.");
-
- // extract the min period
- if((err = snd_pcm_hw_params_get_period_size_min(hwParams, &minPeriodFrmCnt,&dir )) < 0 )
- _cmApDevSetupError(p,err,inputFl,drp,"Error getting min. period frame count.");
-
- // extract the max period
- if((err = snd_pcm_hw_params_get_period_size_max(hwParams, &maxPeriodFrmCnt,&dir )) < 0 )
- _cmApDevSetupError(p,err,inputFl,drp,"Error getting max. period frame count.");
-
- // extract the min buf
- if((err = snd_pcm_hw_params_get_buffer_size_min(hwParams, &minBufFrmCnt )) < 0 )
- _cmApDevSetupError(p,err,inputFl,drp,"Error getting min. period frame count.");
-
- // extract the max buffer
- if((err = snd_pcm_hw_params_get_buffer_size_max(hwParams, &maxBufFrmCnt )) < 0 )
- _cmApDevSetupError(p,err,inputFl,drp,"Error getting max. period frame count.");
-
- cmRptPrintf(rpt,"%s chs:%i - %i srate:%i - %i period:%i - %i buf:%i - %i half duplex only:%s joint duplex:%s\n",
- ioLabel,minChCnt,maxChCnt,minSrate,maxSrate,minPeriodFrmCnt,maxPeriodFrmCnt,minBufFrmCnt,maxBufFrmCnt,
- (snd_pcm_hw_params_is_half_duplex(hwParams) ? "yes" : "no"),
- (snd_pcm_hw_params_is_joint_duplex(hwParams) ? "yes" : "no"));
- }
-
- if((err = snd_pcm_close(pcmH)) < 0)
- _cmApDevSetupError(p,err,inputFl,drp,"Error closing PCM handle");
- }
- }
- }
- }
-
-
- // Called by cmApInitialize() to append a cmApDevRecd to the _cmApRoot.devArray[].
- void _cmApDevAppend( cmApRoot_t* p, cmApDevRecd_t* drp )
- {
- if( p->devCnt == p->devAllocCnt )
- {
- p->devArray = cmMemResizePZ( cmApDevRecd_t, p->devArray, p->devAllocCnt + INIT_DEV_CNT );
- p->devAllocCnt += INIT_DEV_CNT;
- }
-
- drp->devIdx = p->devCnt; // set the device index
- drp->rootPtr = p; // set the pointer back to the root
-
- memcpy(p->devArray + p->devCnt, drp, sizeof(cmApDevRecd_t));
-
- ++p->devCnt;
- }
-
- cmApRC_t _cmApDevShutdown( cmApRoot_t* p, cmApDevRecd_t* drp, bool inputFl )
- {
- int err;
-
- snd_pcm_t** pcmH = inputFl ? &drp->iPcmH : &drp->oPcmH;
-
- if( *pcmH != NULL )
- {
- if((err = snd_pcm_close(*pcmH)) < 0 )
- {
- _cmApDevSetupError(p,err,inputFl,drp,"Error closing device handle.");
- return kSysErrApRC;
- }
-
- *pcmH = NULL;
- }
-
- return kOkApRC;
- }
-
- int _cmApDevOpen( snd_pcm_t** pcmHPtr, const char* devNameStr, bool inputFl )
- {
- int cnt = 0;
- int err;
-
- do
- {
- if((err = snd_pcm_open(pcmHPtr,devNameStr,inputFl ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,0)) < 0 )
- {
- cnt++;
- usleep(10000); // sleep for 10 milliseconds
- }
-
- }while(cnt<100 && err == -EBUSY );
-
- return err;
- }
-
- void _cmApXrun_recover( snd_pcm_t* pcmH, int err, cmApDevRecd_t* drp, bool inputFl, int line )
- {
- char dirCh = inputFl ? 'I' : 'O';
-
- inputFl ? drp->iErrCnt++ : drp->oErrCnt++;
-
- recdRecover(drp,inputFl,err,line);
-
- // -EPIPE signals and over/underrun (see pcm.c example xrun_recovery())
- switch( err )
- {
- case -EPIPE:
- {
-
- int silentFl = 1;
- if((err = snd_pcm_recover( pcmH, err, silentFl )) < 0 )
- _cmApDevSetupError(drp->rootPtr,err,inputFl,drp,"recover failed.");
-
- if( inputFl )
- {
- if((err= snd_pcm_prepare(pcmH)) < 0 )
- _cmApDevSetupError(drp->rootPtr,err,inputFl,drp,"re-prepare failed.");
- else
- if((err = snd_pcm_start(pcmH)) < 0 )
- _cmApDevSetupError(drp->rootPtr,err,inputFl,drp,"restart failed.");
- }
- else
- {
- /*
- _cmApWriteBuf(pcmH, NULL, drp->oChCnt, drp->oFpC );
- _cmApWriteBuf(pcmH, NULL, drp->oChCnt, drp->oFpC );
-
- if((err = snd_pcm_prepare(pcmH))<0)
- _cmApDevSetupError(drp->rootPtr,err,inputFl,drp,"Recovery failed.\n");
- else
- if((err = snd_pcm_resume(pcmH))<0)
- _cmApDevSetupError(drp->rootPtr,err,inputFl,drp,"Resume failed.\n");
- */
- }
-
-
- printf("EPIPE %c %i %i %i\n",dirCh,drp->devIdx,drp->oCbCnt,line);
-
- //if((err = snd_pcm_prepare(pcmH))<0)
- // _devSetupError(err,inputFl,*drp,"Recovery failed.\n");
- //else
- // if((err = snd_pcm_resume(pcmH))<0)
- // _devSetupError(err,inputFl,*drp,"Resume failed.\n");
- break;
- }
-
- case -ESTRPIPE:
- {
- int silentFl = 1;
- if((err = snd_pcm_recover( pcmH, err, silentFl )) < 0 )
- _cmApDevSetupError(drp->rootPtr,err,inputFl,drp,"recover failed.");
-
- printf("audio port impl ESTRPIPE:%c\n",dirCh);
- break;
- }
-
- case -EBADFD:
- {
- _cmApDevSetupError(drp->rootPtr,err,inputFl,drp,"%s failed.",inputFl ? "Read" : "Write" );
- break;
- }
-
- default:
- _cmApDevSetupError(drp->rootPtr,err,inputFl,drp,"Unknown rd/wr error.\n");
-
- } // switch
-
- }
-
- void _cmApStateRecover( snd_pcm_t* pcmH, cmApDevRecd_t* drp, bool inputFl )
- {
- int err = 0;
-
- switch( snd_pcm_state(pcmH))
- {
- case SND_PCM_STATE_XRUN:
- err = -EPIPE;
- break;
-
- case SND_PCM_STATE_SUSPENDED:
- err = -ESTRPIPE;
- break;
-
- case SND_PCM_STATE_OPEN:
- case SND_PCM_STATE_SETUP:
- case SND_PCM_STATE_PREPARED:
- case SND_PCM_STATE_RUNNING:
- case SND_PCM_STATE_DRAINING:
- case SND_PCM_STATE_PAUSED:
- case SND_PCM_STATE_DISCONNECTED:
- //case SND_PCM_STATE_LAST:
- break;
- }
-
- if( err < 0 )
- _cmApXrun_recover( pcmH, err, drp, inputFl, __LINE__ );
-
- }
-
-
- // Returns count of frames written on success or < 0 on error;
- // set smpPtr to NULL to write a buffer of silence
- int _cmApWriteBuf( const cmApDevRecd_t* drp, snd_pcm_t* pcmH, const cmApSample_t* sp, unsigned chCnt, unsigned frmCnt, unsigned bits, unsigned sigBits )
- {
- int err = 0;
- unsigned bytesPerSmp = (bits==24 ? 32 : bits)/8;
- unsigned smpCnt = chCnt * frmCnt;
- unsigned byteCnt = bytesPerSmp * smpCnt;
- const cmApSample_t* ep = sp + smpCnt;
- char obuf[ byteCnt ];
-
- // if no output was given then fill the device buffer with zeros
- if( sp == NULL )
- memset(obuf,0,byteCnt);
- else
- {
- // otherwise convert the floating point samples to integers
- switch( bits )
- {
- case 8:
- {
- char* dp = (char*)obuf;
- while( sp < ep )
- *dp++ = (char)(*sp++ * 0x7f);
- }
- break;
-
- case 16:
- {
- short* dp = (short*)obuf;
- while( sp < ep )
- *dp++ = (short)(*sp++ * 0x7fff);
- }
- break;
-
- case 24:
- {
- int* dp = (int*)obuf;
- while( sp < ep )
- *dp++ = (int)(*sp++ * 0x7fffff);
- }
- break;
-
- case 32:
- {
- int* dp = (int*)obuf;
- while( sp < ep )
- *dp++ = (int)(*sp++ * 0x7fffffff);
- }
- break;
- }
- }
-
- // send the bytes to the device
- err = snd_pcm_writei( pcmH, obuf, frmCnt );
-
- if( err < 0 )
- {
- recdAppErr(drp,false,kWriteErrRId,err);
- _cmApDevSetupError(drp->rootPtr, err, false, drp, "ALSA write error" );
- }
- else
- if( err > 0 && err != frmCnt )
- {
- _cmApDevSetupError(drp->rootPtr, 0, false, drp, "Actual count of bytes written did not match the count provided." );
- }
-
-
- return err;
- }
-
-
-
- // Returns frames read on success or < 0 on error.
- // Set smpPtr to NULL to read the incoming buffer and discard it
- int _cmApReadBuf( const cmApDevRecd_t* drp, snd_pcm_t* pcmH, cmApSample_t* smpPtr, unsigned chCnt, unsigned frmCnt, unsigned bits, unsigned sigBits )
- {
- int err = 0;
- unsigned bytesPerSmp = (bits==24 ? 32 : bits)/8;
- unsigned smpCnt = chCnt * frmCnt;
- unsigned byteCnt = smpCnt * bytesPerSmp;
-
- char buf[ byteCnt ];
-
- // get the incoming samples into buf[] ...
- err = snd_pcm_readi(pcmH,buf,frmCnt);
-
- // if a read error occurred
- if( err < 0 )
- {
- recdAppErr(drp,true,kReadErrRId,err);
- _cmApDevSetupError(drp->rootPtr, err, false, drp, "ALSA read error" );
- }
- else
- if( err > 0 && err != frmCnt )
- {
- _cmApDevSetupError(drp->rootPtr, 0, false, drp, "Actual count of bytes read did not match the count requested." );
- }
-
- // if no buffer was given then there is nothing else to do
- if( smpPtr == NULL )
- return err;
-
- // setup the return buffer
- cmApSample_t* dp = smpPtr;
-
- cmApSample_t* ep = dp + cmMin(smpCnt,err*chCnt);
-
- switch(bits)
- {
- case 8:
- {
- char* sp = buf;
- while(dp < ep)
- *dp++ = ((cmApSample_t)*sp++) / 0x7f;
- }
- break;
-
- case 16:
- {
- short* sp = (short*)buf;
- while(dp < ep)
- *dp++ = ((cmApSample_t)*sp++) / 0x7fff;
- }
- break;
-
- case 24:
- {
- int* sp = (int*)buf;
- while(dp < ep)
- *dp++ = ((cmApSample_t)*sp++) / 0x7fffff;
- }
- break;
-
-
- case 32:
- {
- int* sp = (int*)buf;
- // The delta1010 (ICE1712) uses only the 24 highest bits according to
- //
- // http://www.alsa-project.org/alsa-doc/alsa-lib/pcm.html
- // <snip> The example: ICE1712 chips support 32-bit sample processing,
- // but low byte is ignored (playback) or zero (capture).
- //
- int mv = sigBits==24 ? 0x7fffff00 : 0x7fffffff;
- while(dp < ep)
- *dp++ = ((cmApSample_t)*sp++) / mv;
- }
- break;
- default:
- { assert(0); }
- }
-
- return err;
-
- }
-
- void _cmApStaticAsyncHandler( snd_async_handler_t* ahandler )
- {
- int err;
- snd_pcm_sframes_t avail;
- cmApDevRecd_t* drp = (cmApDevRecd_t*)snd_async_handler_get_callback_private(ahandler);
- snd_pcm_t* pcmH = snd_async_handler_get_pcm(ahandler);
- bool inputFl = snd_pcm_stream(pcmH) == SND_PCM_STREAM_CAPTURE;
- cmApSample_t* b = inputFl ? drp->iBuf : drp->oBuf;
- unsigned chCnt = inputFl ? drp->iChCnt : drp->oChCnt;
- unsigned frmCnt = inputFl ? drp->iFpC : drp->oFpC;
- cmApAudioPacket_t pkt;
-
- inputFl ? drp->iCbCnt++ : drp->oCbCnt++;
-
- pkt.devIdx = drp->devIdx;
- pkt.begChIdx = 0;
- pkt.chCnt = chCnt;
- pkt.audioFramesCnt = frmCnt;
- pkt.bitsPerSample = 32;
- pkt.flags = kInterleavedApFl | kFloatApFl;
- pkt.audioBytesPtr = b;
- pkt.userCbPtr = drp->userCbPtr;
-
- recdCb(drp,inputFl,0);
-
- _cmApStateRecover( pcmH, drp, inputFl );
-
- while( (avail = snd_pcm_avail_update(pcmH)) >= (snd_pcm_sframes_t)frmCnt )
- {
-
- // Handle inpuut
- if( inputFl )
- {
- // read samples from the device
- if((err = _cmApReadBuf(drp,pcmH,drp->iBuf,chCnt,frmCnt,drp->iBits,drp->oBits)) > 0 )
- {
- pkt.audioFramesCnt = err;
-
- drp->cbPtr(&pkt,1,NULL,0 ); // send the samples to the application
- }
- }
-
- // Handle output
- else
- {
- // callback to fill the buffer
- drp->cbPtr(NULL,0,&pkt,1);
-
- // note that the application may return fewer samples than were requested
- err = _cmApWriteBuf(drp, pcmH, pkt.audioFramesCnt < frmCnt ? NULL : drp->oBuf,chCnt,frmCnt,drp->oBits,drp->oSigBits);
-
- }
-
- // Handle read/write errors
- if( err < 0 )
- {
- inputFl ? drp->iErrCnt++ : drp->oErrCnt++;
- _cmApXrun_recover( pcmH, err, drp, inputFl, __LINE__ );
- }
- else
- {
- // _cmApStateRecover( pcmH, drp, inputFl );
- }
-
- } // while
-
- }
-
- bool _cmApThreadFunc(void* param)
- {
- cmApRoot_t* p = (cmApRoot_t*)param;
- int result;
- bool retFl = true;
-
- switch( result = poll(p->pollfds, p->pollfdsCnt, 250) )
- {
- case 0:
- // time out
- break;
-
- case -1:
- _cmApOsError(p,errno,"Poll fail.");
- break;
-
- default:
- {
- assert( result > 0 );
-
- unsigned i;
-
- // for each i/o stream
- for(i=0; i<p->pollfdsCnt; ++i)
- {
- cmApDevRecd_t* drp = p->pollfdsDesc[i].devPtr;
- bool inputFl = p->pollfdsDesc[i].inputFl;
- snd_pcm_t* pcmH = inputFl ? drp->iPcmH : drp->oPcmH;
- unsigned chCnt = inputFl ? drp->iChCnt : drp->oChCnt;
- unsigned frmCnt = inputFl ? drp->iFpC : drp->oFpC;
- cmApSample_t* b = inputFl ? drp->iBuf : drp->oBuf;
- unsigned short revents = 0;
- int err;
- cmApAudioPacket_t pkt;
- snd_pcm_uframes_t avail_frames;
-
- inputFl ? drp->iCbCnt++ : drp->oCbCnt++;
-
- pkt.devIdx = drp->devIdx;
- pkt.begChIdx = 0;
- pkt.chCnt = chCnt;
- pkt.audioFramesCnt = frmCnt;
- pkt.bitsPerSample = 32;
- pkt.flags = kInterleavedApFl | kFloatApFl;
- pkt.audioBytesPtr = b;
- pkt.userCbPtr = drp->userCbPtr;
-
- inputFl ? drp->iCbCnt++ : drp->oCbCnt++;
-
- // get the timestamp for this buffer
- if((err = snd_pcm_htimestamp(pcmH,&avail_frames,&pkt.timeStamp)) != 0 )
- {
- _cmApDevSetupError(p, err, p->pollfdsDesc[i].inputFl, drp, "Get timestamp error.");
- pkt.timeStamp.tv_sec = 0;
- pkt.timeStamp.tv_nsec = 0;
- }
-
- // Note that based on experimenting with the timestamp and the current
- // clock_gettime(CLOCK_MONOTONIC) time it appears that the time stamp
- // marks the end of the current buffer - so in fact the time stamp should
- // be backed up by the availble sample count period to get the time of the
- // first sample in the buffer
- /*
- unsigned avail_nano_secs = (unsigned)(avail_frames * (1000000000.0/drp->srate));
- if( pkt.timeStamp.tv_nsec > avail_nano_secs )
- pkt.timeStamp.tv_nsec -= avail_nano_secs;
- else
- {
- pkt.timeStamp.tv_sec -= 1;
- pkt.timeStamp.tv_nsec = 1000000000 - avail_nano_secs;
- }
- */
-
- //printf("AUDI: %ld %ld\n",pkt.timeStamp.tv_sec,pkt.timeStamp.tv_nsec);
- //cmTimeSpec_t t;
- //clock_gettime(CLOCK_PROCESS_CPUTIME_ID,&t);
- //printf("AUDI: %ld %ld\n",t.tv_sec,t.tv_nsec);
-
-
- switch( snd_pcm_state(pcmH) )
- {
- case SND_PCM_STATE_OPEN:
- case SND_PCM_STATE_SETUP:
- case SND_PCM_STATE_PREPARED:
- case SND_PCM_STATE_DRAINING:
- case SND_PCM_STATE_PAUSED:
- case SND_PCM_STATE_DISCONNECTED:
- continue;
-
- case SND_PCM_STATE_RUNNING:
- case SND_PCM_STATE_XRUN:
- case SND_PCM_STATE_SUSPENDED:
- break;
- }
-
- if(( err = snd_pcm_poll_descriptors_revents(pcmH, p->pollfds + i, 1 , &revents)) != 0 )
- {
- _cmApDevSetupError(p, err, p->pollfdsDesc[i].inputFl, drp, "Return poll events failed.");
- retFl = false;
- goto errLabel;
- }
-
- if(revents & POLLERR)
- {
- _cmApDevSetupError(p, err, p->pollfdsDesc[i].inputFl, drp, "Poll error.");
- _cmApStateRecover( pcmH, drp, inputFl );
- //goto errLabel;
- }
-
- if( inputFl && (revents & POLLIN) )
- {
- if((err = _cmApReadBuf(drp,pcmH,drp->iBuf,chCnt,frmCnt,drp->iBits,drp->oBits)) > 0 )
- {
- pkt.audioFramesCnt = err;
- drp->cbPtr(&pkt,1,NULL,0 ); // send the samples to the application
-
- }
- }
-
- if( !inputFl && (revents & POLLOUT) )
- {
-
- /*
- unsigned srate = 96;
- cmTimeSpec_t t1;
- static cmTimeSpec_t t0 = {0,0};
- clock_gettime(CLOCK_MONOTONIC,&t1);
-
- // time since the time-stamp was generated
- unsigned smp = (srate * (t1.tv_nsec - pkt.timeStamp.tv_nsec)) / 1000000;
-
- // time since the last output buffer was sent
- unsigned dsmp = (srate * (t1.tv_nsec - t0.tv_nsec)) / 1000000;
- 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);
- t0 = t1;
- */
-
- // callback to fill the buffer
- drp->cbPtr(NULL,0,&pkt,1);
-
- // note that the application may return fewer samples than were requested
- err = _cmApWriteBuf(drp, pcmH, pkt.audioFramesCnt < frmCnt ? NULL : drp->oBuf,chCnt,frmCnt,drp->oBits,drp->oSigBits);
-
- }
-
-
- }
- }
-
- }
-
- errLabel:
- return retFl;
- }
-
-
- bool _cmApDevSetup( cmApDevRecd_t *drp, unsigned srate, unsigned framesPerCycle, unsigned periodsPerBuf )
- {
- int err;
- int dir;
- unsigned i;
- bool retFl = true;
- bool inputFl = true;
- snd_pcm_uframes_t periodFrameCnt = framesPerCycle;
- snd_pcm_uframes_t bufferFrameCnt;
- unsigned bits = 0;
- int sig_bits = 0;
- cmApRoot_t* p = drp->rootPtr;
-
-
- // setup input, then output device
- for(i=0; i<2; i++,inputFl=!inputFl)
- {
- unsigned chCnt = inputFl ? drp->iChCnt : drp->oChCnt;
- snd_pcm_uframes_t actFpC = 0;
-
- // if this is the in/out pass and the in/out flag is set
- if( ((inputFl==true) && (drp->flags & kInFl)) || ((inputFl==false) && (drp->flags & kOutFl)) )
- {
- snd_pcm_t* pcmH = NULL;
-
- if( _cmApDevShutdown(p, drp, inputFl ) != kOkApRC )
- retFl = false;
-
-
- // attempt to open the sub-device
- if((err = snd_pcm_open(&pcmH,drp->nameStr, inputFl ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK, 0)) < 0 )
- retFl = _cmApDevSetupError(p,err,inputFl,drp,"Unable to open the PCM handle");
- else
- {
-
- snd_pcm_hw_params_t* hwParams;
- snd_pcm_sw_params_t* swParams;
-
- // prepare the hwParam recd
- snd_pcm_hw_params_alloca(&hwParams);
- memset(hwParams,0,snd_pcm_hw_params_sizeof());
-
-
- // load the hw parameter record
- if((err = snd_pcm_hw_params_any(pcmH,hwParams)) < 0 )
- retFl = _cmApDevSetupError(p,err,inputFl,drp,"Error obtaining hw param record");
- else
- {
- if((err = snd_pcm_hw_params_set_rate_resample(pcmH,hwParams,0)) < 0 )
- retFl = _cmApDevSetupError(p,err,inputFl, drp,"Unable to disable the ALSA sample rate converter.");
-
-
- if((err = snd_pcm_hw_params_set_channels(pcmH,hwParams,chCnt)) < 0 )
- retFl = _cmApDevSetupError(p,err,inputFl, drp,"Unable to set channel count to: %i",chCnt);
-
-
- if((err = snd_pcm_hw_params_set_rate(pcmH,hwParams,srate,0)) < 0 )
- retFl = _cmApDevSetupError(p,err,inputFl, drp, "Unable to set sample rate to: %i",srate);
-
-
- if((err = snd_pcm_hw_params_set_access(pcmH,hwParams,SND_PCM_ACCESS_RW_INTERLEAVED )) < 0 )
- retFl = _cmApDevSetupError(p,err,inputFl, drp, "Unable to set access to: RW Interleaved");
-
- // select the widest possible sample width
- if((err = snd_pcm_hw_params_set_format(pcmH,hwParams,SND_PCM_FORMAT_S32)) >= 0 )
- bits = 32;
- else
- {
- if((err = snd_pcm_hw_params_set_format(pcmH,hwParams,SND_PCM_FORMAT_S24)) >= 0 )
- bits = 24;
- else
- {
- if((err = snd_pcm_hw_params_set_format(pcmH,hwParams,SND_PCM_FORMAT_S16)) >= 0 )
- bits = 16;
- else
- retFl = _cmApDevSetupError(p,err,inputFl, drp, "Unable to set format to: S16");
- }
- }
-
- sig_bits = snd_pcm_hw_params_get_sbits(hwParams);
-
- snd_pcm_uframes_t ps_min,ps_max;
- if((err = snd_pcm_hw_params_get_period_size_min(hwParams,&ps_min,NULL)) < 0 )
- retFl = _cmApDevSetupError(p,err,inputFl, drp, "Unable to get the minimum period size.");
-
- if((err = snd_pcm_hw_params_get_period_size_max(hwParams,&ps_max,NULL)) < 0 )
- retFl = _cmApDevSetupError(p,err,inputFl, drp, "Unable to get the maximum period size.");
-
- if( periodFrameCnt < ps_min )
- periodFrameCnt = ps_min;
- else
- if( periodFrameCnt > ps_max )
- periodFrameCnt = ps_max;
-
- if((err = snd_pcm_hw_params_set_period_size_near(pcmH,hwParams,&periodFrameCnt,NULL)) < 0 )
- retFl = _cmApDevSetupError(p,err,inputFl, drp, "Unable to set period to %i.",periodFrameCnt);
-
- bufferFrameCnt = periodFrameCnt * periodsPerBuf + 1;
-
- if((err = snd_pcm_hw_params_set_buffer_size_near(pcmH,hwParams,&bufferFrameCnt)) < 0 )
- retFl = _cmApDevSetupError(p,err,inputFl, drp, "Unable to set buffer to %i.",bufferFrameCnt);
-
-
- // Note: snd_pcm_hw_params() automatically calls snd_pcm_prepare()
- if((err = snd_pcm_hw_params(pcmH,hwParams)) < 0 )
- retFl = _cmApDevSetupError(p,err,inputFl, drp, "Parameter application failed.");
-
-
- //_reportActualParams( hwParams, inputFl, dr, srate, periodFrameCnt, bufferFrameCnt );
- }
-
- // prepare the sw param recd
- snd_pcm_sw_params_alloca(&swParams);
- memset(swParams,0,snd_pcm_sw_params_sizeof());
-
- // load the sw param recd
- if((err = snd_pcm_sw_params_current(pcmH,swParams)) < 0 )
- retFl = _cmApDevSetupError(p,err,inputFl,drp,"Error obtaining sw param record.");
- else
- {
-
- if((err = snd_pcm_sw_params_set_start_threshold(pcmH,swParams, inputFl ? 0x7fffffff : periodFrameCnt)) < 0 )
- retFl = _cmApDevSetupError(p,err,inputFl,drp,"Error seting the start threshold.");
-
- // setting the stop-threshold to twice the buffer frame count is intended to stop spurious
- // XRUN states - it will also mean that there will have no direct way of knowing about a
- // in/out buffer over/under run.
- if((err = snd_pcm_sw_params_set_stop_threshold(pcmH,swParams,bufferFrameCnt*2)) < 0 )
- retFl = _cmApDevSetupError(p,err,inputFl,drp,"Error setting the stop threshold.");
-
- if((err = snd_pcm_sw_params_set_avail_min(pcmH,swParams,periodFrameCnt)) < 0 )
- retFl = _cmApDevSetupError(p,err,inputFl,drp,"Error setting the avail. min. setting.");
-
- if((err = snd_pcm_sw_params_set_tstamp_mode(pcmH,swParams,SND_PCM_TSTAMP_MMAP)) < 0 )
- retFl = _cmApDevSetupError(p,err,inputFl,drp,"Error setting the time samp mode.");
-
- if((err = snd_pcm_sw_params(pcmH,swParams)) < 0 )
- retFl = _cmApDevSetupError(p,err,inputFl,drp,"Error applying sw params.");
- }
-
- // setup the callback
- if( p->asyncFl )
- if((err = snd_async_add_pcm_handler(&drp->ahandler,pcmH,_cmApStaticAsyncHandler, drp )) < 0 )
- retFl = _cmApDevSetupError(p,err,inputFl,drp,"Error assigning callback handler.");
-
- // get the actual frames per cycle
- if((err = snd_pcm_hw_params_get_period_size(hwParams,&actFpC,&dir)) < 0 )
- retFl = _cmApDevSetupError(p,err,inputFl,drp,"Unable to get the actual period.");
-
- // store the device handle
- if( inputFl )
- {
- drp->iBits = bits;
- drp->iSigBits = sig_bits;
- drp->iPcmH = pcmH;
- drp->iBuf = cmMemResizeZ( cmApSample_t, drp->iBuf, actFpC * drp->iChCnt );
- drp->iFpC = actFpC;
- }
- else
- {
- drp->oBits = bits;
- drp->oSigBits = sig_bits;
- drp->oPcmH = pcmH;
- drp->oBuf = cmMemResizeZ( cmApSample_t, drp->oBuf, actFpC * drp->oChCnt );
- drp->oFpC = actFpC;
- }
-
- if( p->asyncFl == false )
- {
- assert( p->pollfdsCnt < p->pollfdsAllocCnt );
-
- unsigned incrFdsCnt = 0;
- unsigned fdsCnt = 0;
-
- // locate the pollfd associated with this device/direction
- unsigned j;
- for(j=0; j<p->pollfdsCnt; j+=p->pollfdsDesc[j].fdsCnt)
- if( p->pollfdsDesc[j].devPtr == drp && inputFl == p->pollfdsDesc[j].inputFl )
- break;
-
- // get the count of descriptors for this device/direction
- fdsCnt = snd_pcm_poll_descriptors_count(pcmH);
-
- // if the device was not found
- if( j == p->pollfdsCnt )
- {
- j = p->pollfdsCnt;
- incrFdsCnt = fdsCnt;
-
- // if the pollfds[] needs more memroy
- if( p->pollfdsCnt + fdsCnt > p->pollfdsAllocCnt )
- {
- p->pollfds = cmMemResizePZ(struct pollfd, p->pollfds, p->pollfdsCnt + fdsCnt );
- p->pollfdsDesc = cmMemResizePZ(cmApPollfdsDesc_t, p->pollfdsDesc, p->pollfdsCnt + fdsCnt );
- p->pollfdsAllocCnt += fdsCnt;
- }
- }
-
- // get the poll descriptors for this device/dir
- if( snd_pcm_poll_descriptors(pcmH,p->pollfds + j,fdsCnt) != 1 )
- retFl = _cmApDevSetupError(p,0,inputFl,drp,"Poll descriptor assignment failed.");
- else
- {
- // store the desc. record assicoated with the poll descriptor
- p->pollfdsDesc[ j ].fdsCnt = fdsCnt;
- p->pollfdsDesc[ j ].devPtr = drp;
- p->pollfdsDesc[ j ].inputFl = inputFl;
- }
-
- p->pollfdsCnt += incrFdsCnt;
- }
- 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);
-
- }
- //_dumpAlsaDevice(pcmH);
-
- } // end if
-
- } // end for
-
- return retFl;
- }
-
- #ifdef NOTDEF
- #define TRY(e) while(e<0){ printf("LINE:%i ALSA ERROR:%s\n",__LINE__,snd_strerror(e)); exit(EXIT_FAILURE); }
-
- void open_device( const char* device_name, bool inputFl )
- {
- snd_pcm_t *pcm_handle = NULL;
- snd_pcm_hw_params_t *hw_params;
-
-
- snd_pcm_uframes_t bs_min,bs_max,ps_min,ps_max;
- unsigned rt_min,rt_max,ch_min,ch_max;
- const char* ioLabel = inputFl ? "in" : "out";
-
-
- // Open the device
- TRY( snd_pcm_open (&pcm_handle, device_name, inputFl ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK, 0));
-
- TRY( snd_pcm_hw_params_malloc (&hw_params) );
- TRY( snd_pcm_hw_params_any (pcm_handle, hw_params));
-
- TRY( snd_pcm_hw_params_test_format(pcm_handle, hw_params, SND_PCM_FORMAT_S16_LE));
-
- // get the sample rate range
- TRY(snd_pcm_hw_params_get_rate_min(hw_params,&rt_min,NULL));
- TRY(snd_pcm_hw_params_get_rate_max(hw_params,&rt_max,NULL));
- TRY(snd_pcm_hw_params_get_channels_min(hw_params,&ch_min));
- TRY(snd_pcm_hw_params_get_channels_max(hw_params,&ch_max));
-
- // set the basic device format - setting the format may influence the size of the possible
- // buffer and period size
- //TRY( snd_pcm_hw_params_set_access (pcm_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED));
- //TRY( snd_pcm_hw_params_set_format (pcm_handle, hw_params, SND_PCM_FORMAT_S16_LE));
- //TRY( snd_pcm_hw_params_set_rate_near(pcm_handle, hw_params, &srate, NULL));
- //TRY( snd_pcm_hw_params_set_channels(pcm_handle, hw_params, ch_cnt));
-
-
- // get the range of possible buffer and period sizes
- TRY(snd_pcm_hw_params_get_buffer_size_min(hw_params,&bs_min));
- TRY(snd_pcm_hw_params_get_buffer_size_max(hw_params,&bs_max));
- TRY(snd_pcm_hw_params_get_period_size_min(hw_params,&ps_min,NULL));
- TRY(snd_pcm_hw_params_get_period_size_max(hw_params,&ps_max,NULL));
-
- //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);
- 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);
-
- snd_pcm_hw_params_free(hw_params);
- snd_pcm_close(pcm_handle);
- }
- #endif
-
- cmApRC_t cmApAlsaInitialize( cmRpt_t* rpt, unsigned baseApDevIdx )
- {
- cmApRC_t rc = kOkApRC;
- int err;
- int cardNum = -1;
-
- if((rc = cmApAlsaFinalize()) != kOkApRC )
- return rc;
-
- recdSetup();
-
- cmApRoot_t* p = &_cmApRoot;
- memset(p,0,sizeof(cmApRoot_t));
- p->rpt = rpt;
- p->asyncFl = false;
-
- // for each sound card
- while(1)
- {
- snd_ctl_t* cardH;
- char* cardNamePtr = NULL;
- char* cardLongNamePtr = NULL;
- int devNum = -1;
- int devStrN = 31;
- char devStr[devStrN+1];
-
-
- // get the next card handle
- if((err = snd_card_next(&cardNum)) < 0 )
- {
- _cmApOsError(p,err,"Error getting sound card handle");
- return kSysErrApRC;
- }
-
- // if no more card's to get
- if( cardNum < 0 )
- break;
-
- // get the card short name
- if(((err = snd_card_get_name(cardNum,&cardNamePtr)) < 0) || (cardNamePtr == NULL))
- {
- _cmApOsError(p,err,"Unable to get card name for card number %i\n",cardNum);
- goto releaseCard;
- }
-
- // get the card long name
- if((err = snd_card_get_longname(cardNum,&cardLongNamePtr)) < 0 || cardLongNamePtr == NULL )
- {
- _cmApOsError(p,err,"Unable to get long card name for card number %i\n",cardNum);
- goto releaseCard;
- }
-
- // form the device name for this card
- if(snprintf(devStr,devStrN,"hw:%i",cardNum) > devStrN )
- {
- _cmApOsError(p,0,"Device name is too long for buffer.");
- goto releaseCard;
- }
-
- // open the card device driver
- if((err = snd_ctl_open(&cardH, devStr, 0)) < 0 )
- {
- _cmApOsError(p,err,"Error opening sound card %i.",cardNum);
- goto releaseCard;
- }
-
-
- // for each device on this card
- while(1)
- {
- snd_pcm_info_t* info;
- int subDevCnt = 1;
- int i,j;
-
- // get the next device on this card
- if((err = snd_ctl_pcm_next_device(cardH,&devNum)) < 0 )
- {
- _cmApOsError(p,err,"Error gettign next device on card %i",cardNum);
- break;
- }
-
- // if no more devices to get
- if( devNum < 0 )
- break;
-
- // allocate a pcmInfo record
- snd_pcm_info_alloca(&info);
- memset(info, 0, snd_pcm_info_sizeof());
-
- // set the device to query
- snd_pcm_info_set_device(info, devNum );
-
- for(i=0; i<subDevCnt; i++)
- {
- cmApDevRecd_t dr;
- bool inputFl = false;
-
- memset(&dr,0,sizeof(dr));
-
- for(j=0; j<2; j++,inputFl=!inputFl)
- {
- snd_pcm_t* pcmH = NULL;
-
- dr.devIdx = -1;
-
- // set the subdevice and I/O direction to query
- snd_pcm_info_set_subdevice(info,i);
- snd_pcm_info_set_stream(info,inputFl ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK);
-
- // if this device does not use this sub-device
- if((err = snd_ctl_pcm_info(cardH,info)) < 0 )
- continue;
-
- // get the count of subdevices this device uses
- if(i == 0 )
- subDevCnt = snd_pcm_info_get_subdevices_count(info);
-
- // if this device has no sub-devices
- if(subDevCnt == 0 )
- continue;
-
- // form the device name and desc. string
- if(strlen(dr.nameStr) == 0)
- snprintf(dr.nameStr,NAME_CHAR_CNT,"hw:%i,%i,%i",cardNum,devNum,i);
-
-
- if(strlen(dr.descStr) == 0)
- {
- snprintf(dr.descStr,DESC_CHAR_CNT,"%s %s",cardNamePtr,snd_pcm_info_get_name(info));
- //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));
- }
-
- // attempt to open the sub-device
- if((err = _cmApDevOpen(&pcmH,dr.nameStr,inputFl)) < 0 )
- _cmApDevSetupError(p,err,inputFl,&dr,"Unable to open the PCM handle");
- else
- {
- snd_pcm_hw_params_t* hwParams;
-
- // allocate the parameter record
- snd_pcm_hw_params_alloca(&hwParams);
- memset( hwParams,0,snd_pcm_hw_params_sizeof());
-
-
- // load the parameter record
- if((err = snd_pcm_hw_params_any(pcmH,hwParams)) < 0 )
- _cmApDevSetupError(p,err,inputFl,&dr,"Error obtaining hw param record");
- else
- {
- unsigned* chCntPtr = inputFl ? &dr.iChCnt : &dr.oChCnt;
- unsigned rate;
-
- snd_pcm_hw_params_get_rate_max(hwParams,&rate,NULL);
-
-
- // extract the channel count
- if((err = snd_pcm_hw_params_get_channels_max(hwParams, chCntPtr )) < 0 )
- _cmApDevSetupError(p,err,inputFl,&dr,"Error getting channel count.");
- else
- // this device uses this subdevice in the current direction
- dr.flags += inputFl ? kInFl : kOutFl;
-
- printf("%s in:%i chs:%i rate:%i\n",dr.nameStr,inputFl,*chCntPtr,rate);
-
- }
-
- // close the sub-device
- snd_pcm_close(pcmH);
-
- }
-
- } // in/out loop
-
- // insert the device in the device array
- if( dr.flags != 0 )
- _cmApDevAppend(p,&dr);
-
- } // sub-dev loop
-
- } // device loop
-
- releaseCard:
- snd_ctl_close(cardH);
-
- } // card loop
-
- if( rc == kOkApRC && p->asyncFl==false )
- {
- p->pollfdsCnt = 0;
- p->pollfdsAllocCnt = 2*p->devCnt;
- p->pollfds = cmMemAllocZ(struct pollfd, p->pollfdsAllocCnt );
- p->pollfdsDesc = cmMemAllocZ(cmApPollfdsDesc_t, p->pollfdsAllocCnt );
-
- if( cmThreadCreate(&p->thH,_cmApThreadFunc,p,rpt) != kOkThRC )
- {
- _cmApOsError(p,0,"Thread create failed.");
- rc = kThreadFailApRC;
- }
- }
-
- return rc;
- }
-
- cmApRC_t cmApAlsaFinalize()
- {
- cmApRoot_t* p = &_cmApRoot;
- int i;
- cmApRC_t rc = kOkApRC;
-
- if( p->asyncFl==false && cmThreadIsValid(p->thH) )
- if( cmThreadDestroy(&p->thH) != kOkThRC )
- {
- _cmApOsError(p,0,"Thread destroy failed.");
- rc = kThreadFailApRC;
- }
-
- for(i=0; i<p->devCnt; ++i)
- {
- _cmApDevShutdown(p,p->devArray+i,true);
- _cmApDevShutdown(p,p->devArray+i,false);
- cmMemPtrFree(&p->devArray[i].iBuf);
- cmMemPtrFree(&p->devArray[i].oBuf);
- }
-
- cmMemPtrFree(&p->pollfds);
- cmMemPtrFree(&p->pollfdsDesc);
-
- cmMemPtrFree(&p->devArray);
- p->devAllocCnt = 0;
- p->devCnt = 0;
-
- recdFree();
- //write_rec(2);
-
- return rc;
- }
-
- cmApRC_t cmApAlsaDeviceCount()
- { return _cmApRoot.devCnt; }
-
- const char* cmApAlsaDeviceLabel( unsigned devIdx )
- {
- assert(devIdx < cmApAlsaDeviceCount());
- return _cmApRoot.devArray[devIdx].descStr;
- }
-
- unsigned cmApAlsaDeviceChannelCount( unsigned devIdx, bool inputFl )
- {
- assert(devIdx < cmApAlsaDeviceCount());
- return inputFl ? _cmApRoot.devArray[devIdx].iChCnt : _cmApRoot.devArray[devIdx].oChCnt;
- }
-
- double cmApAlsaDeviceSampleRate( unsigned devIdx )
- {
- assert(devIdx < cmApAlsaDeviceCount());
- return (double)_cmApRoot.devArray[devIdx].srate;
- }
-
- unsigned cmApAlsaDeviceFramesPerCycle( unsigned devIdx, bool inputFl )
- {
- assert(devIdx < cmApAlsaDeviceCount());
- return _cmApRoot.devArray[devIdx].framesPerCycle;
- }
-
- cmApRC_t cmApAlsaDeviceSetup(
- unsigned devIdx,
- double srate,
- unsigned framesPerCycle,
- cmApCallbackPtr_t callbackPtr,
- void* userCbPtr )
- {
- assert( devIdx < cmApAlsaDeviceCount());
- cmApRoot_t* p = &_cmApRoot;
- cmApDevRecd_t* drp = _cmApRoot.devArray + devIdx;
- unsigned periodsPerBuf = kDfltPeriodsPerBuf;
-
- if( p->asyncFl == false )
- if( cmThreadPause(p->thH,kWaitThFl | kPauseThFl) != kOkThRC )
- {
- _cmApOsError(p,0,"Audio thread pause failed.");
- return kThreadFailApRC;
- }
-
- if( _cmApDevSetup(drp, srate, framesPerCycle, periodsPerBuf ) )
- {
- drp->srate = srate;
- drp->framesPerCycle = framesPerCycle;
- drp->periodsPerBuf = periodsPerBuf;
- drp->cbPtr = callbackPtr;
- drp->userCbPtr = userCbPtr;
-
- return kOkApRC;
- }
-
- return kSysErrApRC;
- }
-
-
- cmApRC_t cmApAlsaDeviceStart( unsigned devIdx )
- {
- assert( devIdx < cmApAlsaDeviceCount());
- int err;
- cmApRoot_t* p = &_cmApRoot;
- cmApDevRecd_t* drp = p->devArray + devIdx;
- bool retFl = true;
- bool inputFl = true;
- unsigned i;
-
- for(i=0; i<2; ++i,inputFl=!inputFl)
- {
- snd_pcm_t* pcmH = inputFl ? drp->iPcmH : drp->oPcmH;
-
- if( pcmH != NULL )
- {
-
- snd_pcm_state_t state = snd_pcm_state(pcmH);
-
- if( state != SND_PCM_STATE_RUNNING )
- {
- unsigned chCnt = inputFl ? drp->iChCnt : drp->oChCnt;
- unsigned frmCnt = inputFl ? drp->iFpC : drp->oFpC;
- const char* ioLabel = inputFl ? "Input" : "Output";
-
- //printf("%i %s state:%s %i %i\n",drp->devIdx, ioLabel,_pcmStateToString(snd_pcm_state(pcmH)),chCnt,frmCnt);
-
- // preparing may not always be necessary because the earlier call to snd_pcm_hw_params()
- // may have left the device prepared - the redundant call however doesn't seem to hurt
- if((err= snd_pcm_prepare(pcmH)) < 0 )
- retFl = _cmApDevSetupError(p,err,inputFl,drp,"Error preparing the %i device.",ioLabel);
- else
- {
-
- recdStart(drp,inputFl);
-
- if( inputFl == false )
- {
- int j;
- for(j=0; j<1; ++j)
- if((err = _cmApWriteBuf( drp, pcmH, NULL, chCnt, frmCnt, drp->oBits, drp->oSigBits )) < 0 )
- {
- retFl = _cmApDevSetupError(p,err,inputFl,drp,"Write before start failed.");
- break;
- }
- }
- else
- {
-
- if((err = snd_pcm_start(pcmH)) < 0 )
- retFl = _cmApDevSetupError(p,err,inputFl,drp,"'%s' start failed.",ioLabel);
- }
-
- // wait 500 microseconds between starting and stopping - this prevents
- // input and output and other device callbacks from landing on top of
- // each other - when this happens callbacks are dropped.
- if( p->asyncFl )
- usleep(500);
-
- }
- //printf("%i %s state:%s %i %i\n",drp->devIdx, ioLabel,_cmApPcmStateToString(snd_pcm_state(pcmH)),chCnt,frmCnt);
-
- }
-
- }
- }
-
- if( p->asyncFl == false )
- if( cmThreadPause(p->thH,0) != kOkThRC )
- {
- _cmApOsError(p,0,"Audio thread start failed.");
- retFl = false;
- }
-
- return retFl ? kOkApRC : kSysErrApRC;
- }
-
- cmApRC_t cmApAlsaDeviceStop( unsigned devIdx )
- {
- int err;
- bool retFl = true;
- cmApRoot_t* p = &_cmApRoot;
- cmApDevRecd_t* drp = p->devArray + devIdx;
-
- if( drp->iPcmH != NULL )
- if((err = snd_pcm_drop(drp->iPcmH)) < 0 )
- retFl = _cmApDevSetupError(p,err,true,drp,"Input stop failed.");
-
- if( drp->oPcmH != NULL )
- if((err = snd_pcm_drop(drp->oPcmH)) < 0 )
- retFl = _cmApDevSetupError(p,err,false,drp,"Output stop failed.");
-
- if( p->asyncFl == false )
- if( cmThreadPause(p->thH,kPauseThFl) != kOkThRC )
- {
- _cmApOsError(p,0,"Audio thread pause failed.");
- retFl = false;
- }
-
- return retFl ? kOkApRC : kSysErrApRC;
- }
-
- bool cmApAlsaDeviceIsStarted( unsigned devIdx )
- {
- assert( devIdx < cmApAlsaDeviceCount());
-
- bool iFl = false;
- bool oFl = false;
- const cmApDevRecd_t* drp = _cmApRoot.devArray + devIdx;
-
- if( drp->iPcmH != NULL )
- iFl = snd_pcm_state(drp->iPcmH) == SND_PCM_STATE_RUNNING;
-
- if( drp->oPcmH != NULL )
- oFl = snd_pcm_state(drp->oPcmH) == SND_PCM_STATE_RUNNING;
-
- return iFl || oFl;
- }
-
- //{ { label:alsaDevRpt }
- //(
- // Here's an example of generating a report of available
- // ALSA devices.
- //)
-
- //[
- void cmApAlsaDeviceReport( cmRpt_t* rpt )
- {
- unsigned i;
- for(i=0; i<_cmApRoot.devCnt; i++)
- {
- cmRptPrintf(rpt,"%i : ",i);
- _cmApDevReport(rpt,_cmApRoot.devArray + i );
- }
- }
- //]
- //}
-
- void cmApAlsaDeviceRtReport( cmRpt_t* rpt, unsigned devIdx )
- {
- _cmApDevRtReport(rpt, _cmApRoot.devArray + devIdx );
-
- }
|