<commit_before>/*
mediastreamer2 library - modular sound and video processing and streaming
Copyright (C) 2006  <NAME> (<EMAIL>)

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

#include "mediastreamer2/msfilter.h"
#include <math.h>

#if defined(_WIN32_WCE)
#define DISABLE_SPEEX
#endif

#ifndef HAVE_SPEEXDSP
#define DISABLE_SPEEX
#endif

#ifndef DISABLE_SPEEX
#include <speex/speex_preprocess.h>
#endif

#ifndef CONF_GRAN_MAX
#define CONF_GRAN_MAX 12 /* limit for 'too much data' */
#endif

#define CONF_NSAMPLES 160*4*4 /* (CONF_GRAN/2) */
#ifndef CONF_MAX_PINS
#define CONF_MAX_PINS 32
#endif

static const float max_e=32767*32767;
static const float coef=0.1;

typedef struct Channel{
	MSBufferizer buff;
	int16_t input[CONF_NSAMPLES];
	bool_t has_contributed;
	bool_t is_used;

	int is_speaking;

	int count;
	int missed;

	int stat_discarded;
	int stat_missed;
	int stat_processed;

#ifndef DISABLE_SPEEX
	SpeexPreprocessState *speex_pp;
#endif

	float energy;

} Channel;

typedef struct ConfState{
	Channel channels[CONF_MAX_PINS];
	int sum[CONF_NSAMPLES];
	int enable_directmode;
	int enable_vad;

	int enable_halfduplex;
	int vad_prob_start;
	int vad_prob_continue;

	int agc_level;
	int max_gain;
	int mix_mode;
	int samplerate;

	int adaptative_msconf_buf;
	int conf_gran;
	int conf_nsamples;
} ConfState;


static void channel_init(ConfState *s, Channel *chan, int pos){
	float f;
	int val;
	memset(chan, 0, sizeof(Channel));
	ms_bufferizer_init(&chan->buff);
#ifndef DISABLE_SPEEX
	//chan->speex_pp = speex_preprocess_state_init((s->conf_gran/2) *(s->samplerate/8000), s->samplerate);
	chan->speex_pp = speex_preprocess_state_init(s->conf_gran/2, s->samplerate);
	if (chan->speex_pp==NULL)
		return;

	/* configure sound card input on pin 0 */
	val=0;
	if (pos==0)
		val=1;

	if (s->enable_halfduplex>0 && pos%2==1)
		val=1;

	speex_preprocess_ctl(chan->speex_pp, SPEEX_PREPROCESS_SET_DENOISE, &val);
	val = -30;
	speex_preprocess_ctl(chan->speex_pp, SPEEX_PREPROCESS_SET_NOISE_SUPPRESS, &val);

	/* enable VAD only on incoming RTP stream */
	val=0;
	if (pos%2==1 || (pos==0 && s->enable_halfduplex>0))
	{
		val=1;
	}

	speex_preprocess_ctl(chan->speex_pp, SPEEX_PREPROCESS_SET_VAD, &val);
	if (s->vad_prob_start>0 && s->vad_prob_continue>0)
	{
		val = s->vad_prob_start; // xx%
		speex_preprocess_ctl(chan->speex_pp, SPEEX_PREPROCESS_SET_PROB_START, &val);
		val = s->vad_prob_continue; // xx%
		speex_preprocess_ctl(chan->speex_pp, SPEEX_PREPROCESS_SET_PROB_CONTINUE, &val);
	}

	/* enable AGC only on local soundcard */
	val=0;
	f=8000;
	if (s->agc_level>0 && pos==0)
		val=1;
	else if (pos==0 && s->enable_halfduplex>0)
		val=1;
	else if ( pos%2==1 && s->enable_halfduplex>0)
		val=1;
	if (s->agc_level>0)
		f=s->agc_level;


	speex_preprocess_ctl(chan->speex_pp, SPEEX_PREPROCESS_SET_AGC, &val);
	speex_preprocess_ctl(chan->speex_pp, SPEEX_PREPROCESS_SET_AGC_LEVEL, &f);
	val=s->max_gain;
	speex_preprocess_ctl(chan->speex_pp, SPEEX_PREPROCESS_SET_AGC_MAX_GAIN, &val);

	val=0;
#if 0
	val=1; // do more testing
#endif
	speex_preprocess_ctl(chan->speex_pp, SPEEX_PREPROCESS_SET_DEREVERB, &val);
	f=.4;
	speex_preprocess_ctl(chan->speex_pp, SPEEX_PREPROCESS_SET_DEREVERB_DECAY, &f);
	f=.3;
	speex_preprocess_ctl(chan->speex_pp, SPEEX_PREPROCESS_SET_DEREVERB_LEVEL, &f);

#endif
}

