cwAudioDeviceFile.h/cpp : Converted to async device using internal thread.

Added deviceIsAsync(),deviceExecute(), and cbDevidx to deviceSetup().
Added start(),stop(),createInDevice(),createOutDevice().
This commit is contained in:
kevin 2023-02-14 20:52:32 -05:00
parent d46101f4b9
commit 93837891a4
2 changed files with 471 additions and 65 deletions

View File

@ -2,11 +2,17 @@
#include "cwLog.h" #include "cwLog.h"
#include "cwCommonImpl.h" #include "cwCommonImpl.h"
#include "cwMem.h" #include "cwMem.h"
#include "cwText.h"
#include "cwFile.h" #include "cwFile.h"
#include "cwThread.h"
#include "cwMutex.h"
#include "cwObject.h" #include "cwObject.h"
#include "cwTime.h"
#include "cwAudioFile.h" #include "cwAudioFile.h"
#include "cwAudioDeviceDecls.h" #include "cwAudioDeviceDecls.h"
#include "cwAudioDevice.h" #include "cwAudioDevice.h"
#include "cwAudioDeviceFile.h"
#include "cwVectOps.h"
namespace cw namespace cw
{ {
@ -24,29 +30,55 @@ namespace cw
typedef struct dev_str typedef struct dev_str
{ {
char* label; char* label;
audioPacket_t pkt;
float* pktAudioBuf; std::atomic_uint readyCnt;
unsigned framesPerCycle; unsigned framesPerCycle;
double srate; double srate;
char* iInFname; cbFunc_t cbFunc;
void* cbArg;
unsigned cbDevIdx;
bool isStartedFl;
char* iFname;
audiofile::handle_t iFileH; audiofile::handle_t iFileH;
unsigned iFlags; unsigned iFlags;
unsigned iChCnt;
audioPacket_t iPkt;
float* iPktAudioBuf;
unsigned iCbCnt;
float** iChArray; // iChArray[2]
float* iChSmpBuf; //
unsigned iErrCnt; // count of errors
unsigned iFrmCnt; // count of frames read
char* oInFname; char* oFname;
audiofile::handle_t oFileH; audiofile::handle_t oFileH;
unsigned oFlags; unsigned oFlags;
unsigned oChCnt; unsigned oChCnt;
unsigned oBitsPerSample; unsigned oBitsPerSample;
audioPacket_t oPkt;
float* oPktAudioBuf;
unsigned oCbCnt;
float** oChArray; // oChArray[2]
float* oChSmpBuf; //
unsigned oErrCnt; // count of errors
unsigned oFrmCnt; // count of frames written
struct dev_str* link; struct dev_str* link;
} dev_t; } dev_t;
typedef struct dev_mgr_str typedef struct dev_mgr_str
{ {
driver_t* driver; driver_t driver;
dev_t* list; dev_t* list;
unsigned threadTimeOutMs;
unsigned threadCbCnt;
thread::handle_t threadH;
mutex::handle_t mutexH;
} dev_mgr_t; } dev_mgr_t;
dev_mgr_t* _handleToPtr( handle_t h ) dev_mgr_t* _handleToPtr( handle_t h )
@ -57,7 +89,7 @@ namespace cw
dev_t* _labelToDev( dev_mgr_t* p, const char* label ) dev_t* _labelToDev( dev_mgr_t* p, const char* label )
{ {
dev_t* d = p->label; dev_t* d = p->list;
for(; d!=nullptr; d=d->link) for(; d!=nullptr; d=d->link)
if( textCompare(d->label,label) == 0 ) if( textCompare(d->label,label) == 0 )
return d; return d;
@ -68,7 +100,7 @@ namespace cw
dev_t* _indexToDev( dev_mgr_t* p, unsigned devIdx ) dev_t* _indexToDev( dev_mgr_t* p, unsigned devIdx )
{ {
unsigned i = 0; unsigned i = 0;
dev_t* d = p->dev; dev_t* d = p->list;
for(; d!=nullptr; d=d->link) for(; d!=nullptr; d=d->link)
{ {
@ -76,6 +108,8 @@ namespace cw
return d; return d;
++i; ++i;
} }
return nullptr;
} }
rc_t _indexToDev( dev_mgr_t* p, unsigned devIdx, dev_t*& devPtrRef ) rc_t _indexToDev( dev_mgr_t* p, unsigned devIdx, dev_t*& devPtrRef )
@ -97,88 +131,308 @@ namespace cw
d = mem::allocZ<dev_t>(); d = mem::allocZ<dev_t>();
d->label = mem::duplStr(label); d->label = mem::duplStr(label);
if(d0 == nullptr) if(p->list == nullptr)
p->list = d; p->list = d;
else else
{
// set d0 to the last dev on the list
dev_t* d0 = p->list;
while( d0->link != nullptr )
d0 = d0->link;
d0->link = d; d0->link = d;
}
return d; return d;
} }
void _devReport( dev_t* d )
{
cwLogInfo("%s : FpC:%i sr:%f",cwStringNullGuard(d->label),d->framesPerCycle,d->srate);
if( d->iFileH.isValid() )
cwLogInfo(" in: flags:0x%x ch:%i sr:%f : %s", d->iFlags,channelCount(d->iFileH),sampleRate(d->iFileH),cwStringNullGuard(d->iFname));
if( d->oFileH.isValid() )
cwLogInfo(" out: flags:0x%x ch:%i sr:%f : %s", d->oFlags,d->oChCnt,d->srate,cwStringNullGuard(d->oFname));
}
void _close_input( dev_t* d ) void _close_input( dev_t* d )
{ {
if( d->iFileH.isValid() ) if( d->iFileH.isValid() )
{ {
close(d->iFileH); close(d->iFileH);
d->israte = 0; d->iCbCnt = 0;
d->iFramesPerCycle = 0; mem::release(d->iFname);
mem::release(d->iPktAudioBuf);
mem::release(d->iChArray);
mem::release(d->iChSmpBuf);
} }
} }
rc_t _open_input( dev_t* d ) rc_t _open_input( dev_t* d, unsigned devIdx )
{ {
rc_t rc; rc_t rc = kOkRC;
audiofile::handle_t iFileH; audiofile::handle_t iFileH;
audiofile::info_t info;
if( d->iFname == nullptr )
return rc;
// open the requested audio flie // open the requested audio flie
if((rc = open( iFileH, audioInFile, &info )) != kOkRC ) if((rc = open( iFileH, d->iFname, &info )) != kOkRC )
{ {
rc = cwLogError(rc,"Audio file device open failed on '%s'.",cwStringNullGuard(audioInFile)); rc = cwLogError(rc,"Audio file device open failed on '%s'.",cwStringNullGuard(d->iFname));
goto errLabel; goto errLabel;
} }
// if the device input file is already open - then close it // if the device input file is already open - then close it
if( d->iFileH.isValid() ) if( d->iFileH.isValid() )
{ {
cwLogWarning("The audio file device '%s' input file '%s' has been closed prior to opening '%s'.",name(d->iFileH),cwStringNullGuard(audioInFile)); cwLogWarning("The audio file device '%s' input file '%s' has been closed prior to opening '%s'.",name(d->iFileH),cwStringNullGuard(d->iFname));
_close_input(d); _close_input(d);
} }
if( info.srate != d->srate )
cwLogWarning("The audio file sample rate (%f) does not match device sample rate (%f).",info.srate,d->srate);
d->iFileH = iFileH;
d->iPktAudioBuf = mem::resize<sample_t>(d->iPktAudioBuf,d->framesPerCycle*info.chCnt);
d->iChArray = mem::resize<sample_t*>(d->iChArray,info.chCnt);
d->iChSmpBuf = mem::resize<sample_t>(d->iChSmpBuf, d->framesPerCycle*info.chCnt );
d->iChCnt = info.chCnt;
d->iErrCnt = 0;
d->iFrmCnt = 0;
d->iPkt.devIdx = d->cbDevIdx;
d->iPkt.begChIdx = 0;
d->iPkt.chCnt = info.chCnt;
d->iPkt.audioFramesCnt = d->framesPerCycle;
d->iPkt.bitsPerSample = sizeof(sample_t);
d->iPkt.flags = kInterleavedApFl | kFloatApFl;
d->iPkt.audioBytesPtr = d->iPktAudioBuf;
d->iPkt.cbArg = d->cbArg;
for(unsigned i=0; i<d->iChCnt; ++i)
d->iChArray[i] = d->iChSmpBuf + i*d->framesPerCycle;
errLabel:
return rc;
} }
rc_t _read_input( dev_t* d )
{
rc_t rc = kOkRC;
unsigned actualFrameCnt = 0;
// read the file
if((rc = readFloat(d->iFileH, d->framesPerCycle, 0, d->iChCnt, d->iChArray, &actualFrameCnt)) == kOkRC )
d->iFrmCnt += actualFrameCnt;
else
{
rc = cwLogError(rc,"File read failed on audio device file: %s.",cwStringNullGuard(d->label));
d->iErrCnt += 1;
goto errLabel;
}
// interleave into the iPkt audio buffer
vop::interleave( d->iPktAudioBuf, d->iChSmpBuf, actualFrameCnt, d->iChCnt );
if( actualFrameCnt < d->framesPerCycle )
{
for(unsigned i=0; i<d->iChCnt; ++i)
vop::fill( d->iPktAudioBuf + (actualFrameCnt * d->iChCnt) + i, 0, d->framesPerCycle-actualFrameCnt, d->iChCnt );
}
errLabel:
return rc;
}
void _close_output( dev_t* d ) void _close_output( dev_t* d )
{ {
if( d->oFileH.isValid() ) if( d->oFileH.isValid() )
{ {
close(d->oFileH); close(d->oFileH);
d->osrate = 0; d->oCbCnt = 0;
d->oFramesPerCycle = 0; mem::release(d->oFname);
mem::release(d->oPktAudioBuf);
mem::release(d->oChArray);
mem::release(d->oChSmpBuf);
} }
} }
rc_t _open_output( dev_t* d ) rc_t _open_output( dev_t* d, unsigned devIdx )
{ {
rc_t rc = kOkRC;
audiofile::handle_t oFileH; audiofile::handle_t oFileH;
// open the requested audio flie if( d->oFname == nullptr )
if((rc = create( oFileH, audioOutFile, srate, bitsPerSample, chN )) != kOkRC ) return rc;
// open the requested audio output flie
if((rc = create( oFileH, d->oFname, d->srate, d->oBitsPerSample, d->oChCnt )) != kOkRC )
{ {
rc = cwLogError(rc,"Audio file device open failed on '%s'.",cwStringNullGuard(audioInFile)); rc = cwLogError(rc,"Audio file device open failed on '%s'.",cwStringNullGuard(d->oFname));
goto errLabel; goto errLabel;
} }
d->oFileH = oFileH;
d->oPktAudioBuf = mem::resize<sample_t>(d->oPktAudioBuf,d->framesPerCycle*d->oChCnt);
d->oChArray = mem::resize<sample_t*>(d->oChArray,d->oChCnt);
d->oChSmpBuf = mem::resize<sample_t>(d->oChSmpBuf, d->framesPerCycle*d->oChCnt );
d->oFrmCnt = 0;
d->oErrCnt = 0;
d->oPkt.devIdx = d->cbDevIdx;
d->oPkt.begChIdx = 0;
d->oPkt.chCnt = d->oChCnt;
d->oPkt.audioFramesCnt = d->framesPerCycle;
d->oPkt.bitsPerSample = 0;
d->oPkt.flags = kInterleavedApFl | kFloatApFl;
d->oPkt.audioBytesPtr = d->oPktAudioBuf;
d->oPkt.cbArg = d->cbArg;
for(unsigned i=0; i<d->oChCnt; ++i)
d->oChArray[i] = d->oChSmpBuf + i*d->framesPerCycle;
errLabel:
return rc;
} }
rc_t _write_output( dev_t* d )
{
rc_t rc = kOkRC;
if( d->oFileH.isValid() )
{
/*
// deinterleave the audio into d->oChArray[]
vop::deinterleave( d->oChSmpBuf, d->oPktAudioBuf, d->framesPerCycle, d->oChCnt );
// write the audio
if((rc = writeFloat( d->oFileH, d->framesPerCycle, d->oChCnt, d->oChArray )) == kOkRC )
*/
if((rc = writeFloatInterleaved( d->oFileH, d->framesPerCycle, d->oChCnt, d->oPktAudioBuf )) == kOkRC )
d->oFrmCnt += d->framesPerCycle;
else
{
rc = cwLogError(rc,"Audio device file write failed on device '%s'.",d->label);
d->oErrCnt += 1;
goto errLabel;
}
}
errLabel:
return rc;
}
rc_t _update_device( dev_t* d )
{
rc_t rc0 = kOkRC;
rc_t rc1 = kOkRC;
audioPacket_t* iPkt = nullptr;
unsigned iPktN = 0;
audioPacket_t* oPkt = nullptr;
unsigned oPktN = 0;
if( d->iFileH.isValid() )
{
iPkt = &d->iPkt;
iPktN = 1;
d->iCbCnt += 1;
rc0 = _read_input( d );
}
if( d->oFileH.isValid() )
{
oPkt = &d->oPkt;
oPktN = 1;
d->oCbCnt += 1;
vop::zero( d->oPktAudioBuf, d->oChCnt * d->framesPerCycle );
}
d->cbFunc( d->cbArg, iPkt, iPktN, oPkt, oPktN );
if( d->oFileH.isValid() )
rc1 = _write_output( d );
return rcSelect(rc0,rc1);
}
bool _threadCbFunc( void* arg )
{
rc_t rc = kOkRC;
dev_mgr_t* p = (dev_mgr_t*)arg;
dev_t* d = nullptr;
// block on the cond. var - unlock the mutex
if((rc = mutex::waitOnCondVar(p->mutexH,false,p->threadTimeOutMs)) != kOkRC )
{
if( rc != kTimeOutRC )
{
cwLogError(rc,"Audio device file thread Wait-on-condition-var failed.");
return false;
}
}
p->threadCbCnt += 1;
// if the cond. var was signaled and p->mutexH is locked
if( rc == kOkRC )
{
// check for ready devices
for(d=p->list; d!=nullptr; d=d->link)
{
// if this device is ready
if( std::atomic_load_explicit(&d->readyCnt, std::memory_order_acquire) > 0 ) // ACQUIRE
{
// atomic incr - note that the ordering doesn't matter because the update does not control access to any other variables from another thread
std::atomic_store_explicit(&d->readyCnt, d->readyCnt-1, std::memory_order_relaxed); // decrement
if((rc = _update_device(d)) != kOkRC )
cwLogError(rc,"The update of audio device file %s failed.",cwStringNullGuard(d->label));
}
}
}
return true;
}
rc_t _destroy( dev_mgr_t* p ) rc_t _destroy( dev_mgr_t* p )
{ {
rc_t rc = kOkRC; rc_t rc = kOkRC;
dev_t* d = p->list;
while( d != nullptr ) // destroy the thread
if((rc = thread::destroy(p->threadH)) != kOkRC )
{
rc = cwLogError(rc,"Audio file device thread destroy failed.");
goto errLabel;
}
// destroy the mutex
if((rc = mutex::destroy( p->mutexH )) != kOkRC )
{
rc = cwLogError(rc,"Audio file device thread mutex destroy failed.");
goto errLabel;
}
// destroy each device
for(dev_t* d = p->list; d != nullptr; )
{ {
dev_t* d0 = d->link; dev_t* d0 = d->link;
_close_input(d); _close_input(d);
_close_output(d); _close_output(d);
mem::release(d->label); mem::release(d->label);
mem::release(d); mem::release(d);
d = d0;
d = d0;
} }
errLabel:
return rc; return rc;
} }
} }
} }
} }
@ -203,12 +457,32 @@ cw::rc_t cw::audio::device::file::create( handle_t& hRef, struct driver_str*&
p->driver.deviceStart = deviceStart; p->driver.deviceStart = deviceStart;
p->driver.deviceStop = deviceStop; p->driver.deviceStop = deviceStop;
p->driver.deviceIsStarted = deviceIsStarted; p->driver.deviceIsStarted = deviceIsStarted;
p->driver.deviceIsAsync = deviceIsAsync;
p->driver.deviceExecute = deviceExecute;
p->driver.deviceRealTimeReport = deviceRealTimeReport; p->driver.deviceRealTimeReport = deviceRealTimeReport;
if((rc = create( p->threadH, _threadCbFunc, p )) != kOkRC )
{
rc = cwLogError(rc,"Audio device file thread create failed.");
goto errLabel;
}
// create the audio group thread mutex/cond var
if((rc = mutex::create(p->mutexH)) != kOkRC )
{
rc = cwLogError(rc,"Audio device file mutex create failed.");
goto errLabel;
}
drvRef = &p->driver; drvRef = &p->driver;
hRef.set(p); hRef.set(p);
errLabel:
if( rc != kOkRC )
rc = cwLogError(rc,"Audio device file create failed.");
return rc; return rc;
} }
@ -223,15 +497,50 @@ cw::rc_t cw::audio::device::file::destroy( handle_t& hRef )
if((rc = _destroy(p)) != kOkRC ) if((rc = _destroy(p)) != kOkRC )
return rc; return rc;
h.clear(); hRef.clear();
return rc; return rc;
} }
cw::rc_t cw::audio::device::file::start( handle_t h )
{
rc_t rc = kOkRC;
dev_mgr_t* p = _handleToPtr(h);
// if there are any audio device files
if( p->list != nullptr )
{
if((rc = thread::unpause(p->threadH)) != kOkRC )
{
rc = cwLogError(rc,"Audio file device thread start failed.");
goto errLabel;
}
}
errLabel:
return rc;
}
cw::rc_t cw::audio::device::file::stop( handle_t h )
{
rc_t rc = kOkRC;
dev_mgr_t* p = _handleToPtr(h);
if((rc = thread::pause(p->threadH)) != kOkRC )
{
rc = cwLogError(rc,"Audio file device thread stop failed.");
goto errLabel;
}
errLabel:
return rc;
}
unsigned cw::audio::device::file::deviceCount( struct driver_str* drv) unsigned cw::audio::device::file::deviceCount( struct driver_str* drv)
{ {
dev_mgr_t* p = _driverToPtr(drv); dev_mgr_t* p = _driverToPtr(drv);
dev_t* d = p->list; dev_t* d = p->list;
unsigned n = 0;
for(; d!=nullptr; d=d->link) for(; d!=nullptr; d=d->link)
++n; ++n;
return n; return n;
@ -239,6 +548,7 @@ unsigned cw::audio::device::file::deviceCount( struct driver_str* dr
const char* cw::audio::device::file::deviceLabel( struct driver_str* drv, unsigned devIdx ) const char* cw::audio::device::file::deviceLabel( struct driver_str* drv, unsigned devIdx )
{ {
rc_t rc= kOkRC;
dev_t* d = nullptr; dev_t* d = nullptr;
dev_mgr_t* p = _driverToPtr(drv); dev_mgr_t* p = _driverToPtr(drv);
@ -250,41 +560,78 @@ const char* cw::audio::device::file::deviceLabel( struct driver_str* dr
unsigned cw::audio::device::file::deviceChannelCount( struct driver_str* drv, unsigned devIdx, bool inputFl ) unsigned cw::audio::device::file::deviceChannelCount( struct driver_str* drv, unsigned devIdx, bool inputFl )
{ {
rc_t rc = kOkRC;
dev_t* d = nullptr; dev_t* d = nullptr;
dev_mgr_t* p = _driverToPtr(drv); dev_mgr_t* p = _driverToPtr(drv);
if((rc = _indexToDev( p, devIdx, d )) != kOkRC ) if((rc = _indexToDev( p, devIdx, d )) != kOkRC )
return 0; return 0;
return channelCount( inputFl ? d->iFileH : d->oFileH ); audiofile::handle_t fH = inputFl ? d->iFileH : d->oFileH;
return fH.isValid() ? channelCount( fH ) : 0;
} }
double cw::audio::device::file::deviceSampleRate( struct driver_str* drv, unsigned devIdx ) double cw::audio::device::file::deviceSampleRate( struct driver_str* drv, unsigned devIdx )
{ {
rc_t rc= kOkRC;
dev_t* d = nullptr; dev_t* d = nullptr;
dev_mgr_t* p = _driverToPtr(drv); dev_mgr_t* p = _driverToPtr(drv);
if((rc = _indexToDev( p, devIdx, d )) != kOkRC ) if((rc = _indexToDev( p, devIdx, d )) != kOkRC )
return 0; return 0;
return inputFl ? d->israte : d->osrate; return d->srate;
} }
unsigned cw::audio::device::file::deviceFramesPerCycle( struct driver_str* drv, unsigned devIdx, bool inputFl ) unsigned cw::audio::device::file::deviceFramesPerCycle( struct driver_str* drv, unsigned devIdx, bool inputFl )
{ {
rc_t rc= kOkRC;
dev_t* d = nullptr; dev_t* d = nullptr;
dev_mgr_t* p = _driverToPtr(drv); dev_mgr_t* p = _driverToPtr(drv);
if((rc = _indexToDev( p, devIdx, d )) != kOkRC ) if((rc = _indexToDev( p, devIdx, d )) != kOkRC )
return nullptr; return 0;
return inputFl ? d->iFramesPerCycle : d->oFramesPerCycle; return d->framesPerCycle;
} }
cw::rc_t cw::audio::device::file::deviceSetup( struct driver_str* drv, unsigned devIdx, double sr, unsigned frmPerCycle, cbFunc_t cbFunc, void* cbArg ) cw::rc_t cw::audio::device::file::deviceSetup( struct driver_str* drv, unsigned devIdx, double srate, unsigned frmPerCycle, cbFunc_t cbFunc, void* cbArg, unsigned cbDevIdx )
{ {
assert(0); // not yet implemented rc_t rc0 = kOkRC;
rc_t rc1 = kOkRC;
dev_t* d = nullptr;
dev_mgr_t* p = _driverToPtr(drv);
if((rc0 = _indexToDev( p, devIdx, d )) != kOkRC )
return rc0;
_close_input(d);
_close_output(d);
d->framesPerCycle = frmPerCycle;
d->srate = srate;
d->cbFunc = cbFunc;
d->cbArg = cbArg;
d->cbDevIdx = cbDevIdx;
unsigned ms = (frmPerCycle * 1000) / srate;
if( p->threadTimeOutMs==0 || ms < p->threadTimeOutMs )
{
p->threadTimeOutMs = ms;
cwLogInfo("Audio device file time out %i ms.",p->threadTimeOutMs);
}
rc0 = _open_input(d, devIdx);
rc1 = _open_output(d, devIdx);
rc0 = rcSelect(rc0,rc1);
if(rc0 != kOkRC )
rc0 = cwLogError(rc0,"Audio device file '%s' setup failed.",cwStringNullGuard(d->label));
return rc0;
} }
cw::rc_t cw::audio::device::file::deviceStart( struct driver_str* drv, unsigned devIdx ) cw::rc_t cw::audio::device::file::deviceStart( struct driver_str* drv, unsigned devIdx )
@ -296,16 +643,19 @@ cw::rc_t cw::audio::device::file::deviceStart( struct driver_str* dr
if((rc0 = _indexToDev( p, devIdx, d )) == kOkRC ) if((rc0 = _indexToDev( p, devIdx, d )) == kOkRC )
{ {
if( d->iFileH.isValid() && cwIsSet(d->iFlags,kRewindOnStartFl) ) if( d->iFileH.isValid() && cwIsFlag(d->iFlags,kRewindOnStartFl) )
if((rc0 = seek(d->iFileH,0)) != kOkRC ) if((rc0 = seek(d->iFileH,0)) != kOkRC )
rc0 = cwLogError(rc0,"Rewind on start failed on the audio device file input file."); rc0 = cwLogError(rc0,"Rewind on start failed on the audio device file input file.");
if( d->oFileH.isValid() && cwIsSet(d->oFlags,kRewindOnStartFl) ) if( d->oFileH.isValid() && cwIsFlag(d->oFlags,kRewindOnStartFl) )
if((rc1 = seek(d->oFileH,0)) != kOkRC ) if((rc1 = seek(d->oFileH,0)) != kOkRC )
rc1 = cwLogError(rc1,"Rewind on start failed on the audio device file output file."); rc1 = cwLogError(rc1,"Rewind on start failed on the audio device file output file.");
} }
return rcSelect(rc0,rc1); if((rc0 = rcSelect(rc0,rc1)) == kOkRC )
d->isStartedFl = true;
return rc0;
} }
cw::rc_t cw::audio::device::file::deviceStop( struct driver_str* drv, unsigned devIdx ) cw::rc_t cw::audio::device::file::deviceStop( struct driver_str* drv, unsigned devIdx )
@ -315,28 +665,71 @@ cw::rc_t cw::audio::device::file::deviceStop( struct driver_str* dr
dev_mgr_t* p = _driverToPtr(drv); dev_mgr_t* p = _driverToPtr(drv);
if((rc = _indexToDev( p, devIdx, d )) == kOkRC ) if((rc = _indexToDev( p, devIdx, d )) == kOkRC )
if( d->oFileH.isValid() ) if( d->isStartedFl && d->oFileH.isValid() )
if((rc = flush(d->ofileH)) != kOkRC ) {
rc = cwLogError(rc,"Flush on stop failed on the audio device file output file."); // TODO: implement audiofile::flush()
// if((rc = flush(d->ofileH)) != kOkRC )
// rc = cwLogError(rc,"Flush on stop failed on the audio device file output file.");
d->isStartedFl = false;
}
return rc; return rc;
} }
bool cw::audio::device::file::deviceIsStarted( struct driver_str* drv, unsigned devIdx ) bool cw::audio::device::file::deviceIsStarted( struct driver_str* drv, unsigned devIdx )
{ {
rc_t rc = kOkRC;
dev_t* d = nullptr;
dev_mgr_t* p = _driverToPtr(drv);
if((rc = _indexToDev( p, devIdx, d )) == kOkRC )
return d->isStartedFl;
return false;
}
bool cw::audio::device::file::deviceIsAsync( struct driver_str* drv, unsigned devIdx )
{
return false;
}
cw::rc_t cw::audio::device::file::deviceExecute( struct driver_str* drv, unsigned devIdx )
{
rc_t rc = kOkRC;
dev_mgr_t* p = _driverToPtr(drv);
dev_t* d = nullptr;
if((rc = _indexToDev( p, devIdx, d )) != kOkRC )
goto errLabel;
std::atomic_store_explicit(&d->readyCnt, d->readyCnt+1, std::memory_order_relaxed); // atomic incr
mutex::signalCondVar(p->mutexH);
errLabel:
return rc;
} }
void cw::audio::device::file::deviceRealTimeReport( struct driver_str* drv, unsigned devIdx ) void cw::audio::device::file::deviceRealTimeReport( struct driver_str* drv, unsigned devIdx )
{ {
rc_t rc = kOkRC;
dev_t* d = nullptr;
dev_mgr_t* p = _driverToPtr(drv);
if((rc = _indexToDev( p, devIdx, d )) != kOkRC )
return;
cwLogInfo("file cb i:%i o:%i err i:%i o:%i frames: i:%i o:%i : th:%i : %s",d->iCbCnt,d->oCbCnt,d->iErrCnt,d->oErrCnt,d->iFrmCnt,d->oFrmCnt,p->threadCbCnt,cwStringNullGuard(d->label));
} }
cw::rc_t cw::audio::device::file::createInDevice( handle_t& h, const char* label, const char* audioInFile, unsigned framesPerCycle, unsigned flags ) cw::rc_t cw::audio::device::file::createInDevice( handle_t& h, const char* label, const char* audioInFName, unsigned flags )
{ {
rc_t rc; rc_t rc = kOkRC;
handle_t iFileH; handle_t iFileH;
info_t info;
dev_t* d; dev_t* d;
dev_mgr_t* p = _handleToPtr(hRef); dev_mgr_t* p = _handleToPtr(h);
// create or find a device // create or find a device
if((d = _findOrCreateDev(p, label )) == nullptr ) if((d = _findOrCreateDev(p, label )) == nullptr )
@ -345,7 +738,7 @@ cw::rc_t cw::audio::device::file::createInDevice( handle_t& h, const char* l
goto errLabel; goto errLabel;
} }
d->iFname = mem::duplStr(audioInFile); d->iFname = mem::duplStr(audioInFName);
d->iFlags = flags; d->iFlags = flags;
errLabel: errLabel:
@ -353,11 +746,11 @@ cw::rc_t cw::audio::device::file::createInDevice( handle_t& h, const char* l
return rc; return rc;
} }
cw::rc_t cw::audio::device::file::createOutDevice( handle_t& h, const char* label, const char* audioOutFile, unsigned flags, unsigned chCnt, unsigned bitsPerSample ) cw::rc_t cw::audio::device::file::createOutDevice( handle_t& h, const char* label, const char* audioOutFName, unsigned flags, unsigned chCnt, unsigned bitsPerSample )
{ {
rc_t rc; rc_t rc = kOkRC;
dev_t* d; dev_t* d;
dev_mgr_t* p = _handleToPtr(hRef); dev_mgr_t* p = _handleToPtr(h);
// create or find a device // create or find a device
if((d = _findOrCreateDev(p, label )) == nullptr ) if((d = _findOrCreateDev(p, label )) == nullptr )
@ -366,7 +759,7 @@ cw::rc_t cw::audio::device::file::createOutDevice( handle_t& h, const char* l
goto errLabel; goto errLabel;
} }
d->oFname = mem::duplStr(audioOutFile); d->oFname = mem::duplStr(audioOutFName);
d->oFlags = flags; d->oFlags = flags;
d->oChCnt = chCnt; d->oChCnt = chCnt;
d->oBitsPerSample = bitsPerSample; d->oBitsPerSample = bitsPerSample;
@ -376,14 +769,21 @@ cw::rc_t cw::audio::device::file::createOutDevice( handle_t& h, const char* l
return rc; return rc;
} }
cw::rc_t cw::audio::device::file::deviceExec( handle_t& h, unsigned devIdx )
{
}
cw::rc_t cw::audio::device::file::report(handle_t h ) cw::rc_t cw::audio::device::file::report(handle_t h )
{ {
rc_t rc = kOkRC;
dev_mgr_t* p = _handleToPtr(h);
dev_t* d = p->list;
for(; d!=nullptr; d=d->link)
_devReport(d);
return rc;
} }
cw::rc_t cw::audio::device::file::report() cw::rc_t cw::audio::device::file::report()
{ {
rc_t rc = kOkRC;
return rc;
} }

View File

@ -14,24 +14,30 @@ namespace cw
rc_t create( handle_t& hRef, struct driver_str*& drvRef ); rc_t create( handle_t& hRef, struct driver_str*& drvRef );
rc_t destroy( handle_t& hRef ); rc_t destroy( handle_t& hRef );
rc_t start( handle_t h );
rc_t stop( handle_t h );
unsigned deviceCount( struct driver_str* drv); unsigned deviceCount( struct driver_str* drv);
const char* deviceLabel( struct driver_str* drv, unsigned devIdx ); const char* deviceLabel( struct driver_str* drv, unsigned devIdx );
unsigned deviceChannelCount( struct driver_str* drv, unsigned devIdx, bool inputFl ); unsigned deviceChannelCount( struct driver_str* drv, unsigned devIdx, bool inputFl );
double deviceSampleRate( struct driver_str* drv, unsigned devIdx ); double deviceSampleRate( struct driver_str* drv, unsigned devIdx );
unsigned deviceFramesPerCycle( struct driver_str* drv, unsigned devIdx, bool inputFl ); unsigned deviceFramesPerCycle( struct driver_str* drv, unsigned devIdx, bool inputFl );
rc_t deviceSetup( struct driver_str* drv, unsigned devIdx, double sr, unsigned frmPerCycle, cbFunc_t cbFunc, void* cbArg ); rc_t deviceSetup( struct driver_str* drv, unsigned devIdx, double sr, unsigned frmPerCycle, cbFunc_t cbFunc, void* cbArg, unsigned cbDevIdx );
rc_t deviceStart( struct driver_str* drv, unsigned devIdx ); rc_t deviceStart( struct driver_str* drv, unsigned devIdx );
rc_t deviceStop( struct driver_str* drv, unsigned devIdx ); rc_t deviceStop( struct driver_str* drv, unsigned devIdx );
bool deviceIsStarted( struct driver_str* drv, unsigned devIdx ); bool deviceIsStarted( struct driver_str* drv, unsigned devIdx );
bool deviceIsAsync( struct driver_str* drv, unsigned devIdx );
rc_t deviceExecute( struct driver_str* drv, unsigned devIdx );
void deviceRealTimeReport( struct driver_str* drv, unsigned devIdx ); void deviceRealTimeReport( struct driver_str* drv, unsigned devIdx );
enum { enum {
kRewindOnStartFl = 0x01, kRewindOnStartFl = 0x01,
}; };
rc_t createInDevice( handle_t& h, const char* label, const char* audioInFile, unsigned flags ); // A device may have an input, an output or both.
rc_t createOutDevice( handle_t& h, const char* label, const char* audioOutFile, unsigned flags, unsigned chCnt, unsigned bitsPerSample ); // A device with both an input and output can be created by assigning both to the same label
rc_t createInDevice( handle_t& h, const char* label, const char* audioInFName, unsigned flags );
rc_t createOutDevice( handle_t& h, const char* label, const char* audioOutFName, unsigned flags, unsigned chCnt, unsigned bitsPerSample );
// Generate an audio callback on the specified device. // Generate an audio callback on the specified device.
rc_t deviceExec( handle_t& h, unsigned devIdx ); rc_t deviceExec( handle_t& h, unsigned devIdx );