#include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" #include "cwMem.h" #include "cwTime.h" #include "cwTextBuf.h" #include "cwAudioDevice.h" #include "cwAudioBuf.h" #include "cwAudioDeviceAlsa.h" #include "cwAudioDeviceTest.h" namespace cw { namespace audio { namespace device { /// [cmAudioPortExample] // See test() below for the main point of entry. // Data structure used to hold the parameters for cpApPortTest() // and the user defined data record passed to the host from the // audio port callback functions. typedef struct { unsigned bufCnt; // 2=double buffering 3=triple buffering unsigned chIdx; // first test channel //unsigned chCnt; // count of channels to test unsigned framesPerCycle; // DSP frames per cycle unsigned bufFrmCnt; // count of DSP frames used by the audio buffer (bufCnt * framesPerCycle) unsigned bufSmpCnt; // count of samples used by the audio buffer (chCnt * bufFrmCnt) unsigned inDevIdx; // input device index unsigned outDevIdx; // output device index double srate; // audio sample rate unsigned meterMs; // audio meter buffer length unsigned iCbCnt; // count the callback unsigned oCbCnt; buf::handle_t audioBufH; } cmApPortTestRecd; // print the usage message for cmAudioPortTest.c void _cmApPrintUsage() { char msg[] = "cmApPortTest() command switches\n" "-r <srate> -c <chcnt> -b <bufcnt> -f <frmcnt> -i <idevidx> -o <odevidx> -t -p -h \n" "\n" "-r <srate> = sample rate\n" "-a <chidx> = first channel\n" "-c <chcnt> = audio channels\n" "-b <bufcnt> = count of buffers\n" "-f <frmcnt> = count of samples per buffer\n" "-i <idevidx> = input device index\n" "-o <odevidx> = output device index\n" "-p = print report but do not start audio devices\n" "-h = print this usage message\n"; cwLogInfo(msg); } // Get a command line option. Note that if 'boolFl' is set to 'true' then the function simply // returns '1'. This is used to handle arguments whose presense indicates a positive boolean // flag. For example -h (help) indicates that the usage data should be printed - it needs no other argument. int _cmApGetOpt( int argc, const char* argv[], const char* label, int defaultVal, bool boolFl=false ) { int i = 0; for(; i<argc; ++i) if( strcmp(label,argv[i]) == 0 ) { if(boolFl) return 1; if( i == (argc-1) ) return defaultVal; return atoi(argv[i+1]); } return defaultVal; } unsigned _cmGlobalInDevIdx = 0; unsigned _cmGlobalOutDevIdx = 0; void _cmApPortCb2( void* arg, audioPacket_t* inPktArray, unsigned inPktCnt, audioPacket_t* outPktArray, unsigned outPktCnt ) { cmApPortTestRecd* p = static_cast<cmApPortTestRecd*>(arg); for(unsigned i=0; i<inPktCnt; ++i) static_cast<cmApPortTestRecd*>(inPktArray[i].cbArg)->iCbCnt++; for(unsigned i=0; i<outPktCnt; ++i) static_cast<cmApPortTestRecd*>(outPktArray[i].cbArg)->oCbCnt++; buf::inputToOutput( p->audioBufH, _cmGlobalInDevIdx, _cmGlobalOutDevIdx ); buf::update( p->audioBufH, inPktArray, inPktCnt, outPktArray, outPktCnt ); } } } } // Audio Port testing function cw::rc_t cw::audio::device::test( int argc, const char** argv ) { cmApPortTestRecd r; unsigned i; rc_t rc; driver_t* drv = nullptr; handle_t h; alsa::handle_t alsaH; bool runFl = true; if( _cmApGetOpt(argc,argv,"-h",0,true) ) _cmApPrintUsage(); runFl = _cmApGetOpt(argc,argv,"-p",0,true)?false:true; r.srate = _cmApGetOpt(argc,argv,"-r",44100); //r.chIdx = _cmApGetOpt(argc,argv,"-a",0); //r.chCnt = _cmApGetOpt(argc,argv,"-c",2); r.bufCnt = _cmApGetOpt(argc,argv,"-b",3); r.framesPerCycle = _cmApGetOpt(argc,argv,"-f",512); //r.bufFrmCnt = (r.bufCnt*r.framesPerCycle); //r.bufSmpCnt = (r.chCnt * r.bufFrmCnt); //r.logCnt = 100; r.meterMs = 50; r.inDevIdx = _cmGlobalInDevIdx = _cmApGetOpt(argc,argv,"-i",0); r.outDevIdx = _cmGlobalOutDevIdx = _cmApGetOpt(argc,argv,"-o",0); r.iCbCnt = 0; r.oCbCnt = 0; //cwLogInfo("Program cfg: %s in:%i out:%i chidx:%i chs:%i bufs=%i frm=%i rate=%f",runFl?"exec":"rpt",r.inDevIdx,r.outDevIdx,r.chIdx,r.chCnt,r.bufCnt,r.framesPerCycle,r.srate); // initialize the audio device interface if((rc = create(h)) != kOkRC ) { cwLogInfo("Initialize failed."); goto errLabel; } // initialize the ALSA device driver interface if((rc = alsa::create(alsaH, drv )) != kOkRC ) { cwLogInfo("ALSA initialize failed."); goto errLabel; } // register the ALSA device driver with the audio interface if((rc = registerDriver( h, drv )) != kOkRC ) { cwLogInfo("ALSA driver registration failed."); goto errLabel; } // report the current audio device configuration for(i=0; i<deviceCount(h); ++i) { cwLogInfo("%i [in: chs=%i frames=%i] [out: chs=%i frames=%i] srate:%8.1f %s",i,deviceChannelCount(h,i,true),deviceFramesPerCycle(h,i,true),deviceChannelCount(h,i,false),deviceFramesPerCycle(h,i,false),deviceSampleRate(h,i),deviceLabel(h,i)); } // report the current audio devices using the audio port interface function //report(h); if( runFl ) { // initialize the audio bufer buf::create( r.audioBufH, deviceCount(h), r.meterMs ); // setup the buffer for the output device buf::setup( r.audioBufH, r.outDevIdx, r.srate, r.framesPerCycle, r.bufCnt, deviceChannelCount(h,r.outDevIdx,true), r.framesPerCycle, deviceChannelCount(h,r.outDevIdx,false), r.framesPerCycle ); // setup the buffer for the input device //if( r.inDevIdx != r.outDevIdx ) buf::setup( r.audioBufH, r.inDevIdx, r.srate, r.framesPerCycle, r.bufCnt, deviceChannelCount(h,r.inDevIdx,true), r.framesPerCycle, deviceChannelCount(h,r.inDevIdx,false), r.framesPerCycle ); // setup an output device if(deviceSetup(h, r.outDevIdx,r.srate,r.framesPerCycle,_cmApPortCb2,&r) != kOkRC ) cwLogInfo("Out device setup failed."); else // setup an input device if( deviceSetup(h, r.inDevIdx,r.srate,r.framesPerCycle,_cmApPortCb2,&r) != kOkRC ) cwLogInfo("In device setup failed."); else // start the input device if( deviceStart(h, r.inDevIdx) != kOkRC ) cwLogInfo("In device start failed."); else // start the output device if( deviceStart(h, r.outDevIdx) != kOkRC ) cwLogInfo("Out Device start failed."); else cwLogInfo("Setup complete!"); cwLogInfo("q=quit O/o output tone, I/i input tone, P/p pass M/m meter s=buf report"); // turn on the meters buf::enableMeter(r.audioBufH, r.outDevIdx,-1,buf::kOutFl | buf::kEnableFl); char c; while((c=getchar()) != 'q') { deviceRealTimeReport(h, r.outDevIdx ); switch(c) { case 'i': case 'I': buf::enableTone(r.audioBufH, r.inDevIdx,-1,buf::kInFl | (c=='I'?buf::kEnableFl:0)); break; case 'o': case 'O': buf::enableTone(r.audioBufH, r.outDevIdx,-1,buf::kOutFl | (c=='O'?buf::kEnableFl:0)); break; case 'p': case 'P': buf::enablePass(r.audioBufH, r.outDevIdx,-1,buf::kOutFl | (c=='P'?buf::kEnableFl:0)); break; case 'M': case 'm': buf::enableMeter( r.audioBufH, r.inDevIdx, -1, buf::kInFl | (c=='M'?buf::kEnableFl:0)); buf::enableMeter( r.audioBufH, r.outDevIdx, -1, buf::kOutFl | (c=='M'?buf::kEnableFl:0)); break; case 's': buf::report(r.audioBufH); break; } } // stop the input device if( deviceIsStarted(h,r.inDevIdx) ) if( deviceStop(h,r.inDevIdx) != kOkRC ) cwLogInfo("In device stop failed."); // stop the output device if( deviceIsStarted(h,r.outDevIdx) ) if( deviceStop(h,r.outDevIdx) != kOkRC ) cwLogInfo("Out device stop failed."); } errLabel: // release the ALSA driver rc_t rc0 = alsa::destroy(alsaH); // release any resources held by the audio port interface rc_t rc1 = destroy(h); rc_t rc2 = buf::destroy(r.audioBufH); //cmApNrtFree(); //cmApFileFree(); // report the count of audio buffer callbacks cwLogInfo("cb count: i:%i o:%i", r.iCbCnt, r.oCbCnt ); return rcSelect(rc,rc0,rc1,rc2); } /// [cmAudioPortExample] cw::rc_t cw::audio::device::report() { const char* argv[] = { "-p" }; return test(0,argv); }