static void channel_uninit(Channel *chan){
	ms_bufferizer_uninit(&chan->buff);
#ifndef DISABLE_SPEEX
	if (chan->speex_pp!=NULL)
	    speex_preprocess_state_destroy(chan->speex_pp);
	chan->speex_pp=NULL;
#endif
}

static void conf_init(MSFilter *f){
	ConfState *s=(ConfState *)ms_new0(ConfState,1);
	int i;
	s->samplerate=8000;
	s->conf_gran=((16 * s->samplerate) / 800) *2;
	s->conf_nsamples=s->conf_gran/2;
    for (i=0;i<CONF_MAX_PINS;i++)
		channel_init(s, &s->channels[i], i);
	s->enable_directmode=FALSE;
	s->enable_vad=TRUE;
	s->agc_level=0;
	s->max_gain=30;
	s->mix_mode=TRUE;
	s->adaptative_msconf_buf=2;
	f->data=s;
}

static void conf_uninit(MSFilter *f){
	ConfState *s=(ConfState*)f->data;
	int i;
	for (i=0;i<CONF_MAX_PINS;i++)
		channel_uninit(&s->channels[i]);
	ms_free(f->data);
}

static void conf_preprocess(MSFilter *f){
	ConfState *s=(ConfState*)f->data;
	int i;
	for (i=0;i<CONF_MAX_PINS;i++)
	  {
	    s->channels[i].is_used=FALSE;
	    s->channels[i].missed=0;
	    s->channels[i].stat_discarded=0;
	    s->channels[i].stat_missed=0;
	    s->channels[i].stat_processed=0;
	  }
}

static bool_t should_process(MSFilter *f, ConfState *s){
	Channel *chan;
	int active_channel=0;
	int i;

	if (ms_bufferizer_get_avail(&(&s->channels[0])->buff)>s->conf_gran
	    && s->channels[0].is_used==FALSE)
	  {
	    /* soundread has just started */
	    s->channels[0].is_used=TRUE;
	  }
	else if (s->channels[0].is_used==FALSE)
	  {
	    return FALSE;
	  }

	/* count active channel */
	for (i=1;i<CONF_MAX_PINS;++i){
		chan=&s->channels[i];
		if (chan->is_used == TRUE)
		{
			active_channel++;
		}
	}

	if (active_channel<=1) /* disable mix mode when it's not needed */
		s->mix_mode = FALSE;
	else
		s->mix_mode = TRUE;

	if (s->enable_directmode==FALSE)
	{
		s->mix_mode = TRUE;
	}

	if (s->mix_mode == FALSE)
		return FALSE;

	if (ms_bufferizer_get_avail(&(&s->channels[0])->buff)>=s->conf_gran)
	{
		return TRUE;
	}
	return FALSE;
}

