diff --git a/cwAudioDeviceFile.cpp b/cwAudioDeviceFile.cpp index fea83a4..36a4277 100644 --- a/cwAudioDeviceFile.cpp +++ b/cwAudioDeviceFile.cpp @@ -2,11 +2,17 @@ #include "cwLog.h" #include "cwCommonImpl.h" #include "cwMem.h" +#include "cwText.h" #include "cwFile.h" +#include "cwThread.h" +#include "cwMutex.h" #include "cwObject.h" +#include "cwTime.h" #include "cwAudioFile.h" #include "cwAudioDeviceDecls.h" #include "cwAudioDevice.h" +#include "cwAudioDeviceFile.h" +#include "cwVectOps.h" namespace cw { @@ -24,29 +30,55 @@ namespace cw typedef struct dev_str { char* label; - audioPacket_t pkt; - float* pktAudioBuf; + + std::atomic_uint readyCnt; unsigned framesPerCycle; double srate; - char* iInFname; + cbFunc_t cbFunc; + void* cbArg; + unsigned cbDevIdx; + + bool isStartedFl; + + char* iFname; audiofile::handle_t iFileH; unsigned iFlags; - - char* oInFname; + 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* oFname; audiofile::handle_t oFileH; unsigned oFlags; unsigned oChCnt; 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; } dev_t; typedef struct dev_mgr_str { - driver_t* driver; - dev_t* list; + driver_t driver; + dev_t* list; + unsigned threadTimeOutMs; + unsigned threadCbCnt; + thread::handle_t threadH; + mutex::handle_t mutexH; + } dev_mgr_t; 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* d = p->label; + dev_t* d = p->list; for(; d!=nullptr; d=d->link) if( textCompare(d->label,label) == 0 ) return d; @@ -68,7 +100,7 @@ namespace cw dev_t* _indexToDev( dev_mgr_t* p, unsigned devIdx ) { unsigned i = 0; - dev_t* d = p->dev; + dev_t* d = p->list; for(; d!=nullptr; d=d->link) { @@ -76,6 +108,8 @@ namespace cw return d; ++i; } + + return nullptr; } rc_t _indexToDev( dev_mgr_t* p, unsigned devIdx, dev_t*& devPtrRef ) @@ -97,88 +131,308 @@ namespace cw d = mem::allocZ(); d->label = mem::duplStr(label); - if(d0 == nullptr) + if(p->list == nullptr) p->list = d; 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; + } 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 ) { if( d->iFileH.isValid() ) { close(d->iFileH); - d->israte = 0; - d->iFramesPerCycle = 0; + d->iCbCnt = 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::info_t info; + + if( d->iFname == nullptr ) + return rc; + // 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; } // if the device input file is already open - then close it 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); } + + 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(d->iPktAudioBuf,d->framesPerCycle*info.chCnt); + d->iChArray = mem::resize(d->iChArray,info.chCnt); + d->iChSmpBuf = mem::resize(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; iiChCnt; ++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; iiChCnt; ++i) + vop::fill( d->iPktAudioBuf + (actualFrameCnt * d->iChCnt) + i, 0, d->framesPerCycle-actualFrameCnt, d->iChCnt ); + } + + errLabel: + return rc; } - void _close_output( dev_t* d ) { if( d->oFileH.isValid() ) { close(d->oFileH); - d->osrate = 0; - d->oFramesPerCycle = 0; + d->oCbCnt = 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; + + if( d->oFname == nullptr ) + return rc; - // open the requested audio flie - if((rc = create( oFileH, audioOutFile, srate, bitsPerSample, chN )) != kOkRC ) + // 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; } - + + d->oFileH = oFileH; + d->oPktAudioBuf = mem::resize(d->oPktAudioBuf,d->framesPerCycle*d->oChCnt); + d->oChArray = mem::resize(d->oChArray,d->oChCnt); + d->oChSmpBuf = mem::resize(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; ioChCnt; ++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 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; _close_input(d); _close_output(d); mem::release(d->label); mem::release(d); - d = d0; - } + d = d0; + } + + errLabel: 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.deviceStop = deviceStop; p->driver.deviceIsStarted = deviceIsStarted; + p->driver.deviceIsAsync = deviceIsAsync; + p->driver.deviceExecute = deviceExecute; 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; hRef.set(p); + errLabel: + if( rc != kOkRC ) + rc = cwLogError(rc,"Audio device file create failed."); + return rc; } @@ -223,15 +497,50 @@ cw::rc_t cw::audio::device::file::destroy( handle_t& hRef ) if((rc = _destroy(p)) != kOkRC ) return rc; - h.clear(); + hRef.clear(); 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) { dev_mgr_t* p = _driverToPtr(drv); dev_t* d = p->list; + unsigned n = 0; for(; d!=nullptr; d=d->link) ++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 ) { + rc_t rc= kOkRC; dev_t* d = nullptr; 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 ) { + rc_t rc = kOkRC; dev_t* d = nullptr; dev_mgr_t* p = _driverToPtr(drv); if((rc = _indexToDev( p, devIdx, d )) != kOkRC ) 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 ) { + rc_t rc= kOkRC; dev_t* d = nullptr; dev_mgr_t* p = _driverToPtr(drv); if((rc = _indexToDev( p, devIdx, d )) != kOkRC ) 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 ) { + rc_t rc= kOkRC; dev_t* d = nullptr; dev_mgr_t* p = _driverToPtr(drv); 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 ) @@ -296,16 +643,19 @@ cw::rc_t cw::audio::device::file::deviceStart( struct driver_str* dr 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 ) 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 ) 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 ) @@ -313,30 +663,73 @@ cw::rc_t cw::audio::device::file::deviceStop( struct driver_str* dr rc_t rc = kOkRC; dev_t* d = nullptr; dev_mgr_t* p = _driverToPtr(drv); - + if((rc = _indexToDev( p, devIdx, d )) == kOkRC ) - if( d->oFileH.isValid() ) - if((rc = flush(d->ofileH)) != kOkRC ) - rc = cwLogError(rc,"Flush on stop failed on the audio device file output file."); + if( d->isStartedFl && d->oFileH.isValid() ) + { + // 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; } 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 ) { + 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; - info_t info; dev_t* d; - dev_mgr_t* p = _handleToPtr(hRef); + dev_mgr_t* p = _handleToPtr(h); // create or find a device 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; } - d->iFname = mem::duplStr(audioInFile); + d->iFname = mem::duplStr(audioInFName); d->iFlags = flags; errLabel: @@ -353,11 +746,11 @@ cw::rc_t cw::audio::device::file::createInDevice( handle_t& h, const char* l 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_mgr_t* p = _handleToPtr(hRef); + dev_mgr_t* p = _handleToPtr(h); // create or find a device 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; } - d->oFname = mem::duplStr(audioOutFile); + d->oFname = mem::duplStr(audioOutFName); d->oFlags = flags; d->oChCnt = chCnt; d->oBitsPerSample = bitsPerSample; @@ -376,14 +769,21 @@ cw::rc_t cw::audio::device::file::createOutDevice( handle_t& h, const char* l 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 ) { + 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() { + rc_t rc = kOkRC; + return rc; } diff --git a/cwAudioDeviceFile.h b/cwAudioDeviceFile.h index 446934c..ced01d0 100644 --- a/cwAudioDeviceFile.h +++ b/cwAudioDeviceFile.h @@ -14,24 +14,30 @@ namespace cw rc_t create( handle_t& hRef, struct driver_str*& drvRef ); rc_t destroy( handle_t& hRef ); + rc_t start( handle_t h ); + rc_t stop( handle_t h ); unsigned deviceCount( struct driver_str* drv); const char* deviceLabel( struct driver_str* drv, unsigned devIdx ); unsigned deviceChannelCount( struct driver_str* drv, unsigned devIdx, bool inputFl ); double deviceSampleRate( struct driver_str* drv, unsigned devIdx ); 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 deviceStop( 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 ); enum { kRewindOnStartFl = 0x01, }; - - rc_t createInDevice( handle_t& h, const char* label, const char* audioInFile, unsigned flags ); - rc_t createOutDevice( handle_t& h, const char* label, const char* audioOutFile, unsigned flags, unsigned chCnt, unsigned bitsPerSample ); + + // A device may have an input, an output or both. + // 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. rc_t deviceExec( handle_t& h, unsigned devIdx );