diff --git a/cwDspTransforms.cpp b/cwDspTransforms.cpp index 412bc52..801fea8 100644 --- a/cwDspTransforms.cpp +++ b/cwDspTransforms.cpp @@ -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 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(); + p->maxWndMs = maxWndMs; + p->maxWndSmpN = (unsigned)((maxWndMs * srate)/1000.0); + p->wndV = mem::allocZ(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; + } + + +} diff --git a/cwDspTransforms.h b/cwDspTransforms.h index fd0c455..0d361bc 100644 --- a/cwDspTransforms.h +++ b/cwDspTransforms.h @@ -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 ); + + } } } diff --git a/cwFlowProc.cpp b/cwFlowProc.cpp index f9856a9..6d60318 100644 --- a/cwFlowProc.cpp +++ b/cwFlowProc.cpp @@ -1437,7 +1437,7 @@ namespace cw // namespace pv_analysis { - typedef struct dsp::pv_anl::obj_str pv_t; + typedef struct dsp::pv_anl::obj_str pv_t; enum { kInPId, @@ -1477,9 +1477,9 @@ namespace cw inst->pvN = srcBuf->chN; inst->pvA = mem::allocZ( 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 pv_t; + typedef struct dsp::pv_syn::obj_str pv_t; enum { kInPId, @@ -1751,7 +1751,7 @@ namespace cw // namespace spec_dist { - typedef struct dsp::spec_dist::obj_str spec_dist_t; + typedef struct dsp::spec_dist::obj_str spec_dist_t; enum { @@ -1794,9 +1794,9 @@ namespace cw inst->sdN = srcBuf->chN; inst->sdA = mem::allocZ( 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(); + + 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( inst->mtrN ); + + // create a audio_meter object for each input channel + for(unsigned i=0; ichN; ++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; imtrN; ++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; imtrA[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; imtrN; ++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 diff --git a/cwFlowProc.h b/cwFlowProc.h index a5d053a..9adcb95 100644 --- a/cwFlowProc.h +++ b/cwFlowProc.h @@ -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; } } } diff --git a/cwIo.cpp b/cwIo.cpp index 71dd515..17b8d56 100644 --- a/cwIo.cpp +++ b/cwIo.cpp @@ -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; iaudioDevN; ++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 {