static void conf_sum(MSFilter *f, ConfState *s){
	int i,j;
	Channel *chan;
	memset(s->sum,0,s->conf_nsamples*sizeof(int));

	chan=&s->channels[0];
	if (s->adaptative_msconf_buf*s->conf_gran<ms_bufferizer_get_avail(&chan->buff))
	{
		i = ms_bufferizer_get_avail(&chan->buff)/s->conf_gran;
		if (i>5)
			ms_message("Increasing buffer because sound card is late. (nb_buf=%i /old=%i)", i, s->adaptative_msconf_buf);
		s->adaptative_msconf_buf=i;
		if (s->adaptative_msconf_buf>10)
		{
			while (ms_bufferizer_get_avail(&chan->buff)> s->conf_gran*6)
			{
				ms_bufferizer_read(&chan->buff,(uint8_t*)chan->input,s->conf_gran);
				ms_message("Deleting extra sound card data %i", ms_bufferizer_get_avail(&chan->buff));
			}
		}
	}
	else if (s->adaptative_msconf_buf*s->conf_gran>ms_bufferizer_get_avail(&chan->buff))
	{
		if (s->adaptative_msconf_buf>3)
		{
			s->adaptative_msconf_buf--;
			s->adaptative_msconf_buf=ms_bufferizer_get_avail(&chan->buff)/s->conf_gran;
			//ms_message("decreasing buffer because sound card is in advance. (nb_buf=%i)", s->adaptative_msconf_buf);
		}
	}

	if (s->adaptative_msconf_buf>6)
		s->adaptative_msconf_buf=6;

	for (i=0;i<CONF_MAX_PINS;++i){
		chan=&s->channels[i];

		/* skip soundread and short buffer entry */
		if (i>0 
			&& ms_bufferizer_get_avail(&chan->buff)> s->conf_gran
			&& ms_bufferizer_get_avail(&chan->buff)> (ms_bufferizer_get_avail(&s->channels[0].buff)+s->conf_gran*6) )
		{
#if 0
			int loudness;
#endif
			int vad=0;
			while (ms_bufferizer_get_avail(&chan->buff)> (ms_bufferizer_get_avail(&s->channels[0].buff)) )
			{
				ms_bufferizer_read(&chan->buff,(uint8_t*)chan->input,s->conf_gran);
#if 0
				speex_preprocess_ctl(chan->speex_pp, SPEEX_PREPROCESS_GET_AGC_LOUDNESS, &loudness);
#endif
				/* we want to remove 4 packets (40ms) in a near future: */
#ifndef DISABLE_SPEEX
				if (chan->speex_pp!=NULL && s->enable_vad==TRUE)
				{
					vad = speex_preprocess(chan->speex_pp, (short*)chan->input, NULL);
					if (vad==1)
						break; /* voice detected: process as usual */
					if (ms_bufferizer_get_avail(&chan->buff)<s->conf_gran)
						break; /* no more data to remove */
					ms_message("No voice detected: discarding sample. (idx=%i - bufsize=%i sncardbufsize=%i)",
						i, ms_bufferizer_get_avail(&chan->buff), ms_bufferizer_get_avail(&s->channels[0].buff));
				}
				if (ms_bufferizer_get_avail(&chan->buff) == (ms_bufferizer_get_avail(&s->channels[0].buff)))
					ms_message("same data in soundcard and incoming rtp. (idx=%i - bufsize=%i sncardbufsize=%i)",
						i, ms_bufferizer_get_avail(&chan->buff), ms_bufferizer_get_avail(&s->channels[0].buff));
#endif
				chan->stat_discarded++;
			}


			if (s->enable_halfduplex>0)
			{
#if 0
				ms_message("prob=%i", loudness);
#endif
				// LIMITATION:
				// HALF DUPLEX MODE IS TO BE USED WITH ONLY 
				// ONE SOUND CARD AND ONE RTP STREAM.
				if (vad>0) // && loudness>20)
				{
					/* speech detected */
					s->channels[0].is_speaking++;
					if (s->channels[0].is_speaking>2)
						s->channels[0].is_speaking=2;
				}
				else
				{
					s->channels[0].is_speaking--;
					if (s->channels[0].is_speaking<-2)
						s->channels[0].is_speaking=-2;
				}

				if (s->channels[0].is_speaking<=0)
					ms_message("silence on! (%i)", s->channels[0].is_speaking);
				else
					ms_message("speech on! (%i)", s->channels[0].is_speaking);
			}

			for(j=0;j<s->conf_nsamples;++j){
				s->sum[j]+=chan->input[j];
			}
			chan->has_contributed=TRUE;

			chan->stat_processed++;
		}
		else if (ms_bufferizer_get_avail(&chan->buff)>=s->conf_gran)
		{
			ms_bufferizer_read(&chan->buff,(uint8_t*)chan->input,s->conf_gran);
#ifndef DISABLE_SPEEX
			if (chan->speex_pp!=NULL && s->enable_vad==TRUE && i==0)
			{
				int vad;
				vad = speex_preprocess(chan->speex_pp, (short*)chan->input, NULL);
				ms_filter_notify(f, MS_CONF_SPEEX_PREPROCESS_MIC, (void*)chan->speex_pp);
			}
			else if (chan->speex_pp!=NULL && s->enable_vad==TRUE)
			{
#if 0
				int loudness;
#endif
				int vad;
				if (s->enable_halfduplex>0)
				{
					vad = speex_preprocess(chan->speex_pp, (short*)chan->input, NULL);
#if 0
					speex_preprocess_ctl(chan->speex_pp, SPEEX_PREPROCESS_GET_AGC_LOUDNESS, &loudness);
					ms_message("prob=%i", loudness);
#endif
					// LIMITATION:
					// HALF DUPLEX MODE IS TO BE USED WITH ONLY 
					// ONE SOUND CARD AND ONE RTP STREAM.
					if (vad>0) //&& loudness>20)
					{
						s->channels[0].is_speaking++;
						if (s->channels[0].is_speaking>2)
							s->channels[0].is_speaking=2;
					}
					else
					{
						s->channels[0].is_speaking--;
						if (s->channels[0].is_speaking<-2)
							s->channels[0].is_speaking=-2;
					}

					if (s->channels[0].is_speaking<=0)
						ms_message("silence on! (%i)", s->channels[0].is_speaking);
					else
						ms_message("speech on! (%i)", s->channels[0].is_speaking);
				}
				else
				{
					vad = speex_preprocess(chan->speex_pp, (short*)chan->input, NULL);
					//speex_preprocess_estimate_update(chan->speex_pp, (short*)chan->input);
				}
			}
#endif

			for(j=0;j<s->conf_nsamples;++j){
				s->sum[j]+=chan->input[j];
			}
			chan->has_contributed=TRUE;

			chan->stat_processed++;
		} else {
			chan->stat_missed++;
			if (i>0 && chan->is_used == TRUE)
			{
				chan->missed++;
				/* delete stream if data is missing since a long time */
				if (chan->missed>15)
				{
					chan->is_used=FALSE;
					ms_message("msconf: deleted contributing stream (pin=%i)", i);
				}
				/* couldn't we add confort noise for those outputs? */
			}
			chan->has_contributed=FALSE;
		}
	}
	return;
}

