#include "precompile.h" #include "audiodbmeter.h" #include "audiocontext.h" #include #include #include #define DB_RANGE 60.0 #define READ_IDX (STREAM_DIR_READ-1) #define WRITE_IDX (STREAM_DIR_WRITE-1) static const float const_1_div_32768_ = 1.0f / 32768.f; /* 16 bit multiplier */ static float ClipZeroToOne(float z) { if (z > 1.0) return 1.0; else if (z < 0.0) return 0.0; else return z; } static float ToDB(float v, float range) { float db; if (v > 0) db = 20 * log10(fabs(v)); else db = -999; return ClipZeroToOne((db + range) / range); } static apr_status_t read_frame(void *self, audioframe_t *frame) { audiodbmeter_t *audiometer = CONTAINING_RECORD(self, audiodbmeter_t, base); apr_status_t status; status = audiometer->base.downstream->vtbl->read_frame(audiometer->base.downstream, frame); if (status == APR_SUCCESS && frame->size > 0) { int i; int frames = frame->size / 2; short *sptr = (short*)frame->buffer; float peak = 0.0; float rms = 0.0; int old; for (i = 0; i < frames; ++i) { float v = *sptr * const_1_div_32768_; peak = (float)max(v, peak); rms += (float)(v * v); sptr++; } rms = sqrt(rms / frames); rms = ToDB(rms, DB_RANGE); peak = ToDB(peak, DB_RANGE); old = audiometer->stat[READ_IDX].rms; audiometer->stat[READ_IDX].rms = (old * 9 + (int)(100*rms)) / 10; old = audiometer->stat[READ_IDX].peak; audiometer->stat[READ_IDX].peak = (old * 9 + (int)(100*peak)) / 10; } return APR_SUCCESS; } static apr_status_t write_frame(void *self, const audioframe_t *frame) { audiodbmeter_t *audiometer = CONTAINING_RECORD(self, audiodbmeter_t, base); apr_status_t status; status = audiometer->base.downstream->vtbl->write_frame(audiometer->base.downstream, frame); if (status == APR_SUCCESS && frame->size > 0) { int i; int frames = frame->size / 2; const short *sptr = (const short *)frame->buffer; float peak = 0.0; float rms = 0.0; int old; for (i = 0; i < frames; ++i) { float v = *sptr * const_1_div_32768_; peak = (float)max(v, peak); rms += (float)(v * v); sptr++; } rms = sqrt(rms / frames); rms = ToDB(rms, DB_RANGE); peak = ToDB(peak, DB_RANGE); old = audiometer->stat[WRITE_IDX].rms; audiometer->stat[WRITE_IDX].rms = (old * 9 + (int)(100*rms)) / 10; old = audiometer->stat[WRITE_IDX].peak; audiometer->stat[WRITE_IDX].peak = (old * 9 + (int)(100*peak)) / 10; } return APR_SUCCESS; } static audiostream_vtbl_t g_stream_vtbl = { &read_frame, &write_frame, }; apr_status_t audiodbmeter_create(apr_pool_t *pool, audioengine_t *engine, audiodbmeter_t **p_audiometer) { audiodbmeter_t *audiometer; audiometer = apr_palloc(pool, sizeof(audiodbmeter_t)); memset(audiometer, 0, sizeof(audiodbmeter_t)); assert(0 == READ_IDX); assert(1 == WRITE_IDX); audiostream_init(engine, &g_stream_vtbl, &audiometer->base); *p_audiometer = audiometer; return APR_SUCCESS; } apr_status_t audiodbmeter_destroy(audiodbmeter_t *audiometer) { return APR_SUCCESS; } apr_status_t audiodbmeter_get_peak_db(audiodbmeter_t *audiometer, int direction, int *pval) { int idx = direction - 1; *pval = audiometer->stat[idx].peak; return APR_SUCCESS; } apr_status_t audiodbmeter_get_rms_db(audiodbmeter_t *audiometer, int direction, int *pval) { int idx = direction - 1; *pval = audiometer->stat[idx].rms; return APR_SUCCESS; }