Added audio_meter and cwIo and flow support for metering.

Also updated spec_dist,pv_anl, pv_syn to use fd_real_t.
This commit is contained in:
kevin 2023-01-05 07:30:57 -05:00
parent 9782c1b3c9
commit 11636a3f43
5 changed files with 438 additions and 95 deletions

View File

@ -289,11 +289,8 @@ cw::rc_t cw::dsp::recorder::destroy( obj_t*& pRef)
{
obj_t* p = pRef;
if( p != nullptr )
{
mem::release(p->buf);
mem::release(p);
}
mem::release(p->buf);
mem::release(p);
pRef = nullptr;
return kOkRC;
@ -363,3 +360,132 @@ cw::rc_t cw::dsp::recorder::write( obj_t* p, const char* fname )
return rc;
}
//----------------------------------------------------------------------------------------------------------------
// Audio Meter
//
namespace cw {
namespace dsp {
namespace audio_meter {
sample_t _sum_square( const sample_t* v, unsigned vn, bool& clipFlRef )
{
sample_t sum = 0;
for(unsigned i=0; i<vn; ++i)
{
sample_t x = v[i]*v[i];
sum += x;
clipFlRef = x > 1.0;
}
return sum;
}
}
}
}
cw::rc_t cw::dsp::audio_meter::create( obj_t*& p, real_t srate, real_t maxWndMs, real_t wndMs, real_t peakThreshDb )
{
rc_t rc = kOkRC;
if( maxWndMs < wndMs )
{
cwLogWarning("Audio meter Max. window length (%f ms) is less than requested window length (%f ms). Setting max window length to %f ms.",maxWndMs,wndMs,wndMs);
maxWndMs = wndMs;
}
p = mem::allocZ<obj_t>();
p->maxWndMs = maxWndMs;
p->maxWndSmpN = (unsigned)((maxWndMs * srate)/1000.0);
p->wndV = mem::allocZ<sample_t>(p->maxWndSmpN);
p->srate = srate;
p->peakThreshDb = peakThreshDb;
p->wi = 0;
set_window_ms( p, wndMs );
reset(p);
return rc;
}
cw::rc_t cw::dsp::audio_meter::destroy( obj_t*& pp )
{
rc_t rc = kOkRC;
mem::release(pp->wndV);
mem::release(pp);
return rc;
}
cw::rc_t cw::dsp::audio_meter::exec( obj_t* p, const sample_t* xV, unsigned xN )
{
rc_t rc = kOkRC;
unsigned n = 0;
// copy the incoming audio samples to the buffer
while( xN )
{
unsigned n0 = std::min( p->maxWndSmpN - p->wi, xN );
vop::copy(p->wndV + p->wi, xV + n, n0 );
n += n0;
xN -= n0;
p->wi = (p->wi + n0) % p->maxWndSmpN;
}
// get the starting and ending locations of the RMS sub-buffers
unsigned i0 = 0, i1 = 0;
unsigned n0 = 0, n1 = 0;
if( p->wi >= p->wndSmpN )
{
i0 = p->wi - p->wndSmpN;
n0 = p->wndSmpN;
}
else
{
i1 = 0;
n1 = p->wi;
n0 = p->wndSmpN - n1;
i0 = p->maxWndSmpN - n0;
}
// calc the squared-sum of the RMS buffers
sample_t sum = _sum_square(p->wndV + i0, n0, p->clipFl);
if( n1 )
sum += _sum_square(p->wndV + i1, n1, p->clipFl );
p->outLin = std::sqrt( sum / (n0+n1) ); // linear RMS
p->outDb = ampl_to_db(p->outLin); // RMS dB
p->peakFl = p->outDb > p->peakThreshDb; // set peak flag
p->clipFl = vop::max(xV,xN) >= 1.0; // set clip flag
p->peakCnt += p->peakFl ? 1 : 0; // count peak violations
return rc;
}
void cw::dsp::audio_meter::reset( obj_t* p )
{
p->peakFl = false;
p->clipFl = false;
p->peakCnt = 0;
p->clipCnt = 0;
}
void cw::dsp::audio_meter::set_window_ms( obj_t* p, real_t wndMs )
{
unsigned wndSmpN = (unsigned)((wndMs * p->srate)/1000.0);
if( wndSmpN <= p->maxWndSmpN )
p->wndSmpN = wndSmpN;
else
{
cwLogWarning("The audio meter window length (%f ms) exceeds the max. window length (%f ms). The window length was reduced to (%f ms).",wndMs,p->maxWndMs,p->maxWndMs);
p->wndSmpN = p->maxWndSmpN;
}
}

