This commit is contained in:
kevin 2025-04-21 09:35:11 -04:00
commit 18ece08b53
8 changed files with 171 additions and 20 deletions

View File

@ -103,6 +103,8 @@ namespace cw
sample_t* zeroBuf; // buffer of zeros
unsigned zeroBufCnt; // max of all dspFrameCnt for all devices.
float meter_damp_coeff;
} audioBuf_t;
inline audioBuf_t* _handleToPtr( handle_t h ) { return handleToPtr<handle_t,audioBuf_t>(h); }
@ -263,7 +265,7 @@ namespace cw
}
}
cw::rc_t cw::audio::buf::create( handle_t& hRef, unsigned devCnt, unsigned meterMs )
cw::rc_t cw::audio::buf::create( handle_t& hRef, unsigned devCnt, unsigned meterMs, float meterDampCoeff )
{
rc_t rc;
@ -274,7 +276,8 @@ cw::rc_t cw::audio::buf::create( handle_t& hRef, unsigned devCnt, unsigned meter
p->devArray = mem::allocZ<cmApDev>(devCnt );
p->devCnt = devCnt;
p->meter_damp_coeff = meterDampCoeff;
hRef.set(p);
setMeterMs(hRef,meterMs);
@ -422,11 +425,10 @@ cw::rc_t cw::audio::buf::update(
sample_t* dp = cp->b + cp->ii;
const sample_t* ep = dp + n0;
// update the meter
if( cwIsFlag(cp->fl,kMeterFl) )
{
cp->m[cp->mi] = _cmApMeter(sp,pp->audioFramesCnt,pp->chCnt);
cp->m[cp->mi] = (1.0f - p->meter_damp_coeff) * cp->m[cp->mi] + p->meter_damp_coeff*_cmApMeter(sp,pp->audioFramesCnt,pp->chCnt);
cp->mi = (cp->mi + 1) % cp->mn;
}
@ -538,7 +540,9 @@ cw::rc_t cw::audio::buf::update(
// update the meter
if( cwIsFlag(cp->fl,kMeterFl) )
{
cp->m[cp->mi] = _cmApMeter(((sample_t*)pp->audioBytesPtr)+j,pp->audioFramesCnt,pp->chCnt);
//cp->m[cp->mi] = _cmApMeter(((sample_t*)pp->audioBytesPtr)+j,pp->audioFramesCnt,pp->chCnt);
cp->m[cp->mi] = (1.0f - p->meter_damp_coeff) * cp->m[cp->mi] + p->meter_damp_coeff*_cmApMeter(((sample_t*)pp->audioBytesPtr)+j,pp->audioFramesCnt,pp->chCnt);
cp->mi = (cp->mi + 1) % cp->mn;
}

View File

@ -66,7 +66,7 @@ namespace cw
// Allocate and initialize an audio buffer.
// devCnt - count of devices this buffer will handle.
// meterMs - length of the meter buffers in milliseconds (automatically limit to the range:10 to 1000)
rc_t create( handle_t& hRef, unsigned devCnt, unsigned meterMs );
rc_t create( handle_t& hRef, unsigned devCnt, unsigned meterMs, float meterDampCoeff=0.4 );
// Deallocate and release any resource held by an audio buffer allocated via initialize().
rc_t destroy( handle_t& hRef );

View File

@ -1347,7 +1347,7 @@ namespace cw
unsigned audioBufFlags = 0;
if((rc = _audioDeviceParams( p, devIdx, inOutEnaFlags, ad, audioBufFlags )) != kOkRC )
rc = cwLogError(rc,"Enable tone failed.");
rc = cwLogError(rc,"Audio device to buffer parameter translation failed.");
else
{
bool enaFl = inOutEnaFlags & kEnableFl;
@ -2011,11 +2011,11 @@ namespace cw
for(unsigned i=0; i<p->audioDevN; ++i)
if( p->audioDevA[i].activeFl && 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;
}
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;

View File

@ -47,7 +47,10 @@ namespace cw
kSaveBtnId,
kOpenBtnId,
kFnStringId
kFnStringId,
kMeterPanelId,
kBaseAudioMeterId
};
enum
@ -81,10 +84,21 @@ namespace cw
{ kPanelDivId, kSaveBtnId, "saveBtnId" },
{ kPanelDivId, kOpenBtnId, "openBtnId" },
{ kPanelDivId, kFnStringId, "filenameId" },
{ kPanelDivId, kMeterPanelId, "meterPanelId" }
};
unsigned mapN = sizeof(mapA)/sizeof(mapA[0]);
const double kMeterMinVal = 0.0;
const double kMeterMaxVal = 100.0;
typedef struct audio_meter_str
{
unsigned uuid;
char* title;
} audio_meter_t;
typedef struct app_str
{
@ -101,6 +115,13 @@ namespace cw
const object_t* midi_play_record_cfg;
unsigned audioDevIdx;
unsigned audioInChCnt;
bool activate_meters_fl;
audio_meter_t* meterA;
std::atomic<bool> meterSetupCompleteFl;
} app_t;
rc_t _parseCfg(app_t* app, const object_t* cfg )
@ -111,6 +132,7 @@ namespace cw
"record_dir", app->record_dir,
"record_folder", app->record_folder,
"record_fn_ext", app->record_fn_ext,
"activate_meters_fl", app->activate_meters_fl,
"midi_play_record", app->midi_play_record_cfg)) != kOkRC )
{
rc = cwLogError(kSyntaxErrorRC,"Audio MIDI app configuration parse failed.");
@ -136,6 +158,10 @@ namespace cw
rc_t _free( app_t& app )
{
for(unsigned i=0; i<app.audioInChCnt; ++i)
mem::release(app.meterA[i].title);
mem::release(app.meterA);
mem::release(app.directory);
return kOkRC;
}
@ -360,11 +386,98 @@ namespace cw
return rc;
}
rc_t _get_active_audio_dev_and_ch_count( app_t* app )
{
rc_t rc = kOkRC;
unsigned i = 0;
unsigned n = audioDeviceCount( app->ioH );
for(unsigned i=0; i<n; ++i)
if( audioDeviceIsActive(app->ioH,i) )
{
app->audioDevIdx = i;
app->audioInChCnt = audioDeviceChannelCount(app->ioH,i,io::kInFl);
if((rc = audioDeviceEnableMeters(app->ioH, app->audioDevIdx, io::kInFl | io::kEnableFl )) != kOkRC )
{
rc = cwLogError(rc,"Audio meter enable failed on device index:%i.",app->audioDevIdx);
goto errLabel;
}
cwLogInfo("Active audio device index:%i in chs:%i\n",app->audioDevIdx,app->audioInChCnt);
break;
}
errLabel:
return rc;
}
rc_t _create_audio_meters( app_t* app )
{
rc_t rc = kOkRC;
unsigned meterPanelUuId = uiFindElementUuId( app->ioH, "meterPanelId" );
if( app->audioDevIdx == kInvalidIdx || app->audioInChCnt==0 )
{
cwLogWarning("No meters created. No active input audio device was found.");
goto errLabel;
}
app->meterA = mem::resizeZ(app->meterA,app->audioInChCnt );
for(unsigned i=0; i<app->audioInChCnt; ++i)
{
app->meterA[i].title = mem::printf(app->meterA[i].title,"%i",i);
if((rc = uiCreateProg(app->ioH, app->meterA[i].uuid, meterPanelUuId, nullptr, kBaseAudioMeterId+i, 0, NULL, app->meterA[i].title, kMeterMinVal, kMeterMaxVal )) != kOkRC )
{
cwLogError(rc,"Audio input meter create failed on channel index:%i.",i);
goto errLabel;
}
}
app->meterSetupCompleteFl.store(true);
errLabel:
if(rc != kOkRC )
cwLogError(rc,"Audio meter creation failed.");
return rc;
}
rc_t _on_audio_meters(app_t* app, const io::audio_group_dev_t* agd )
{
rc_t rc = kOkRC;
if( app->activate_meters_fl && app->meterSetupCompleteFl.load() && cwIsFlag(agd->flags,io::kInFl) )
{
unsigned n = std::min(app->audioInChCnt,agd->chCnt);
for(unsigned i=0; i<n; ++i)
{
double db = std::max(0.0, 20.0 * log10(agd->meterA[i]) + 100.0 );
if((rc = io::uiSendValue(app->ioH, app->meterA[i].uuid, db)) != kOkRC )
{
rc = cwLogError(rc,"Audio meter update failed on channel index:%i.",i);
goto errLabel;
}
}
}
errLabel:
return rc;
}
rc_t _onUiInit(app_t* app, const io::ui_msg_t& m )
{
rc_t rc = kOkRC;
if( app->activate_meters_fl)
if((rc = _get_active_audio_dev_and_ch_count(app)) == kOkRC )
_create_audio_meters(app);
return rc;
}
@ -511,12 +624,15 @@ namespace cw
break;
case io::kMidiTId:
// Drop the MIDI messages that were processed on this call.
midiDeviceClearBuffer(app->ioH,m->u.midi->pkt->msgCnt);
break;
case io::kAudioTId:
break;
case io::kAudioMeterTId:
_on_audio_meters(app,m->u.audioGroupDev);
break;
case io::kSockTId:
@ -550,6 +666,10 @@ cw::rc_t cw::audio_midi_app::main( const object_t* cfg )
rc_t rc;
app_t app = {};
app.audioDevIdx = kInvalidIdx;
app.audioInChCnt = 0;
app.meterSetupCompleteFl.store(false);
// Parse the configuration
if((rc = _parseCfg(&app,cfg)) != kOkRC )
goto errLabel;
@ -596,6 +716,8 @@ cw::rc_t cw::audio_midi_app::main( const object_t* cfg )
}
errLabel:
destroy(app.mrpH);
destroy(app.arpH);
_free(app);
io::destroy(app.ioH);
printf("Audio-MIDI Done.\n");