static inline int16_t saturate(int sample){
	if (sample>32000)
		sample=32000;
	else if (sample<-32000)
		sample=-32000;
	return (int16_t)sample;
}

static mblk_t * conf_output(ConfState *s, Channel *chan){
	mblk_t *m=allocb(s->conf_gran,0);
	int i;
	int tmp;
	if (chan->has_contributed==TRUE){
		for (i=0;i<s->conf_nsamples;++i){
			tmp=s->sum[i]-(int)chan->input[i];
			*((int16_t*)m->b_wptr)=saturate(tmp);
			m->b_wptr+=2;
		}
	}else{
		for (i=0;i<s->conf_nsamples;++i){
			tmp=s->sum[i];
			*((int16_t*)m->b_wptr)=saturate(tmp);
			m->b_wptr+=2;
		}
	}
	return m;
}

static void conf_dispatch(MSFilter *f, ConfState *s){
	int i;
	Channel *chan;
	mblk_t *m;
	//memset(s->sum,0,s->conf_nsamples*sizeof(int));
	for (i=0;i<CONF_MAX_PINS;++i){
		if (f->outputs[i]!=NULL){
			chan=&s->channels[i];
			if (s->channels[0].is_speaking<=0 || i%2==0) // RTP is silent, work as usual
				m=conf_output(s,chan);
			else
			{
				int k;
				m=allocb(s->conf_gran,0);
				for (k=0;k<s->conf_nsamples;++k){
					*((int16_t*)m->b_wptr)=0;
					m->b_wptr+=2;
				}
			}
			ms_queue_put(f->outputs[i],m);
		}
	}
}