View File

@ -96,9 +96,35 @@ namespace cw
rc_t exec( obj_t* p, const sample_t* buf, unsigned chN, unsigned frameN );
rc_t exec( obj_t* p, const sample_t* chA[], unsigned chN, unsigned frameN );
rc_t write( obj_t* p, const char* fname );
rc_t write( obj_t* p, const char* fname );
}
namespace audio_meter
{
typedef struct
{
unsigned maxWndMs;
unsigned maxWndSmpN;
unsigned wndSmpN;
sample_t* wndV;
real_t srate;
real_t peakThreshDb;
real_t outLin;
real_t outDb;
bool peakFl;
bool clipFl;
unsigned peakCnt;
unsigned clipCnt;
unsigned wi;
} obj_t;
}
rc_t create( obj_t*& p, real_t srate, real_t maxWndMs, real_t wndMs, real_t peakThreshDb );
rc_t destroy( obj_t*& pp );
rc_t exec( obj_t* p, const sample_t* x, unsigned n );
void reset( obj_t* p );
void set_window_ms( obj_t* p, real_t wndMs );
}
}
}

View File

@ -1437,7 +1437,7 @@ namespace cw
//
namespace pv_analysis
{
typedef struct dsp::pv_anl::obj_str<sample_t> pv_t;
typedef struct dsp::pv_anl::obj_str<sample_t,fd_real_t> pv_t;
enum {
kInPId,
@ -1477,9 +1477,9 @@ namespace cw
inst->pvN = srcBuf->chN;
inst->pvA = mem::allocZ<pv_t*>( inst->pvN ); // allocate pv channel array
const sample_t* magV[ srcBuf->chN ];
const sample_t* phsV[ srcBuf->chN ];
const sample_t* hzV[ srcBuf->chN ];
const fd_real_t* magV[ srcBuf->chN ];
const fd_real_t* phsV[ srcBuf->chN ];
const fd_real_t* hzV[ srcBuf->chN ];
unsigned maxBinNV[ srcBuf->chN ];
unsigned binNV[ srcBuf->chN ];
unsigned hopNV[ srcBuf->chN ];
@ -1625,7 +1625,7 @@ namespace cw
//
namespace pv_synthesis
{
typedef struct dsp::pv_syn::obj_str<sample_t> pv_t;
typedef struct dsp::pv_syn::obj_str<sample_t,fd_real_t> pv_t;
enum {
kInPId,
@ -1751,7 +1751,7 @@ namespace cw
//
namespace spec_dist
{
typedef struct dsp::spec_dist::obj_str<sample_t,sample_t> spec_dist_t;
typedef struct dsp::spec_dist::obj_str<fd_real_t,fd_real_t> spec_dist_t;
enum
{
@ -1794,9 +1794,9 @@ namespace cw
inst->sdN = srcBuf->chN;
inst->sdA = mem::allocZ<spec_dist_t*>( inst->sdN );
const sample_t* magV[ srcBuf->chN ];
const sample_t* phsV[ srcBuf->chN ];
const sample_t* hzV[ srcBuf->chN ];
const fd_real_t* magV[ srcBuf->chN ];
const fd_real_t* phsV[ srcBuf->chN ];
const fd_real_t* hzV[ srcBuf->chN ];
//if((rc = var_register(ctx, kAnyChIdx, kInPId, "in")) != kOkRC )
// goto errLabel;
@ -1864,6 +1864,7 @@ namespace cw
double val = 0;
spec_dist_t* sd = inst->sdA[ var->chIdx ];
switch( var->vid )
{
case kBypassPId: rc = var_get( var, val ); sd->bypassFl = val; break;
@ -1877,8 +1878,8 @@ namespace cw
cwLogWarning("Unhandled variable id '%i' on instance: %s.", var->vid, ctx->label );
}
//printf("%i sd: ceil:%f expo:%f thresh:%f upr:%f lwr:%f mix:%f : rc:%i val:%f\n",
// var->chIdx,sd->ceiling, sd->expo, sd->thresh, sd->uprSlope, sd->lwrSlope, sd->mix, rc, val );
//printf("%i sd: ceil:%f expo:%f thresh:%f upr:%f lwr:%f mix:%f : rc:%i val:%f var:%s \n",
// var->chIdx,sd->ceiling, sd->expo, sd->thresh, sd->uprSlope, sd->lwrSlope, sd->mix, rc, val, var->label );
}
return rc;
@ -2596,8 +2597,164 @@ namespace cw
}
//------------------------------------------------------------------------------------------------------------------
//
// audio_meter
//
namespace audio_meter
{
enum
{
kInPId,
kDbFlPId,
kWndMsPId,
kPeakDbPId,
kOutPId,
kPeakFlPId,
kClipFlPId
};
typedef dsp::audio_meter::obj_t audio_meter_t;
typedef struct
{
audio_meter_t** mtrA;
unsigned mtrN;
} inst_t;
}
}
rc_t create( instance_t* ctx )
{
rc_t rc = kOkRC;
const abuf_t* srcBuf = nullptr; //
inst_t* inst = mem::allocZ<inst_t>();
ctx->userPtr = inst;
// verify that a source buffer exists
if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",srcBuf )) != kOkRC )
{
rc = cwLogError(rc,"The instance '%s' does not have a valid input connection.",ctx->label);
goto errLabel;
}
else
{
// allocate channel array
inst->mtrN = srcBuf->chN;
inst->mtrA = mem::allocZ<audio_meter_t*>( inst->mtrN );
// create a audio_meter object for each input channel
for(unsigned i=0; i<srcBuf->chN; ++i)
{
real_t wndMs, peakThreshDb;
bool dbFl;
// get the audio_meter variable values
if((rc = var_register_and_get( ctx, i,
kDbFlPId, "dbFl", dbFl,
kWndMsPId, "wndMs", wndMs,
kPeakDbPId, "peakDb", peakThreshDb )) != kOkRC )
{
goto errLabel;
}
// get the audio_meter variable values
if((rc = var_register( ctx, i,
kOutPId, "out",
kPeakFlPId, "peakFl",
kClipFlPId, "clipFl" )) != kOkRC )
{
goto errLabel;
}
unsigned maxWndMs = std::max(wndMs,1000.0f);
// create the audio_meter instance
if((rc = dsp::audio_meter::create( inst->mtrA[i], srcBuf->srate, maxWndMs, wndMs, peakThreshDb)) != kOkRC )
{
rc = cwLogError(kOpFailRC,"The 'audio_meter' object create failed on the instance '%s'.",ctx->label);
goto errLabel;
}
}
}
errLabel:
return rc;
}
rc_t destroy( instance_t* ctx )
{
rc_t rc = kOkRC;
inst_t* inst = (inst_t*)ctx->userPtr;
for(unsigned i=0; i<inst->mtrN; ++i)
destroy(inst->mtrA[i]);
mem::release(inst->mtrA);
mem::release(inst);
return rc;
}
rc_t value( instance_t* ctx, variable_t* var )
{
return kOkRC;
}
rc_t exec( instance_t* ctx )
{
rc_t rc = kOkRC;
inst_t* inst = (inst_t*)ctx->userPtr;
const abuf_t* srcBuf = nullptr;
unsigned chN = 0;
// get the src buffer
if((rc = var_get(ctx,kInPId, kAnyChIdx, srcBuf )) != kOkRC )
goto errLabel;
chN = std::min(srcBuf->chN,inst->mtrN);
for(unsigned i=0; i<chN; ++i)
{
dsp::audio_meter::exec( inst->mtrA[i], srcBuf->buf + i*srcBuf->frameN, srcBuf->frameN );
var_set(ctx, kOutPId, i, inst->mtrA[i]->outDb );
var_set(ctx, kPeakFlPId, i, inst->mtrA[i]->peakFl );
var_set(ctx, kClipFlPId, i, inst->mtrA[i]->clipFl );
}
errLabel:
return rc;
}
rc_t report( instance_t* ctx )
{
rc_t rc = kOkRC;
inst_t* inst = (inst_t*)ctx->userPtr;
for(unsigned i=0; i<inst->mtrN; ++i)
{
audio_meter_t* c = inst->mtrA[i];
cwLogInfo("%s ch:%i : %f %f db : pk:%i %i clip:%i %i ",
ctx->label,i,c->outLin,c->outDb,c->peakFl,c->peakCnt,c->clipFl,c->clipCnt );
}
return rc;
}
class_members_t members = {
.create = create,
.destroy = destroy,
.value = value,
.exec = exec,
.report = report
};
}
} // flow
} // cw

