2019-12-24 15:05:24 +00:00
|
|
|
#include "cwCommon.h"
|
|
|
|
#include "cwLog.h"
|
|
|
|
#include "cwCommonImpl.h"
|
|
|
|
#include "cwMem.h"
|
|
|
|
#include "cwTime.h"
|
2019-12-26 02:44:14 +00:00
|
|
|
#include "cwTextBuf.h"
|
|
|
|
#include "cwAudioDevice.h"
|
2019-12-24 15:05:24 +00:00
|
|
|
#include "cwAudioBuf.h"
|
2019-12-26 02:44:14 +00:00
|
|
|
#include "cwAudioDeviceAlsa.h"
|
|
|
|
#include "cwAudioDeviceTest.h"
|
2019-12-24 15:05:24 +00:00
|
|
|
|
|
|
|
namespace cw
|
|
|
|
{
|
|
|
|
namespace audio
|
|
|
|
{
|
|
|
|
namespace device
|
|
|
|
{
|
|
|
|
/// [cmAudioPortExample]
|
|
|
|
|
2019-12-26 02:44:14 +00:00
|
|
|
// See test() below for the main point of entry.
|
2019-12-24 15:05:24 +00:00
|
|
|
|
|
|
|
// 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 framesPerCycle; // DSP frames per cycle
|
|
|
|
unsigned inDevIdx; // input device index
|
|
|
|
unsigned outDevIdx; // output device index
|
|
|
|
double srate; // audio sample rate
|
|
|
|
unsigned meterMs; // audio meter buffer length
|
2019-12-28 02:51:28 +00:00
|
|
|
|
2019-12-27 21:52:45 +00:00
|
|
|
unsigned iCbCnt; // count the callback
|
|
|
|
unsigned oCbCnt;
|
2019-12-24 15:05:24 +00:00
|
|
|
|
2019-12-28 02:51:28 +00:00
|
|
|
buf::handle_t audioBufH;
|
|
|
|
} cmApPortTestRecd;
|
2019-12-24 15:05:24 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 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"
|
|
|
|
"-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);
|
|
|
|
}
|
|
|
|
|
2019-12-26 02:44:14 +00:00
|
|
|
// 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 )
|
2019-12-24 15:05:24 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
|
2019-12-28 02:51:28 +00:00
|
|
|
void _cmApPortCb2( void* arg, audioPacket_t* inPktArray, unsigned inPktCnt, audioPacket_t* outPktArray, unsigned outPktCnt )
|
2019-12-24 15:05:24 +00:00
|
|
|
{
|
2019-12-28 02:51:28 +00:00
|
|
|
cmApPortTestRecd* p = static_cast<cmApPortTestRecd*>(arg);
|
|
|
|
|
2019-12-26 02:44:14 +00:00
|
|
|
for(unsigned i=0; i<inPktCnt; ++i)
|
2019-12-27 21:52:45 +00:00
|
|
|
static_cast<cmApPortTestRecd*>(inPktArray[i].cbArg)->iCbCnt++;
|
2019-12-26 02:44:14 +00:00
|
|
|
|
|
|
|
for(unsigned i=0; i<outPktCnt; ++i)
|
2019-12-27 21:52:45 +00:00
|
|
|
static_cast<cmApPortTestRecd*>(outPktArray[i].cbArg)->oCbCnt++;
|
2019-12-26 02:44:14 +00:00
|
|
|
|
2019-12-28 02:51:28 +00:00
|
|
|
buf::inputToOutput( p->audioBufH, _cmGlobalInDevIdx, _cmGlobalOutDevIdx );
|
2019-12-24 15:05:24 +00:00
|
|
|
|
2019-12-28 02:51:28 +00:00
|
|
|
buf::update( p->audioBufH, inPktArray, inPktCnt, outPktArray, outPktCnt );
|
2019-12-24 15:05:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Audio Port testing function
|
2019-12-26 02:44:14 +00:00
|
|
|
cw::rc_t cw::audio::device::test( int argc, const char** argv )
|
2019-12-24 15:05:24 +00:00
|
|
|
{
|
|
|
|
cmApPortTestRecd r;
|
|
|
|
unsigned i;
|
2019-12-26 02:44:14 +00:00
|
|
|
rc_t rc;
|
|
|
|
driver_t* drv = nullptr;
|
|
|
|
handle_t h;
|
|
|
|
alsa::handle_t alsaH;
|
|
|
|
bool runFl = true;
|
2019-12-24 15:05:24 +00:00
|
|
|
|
|
|
|
if( _cmApGetOpt(argc,argv,"-h",0,true) )
|
|
|
|
_cmApPrintUsage();
|
|
|
|
|
2020-03-23 14:41:28 +00:00
|
|
|
runFl = _cmApGetOpt(argc,argv,"-p",0,true) ? false : true;
|
2019-12-26 02:44:14 +00:00
|
|
|
r.srate = _cmApGetOpt(argc,argv,"-r",44100);
|
|
|
|
r.bufCnt = _cmApGetOpt(argc,argv,"-b",3);
|
|
|
|
r.framesPerCycle = _cmApGetOpt(argc,argv,"-f",512);
|
2019-12-24 15:05:24 +00:00
|
|
|
r.meterMs = 50;
|
|
|
|
|
2019-12-26 02:44:14 +00:00
|
|
|
r.inDevIdx = _cmGlobalInDevIdx = _cmApGetOpt(argc,argv,"-i",0);
|
2019-12-27 21:52:45 +00:00
|
|
|
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);
|
2019-12-24 15:05:24 +00:00
|
|
|
|
2019-12-26 02:44:14 +00:00
|
|
|
|
|
|
|
// initialize the audio device interface
|
|
|
|
if((rc = create(h)) != kOkRC )
|
2019-12-24 15:05:24 +00:00
|
|
|
{
|
2019-12-26 02:44:14 +00:00
|
|
|
cwLogInfo("Initialize failed.");
|
2019-12-24 15:05:24 +00:00
|
|
|
goto errLabel;
|
|
|
|
}
|
|
|
|
|
2019-12-26 02:44:14 +00:00
|
|
|
// initialize the ALSA device driver interface
|
|
|
|
if((rc = alsa::create(alsaH, drv )) != kOkRC )
|
2019-12-24 15:05:24 +00:00
|
|
|
{
|
2019-12-26 02:44:14 +00:00
|
|
|
cwLogInfo("ALSA initialize failed.");
|
2019-12-24 15:05:24 +00:00
|
|
|
goto errLabel;
|
|
|
|
}
|
2019-12-26 02:44:14 +00:00
|
|
|
|
|
|
|
// register the ALSA device driver with the audio interface
|
|
|
|
if((rc = registerDriver( h, drv )) != kOkRC )
|
2019-12-24 15:05:24 +00:00
|
|
|
{
|
2019-12-26 02:44:14 +00:00
|
|
|
cwLogInfo("ALSA driver registration failed.");
|
2019-12-24 15:05:24 +00:00
|
|
|
goto errLabel;
|
|
|
|
}
|
|
|
|
|
|
|
|
// report the current audio device configuration
|
2020-03-23 14:41:28 +00:00
|
|
|
for(i=0; i<device::count(h); ++i)
|
2019-12-24 15:05:24 +00:00
|
|
|
{
|
2020-03-23 14:41:28 +00:00
|
|
|
cwLogInfo("%i [in: chs=%i frames=%i] [out: chs=%i frames=%i] srate:%8.1f %s",i,device::channelCount(h,i,true),framesPerCycle(h,i,true),channelCount(h,i,false),framesPerCycle(h,i,false),sampleRate(h,i),label(h,i));
|
2019-12-24 15:05:24 +00:00
|
|
|
}
|
2019-12-26 02:44:14 +00:00
|
|
|
|
2019-12-24 15:05:24 +00:00
|
|
|
// report the current audio devices using the audio port interface function
|
2019-12-27 21:52:45 +00:00
|
|
|
//report(h);
|
2019-12-24 15:05:24 +00:00
|
|
|
|
|
|
|
if( runFl )
|
|
|
|
{
|
|
|
|
// initialize the audio bufer
|
2020-03-23 14:41:28 +00:00
|
|
|
buf::create( r.audioBufH, device::count(h), r.meterMs );
|
2019-12-24 15:05:24 +00:00
|
|
|
|
|
|
|
// setup the buffer for the output device
|
2020-03-23 14:41:28 +00:00
|
|
|
buf::setup( r.audioBufH, r.outDevIdx, r.srate, r.framesPerCycle, r.bufCnt, channelCount(h,r.outDevIdx,true), r.framesPerCycle, channelCount(h,r.outDevIdx,false), r.framesPerCycle );
|
2019-12-24 15:05:24 +00:00
|
|
|
|
|
|
|
// setup the buffer for the input device
|
2019-12-27 21:52:45 +00:00
|
|
|
//if( r.inDevIdx != r.outDevIdx )
|
2020-03-23 14:41:28 +00:00
|
|
|
buf::setup( r.audioBufH, r.inDevIdx, r.srate, r.framesPerCycle, r.bufCnt, channelCount(h,r.inDevIdx,true), r.framesPerCycle, channelCount(h,r.inDevIdx,false), r.framesPerCycle );
|
2019-12-24 15:05:24 +00:00
|
|
|
|
|
|
|
// setup an output device
|
2020-03-23 14:41:28 +00:00
|
|
|
if(setup(h, r.outDevIdx,r.srate,r.framesPerCycle,_cmApPortCb2,&r) != kOkRC )
|
2019-12-24 15:05:24 +00:00
|
|
|
cwLogInfo("Out device setup failed.");
|
|
|
|
else
|
|
|
|
// setup an input device
|
2020-03-23 14:41:28 +00:00
|
|
|
if( setup(h, r.inDevIdx,r.srate,r.framesPerCycle,_cmApPortCb2,&r) != kOkRC )
|
2019-12-24 15:05:24 +00:00
|
|
|
cwLogInfo("In device setup failed.");
|
|
|
|
else
|
|
|
|
// start the input device
|
2020-03-23 14:41:28 +00:00
|
|
|
if( start(h, r.inDevIdx) != kOkRC )
|
2019-12-24 15:05:24 +00:00
|
|
|
cwLogInfo("In device start failed.");
|
|
|
|
else
|
|
|
|
// start the output device
|
2020-03-23 14:41:28 +00:00
|
|
|
if( start(h, r.outDevIdx) != kOkRC )
|
2019-12-24 15:05:24 +00:00
|
|
|
cwLogInfo("Out Device start failed.");
|
2019-12-26 02:44:14 +00:00
|
|
|
else
|
|
|
|
cwLogInfo("Setup complete!");
|
2019-12-24 15:05:24 +00:00
|
|
|
|
2019-12-26 02:44:14 +00:00
|
|
|
|
2019-12-27 21:52:45 +00:00
|
|
|
cwLogInfo("q=quit O/o output tone, I/i input tone, P/p pass M/m meter s=buf report");
|
|
|
|
|
2019-12-28 02:51:28 +00:00
|
|
|
// turn on the meters
|
|
|
|
buf::enableMeter(r.audioBufH, r.outDevIdx,-1,buf::kOutFl | buf::kEnableFl);
|
2019-12-27 21:52:45 +00:00
|
|
|
|
2019-12-24 15:05:24 +00:00
|
|
|
char c;
|
|
|
|
while((c=getchar()) != 'q')
|
|
|
|
{
|
2020-03-23 14:41:28 +00:00
|
|
|
realTimeReport(h, r.outDevIdx );
|
2019-12-24 15:05:24 +00:00
|
|
|
|
|
|
|
switch(c)
|
|
|
|
{
|
|
|
|
case 'i':
|
|
|
|
case 'I':
|
2019-12-28 02:51:28 +00:00
|
|
|
buf::enableTone(r.audioBufH, r.inDevIdx,-1,buf::kInFl | (c=='I'?buf::kEnableFl:0));
|
2019-12-24 15:05:24 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 'o':
|
|
|
|
case 'O':
|
2019-12-28 02:51:28 +00:00
|
|
|
buf::enableTone(r.audioBufH, r.outDevIdx,-1,buf::kOutFl | (c=='O'?buf::kEnableFl:0));
|
2019-12-24 15:05:24 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 'p':
|
|
|
|
case 'P':
|
2019-12-28 02:51:28 +00:00
|
|
|
buf::enablePass(r.audioBufH, r.outDevIdx,-1,buf::kOutFl | (c=='P'?buf::kEnableFl:0));
|
2019-12-24 15:05:24 +00:00
|
|
|
break;
|
2019-12-27 21:52:45 +00:00
|
|
|
|
|
|
|
case 'M':
|
|
|
|
case 'm':
|
2019-12-28 02:51:28 +00:00
|
|
|
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));
|
2019-12-27 21:52:45 +00:00
|
|
|
break;
|
2019-12-24 15:05:24 +00:00
|
|
|
|
|
|
|
case 's':
|
2019-12-28 02:51:28 +00:00
|
|
|
buf::report(r.audioBufH);
|
2019-12-24 15:05:24 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// stop the input device
|
2020-03-23 14:41:28 +00:00
|
|
|
if( isStarted(h,r.inDevIdx) )
|
|
|
|
if( stop(h,r.inDevIdx) != kOkRC )
|
2019-12-24 15:05:24 +00:00
|
|
|
cwLogInfo("In device stop failed.");
|
|
|
|
|
|
|
|
// stop the output device
|
2020-03-23 14:41:28 +00:00
|
|
|
if( isStarted(h,r.outDevIdx) )
|
|
|
|
if( stop(h,r.outDevIdx) != kOkRC )
|
2019-12-24 15:05:24 +00:00
|
|
|
cwLogInfo("Out device stop failed.");
|
|
|
|
}
|
|
|
|
|
|
|
|
errLabel:
|
|
|
|
|
2019-12-26 02:44:14 +00:00
|
|
|
// release the ALSA driver
|
|
|
|
rc_t rc0 = alsa::destroy(alsaH);
|
|
|
|
|
2019-12-24 15:05:24 +00:00
|
|
|
// release any resources held by the audio port interface
|
2019-12-26 02:44:14 +00:00
|
|
|
rc_t rc1 = destroy(h);
|
|
|
|
|
2019-12-28 02:51:28 +00:00
|
|
|
rc_t rc2 = buf::destroy(r.audioBufH);
|
2019-12-24 15:05:24 +00:00
|
|
|
|
|
|
|
//cmApNrtFree();
|
|
|
|
//cmApFileFree();
|
|
|
|
|
|
|
|
// report the count of audio buffer callbacks
|
2019-12-27 21:52:45 +00:00
|
|
|
cwLogInfo("cb count: i:%i o:%i", r.iCbCnt, r.oCbCnt );
|
2019-12-24 15:05:24 +00:00
|
|
|
|
2019-12-26 02:44:14 +00:00
|
|
|
return rcSelect(rc,rc0,rc1,rc2);
|
2019-12-24 15:05:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// [cmAudioPortExample]
|
|
|
|
|
2020-02-12 18:22:48 +00:00
|
|
|
cw::rc_t cw::audio::device::report()
|
|
|
|
{
|
|
|
|
const char* argv[] = { "-p" };
|
|
|
|
return test(0,argv);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-12-24 15:05:24 +00:00
|
|
|
|