cwIo.h,cpp, cwIoTest.cpp : Many changes to get an initial working version of audio with a audio meter UI.

This commit is contained in:
kevin 2021-01-22 09:49:57 -05:00
parent 469df69857
commit f123be0323
3 changed files with 777 additions and 231 deletions

750
cwIo.cpp
View File

@ -57,21 +57,22 @@ namespace cw
typedef struct audioDev_str typedef struct audioDev_str
{ {
bool enableFl; // bool enableFl; // True if this device was enabled by the user
char* devName; // const char* label; // User label
unsigned devIdx; // const char* devName; // System device name
unsigned iCbCnt; // Count the audio driver in/out callbacks unsigned devIdx; // AudioDevice interface device index
unsigned oCbCnt; // audioGroup_t* iGroup; // Audio group pointers for this device
audioGroup_t* iGroup; // Audio group points for this device
audioGroup_t* oGroup; // audioGroup_t* oGroup; //
audio_group_dev_t* iagd; // audio group device record assoc'd with this audio_group_dev_t* iagd; // Audio group device record assoc'd with this device
audio_group_dev_t* oagd; audio_group_dev_t* oagd; //
} audioDev_t; } audioDev_t;
typedef struct io_str typedef struct io_str
{ {
std::atomic<bool> quitFl; std::atomic<bool> quitFl;
time::spec_t t0;
cbFunc_t cbFunc; cbFunc_t cbFunc;
void* cbArg; void* cbArg;
@ -88,6 +89,9 @@ namespace cw
audio::device::alsa::handle_t alsaH; audio::device::alsa::handle_t alsaH;
audio::buf::handle_t audioBufH; audio::buf::handle_t audioBufH;
unsigned audioThreadTimeOutMs; unsigned audioThreadTimeOutMs;
unsigned audioMeterDevEnabledN;
unsigned audioMeterCbPeriodMs;
time::spec_t audioMeterNextTime;
audioDev_t* audioDevA; audioDev_t* audioDevA;
unsigned audioDevN; unsigned audioDevN;
@ -105,153 +109,11 @@ namespace cw
io_t* _handleToPtr( handle_t h ) io_t* _handleToPtr( handle_t h )
{ return handleToPtr<handle_t,io_t>(h); } { return handleToPtr<handle_t,io_t>(h); }
// Start or stop all the audio devices in p->audioDevA[]
rc_t _audioDeviceStartStop( io_t* p, bool startFl )
{
rc_t rc = kOkRC;
for(unsigned i=0; i<p->audioDevN; ++i) //----------------------------------------------------------------------------------------------------------
if( p->audioDevA[i].enableFl ) //
{ // Serial
rc_t rc0 = kOkRC; //
if( startFl )
rc0 = audio::device::start( p->audioH, p->audioDevA[i].devIdx );
else
rc0 = audio::device::stop( p->audioH, p->audioDevA[i].devIdx );
if(rc0 != kOkRC )
rc = cwLogError(rc0,"The audio device: %s failed to %s.", cwStringNullGuard(p->audioDevA[i].devName), startFl ? "start" : "stop");
}
return rc;
}
// Release all resource associated with a group-device record.
void _audioGroupDestroyDevs( audio_group_dev_t* agd )
{
while( agd != nullptr )
{
audio_group_dev_t* agd0 = agd->link;
mem::release(agd);
agd = agd0;
}
}
// Release all resource associated with all audio group records
rc_t _audioGroupDestroyAll( io_t* p )
{
rc_t rc = kOkRC;
for(unsigned i=0; i<p->audioGroupN; ++i)
{
audioGroup_t* ag = p->audioGroupA + i;
_audioGroupDestroyDevs( ag->msg.iDevL );
_audioGroupDestroyDevs( ag->msg.oDevL );
mem::release(ag->msg.iBufArray);
mem::release(ag->msg.oBufArray);
mutex::unlock( ag->mutexH );
mutex::destroy( ag->mutexH );
}
mem::release(p->audioGroupA);
p->audioGroupN = 0;
return rc;
}
rc_t _audioDestroy( io_t* p )
{
rc_t rc = kOkRC;
// stop each device - this will stop the callbacks to _audioDeviceCallback()
if((rc = _audioDeviceStartStop(p,false)) != kOkRC )
{
rc = cwLogError(rc,"Audio device stop failed.");
goto errLabel;
}
if((rc = audio::device::alsa::destroy(p->alsaH)) != kOkRC )
{
rc = cwLogError(rc,"ALSA sub-system shutdown failed.");
goto errLabel;
}
if((rc = audio::device::destroy(p->audioH)) != kOkRC )
{
rc = cwLogError(rc,"Audio device sub-system shutdown failed.");
goto errLabel;
}
if((rc = audio::buf::destroy(p->audioBufH)) != kOkRC )
{
rc = cwLogError(rc,"Audio buffer release failed.");
goto errLabel;
}
if((rc = _audioGroupDestroyAll(p)) != kOkRC )
{
rc = cwLogError(rc,"Audio group release failed.");
goto errLabel;
}
mem::free(p->audioDevA);
p->audioDevN = 0;
errLabel:
if(rc != kOkRC )
rc = cwLogError(rc,"Audio sub-system shutdown failed.");
return rc;
}
rc_t _destroy( io_t* p )
{
rc_t rc = kOkRC;
// stop thread callbacks
if((rc = thread_mach::destroy(p->threadMachH)) != kOkRC )
return rc;
for(unsigned i=0; i<p->serialN; ++i)
serialPortSrv::destroy( p->serialA[i].serialH );
mem::free(p->serialA);
p->serialN = 0;
// TODO: clean up the audio system more systematically
// by first stopping all the devices and then
// reversing the creating process.
_audioDestroy(p);
midi::device::destroy(p->midiH);
for(unsigned i=0; i<p->uiMapN; ++i)
mem::free(const_cast<char*>(p->uiMapA[i].eleName));
mem::release(p->uiMapA);
p->uiMapN = 0;
ui::ws::destroy(p->wsUiH);
// free the cfg object
if( p->cfg != nullptr )
p->cfg->free();
mem::release(p);
return rc;
}
void _serialPortCb( void* arg, const void* byteA, unsigned byteN ) void _serialPortCb( void* arg, const void* byteA, unsigned byteN )
{ {
@ -366,6 +228,10 @@ namespace cw
} }
//----------------------------------------------------------------------------------------------------------
//
// MIDI
//
void _midiCallback( const midi::packet_t* pktArray, unsigned pktCnt ) void _midiCallback( const midi::packet_t* pktArray, unsigned pktCnt )
{ {
unsigned i,j; unsigned i,j;
@ -403,6 +269,181 @@ namespace cw
return rc; return rc;
} }
//----------------------------------------------------------------------------------------------------------
//
// Audio
//
// Start or stop all the audio devices in p->audioDevA[]
rc_t _audioDeviceStartStop( io_t* p, bool startFl )
{
rc_t rc = kOkRC;
for(unsigned i=0; i<p->audioDevN; ++i)
if( p->audioDevA[i].enableFl )
{
rc_t rc0 = kOkRC;
if( startFl )
rc0 = audio::device::start( p->audioH, p->audioDevA[i].devIdx );
else
rc0 = audio::device::stop( p->audioH, p->audioDevA[i].devIdx );
if(rc0 != kOkRC )
rc = cwLogError(rc0,"The audio device: %s failed to %s.", cwStringNullGuard(p->audioDevA[i].devName), startFl ? "start" : "stop");
}
return rc;
}
// Release all resource associated with a group-device record.
void _audioGroupDestroyDevs( audio_group_dev_t* agd )
{
while( agd != nullptr )
{
audio_group_dev_t* agd0 = agd->link;
mem::release(agd->meterA);
mem::release(agd);
agd = agd0;
}
}
// Release all resource associated with all audio group records
rc_t _audioGroupDestroyAll( io_t* p )
{
rc_t rc = kOkRC;
for(unsigned i=0; i<p->audioGroupN; ++i)
{
audioGroup_t* ag = p->audioGroupA + i;
_audioGroupDestroyDevs( ag->msg.iDevL );
_audioGroupDestroyDevs( ag->msg.oDevL );
mem::release(ag->msg.iBufArray);
mem::release(ag->msg.oBufArray);
mutex::unlock( ag->mutexH ); // the mutex is expected to be locked at this point
mutex::destroy( ag->mutexH );
}
mem::release(p->audioGroupA);
p->audioGroupN = 0;
return rc;
}
rc_t _audioDestroy( io_t* p )
{
rc_t rc = kOkRC;
// stop each device - this will stop the callbacks to _audioDeviceCallback()
if((rc = _audioDeviceStartStop(p,false)) != kOkRC )
{
rc = cwLogError(rc,"Audio device stop failed.");
goto errLabel;
}
if((rc = audio::device::alsa::destroy(p->alsaH)) != kOkRC )
{
rc = cwLogError(rc,"ALSA sub-system shutdown failed.");
goto errLabel;
}
if((rc = audio::device::destroy(p->audioH)) != kOkRC )
{
rc = cwLogError(rc,"Audio device sub-system shutdown failed.");
goto errLabel;
}
if((rc = audio::buf::destroy(p->audioBufH)) != kOkRC )
{
rc = cwLogError(rc,"Audio buffer release failed.");
goto errLabel;
}
if((rc = _audioGroupDestroyAll(p)) != kOkRC )
{
rc = cwLogError(rc,"Audio group release failed.");
goto errLabel;
}
mem::free(p->audioDevA);
p->audioDevN = 0;
errLabel:
if(rc != kOkRC )
rc = cwLogError(rc,"Audio sub-system shutdown failed.");
return rc;
}
// Are either input or output meter enabled on this audio devices.
// Set flags to kInFl or kOutput to select inut or output meters.
// Set both flags to check if either input or output meters are enabled.
bool _audioDeviceIsMeterEnabled( audioDev_t* ad, unsigned flags )
{
return (cwIsFlag(flags,kInFl ) && ad->iagd!=nullptr && cwIsFlag(ad->iagd->flags,kMeterFl))
|| (cwIsFlag(flags,kOutFl) && ad->oagd!=nullptr && cwIsFlag(ad->oagd->flags,kMeterFl));
}
void _audioGroupDeviceUpdateMeter( io_t* p, audio_group_dev_t* agd, unsigned flags )
{
for(unsigned i=0; i<agd->chCnt; ++i)
agd->meterA[i] = audio::buf::meter( p->audioBufH, agd->devIdx, i, flags + audio::buf::kMeterFl );
}
rc_t _audioDeviceMeterCallback( io_t* p, audio_group_dev_t* agd )
{
msg_t m;
m.tid = kAudioMeterTId;
m.u.audioGroupDev = agd;
return p->cbFunc(p->cbArg,&m);
}
rc_t _audioDeviceUpdateMeters( io_t* p )
{
rc_t rc = kOkRC;
for(unsigned i=0; i<p->audioDevN; ++i)
{
audioDev_t* ad = p->audioDevA + i;
if( ad->iagd != nullptr && cwIsFlag(ad->iagd->flags,kMeterFl))
{
_audioGroupDeviceUpdateMeter( p, ad->iagd, audio::buf::kInFl );
_audioDeviceMeterCallback( p, ad->iagd );
}
if( ad->oagd != nullptr && cwIsFlag(ad->oagd->flags,kMeterFl))
{
_audioGroupDeviceUpdateMeter( p, ad->oagd, audio::buf::kOutFl );
_audioDeviceMeterCallback( p, ad->oagd );
}
}
return rc;
}
rc_t _audioDeviceProcessMeters( io_t* p )
{
rc_t rc = kOkRC;
if( time::isGTE(p->t0,p->audioMeterNextTime) )
{
rc = _audioDeviceUpdateMeters(p);
p->audioMeterNextTime = p->t0;
time::advanceMs(p->audioMeterNextTime,p->audioMeterCbPeriodMs);
}
return rc;
}
bool _audioGroupBufIsReady( io_t* p, audioGroup_t* ag, bool inputFl ) bool _audioGroupBufIsReady( io_t* p, audioGroup_t* ag, bool inputFl )
{ {
audio_group_dev_t* agd = inputFl ? ag->msg.iDevL : ag->msg.oDevL; audio_group_dev_t* agd = inputFl ? ag->msg.iDevL : ag->msg.oDevL;
@ -478,23 +519,28 @@ namespace cw
_audioGroupProcSampleBufs( ag->p, ag, kAudioGroupAdvBuf, true ); _audioGroupProcSampleBufs( ag->p, ag, kAudioGroupAdvBuf, true );
_audioGroupProcSampleBufs( ag->p, ag, kAudioGroupAdvBuf, false ); _audioGroupProcSampleBufs( ag->p, ag, kAudioGroupAdvBuf, false );
} }
} }
return true; return true;
} }
// Given a device index return the associated audioDev_t record. // Given a device index return the associated audioDev_t record.
audioDev_t* _audioDeviceIndexToRecd( io_t* p, unsigned devIdx ) audioDev_t* _audioDeviceIndexToRecd( io_t* p, unsigned devIdx, bool reportMissingFl=true )
{ {
for(unsigned i=0; i<p->audioDevN; ++i) for(unsigned i=0; i<p->audioDevN; ++i)
if( p->audioDevA[i].devIdx == devIdx ) if( p->audioDevA[i].devIdx == devIdx )
return p->audioDevA + i; return p->audioDevA + i;
cwLogError(kInvalidArgRC,"A device with index %i could not be found.",devIdx);
return nullptr; return nullptr;
} }
// Add a audioGroup pointer to groupA[] and return the new count of elements in the array. // Add an audioGroup pointer to groupA[] and return the new count of elements in the array.
unsigned _audioDeviceUpdateGroupArray( audioGroup_t** groupA, unsigned groupN, unsigned curGroupN, audioGroup_t* ag ) unsigned _audioDeviceUpdateGroupArray( audioGroup_t** groupA, unsigned groupN, unsigned curGroupN, audioGroup_t* ag )
{ {
if( ag != nullptr ) if( ag != nullptr )
@ -535,7 +581,7 @@ namespace cw
if( audio::buf::isDeviceReady( p->audioBufH, devIdx, audio::buf::kInFl) ) if( audio::buf::isDeviceReady( p->audioBufH, devIdx, audio::buf::kInFl) )
{ {
// atomic incr - note that the ordering doesn't matter because the update does not control access to any other variables from another thread // 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(&ad->iagd->readyCnt, ad->iagd->readyCnt++, std::memory_order_relaxed); std::atomic_store_explicit(&ad->iagd->readyCnt, ad->iagd->readyCnt+1, std::memory_order_relaxed);
curGroupN = _audioDeviceUpdateGroupArray( groupA, groupN, curGroupN, ad->iGroup ); curGroupN = _audioDeviceUpdateGroupArray( groupA, groupN, curGroupN, ad->iGroup );
ad->iagd->cbCnt += 1; // update the callback count for this device ad->iagd->cbCnt += 1; // update the callback count for this device
} }
@ -544,7 +590,7 @@ namespace cw
{ {
if( audio::buf::isDeviceReady( p->audioBufH, devIdx, audio::buf::kOutFl ) ) if( audio::buf::isDeviceReady( p->audioBufH, devIdx, audio::buf::kOutFl ) )
{ {
std::atomic_store_explicit(&ad->oagd->readyCnt, ad->oagd->readyCnt++, std::memory_order_relaxed); // atomic incr std::atomic_store_explicit(&ad->oagd->readyCnt, ad->oagd->readyCnt+1, std::memory_order_relaxed); // atomic incr
curGroupN = _audioDeviceUpdateGroupArray( groupA, groupN, curGroupN, ad->oGroup ); curGroupN = _audioDeviceUpdateGroupArray( groupA, groupN, curGroupN, ad->oGroup );
ad->oagd->cbCnt += 1; ad->oagd->cbCnt += 1;
} }
@ -561,7 +607,7 @@ namespace cw
{ {
// are all devices in this group ready to provide/accept new audio data // are all devices in this group ready to provide/accept new audio data
for(; agd!=nullptr; agd=agd->link) for(; agd!=nullptr; agd=agd->link)
if( std::atomic_load_explicit(&agd->readyCnt, std::memory_order_acquire) > 0 ) // ACQUIRE if( std::atomic_load_explicit(&agd->readyCnt, std::memory_order_acquire) == 0 ) // ACQUIRE
return false; return false;
return true; return true;
@ -584,6 +630,7 @@ namespace cw
for(unsigned i=0; i<groupN; ++i) for(unsigned i=0; i<groupN; ++i)
{ {
audioGroup_t* ag = groupA[i]; audioGroup_t* ag = groupA[i];
if( _audioGroupIsReady( ag->msg.iDevL ) && _audioGroupIsReady( ag->msg.oDevL ) ) if( _audioGroupIsReady( ag->msg.iDevL ) && _audioGroupIsReady( ag->msg.oDevL ) )
{ {
// we now know the group is ready and so the ready count maybe decremented on each device // we now know the group is ready and so the ready count maybe decremented on each device
@ -625,6 +672,7 @@ namespace cw
// groupA[] contains the set of groups which may have been made ready during this callback // groupA[] contains the set of groups which may have been made ready during this callback
_audioGroupNotifyIfReady( p, groupA, curGroupN ); _audioGroupNotifyIfReady( p, groupA, curGroupN );
} }
@ -635,9 +683,13 @@ namespace cw
rc_t rc = kOkRC; rc_t rc = kOkRC;
audio_group_dev_t* new_agd = mem::allocZ<audio_group_dev_t>(); audio_group_dev_t* new_agd = mem::allocZ<audio_group_dev_t>();
new_agd->name = ad->devName; new_agd->label = ad->label;
new_agd->devName= ad->devName;
new_agd->devIdx = ad->devIdx; new_agd->devIdx = ad->devIdx;
new_agd->flags = inputFl ? kInFl : kOutFl;
new_agd->chCnt = chCnt; new_agd->chCnt = chCnt;
new_agd->chIdx = inputFl ? ag->msg.iBufChCnt : ag->msg.oBufChCnt;
new_agd->meterA = mem::allocZ<sample_t>(chCnt);
audio_group_dev_t*& agd = inputFl ? ag->msg.iDevL : ag->msg.oDevL; audio_group_dev_t*& agd = inputFl ? ag->msg.iDevL : ag->msg.oDevL;
@ -700,6 +752,7 @@ namespace cw
{ {
if(( rc = node->getv( if(( rc = node->getv(
"enableFl", p->audioGroupA[i].enableFl, "enableFl", p->audioGroupA[i].enableFl,
"label", p->audioGroupA[i].msg.label,
"id", p->audioGroupA[i].msg.groupId, "id", p->audioGroupA[i].msg.groupId,
"srate", p->audioGroupA[i].msg.srate, "srate", p->audioGroupA[i].msg.srate,
"dspFrameCnt", p->audioGroupA[i].msg.dspFrameCnt )) != kOkRC ) "dspFrameCnt", p->audioGroupA[i].msg.dspFrameCnt )) != kOkRC )
@ -715,6 +768,9 @@ namespace cw
goto errLabel; goto errLabel;
} }
// Lock the mutex so that it is already locked when it is used to block the audio thread
// This avoids having to use logic in the thread callback to lock it on the first entry
// while not locking it on all following entries.
if((rc = mutex::lock(p->audioGroupA[i].mutexH)) != kOkRC ) if((rc = mutex::lock(p->audioGroupA[i].mutexH)) != kOkRC )
{ {
rc = cwLogError(rc,"Error locking audio group mutex."); rc = cwLogError(rc,"Error locking audio group mutex.");
@ -784,6 +840,7 @@ namespace cw
{ {
if(( rc = node->getv( if(( rc = node->getv(
"enableFl", ad->enableFl, "enableFl", ad->enableFl,
"label", ad->label,
"inGroupId", inGroupId, "inGroupId", inGroupId,
"outGroupId", outGroupId, "outGroupId", outGroupId,
"device", ad->devName, "device", ad->devName,
@ -862,14 +919,21 @@ namespace cw
goto errLabel; goto errLabel;
} }
if( iag != nullptr ) if( iag != nullptr )
{
if((rc = _audioGroupAddDevice( iag, true, ad, iChCnt )) != kOkRC ) if((rc = _audioGroupAddDevice( iag, true, ad, iChCnt )) != kOkRC )
goto errLabel; goto errLabel;
}
if( oag != nullptr ) if( oag != nullptr )
{
if((rc = _audioGroupAddDevice( oag, false, ad, oChCnt )) != kOkRC ) if((rc = _audioGroupAddDevice( oag, false, ad, oChCnt )) != kOkRC )
goto errLabel; goto errLabel;
}
// set the device group pointers // set the device group pointers
ad->iGroup = iag; ad->iGroup = iag;
ad->oGroup = oag; ad->oGroup = oag;
@ -903,7 +967,6 @@ namespace cw
rc_t _audioDeviceParseConfig( io_t* p, const object_t* cfg ) rc_t _audioDeviceParseConfig( io_t* p, const object_t* cfg )
{ {
rc_t rc = kOkRC; rc_t rc = kOkRC;
unsigned meterMs = 50;
const object_t* node = nullptr; const object_t* node = nullptr;
// get the audio port node // get the audio port node
@ -911,14 +974,14 @@ namespace cw
return cwLogError(kSyntaxErrorRC,"Unable to locate the 'audio' configuration node."); return cwLogError(kSyntaxErrorRC,"Unable to locate the 'audio' configuration node.");
// get the meterMs value // get the meterMs value
if((rc = node->getv("meterMs", meterMs, "threadTimeOutMs", p->audioThreadTimeOutMs )) != kOkRC ) if((rc = node->getv("meterMs", p->audioMeterCbPeriodMs, "threadTimeOutMs", p->audioThreadTimeOutMs )) != kOkRC )
{ {
rc = cwLogError(kSyntaxErrorRC,"Audio 'meterMs' or 'dspFrameCnt' parse failed."); rc = cwLogError(kSyntaxErrorRC,"Audio 'meterMs' or 'dspFrameCnt' parse failed.");
goto errLabel; goto errLabel;
} }
// initialize the audio buffer // initialize the audio buffer
if((rc = audio::buf::create( p->audioBufH, audio::device::count(p->audioH), meterMs )) != kOkRC ) if((rc = audio::buf::create( p->audioBufH, audio::device::count(p->audioH), p->audioMeterCbPeriodMs )) != kOkRC )
{ {
rc = cwLogError(rc,"Audio device buffer failed."); rc = cwLogError(rc,"Audio device buffer failed.");
goto errLabel; goto errLabel;
@ -987,6 +1050,10 @@ namespace cw
return rc; return rc;
} }
//----------------------------------------------------------------------------------------------------------
//
// UI
//
// This function is called by the websocket with messages comring from a remote UI. // This function is called by the websocket with messages comring from a remote UI.
rc_t _uiCallback( void* cbArg, unsigned wsSessId, ui::opId_t opId, unsigned parentAppId, unsigned uuId, unsigned appId, const ui::value_t* v ) rc_t _uiCallback( void* cbArg, unsigned wsSessId, ui::opId_t opId, unsigned parentAppId, unsigned uuId, unsigned appId, const ui::value_t* v )
@ -1063,10 +1130,87 @@ namespace cw
} }
//----------------------------------------------------------------------------------------------------------
//
// IO
//
rc_t _destroy( io_t* p )
{
rc_t rc = kOkRC;
// stop thread callbacks
if((rc = thread_mach::destroy(p->threadMachH)) != kOkRC )
return rc;
for(unsigned i=0; i<p->serialN; ++i)
serialPortSrv::destroy( p->serialA[i].serialH );
mem::free(p->serialA);
p->serialN = 0;
// TODO: clean up the audio system more systematically
// by first stopping all the devices and then
// reversing the creating process.
_audioDestroy(p);
midi::device::destroy(p->midiH);
for(unsigned i=0; i<p->uiMapN; ++i)
mem::free(const_cast<char*>(p->uiMapA[i].eleName));
mem::release(p->uiMapA);
p->uiMapN = 0;
ui::ws::destroy(p->wsUiH);
// free the cfg object
if( p->cfg != nullptr )
p->cfg->free();
mem::release(p);
return rc;
}
rc_t _audioDeviceParams( handle_t h, unsigned devIdx, unsigned flags, io_t*& pRef, audioDev_t*& adRef, unsigned& audioBufFlagsRef )
{
rc_t rc = kOkRC;
pRef = _handleToPtr(h);
if((adRef = _audioDeviceIndexToRecd(pRef,devIdx)) == nullptr )
rc = kInvalidArgRC;
else
{
audioBufFlagsRef = 0;
if( cwIsFlag(flags,kInFl) )
audioBufFlagsRef += audio::buf::kInFl;
if( cwIsFlag(flags,kOutFl) )
audioBufFlagsRef += audio::buf::kOutFl;
if( cwIsFlag(flags,kEnableFl) )
audioBufFlagsRef += audio::buf::kEnableFl;
}
return rc;
}
} }
} }
//----------------------------------------------------------------------------------------------------------
//
// IO
//
cw::rc_t cw::io::create( cw::rc_t cw::io::create(
handle_t& h, handle_t& h,
@ -1113,6 +1257,9 @@ cw::rc_t cw::io::create(
p->cbFunc = cbFunc; p->cbFunc = cbFunc;
p->cbArg = cbArg; p->cbArg = cbArg;
p->quitFl.store(false); p->quitFl.store(false);
time::get(p->t0);
h.set(p); h.set(p);
@ -1167,11 +1314,14 @@ cw::rc_t cw::io::exec( handle_t h )
{ {
rc_t rc = kOkRC; rc_t rc = kOkRC;
io_t* p = _handleToPtr(h); io_t* p = _handleToPtr(h);
if( p->wsUiH.isValid() ) if( p->wsUiH.isValid() )
rc = ui::ws::exec(p->wsUiH ); rc = ui::ws::exec(p->wsUiH );
// if the application quit flag is set then signal that the main thread needs to exit time::get(p->t0);
if( p->audioMeterDevEnabledN )
_audioDeviceProcessMeters(p);
return rc; return rc;
} }
@ -1280,22 +1430,221 @@ cw::rc_t cw::io::midiDeviceSend( handle_t h, unsigned devIdx, unsigned portIdx,
unsigned cw::io::audioDeviceCount( handle_t h ) unsigned cw::io::audioDeviceCount( handle_t h )
{ {
io_t* p = _handleToPtr(h); io_t* p = _handleToPtr(h);
return audio::device::count(p->audioH); return p->audioDevN;
} }
unsigned cw::io::audioDeviceLabelToIndex( handle_t h, const char* label ) unsigned cw::io::audioDeviceLabelToIndex( handle_t h, const char* label )
{ {
io_t* p = _handleToPtr(h); io_t* p = _handleToPtr(h);
return audio::device::labelToIndex(p->audioH,label); for(unsigned i=0; i<p->audioDevN; ++i)
if( textCompare(label,p->audioDevA[i].label) == 0 )
return p->audioDevA[i].devIdx;
return kInvalidIdx;
//return audio::device::labelToIndex(p->audioH,label);
} }
const char* cw::io::audioDeviceLabel( handle_t h, unsigned devIdx ) const char* cw::io::audioDeviceName( handle_t h, unsigned devIdx )
{ {
io_t* p = _handleToPtr(h); io_t* p = _handleToPtr(h);
assert( devIdx < audioDeviceCount(h) ); audioDev_t* ad;
return audio::device::label(p->audioH,devIdx);
if((ad = _audioDeviceIndexToRecd(p, devIdx )) == nullptr )
return nullptr;
return ad->devName;
} }
double cw::io::audioDeviceSampleRate( handle_t h, unsigned devIdx )
{
io_t* p = _handleToPtr(h);
return audio::device::sampleRate(p->audioH, devIdx );
}
unsigned cw::io::audioDeviceFramesPerCycle( handle_t h, unsigned devIdx )
{
io_t* p = _handleToPtr(h);
audioDev_t* ad;
if((ad = _audioDeviceIndexToRecd(p, devIdx )) == nullptr )
return audio::device::framesPerCycle(p->audioH, devIdx, ad->iGroup != nullptr );
return 0;
}
unsigned cw::io::audioDeviceChannelCount( handle_t h, unsigned devIdx, unsigned inOrOutFlag )
{
io_t* p = _handleToPtr(h);
return audio::device::channelCount(p->audioH, devIdx, inOrOutFlag & kInFl );
}
cw::rc_t cw::io::audioDeviceEnableMeters( handle_t h, unsigned devIdx, unsigned inOutEnaFlags )
{
rc_t rc = kOkRC;
io_t* p = nullptr;
audioDev_t* ad = nullptr;
unsigned audioBufFlags = 0;
if((rc = _audioDeviceParams( h, devIdx, inOutEnaFlags, p, ad, audioBufFlags )) != kOkRC )
rc = cwLogError(rc,"Enable tone failed.");
else
{
bool enaFl = inOutEnaFlags & kEnableFl;
bool enaState0Fl = _audioDeviceIsMeterEnabled(ad, kInFl | kOutFl);
audioBufFlags += audio::buf::kMeterFl;
if( inOutEnaFlags & kInFl )
ad->iagd->flags = cwEnaFlag(ad->iagd->flags,kMeterFl,enaFl);
if( inOutEnaFlags & kOutFl )
ad->oagd->flags = cwEnaFlag(ad->oagd->flags,kMeterFl,enaFl);
audio::buf::setFlag( p->audioBufH, devIdx, kInvalidIdx, audioBufFlags );
bool enaState1Fl= _audioDeviceIsMeterEnabled(ad, kInFl | kOutFl);
if( enaState1Fl and !enaState0Fl )
p->audioMeterDevEnabledN += 1;
else
if( p->audioMeterDevEnabledN > 0 && !enaState1Fl && enaState0Fl )
p->audioMeterDevEnabledN -= 1;
}
if( rc != kOkRC )
rc = cwLogError(rc,"Enable meters failed.");
return rc;
}
const cw::io::sample_t* cw::io::audioDeviceMeters( handle_t h, unsigned devIdx, unsigned& chCntRef, unsigned inOrOutFlag )
{
rc_t rc = kOkRC;
io_t* p = _handleToPtr(h);
sample_t* meterA;
audioDev_t* ad;
if((ad = _audioDeviceIndexToRecd(p,devIdx)) == nullptr )
rc = kInvalidArgRC;
else
{
bool inputFl = inOrOutFlag & kInFl;
audio_group_dev_t* agd = inputFl ? ad->iagd : ad->oagd;
unsigned flags = inputFl ? audio::buf::kInFl : audio::buf::kOutFl;
if( !cwIsFlag(agd->flags,kMeterFl) )
rc = cwLogError(kInvalidArgRC,"The %s meters on device %s are not enabled.", inputFl ? "input" : "output", cwStringNullGuard(ad->label));
else
{
_audioGroupDeviceUpdateMeter(p, agd, flags );
meterA = agd->meterA;
chCntRef = agd->chCnt;
}
}
if( rc != kOkRC )
rc = cwLogError(rc,"Get meters failed.");
return meterA;
}
cw::rc_t cw::io::audioDeviceEnableTone( handle_t h, unsigned devIdx, unsigned inOutEnaFlags )
{
rc_t rc = kOkRC;
io_t* p = nullptr;
audioDev_t* ad = nullptr;
unsigned audioBufFlags = 0;
if((rc = _audioDeviceParams( h, devIdx, inOutEnaFlags, p, ad, audioBufFlags )) != kOkRC )
rc = cwLogError(rc,"Enable tone failed.");
else
{
audioBufFlags += audio::buf::kToneFl;
audio::buf::setFlag( p->audioBufH, devIdx, kInvalidIdx, audioBufFlags );
}
return rc;
}
cw::rc_t cw::io::audioDeviceToneFlags( handle_t h, unsigned devIdx, unsigned inOrOutFlag, bool* toneFlA, unsigned chCnt )
{
rc_t rc = kOkRC;
io_t* p = nullptr;
audioDev_t* ad = nullptr;
unsigned audioBufFlags = 0;
if((rc = _audioDeviceParams( h, devIdx, inOrOutFlag, p, ad, audioBufFlags )) != kOkRC )
rc = cwLogError(rc,"Enable tone failed.");
else
{
audioBufFlags += audio::buf::kToneFl;
audio::buf::toneFlags( p->audioBufH, devIdx, audioBufFlags, toneFlA, chCnt );
}
return rc;
}
cw::rc_t cw::io::audioDeviceEnableMute( handle_t h, unsigned devIdx, unsigned inOutEnaFlags )
{
rc_t rc = kOkRC;
io_t* p = nullptr;
audioDev_t* ad = nullptr;
unsigned audioBufFlags = 0;
if((rc = _audioDeviceParams( h, devIdx, inOutEnaFlags, p, ad, audioBufFlags )) != kOkRC )
rc = cwLogError(rc,"Enable mute failed.");
else
{
audioBufFlags += audio::buf::kMuteFl;
audio::buf::setFlag( p->audioBufH, devIdx, kInvalidIdx, audioBufFlags );
}
return rc;
}
cw::rc_t cw::io::audioDeviceMuteFlags( handle_t h, unsigned devIdx, unsigned inOrOutFlag, bool* muteFlA, unsigned chCnt )
{
rc_t rc = kOkRC;
io_t* p = nullptr;
audioDev_t* ad = nullptr;
unsigned audioBufFlags = 0;
if((rc = _audioDeviceParams( h, devIdx, inOrOutFlag, p, ad, audioBufFlags )) != kOkRC )
rc = cwLogError(rc,"Enable mute failed.");
else
{
audioBufFlags += audio::buf::kMuteFl;
audio::buf::muteFlags( p->audioBufH, devIdx, audioBufFlags, muteFlA, chCnt );
}
return rc;
}
cw::rc_t cw::io::audioDeviceSetGain( handle_t h, unsigned devIdx, unsigned inOrOutFlags, double gain )
{
rc_t rc = kOkRC;
io_t* p = nullptr;
audioDev_t* ad = nullptr;
unsigned audioBufFlags = 0;
if((rc = _audioDeviceParams( h, devIdx, inOrOutFlags, p, ad, audioBufFlags )) != kOkRC )
rc = cwLogError(rc,"Set gain failed.");
else
{
audio::buf::setGain( p->audioBufH, devIdx, kInvalidIdx, audioBufFlags, gain );
}
return rc;
}
//---------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------
@ -1361,6 +1710,15 @@ unsigned cw::io::uiFindElementUuId( handle_t h, const char* eleName )
return kInvalidId; return kInvalidId;
} }
unsigned cw::io::uiFindElementUuId( handle_t h, unsigned appId )
{
rc_t rc;
ui::handle_t uiH;
if((rc = _handleToUiHandle(h,uiH)) == kOkRC )
return ui::findElementUuId(uiH, appId );
return kInvalidId;
}
cw::rc_t cw::io::uiCreateFromObject( handle_t h, const object_t* o, unsigned wsSessId, unsigned parentUuId, const char* eleName) cw::rc_t cw::io::uiCreateFromObject( handle_t h, const object_t* o, unsigned wsSessId, unsigned parentUuId, const char* eleName)
{ {
rc_t rc; rc_t rc;
@ -1541,3 +1899,57 @@ cw::rc_t cw::io::uiRegisterAppIdMap( handle_t h, const ui::appIdMap_t* map, uns
return rc; return rc;
} }
cw::rc_t cw::io::uiSendValue( handle_t h, unsigned wsSessId, unsigned uuId, bool value )
{
rc_t rc;
ui::handle_t uiH;
if((rc = _handleToUiHandle(h,uiH)) == kOkRC )
rc = ui::sendValueBool(uiH, wsSessId, uuId, value );
return rc;
}
cw::rc_t cw::io::uiSendValue( handle_t h, unsigned wsSessId, unsigned uuId, int value )
{
rc_t rc;
ui::handle_t uiH;
if((rc = _handleToUiHandle(h,uiH)) == kOkRC )
rc = ui::sendValueInt(uiH, wsSessId, uuId, value );
return rc;
}
cw::rc_t cw::io::uiSendValue( handle_t h, unsigned wsSessId, unsigned uuId, unsigned value )
{
rc_t rc;
ui::handle_t uiH;
if((rc = _handleToUiHandle(h,uiH)) == kOkRC )
rc = ui::sendValueUInt(uiH, wsSessId, uuId, value );
return rc;
}
cw::rc_t cw::io::uiSendValue( handle_t h, unsigned wsSessId, unsigned uuId, float value )
{
rc_t rc;
ui::handle_t uiH;
if((rc = _handleToUiHandle(h,uiH)) == kOkRC )
rc = ui::sendValueFloat(uiH, wsSessId, uuId, value );
return rc;
}
cw::rc_t cw::io::uiSendValue( handle_t h, unsigned wsSessId, unsigned uuId, double value )
{
rc_t rc;
ui::handle_t uiH;
if((rc = _handleToUiHandle(h,uiH)) == kOkRC )
rc = ui::sendValueDouble(uiH, wsSessId, uuId, value );
return rc;
}
cw::rc_t cw::io::uiSendValue( handle_t h, unsigned wsSessId, unsigned uuId, const char* value )
{
rc_t rc;
ui::handle_t uiH;
if((rc = _handleToUiHandle(h,uiH)) == kOkRC )
rc = ui::sendValueString(uiH, wsSessId, uuId, value );
return rc;
}

68
cwIo.h
View File

@ -15,11 +15,22 @@ namespace cw
typedef handle<struct io_str> handle_t; typedef handle<struct io_str> handle_t;
enum
{
kDisableFl = 0x00,
kEnableFl = 0x01,
kInFl = 0x02,
kOutFl = 0x04,
kMeterFl = 0x08
};
enum enum
{ {
kSerialTId, kSerialTId,
kMidiTId, kMidiTId,
kAudioTId, kAudioTId,
kAudioMeterTId,
kSockTid, kSockTid,
kWebSockTId, kWebSockTId,
kUiTId kUiTId
@ -41,21 +52,28 @@ namespace cw
typedef struct audio_group_dev_str typedef struct audio_group_dev_str
{ {
const char* name; // Audio device name const char* label; // User label
const char* devName; // Audio device name
unsigned devIdx; // Audio device index unsigned devIdx; // Audio device index
unsigned flags; // kInFl | kOutFl | kMeterFl
unsigned chIdx; // First channel of this device in i/oBufArray
unsigned chCnt; // Count of audio channels on this device unsigned chCnt; // Count of audio channels on this device
unsigned cbCnt; // Count of device driver callbacks unsigned cbCnt; // Count of device driver callbacks
sample_t* meterA; // Meter values for this device.
std::atomic_uint readyCnt;// Used internally do not read or write. std::atomic_uint readyCnt;// Used internally do not read or write.
struct audio_group_dev_str* link; // struct audio_group_dev_str* link; //
} audio_group_dev_t; } audio_group_dev_t;
typedef struct audio_msg_str typedef struct audio_msg_str
{ {
unsigned groupId; // Unique group id const char* label; // User provided label
unsigned groupId; // User provided group id
double srate; // Group sample rate. double srate; // Group sample rate.
unsigned dspFrameCnt; // Count of samples in each buffer pointed to by iBufArray[] and oBufArray[] unsigned dspFrameCnt; // Count of samples in each buffer pointed to by iBufArray[] and oBufArray[]
sample_t** iBufArray; // Array of ptrs to buffers of size bufSmpCnt sample_t** iBufArray; // Array of iBufChCnt ptrs to buffers of size bufSmpCnt
unsigned iBufChCnt; // Count of elements in iBufArray[] unsigned iBufChCnt; // Count of elements in iBufArray[]
time::spec_t* iTimeStampPtr; // time::spec_t* iTimeStampPtr; //
audio_group_dev_t* iDevL; // Linked list of input devices which map directly to channels in iBufArray[] audio_group_dev_t* iDevL; // Linked list of input devices which map directly to channels in iBufArray[]
@ -95,6 +113,7 @@ namespace cw
serial_msg_t* serial; serial_msg_t* serial;
midi_msg_t* midi; midi_msg_t* midi;
audio_msg_t* audio; audio_msg_t* audio;
audio_group_dev_t* audioGroupDev; // audioMeterTId
socket_msg_t* sock; socket_msg_t* sock;
ui_msg_t ui; ui_msg_t ui;
} u; } u;
@ -150,30 +169,17 @@ namespace cw
unsigned audioDeviceCount( handle_t h ); unsigned audioDeviceCount( handle_t h );
unsigned audioDeviceLabelToIndex( handle_t h, const char* label ); unsigned audioDeviceLabelToIndex( handle_t h, const char* label );
const char* audioDeviceLabel( handle_t h, unsigned devIdx ); const char* audioDeviceName( handle_t h, unsigned devIdx );
rc_t audioDeviceSetup(
handle_t h,
unsigned devIdx,
double srate,
unsigned framesPerDeviceCycle,
unsigned devBufBufN,
unsigned framesPerDspCycle,
unsigned inputFlags,
unsigned outputFlags );
unsigned audioDeviceChannelCount( handle_t h, unsigned devIdx, unsigned dirFl );
double audioDeviceSampleRate( handle_t h, unsigned devIdx ); double audioDeviceSampleRate( handle_t h, unsigned devIdx );
unsigned audioDeviceFramesPerCycle( handle_t h, unsigned devIdx ); unsigned audioDeviceFramesPerCycle( handle_t h, unsigned devIdx );
unsigned audioDeviceChannelFlags( handle_t h, unsigned devIdx, unsigned chIdx, unsigned dirFl ); unsigned audioDeviceChannelCount( handle_t h, unsigned devIdx, unsigned inOrOutFlag );
rc_t audioDeviceChannelSetFlags(handle_t h, unsigned devidx, unsigned chIdx, unsigned dirFl, unsigned flags ); rc_t audioDeviceEnableMeters( handle_t h, unsigned devIdx, unsigned inOutEnaFlags );
sample_t audioDeviceChannelMeter( handle_t h, unsigned devIdx, unsigned chIdx, unsigned dirFl ); const sample_t* audioDeviceMeters( handle_t h, unsigned devIdx, unsigned& chCntRef, unsigned inOrOutFlag );
rc_t audioDeviceChannelSetGain( handle_t h, unsigned devIdx, unsigned chIdx, unsigned dirFl, double gain ); rc_t audioDeviceEnableTone( handle_t h, unsigned devidx, unsigned inOutEnaFlags );
double audioDeviceChannelGain( handle_t h, unsigned devIdx, unsigned chIdx, unsigned dirFl ); rc_t audioDeviceToneFlags( handle_t h, unsigned devIdx, unsigned inOrOutFlag, bool* toneFlA, unsigned chCnt );
rc_t audioDeviceEnableMute( handle_t h, unsigned devidx, unsigned inOutEnaFlags );
unsigned audioGroupCount( handle_t h ); rc_t audioDeviceMuteFlags( handle_t h, unsigned devIdx, unsigned inOrOutFlag, bool* muteFlA, unsigned chCnt );
const char* audioGroupLabel( handle_t h, unsigned groupIdx ); rc_t audioDeviceSetGain( handle_t h, unsigned devidx, unsigned inOrOutFlags, double gain );
unsigned audioGroupId( handle_t h, unsigned groupIdx );
rc_t audioGroupSetId( handle_t h, unsigned groupIdx, unsigned groupId );
//---------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------
@ -220,6 +226,7 @@ namespace cw
// Return the uuid of the first matching 'eleName'. // Return the uuid of the first matching 'eleName'.
unsigned uiFindElementUuId( handle_t h, const char* eleName ); unsigned uiFindElementUuId( handle_t h, const char* eleName );
unsigned uiFindElementUuId( handle_t h, unsigned appId );
rc_t uiCreateFromObject( handle_t h, const object_t* o, unsigned wsSessId, unsigned parentUuId=kInvalidId, const char* eleName=nullptr); rc_t uiCreateFromObject( handle_t h, const object_t* o, unsigned wsSessId, unsigned parentUuId=kInvalidId, const char* eleName=nullptr);
rc_t uiCreateFromFile( handle_t h, const char* fn, unsigned wsSessId, unsigned parentUuId=kInvalidId); rc_t uiCreateFromFile( handle_t h, const char* fn, unsigned wsSessId, unsigned parentUuId=kInvalidId);
@ -253,9 +260,14 @@ namespace cw
// Register parent/child/name app id's // Register parent/child/name app id's
rc_t uiRegisterAppIdMap( handle_t h, const ui::appIdMap_t* map, unsigned mapN ); rc_t uiRegisterAppIdMap( handle_t h, const ui::appIdMap_t* map, unsigned mapN );
// Send a value to the user interface // Send a value from the application to the UI.
template< typename T > rc_t uiSendValue( handle_t h, unsigned wsSessId, unsigned uuId, bool value );
rc_t uiValueToUI( handle_t h, unsigned wsSessId, unsigned uuId, const T& value ); rc_t uiSendValue( handle_t h, unsigned wsSessId, unsigned uuId, int value );
rc_t uiSendValue( handle_t h, unsigned wsSessId, unsigned uuId, unsigned value );
rc_t uiSendValue( handle_t h, unsigned wsSessId, unsigned uuId, float value );
rc_t uiSendValue( handle_t h, unsigned wsSessId, unsigned uuId, double value );
rc_t uiSendValue( handle_t h, unsigned wsSessId, unsigned uuId, const char* value );
} }

View File

@ -18,7 +18,7 @@ namespace cw
enum enum
{ {
// Resource Based elements // Resource Based elements
kPanelDivId, kPanelDivId = 1000,
kQuitBtnId, kQuitBtnId,
kPanelBtn1Id, kPanelBtn1Id,
kPanelCheck1Id, kPanelCheck1Id,
@ -28,7 +28,20 @@ namespace cw
kSelId, kSelId,
kOpt1Id, kOpt1Id,
kOpt2Id, kOpt2Id,
kOpt3Id kOpt3Id,
kInMeterDivId,
kOutMeterDivId,
kInMeterBaseId = 2000, kInMeterMaxId = 2999,
kInGainBaseId = 3000, kInGainMaxId = 3999,
kInToneBaseId = 4000, kInToneMaxId = 4999,
kInMuteBaseId = 5000, kInMuteMaxId = 5999,
kOutMeterBaseId = 6000, kOutMeterMaxId= 6999,
kOutGainBaseId = 7000, kOutGainMaxId = 7999,
kOutToneBaseId = 8000, kOutToneMaxId = 8999,
kOutMuteBaseId = 9000, kOutMuteMaxId = 9999
}; };
// Application Id's for the resource based UI elements. // Application Id's for the resource based UI elements.
@ -36,6 +49,7 @@ namespace cw
{ {
{ ui::kRootAppId, kPanelDivId, "panelDivId" }, { ui::kRootAppId, kPanelDivId, "panelDivId" },
{ kPanelDivId, kQuitBtnId, "quitBtnId" }, { kPanelDivId, kQuitBtnId, "quitBtnId" },
/*
{ kPanelDivId, kPanelBtn1Id, "myBtn1Id" }, { kPanelDivId, kPanelBtn1Id, "myBtn1Id" },
{ kPanelDivId, kPanelCheck1Id, "myCheck1Id" }, { kPanelDivId, kPanelCheck1Id, "myCheck1Id" },
{ kPanelDivId, kPanelBtn2Id, "myBtn2Id" }, { kPanelDivId, kPanelBtn2Id, "myBtn2Id" },
@ -44,7 +58,8 @@ namespace cw
{ kPanelDivId, kSelId, "mySel" }, { kPanelDivId, kSelId, "mySel" },
{ kSelId, kOpt1Id, "myOpt1" }, { kSelId, kOpt1Id, "myOpt1" },
{ kSelId, kOpt2Id, "myOpt2" }, { kSelId, kOpt2Id, "myOpt2" },
{ kSelId, kOpt3Id, "myOpt3" }, { SelId, kOpt3Id, "myOpt3" },
*/
}; };
unsigned mapN = sizeof(mapA)/sizeof(mapA[0]); unsigned mapN = sizeof(mapA)/sizeof(mapA[0]);
@ -55,9 +70,67 @@ namespace cw
handle_t ioH; handle_t ioH;
} app_t; } app_t;
rc_t _uiCreateMeters( app_t* app, unsigned wsSessId, unsigned chCnt, bool inputFl )
{
rc_t rc = kOkRC;
unsigned parentUuId = ui::kRootAppId;
unsigned divUuId = kInvalidId;
unsigned titleUuId = kInvalidId;
const char* title = inputFl ? "Input" : "Output";
const char* divEleName = inputFl ? "inMeterId" : "outMeterId";
unsigned divAppId = inputFl ? kInMeterDivId : kOutMeterDivId;
unsigned baseMeterId = inputFl ? kInMeterBaseId : kOutMeterBaseId;
unsigned baseToneId = inputFl ? kInToneBaseId : kOutToneBaseId;
unsigned baseMuteId = inputFl ? kInMuteBaseId : kOutMuteBaseId;
unsigned baseGainId = inputFl ? kInGainBaseId : kOutGainBaseId;
unsigned colUuId;
unsigned uuid;
uiCreateTitle(app->ioH, titleUuId, wsSessId, parentUuId, nullptr, kInvalidId, nullptr, title );
uiCreateDiv( app->ioH, divUuId, wsSessId, parentUuId, divEleName, divAppId, "uiRow", nullptr );
uiCreateDiv( app->ioH, colUuId, wsSessId, divUuId, nullptr, kInvalidId, "uiCol", nullptr );
uiCreateTitle(app->ioH, uuid, wsSessId, colUuId, nullptr, kInvalidId, nullptr, "Tone" );
uiCreateTitle(app->ioH, uuid, wsSessId, colUuId, nullptr, kInvalidId, nullptr, "Mute" );
uiCreateTitle(app->ioH, uuid, wsSessId, colUuId, nullptr, kInvalidId, nullptr, "Gain" );
uiCreateTitle(app->ioH, uuid, wsSessId, colUuId, nullptr, kInvalidId, nullptr, "Meter" );
for(unsigned i=0; i<chCnt; ++i)
{
unsigned chLabelN = 32;
char chLabel[ chLabelN+1 ];
snprintf(chLabel,chLabelN,"%i",i+1);
uiCreateDiv( app->ioH, colUuId, wsSessId, divUuId, nullptr, kInvalidId, "uiCol", chLabel );
uiCreateCheck(app->ioH, uuid, wsSessId, colUuId, nullptr, baseToneId + i, "checkClass", nullptr, false );
uiCreateCheck(app->ioH, uuid, wsSessId, colUuId, nullptr, baseMuteId + i, "checkClass", nullptr, false );
uiCreateNumb( app->ioH, uuid, wsSessId, colUuId, nullptr, baseGainId + i, "floatClass", nullptr, 0.0, 3.0, 0.001, 3, 0 );
uiCreateNumb( app->ioH, uuid, wsSessId, colUuId, nullptr, baseMeterId + i, "floatClass", nullptr, -100.0, 100, 1, 2, 0 );
}
return rc;
}
rc_t _uiCreateMeterPanel( app_t* app, unsigned wsSessId )
{
unsigned devIdx = audioDeviceLabelToIndex( app->ioH, "main");
unsigned iChCnt = audioDeviceChannelCount( app->ioH, devIdx, kInFl);
unsigned oChCnt = audioDeviceChannelCount( app->ioH, devIdx, kOutFl);
_uiCreateMeters( app, wsSessId, iChCnt, true );
_uiCreateMeters( app, wsSessId, oChCnt, false );
return kOkRC;
}
rc_t _onUiInit(app_t* app, const ui_msg_t& m ) rc_t _onUiInit(app_t* app, const ui_msg_t& m )
{ {
rc_t rc = kOkRC; rc_t rc = kOkRC;
_uiCreateMeterPanel(app,m.wsSessId);
return rc; return rc;
} }
@ -71,6 +144,28 @@ namespace cw
io::stop( app->ioH ); io::stop( app->ioH );
break; break;
} }
if( kInMeterBaseId <= m.appId && m.appId < kInMeterMaxId )
{
}
else
if( kInGainBaseId <= m.appId && m.appId < kInGainMaxId )
{
}
else
if( kInToneBaseId <= m.appId && m.appId < kInToneMaxId )
{
}
else
if( kInMuteBaseId <= m.appId && m.appId < kInMuteMaxId )
{
}
return rc; return rc;
} }
@ -133,17 +228,29 @@ namespace cw
{ {
// the input channel is not disabled // the input channel is not disabled
if( m.iBufArray[i] != NULL ) if( m.iBufArray[i] != NULL )
memcpy(m.oBufArray[i], m.iBufArray[i], byteCnt); {
for(unsigned j=0; j<m.dspFrameCnt; ++j )
m.oBufArray[i][j] = m.iBufArray[i][j];
//memcpy(m.oBufArray[i],m.iBufArray[i],byteCnt);
}
else else
{
// the input channel is disabled but the output is not - so fill the output with zeros // the input channel is disabled but the output is not - so fill the output with zeros
memset(m.oBufArray[i], 0, byteCnt); memset(m.oBufArray[i], 0, byteCnt);
} }
}
return rc; return rc;
} }
rc_t _audioMeterCb( app_t* app, const audio_group_dev_t* agd )
{
unsigned baseAppId = cwIsFlag(agd->flags,kInFl) ? kInMeterBaseId : kOutMeterBaseId;
for(unsigned i=0; i<agd->chCnt; ++i)
uiSendValue( app->ioH, kInvalidId, uiFindElementUuId(app->ioH,baseAppId+i), agd->meterA[i] );
return kOkRC;
}
// The main application callback // The main application callback
rc_t testCb( void* arg, const msg_t* m ) rc_t testCb( void* arg, const msg_t* m )
{ {
@ -163,6 +270,11 @@ namespace cw
rc = audioCb(app,*m->u.audio); rc = audioCb(app,*m->u.audio);
break; break;
case kAudioMeterTId:
if( m->u.audioGroupDev != nullptr )
rc = _audioMeterCb(app,m->u.audioGroupDev);
break;
case kSockTid: case kSockTid:
break; break;
@ -197,10 +309,9 @@ namespace cw
} }
for(unsigned i=0; i<audioDeviceCount(h); ++i) for(unsigned i=0; i<audioDeviceCount(h); ++i)
printf("audio: %s\n", audioDeviceLabel(h,i)); printf("audio: %s\n", audioDeviceName(h,i));
} }
} }
} }
@ -219,6 +330,17 @@ cw::rc_t cw::io::test( const object_t* cfg )
cwLogError(rc,"Test app start failed."); cwLogError(rc,"Test app start failed.");
else else
{ {
unsigned devIdx = audioDeviceLabelToIndex(app.ioH, "main");
if( devIdx == kInvalidIdx )
cwLogError(kOpFailRC, "Unable to locate the requested audio device.");
else
{
audioDeviceEnableMeters( app.ioH, devIdx, kInFl | kOutFl | kEnableFl );
//audioDeviceEnableTone( app.ioH, devIdx, kOutFl | kEnableFl );
}
while( !isShuttingDown(app.ioH)) while( !isShuttingDown(app.ioH))
{ {
exec(app.ioH); exec(app.ioH);