View File

@ -20,5 +20,6 @@ namespace cw
namespace audio_delay { extern class_members_t members; }
namespace dc_filter { extern class_members_t members; }
namespace balance { extern class_members_t members; }
namespace audio_meter { extern class_members_t members; }
}
}

183
cwIo.cpp
View File

@ -82,6 +82,7 @@ namespace cw
typedef struct audioDev_str
{
bool enableFl; // True if this device was enabled by the user
bool meterFl; // True if meters are enabled on this device
const char* label; // User label
unsigned userId; // User id
char* devName; // System device name
@ -827,6 +828,8 @@ namespace cw
// Audio
//
// Start or stop all the audio devices in p->audioDevA[]
rc_t _audioDeviceStartStop( io_t* p, bool startFl )
{
@ -1123,6 +1126,68 @@ namespace cw
return nullptr;
}
rc_t _audioDeviceParams( io_t* p, unsigned devIdx, unsigned flags, audioDev_t*& adRef, unsigned& audioBufFlagsRef )
{
rc_t rc = kOkRC;
if((adRef = _audioDeviceIndexToRecd(p,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;
}
rc_t _audioDeviceEnableMeter( io_t* p, unsigned devIdx, unsigned inOutEnaFlags )
{
rc_t rc = kOkRC;
audioDev_t* ad = nullptr;
unsigned audioBufFlags = 0;
if((rc = _audioDeviceParams( p, devIdx, inOutEnaFlags, 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;
}
// 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 )
@ -1438,6 +1503,7 @@ namespace cw
{
audioDev_t* ad = nullptr; //p->audioDevA + i;
bool enableFl = false;
bool meterFl = false;
char* userLabel = nullptr;
unsigned userId = kInvalidId;
char* devName = nullptr;
@ -1477,6 +1543,7 @@ namespace cw
if((rc = node->getv_opt(
"userId", userId,
"meterFl", meterFl,
"inGroup", inGroupLabel,
"outGroup", outGroupLabel )) != kOkRC )
{
@ -1575,14 +1642,15 @@ namespace cw
if((rc = _audioGroupAddDevice( oag, false, ad, oChCnt )) != kOkRC )
goto errLabel;
}
// set the device group pointers
ad->enableFl = enableFl;
ad->meterFl = meterFl;
ad->label = userLabel;
ad->userId = userId;
ad->iGroup = iag;
ad->oGroup = oag;
}
}
@ -1590,6 +1658,23 @@ namespace cw
return rc;
}
rc_t _audioDeviceEnableMeters( io_t* p )
{
rc_t rc = kOkRC;
for(unsigned i=0; i<p->audioDevN; ++i)
if( p->audioDevA[i].enableFl && p->audioDevA[i].meterFl )
if((rc = _audioDeviceEnableMeter(p, p->audioDevA[i].devIdx, kEnableFl | kInFl | kOutFl )) != kOkRC )
{
cwLogError(rc,"Audio enable on device '%s' failed.",p->audioDevA[i].label);
goto errLabel;
}
errLabel:
return rc;
}
// Allocate the sample ptr buffers for each audio group.
rc_t _audioGroupAllocBuffer( io_t* p )
{
@ -1656,6 +1741,13 @@ namespace cw
goto errLabel;
}
// initialize enabled audio meters
if((rc = _audioDeviceEnableMeters(p)) != kOkRC )
{
rc = cwLogError(rc,"Audio device enabled failed.");
goto errLabel;
}
errLabel:
return rc;
}
@ -1839,31 +1931,7 @@ namespace cw
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;
}
}
@ -2386,43 +2454,8 @@ unsigned cw::io::audioDeviceChannelCount( handle_t h, unsigned devIdx, unsigne
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;
io_t* p = _handleToPtr(h);
return _audioDeviceEnableMeter(p, devIdx, inOutEnaFlags );
}
@ -2460,11 +2493,11 @@ const cw::io::sample_t* cw::io::audioDeviceMeters( handle_t h, unsigned devIdx,
cw::rc_t cw::io::audioDeviceEnableTone( handle_t h, unsigned devIdx, unsigned inOutEnaFlags )
{
rc_t rc = kOkRC;
io_t* p = nullptr;
io_t* p = _handleToPtr(h);
audioDev_t* ad = nullptr;
unsigned audioBufFlags = 0;
if((rc = _audioDeviceParams( h, devIdx, inOutEnaFlags, p, ad, audioBufFlags )) != kOkRC )
if((rc = _audioDeviceParams( p, devIdx, inOutEnaFlags, ad, audioBufFlags )) != kOkRC )
rc = cwLogError(rc,"Enable tone failed.");
else
{
@ -2479,11 +2512,11 @@ cw::rc_t cw::io::audioDeviceToneFlags( handle_t h, unsigned devIdx, unsigned inO
{
rc_t rc = kOkRC;
io_t* p = nullptr;
io_t* p = _handleToPtr(h);
audioDev_t* ad = nullptr;
unsigned audioBufFlags = 0;
if((rc = _audioDeviceParams( h, devIdx, inOrOutFlag, p, ad, audioBufFlags )) != kOkRC )
if((rc = _audioDeviceParams( p, devIdx, inOrOutFlag, ad, audioBufFlags )) != kOkRC )
rc = cwLogError(rc,"Get tone flags failed.");
else
{
@ -2497,11 +2530,11 @@ cw::rc_t cw::io::audioDeviceToneFlags( handle_t h, unsigned devIdx, unsigned inO
cw::rc_t cw::io::audioDeviceEnableMute( handle_t h, unsigned devIdx, unsigned inOutEnaFlags )
{
rc_t rc = kOkRC;
io_t* p = nullptr;
io_t* p = _handleToPtr(h);
audioDev_t* ad = nullptr;
unsigned audioBufFlags = 0;
if((rc = _audioDeviceParams( h, devIdx, inOutEnaFlags, p, ad, audioBufFlags )) != kOkRC )
if((rc = _audioDeviceParams( p, devIdx, inOutEnaFlags, ad, audioBufFlags )) != kOkRC )
rc = cwLogError(rc,"Enable mute failed.");
else
{
@ -2515,11 +2548,11 @@ cw::rc_t cw::io::audioDeviceEnableMute( handle_t h, unsigned devIdx, unsigned in
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;
io_t* p = _handleToPtr(h);
audioDev_t* ad = nullptr;
unsigned audioBufFlags = 0;
if((rc = _audioDeviceParams( h, devIdx, inOrOutFlag, p, ad, audioBufFlags )) != kOkRC )
if((rc = _audioDeviceParams( p, devIdx, inOrOutFlag, ad, audioBufFlags )) != kOkRC )
rc = cwLogError(rc,"Get mute flags failed.");
else
{
@ -2534,11 +2567,11 @@ cw::rc_t cw::io::audioDeviceMuteFlags( handle_t h, unsigned devIdx, unsigned inO
cw::rc_t cw::io::audioDeviceSetGain( handle_t h, unsigned devIdx, unsigned inOrOutFlag, double gain )
{
rc_t rc = kOkRC;
io_t* p = nullptr;
io_t* p = _handleToPtr(h);
audioDev_t* ad = nullptr;
unsigned audioBufFlags = 0;
if((rc = _audioDeviceParams( h, devIdx, inOrOutFlag, p, ad, audioBufFlags )) != kOkRC )
if((rc = _audioDeviceParams( p, devIdx, inOrOutFlag, ad, audioBufFlags )) != kOkRC )
rc = cwLogError(rc,"Set gain failed.");
else
{
@ -2551,11 +2584,11 @@ cw::rc_t cw::io::audioDeviceSetGain( handle_t h, unsigned devIdx, unsigned inOrO
cw::rc_t cw::io::audioDeviceGain( handle_t h, unsigned devIdx, unsigned inOrOutFlag, double* gainA, unsigned chCnt )
{
rc_t rc = kOkRC;
io_t* p = nullptr;
io_t* p = _handleToPtr(h);
audioDev_t* ad = nullptr;
unsigned audioBufFlags = 0;
if((rc = _audioDeviceParams( h, devIdx, inOrOutFlag, p, ad, audioBufFlags )) != kOkRC )
if((rc = _audioDeviceParams( p, devIdx, inOrOutFlag, ad, audioBufFlags )) != kOkRC )
rc = cwLogError(rc,"Get gain failed.");
else
{