static void conf_process(MSFilter *f){
	int i;
	ConfState *s=(ConfState*)f->data;
	Channel *chan;
	Channel *chan0;
	/*read from all inputs and put into bufferizers*/
	for (i=0;i<CONF_MAX_PINS;++i){
		if (f->inputs[i]!=NULL){
			chan=&s->channels[i];
			ms_bufferizer_put_from_queue(&chan->buff,f->inputs[i]);
			if (ms_bufferizer_get_avail(&chan->buff)>0)
			{
				chan->missed=0; /* reset counter of missed packet */
				if (i>0 && chan->is_used==FALSE)
				{<commit_msg>code for energy of signals<commit_after>/*
mediastreamer2 library - modular sound and video processing and streaming
Copyright (C) 2006  <NAME> (<EMAIL>)

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

#include "mediastreamer2/msfilter.h"
#include <math.h>

#if defined(_WIN32_WCE)
#define DISABLE_SPEEX
#endif

#ifndef HAVE_SPEEXDSP
#define DISABLE_SPEEX
#endif

#ifndef DISABLE_SPEEX
#include <speex/speex_preprocess.h>
#endif

#ifndef CONF_GRAN_MAX
#define CONF_GRAN_MAX 12 /* limit for 'too much data' */
#endif

#define CONF_NSAMPLES 160*4*4 /* (CONF_GRAN/2) */
#ifndef CONF_MAX_PINS
#define CONF_MAX_PINS 32
#endif

static const float max_e=32767*32767;
static const float coef=0.1;

typedef struct Channel{
	MSBufferizer buff;
	int16_t input[CONF_NSAMPLES];
	bool_t has_contributed;
	bool_t is_used;

	int is_speaking;

	int count;
	int missed;

	int stat_discarded;
	int stat_missed;
	int stat_processed;

#ifndef DISABLE_SPEEX
	SpeexPreprocessState *speex_pp;
#endif

	float energy;

} Channel;

typedef struct ConfState{
	Channel channels[CONF_MAX_PINS];
	int sum[CONF_NSAMPLES];
	int enable_directmode;
	int enable_vad;

	int enable_halfduplex;
	int vad_prob_start;
	int vad_prob_continue;

	int agc_level;
	int max_gain;
	int mix_mode;
	int samplerate;

	int adaptative_msconf_buf;
	int conf_gran;
	int conf_nsamples;
} ConfState;


static void channel_init(ConfState *s, Channel *chan, int pos){
	float f;
	int val;
	memset(chan, 0, sizeof(Channel));
	ms_bufferizer_init(&chan->buff);
#ifndef DISABLE_SPEEX
	//chan->speex_pp = speex_preprocess_state_init((s->conf_gran/2) *(s->samplerate/8000), s->samplerate);
	chan->speex_pp = speex_preprocess_state_init(s->conf_gran/2, s->samplerate);
	if (chan->speex_pp==NULL)
		return;

	/* configure sound card input on pin 0 */
	val=0;
	if (pos==0)
		val=1;

	if (s->enable_halfduplex>0 && pos%2==1)
		val=1;

	speex_preprocess_ctl(chan->speex_pp, SPEEX_PREPROCESS_SET_DENOISE, &val);
	val = -30;
	speex_preprocess_ctl(chan->speex_pp, SPEEX_PREPROCESS_SET_NOISE_SUPPRESS, &val);

	/* enable VAD only on incoming RTP stream */
	val=0;
	if (pos%2==1 || (pos==0 && s->enable_halfduplex>0))
	{
		val=1;
	}

	speex_preprocess_ctl(chan->speex_pp, SPEEX_PREPROCESS_SET_VAD, &val);
	if (s->vad_prob_start>0 && s->vad_prob_continue>0)
	{
		val = s->vad_prob_start; // xx%
		speex_preprocess_ctl(chan->speex_pp, SPEEX_PREPROCESS_SET_PROB_START, &val);
		val = s->vad_prob_continue; // xx%
		speex_preprocess_ctl(chan->speex_pp, SPEEX_PREPROCESS_SET_PROB_CONTINUE, &val);
	}

	/* enable AGC only on local soundcard */
	val=0;
	f=8000;
	if (s->agc_level>0 && pos==0)
		val=1;
	else if (pos==0 && s->enable_halfduplex>0)
		val=1;
	else if ( pos%2==1 && s->enable_halfduplex>0)
		val=1;
	if (s->agc_level>0)
		f=s->agc_level;


	speex_preprocess_ctl(chan->speex_pp, SPEEX_PREPROCESS_SET_AGC, &val);
	speex_preprocess_ctl(chan->speex_pp, SPEEX_PREPROCESS_SET_AGC_LEVEL, &f);
	val=s->max_gain;
	speex_preprocess_ctl(chan->speex_pp, SPEEX_PREPROCESS_SET_AGC_MAX_GAIN, &val);

	val=0;
#if 0
	val=1; // do more testing
#endif
	speex_preprocess_ctl(chan->speex_pp, SPEEX_PREPROCESS_SET_DEREVERB, &val);
	f=.4;
	speex_preprocess_ctl(chan->speex_pp, SPEEX_PREPROCESS_SET_DEREVERB_DECAY, &f);
	f=.3;
	speex_preprocess_ctl(chan->speex_pp, SPEEX_PREPROCESS_SET_DEREVERB_LEVEL, &f);

#endif
}

static void channel_uninit(Channel *chan){
	ms_bufferizer_uninit(&chan->buff);
#ifndef DISABLE_SPEEX
	if (chan->speex_pp!=NULL)
	    speex_preprocess_state_destroy(chan->speex_pp);
	chan->speex_pp=NULL;
#endif
}

static void conf_init(MSFilter *f){
	ConfState *s=(ConfState *)ms_new0(ConfState,1);
	int i;
	s->samplerate=8000;
	s->conf_gran=((16 * s->samplerate) / 800) *2;
	s->conf_nsamples=s->conf_gran/2;
    for (i=0;i<CONF_MAX_PINS;i++)
		channel_init(s, &s->channels[i], i);
	s->enable_directmode=FALSE;
	s->enable_vad=TRUE;
	s->agc_level=0;
	s->max_gain=30;
	s->mix_mode=TRUE;
	s->adaptative_msconf_buf=2;
	f->data=s;
}

static void conf_uninit(MSFilter *f){
	ConfState *s=(ConfState*)f->data;
	int i;
	for (i=0;i<CONF_MAX_PINS;i++)
		channel_uninit(&s->channels[i]);
	ms_free(f->data);
}

static void conf_preprocess(MSFilter *f){
	ConfState *s=(ConfState*)f->data;
	int i;
	for (i=0;i<CONF_MAX_PINS;i++)
	  {
	    s->channels[i].is_used=FALSE;
	    s->channels[i].missed=0;
	    s->channels[i].stat_discarded=0;
	    s->channels[i].stat_missed=0;
	    s->channels[i].stat_processed=0;
	  }
}

static bool_t should_process(MSFilter *f, ConfState *s){
	Channel *chan;
	int active_channel=0;
	int i;

	if (ms_bufferizer_get_avail(&(&s->channels[0])->buff)>s->conf_gran
	    && s->channels[0].is_used==FALSE)
	  {
	    /* soundread has just started */
	    s->channels[0].is_used=TRUE;
	  }
	else if (s->channels[0].is_used==FALSE)
	  {
	    return FALSE;
	  }

	/* count active channel */
	for (i=1;i<CONF_MAX_PINS;++i){
		chan=&s->channels[i];
		if (chan->is_used == TRUE)
		{
			active_channel++;
		}
	}

	if (active_channel<=1) /* disable mix mode when it's not needed */
		s->mix_mode = FALSE;
	else
		s->mix_mode = TRUE;

	if (s->enable_directmode==FALSE)
	{
		s->mix_mode = TRUE;
	}

	if (s->mix_mode == FALSE)
		return FALSE;

	if (ms_bufferizer_get_avail(&(&s->channels[0])->buff)>=s->conf_gran)
	{
		return TRUE;
	}
	return FALSE;
}

static void conf_sum(MSFilter *f, ConfState *s){
	int i,j;
	Channel *chan;
	memset(s->sum,0,s->conf_nsamples*sizeof(int));

	chan=&s->channels[0];
	if (s->adaptative_msconf_buf*s->conf_gran<ms_bufferizer_get_avail(&chan->buff))
	{
		i = ms_bufferizer_get_avail(&chan->buff)/s->conf_gran;
		if (i>5)
			ms_message("Increasing buffer because sound card is late. (nb_buf=%i /old=%i)", i, s->adaptative_msconf_buf);
		s->adaptative_msconf_buf=i;
		if (s->adaptative_msconf_buf>10)
		{
			while (ms_bufferizer_get_avail(&chan->buff)> s->conf_gran*6)
			{
				ms_bufferizer_read(&chan->buff,(uint8_t*)chan->input,s->conf_gran);
				ms_message("Deleting extra sound card data %i", ms_bufferizer_get_avail(&chan->buff));
			}
		}
	}
	else if (s->adaptative_msconf_buf*s->conf_gran>ms_bufferizer_get_avail(&chan->buff))
	{
		if (s->adaptative_msconf_buf>3)
		{
			s->adaptative_msconf_buf--;
			s->adaptative_msconf_buf=ms_bufferizer_get_avail(&chan->buff)/s->conf_gran;
			//ms_message("decreasing buffer because sound card is in advance. (nb_buf=%i)", s->adaptative_msconf_buf);
		}
	}

	if (s->adaptative_msconf_buf>6)
		s->adaptative_msconf_buf=6;

	for (i=0;i<CONF_MAX_PINS;++i){
		chan=&s->channels[i];

		/* skip soundread and short buffer entry */
		if (i>0 
			&& ms_bufferizer_get_avail(&chan->buff)> s->conf_gran
			&& ms_bufferizer_get_avail(&chan->buff)> (ms_bufferizer_get_avail(&s->channels[0].buff)+s->conf_gran*6) )
		{
#if 0
			int loudness;
#endif
			int vad=0;
			while (ms_bufferizer_get_avail(&chan->buff)> (ms_bufferizer_get_avail(&s->channels[0].buff)) )
			{
				ms_bufferizer_read(&chan->buff,(uint8_t*)chan->input,s->conf_gran);
#if 0
				speex_preprocess_ctl(chan->speex_pp, SPEEX_PREPROCESS_GET_AGC_LOUDNESS, &loudness);
#endif
				/* we want to remove 4 packets (40ms) in a near future: */
#ifndef DISABLE_SPEEX
				if (chan->speex_pp!=NULL && s->enable_vad==TRUE)
				{
					vad = speex_preprocess(chan->speex_pp, (short*)chan->input, NULL);
					if (vad==1)
						break; /* voice detected: process as usual */
					if (ms_bufferizer_get_avail(&chan->buff)<s->conf_gran)
						break; /* no more data to remove */
					ms_message("No voice detected: discarding sample. (idx=%i - bufsize=%i sncardbufsize=%i)",
						i, ms_bufferizer_get_avail(&chan->buff), ms_bufferizer_get_avail(&s->channels[0].buff));
				}
				if (ms_bufferizer_get_avail(&chan->buff) == (ms_bufferizer_get_avail(&s->channels[0].buff)))
					ms_message("same data in soundcard and incoming rtp. (idx=%i - bufsize=%i sncardbufsize=%i)",
						i, ms_bufferizer_get_avail(&chan->buff), ms_bufferizer_get_avail(&s->channels[0].buff));
#endif
				chan->stat_discarded++;
			}


			if (s->enable_halfduplex>0)
			{
#if 0
				ms_message("prob=%i", loudness);
#endif
				// LIMITATION:
				// HALF DUPLEX MODE IS TO BE USED WITH ONLY 
				// ONE SOUND CARD AND ONE RTP STREAM.
				if (vad>0) // && loudness>20)
				{
					/* speech detected */
					s->channels[0].is_speaking++;
					if (s->channels[0].is_speaking>2)
						s->channels[0].is_speaking=2;
				}
				else
				{
					s->channels[0].is_speaking--;
					if (s->channels[0].is_speaking<-2)
						s->channels[0].is_speaking=-2;
				}

				//if (s->channels[0].is_speaking<=0)
				//	ms_message("silence on! (%i)", s->channels[0].is_speaking);
				//else
				//	ms_message("speech on! (%i)", s->channels[0].is_speaking);
			}

			for(j=0;j<s->conf_nsamples;++j){
				s->sum[j]+=chan->input[j];
			}
			chan->has_contributed=TRUE;

			chan->stat_processed++;
		}
		else if (ms_bufferizer_get_avail(&chan->buff)>=s->conf_gran)
		{
			ms_bufferizer_read(&chan->buff,(uint8_t*)chan->input,s->conf_gran);
#ifndef DISABLE_SPEEX
			if (chan->speex_pp!=NULL && s->enable_vad==TRUE && i==0)
			{
				struct channel_volume {
					float energy;
					int channel;
				};
				struct channel_volume vol;
				float en;
				int vad;

				en=chan->energy;
				for(j=0;j<s->conf_nsamples;++j){
					float s=chan->input[j];
					en=(s*s*coef) + (1.0-coef)*en;
				}
				chan->energy=en;
				vol.energy = 10*log10f(chan->energy/max_e);
				vol.channel = i;
				ms_filter_notify(f, MS_CONF_CHANNEL_VOLUME, (void*)&vol);

				vad = speex_preprocess(chan->speex_pp, (short*)chan->input, NULL);
				ms_filter_notify(f, MS_CONF_SPEEX_PREPROCESS_MIC, (void*)chan->speex_pp);
			}
			else if (chan->speex_pp!=NULL && s->enable_vad==TRUE)
			{
				struct channel_volume {
					float energy;
					int channel;
				};
				struct channel_volume vol;
				float en;

				int vad;

				en=chan->energy;
				for(j=0;j<s->conf_nsamples;++j){
					float s=chan->input[j];
					en=(s*s*coef) + (1.0-coef)*en;
				}
				chan->energy=en;
				vol.energy = 10*log10f(chan->energy/max_e);
				vol.channel = i;
				ms_filter_notify(f, MS_CONF_CHANNEL_VOLUME, (void*)&vol);

				if (s->enable_halfduplex>0)
				{
					vad = speex_preprocess(chan->speex_pp, (short*)chan->input, NULL);
#if 0
					speex_preprocess_ctl(chan->speex_pp, SPEEX_PREPROCESS_GET_AGC_LOUDNESS, &loudness);
					ms_message("prob=%i", loudness);
#endif
					// LIMITATION:
					// HALF DUPLEX MODE IS TO BE USED WITH ONLY 
					// ONE SOUND CARD AND ONE RTP STREAM.
					if (vad>0) //&& loudness>20)
					{
						s->channels[0].is_speaking++;
						if (s->channels[0].is_speaking>2)
							s->channels[0].is_speaking=2;
					}
					else
					{
						s->channels[0].is_speaking--;
						if (s->channels[0].is_speaking<-2)
							s->channels[0].is_speaking=-2;
					}

					//if (s->channels[0].is_speaking<=0)
					//	ms_message("silence on! (%i)", s->channels[0].is_speaking);
					//else
					//	ms_message("speech on! (%i)", s->channels[0].is_speaking);
				}
				else
				{
					vad = speex_preprocess(chan->speex_pp, (short*)chan->input, NULL);
					//speex_preprocess_estimate_update(chan->speex_pp, (short*)chan->input);
				}
			}
#endif

			for(j=0;j<s->conf_nsamples;++j){
				s->sum[j]+=chan->input[j];
			}
			chan->has_contributed=TRUE;

			chan->stat_processed++;
		} else {
			chan->stat_missed++;
			if (i>0 && chan->is_used == TRUE)
			{
				chan->missed++;
				/* delete stream if data is missing since a long time */
				if (chan->missed>15)
				{
					chan->is_used=FALSE;
					ms_message("msconf: deleted contributing stream (pin=%i)", i);
				}
				/* couldn't we add confort noise for those outputs? */
			}
			chan->has_contributed=FALSE;
		}
	}
	return;
}

static inline int16_t saturate(int sample){
	if (sample>32000)
		sample=32000;
	else if (sample<-32000)
		sample=-32000;
	return (int16_t)sample;
}

static mblk_t * conf_output(ConfState *s, Channel *chan){
	mblk_t *m=allocb(s->conf_gran,0);
	int i;
	int tmp;
	if (chan->has_contributed==TRUE){
		for (i=0;i<s->conf_nsamples;++i){
			tmp=s->sum[i]-(int)chan->input[i];
			*((int16_t*)m->b_wptr)=saturate(tmp);
			m->b_wptr+=2;
		}
	}else{
		for (i=0;i<s->conf_nsamples;++i){
			tmp=s->sum[i];
			*((int16_t*)m->b_wptr)=saturate(tmp);
			m->b_wptr+=2;
		}
	}
	return m;
}

static void conf_dispatch(MSFilter *f, ConfState *s){
	int i;
	Channel *chan;
	mblk_t *m;
	//memset(s->sum,0,s->conf_nsamples*sizeof(int));
	for (i=0;i<CONF_MAX_PINS;++i){
		if (f->outputs[i]!=NULL){
			chan=&s->channels[i];
			//if (s->channels[0].is_speaking<=0 || i%2==0) // RTP is silent, work as usual
				m=conf_output(s,chan);
			//else
			//{
			//	int k;
			//	m=allocb(s->conf_gran,0);
			//	for (k=0;k<s->conf_nsamples;++k){
			//		*((int16_t*)m->b_wptr)=0;
			//		m->b_wptr+=2;
			//	}
			//}
			ms_queue_put(f->outputs[i],m);
		}
	}
}

static void conf_process(MSFilter *f){
	int i;
	ConfState *s=(ConfState*)f->data;
	Channel *chan;
	Channel *chan0;
	/*read from all inputs and put into bufferizers*/
	for (i=0;i<CONF_MAX_PINS;++i){
		if (f->inputs[i]!=NULL){
			chan=&s->channels[i];
			ms_bufferizer_put_from_queue(&chan->buff,f->inputs[i]);
			if (ms_bufferizer_get_avail(&chan->buff)>0)
			{
				chan->missed=0; /* reset counter of missed packet */
				if (i>0 && chan->is_used==FALSE)
				{
{'fraction_non_alphanumeric': 0.16133596455300295, 'fraction_numerical': 0.018877759988085042, 'mean_word_length': 4.14619658938494, 'pattern_counts': {'":': 0, '<': 55, '<?xml version=': 0, '>': 511, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6184502', 'n_tokens_mistral': 12441, 'n_tokens_neox': 10887, 'n_words': 1981}