View File

@ -937,7 +937,7 @@ namespace cw
file::handle_t fH;
// NOTE: version must be a small negative number to differentiate from file version that
// whose first word is the count of records in the file, rather than the version number
int version = -1;
int version = -2;
if( p->iMsgArrayInIdx == 0 )
{

View File

@ -64,6 +64,8 @@ label {
flex-direction: row;
align-items: center;
background-color: LightBlue;
margin-top: 10px;
margin-bottom: 10px;
}
.uiCol {

View File

@ -572,6 +572,13 @@ function ui_create_number( parent_ele, d )
return ele;
}
function precisionRound(number, precision)
{
var factor = Math.pow(10, precision);
return Math.round(number * factor) / factor;
}
function ui_set_number_display( ele_id, value )
{
var ele = dom_id_to_ele(ele_id);
@ -579,11 +586,20 @@ function ui_set_number_display( ele_id, value )
if( typeof(value)=="number")
{
var val = value.toString();
if( ele.decpl == 0 )
ele.innerHTML = parseInt(val,10);
var defined_fl = (typeof ele.decpl !== 'undefined');
if( defined_fl )
{
if( ele.decpl == 0 )
ele.innerHTML = parseInt(val,10);
else
ele.innerHTML = precisionRound(parseFloat(val),ele.decpl)
}
else
ele.innerHTML = parseFloat(val);
{
ele.innerHTML = parseFloat(val)
}
}
}
@ -617,7 +633,7 @@ function ui_create_text_display( parent_ele, d )
function ui_set_progress( ele, value )
{
var ele = dom_id_to_ele(ele_id);
//var ele = dom_id_to_ele(ele_id);
ele.value = Math.round( ele.max * (value - ele.minValue) / (ele.maxValue - ele.minValue));
}

View File

@ -43,5 +43,12 @@
button:{ name: openBtnId, title:"Open" },
button:{ name: saveBtnId, title:"Save" },
},
row: {
panel: {
name: "meterPanelId",
title: "Meters"
}
}
}
}