Index: apps/app_meetme.c =================================================================== --- apps/app_meetme.c (revision 209515) +++ apps/app_meetme.c (working copy) @@ -82,6 +82,8 @@ #define MEETME_DELAYDETECTTALK 300 #define MEETME_DELAYDETECTENDTALK 1000 +/* 100 frames = 2 seconds */ +#define MEETME_GAINFRAME 100 #define AST_FRAME_BITS 32 @@ -163,6 +165,8 @@ CONFFLAG_DURATION_LIMIT = (1 << 30), /*! Do not write any audio to this channel until the state is up. */ CONFFLAG_NO_AUDIO_UNTIL_UP = (1 << 31), + /*! Maintains gain of each user and throws MeetmeGain events */ + CONFFLAG_MONITORGAIN = (1 << 32), }; enum { @@ -185,6 +189,7 @@ AST_APP_OPTION('E', CONFFLAG_EMPTYNOPIN ), AST_APP_OPTION('e', CONFFLAG_EMPTY ), AST_APP_OPTION('F', CONFFLAG_PASS_DTMF ), + AST_APP_OPTION('g', CONFFLAG_MONITORGAIN), AST_APP_OPTION('i', CONFFLAG_INTROUSER ), AST_APP_OPTION('I', CONFFLAG_INTROUSERNOREVIEW ), AST_APP_OPTION_ARG('M', CONFFLAG_MOH, OPT_ARG_MOH_CLASS ), @@ -2475,6 +2480,7 @@ res = ast_dsp_silence(dsp, f, &totalsilence); if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) { user->talking = 1; + ast_dsp_gain_reset(dsp); if (confflags & CONFFLAG_MONITORTALKER) manager_event(EVENT_FLAG_CALL, "MeetmeTalking", "Channel: %s\r\n" @@ -2496,6 +2502,22 @@ chan->name, chan->uniqueid, conf->confno, user->user_no); } } + + /* If user is talking, adjust his gain */ + if ((confflags & CONFFLAG_MONITORGAIN) && + (confflags & CONFFLAG_MONITORTALKER) && + (user->talking > 0)) { + ast_dsp_gain(dsp, f); + int gain_mult_factor = ast_dsp_getgain_mult_factor(dsp); + ast_frame_adjust_volume(f, gain_mult_factor); + if (ast_dsp_getnumgainframes(dsp) >= MEETME_GAINFRAMES) { + ast_debug(3, "MONITORGAIN: user=%d, gain_mult_factor=%d\n", + user->user_no, gain_mult_factor); + ast_dsp_calc_gain_mult_factor(dsp); + ast_dsp_gain_reset(dsp); + } + } + if (using_pseudo) { /* Absolutely do _not_ use careful_write here... it is important that we read data from the channel Index: include/asterisk/dsp.h =================================================================== --- include/asterisk/dsp.h (revision 209515) +++ include/asterisk/dsp.h (working copy) @@ -168,4 +168,22 @@ * \return nothing */ void ast_dsp_frame_freed(struct ast_frame *fr); + +/* \brief Return RMS gain for this frame, also maintain (bucket) the value + * for gain normalization in meetme + */ +float ast_dsp_gain(struct ast_dsp* dsp, struct ast_frame* f); + +/* \brief Calculate the gain multiplication factor */ +void ast_dsp_calc_gain_mult_factor(struct ast_dsp* dsp); + +/* \brief Reset the gain buckets */ +void ast_dsp_gain_reset(struct ast_dsp* dsp); + +/* \brief Returns the number of frames till now used in gain normalization */ +int ast_dsp_getnumgainframes(struct ast_dsp* dsp); + +/* \brief Returns the current gain multiplication factor */ +int ast_dsp_getgain_mult_factor(struct ast_dsp* dsp); + #endif /* _ASTERISK_DSP_H */ Index: main/dsp.c =================================================================== --- main/dsp.c (revision 209515) +++ main/dsp.c (working copy) @@ -301,6 +301,46 @@ static int thresholds[THRESHOLD_MAX]; +#define NUMGAINBUCKETS 10 + +/* + * Divide the range 0-32767 into 10 equal parts + * These numbers are the squares of those boundaries + */ +static float GAIN_SQUARED_BOUNDS[NUMGAINBUCKETS] = +{ + 1.073e7, + 4.294e7, + 9.662e7, + 1.718e8, + 2.651e8, + 3.865e8, + 5.261e8, + 6.872e8, + 8.697e8, + 1.074e9 +}; + +/* + * The number of frames in a bucket to qualify it as a valid bucket + * when trying to find the highest RMS in a gain window + */ +#define GAIN_BUCKET_THRESHOLD 4 + +/* + * The gain multiplication factor corresponding to each bucket + */ +static int GMF_VALUES[NUMGAINBUCKETS] = +{ + 5, 3, 2, 1, 1, 1, 1, -2, -3, -5 +}; + +/* + * The number of frames in the first bucket to qualify it as background noise + * See MEETME_GAINFRAMES in apps/app_meetme.c + */ +#define NUM_GAINFRAMES_FOR_NOISE 95 + static inline void goertzel_sample(goertzel_state_t *s, short sample) { int v1; @@ -396,6 +436,9 @@ tone_detect_state_t cng_tone_state; tone_detect_state_t ced_tone_state; int destroy; + int gain_mult_factor; + int gain_buckets[NUMGAINBUCKETS]; + int numgainframes; }; static void mute_fragment(struct ast_dsp *dsp, fragment_t *fragment) @@ -1265,7 +1308,108 @@ return __ast_dsp_silence_noise(dsp, s, len, NULL, totalnoise); } +/* Init all gain buckets to 0 */ +static void ast_dsp_gain_buckets_init(struct ast_dsp* dsp) { + int i = 0; + for (i = 0; i < NUMGAINBUCKETS; i++) { + dsp->gain_buckets[i] = 0; + } +} +/* + * Bucket this RMS gain value into one of the buckets + */ +static void ast_dsp_gain_bucket_value(struct ast_dsp* dsp, float rms_squared) { + int i; + for (i = 0; i < NUMGAINBUCKETS; i++) { + if (rms_squared < GAIN_SQUARED_BOUNDS[i]) { + dsp->gain_buckets[i]++; + break; + } + } +} + +/* + * Return the RMS gain for this frame + * Bucket the RMS gain in dsp->gain_buckets + */ +float ast_dsp_gain(struct ast_dsp* dsp, struct ast_frame* f) { + short *s; + int len, x, gain; + float accum, rms_squared; + + if (f->frametype != AST_FRAME_VOICE) { + ast_log(LOG_WARNING, "Can't calculate gain on a non-voice frame\n"); + return 0; + } + if (f->subclass != AST_FORMAT_SLINEAR) { + ast_log(LOG_WARNING, "Can only calculate gain on signed-linear frames :(\n"); + return 0; + } + s = f->data.ptr; + len = f->datalen / 2; + + if (!len) { + return 0; + } + + accum = 0.0; + for (x = 0; x < len; x++) { + gain = abs(s[x]); + accum += (gain * gain); + } + rms_squared = accum / len; + + /* bucket this value */ + ast_dsp_gain_bucket_value(dsp, rms_squared); + + (dsp->numgainframes)++; + return rms_squared; +} + +/* + * Based on the buckets, determine the gain multiplication factor + * We find the highest bucket which has at least THRESHOLD amount of frames + * This is to exclude outliers + * Then just choose the gain_mult_factor corresponding to that bucket + */ +void ast_dsp_calc_gain_mult_factor(struct ast_dsp* dsp) { + int i; + + dsp->gain_mult_factor = 1; + /* Detect noise and let gain_mult_factor be 1 + If we have NUM_GAINFRAMES_FOR_NOISE samples in the + first (lowest) gain bucket, assume its just noise and don't modify it + */ + if (dsp->gain_buckets[0] > NUM_GAINFRAMES_FOR_NOISE) { + return; + } + + for (i = NUMGAINBUCKETS - 1; i >= 0; i--) { + if (dsp->gain_buckets[i] >= GAIN_BUCKET_THRESHOLD) { + dsp->gain_mult_factor = GMF_VALUES[i]; + break; + } + } +} + +/* + * Get prepared for next gain window + * Don't reinitialise MF + */ +void ast_dsp_gain_reset(struct ast_dsp *dsp) { + dsp->numgainframes = 0; + ast_dsp_gain_buckets_init(dsp); +} + +int ast_dsp_getnumgainframes(struct ast_dsp* dsp) { + return dsp->numgainframes; +} + +int ast_dsp_getgain_mult_factor(struct ast_dsp* dsp) { + return dsp->gain_mult_factor; +} + struct ast_frame *ast_dsp_process(struct ast_channel *chan, struct ast_dsp *dsp, struct ast_frame *af) { int silence; @@ -1473,6 +1617,10 @@ ast_dsp_prog_reset(dsp); /* Initialize fax detector */ ast_fax_detect_init(dsp); + + dsp->gain_mult_factor = 1; + dsp->numgainframes = 0; + ast_dsp_gain_buckets_init(dsp); } return dsp; }